Checkpoint validation work + some misc stock fixes


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7742 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/FieldUpdater.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/FieldUpdater.java
index 2e0fd85..a02cdb9 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/FieldUpdater.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/FieldUpdater.java
@@ -26,9 +26,9 @@
   
   /**
    * Announces a new value for a field within a base object.
-   * 
+   * @param index TODO
    * @param object the base object to be updated
    * @param value the new value of the field being updated.
    */
-  void update(T object, C value);
+  void update(int index, T object, C value);
 }
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
index dc81c23..7913c6a 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
@@ -35,17 +35,19 @@
     this.cell = cell;
   }
 
-  public void onBrowserEvent(Element elem, final T object, NativeEvent event) {
+  public void onBrowserEvent(Element elem, final int index, final T object,
+      NativeEvent event) {
     cell.onBrowserEvent(elem, getValue(object), event,
         fieldUpdater == null ? null : new ValueUpdater<C>() {
       public void update(C value) {
-        fieldUpdater.update(object, value);
+        fieldUpdater.update(index, object, value);
       }
     });
   }
 
   public void render(T object, StringBuilder sb) {
-    cell.render(getValue(object), sb);
+    C value = getValue(object);
+    cell.render(value, sb);
   }
 
   public void setFieldUpdater(FieldUpdater<T, C> fieldUpdater) {
@@ -56,5 +58,9 @@
     return cell;
   }
 
+  protected FieldUpdater<T, C> getFieldUpdater() {
+    return fieldUpdater;
+  }
+
   protected abstract C getValue(T object);
-}
\ No newline at end of file
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
index 271cd97..5959cd7 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
@@ -149,7 +149,7 @@
       int row = tr.getSectionRowIndex();
       T value = data.get(row);
       Column<T, ?> column = columns.get(col);
-      column.onBrowserEvent(cell, value, event);
+      column.onBrowserEvent(cell, curPage * pageSize + row, value, event);
     }
   }
 
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
index 7152c27..f46dba0 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
@@ -151,10 +151,6 @@
   protected void updateViewData(int start, int length, List<T> values) {
     int end = start + length;
     for (DefaultListRegistration reg : registrations) {
-      // Inform the views if there is no data
-      if (values.size() == 0) {
-        reg.getHandler().onSizeChanged(new SizeChangeEvent(0, true));
-      }
       int curStart = reg.getStart();
       int curLength = reg.getLength();
       int curEnd = curStart + curLength;
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListListModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListListModel.java
index 5eaf458..74e8977 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListListModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListListModel.java
@@ -223,6 +223,7 @@
 
   @Override
   protected void onRangeChanged() {
+    updateDataSize(listWrapper.size(), true);
     updateViewData(0, listWrapper.size(), listWrapper);
   }
 }
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/FavoritesWidget.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/FavoritesWidget.java
index c099b4d..ef2f97e 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/FavoritesWidget.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/FavoritesWidget.java
@@ -47,6 +47,7 @@
     listView.addColumn(Columns.changeColumn, new TextHeader("change"));
     listView.addColumn(Columns.sharesColumn, new TextHeader("shares"));
     listView.addColumn(Columns.dollarsColumn, new TextHeader("value"));
+    listView.addColumn(Columns.profitLossColumn, new TextHeader("profit/loss"));
     listView.addColumn(Columns.buyColumn);
     listView.addColumn(Columns.sellColumn);
   }
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
index 619feff..6aa5f9f 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
@@ -143,19 +143,19 @@
 
     // Hook up handlers to columns and the buy/sell popup.
     Columns.favoriteColumn.setFieldUpdater(new FieldUpdater<StockQuote, Boolean>() {
-      public void update(StockQuote object, Boolean value) {
+      public void update(int index, StockQuote object, Boolean value) {
         setFavorite(object.getTicker(), value);
       }
     });
 
     Columns.buyColumn.setFieldUpdater(new FieldUpdater<StockQuote, String>() {
-      public void update(StockQuote quote, String value) {
+      public void update(int index, StockQuote quote, String value) {
         buy(quote);
       }
     });
 
     Columns.sellColumn.setFieldUpdater(new FieldUpdater<StockQuote, String>() {
-      public void update(StockQuote quote, String value) {
+      public void update(int index, StockQuote quote, String value) {
         sell(quote);
       }
     });
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/validation/Validation.gwt.xml b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/Validation.gwt.xml
new file mode 100644
index 0000000..ea4cccf
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/Validation.gwt.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Could not determine the version of your GWT SDK; using the module DTD from GWT 1.6.4. You may want to change this. -->
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd">
+<module rename-to='validation'>
+	<inherits name="com.google.gwt.bikeshed.list.List" />
+	<source path="client" />
+	<entry-point
+		class="com.google.gwt.bikeshed.sample.validation.client.Validation">
+	</entry-point>
+</module>
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableColumn.java b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableColumn.java
new file mode 100644
index 0000000..e3600ba
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableColumn.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bikeshed.sample.validation.client;
+
+import com.google.gwt.bikeshed.cells.client.Cell;
+import com.google.gwt.bikeshed.cells.client.FieldUpdater;
+import com.google.gwt.bikeshed.cells.client.ValueUpdater;
+import com.google.gwt.bikeshed.list.client.Column;
+import com.google.gwt.bikeshed.sample.validation.client.ValidatableField.DefaultValidatableField;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A column that support validation.
+ * 
+ * @param <T> the row type
+ * @param <C> the column type
+ */
+// TODO - avoid wrapping cells that are never modified
+public abstract class ValidatableColumn<T, C> extends Column<T, ValidatableField<C>> {
+  
+  Map<T, ValidatableField<C>> fieldMap = new HashMap<T, ValidatableField<C>>();
+
+  public ValidatableColumn(Cell<ValidatableField<C>> cell) {
+    super(cell);
+  }
+
+  // Override onBrowserEvent to copy the ValueUpdater value into our copy
+  @Override
+  public void onBrowserEvent(Element elem, final int index, final T object,
+      NativeEvent event) {
+    final FieldUpdater<T, ValidatableField<C>> fieldUpdater = getFieldUpdater();
+    final ValidatableField<C> field = getValue(object);
+    getCell().onBrowserEvent(elem, field, event,
+        fieldUpdater == null ? null : new ValueUpdater<ValidatableField<C>>() {
+      public void update(ValidatableField<C> value) {
+        // Copy pending value from value (copy) to field (original)
+        field.setPendingValue(value.getPendingValue());
+        fieldUpdater.update(index, object, field);
+      }
+    });
+  }
+
+  /**
+   * Returns the value of the field with the underlying object that is to be
+   * validated.
+   *
+   * @param object the underlying data transfer object, of type T
+   * @return a value of type C
+   */
+  protected abstract C getValidatableValue(T object);
+  
+  @Override
+  protected ValidatableField<C> getValue(T object) {
+    ValidatableField<C> vfield = fieldMap.get(object);
+    if (vfield == null) {
+      C validatableValue = getValidatableValue(object);
+      vfield = new DefaultValidatableField<C>(validatableValue);
+      fieldMap.put(object, vfield);
+    }
+    
+    return vfield;
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableField.java b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableField.java
new file mode 100644
index 0000000..9b9d070
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableField.java
@@ -0,0 +1,81 @@
+/*
+ * 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.bikeshed.sample.validation.client;
+
+/**
+ * A field value with a pending future value and a valid flag.
+ *
+ * @param <T> the value type of the field
+ */
+public interface ValidatableField<T> {
+  T getPendingValue();
+  T getValue();
+  boolean isInvalid();
+  void setInvalid(boolean isInvalid);
+  void setPendingValue(T pendingValue);
+  void setValue(T value);
+
+  /**
+   * Default implementation of ValidatableField.
+   *
+   * @param <T> the value type of the field
+   */
+  public static class DefaultValidatableField<T> implements ValidatableField<T> {
+    static int genserial = 0;
+    int serial;
+    boolean isInvalid;
+    T pendingValue;
+    T value;
+
+    public DefaultValidatableField(T value) {
+      this.serial = genserial++;
+      this.value = value;
+    }
+
+    public DefaultValidatableField(ValidatableField<T> other) {
+      if (other instanceof DefaultValidatableField<?>) {
+        this.serial = ((DefaultValidatableField<T>) other).serial;
+      }
+      this.value = other.getValue();
+      this.pendingValue = other.getPendingValue();
+      this.isInvalid = other.isInvalid();
+    }
+
+    public T getPendingValue() {
+      return pendingValue;
+    }
+
+    public T getValue() {
+      return value;
+    }
+
+    public boolean isInvalid() {
+      return isInvalid;
+    }
+
+    public void setInvalid(boolean isInvalid) {
+      this.isInvalid = isInvalid;
+    }
+
+    public void setPendingValue(T pendingValue) {
+      this.pendingValue = pendingValue;
+    }
+
+    public void setValue(T value) {
+      this.value = value;
+    }
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableInputCell.java b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableInputCell.java
new file mode 100644
index 0000000..2babea5
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/ValidatableInputCell.java
@@ -0,0 +1,63 @@
+/*
+ * 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.bikeshed.sample.validation.client;
+
+import com.google.gwt.bikeshed.cells.client.Cell;
+import com.google.gwt.bikeshed.cells.client.ValueUpdater;
+import com.google.gwt.bikeshed.sample.validation.client.ValidatableField.DefaultValidatableField;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.dom.client.NativeEvent;
+
+/**
+ * A String Cell that supports validation.
+ */
+public class ValidatableInputCell extends Cell<ValidatableField<String>> {
+
+  @Override
+  public void onBrowserEvent(Element parent, ValidatableField<String> value, NativeEvent event,
+      ValueUpdater<ValidatableField<String>> valueUpdater) {
+    if (event.getType().equals("change")) {
+      InputElement input = parent.getFirstChild().cast();
+
+      // Mark as pending
+      input.getStyle().setColor("blue");
+
+      ValidatableField<String> field = new DefaultValidatableField<String>(value);
+      field.setPendingValue(input.getValue());
+      valueUpdater.update(field);
+    }
+  }
+
+  @Override
+  public void render(ValidatableField<String> value, StringBuilder sb) {
+    String pendingValue = value.getPendingValue();
+    sb.append("<input type=\"text\" value=\"");
+    boolean invalid = value.isInvalid();
+    if (pendingValue != null) {
+      sb.append(pendingValue);        
+    } else {
+      sb.append(value.getValue());
+    }
+    sb.append("\" style=\"color:");
+    if (pendingValue != null) {
+      sb.append(invalid ? "red" : "blue");
+    } else {
+      sb.append("black");
+    }
+    sb.append("\"></input>");
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/Validation.java b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/Validation.java
new file mode 100644
index 0000000..cfd1910
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/validation/client/Validation.java
@@ -0,0 +1,139 @@
+/*
+ * 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.bikeshed.sample.validation.client;
+
+import com.google.gwt.bikeshed.cells.client.FieldUpdater;
+import com.google.gwt.bikeshed.cells.client.TextCell;
+import com.google.gwt.bikeshed.list.client.Column;
+import com.google.gwt.bikeshed.list.client.PagingTableListView;
+import com.google.gwt.bikeshed.list.shared.ListListModel;
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.RootPanel;
+
+import java.util.List;
+
+/**
+ * Validation demo.
+ */
+public class Validation implements EntryPoint {
+  
+  static class Address {
+    static int genkey = 0;
+    int key;
+    String message;
+    String state;
+    String zip;
+    boolean zipInvalid;
+
+    public Address(Address address) {
+      this.key = address.key;
+      this.state = address.state;
+      this.zip = address.zip;
+    }
+    
+    public Address(String state, String zip) {
+      this.key = genkey++;
+      this.state = state;
+      this.zip = zip;
+    }
+    
+    @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof Address)) {
+        return false;
+      }
+      return ((Address) other).key == key;
+    }
+
+    @Override
+    public int hashCode() {
+      return key;
+    }
+  }
+  
+  public static boolean zipInvalid(int zip) {
+    return zip % 3 == 0;
+  }
+
+  public void onModuleLoad() {
+    ListListModel<Address> listModel = new ListListModel<Address>();
+    final List<Address> list = listModel.getList();
+    for (int i = 10; i < 50; i++) {
+      if (zipInvalid(30000 + i)) {
+        continue;
+      }
+        
+      String zip = "300" + i;
+      list.add(new Address("GA", zip));
+    }
+    
+    PagingTableListView<Address> table = new PagingTableListView<Address>(listModel, 10);
+    Column<Address, String> stateColumn = new Column<Address, String>(new TextCell()) {
+      @Override
+      protected String getValue(Address object) {
+        return object.state;
+      }
+    };
+    
+    ValidatableColumn<Address, String> zipColumn =
+      new ValidatableColumn<Address, String>(new ValidatableInputCell()) {
+        @Override
+        protected String getValidatableValue(Address object) {
+          return object.zip;
+        }
+    };
+    zipColumn.setFieldUpdater(new FieldUpdater<Address, ValidatableField<String>>() {
+      public void update(final int index, final Address object, final ValidatableField<String> value) {
+        // Perform validation after a 2-second delay
+        new Timer() {
+          @Override
+          public void run() {
+            String pendingValue = value.getPendingValue();
+            
+            int zip = Integer.parseInt(pendingValue);
+            boolean zipInvalid = Validation.zipInvalid(zip);
+
+            final Address newValue = new Address(object);
+            newValue.zip = pendingValue == null ? value.getValue() : pendingValue;
+            newValue.zipInvalid = zipInvalid;
+            
+            value.setInvalid(zipInvalid);
+            if (!zipInvalid) {
+              value.setValue(pendingValue);
+              value.setPendingValue(null);
+            }
+            
+            list.set(index, newValue);
+          }
+        }.schedule(2000);
+      }
+    });
+    
+    Column<Address, String> messageColumn = new Column<Address, String>(new TextCell()) {
+      @Override
+      protected String getValue(Address object) {
+        return object.zipInvalid ? "Please fix the zip code" : "";
+      }
+    };
+
+    table.addColumn(stateColumn);
+    table.addColumn(zipColumn);
+    table.addColumn(messageColumn);
+
+    RootPanel.get().add(table);
+  }
+}
diff --git a/bikeshed/war/Validation.html b/bikeshed/war/Validation.html
new file mode 100644
index 0000000..93317bf
--- /dev/null
+++ b/bikeshed/war/Validation.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+    <title>Validation</title>
+    <script type="text/javascript" language="javascript" src="validation/validation.nocache.js"></script>
+  </head>
+
+  <body>
+    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
+
+  </body>
+</html>