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     public static TimeValue ofMilliseconds(final long millis) {
165         return of(millis, TimeUnit.MILLISECONDS);
166     }
167 
168     public static TimeValue ofMinutes(final long minutes) {
169         return of(minutes, TimeUnit.MINUTES);
170     }
171 
172     public static TimeValue ofNanoseconds(final long nanoseconds) {
173         return of(nanoseconds, TimeUnit.NANOSECONDS);
174     }
175 
176     public static TimeValue ofSeconds(final long seconds) {
177         return of(seconds, TimeUnit.SECONDS);
178     }
179 
180     /**
181      * Parses a TimeValue in the format {@code <Long><SPACE><TimeUnit>}, for example {@code "1,200 MILLISECONDS"}.
182      * <p>
183      * Parses:
184      * </p>
185      * <ul>
186      * <li>{@code "1,200 MILLISECONDS"} Note the comma.</li>
187      * <li>{@code "1200 MILLISECONDS"} Without a comma.</li>
188      * <li>{@code " 1,200 MILLISECONDS "} Spaces are ignored.</li>
189      * <li></li>
190      * </ul>
191      *
192      *
193      * @param value the TimeValue to parse
194      * @return a new TimeValue
195      * @throws ParseException if the number cannot be parsed
196      */
197     public static TimeValue parse(final String value) throws ParseException {
198         final Locale locale = Locale.ROOT;
199         final String split[] = value.trim().split("\\s+");
200         if (split.length < 2) {
201             throw new IllegalArgumentException(String.format("Expected format for <Long><SPACE><TimeUnit>: ", value));
202         }
203         final String clean0 = split[0].trim();
204         final String clean1 = split[1].trim().toUpperCase(Locale.ROOT);
205         final String timeUnitStr = clean1.endsWith("S") ? clean1 : clean1 + "S";
206         return TimeValue.of(NumberFormat.getInstance(locale).parse(clean0).longValue(), TimeUnit.valueOf(timeUnitStr));
207     }
208 
209     private final long duration;
210 
211     private final TimeUnit timeUnit;
212 
213     TimeValue(final long duration, final TimeUnit timeUnit) {
214         super();
215         this.duration = duration;
216         this.timeUnit = Args.notNull(timeUnit, "timeUnit");
217     }
218 
219     public long convert(final TimeUnit targetTimeUnit) {
220         return targetTimeUnit.convert(duration, timeUnit);
221     }
222 
223     @Override
224     public boolean equals(final Object obj) {
225         if (this == obj) {
226             return true;
227         }
228         if (obj instanceof TimeValue) {
229             final TimeValue./../../../../org/apache/hc/core5/util/TimeValue.html#TimeValue">TimeValue that = (TimeValue) obj;
230             return this.duration == that.duration && LangUtils.equals(this.timeUnit, that.timeUnit);
231         }
232         return false;
233     }
234 
235     /**
236      * Returns a TimeValue whose value is {@code (this / divisor)}.
237      *
238      * @param divisor
239      *            value by which this TimeValue is to be divided.
240      * @return {@code this / divisor}
241      * @throws ArithmeticException
242      *             if {@code divisor} is zero.
243      */
244     public TimeValue divide(final long divisor) {
245         final long newDuration = duration / divisor;
246         return of(newDuration, timeUnit);
247     }
248 
249     /**
250      * Returns a TimeValue whose value is {@code (this / divisor)}.
251      *
252      * @param divisor
253      *            value by which this TimeValue is to be divided.
254      * @param targetTimeUnit
255      *            the target TimeUnit
256      * @return {@code this / divisor}
257      * @throws ArithmeticException
258      *             if {@code divisor} is zero.
259      */
260     public TimeValue divide(final long divisor, final TimeUnit targetTimeUnit) {
261         return of(convert(targetTimeUnit) / divisor, targetTimeUnit);
262     }
263 
264     public long getDuration() {
265         return duration;
266     }
267 
268     public TimeUnit getTimeUnit() {
269         return timeUnit;
270     }
271 
272     @Override
273     public int hashCode() {
274         int hash = LangUtils.HASH_SEED;
275         hash = LangUtils.hashCode(hash, duration);
276         hash = LangUtils.hashCode(hash, timeUnit);
277         return hash;
278     }
279 
280     public TimeValueTimeValue.html#TimeValue">TimeValue min(final TimeValue other) {
281         return isGreaterThan(other) ? other : this;
282     }
283 
284     private boolean isGreaterThan(final TimeValue other) {
285         final TimeUnit targetTimeUnit = min(other.getTimeUnit());
286         return convert(targetTimeUnit) > other.convert(targetTimeUnit);
287     }
288 
289     private TimeUnit min(final TimeUnit other) {
290         return scale() > scale(other) ? other : getTimeUnit();
291     }
292 
293     private int scale() {
294         return scale(timeUnit);
295     }
296 
297     /**
298      * Returns a made up scale for TimeUnits.
299      *
300      * @param tUnit
301      *            a TimeUnit
302      * @return a number from 1 to 7, where 1 is NANOSECONDS and 7 DAYS.
303      */
304     private int scale(final TimeUnit tUnit) {
305         switch (tUnit) {
306         case NANOSECONDS:
307             return 1;
308         case MICROSECONDS:
309             return 2;
310         case MILLISECONDS:
311             return 3;
312         case SECONDS:
313             return 4;
314         case MINUTES:
315             return 5;
316         case HOURS:
317             return 6;
318         case DAYS:
319             return 7;
320         default:
321             // Should never happens unless Java adds to the enum.
322             throw new IllegalStateException();
323         }
324     }
325 
326     public void sleep() throws InterruptedException {
327         timeUnit.sleep(duration);
328     }
329 
330     public void timedJoin(final Thread thread) throws InterruptedException {
331         timeUnit.timedJoin(thread, duration);
332     }
333 
334     public void timedWait(final Object obj) throws InterruptedException {
335         timeUnit.timedWait(obj, duration);
336     }
337 
338     public long toDays() {
339         return timeUnit.toDays(duration);
340     }
341 
342     public long toHours() {
343         return timeUnit.toHours(duration);
344     }
345 
346     public long toMicros() {
347         return timeUnit.toMicros(duration);
348     }
349 
350     public long toMillis() {
351         return timeUnit.toMillis(duration);
352     }
353 
354     public int toMillisIntBound() {
355         return asBoundInt(toMillis());
356     }
357 
358     public long toMinutes() {
359         return timeUnit.toMinutes(duration);
360     }
361 
362     public long toNanos() {
363         return timeUnit.toNanos(duration);
364     }
365 
366     public long toSeconds() {
367         return timeUnit.toSeconds(duration);
368     }
369 
370     public int toSecondsIntBound() {
371         return asBoundInt(toSeconds());
372     }
373 
374     @Override
375     public String toString() {
376         return String.format(Locale.ROOT, "%,d %s", duration, timeUnit);
377     }
378 
379     public Timeout toTimeout() {
380         return Timeout.of(duration, timeUnit);
381     }
382 
383 }