1 /*
   2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.util;
  27 
  28 import java.io.Serializable;
  29 import javafx.beans.NamedArg;
  30 
  31 /**
  32  * <p>
  33  *     A class that defines a duration of time. Duration instances are immutable,
  34  *     and are therefore replaced rather than modified, similar to {@link java.math.BigDecimal}.
  35  *     Durations can be created using the constructor, or one of the static construction
  36  *     methods such as {@link #seconds} or {@link #minutes}.
  37  * </p>
  38  * @since JavaFX 2.0
  39  */
  40 public class Duration implements Comparable<Duration>, Serializable {
  41     /**
  42      * A Duration of 0 (no time).
  43      */
  44     public static final Duration ZERO = new Duration(0);
  45 
  46     /**
  47      * A Duration of 1 millisecond.
  48      */
  49     public static final Duration ONE = new Duration(1);
  50 
  51     /**
  52      * An Infinite Duration.
  53      */
  54     public static final Duration INDEFINITE = new Duration(Double.POSITIVE_INFINITY);
  55 
  56     /**
  57      * A Duration of some unknown amount of time.
  58      */
  59     public static final Duration UNKNOWN = new Duration(Double.NaN);
  60 
  61     /**
  62      * Factory method that returns a Duration instance for a specified
  63      * amount of time. The syntax is "[number][ms|s|m|h]".
  64      *
  65      * @param time A non-null string properly formatted. Leading or trailing
  66      * spaces will not parse correctly. Throws a NullPointerException if
  67      * time is null.
  68      * @return a Duration which is represented by the <code>time</code>
  69      */
  70     public static Duration valueOf(String time) {
  71         int index = -1;
  72         for (int i=0; i<time.length(); i++) {
  73             char c = time.charAt(i);
  74             if (!Character.isDigit(c) && c != '.' && c != '-') {
  75                 index = i;
  76                 break;
  77             }
  78         }
  79 
  80         if (index == -1) {
  81             // Never found the suffix!
  82             throw new IllegalArgumentException("The time parameter must have a suffix of [ms|s|m|h]");
  83         }
  84 
  85         double value = Double.parseDouble(time.substring(0, index));
  86         String suffix = time.substring(index);
  87         if ("ms".equals(suffix)) {
  88             return millis(value);
  89         } else if ("s".equals(suffix)) {
  90             return seconds(value);
  91         } else if ("m".equals(suffix)) {
  92             return minutes(value);
  93         } else if ("h".equals(suffix)) {
  94             return hours(value);
  95         } else {
  96             // Malformed suffix
  97             throw new IllegalArgumentException("The time parameter must have a suffix of [ms|s|m|h]");
  98         }
  99     }
 100 
 101     /**
 102      * Factory method that returns a Duration instance for a specified
 103      * number of milliseconds.
 104      *
 105      * @param ms the number of milliseconds
 106      * @return a Duration instance of the specified number of milliseconds
 107      */
 108     public static Duration millis(double ms) {
 109         if (ms == 0) {
 110             return ZERO;
 111         } else if (ms == 1) {
 112             return ONE;
 113         } else if (ms == Double.POSITIVE_INFINITY) {
 114             return INDEFINITE;
 115         } else if (Double.isNaN(ms)) {
 116             return UNKNOWN;
 117         } else {
 118             return new Duration(ms);
 119         }
 120     }
 121 
 122     /**
 123      * Factory method that returns a Duration instance representing the specified
 124      * number of seconds.
 125      *
 126      * @param s the number of seconds
 127      * @return a Duration instance of the specified number of seconds
 128      */
 129     public static Duration seconds(double s) {
 130         if (s == 0) {
 131             return ZERO;
 132         } else if (s == Double.POSITIVE_INFINITY) {
 133             return INDEFINITE;
 134         } else if (Double.isNaN(s)) {
 135             return UNKNOWN;
 136         } else {
 137             return new Duration(s * 1000.0);
 138         }
 139     }
 140 
 141     /**
 142      * Factory method that returns a Duration instance representing the specified
 143      * number of minutes.
 144      *
 145      * @param m the number of minutes
 146      * @return a Duration instance of the specified number of minutes
 147      */
 148     public static Duration minutes(double m) {
 149         if (m == 0) {
 150             return ZERO;
 151         } else if (m == Double.POSITIVE_INFINITY) {
 152             return INDEFINITE;
 153         } else if (Double.isNaN(m)) {
 154             return UNKNOWN;
 155         } else {
 156             return new Duration(m * (1000.0 * 60.0));
 157         }
 158     }
 159 
 160     /**
 161      * Factory method that returns a Duration instance representing the specified
 162      * number of hours.
 163      *
 164      * @param h the number of hours
 165      * @return a Duration instance representing the specified number of hours
 166      */
 167     public static Duration hours(double h) {
 168         if (h == 0) {
 169             return ZERO;
 170         } else if (h == Double.POSITIVE_INFINITY) {
 171             return INDEFINITE;
 172         } else if (Double.isNaN(h)) {
 173             return UNKNOWN;
 174         } else {
 175             return new Duration(h * (1000.0 * 60.0 * 60.0));
 176         }
 177     }
 178 
 179     /**
 180      * The value of this duration, in fractional milliseconds
 181      */
 182     private final double millis;
 183 
 184     /**
 185      * Creates a new Duration with potentially fractional millisecond resolution.
 186      * @param millis The number of milliseconds
 187      */
 188     public Duration(@NamedArg("millis") double millis) {
 189         this.millis = millis;
 190     }
 191 
 192     /**
 193      * Returns the number of milliseconds in this period or Double.POSITIVE_INFINITY
 194      * if the period is INDEFINITE or NaN if the period is UNKNOWN.
 195      * @return the Duration in fractional milliseconds
 196      */
 197     public double toMillis() {
 198         return millis;
 199     }
 200 
 201     /**
 202      * Returns the number of seconds in this period or Double.POSITIVE_INFINITY
 203      * if the period is INDEFINITE or NaN if the period is UNKNOWN.
 204      * @return the Duration in fractional seconds
 205      */
 206     public double toSeconds() {
 207         return millis / 1000.0;
 208     }
 209 
 210     /**
 211      * Returns the number of minutes in this period or Double.POSITIVE_INFINITY
 212      * if the period is INDEFINITE or NaN if the period is UNKNOWN.
 213      * @return the Duration in fractional minutes
 214      */
 215     public double toMinutes() {
 216         return millis / (60 * 1000.0);
 217     }
 218 
 219     /**
 220      * Returns the number of hours in this period or Double.POSITIVE_INFINITY
 221      * if the period is INDEFINITE or NaN if the period is UNKNOWN.
 222      * @return the Duration in fractional hours
 223      */
 224     public double toHours() {
 225         return millis / (60 * 60 * 1000.0);
 226     }
 227 
 228     /**
 229      * Add this instance and another Duration instance to return a new Duration instance.
 230      * If either instance is INDEFINITE, return INDEFINITE.
 231      * If either instance is UNKNOWN, return UNKNOWN.
 232      * This method does not change the value of the called Duration instance.
 233      *
 234      * @param other must not be null
 235      * @return the result of adding this duration to the other duration. This is
 236      *         the same as millis + other.millis using double arithmetic
 237      */
 238     public Duration add(Duration other) {
 239         // Note that several of these functions assume that the value of millis in INDEFINITE
 240         // is Double.POSITIVE_INFINITY.
 241         return millis(millis + other.millis);
 242     }
 243 
 244     /**
 245      * Subtract other Duration instance from this instance to return a new Duration instance.
 246      * If either instance is UNKNOWN, return UNKNOWN.
 247      * Otherwise, if either instance is INDEFINITE, return INDEFINITE.
 248      * This method does not change the value of the called Duration instance.
 249      *
 250      * @param other must not be null
 251      * @return the result of subtracting the other duration from this duration. This is
 252      *         the same as millis - other.millis using double arithmetic
 253      */
 254     public Duration subtract(Duration other) {
 255         return millis(millis - other.millis);
 256     }
 257 
 258     /**
 259      * Multiply this instance with a number to return a new Duration instance.
 260      * If either instance is INDEFINITE, return INDEFINITE.
 261      * If either Duration instance is UNKNOWN, return UNKNOWN.
 262      * This method does not change the value of the called Duration instance.
 263      *
 264      * @deprecated This method produces surprising results by not taking units into
 265      *             account. Use {@link #multiply(double)} instead.
 266      * @param other must not be null
 267      * @return the result of multiplying this duration with the other duration. This is
 268      *         the same as millis * other.millis using double arithmetic
 269      */
 270     @Deprecated
 271     public Duration multiply(Duration other) {
 272         return millis(millis * other.millis);
 273     }
 274 
 275     /**
 276      * Multiply this instance with a number representing millis and return a new Duration.
 277      * If the called Duration instance is INDEFINITE, return INDEFINITE.
 278      * If the called Duration instance is UNKNOWN, return UNKNOWN.
 279      * This method does not change the value of the called Duration instance.
 280      *
 281      * @param n the amount to multiply by in fractional milliseconds
 282      * @return the result of multiplying this duration with n. This is
 283      *         the same as millis * n using double arithmetic
 284      */
 285     public Duration multiply(double n) {
 286         return millis(millis * n);
 287     }
 288 
 289     /**
 290      * Divide this instance by a number to return a new Duration instance.
 291      * If the called Duration instance is INDEFINITE, return INDEFINITE.
 292      * If the called Duration instance is UNKNOWN, return UNKNOWN.
 293      * This method does not change the value of the called Duration instance.
 294      *
 295      * @param n the amount to divide by in fractional milliseconds
 296      * @return the result of dividing this duration with n. This is
 297      *         the same as millis / n using double arithmetic
 298      */
 299     public Duration divide(double n) {
 300         return millis(millis / n);
 301     }
 302 
 303     /**
 304      * Divide this instance by another Duration to return the ratio.
 305      * If both instances are INDEFINITE, return NaN.
 306      * If this instance is INDEFINITE, return POSITIVE_INFINITY
 307      * If the other instance is INDEFINITE, return 0.0.
 308      * This function does not change the value of the called Duration instance.
 309      *
 310      * @deprecated This method produces surprising results by not taking units into
 311      *             account. Use {@link #divide(double)} instead.
 312      * @param other must not be null
 313      * @return the result of dividing this duration by the other duration. This is
 314      *         the same as millis / other.millis using double arithmetic
 315      */
 316     @Deprecated
 317     public Duration divide(Duration other) {
 318         return millis(millis / other.millis);
 319     }
 320 
 321     /**
 322      * Return a new Duration instance which has a negative number of milliseconds
 323      * from this instance.  For example, <code>Duration.millis(50).negate()</code> returns
 324      * a Duration of -50 milliseconds.
 325      * If the called Duration instance is INDEFINITE, return INDEFINITE.
 326      * This function does not change the value of the called Duration instance.
 327      *
 328      * @return the result of negating this duration. This is
 329      *         the same as -millis using double arithmetic
 330      */
 331     public Duration negate() {
 332         return millis(-millis);
 333     }
 334 
 335     /**
 336      * Gets whether this Duration instance is Indefinite. A Duration is Indefinite
 337      * if it equals Duration.INDEFINITE.
 338      * @return true if this Duration is equivalent to Duration.INDEFINITE or Double.POSITIVE_INFINITY.
 339      */
 340     public boolean isIndefinite() {
 341         return millis == Double.POSITIVE_INFINITY;
 342     }
 343 
 344     /**
 345      * Gets whether this Duration instance is Unknown. A Duration is Unknown
 346      * if it equals Duration.UNKNOWN.
 347      * @return true if this Duration is equivalent to Duration.UNKNOWN or Double.isNaN(millis)
 348      */
 349     public boolean isUnknown() {
 350         return Double.isNaN(millis);
 351     }
 352 
 353     /**
 354      * Returns true if the specified duration is less than (&lt;) this instance.
 355      * INDEFINITE is treated as if it were positive infinity.
 356      *
 357      * @param other cannot be null
 358      * @return true if millis &lt; other.millis using double arithmetic
 359      */
 360     public boolean lessThan(Duration other) {
 361         return millis < other.millis;
 362     }
 363 
 364     /**
 365      * Returns true if the specified duration is less than or equal to (&lt;=) this instance.
 366      * INDEFINITE is treated as if it were positive infinity.
 367      *
 368      * @param other cannot be null
 369      * @return true if millis &lt;= other.millis using double arithmetic
 370      */
 371     public boolean lessThanOrEqualTo(Duration other) {
 372         return millis <= other.millis;
 373     }
 374 
 375     /**
 376      * Returns true if the specified duration is greater than (&gt;) this instance.
 377      * INDEFINITE is treated as if it were positive infinity.
 378      *
 379      * @param other cannot be null
 380      * @return true if millis &gt; other.millis using double arithmetic
 381      */
 382     public boolean greaterThan(Duration other) {
 383         return millis > other.millis;
 384     }
 385 
 386     /**
 387      * Returns true if the specified duration is greater than or equal to (&gt;=) this instance.
 388      * INDEFINITE is treated as if it were positive infinity.
 389      *
 390      * @param other cannot be null
 391      * @return true if millis &gt;= other.millis using double arithmetic
 392      */
 393     public boolean greaterThanOrEqualTo(Duration other) {
 394         return millis >= other.millis;
 395     }
 396 
 397     /**
 398      * Returns a string representation of this {@code Duration} object.
 399      * @return a string representation of this {@code Duration} object.
 400      */
 401     @Override public String toString() {
 402         return isIndefinite() ? "INDEFINITE" : (isUnknown() ? "UNKNOWN" : millis + " ms");
 403     }
 404 
 405     /**
 406      * Compares durations represented by this object and the specified object.
 407      * Returns a negative integer, zero, or a positive integer as this duration
 408      * is less than, equal to, or greater than the specified duration.
 409      * @param d the duration to be compared.
 410      * @return a negative integer, zero, or a positive integer as this duration
 411      * is less than, equal to, or greater than the specified duration.
 412      */
 413     @Override public int compareTo(Duration d) {
 414         // Reuse the Double.compare implementation
 415         return Double.compare(millis, d.millis);
 416     }
 417 
 418     /**
 419      * Indicates whether some other object is "equal to" this one.
 420      * @param obj the reference object with which to compare.
 421      * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
 422      */
 423     @Override public boolean equals(Object obj) {
 424         // Rely on Java's handling of double == double
 425         return obj == this || obj instanceof Duration && millis == ((Duration) obj).millis;
 426     }
 427 
 428     /**
 429      * Returns a hash code for this {@code Duration} object.
 430      * @return a hash code for this {@code Duration} object.
 431      */
 432     @Override public int hashCode() {
 433         // Uses the same implementation as Double.hashCode
 434         long bits = Double.doubleToLongBits(millis);
 435         return (int)(bits ^ (bits >>> 32));
 436     }
 437 }