001: /*
002: * $Id: Duration.java 460527 2006-05-05 16:16:55Z jonl $ $Revision:
003: * 1.5 $ $Date: 2006-05-05 18:16:55 +0200 (Fri, 05 May 2006) $
004: *
005: * ==============================================================================
006: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
007: * use this file except in compliance with the License. You may obtain a copy of
008: * the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
014: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
015: * License for the specific language governing permissions and limitations under
016: * the License.
017: */
018: package wicket.util.time;
019:
020: import java.util.Locale;
021: import java.util.regex.Matcher;
022: import java.util.regex.Pattern;
023:
024: import org.apache.commons.logging.Log;
025:
026: import wicket.util.string.StringValue;
027: import wicket.util.string.StringValueConversionException;
028: import wicket.util.thread.ICode;
029:
030: /**
031: * A duration is an immutable length of time stored as a number of milliseconds.
032: * Various factory and conversion methods are available for convenience.
033: * <P>
034: * These static factory methods allow easy construction of value objects using
035: * either long values like seconds(2034) or hours(3):
036: * <p>
037: * <ul>
038: * <li>Duration.milliseconds(long)
039: * <li>Duration.seconds(int)
040: * <li>Duration.minutes(int)
041: * <li>Duration.hours(int)
042: * <li>Duration.days(int)
043: * </ul>
044: * <p>
045: * or double precision floating point values like days(3.2):
046: * <p>
047: * <ul>
048: * <li>Duration.milliseconds(double)
049: * <li>Duration.seconds(double)
050: * <li>Duration.minutes(double)
051: * <li>Duration.hours(double)
052: * <li>Duration.days(double)
053: * </ul>
054: * <p>
055: * In the case of milliseconds(double), the value will be rounded off to the
056: * nearest integral millisecond using Math.round().
057: * <p>
058: * The precise number of milliseconds represented by a Duration object can be
059: * retrieved by calling the milliseconds() method. The value of a Duration
060: * object in a given unit like days or hours can be retrieved by calling one of
061: * the following unit methods, each of which returns a double precision floating
062: * point number:
063: * <p>
064: * <ul>
065: * <li>seconds()
066: * <li>minutes()
067: * <li>hours()
068: * <li>days()
069: * </ul>
070: * <p>
071: * Values can be added and subtracted using the add() and subtract() methods,
072: * each of which returns a new immutable Duration object.
073: * <p>
074: * String values can be converted to Duration objects using the static valueOf
075: * factory methods. The string format is the opposite of the one created by
076: * toString(), which converts a Duration object to a readable form, such as "3.2
077: * hours" or "32.5 minutes". Valid units are: milliseconds, seconds, minutes
078: * hours and days. Correct English plural forms are used in creating string
079: * values and are parsed as well. The Locale is respected and "," will be used
080: * instead of "." in the Eurozone.
081: * <p>
082: * The benchmark method will "benchmark" a Runnable or an ICode implementing
083: * object, returning a Duration object that represents the amount of time
084: * elapsed in running the code.
085: * <p>
086: * Finally, the sleep() method will sleep for the value of a Duration.
087: *
088: * @author Jonathan Locke
089: */
090: public class Duration extends AbstractTimeValue {
091: private static final long serialVersionUID = 1L;
092:
093: /** Constant for maximum duration */
094: public static final Duration MAXIMUM = milliseconds(Long.MAX_VALUE);
095:
096: /** Constant for no duration. */
097: public static final Duration NONE = milliseconds(0);
098:
099: /** Constant for one day. */
100: public static final Duration ONE_DAY = days(1);
101:
102: /** Constant for one hour. */
103: public static final Duration ONE_HOUR = hours(1);
104:
105: /** Constant for on minute. */
106: public static final Duration ONE_MINUTE = minutes(1);
107:
108: /** Constant for one second. */
109: public static final Duration ONE_SECOND = seconds(1);
110:
111: /** Constant for one week. */
112: public static final Duration ONE_WEEK = days(7);
113:
114: /** Pattern to match strings. */
115: private static final Pattern pattern = Pattern
116: .compile(
117: "([0-9]+([.,][0-9]+)?)\\s+(millisecond|second|minute|hour|day)s?",
118: Pattern.CASE_INSENSITIVE);
119:
120: /**
121: * @param code
122: * The code
123: * @param log
124: * Optional log to use with errors and exceptions
125: * @return The duration it took to run the code
126: */
127: public static Duration benchmark(final ICode code, final Log log) {
128: // Get time before running code
129: final Time start = Time.now();
130:
131: // Run the code
132: code.run(log);
133:
134: // Return the difference
135: return Time.now().subtract(start);
136: }
137:
138: /**
139: * Benchmark the given command.
140: *
141: * @param code
142: * The code
143: * @return The duration it took to run the code
144: */
145: public static Duration benchmark(final Runnable code) {
146: // Get time before running code
147: final Time start = Time.now();
148:
149: // Run code
150: code.run();
151:
152: // Return the difference
153: return Time.now().subtract(start);
154: }
155:
156: /**
157: * Gets the duration based on days.
158: *
159: * @param days
160: * @return duration
161: */
162: public static Duration days(final double days) {
163: return hours(24.0 * days);
164: }
165:
166: /**
167: * Gets the duration based on days.
168: *
169: * @param days
170: * @return duration
171: */
172: public static Duration days(final int days) {
173: return hours(24 * days);
174: }
175:
176: /**
177: * The amount of time elapsed since start time
178: *
179: * @param start
180: * The start time
181: * @return The elapsed period
182: * @throws IllegalStateException Thrown if start is in the future
183: */
184: public static Duration elapsed(final Time start) {
185: return start.elapsedSince();
186: }
187:
188: /**
189: * Gets the duration based on hours.
190: *
191: * @param hours
192: * @return duration
193: */
194: public static Duration hours(final double hours) {
195: return minutes(60.0 * hours);
196: }
197:
198: /**
199: * Gets the duration based on hours.
200: *
201: * @param hours
202: * @return duration
203: */
204: public static Duration hours(final int hours) {
205: return minutes(60 * hours);
206: }
207:
208: /**
209: * Gets the duration based on milliseconds.
210: *
211: * @param milliseconds
212: * @return duration
213: */
214: public static Duration milliseconds(final double milliseconds) {
215: return milliseconds(Math.round(milliseconds));
216: }
217:
218: /**
219: * Gets the duration based on miliseconds.
220: *
221: * @param milliseconds
222: * @return duration
223: */
224: public static Duration milliseconds(final long milliseconds) {
225: return new Duration(milliseconds);
226: }
227:
228: /**
229: * Gets the duration based on minutes.
230: *
231: * @param minutes
232: * @return duration
233: */
234: public static Duration minutes(final double minutes) {
235: return seconds(60.0 * minutes);
236: }
237:
238: /**
239: * Gets the duration based on minutes.
240: *
241: * @param minutes
242: * @return duration
243: */
244: public static Duration minutes(final int minutes) {
245: return seconds(60 * minutes);
246: }
247:
248: /**
249: * Gets the duration based on seconds.
250: *
251: * @param seconds
252: * @return duration
253: */
254: public static Duration seconds(final double seconds) {
255: return milliseconds(seconds * 1000.0);
256: }
257:
258: /**
259: * Gets the duration based on seconds.
260: *
261: * @param seconds
262: * @return duration
263: */
264: public static Duration seconds(final int seconds) {
265: return milliseconds(seconds * 1000L);
266: }
267:
268: /**
269: * Gets the given long as a duration.
270: *
271: * @param time
272: * The duration value in milliseconds
273: * @return Duration value
274: */
275: public static Duration valueOf(final long time) {
276: return new Duration(time);
277: }
278:
279: /**
280: * Converts the given string to a new duration object. The string can take
281: * the form of a floating point number followed by a number of milliseconds,
282: * seconds, minutes, hours or days. For example "6 hours" or "3.4 days".
283: * Parsing is case insensitive.
284: *
285: * @param string
286: * The string to parse
287: * @return The duration value of the given string
288: * @throws StringValueConversionException
289: */
290: public static Duration valueOf(final String string)
291: throws StringValueConversionException {
292: return valueOf(string, Locale.getDefault());
293: }
294:
295: /**
296: * Converts the given string to a new Duration object. The string can take
297: * the form of a floating point number followed by a number of milliseconds,
298: * seconds, minutes, hours or days. For example "6 hours" or "3.4 days".
299: * Parsing is case insensitive.
300: *
301: * @param string
302: * The string to parse
303: * @param locale
304: * Locale used for parsing
305: * @return The duration value of the given string
306: * @throws StringValueConversionException
307: */
308: public static Duration valueOf(final String string,
309: final Locale locale) throws StringValueConversionException {
310: final Matcher matcher = pattern.matcher(string);
311:
312: if (matcher.matches()) {
313: final double value = StringValue.valueOf(matcher.group(1),
314: locale).toDouble();
315: final String units = matcher.group(3);
316:
317: if (units.equalsIgnoreCase("millisecond")) {
318: return milliseconds(value);
319: } else if (units.equalsIgnoreCase("second")) {
320: return seconds(value);
321: } else if (units.equalsIgnoreCase("minute")) {
322: return minutes(value);
323: } else if (units.equalsIgnoreCase("hour")) {
324: return hours(value);
325: } else if (units.equalsIgnoreCase("day")) {
326: return days(value);
327: } else {
328: throw new StringValueConversionException(
329: "Unrecognized units: " + string);
330: }
331: } else {
332: throw new StringValueConversionException(
333: "Unable to parse duration: " + string);
334: }
335: }
336:
337: /**
338: * Private constructor forces use of static factory methods.
339: *
340: * @param milliseconds
341: * Number of milliseconds in this duration
342: */
343: protected Duration(final long milliseconds) {
344: super (milliseconds);
345: }
346:
347: /**
348: * Adds a given duration to this duration.
349: *
350: * @param duration
351: * The duration to add
352: * @return The sum of the durations
353: */
354: public Duration add(final Duration duration) {
355: return valueOf(getMilliseconds() + duration.getMilliseconds());
356: }
357:
358: /**
359: * Gets number of days of the current duration.
360: *
361: * @return number of days of the current duration
362: */
363: public final double days() {
364: return hours() / 24.0;
365: }
366:
367: /**
368: * Gets number of hours of the current duration.
369: *
370: * @return number of hours of the current duration
371: */
372: public final double hours() {
373: return minutes() / 60.0;
374: }
375:
376: /**
377: * Gets number of minutes of the current duration.
378: *
379: * @return number of minutes of the current duration
380: */
381: public final double minutes() {
382: return seconds() / 60.0;
383: }
384:
385: /**
386: * Gets number of seconds of the current duration.
387: *
388: * @return number of seconds of the current duration
389: */
390: public final double seconds() {
391: return getMilliseconds() / 1000.0;
392: }
393:
394: /**
395: * Sleep for the current duration.
396: */
397: public final void sleep() {
398: if (getMilliseconds() > 0) {
399: try {
400: Thread.sleep(getMilliseconds());
401: } catch (InterruptedException e) {
402: // Ignored
403: }
404: }
405: }
406:
407: /**
408: * Subtract a given duration from this duration.
409: *
410: * @param that
411: * The duration to subtract
412: * @return This duration minus that duration
413: */
414: public Duration subtract(final Duration that) {
415: return valueOf(getMilliseconds() - that.getMilliseconds());
416: }
417:
418: /**
419: * Gets the string representation of this duration in days, hours, minutes,
420: * seconds or milliseconds, as appropriate. Uses the default locale.
421: *
422: * @return String representation
423: */
424: public String toString() {
425: return toString(Locale.getDefault());
426: }
427:
428: /**
429: * Gets the string representation of this duration in days, hours, minutes,
430: * seconds or milliseconds, as appropriate.
431: *
432: * @param locale
433: * the locale
434: * @return String representation
435: */
436: public String toString(final Locale locale) {
437: if (getMilliseconds() >= 0) {
438: if (days() >= 1.0) {
439: return unitString(days(), "day", locale);
440: }
441:
442: if (hours() >= 1.0) {
443: return unitString(hours(), "hour", locale);
444: }
445:
446: if (minutes() >= 1.0) {
447: return unitString(minutes(), "minute", locale);
448: }
449:
450: if (seconds() >= 1.0) {
451: return unitString(seconds(), "second", locale);
452: }
453:
454: return getMilliseconds() + " milliseconds";
455: } else {
456: return "N/A";
457: }
458: }
459:
460: /**
461: * Converts a value to a unit suffixed value, taking care of English
462: * singular/plural suffix.
463: *
464: * @param value
465: * The value to format
466: * @param units
467: * The units to apply singular or plural suffix to
468: * @param locale
469: * The locale
470: * @return A string for the value
471: */
472: private String unitString(final double value, final String units,
473: final Locale locale) {
474: return StringValue.valueOf(value, locale) + " " + units
475: + ((value > 1.0) ? "s" : "");
476: }
477: }
|