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 {@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 negative one millisecond {@link TimeValue}.
50       */
51      public static final TimeValue NEG_ONE_MILLISECONDS = TimeValue.of(INT_UNDEFINED, TimeUnit.MILLISECONDS);
52  
53      /**
54       * A negative one second {@link TimeValue}.
55       */
56      public static final TimeValue NEG_ONE_SECONDS = TimeValue.of(INT_UNDEFINED, TimeUnit.SECONDS);
57  
58      /**
59       * A zero milliseconds {@link TimeValue}.
60       */
61      public static final TimeValue ZERO_MILLISECONDS = TimeValue.of(0, TimeUnit.MILLISECONDS);
62  
63      /**
64       * Returns the given {@code long} value as an {@code int} where long values out of int range are returned as
65       * {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
66       *
67       * <p>
68       * For example: {@code TimeValue.asBoundInt(Long.MAX_VALUE)} returns {@code Integer.MAX_VALUE}.
69       * </p>
70       *
71       * @param value
72       *            a long value to convert
73       * @return an int value bound within {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}.
74       */
75      public static int asBoundInt(final long value) {
76          if (value > Integer.MAX_VALUE) {
77              return Integer.MAX_VALUE;
78          } else if (value < Integer.MIN_VALUE) {
79              return Integer.MIN_VALUE;
80          }
81          return (int) value;
82      }
83  
84      /**
85       * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns the given
86       * {@code defaultValue}.
87       *
88       * @param timeValue
89       *            may be {@code null}
90       * @param defaultValue
91       *            may be {@code null}
92       * @return {@code timeValue} or {@code defaultValue}
93       */
94      public static <T extends TimeValue> T defaultsTo(final T timeValue, final T defaultValue) {
95          return timeValue != null ? timeValue : defaultValue;
96      }
97  
98      /**
99       * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
100      * {@link #NEG_ONE_SECONDS}.
101      *
102      * @param timeValue
103      *            may be {@code null}
104      * @return {@code timeValue} or {@link #NEG_ONE_SECONDS}
105      */
106     public static TimeValue defaultsToNegativeOneMillisecond(final TimeValue timeValue) {
107         return defaultsTo(timeValue, NEG_ONE_MILLISECONDS);
108     }
109 
110     /**
111      * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns
112      * {@link #NEG_ONE_SECONDS}.
113      *
114      * @param timeValue
115      *            may be {@code null}
116      * @return {@code timeValue} or {@link #NEG_ONE_SECONDS}
117      */
118     public static 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
127      *            may be {@code null}
128      * @return {@code timeValue} or {@link #ZERO_MILLISECONDS}
129      */
130     public static TimeValue defaultsToZeroMillis(final TimeValue timeValue) {
131         return defaultsTo(timeValue, ZERO_MILLISECONDS);
132     }
133 
134     public static boolean isNonNegative(final TimeValue timeValue) {
135         return timeValue != null && timeValue.getDuration() >= 0;
136     }
137 
138     public static boolean isPositive(final TimeValue timeValue) {
139         return timeValue != null && timeValue.getDuration() > 0;
140     }
141 
142     /**
143      * Creates a TimeValue.
144      *
145      * @param duration
146      *            the time duration in the given {@code timeUnit}.
147      * @param timeUnit
148      *            the time unit for the given durarion.
149      * @return a Timeout
150      */
151     public static TimeValue of(final long duration, final TimeUnit timeUnit) {
152         return new TimeValue(duration, timeUnit);
153     }
154 
155     public static TimeValue ofDays(final long days) {
156         return of(days, TimeUnit.DAYS);
157     }
158 
159     public static TimeValue ofHours(final long hours) {
160         return of(hours, TimeUnit.HOURS);
161     }
162 
163     public static TimeValue ofMicroseconds(final long microseconds) {
164         return of(microseconds, TimeUnit.MICROSECONDS);
165     }
166 
167     public static TimeValue ofMillis(final long millis) {
168         return of(millis, TimeUnit.MILLISECONDS);
169     }
170 
171     public static TimeValue ofMinutes(final long minutes) {
172         return of(minutes, TimeUnit.MINUTES);
173     }
174 
175     public static TimeValue ofNanoseconds(final long nanoseconds) {
176         return of(nanoseconds, TimeUnit.NANOSECONDS);
177     }
178 
179     public static TimeValue ofSeconds(final long seconds) {
180         return of(seconds, TimeUnit.SECONDS);
181     }
182 
183     /**
184      * Parses a TimeValue in the format {@code <Integer><SPACE><TimeUnit>}, for example {@code "1,200 MILLISECONDS"}
185      *
186      * @param value
187      *            the TimeValue to parse
188      * @return a new TimeValue
189      * @throws ParseException
190      *             if the number cannot be parsed
191      */
192     public static TimeValue parse(final String value) throws ParseException {
193         final String split[] = value.split("\\s+");
194         if (split.length < 2) {
195             throw new IllegalArgumentException(
196                     String.format("Expected format for <Integer><SPACE><TimeUnit>: ", value));
197         }
198         return TimeValue.of(NumberFormat.getInstance(Locale.ROOT).parse(split[0]).longValue(),
199                 TimeUnit.valueOf(split[1].trim().toUpperCase(Locale.ROOT)));
200     }
201 
202     /**
203      * Calculates the deadline with the current time in milliseconds and the given time value.
204      * Non-positive time value represents an indefinite timeout without a deadline.
205      *
206      * @param currentTimeMillis current time
207      * @param timeValue time value
208      * @return deadline in milliseconds
209      */
210     public static long calculateDeadline(final long currentTimeMillis, final TimeValue timeValue) {
211         if (TimeValue.isPositive(timeValue)) {
212             final long deadline = currentTimeMillis + timeValue.toMillis();
213             return deadline >= 0 ? deadline : Long.MAX_VALUE;
214         }
215         return Long.MAX_VALUE;
216     }
217 
218     private final long duration;
219 
220     private final TimeUnit timeUnit;
221 
222     TimeValue(final long duration, final TimeUnit timeUnit) {
223         super();
224         this.duration = duration;
225         this.timeUnit = Args.notNull(timeUnit, "timeUnit");
226     }
227 
228     public long convert(final TimeUnit sourceUnit) {
229         return timeUnit.convert(duration, sourceUnit);
230     }
231 
232     @Override
233     public boolean equals(final Object obj) {
234         if (this == obj) {
235             return true;
236         }
237         if (obj instanceof TimeValue) {
238             final TimeValue that = (TimeValue) obj;
239             return this.duration == that.duration && LangUtils.equals(this.timeUnit, that.timeUnit);
240         }
241         return false;
242     }
243 
244     public long getDuration() {
245         return duration;
246     }
247 
248     public TimeUnit getTimeUnit() {
249         return timeUnit;
250     }
251 
252     @Override
253     public int hashCode() {
254         int hash = LangUtils.HASH_SEED;
255         hash = LangUtils.hashCode(hash, duration);
256         hash = LangUtils.hashCode(hash, timeUnit);
257         return hash;
258     }
259 
260     public void sleep() throws InterruptedException {
261         timeUnit.sleep(duration);
262     }
263 
264     public void timedJoin(final Thread thread) throws InterruptedException {
265         timeUnit.timedJoin(thread, duration);
266     }
267 
268     public void timedWait(final Object obj) throws InterruptedException {
269         timeUnit.timedWait(obj, duration);
270     }
271 
272     public long toDays() {
273         return timeUnit.toDays(duration);
274     }
275 
276     public long toHours() {
277         return timeUnit.toHours(duration);
278     }
279 
280     public long toMicros() {
281         return timeUnit.toMicros(duration);
282     }
283 
284     public long toMillis() {
285         return timeUnit.toMillis(duration);
286     }
287 
288     public int toMillisIntBound() {
289         return asBoundInt(toMillis());
290     }
291 
292     public long toMinutes() {
293         return timeUnit.toMinutes(duration);
294     }
295 
296     public long toNanos() {
297         return timeUnit.toNanos(duration);
298     }
299 
300     public long toSeconds() {
301         return timeUnit.toSeconds(duration);
302     }
303 
304     public int toSecondsIntBound() {
305         return asBoundInt(toSeconds());
306     }
307 
308     @Override
309     public String toString() {
310         return String.format(Locale.ROOT, "%,d %s", duration, timeUnit);
311     }
312 
313     public Timeout toTimeout() {
314         return Timeout.of(duration, timeUnit);
315     }
316 
317 }