Added Duration class to supplant System.currentTimeMillis().

Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2336 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/Duration.java b/user/src/com/google/gwt/core/client/Duration.java
new file mode 100644
index 0000000..b7460d2
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/Duration.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008 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.core.client;
+
+/**
+ * A utility class for measuring elapsed time.
+ */
+public class Duration {
+
+  /**
+   * Returns the same result as {@link System#currentTimeMillis()}, but as a
+   * double. Because emulated long math is significantly slower than doubles in
+   * web mode, this method is to be preferred.
+   */
+  public static native double currentTimeMillis() /*-{
+    return (new Date()).getTime();
+  }-*/;
+
+  private static native int uncheckedConversion(double elapsed) /*-{
+    return elapsed;
+  }-*/;
+
+  private double start = currentTimeMillis();
+
+  /**
+   * Creates a new Duration whose start time is now.
+   */
+  public Duration() {
+  }
+
+  /**
+   * Returns the number of milliseconds that have elapsed since this object was
+   * created.
+   */
+  public int elapsedMillis() {
+    return uncheckedConversion(currentTimeMillis() - start);
+  }
+
+}
diff --git a/user/src/com/google/gwt/user/client/CommandExecutor.java b/user/src/com/google/gwt/user/client/CommandExecutor.java
index 6dc4a24..5b2a57f 100644
--- a/user/src/com/google/gwt/user/client/CommandExecutor.java
+++ b/user/src/com/google/gwt/user/client/CommandExecutor.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
 
@@ -130,7 +131,7 @@
     private void setLast(int last) {
       this.last = last;
     }
-    
+
     private boolean wasRemoved() {
       return last == -1;
     }
@@ -142,13 +143,13 @@
    * may need to acquire this value based on a rebind decision. For now, we
    * chose the smallest value known to cause an SSW.
    */
-  private static final long DEFAULT_CANCELLATION_TIMEOUT_MILLIS = 10000;
+  private static final int DEFAULT_CANCELLATION_TIMEOUT_MILLIS = 10000;
 
   /**
    * Default amount of time to spend dispatching commands before we yield to the
    * system.
    */
-  private static final long DEFAULT_TIME_SLICE_MILLIS = 100;
+  private static final int DEFAULT_TIME_SLICE_MILLIS = 100;
 
   /**
    * Returns true the end time has been reached or exceeded.
@@ -157,9 +158,9 @@
    * @param startTimeMillis end time in milliseconds
    * @return true if the end time has been reached
    */
-  private static boolean hasTimeSliceExpired(long currentTimeMillis,
-      long startTimeMillis) {
-    return Math.abs(currentTimeMillis - startTimeMillis) >= DEFAULT_TIME_SLICE_MILLIS;
+  private static boolean hasTimeSliceExpired(double currentTimeMillis,
+      double startTimeMillis) {
+    return currentTimeMillis - startTimeMillis >= DEFAULT_TIME_SLICE_MILLIS;
   }
 
   /**
@@ -202,7 +203,7 @@
 
       setExecutionTimerPending(false);
 
-      doExecuteCommands(System.currentTimeMillis());
+      doExecuteCommands(Duration.currentTimeMillis());
     }
   };
 
@@ -273,17 +274,18 @@
    * This method will dispatch commands from the command queue. It will dispatch
    * commands until one of the following conditions is <code>true</code>:
    * <ul>
-   * <li>It consumed its dispatching time slice {@value #DEFAULT_TIME_SLICE_MILLIS}</li>
+   * <li>It consumed its dispatching time slice
+   * {@value #DEFAULT_TIME_SLICE_MILLIS}</li>
    * <li>It encounters a <code>null</code> in the command queue</li>
    * <li>All commands which were present at the start of the dispatching have
    * been removed from the command queue</li>
-   * <li>The command that it was processing was canceled due to a false 
-   * cancellation -- in this case we exit without updating any state</li> 
+   * <li>The command that it was processing was canceled due to a false
+   * cancellation -- in this case we exit without updating any state</li>
    * </ul>
    * 
    * @param startTimeMillis the time when this method started
    */
-  protected void doExecuteCommands(long startTimeMillis) {
+  protected void doExecuteCommands(double startTimeMillis) {
     assert (!isExecutionTimerPending());
 
     boolean wasCanceled = false;
@@ -292,7 +294,7 @@
 
       iterator.setEnd(commands.size());
 
-      cancellationTimer.schedule((int) DEFAULT_CANCELLATION_TIMEOUT_MILLIS);
+      cancellationTimer.schedule(DEFAULT_CANCELLATION_TIMEOUT_MILLIS);
 
       while (iterator.hasNext()) {
         Object element = iterator.next();
@@ -316,18 +318,18 @@
           wasCanceled = iterator.wasRemoved();
           if (wasCanceled) {
             /*
-             * The iterator may have already had its remove method called, if
-             * it has, then we need to exit without updating any state 
+             * The iterator may have already had its remove method called, if it
+             * has, then we need to exit without updating any state
              */
             return;
           }
-          
+
           if (removeCommand) {
             iterator.remove();
           }
         }
 
-        if (hasTimeSliceExpired(System.currentTimeMillis(), startTimeMillis)) {
+        if (hasTimeSliceExpired(Duration.currentTimeMillis(), startTimeMillis)) {
           // the time slice has expired
           return;
         }
@@ -335,9 +337,9 @@
     } finally {
       if (!wasCanceled) {
         cancellationTimer.cancel();
-  
+
         setExecuting(false);
-  
+
         maybeStartExecutionTimer();
       }
     }
diff --git a/user/src/com/google/gwt/user/client/animation/Animation.java b/user/src/com/google/gwt/user/client/animation/Animation.java
index f4689b6..1f23b9f 100644
--- a/user/src/com/google/gwt/user/client/animation/Animation.java
+++ b/user/src/com/google/gwt/user/client/animation/Animation.java
@@ -15,11 +15,11 @@
  */
 package com.google.gwt.user.client.animation;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.Timer;
 
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
 /**
@@ -36,7 +36,7 @@
     protected void cancel(Animation anim) {
     }
 
-    protected void run(Animation anim, int duration, long startTime) {
+    protected void run(Animation anim, int duration, double startTime) {
       anim.onRunWhenDisabled();
     }
   }
@@ -76,7 +76,7 @@
     }
 
     @Override
-    protected void run(Animation anim, int duration, long startTime) {
+    protected void run(Animation anim, int duration, double startTime) {
       // Cancel the animation if it is running
       anim.cancel();
 
@@ -85,7 +85,7 @@
       anim.startTime = startTime;
 
       // Start synchronously if start time has passed
-      if (anim.update((new Date()).getTime())) {
+      if (anim.update(Duration.currentTimeMillis())) {
         return;
       }
 
@@ -112,7 +112,7 @@
      */
     private void updateAnimations() {
       // Iterator through the animations
-      long curTime = (new Date()).getTime();
+      double curTime = Duration.currentTimeMillis();
       for (int i = 0; i < animations.size(); i++) {
         Animation animation = animations.get(i);
         if (animation.update(curTime)) {
@@ -146,7 +146,7 @@
   /**
    * The start time of the {@link Animation}.
    */
-  private long startTime = -1;
+  private double startTime = -1;
 
   /**
    * Immediately cancel this animation.
@@ -187,7 +187,7 @@
    * @param duration the duration of the animation in milliseconds
    */
   public void run(int duration) {
-    run(duration, (new Date()).getTime());
+    run(duration, Duration.currentTimeMillis());
   }
 
   /**
@@ -198,7 +198,7 @@
    * @param duration the duration of the animation in milliseconds
    * @param startTime the synchronized start time in milliseconds
    */
-  public void run(int duration, long startTime) {
+  public void run(int duration, double startTime) {
     impl.run(this, duration, startTime);
   }
 
@@ -229,7 +229,7 @@
    * @param curTime the current time
    * @return true if the animation is complete, false if still running
    */
-  private boolean update(long curTime) {
+  private boolean update(double curTime) {
     // Start the animation
     if (!started && curTime >= startTime) {
       started = true;
diff --git a/user/src/com/google/gwt/user/client/animation/WidgetAnimation.java b/user/src/com/google/gwt/user/client/animation/WidgetAnimation.java
index e7dd29b..02dd464 100644
--- a/user/src/com/google/gwt/user/client/animation/WidgetAnimation.java
+++ b/user/src/com/google/gwt/user/client/animation/WidgetAnimation.java
@@ -33,7 +33,7 @@
     protected void cancel(WidgetAnimation anim) {
     }
 
-    protected void run(WidgetAnimation anim, int duration, long startTime) {
+    protected void run(WidgetAnimation anim, int duration, double startTime) {
       anim.onRunWhenDisabled();
     }
   }
@@ -49,7 +49,7 @@
     }
 
     @Override
-    protected void run(WidgetAnimation anim, int duration, long startTime) {
+    protected void run(WidgetAnimation anim, int duration, double startTime) {
       Animation.impl.run(anim, duration, startTime);
     }
   }
@@ -82,7 +82,7 @@
    * @param startTime the synchronized start time in milliseconds
    */
   @Override
-  public void run(int duration, long startTime) {
+  public void run(int duration, double startTime) {
     widgetImpl.run(this, duration, startTime);
   }
 
diff --git a/user/test/com/google/gwt/user/client/CommandExecutorTest.java b/user/test/com/google/gwt/user/client/CommandExecutorTest.java
index 57e8b17..1c891a3 100644
--- a/user/test/com/google/gwt/user/client/CommandExecutorTest.java
+++ b/user/test/com/google/gwt/user/client/CommandExecutorTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
 import com.google.gwt.junit.client.GWTTestCase;
@@ -121,7 +122,7 @@
         });
 
         delayTestFinish(TEST_FINISH_DELAY_MILLIS);
-        ce.doExecuteCommands(System.currentTimeMillis());
+        ce.doExecuteCommands(Duration.currentTimeMillis());
       }
     };
 
@@ -172,13 +173,13 @@
    * Test method for
    * {@link com.google.gwt.user.client.CommandExecutor#doExecuteCommands(int)}.
    * 
-   * Checks that calling {@link CommandExecutor#doExecuteCommands(long)} with no
+   * Checks that calling {@link CommandExecutor#doExecuteCommands(double)} with no
    * items in the queue is safe
    */
   public void testDoExecuteCommands_emptyQueue() {
     final CommandExecutor ce = new NonRestartingCommandExecutor();
 
-    ce.doExecuteCommands(System.currentTimeMillis());
+    ce.doExecuteCommands(Duration.currentTimeMillis());
   }
 
   /**
@@ -234,7 +235,7 @@
 
     tic.setDone(true);
     ce.submit(tic);
-    ce.doExecuteCommands(System.currentTimeMillis());
+    ce.doExecuteCommands(Duration.currentTimeMillis());
     assertTrue(tic.getExecuteCount() > 0);
     assertTrue(ce.getPendingCommands().isEmpty());
   }
@@ -255,7 +256,7 @@
     ce.submit((Command) null);
     ce.submit(tc2);
 
-    ce.doExecuteCommands(System.currentTimeMillis());
+    ce.doExecuteCommands(Duration.currentTimeMillis());
 
     assertTrue(tc1.didExecute() && !tc2.didExecute());
     assertEquals(1, ce.getPendingCommands().size());
@@ -277,7 +278,7 @@
 
     TestIncrementalCommand tic = new TestIncrementalCommand();
     ce.submit(tic);
-    ce.doExecuteCommands(System.currentTimeMillis());
+    ce.doExecuteCommands(Duration.currentTimeMillis());
 
     assertEquals(1, ce.getPendingCommands().size());
     assertTrue(ce.getPendingCommands().contains(tic));
diff --git a/user/test/com/google/gwt/user/client/Profile.java b/user/test/com/google/gwt/user/client/Profile.java
index abbbacb..8fafb13 100644
--- a/user/test/com/google/gwt/user/client/Profile.java
+++ b/user/test/com/google/gwt/user/client/Profile.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
@@ -29,27 +30,27 @@
   public static String REPORT_TO_WIKI = "Report to Wiki";
   private static String browser;
   private static String reportType = REPORT_TO_WIKI;
-  private static long time;
+  private static double time;
 
   public static void setReportType(String s) {
     reportType = s;
   }
 
   private void browserTiming(String s) {
-    long elapsed = System.currentTimeMillis() - time;
+    double elapsed = Duration.currentTimeMillis() - time;
     RootPanel.get().add(
         new Label("|" + browser + "|" + s + "|" + elapsed + " milliseconds|"));
   }
 
   protected void resetTimer() {
-    time = System.currentTimeMillis();
+    time = Duration.currentTimeMillis();
     if (browser == null) {
       browser = getBrowser();
     }
   }
 
   protected void timing(String s) {
-    long elapsed = System.currentTimeMillis() - time;
+    double elapsed = Duration.currentTimeMillis() - time;
     if (reportType == REPORT_TO_BROWSER) {
       browserTiming(s);
     } else if (reportType == REPORT_TO_WIKI) {
diff --git a/user/test/com/google/gwt/user/client/animation/AnimationTest.java b/user/test/com/google/gwt/user/client/animation/AnimationTest.java
index b45450f..ef6ddd4 100644
--- a/user/test/com/google/gwt/user/client/animation/AnimationTest.java
+++ b/user/test/com/google/gwt/user/client/animation/AnimationTest.java
@@ -15,11 +15,10 @@
  */
 package com.google.gwt.user.client.animation;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Timer;
 
-import java.util.Date;
-
 /**
  * Tests the {@link Animation} class.
  */
@@ -30,8 +29,8 @@
   private static class TestAnimation extends Animation {
     public boolean cancelled = false;
     public boolean completed = false;
-    public boolean started = false;
     public double curProgress = -1.0;
+    public boolean started = false;
 
     @Override
     public void onCancel() {
@@ -71,9 +70,9 @@
    */
   public void testCancelBeforeStarted() {
     final TestAnimation anim = new TestAnimation();
-    long curTime = (new Date()).getTime();
+    double curTime = Duration.currentTimeMillis();
     anim.run(100, curTime + 200);
-  
+
     // Check progress
     new Timer() {
       @Override
@@ -88,7 +87,7 @@
         anim.reset();
       }
     }.schedule(50);
-  
+
     // Check progress
     new Timer() {
       @Override
@@ -99,18 +98,18 @@
         finishTest();
       }
     }.schedule(100);
-  
+
     // Wait for test to finish
     delayTestFinish(150);
   }
-  
+
   /**
    * Test canceling an {@link Animation} after it completes.
    */
   public void testCancelWhenComplete() {
     final TestAnimation anim = new TestAnimation();
     anim.run(100);
-  
+
     // Check progress
     new Timer() {
       @Override
@@ -124,7 +123,7 @@
         anim.reset();
       }
     }.schedule(150);
-  
+
     // Check progress
     new Timer() {
       @Override
@@ -135,7 +134,7 @@
         finishTest();
       }
     }.schedule(200);
-  
+
     // Wait for test to finish
     delayTestFinish(250);
   }
@@ -183,22 +182,22 @@
     final TestAnimation animNow = new TestAnimation();
     final TestAnimation animPast = new TestAnimation();
     final TestAnimation animFuture = new TestAnimation();
-  
+
     // Run animations
-    long curTime = (new Date()).getTime();
+    double curTime = Duration.currentTimeMillis();
     animNow.run(0);
     animPast.run(0, curTime - 150);
     animFuture.run(0, curTime + 150);
-    
+
     // Test synchronous start
     assertTrue(animNow.started);
     assertTrue(animNow.completed);
     assertEquals(-1.0, animNow.curProgress);
-  
+
     assertTrue(animPast.started);
     assertTrue(animPast.completed);
     assertEquals(-1.0, animFuture.curProgress);
-  
+
     assertFalse(animFuture.started);
     assertFalse(animFuture.completed);
     assertEquals(-1.0, animFuture.curProgress);
@@ -211,13 +210,13 @@
     final TestAnimation animNow = new TestAnimation();
     final TestAnimation animPast = new TestAnimation();
     final TestAnimation animFuture = new TestAnimation();
-  
+
     // Run animations
-    long curTime = (new Date()).getTime();
+    double curTime = Duration.currentTimeMillis();
     animNow.run(300);
     animPast.run(300, curTime - 150);
     animFuture.run(300, curTime + 150);
-  
+
     // Test synchronous start
     assertTrue(animNow.started);
     assertFalse(animNow.completed);
@@ -230,7 +229,7 @@
     assertFalse(animFuture.started);
     assertFalse(animFuture.completed);
     assertEquals(-1.0, animFuture.curProgress);
-    
+
     // Check progress
     new Timer() {
       @Override
@@ -238,17 +237,17 @@
         assertTrue(animNow.started);
         assertFalse(animNow.completed);
         assertTrue(animNow.curProgress > 0.0 && animNow.curProgress <= 2.0);
-  
+
         assertTrue(animPast.started);
         assertFalse(animPast.completed);
         assertTrue(animPast.curProgress > 0.0 && animPast.curProgress <= 1.0);
-  
+
         assertFalse(animFuture.started);
         assertFalse(animFuture.completed);
         assertEquals(-1.0, animFuture.curProgress);
       }
     }.schedule(50);
-  
+
     // Check progress
     new Timer() {
       @Override
@@ -256,11 +255,11 @@
         assertTrue(animNow.started);
         assertTrue(animNow.completed);
         assertTrue(animNow.curProgress > 0.0 && animNow.curProgress <= 1.0);
-  
+
         assertTrue(animPast.started);
         assertTrue(animPast.completed);
         assertTrue(animPast.curProgress > 0.0 && animPast.curProgress <= 1.0);
-  
+
         assertTrue(animFuture.started);
         assertFalse(animFuture.completed);
         assertTrue(animFuture.curProgress > 0.0
@@ -268,7 +267,7 @@
         finishTest();
       }
     }.schedule(350);
-  
+
     // Wait for the test to finish
     delayTestFinish(500);
   }
diff --git a/user/test/com/google/gwt/user/client/animation/WidgetAnimationTest.java b/user/test/com/google/gwt/user/client/animation/WidgetAnimationTest.java
index 57ea73b..466a5fe 100644
--- a/user/test/com/google/gwt/user/client/animation/WidgetAnimationTest.java
+++ b/user/test/com/google/gwt/user/client/animation/WidgetAnimationTest.java
@@ -17,8 +17,6 @@
 
 import com.google.gwt.junit.client.GWTTestCase;
 
-import java.util.Date;
-
 /**
  * Tests the {@link WidgetAnimation} class.
  */
@@ -81,7 +79,6 @@
     TestWidgetAnimation anim = new TestWidgetAnimation();
 
     // Run animations
-    long curTime = (new Date()).getTime();
     anim.run(300);
 
     // Check the results