001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.admin;
028
029
030
031 import java.util.HashMap;
032 import java.util.Map;
033 import java.util.regex.Matcher;
034 import java.util.regex.Pattern;
035
036
037
038 /**
039 * This enumeration defines various duration units.
040 */
041 public enum DurationUnit {
042
043 /**
044 * A day unit.
045 */
046 DAYS((long) 24 * 60 * 60 * 1000, "d", "days"),
047
048 /**
049 * An hour unit.
050 */
051 HOURS((long) 60 * 60 * 1000, "h", "hours"),
052
053 /**
054 * A millisecond unit.
055 */
056 MILLI_SECONDS(1L, "ms", "milliseconds"),
057
058 /**
059 * A minute unit.
060 */
061 MINUTES((long) 60 * 1000, "m", "minutes"),
062
063 /**
064 * A second unit.
065 */
066 SECONDS(1000L, "s", "seconds"),
067
068 /**
069 * A week unit.
070 */
071 WEEKS((long) 7 * 24 * 60 * 60 * 1000, "w", "weeks");
072
073 // A lookup table for resolving a unit from its name.
074 private static final Map<String, DurationUnit> nameToUnit;
075 static {
076 nameToUnit = new HashMap<String, DurationUnit>();
077
078 for (DurationUnit unit : DurationUnit.values()) {
079 nameToUnit.put(unit.shortName, unit);
080 nameToUnit.put(unit.longName, unit);
081 }
082 }
083
084
085
086 /**
087 * Get the unit corresponding to the provided unit name.
088 *
089 * @param s
090 * The name of the unit. Can be the abbreviated or long
091 * name and can contain white space and mixed case
092 * characters.
093 * @return Returns the unit corresponding to the provided unit name.
094 * @throws IllegalArgumentException
095 * If the provided name did not correspond to a known
096 * duration unit.
097 */
098 public static DurationUnit getUnit(String s) throws IllegalArgumentException {
099 DurationUnit unit = nameToUnit.get(s.trim().toLowerCase());
100 if (unit == null) {
101 throw new IllegalArgumentException("Illegal duration unit \"" + s + "\"");
102 }
103 return unit;
104 }
105
106
107
108 /**
109 * Parse the provided duration string and return its equivalent
110 * duration in milliseconds. The duration string must specify the
111 * unit e.g. "10s". This method will parse duration string
112 * representations produced from the {@link #toString(long)} method.
113 * Therefore, a duration can comprise of multiple duration
114 * specifiers, for example <code>1d15m25s</code>.
115 *
116 * @param s
117 * The duration string to be parsed.
118 * @return Returns the parsed duration in milliseconds.
119 * @throws NumberFormatException
120 * If the provided duration string could not be parsed.
121 * @see #toString(long)
122 */
123 public static long parseValue(String s) throws NumberFormatException {
124 return parseValue(s, null);
125 }
126
127
128
129 /**
130 * Parse the provided duration string and return its equivalent
131 * duration in milliseconds. This method will parse duration string
132 * representations produced from the {@link #toString(long)} method.
133 * Therefore, a duration can comprise of multiple duration
134 * specifiers, for example <code>1d15m25s</code>.
135 *
136 * @param s
137 * The duration string to be parsed.
138 * @param defaultUnit
139 * The default unit to use if there is no unit specified in
140 * the duration string, or <code>null</code> if the
141 * string must always contain a unit.
142 * @return Returns the parsed duration in milliseconds.
143 * @throws NumberFormatException
144 * If the provided duration string could not be parsed.
145 * @see #toString(long)
146 */
147 public static long parseValue(String s, DurationUnit defaultUnit)
148 throws NumberFormatException {
149 String ns = s.trim();
150 if (ns.length() == 0) {
151 throw new NumberFormatException("Empty duration value \"" + s + "\"");
152 }
153
154 Pattern p1 = Pattern.compile("^\\s*((\\d+)\\s*w)?" + "\\s*((\\d+)\\s*d)?"
155 + "\\s*((\\d+)\\s*h)?" + "\\s*((\\d+)\\s*m)?" + "\\s*((\\d+)\\s*s)?"
156 + "\\s*((\\d+)\\s*ms)?\\s*$", Pattern.CASE_INSENSITIVE);
157 Matcher m1 = p1.matcher(ns);
158 if (m1.matches()) {
159 // Value must be of the form produced by toString(long).
160 String weeks = m1.group(2);
161 String days = m1.group(4);
162 String hours = m1.group(6);
163 String minutes = m1.group(8);
164 String seconds = m1.group(10);
165 String ms = m1.group(12);
166
167 long duration = 0;
168
169 try {
170 if (weeks != null) {
171 duration += Long.valueOf(weeks) * WEEKS.getDuration();
172 }
173
174 if (days != null) {
175 duration += Long.valueOf(days) * DAYS.getDuration();
176 }
177
178 if (hours != null) {
179 duration += Long.valueOf(hours) * HOURS.getDuration();
180 }
181
182 if (minutes != null) {
183 duration += Long.valueOf(minutes) * MINUTES.getDuration();
184 }
185
186 if (seconds != null) {
187 duration += Long.valueOf(seconds) * SECONDS.getDuration();
188 }
189
190 if (ms != null) {
191 duration += Long.valueOf(ms) * MILLI_SECONDS.getDuration();
192 }
193 } catch (NumberFormatException e) {
194 throw new NumberFormatException("Invalid duration value \"" + s + "\"");
195 }
196
197 return duration;
198 } else {
199 // Value must be a floating point number followed by a unit.
200 Pattern p2 = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*(\\w+)?\\s*$");
201 Matcher m2 = p2.matcher(ns);
202
203 if (!m2.matches()) {
204 throw new NumberFormatException("Invalid duration value \"" + s + "\"");
205 }
206
207 // Group 1 is the float.
208 double d;
209 try {
210 d = Double.valueOf(m2.group(1));
211 } catch (NumberFormatException e) {
212 throw new NumberFormatException("Invalid duration value \"" + s + "\"");
213 }
214
215 // Group 3 is the unit.
216 String unitString = m2.group(3);
217 DurationUnit unit;
218 if (unitString == null) {
219 if (defaultUnit == null) {
220 throw new NumberFormatException("Invalid duration value \"" + s
221 + "\"");
222 } else {
223 unit = defaultUnit;
224 }
225 } else {
226 try {
227 unit = getUnit(unitString);
228 } catch (IllegalArgumentException e) {
229 throw new NumberFormatException("Invalid duration value \"" + s
230 + "\"");
231 }
232 }
233
234 return unit.toMilliSeconds(d);
235 }
236 }
237
238
239
240 /**
241 * Returns a string representation of the provided duration. The
242 * string representation can be parsed using the
243 * {@link #parseValue(String)} method. The string representation is
244 * comprised of one or more of the number of weeks, days, hours,
245 * minutes, seconds, and milliseconds. Here are some examples:
246 *
247 * <pre>
248 * toString(0) // 0 ms
249 * toString(999) // 999 ms
250 * toString(1000) // 1 s
251 * toString(1500) // 1 s 500 ms
252 * toString(3650000) // 1 h 50 s
253 * toString(3700000) // 1 h 1 m 40 s
254 * </pre>
255 *
256 * @param duration
257 * The duration in milliseconds.
258 * @return Returns a string representation of the provided duration.
259 * @throws IllegalArgumentException
260 * If the provided duration is negative.
261 * @see #parseValue(String)
262 * @see #parseValue(String, DurationUnit)
263 */
264 public static String toString(long duration) throws IllegalArgumentException {
265 if (duration < 0) {
266 throw new IllegalArgumentException("Negative duration " + duration);
267 }
268
269 if (duration == 0) {
270 return "0 ms";
271 }
272
273 DurationUnit[] units = new DurationUnit[] { WEEKS, DAYS, HOURS, MINUTES,
274 SECONDS, MILLI_SECONDS };
275 long remainder = duration;
276 StringBuilder builder = new StringBuilder();
277 boolean isFirst = true;
278 for (DurationUnit unit : units) {
279 long count = remainder / unit.getDuration();
280 if (count > 0) {
281 if (!isFirst) {
282 builder.append(' ');
283 }
284 builder.append(count);
285 builder.append(' ');
286 builder.append(unit.getShortName());
287 remainder = remainder - (count * unit.getDuration());
288 isFirst = false;
289 }
290 }
291 return builder.toString();
292 }
293
294 // The long name of the unit.
295 private final String longName;
296
297 // The abbreviation of the unit.
298 private final String shortName;
299
300 // The size of the unit in milliseconds.
301 private final long sz;
302
303
304
305 // Private constructor.
306 private DurationUnit(long sz, String shortName, String longName) {
307 this.sz = sz;
308 this.shortName = shortName;
309 this.longName = longName;
310 }
311
312
313
314 /**
315 * Converts the specified duration in milliseconds to this unit.
316 *
317 * @param duration
318 * The duration in milliseconds.
319 * @return Returns milliseconds in this unit.
320 */
321 public double fromMilliSeconds(long duration) {
322 return ((double) duration / sz);
323 }
324
325
326
327 /**
328 * Get the number of milliseconds that this unit represents.
329 *
330 * @return Returns the number of milliseconds that this unit
331 * represents.
332 */
333 public long getDuration() {
334 return sz;
335 }
336
337
338
339 /**
340 * Get the long name of this unit.
341 *
342 * @return Returns the long name of this unit.
343 */
344 public String getLongName() {
345 return longName;
346 }
347
348
349
350 /**
351 * Get the abbreviated name of this unit.
352 *
353 * @return Returns the abbreviated name of this unit.
354 */
355 public String getShortName() {
356 return shortName;
357 }
358
359
360
361 /**
362 * Converts the specified duration in this unit to milliseconds.
363 *
364 * @param duration
365 * The duration as a quantity of this unit.
366 * @return Returns the number of milliseconds that the duration
367 * represents.
368 */
369 public long toMilliSeconds(double duration) {
370 return (long) (sz * duration);
371 }
372
373
374
375 /**
376 * {@inheritDoc}
377 * <p>
378 * This implementation returns the abbreviated name of this duration
379 * unit.
380 */
381 @Override
382 public String toString() {
383 return shortName;
384 }
385 }