Replacing CurrencyCell with generic NumberCell that uses NumberFormat.

Review at http://gwt-code-reviews.appspot.com/568801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8240 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ExpensesCommon.gwt.xml b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ExpensesCommon.gwt.xml
index ebac66c..6987344 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ExpensesCommon.gwt.xml
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ExpensesCommon.gwt.xml
@@ -11,4 +11,8 @@
   <source path='request'/>
   <source path='place'/>
   <source path='ui'/>
+
+  <!-- Default Locale. -->
+  <extend-property name="locale" values="en"/>
+  <set-property-fallback name="locale" value="en"/>
 </module>
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseDetails.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseDetails.java
index ef3b856..0ffda82 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseDetails.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseDetails.java
@@ -16,9 +16,9 @@
 package com.google.gwt.sample.expenses.gwt.client;
 
 import com.google.gwt.cell.client.Cell;
-import com.google.gwt.cell.client.CurrencyCell;
 import com.google.gwt.cell.client.DateCell;
 import com.google.gwt.cell.client.FieldUpdater;
+import com.google.gwt.cell.client.NumberCell;
 import com.google.gwt.cell.client.SelectionCell;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.cell.client.ValueUpdater;
@@ -38,6 +38,7 @@
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.NumberFormat;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.sample.bikeshed.style.client.Styles;
@@ -339,12 +340,6 @@
     TableStyle cellTableStyle();
   }
 
-  private static final GetValue<ExpenseRecord, Date> createdGetter = new GetValue<ExpenseRecord, Date>() {
-    public Date getValue(ExpenseRecord object) {
-      return object.getCreated();
-    }
-  };
-
   private static ExpenseDetailsUiBinder uiBinder = GWT.create(ExpenseDetailsUiBinder.class);
 
   @UiField
@@ -384,8 +379,12 @@
 
   private List<SortableHeader> allHeaders = new ArrayList<SortableHeader>();
 
-  private SortableColumn<ExpenseRecord, String> approvalColumn;
-  private SortableColumn<ExpenseRecord, Date> createdColumn;
+  private Column<ExpenseRecord, String> approvalColumn;
+
+  /**
+   * The default {@link Comparator} used for sorting.
+   */
+  private Comparator<ExpenseRecord> defaultComparator;
 
   /**
    * The popup used to display errors to the user.
@@ -542,7 +541,7 @@
     reportsLink.setText(ExpenseList.getBreadcrumb(department, employee));
 
     // Reset sorting state of table
-    lastComparator = createdColumn.getComparator(false);
+    lastComparator = defaultComparator;
     for (SortableHeader header : allHeaders) {
       header.setSorted(false);
       header.setReverseSort(true);
@@ -576,32 +575,52 @@
     });
 
     // Created column.
-    createdColumn = addColumn(view, "Created", new DateCell(
-        DateTimeFormat.getFormat("MMM dd yyyy")), createdGetter);
-    lastComparator = createdColumn.getComparator(false);
+    GetValue<ExpenseRecord, Date> createdGetter = new GetValue<ExpenseRecord, Date>() {
+      public Date getValue(ExpenseRecord object) {
+        return object.getCreated();
+      }
+    };
+    defaultComparator = createColumnComparator(createdGetter, false);
+    Comparator<ExpenseRecord> createdDesc = createColumnComparator(
+        createdGetter, true);
+    addColumn(view, "Created", new DateCell(
+        DateTimeFormat.getFormat("MMM dd yyyy")), createdGetter,
+        defaultComparator, createdDesc);
+    lastComparator = defaultComparator;
 
     // Description column.
-    addColumn(view, "Description", new GetValue<ExpenseRecord, String>() {
-      public String getValue(ExpenseRecord object) {
-        return object.getDescription();
-      }
-    });
-
-    // Category column.
-    addColumn(view, "Category", new GetValue<ExpenseRecord, String>() {
-      public String getValue(ExpenseRecord object) {
-        return object.getCategory();
-      }
-    });
-
-    // Amount column.
-    addColumn(view, "Amount", new CurrencyCell(),
-        new GetValue<ExpenseRecord, Integer>() {
-          public Integer getValue(ExpenseRecord object) {
-            return (int) (object.getAmount().doubleValue() * 100);
+    addColumn(view, "Description", new TextCell(),
+        new GetValue<ExpenseRecord, String>() {
+          public String getValue(ExpenseRecord object) {
+            return object.getDescription();
           }
         });
 
+    // Category column.
+    addColumn(view, "Category", new TextCell(),
+        new GetValue<ExpenseRecord, String>() {
+          public String getValue(ExpenseRecord object) {
+            return object.getCategory();
+          }
+        });
+
+    // Amount column.
+    final GetValue<ExpenseRecord, Double> amountGetter = new GetValue<ExpenseRecord, Double>() {
+      public Double getValue(ExpenseRecord object) {
+        return object.getAmount();
+      }
+    };
+    Comparator<ExpenseRecord> amountAsc = createColumnComparator(amountGetter,
+        false);
+    Comparator<ExpenseRecord> amountDesc = createColumnComparator(amountGetter,
+        true);
+    addColumn(view, "Amount", new NumberCell(NumberFormat.getCurrencyFormat()),
+        new GetValue<ExpenseRecord, Number>() {
+          public Number getValue(ExpenseRecord object) {
+            return amountGetter.getValue(object);
+          }
+        }, amountAsc, amountDesc);
+
     // Dialog box to obtain a reason for a denial
     final DenialPopup denialPopup = new DenialPopup();
     denialPopup.addCloseHandler(new CloseHandler<PopupPanel>() {
@@ -652,11 +671,43 @@
     return view;
   }
 
-  private <C extends Comparable<C>> SortableColumn<ExpenseRecord, C> addColumn(
+  /**
+   * Add a column of a {@link Comparable} type using default comparators.
+   * 
+   * @param <C> the column type
+   * @param table the table
+   * @param text the header text
+   * @param cell the cell used to render values
+   * @param getter the {@link GetValue} used to retrieve cell values
+   * @return the new column
+   */
+  private <C extends Comparable<C>> Column<ExpenseRecord, C> addColumn(
       final CellTable<ExpenseRecord> table, final String text,
       final Cell<C> cell, final GetValue<ExpenseRecord, C> getter) {
-    final SortableColumn<ExpenseRecord, C> column = new SortableColumn<ExpenseRecord, C>(
-        cell) {
+    return addColumn(table, text, cell, getter, createColumnComparator(getter,
+        false), createColumnComparator(getter, true));
+  }
+
+  /**
+   * Add a column with the specified comparators.
+   * 
+   * @param <C> the column type
+   * @param table the table
+   * @param text the header text
+   * @param cell the cell used to render values
+   * @param getter the {@link GetValue} used to retrieve cell values
+   * @param ascComparator the comparator used to sort ascending
+   * @param descComparator the comparator used to sort ascending
+   * @return the new column
+   */
+  private <C> Column<ExpenseRecord, C> addColumn(
+      final CellTable<ExpenseRecord> table, final String text,
+      final Cell<C> cell, final GetValue<ExpenseRecord, C> getter,
+      final Comparator<ExpenseRecord> ascComparator,
+      final Comparator<ExpenseRecord> descComparator) {
+
+    // Create the column.
+    final Column<ExpenseRecord, C> column = new Column<ExpenseRecord, C>(cell) {
       @Override
       public C getValue(ExpenseRecord object) {
         return getter.getValue(object);
@@ -665,6 +716,7 @@
     final SortableHeader header = new SortableHeader(text);
     allHeaders.add(header);
 
+    // Hook up sorting.
     header.setUpdater(new ValueUpdater<String>() {
       public void update(String value) {
         header.setSorted(true);
@@ -676,8 +728,9 @@
             otherHeader.setReverseSort(true);
           }
         }
-        sortExpenses(items.getList(),
-            column.getComparator(header.getReverseSort()));
+
+        sortExpenses(items.getList(), header.getReverseSort() ? descComparator
+            : ascComparator);
         table.refreshHeaders();
       }
     });
@@ -685,10 +738,41 @@
     return column;
   }
 
-  private Column<ExpenseRecord, String> addColumn(
-      CellTable<ExpenseRecord> table, final String text,
-      final GetValue<ExpenseRecord, String> getter) {
-    return addColumn(table, text, new TextCell(), getter);
+  /**
+   * Create a comparator for the column.
+   * 
+   * @param <C> the column type
+   * @param getter the {@link GetValue} used to get the cell value
+   * @param descending true if descending, false if ascending
+   * @return the comparator
+   */
+  private <C extends Comparable<C>> Comparator<ExpenseRecord> createColumnComparator(
+      final GetValue<ExpenseRecord, C> getter, final boolean descending) {
+    return new Comparator<ExpenseRecord>() {
+      public int compare(ExpenseRecord o1, ExpenseRecord o2) {
+        // Null check the row object.
+        if (o1 == null && o2 == null) {
+          return 0;
+        } else if (o1 == null) {
+          return descending ? 1 : -1;
+        } else if (o2 == null) {
+          return descending ? -1 : 1;
+        }
+
+        // Compare the column value.
+        C c1 = getter.getValue(o1);
+        C c2 = getter.getValue(o2);
+        if (c1 == null && c2 == null) {
+          return 0;
+        } else if (c1 == null) {
+          return descending ? 1 : -1;
+        } else if (c2 == null) {
+          return descending ? -1 : 1;
+        }
+        int comparison = c1.compareTo(c2);
+        return descending ? -comparison : comparison;
+      }
+    };
   }
 
   /**
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/SortableColumn.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/SortableColumn.java
deleted file mode 100644
index 42db8c2..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/SortableColumn.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.sample.expenses.gwt.client;
-
-import com.google.gwt.cell.client.Cell;
-import com.google.gwt.user.cellview.client.Column;
-
-import java.util.Comparator;
-
-/**
- * A column that provides forward and reverse {@link Comparator}s.
- * 
- * @param <T> the row type
- * @param <C> the column type
- */
-public abstract class SortableColumn<T, C> extends Column<T, C> {
-  private Comparator<T> forwardComparator;
-
-  private Comparator<T> reverseComparator;
-
-  public SortableColumn(Cell<C> cell) {
-    super(cell);
-  }
-
-  /**
-   * Convenience method to return a {@link Comparator} that may be used to sort
-   * records of type T by the values of this column, using the natural ordering
-   * of the column type C. If C does not implement Comparable<C>, a runtime
-   * exception will be thrown when the returned comparator's
-   * {@link Comparator#compare(Object, Object) compare} method is called. If
-   * reverse is true, the returned comparator will sort in reverse order. The
-   * returned comparator instances are cached for future calls.
-   * 
-   * @param reverse if true, sort in reverse
-   * @return an instance of Comparator<T>
-   */
-  public Comparator<T> getComparator(final boolean reverse) {
-    if (!reverse && forwardComparator != null) {
-      return forwardComparator;
-    }
-    if (reverse && reverseComparator != null) {
-      return reverseComparator;
-    }
-    Comparator<T> comparator = new Comparator<T>() {
-      @SuppressWarnings("unchecked")
-      public int compare(T o1, T o2) {
-        // Null check the row object.
-        if (o1 == null && o2 == null) {
-          return 0;
-        } else if (o1 == null) {
-          return reverse ? 1 : -1;
-        } else if (o2 == null) {
-          return reverse ? -1 : 1;
-        }
-
-        // Compare the column value.
-        C c1 = getValue(o1);
-        C c2 = getValue(o2);
-        if (c1 == null && c2 == null) {
-          return 0;
-        } else if (c1 == null) {
-          return reverse ? 1 : -1;
-        } else if (c2 == null) {
-          return reverse ? -1 : 1;
-        }
-        int comparison = ((Comparable<C>) c1).compareTo(c2);
-        return reverse ? -comparison : comparison;
-      }
-    };
-
-    if (reverse) {
-      reverseComparator = comparator;
-    } else {
-      forwardComparator = comparator;
-    }
-    return comparator;
-  }
-}
diff --git a/user/src/com/google/gwt/cell/client/CurrencyCell.java b/user/src/com/google/gwt/cell/client/CurrencyCell.java
deleted file mode 100644
index a0c9464..0000000
--- a/user/src/com/google/gwt/cell/client/CurrencyCell.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.cell.client;
-
-/**
- * A {@link Cell} used to render currency.
- * 
- * <p>
- * Note: This class is new and its interface subject to change.
- * </p>
- */
-public class CurrencyCell extends AbstractCell<Integer> {
-
-  @Override
-  public void render(Integer price, Object viewData, StringBuilder sb) {
-    // TODO: Use legit i18n'd currency formatting.
-    boolean negative = price < 0;
-    if (negative) {
-      price = -price;
-    }
-    int dollars = price / 100;
-    int cents = price % 100;
-
-    if (negative) {
-      sb.append("-");
-    }
-    sb.append("$");
-    sb.append(dollars);
-    sb.append('.');
-    if (cents < 10) {
-      sb.append('0');
-    }
-    sb.append(cents);
-  }
-}
diff --git a/user/src/com/google/gwt/cell/client/NumberCell.java b/user/src/com/google/gwt/cell/client/NumberCell.java
new file mode 100644
index 0000000..5888c0e
--- /dev/null
+++ b/user/src/com/google/gwt/cell/client/NumberCell.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.cell.client;
+
+import com.google.gwt.i18n.client.NumberFormat;
+
+/**
+ * A {@link Cell} used to render formatted numbers.
+ */
+public class NumberCell extends AbstractCell<Number> {
+
+  /**
+   * The {@link NumberFormat} used to render the number.
+   */
+  private final NumberFormat format;
+
+  /**
+   * Construct a new {@link NumberCell} using decimal format.
+   */
+  public NumberCell() {
+    this(NumberFormat.getDecimalFormat());
+  }
+
+  /**
+   * Construct a new {@link NumberCell}.
+   * 
+   * @param format the {@link NumberFormat} used to render the number
+   */
+  public NumberCell(NumberFormat format) {
+    this.format = format;
+  }
+
+  @Override
+  public void render(Number value, Object viewData, StringBuilder sb) {
+    if (value != null) {
+      sb.append(format.format(value));
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/cell/client/NumberCellTest.java b/user/test/com/google/gwt/cell/client/NumberCellTest.java
new file mode 100644
index 0000000..543a5fe
--- /dev/null
+++ b/user/test/com/google/gwt/cell/client/NumberCellTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.cell.client;
+
+import com.google.gwt.i18n.client.NumberFormat;
+
+/**
+ * Tests for {@link ButtonCell}.
+ */
+public class NumberCellTest extends CellTestBase<Number> {
+
+  @Override
+  protected boolean consumesEvents() {
+    return false;
+  }
+
+  @Override
+  protected Cell<Number> createCell() {
+    return new NumberCell(NumberFormat.getFormat("#.##"));
+  }
+
+  @Override
+  protected Number createCellValue() {
+    return new Double(100.12);
+  }
+
+  @Override
+  protected boolean dependsOnSelection() {
+    return false;
+  }
+
+  @Override
+  protected String getExpectedInnerHtml() {
+    return "100.12";
+  }
+
+  @Override
+  protected String getExpectedInnerHtmlNull() {
+    return "";
+  }
+}