| /* |
| ****************************************************************************** |
| * Copyright (C) 2007, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ****************************************************************************** |
| */ |
| |
| package com.ibm.icu.impl.duration; |
| |
| import com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit; |
| |
| /** |
| * Represents an approximate duration in multiple TimeUnits. Each unit, |
| * if set, has a count (which can be fractional and must be non-negative). |
| * In addition Period can either represent the duration as being into the past |
| * or future, and as being more or less than the defined value. |
| * <p> |
| * Use a PeriodFormatter to convert a Period to a String. |
| * <p> |
| * Periods are immutable. Mutating operations return the new |
| * result leaving the original unchanged. |
| * <p> |
| * Example:<pre> |
| * Period p1 = Period.at(3, WEEK).and(2, DAY).inFuture(); |
| * Period p2 = p1.and(12, HOUR);</pre> |
| */ |
| public final class Period { |
| final byte timeLimit; |
| final boolean inFuture; |
| final int[] counts; |
| |
| /** |
| * Constructs a Period representing a duration of |
| * count units extending into the past. |
| * @param count the number of units, must be non-negative |
| * @param unit the unit |
| * @return the new Period |
| */ |
| public static Period at(float count, TimeUnit unit) { |
| checkCount(count); |
| return new Period(ETimeLimit.NOLIMIT, false, count, unit); |
| } |
| |
| /** |
| * Constructs a Period representing a duration more than |
| * count units extending into the past. |
| * @param count the number of units. must be non-negative |
| * @param unit the unit |
| * @return the new Period |
| */ |
| public static Period moreThan(float count, TimeUnit unit) { |
| checkCount(count); |
| return new Period(ETimeLimit.MT, false, count, unit); |
| } |
| |
| /** |
| * Constructs a Period representing a duration |
| * less than count units extending into the past. |
| * @param count the number of units. must be non-negative |
| * @param unit the unit |
| * @return the new Period |
| */ |
| public static Period lessThan(float count, TimeUnit unit) { |
| checkCount(count); |
| return new Period(ETimeLimit.LT, false, count, unit); |
| } |
| |
| /** |
| * Set the given unit to have the given count. Marks the |
| * unit as having been set. This can be used to set |
| * multiple units, or to reset a unit to have a new count. |
| * This does <b>not</b> add the count to an existing count |
| * for this unit. |
| * |
| * @param count the number of units. must be non-negative |
| * @param unit the unit |
| * @return the new Period |
| */ |
| public Period and(float count, TimeUnit unit) { |
| checkCount(count); |
| return setTimeUnitValue(unit, count); |
| } |
| |
| /** |
| * Mark the given unit as not being set. |
| * |
| * @param unit the unit to unset |
| * @return the new Period |
| */ |
| public Period omit(TimeUnit unit) { |
| return setTimeUnitInternalValue(unit, 0); |
| } |
| |
| /** |
| * Mark the duration as being at the defined duration. |
| * |
| * @return the new Period |
| */ |
| public Period at() { |
| return setTimeLimit(ETimeLimit.NOLIMIT); |
| } |
| |
| /** |
| * Mark the duration as being more than the defined duration. |
| * |
| * @return the new Period |
| */ |
| public Period moreThan() { |
| return setTimeLimit(ETimeLimit.MT); |
| } |
| |
| /** |
| * Mark the duration as being less than the defined duration. |
| * |
| * @return the new Period |
| */ |
| public Period lessThan() { |
| return setTimeLimit(ETimeLimit.LT); |
| } |
| |
| /** |
| * Mark the time as being in the future. |
| * |
| * @return the new Period |
| */ |
| public Period inFuture() { |
| return setFuture(true); |
| } |
| |
| /** |
| * Mark the duration as extending into the past. |
| * |
| * @return the new Period |
| */ |
| public Period inPast() { |
| return setFuture(false); |
| } |
| |
| /** |
| * Mark the duration as extending into the future if |
| * future is true, and into the past otherwise. |
| * |
| * @param future true if the time is in the future |
| * @return the new Period |
| */ |
| public Period inFuture(boolean future) { |
| return setFuture(future); |
| } |
| |
| /** |
| * Mark the duration as extending into the past if |
| * past is true, and into the future otherwise. |
| * |
| * @param past true if the time is in the past |
| * @return the new Period |
| */ |
| public Period inPast(boolean past) { |
| return setFuture(!past); |
| } |
| |
| /** |
| * Returns true if any unit is set. |
| * @return true if any unit is set |
| */ |
| public boolean isSet() { |
| for (int i = 0; i < counts.length; ++i) { |
| if (counts[i] != 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns true if the given unit is set. |
| * @param unit the unit to test |
| * @return true if the given unit is set. |
| */ |
| public boolean isSet(TimeUnit unit) { |
| return counts[unit.ordinal] > 0; |
| } |
| |
| /** |
| * Returns the count for the specified unit. If the |
| * unit is not set, returns 0. |
| * @param unit the unit to test |
| * @return the count |
| */ |
| public float getCount(TimeUnit unit) { |
| int ord = unit.ordinal; |
| if (counts[ord] == 0) { |
| return 0; |
| } |
| return (counts[ord] - 1)/1000f; |
| } |
| |
| /** |
| * Returns true if this represents a |
| * duration into the future. |
| * @return true if this represents a |
| * duration into the future. |
| */ |
| public boolean isInFuture() { |
| return inFuture; |
| } |
| |
| /** |
| * Returns true if this represents a |
| * duration into the past |
| * @return true if this represents a |
| * duration into the past |
| */ |
| public boolean isInPast () { |
| return !inFuture; |
| } |
| |
| /** |
| * Returns true if this represents a duration in |
| * excess of the defined duration. |
| * @return true if this represents a duration in |
| * excess of the defined duration. |
| */ |
| public boolean isMoreThan() { |
| return timeLimit == ETimeLimit.MT; |
| } |
| |
| /** |
| * Returns true if this represents a duration |
| * less than the defined duration. |
| * @return true if this represents a duration |
| * less than the defined duration. |
| */ |
| public boolean isLessThan() { |
| return timeLimit == ETimeLimit.LT; |
| } |
| |
| /** |
| * Returns true if rhs extends Period and |
| * the two Periods are equal. |
| * @param rhs the object to compare to |
| * @return true if rhs is a Period and is equal to this |
| */ |
| public boolean equals(Object rhs) { |
| try { |
| return equals((Period)rhs); |
| } |
| catch (ClassCastException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Returns true if the same units are defined with |
| * the same counts, both extend into the future or both into the |
| * past, and if the limits (at, more than, less than) are the same. |
| * Note that this means that a period of 1000ms and a period of 1sec |
| * will not compare equal. |
| * |
| * @param rhs the period to compare to |
| * @return true if the two periods are equal |
| */ |
| public boolean equals(Period rhs) { |
| if (rhs != null && |
| this.timeLimit == rhs.timeLimit && |
| this.inFuture == rhs.inFuture) { |
| for (int i = 0; i < counts.length; ++i) { |
| if (counts[i] != rhs.counts[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the hashCode. |
| * @return the hashCode |
| */ |
| public int hashCode() { |
| int hc = (timeLimit << 1) | (inFuture ? 1 : 0); |
| for (int i = 0; i < counts.length; ++i) { |
| hc = (hc << 2) ^ counts[i]; |
| } |
| return hc; |
| } |
| |
| /** |
| * Private constructor used by static factory methods. |
| */ |
| private Period(int limit, boolean future, float count, TimeUnit unit) { |
| this.timeLimit = (byte) limit; |
| this.inFuture = future; |
| this.counts = new int[TimeUnit.units.length]; |
| this.counts[unit.ordinal] = (int)(count * 1000) + 1; |
| } |
| |
| /** |
| * Package private constructor used by setters and factory. |
| */ |
| Period(int timeLimit, boolean inFuture, int[] counts) { |
| this.timeLimit = (byte) timeLimit; |
| this.inFuture = inFuture; |
| this.counts = counts; |
| } |
| |
| /** |
| * Set the unit's internal value, converting from float to int. |
| */ |
| private Period setTimeUnitValue(TimeUnit unit, float value) { |
| if (value < 0) { |
| throw new IllegalArgumentException("value: " + value); |
| } |
| return setTimeUnitInternalValue(unit, (int)(value * 1000) + 1); |
| } |
| |
| /** |
| * Sets the period to have the provided value, 1/1000 of the |
| * unit plus 1. Thus unset values are '0', 1' is the set value '0', |
| * 2 is the set value '1/1000', 3 is the set value '2/1000' etc. |
| * @param p the period to change |
| * @param value the int value as described above. |
| * @eturn the new Period object. |
| */ |
| private Period setTimeUnitInternalValue(TimeUnit unit, int value) { |
| int ord = unit.ordinal; |
| if (counts[ord] != value) { |
| int[] newCounts = new int[counts.length]; |
| for (int i = 0; i < counts.length; ++i) { |
| newCounts[i] = counts[i]; |
| } |
| newCounts[ord] = value; |
| return new Period(timeLimit, inFuture, newCounts); |
| } |
| return this; |
| } |
| |
| /** |
| * Sets whether this defines a future time. |
| * @param future true if the time is in the future |
| * @return the new Period |
| */ |
| private Period setFuture(boolean future) { |
| if (this.inFuture != future) { |
| return new Period(timeLimit, future, counts); |
| } |
| return this; |
| } |
| |
| /** |
| * Sets whether this is more than, less than, or |
| * 'about' the specified time. |
| * @param limit the kind of limit |
| * @return the new Period |
| */ |
| private Period setTimeLimit(byte limit) { |
| if (this.timeLimit != limit) { |
| return new Period(limit, inFuture, counts); |
| |
| } |
| return this; |
| } |
| |
| /** |
| * Validate count. |
| */ |
| private static void checkCount(float count) { |
| if (count < 0) { |
| throw new IllegalArgumentException("count (" + count + |
| ") cannot be negative"); |
| } |
| } |
| } |