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.ParseException;
31  import java.util.Locale;
32  import java.util.concurrent.TimeUnit;
33  
34  import org.apache.hc.core5.annotation.Contract;
35  import org.apache.hc.core5.annotation.ThreadingBehavior;
36  
37  /**
38   * Represents a time value as a {@code long} time and a {@link TimeUnit}.
39   *
40   * @since 5.0
41   */
42  @Contract(threading = ThreadingBehavior.IMMUTABLE)
43  public class TimeValue implements Comparable<TimeValue> {
44  
45      static final int INT_UNDEFINED = -1;
46  
47      /**
48       * A constant holding the maximum value a {@code TimeValue} can have: <code>Long.MAX_VALUE</code> days.
49       */
50      public static final TimeValue MAX_VALUE = ofDays(Long.MAX_VALUE);
51  
52      /**
53       * A negative one millisecond {@link TimeValue}.
54       */
55      public static final TimeValue NEG_ONE_MILLISECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.MILLISECONDS);
56  
57      /**
58       * A negative one second {@link TimeValue}.
59       */
60      public static final TimeValue NEG_ONE_SECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.SECONDS);
61  
62      /**
63       * A zero milliseconds {@link TimeValue}.
64       */
65      public static final TimeValue ZERO_MILLISECONDS = TimeValue.of(0, TimeUnit.MILLISECONDS);
66  
67      /**
68       * Returns the given {@code long} value as an {@code int} where long values out of int range are returned as
69       * {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
70       *
71       * <p>
72       * For example: {@code TimeValue.asBoundInt(Long.MAX_VALUE)} returns {@code Integer.MAX_VALUE}.
73       * </p>
74       *
75       * @param value a long value to convert
76       * @return an int value bound within {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
77       */
78      public static int asBoundInt(final long value) {
79          if (value > Integer.MAX_VALUE) {
80              return Integer.MAX_VALUE;
81          } else if (value < Integer.MIN_VALUE) {
82              return Integer.MIN_VALUE;
83          }
84          return (int) value;
85      }
86  
87      /**
88       * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns the given
89       * {@code defaultValue}.
90       *
91       * @param timeValue may be {@code null}
92       * @param defaultValue may be {@code null}
93       * @return {@code timeValue} or {@code defaultValue}
94       */
95      public static <T extends TimeValue> T defaultsTo(final T timeValue, final T defaultValue) {
96          return timeValue != null ? timeValue : defaultValue;
97      }
98  
99      /**
100      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
101      * {@link #NEG_ONE_SECOND}.
102      *
103      * @param timeValue may be {@code null}
104      * @return {@code timeValue} or {@link #NEG_ONE_SECOND}
105      */
106     public static TimeValueeValue defaultsToNegativeOneMillisecond(final TimeValue timeValue) {
107         return defaultsTo(timeValue, NEG_ONE_MILLISECOND);
108     }
109 
110     /**
111      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
112      * {@link #NEG_ONE_SECOND}.
113      *
114      * @param timeValue may be {@code null}
115      * @return {@code timeValue} or {@link #NEG_ONE_SECOND}
116      */
117     public static TimeValue">TimeValue defaultsToNegativeOneSecond(final TimeValue timeValue) {
118         return defaultsTo(timeValue, NEG_ONE_SECOND);
119     }
120 
121     /**
122      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
123      * {@link #ZERO_MILLISECONDS}.
124      *
125      * @param timeValue may be {@code null}
126      * @return {@code timeValue} or {@link #ZERO_MILLISECONDS}
127      */
128     public static TimeValuee">TimeValue defaultsToZeroMilliseconds(final TimeValue timeValue) {
129         return defaultsTo(timeValue, ZERO_MILLISECONDS);
130     }
131 
132     public static boolean isNonNegative(final TimeValue timeValue) {
133         return timeValue != null && timeValue.getDuration() >= 0;
134     }
135 
136     public static boolean isPositive(final TimeValue timeValue) {
137         return timeValue != null && timeValue.getDuration() > 0;
138     }
139 
140     /**
141      * Creates a TimeValue.
142      *
143      * @param duration the time duration in the given {@code timeUnit}.
144      * @param timeUnit the time unit for the given duration.
145      * @return a Timeout
146      */
147     public static TimeValue of(final long duration, final TimeUnit timeUnit) {
148         return new TimeValue(duration, timeUnit);
149     }
150 
151     public static TimeValue ofDays(final long days) {
152         return of(days, TimeUnit.DAYS);
153     }
154 
155     public static TimeValue ofHours(final long hours) {
156         return of(hours, TimeUnit.HOURS);
157     }
158 
159     public static TimeValue ofMicroseconds(final long microseconds) {
160         return of(microseconds, TimeUnit.MICROSECONDS);
161     }
162 
163     public static TimeValue ofMilliseconds(final long millis) {
164         return of(millis, TimeUnit.MILLISECONDS);
165     }
166 
167     public static TimeValue ofMinutes(final long minutes) {
168         return of(minutes, TimeUnit.MINUTES);
169     }
170 
171     public static TimeValue ofNanoseconds(final long nanoseconds) {
172         return of(nanoseconds, TimeUnit.NANOSECONDS);
173     }
174 
175     public static TimeValue ofSeconds(final long seconds) {
176         return of(seconds, TimeUnit.SECONDS);
177     }
178 
179     /**
180      * Parses a TimeValue in the format {@code <Long><SPACE><TimeUnit>}, for example {@code "1200 MILLISECONDS"}.
181      * <p>
182      * Parses:
183      * </p>
184      * <ul>
185      * <li>{@code "1200 MILLISECONDS"}.</li>
186      * <li>{@code " 1200 MILLISECONDS "}, spaces are ignored.</li>
187      * <li>{@code "1 MINUTE"}, singular units.</li>
188      * <li></li>
189      * </ul>
190      *
191      *
192      * @param value the TimeValue to parse
193      * @return a new TimeValue
194      * @throws ParseException if the number cannot be parsed
195      */
196     public static TimeValue parse(final String value) throws ParseException {
197         final String split[] = value.trim().split("\\s+");
198         if (split.length < 2) {
199             throw new IllegalArgumentException(
200                     String.format("Expected format for <Long><SPACE><java.util.concurrent.TimeUnit>: %s", value));
201         }
202         final String clean0 = split[0].trim();
203         final String clean1 = split[1].trim().toUpperCase(Locale.ROOT);
204         final String timeUnitStr = clean1.endsWith("S") ? clean1 : clean1 + "S";
205         return TimeValue.of(Long.parseLong(clean0), TimeUnit.valueOf(timeUnitStr));
206     }
207 
208     private final long duration;
209 
210     private final TimeUnit timeUnit;
211 
212     TimeValue(final long duration, final TimeUnit timeUnit) {
213         super();
214         this.duration = duration;
215         this.timeUnit = Args.notNull(timeUnit, "timeUnit");
216     }
217 
218     public long convert(final TimeUnit targetTimeUnit) {
219         Args.notNull(targetTimeUnit, "timeUnit");
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             final long thisDuration = this.convert(TimeUnit.NANOSECONDS);
231             final long thatDuration = that.convert(TimeUnit.NANOSECONDS);
232             return thisDuration == thatDuration;
233         }
234         return false;
235     }
236 
237     /**
238      * Returns a TimeValue whose value is {@code (this / divisor)}.
239      *
240      * @param divisor
241      *            value by which this TimeValue is to be divided.
242      * @return {@code this / divisor}
243      * @throws ArithmeticException
244      *             if {@code divisor} is zero.
245      */
246     public TimeValue divide(final long divisor) {
247         final long newDuration = duration / divisor;
248         return of(newDuration, timeUnit);
249     }
250 
251     /**
252      * Returns a TimeValue whose value is {@code (this / divisor)}.
253      *
254      * @param divisor
255      *            value by which this TimeValue is to be divided.
256      * @param targetTimeUnit
257      *            the target TimeUnit
258      * @return {@code this / divisor}
259      * @throws ArithmeticException
260      *             if {@code divisor} is zero.
261      */
262     public TimeValue divide(final long divisor, final TimeUnit targetTimeUnit) {
263         return of(convert(targetTimeUnit) / divisor, targetTimeUnit);
264     }
265 
266     public long getDuration() {
267         return duration;
268     }
269 
270     public TimeUnit getTimeUnit() {
271         return timeUnit;
272     }
273 
274     @Override
275     public int hashCode() {
276         int hash = LangUtils.HASH_SEED;
277         hash = LangUtils.hashCode(hash, this.convert(TimeUnit.NANOSECONDS));
278         return hash;
279     }
280 
281     public TimeValueTimeValue.html#TimeValue">TimeValue min(final TimeValue other) {
282         return this.compareTo(other) > 0 ? other : this;
283     }
284 
285     private TimeUnit min(final TimeUnit other) {
286         return scale() > scale(other) ? other : getTimeUnit();
287     }
288 
289     private int scale() {
290         return scale(timeUnit);
291     }
292 
293     /**
294      * Returns a made up scale for TimeUnits.
295      *
296      * @param tUnit
297      *            a TimeUnit
298      * @return a number from 1 to 7, where 1 is NANOSECONDS and 7 DAYS.
299      */
300     private int scale(final TimeUnit tUnit) {
301         switch (tUnit) {
302         case NANOSECONDS:
303             return 1;
304         case MICROSECONDS:
305             return 2;
306         case MILLISECONDS:
307             return 3;
308         case SECONDS:
309             return 4;
310         case MINUTES:
311             return 5;
312         case HOURS:
313             return 6;
314         case DAYS:
315             return 7;
316         default:
317             // Should never happens unless Java adds to the enum.
318             throw new IllegalStateException();
319         }
320     }
321 
322     public void sleep() throws InterruptedException {
323         timeUnit.sleep(duration);
324     }
325 
326     public void timedJoin(final Thread thread) throws InterruptedException {
327         timeUnit.timedJoin(thread, duration);
328     }
329 
330     public void timedWait(final Object obj) throws InterruptedException {
331         timeUnit.timedWait(obj, duration);
332     }
333 
334     public long toDays() {
335         return timeUnit.toDays(duration);
336     }
337 
338     public long toHours() {
339         return timeUnit.toHours(duration);
340     }
341 
342     public long toMicroseconds() {
343         return timeUnit.toMicros(duration);
344     }
345 
346     public long toMilliseconds() {
347         return timeUnit.toMillis(duration);
348     }
349 
350     public int toMillisecondsIntBound() {
351         return asBoundInt(toMilliseconds());
352     }
353 
354     public long toMinutes() {
355         return timeUnit.toMinutes(duration);
356     }
357 
358     public long toNanoseconds() {
359         return timeUnit.toNanos(duration);
360     }
361 
362     public long toSeconds() {
363         return timeUnit.toSeconds(duration);
364     }
365 
366     public int toSecondsIntBound() {
367         return asBoundInt(toSeconds());
368     }
369 
370     @Override
371     public int compareTo(final TimeValue other) {
372         final TimeUnit targetTimeUnit = min(other.getTimeUnit());
373         return Long.compare(convert(targetTimeUnit), other.convert(targetTimeUnit));
374     }
375 
376     @Override
377     public String toString() {
378         return String.format("%d %s", duration, timeUnit);
379     }
380 
381     public Timeout toTimeout() {
382         return Timeout.of(duration, timeUnit);
383     }
384 
385 }