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