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 }