/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Portions Copyright 2015 ForgeRock AS. */ package org.forgerock.opendj.config; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This enumeration defines various duration units. */ public enum DurationUnit { /** A day unit. */ DAYS(24 * 60 * 60 * 1000, "d", "days"), /** An hour unit. */ HOURS(60 * 60 * 1000, "h", "hours"), /** A millisecond unit. */ MILLI_SECONDS(1L, "ms", "milliseconds"), /** A minute unit. */ MINUTES(60 * 1000, "m", "minutes"), /** A second unit. */ SECONDS(1000L, "s", "seconds"), /** A week unit. */ WEEKS(7 * 24 * 60 * 60 * 1000, "w", "weeks"); /** A lookup table for resolving a unit from its name. */ private static final Map<String, DurationUnit> NAME_TO_UNIT = new HashMap<>(); static { for (DurationUnit unit : DurationUnit.values()) { NAME_TO_UNIT.put(unit.shortName, unit); NAME_TO_UNIT.put(unit.longName, unit); } } /** * Get the unit corresponding to the provided unit name. * * @param s * The name of the unit. Can be the abbreviated or long name and * can contain white space and mixed case characters. * @return Returns the unit corresponding to the provided unit name. * @throws IllegalArgumentException * If the provided name did not correspond to a known duration * unit. */ public static DurationUnit getUnit(String s) { DurationUnit unit = NAME_TO_UNIT.get(s.trim().toLowerCase()); if (unit == null) { throw new IllegalArgumentException("Illegal duration unit \"" + s + "\""); } return unit; } /** * Parse the provided duration string and return its equivalent duration in * milliseconds. The duration string must specify the unit e.g. "10s". This * method will parse duration string representations produced from the * {@link #toString(long)} method. Therefore, a duration can comprise of * multiple duration specifiers, for example <code>1d15m25s</code>. * * @param s * The duration string to be parsed. * @return Returns the parsed duration in milliseconds. * @throws NumberFormatException * If the provided duration string could not be parsed. * @see #toString(long) */ public static long parseValue(String s) { return parseValue(s, null); } /** * Parse the provided duration string and return its equivalent duration in * milliseconds. This method will parse duration string representations * produced from the {@link #toString(long)} method. Therefore, a duration * can comprise of multiple duration specifiers, for example * <code>1d15m25s</code>. * * @param s * The duration string to be parsed. * @param defaultUnit * The default unit to use if there is no unit specified in the * duration string, or <code>null</code> if the string must * always contain a unit. * @return Returns the parsed duration in milliseconds. * @throws NumberFormatException * If the provided duration string could not be parsed. * @see #toString(long) */ public static long parseValue(String s, DurationUnit defaultUnit) { String ns = s.trim(); if (ns.length() == 0) { throw new NumberFormatException("Empty duration value \"" + s + "\""); } Pattern p1 = Pattern.compile("^\\s*((\\d+)\\s*w)?" + "\\s*((\\d+)\\s*d)?" + "\\s*((\\d+)\\s*h)?" + "\\s*((\\d+)\\s*m)?" + "\\s*((\\d+)\\s*s)?" + "\\s*((\\d+)\\s*ms)?\\s*$", Pattern.CASE_INSENSITIVE); Matcher m1 = p1.matcher(ns); if (m1.matches()) { // Value must be of the form produced by toString(long). String weeks = m1.group(2); String days = m1.group(4); String hours = m1.group(6); String minutes = m1.group(8); String seconds = m1.group(10); String ms = m1.group(12); long duration = 0; try { if (weeks != null) { duration += Long.valueOf(weeks) * WEEKS.getDuration(); } if (days != null) { duration += Long.valueOf(days) * DAYS.getDuration(); } if (hours != null) { duration += Long.valueOf(hours) * HOURS.getDuration(); } if (minutes != null) { duration += Long.valueOf(minutes) * MINUTES.getDuration(); } if (seconds != null) { duration += Long.valueOf(seconds) * SECONDS.getDuration(); } if (ms != null) { duration += Long.valueOf(ms) * MILLI_SECONDS.getDuration(); } } catch (NumberFormatException e) { throw new NumberFormatException("Invalid duration value \"" + s + "\""); } return duration; } else { // Value must be a floating point number followed by a unit. Pattern p2 = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*(\\w+)?\\s*$"); Matcher m2 = p2.matcher(ns); if (!m2.matches()) { throw new NumberFormatException("Invalid duration value \"" + s + "\""); } // Group 1 is the float. double d; try { d = Double.valueOf(m2.group(1)); } catch (NumberFormatException e) { throw new NumberFormatException("Invalid duration value \"" + s + "\""); } // Group 3 is the unit. String unitString = m2.group(3); DurationUnit unit; if (unitString == null) { if (defaultUnit == null) { throw new NumberFormatException("Invalid duration value \"" + s + "\""); } else { unit = defaultUnit; } } else { try { unit = getUnit(unitString); } catch (IllegalArgumentException e) { throw new NumberFormatException("Invalid duration value \"" + s + "\""); } } return unit.toMilliSeconds(d); } } /** * Returns a string representation of the provided duration. The string * representation can be parsed using the {@link #parseValue(String)} * method. The string representation is comprised of one or more of the * number of weeks, days, hours, minutes, seconds, and milliseconds. Here * are some examples: * * <pre> * toString(0) // 0 ms * toString(999) // 999 ms * toString(1000) // 1 s * toString(1500) // 1 s 500 ms * toString(3650000) // 1 h 50 s * toString(3700000) // 1 h 1 m 40 s * </pre> * * @param duration * The duration in milliseconds. * @return Returns a string representation of the provided duration. * @throws IllegalArgumentException * If the provided duration is negative. * @see #parseValue(String) * @see #parseValue(String, DurationUnit) */ public static String toString(long duration) { if (duration < 0) { throw new IllegalArgumentException("Negative duration " + duration); } if (duration == 0) { return "0 ms"; } DurationUnit[] units = new DurationUnit[] { WEEKS, DAYS, HOURS, MINUTES, SECONDS, MILLI_SECONDS }; long remainder = duration; StringBuilder builder = new StringBuilder(); boolean isFirst = true; for (DurationUnit unit : units) { long count = remainder / unit.getDuration(); if (count > 0) { if (!isFirst) { builder.append(' '); } builder.append(count); builder.append(' '); builder.append(unit.getShortName()); remainder = remainder - (count * unit.getDuration()); isFirst = false; } } return builder.toString(); } /** The long name of the unit. */ private final String longName; /** The abbreviation of the unit. */ private final String shortName; /** The size of the unit in milliseconds. */ private final long sz; /** Private constructor. */ private DurationUnit(long sz, String shortName, String longName) { this.sz = sz; this.shortName = shortName; this.longName = longName; } /** * Converts the specified duration in milliseconds to this unit. * * @param duration * The duration in milliseconds. * @return Returns milliseconds in this unit. */ public double fromMilliSeconds(long duration) { return (double) duration / sz; } /** * Get the number of milliseconds that this unit represents. * * @return Returns the number of milliseconds that this unit represents. */ public long getDuration() { return sz; } /** * Get the long name of this unit. * * @return Returns the long name of this unit. */ public String getLongName() { return longName; } /** * Get the abbreviated name of this unit. * * @return Returns the abbreviated name of this unit. */ public String getShortName() { return shortName; } /** * Converts the specified duration in this unit to milliseconds. * * @param duration * The duration as a quantity of this unit. * @return Returns the number of milliseconds that the duration represents. */ public long toMilliSeconds(double duration) { return (long) (sz * duration); } /** * {@inheritDoc} * <p> * This implementation returns the abbreviated name of this duration unit. */ @Override public String toString() { return shortName; } }