View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.util;
29  
30  import java.text.NumberFormat;
31  import java.text.ParseException;
32  import java.util.Locale;
33  import java.util.concurrent.TimeUnit;
34  
35  import org.apache.hc.core5.annotation.Contract;
36  import org.apache.hc.core5.annotation.ThreadingBehavior;
37  
38  /**
39   * Represents a time value as a {@code long} time and a {@link TimeUnit}.
40   *
41   * @since 5.0
42   */
43  @Contract(threading = ThreadingBehavior.IMMUTABLE)
44  public class TimeValue {
45  
46      static final int INT_UNDEFINED = -1;
47  
48      /**
49       * A constant holding the maximum value a {@code TimeValue} can have: <code>Long.MAX_VALUE</code> days.
50       */
51      public static final TimeValue MAX_VALUE = ofDays(Long.MAX_VALUE);
52  
53      /**
54       * A negative one millisecond {@link TimeValue}.
55       */
56      public static final TimeValue NEG_ONE_MILLISECONDS = TimeValue.of(INT_UNDEFINED, TimeUnit.MILLISECONDS);
57  
58      /**
59       * A negative one second {@link TimeValue}.
60       */
61      public static final TimeValue NEG_ONE_SECONDS = TimeValue.of(INT_UNDEFINED, TimeUnit.SECONDS);
62  
63      /**
64       * A zero milliseconds {@link TimeValue}.
65       */
66      public static final TimeValue ZERO_MILLISECONDS = TimeValue.of(0, TimeUnit.MILLISECONDS);
67  
68      /**
69       * Returns the given {@code long} value as an {@code int} where long values out of int range are returned as
70       * {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
71       *
72       * <p>
73       * For example: {@code TimeValue.asBoundInt(Long.MAX_VALUE)} returns {@code Integer.MAX_VALUE}.
74       * </p>
75       *
76       * @param value a long value to convert
77       * @return an int value bound within {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
78       */
79      public static int asBoundInt(final long value) {
80          if (value > Integer.MAX_VALUE) {
81              return Integer.MAX_VALUE;
82          } else if (value < Integer.MIN_VALUE) {
83              return Integer.MIN_VALUE;
84          }
85          return (int) value;
86      }
87  
88      /**
89       * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns the given
90       * {@code defaultValue}.
91       *
92       * @param timeValue may be {@code null}
93       * @param defaultValue may be {@code null}
94       * @return {@code timeValue} or {@code defaultValue}
95       */
96      public static <T extends TimeValue> T defaultsTo(final T timeValue, final T defaultValue) {
97          return timeValue != null ? timeValue : defaultValue;
98      }
99  
100     /**
101      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
102      * {@link #NEG_ONE_SECONDS}.
103      *
104      * @param timeValue may be {@code null}
105      * @return {@code timeValue} or {@link #NEG_ONE_SECONDS}
106      */
107     public static TimeValueeValue defaultsToNegativeOneMillisecond(final TimeValue timeValue) {
108         return defaultsTo(timeValue, NEG_ONE_MILLISECONDS);
109     }
110 
111     /**
112      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
113      * {@link #NEG_ONE_SECONDS}.
114      *
115      * @param timeValue may be {@code null}
116      * @return {@code timeValue} or {@link #NEG_ONE_SECONDS}
117      */
118     public static TimeValue">TimeValue defaultsToNegativeOneSecond(final TimeValue timeValue) {
119         return defaultsTo(timeValue, NEG_ONE_SECONDS);
120     }
121 
122     /**
123      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
124      * {@link #ZERO_MILLISECONDS}.
125      *
126      * @param timeValue may be {@code null}
127      * @return {@code timeValue} or {@link #ZERO_MILLISECONDS}
128      */
129     public static TimeValuemeValue">TimeValue defaultsToZeroMillis(final TimeValue timeValue) {
130         return defaultsTo(timeValue, ZERO_MILLISECONDS);
131     }
132 
133     public static boolean isNonNegative(final TimeValue timeValue) {
134         return timeValue != null && timeValue.getDuration() >= 0;
135     }
136 
137     public static boolean isPositive(final TimeValue timeValue) {
138         return timeValue != null && timeValue.getDuration() > 0;
139     }
140 
141     /**
142      * Creates a TimeValue.
143      *
144      * @param duration the time duration in the given {@code timeUnit}.
145      * @param timeUnit the time unit for the given durarion.
146      * @return a Timeout
147      */
148     public static TimeValue of(final long duration, final TimeUnit timeUnit) {
149         return new TimeValue(duration, timeUnit);
150     }
151 
152     public static TimeValue ofDays(final long days) {
153         return of(days, TimeUnit.DAYS);
154     }
155 
156     public static TimeValue ofHours(final long hours) {
157         return of(hours, TimeUnit.HOURS);
158     }
159 
160     public static TimeValue ofMicroseconds(final long microseconds) {
161         return of(microseconds, TimeUnit.MICROSECONDS);
162     }
163 
164     /**
165      * @deprecated Use {@link #ofMilliseconds(long)}.
166      */
167     @Deprecated
168     public static TimeValue ofMillis(final long millis) {
169         return of(millis, TimeUnit.MILLISECONDS);
170     }
171 
172     public static TimeValue ofMilliseconds(final long millis) {
173         return of(millis, TimeUnit.MILLISECONDS);
174     }
175 
176     public static TimeValue ofMinutes(final long minutes) {
177         return of(minutes, TimeUnit.MINUTES);
178     }
179 
180     public static TimeValue ofNanoseconds(final long nanoseconds) {
181         return of(nanoseconds, TimeUnit.NANOSECONDS);
182     }
183 
184     public static TimeValue ofSeconds(final long seconds) {
185         return of(seconds, TimeUnit.SECONDS);
186     }
187 
188     /**
189      * Parses a TimeValue in the format {@code <Long><SPACE><TimeUnit>}, for example {@code "1,200 MILLISECONDS"}.
190      * <p>
191      * Parses:
192      * </p>
193      * <ul>
194      * <li>{@code "1,200 MILLISECONDS"} Note the comma.</li>
195      * <li>{@code "1200 MILLISECONDS"} Without a comma.</li>
196      * <li>{@code " 1,200 MILLISECONDS "} Spaces are ignored.</li>
197      * <li></li>
198      * </ul>
199      *
200      *
201      * @param value the TimeValue to parse
202      * @return a new TimeValue
203      * @throws ParseException if the number cannot be parsed
204      */
205     public static TimeValue parse(final String value) throws ParseException {
206         final Locale locale = Locale.ROOT;
207         final String split[] = value.trim().split("\\s+");
208         if (split.length < 2) {
209             throw new IllegalArgumentException(String.format("Expected format for <Long><SPACE><TimeUnit>: ", value));
210         }
211         final String clean0 = split[0].trim();
212         final String clean1 = split[1].trim().toUpperCase(Locale.ROOT);
213         final String timeUnitStr = clean1.endsWith("S") ? clean1 : clean1 + "S";
214         return TimeValue.of(NumberFormat.getInstance(locale).parse(clean0).longValue(), TimeUnit.valueOf(timeUnitStr));
215     }
216 
217     private final long duration;
218 
219     private final TimeUnit timeUnit;
220 
221     TimeValue(final long duration, final TimeUnit timeUnit) {
222         super();
223         this.duration = duration;
224         this.timeUnit = Args.notNull(timeUnit, "timeUnit");
225     }
226 
227     public long convert(final TimeUnit targetTimeUnit) {
228         return targetTimeUnit.convert(duration, timeUnit);
229     }
230 
231     @Override
232     public boolean equals(final Object obj) {
233         if (this == obj) {
234             return true;
235         }
236         if (obj instanceof TimeValue) {
237             final TimeValue./../../../../org/apache/hc/core5/util/TimeValue.html#TimeValue">TimeValue that = (TimeValue) obj;
238             return this.duration == that.duration && LangUtils.equals(this.timeUnit, that.timeUnit);
239         }
240         return false;
241     }
242 
243     /**
244      * Returns a TimeValue whose value is {@code (this / divisor)}.
245      *
246      * @param divisor
247      *            value by which this TimeValue is to be divided.
248      * @return {@code this / divisor}
249      * @throws ArithmeticException
250      *             if {@code divisor} is zero.
251      */
252     public TimeValue divide(final long divisor) {
253         final long newDuration = duration / divisor;
254         return of(newDuration, timeUnit);
255     }
256 
257     /**
258      * Returns a TimeValue whose value is {@code (this / divisor)}.
259      *
260      * @param divisor
261      *            value by which this TimeValue is to be divided.
262      * @param targetTimeUnit
263      *            the target TimeUnit
264      * @return {@code this / divisor}
265      * @throws ArithmeticException
266      *             if {@code divisor} is zero.
267      */
268     public TimeValue divide(final long divisor, final TimeUnit targetTimeUnit) {
269         return of(convert(targetTimeUnit) / divisor, targetTimeUnit);
270     }
271 
272     public long getDuration() {
273         return duration;
274     }
275 
276     public TimeUnit getTimeUnit() {
277         return timeUnit;
278     }
279 
280     @Override
281     public int hashCode() {
282         int hash = LangUtils.HASH_SEED;
283         hash = LangUtils.hashCode(hash, duration);
284         hash = LangUtils.hashCode(hash, timeUnit);
285         return hash;
286     }
287 
288     public TimeValueTimeValue.html#TimeValue">TimeValue min(final TimeValue other) {
289         return isGreaterThan(other) ? other : this;
290     }
291 
292     private boolean isGreaterThan(final TimeValue other) {
293         final TimeUnit targetTimeUnit = min(other.getTimeUnit());
294         return convert(targetTimeUnit) > other.convert(targetTimeUnit);
295     }
296 
297     private TimeUnit min(final TimeUnit other) {
298         return scale() > scale(other) ? other : getTimeUnit();
299     }
300 
301     private int scale() {
302         return scale(timeUnit);
303     }
304 
305     /**
306      * Returns a made up scale for TimeUnits.
307      *
308      * @param tUnit
309      *            a TimeUnit
310      * @return a number from 1 to 7, where 1 is NANOSECONDS and 7 DAYS.
311      */
312     private int scale(final TimeUnit tUnit) {
313         switch (tUnit) {
314         case NANOSECONDS:
315             return 1;
316         case MICROSECONDS:
317             return 2;
318         case MILLISECONDS:
319             return 3;
320         case SECONDS:
321             return 4;
322         case MINUTES:
323             return 5;
324         case HOURS:
325             return 6;
326         case DAYS:
327             return 7;
328         default:
329             // Should never happens unless Java adds to the enum.
330             throw new IllegalStateException();
331         }
332     }
333 
334     public void sleep() throws InterruptedException {
335         timeUnit.sleep(duration);
336     }
337 
338     public void timedJoin(final Thread thread) throws InterruptedException {
339         timeUnit.timedJoin(thread, duration);
340     }
341 
342     public void timedWait(final Object obj) throws InterruptedException {
343         timeUnit.timedWait(obj, duration);
344     }
345 
346     public long toDays() {
347         return timeUnit.toDays(duration);
348     }
349 
350     public long toHours() {
351         return timeUnit.toHours(duration);
352     }
353 
354     public long toMicros() {
355         return timeUnit.toMicros(duration);
356     }
357 
358     public long toMillis() {
359         return timeUnit.toMillis(duration);
360     }
361 
362     public int toMillisIntBound() {
363         return asBoundInt(toMillis());
364     }
365 
366     public long toMinutes() {
367         return timeUnit.toMinutes(duration);
368     }
369 
370     public long toNanos() {
371         return timeUnit.toNanos(duration);
372     }
373 
374     public long toSeconds() {
375         return timeUnit.toSeconds(duration);
376     }
377 
378     public int toSecondsIntBound() {
379         return asBoundInt(toSeconds());
380     }
381 
382     @Override
383     public String toString() {
384         return String.format(Locale.ROOT, "%,d %s", duration, timeUnit);
385     }
386 
387     public Timeout toTimeout() {
388         return Timeout.of(duration, timeUnit);
389     }
390 
391 }