Allow DateBox to fire events with null date values for invalid and empty input
strings.

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10658 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
index 3d6b25b..3a6f2f1 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -229,7 +229,7 @@
     }
 
     public void onValueChange(ValueChangeEvent<Date> event) {
-      setValue(parseDate(false), event.getValue(), true);
+      setValue(parseDate(false), event.getValue(), true, true);
       hideDatePicker();
       preventDatePickerPopup();
       box.setFocus(true);
@@ -252,6 +252,7 @@
   private LeafValueEditor<Date> editor;
   private Format format;
   private boolean allowDPShow = true;
+  private boolean fireNullValues = false;
 
   /**
    * Create a date box with a new {@link DatePicker}.
@@ -325,6 +326,14 @@
   }
 
   /**
+   * Returns true iff the date box will fire {@code ValueChangeEvents} with a
+   * date value of {@code null} for invalid or empty string values.
+   */
+  public boolean getFireNullValues() {
+    return fireNullValues;
+  }
+  
+  /**
    * Gets the format instance used to control formatting and parsing of this
    * {@link DateBox}.
    *
@@ -396,6 +405,14 @@
   }
 
   /**
+   * Sets whether or not the date box will fire {@code ValueChangeEvents} with a
+   * date value of {@code null} for invalid or empty string values.
+   */
+  public void setFireNullValues(boolean fireNullValues) {
+    this.fireNullValues = fireNullValues;
+  }
+  
+  /**
    * Explicitly focus/unfocus this widget. Only one widget can have focus at a
    * time, and the widget that does will receive all keyboard events.
    *
@@ -448,7 +465,7 @@
   }
 
   public void setValue(Date date, boolean fireEvents) {
-    setValue(picker.getValue(), date, fireEvents);
+    setValue(picker.getValue(), date, fireEvents, true);
   }
 
   /**
@@ -480,14 +497,17 @@
     });
   }
 
-  private void setValue(Date oldDate, Date date, boolean fireEvents) {
+  private void setValue(Date oldDate, Date date, boolean fireEvents, boolean updateText) {
     if (date != null) {
       picker.setCurrentMonth(date);
     }
     picker.setValue(date, false);
-    format.reset(this, false);
-    box.setText(getFormat().format(this, date));
-
+    
+    if (updateText) {
+      format.reset(this, false);
+      box.setText(getFormat().format(this, date));
+    }
+    
     if (fireEvents) {
       DateChangeEvent.fireIfNotEqualDates(this, oldDate, date);
     }
@@ -495,8 +515,8 @@
 
   private void updateDateFromTextBox() {
     Date parsedDate = parseDate(true);
-    if (parsedDate != null) {
-      setValue(picker.getValue(), parsedDate, true);
+    if (fireNullValues || (parsedDate != null)) {
+      setValue(picker.getValue(), parsedDate, true, false);
     }
   }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/DateBoxTest.java b/user/test/com/google/gwt/user/client/ui/DateBoxTest.java
index cb37907..6838ad1 100644
--- a/user/test/com/google/gwt/user/client/ui/DateBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/DateBoxTest.java
@@ -17,10 +17,14 @@
 
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
 import com.google.gwt.user.datepicker.client.DateBox;
 
+import java.util.Date;
+
 /**
  * Tests {@link DateBox}.
  */
@@ -70,4 +74,40 @@
       }
     }.run();
   }
+  
+  @DoNotRunWith({Platform.HtmlUnitBug})
+  public void testFireNullValues() {
+    DateBox db = new DateBox();
+    db.setFireNullValues(true);
+    assertTrue(db.getFireNullValues());
+    RootPanel.get().add(db);
+    @SuppressWarnings("unchecked")
+    final ValueChangeEvent<Date>[] eventHolder = new ValueChangeEvent[1];
+    final boolean[] wasCalled = new boolean[1];
+    db.addValueChangeHandler(new ValueChangeHandler<Date>() {
+      @Override
+      public void onValueChange(ValueChangeEvent<Date> event) {
+        wasCalled[0] = true;
+        eventHolder[0] = event;
+      }
+    });
+
+    // test that an empty string fires an event
+    db.setValue(new Date(1976,1,20));
+    db.getTextBox().setText("");
+    NativeEvent e = Document.get().createBlurEvent();
+    db.getTextBox().getElement().dispatchEvent(e);
+    assertTrue(wasCalled[0]);
+    assertNull(eventHolder[0].getValue());
+    
+    // test that an invalid date string fires an event, and leaves the text in
+    // the textbox
+    db.setValue(new Date(1976,1,20));
+    db.getTextBox().setText("abcd");
+    e = Document.get().createBlurEvent();
+    db.getTextBox().getElement().dispatchEvent(e);
+    assertTrue(wasCalled[0]);
+    assertNull(eventHolder[0].getValue());
+    assertEquals("abcd", db.getTextBox().getText());
+  }
 }