org.apache.wicket.util.time.Duration.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.util.time.Duration.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.util.time;

import java.util.Locale;
import java.util.Locale.Category;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.StringValueConversionException;
import org.apache.wicket.util.thread.ICode;
import org.slf4j.Logger;

/**
 * A <code>Duration</code> is an immutable length of time stored as a number of milliseconds.
 * Various factory and conversion methods are available for convenience.
 * <p>
 * These static factory methods allow easy construction of value objects using either long values
 * like <code>seconds(2034)</code> or <code>hours(3)</code>:
 * <p>
 * <ul>
 * <li><code>Duration.milliseconds(long)</code>
 * <li><code>Duration.seconds(int)</code>
 * <li><code>Duration.minutes(int)</code>
 * <li><code>Duration.hours(int)</code>
 * <li><code>Duration.days(int)</code>
 * </ul>
 * <p>
 * ...or double-precision floating point values like <code>days(3.2)</code>:
 * <p>
 * <ul>
 * <li><code>Duration.milliseconds(double)</code>
 * <li><code>Duration.seconds(double)</code>
 * <li><code>Duration.minutes(double)</code>
 * <li><code>Duration.hours(double)</code>
 * <li><code>Duration.days(double)</code>
 * </ul>
 * <p>
 * In the case of <code>milliseconds(double)</code>, the value will be rounded off to the nearest
 * integral millisecond using <code>Math.round()</code>.
 * <p>
 * The precise number of milliseconds represented by a <code>Duration</code> object can be retrieved
 * by calling the <code>getMilliseconds</code> method. The value of a <code>Duration</code> object
 * in a given unit like days or hours can be retrieved by calling one of the following unit methods,
 * each of which returns a double-precision floating point number:
 * <p>
 * <ul>
 * <li><code>seconds()</code>
 * <li><code>minutes()</code>
 * <li><code>hours()</code>
 * <li><code>days()</code>
 * </ul>
 * <p>
 * Values can be added and subtracted using the <code>add(Duration)</code> and
 * <code>subtract(Duration)</code> methods, each of which returns a new immutable
 * <code>Duration</code> object.
 * <p>
 * <code>String</code> values can be converted to <code>Duration</code> objects using the static
 * <code>valueOf</code> factory methods. The <code>String</code> format is the opposite of the one
 * created by <code>toString()</code>, which converts a <code>Duration</code> object to a readable
 * form, such as "3.2 hours" or "32.5 minutes". Valid units are: milliseconds, seconds, minutes
 * hours and days. Correct English plural forms are used in creating <code>String</code> values and
 * are parsed as well. The <code>Locale</code> is respected and "," will be used instead of "." in
 * the Eurozone.
 * <p>
 * The benchmark method will "benchmark" a <code>Runnable</code> or an {@link ICode} implementing
 * object, returning a <code>Duration</code> object that represents the amount of time elapsed in
 * running the code.
 * <p>
 * Finally, the <code>sleep</code> method will sleep for the value of a <code>Duration</code>.
 * 
 * @author Jonathan Locke
 * @since 1.2.6
 */
public class Duration extends AbstractTimeValue {
    private static final long serialVersionUID = 1L;

    /** Constant for maximum duration. */
    public static final Duration MAXIMUM = milliseconds(Long.MAX_VALUE);

    /** Constant for no duration. */
    public static final Duration NONE = milliseconds(0);

    /** Constant for one day. */
    public static final Duration ONE_DAY = days(1);

    /** Constant for one hour. */
    public static final Duration ONE_HOUR = hours(1);

    /** Constant for on minute. */
    public static final Duration ONE_MINUTE = minutes(1);

    /** Constant for one second. */
    public static final Duration ONE_SECOND = seconds(1);

    /** Constant for one week. */
    public static final Duration ONE_WEEK = days(7);

    /** pattern to match strings */
    private static final Pattern pattern = Pattern
            .compile("([0-9]+([.,][0-9]+)?)\\s+(millisecond|second|minute|hour|day)s?", Pattern.CASE_INSENSITIVE);

    /**
     * Benchmark the given command.
     * 
     * @param code
     *            an <code>ICode</code>
     * @param log
     *            optional logger to use with errors and exceptions
     * @return the <code>Time</code> value it took to run the code
     */
    public static Duration benchmark(final ICode code, final Logger log) {
        // Get time before running code
        final Time start = Time.now();

        // Run the code
        code.run(log);

        // Return the difference
        return Time.now().subtract(start);
    }

    /**
     * Benchmark the given command.
     * 
     * @param code
     *            a <code>Runnable</code>
     * @return the <code>Time</code> value it took to run the code
     */
    public static Duration benchmark(final Runnable code) {
        // Get time before running code
        final Time start = Time.now();

        // Run code
        code.run();

        // Return the difference
        return Time.now().subtract(start);
    }

    /**
     * Retrieves the <code>Duration</code> based on days.
     * 
     * @param days
     *            days <code>double</code> value
     * @return the <code>Duration</code> based on days
     */
    public static Duration days(final double days) {
        return hours(24.0 * days);
    }

    /**
     * Retrieves the <code>Duration</code> based on days.
     * 
     * @param days
     *            days <code>int</code> value
     * @return the <code>Duration</code> based on days
     */
    public static Duration days(final int days) {
        return hours(24 * days);
    }

    /**
     * Calculates the amount of time elapsed since start time.
     * 
     * @param start
     *            the start <code>Time</code>
     * @return the elapsed period as a <code>Duration</code>
     * @throws IllegalStateException
     *             if start <code>Time</code> is in the future
     */
    public static Duration elapsed(final Time start) {
        return start.elapsedSince();
    }

    /**
     * Retrieves the <code>Duration</code> based on hours.
     * 
     * @param hours
     *            hours <code>double</code> value
     * @return the <code>Duration</code> based on hours
     */
    public static Duration hours(final double hours) {
        return minutes(60.0 * hours);
    }

    /**
     * Retrieves the <code>Duration</code> based on hours.
     * 
     * @param hours
     *            hours <code>int</code> value
     * @return the <code>Duration</code> based on hours
     */
    public static Duration hours(final int hours) {
        return minutes(60 * hours);
    }

    /**
     * Retrieves the <code>Duration</code> based on milliseconds.
     * 
     * @param milliseconds
     *            milliseconds <code>double</code> value
     * @return the <code>Duration</code> based on milliseconds
     */
    public static Duration milliseconds(final double milliseconds) {
        return milliseconds(Math.round(milliseconds));
    }

    /**
     * Retrieves the <code>Duration</code> based on milliseconds.
     * 
     * @param milliseconds
     *            milliseconds <code>long</code> value
     * @return the <code>Duration</code> based on milliseconds
     */
    public static Duration milliseconds(final long milliseconds) {
        return new Duration(milliseconds);
    }

    /**
     * Retrieves the <code>Duration</code> based on minutes.
     * 
     * @param minutes
     *            minutes <code>double</code> value
     * @return the <code>Duration</code> based on minutes
     */
    public static Duration minutes(final double minutes) {
        return seconds(60.0 * minutes);
    }

    /**
     * Retrieves the <code>Duration</code> based on minutes.
     * 
     * @param minutes
     *            minutes <code>int</code> value
     * @return the <code>Duration</code> based on minutes
     */
    public static Duration minutes(final int minutes) {
        return seconds(60 * minutes);
    }

    /**
     * Retrieves the <code>Duration</code> based on seconds.
     * 
     * @param seconds
     *            seconds <code>double</code> value
     * @return the <code>Duration</code> based on seconds
     */
    public static Duration seconds(final double seconds) {
        return milliseconds(seconds * 1000.0);
    }

    /**
     * Retrieves the <code>Duration</code> based on seconds.
     * 
     * @param seconds
     *            seconds <code>int</code> value
     * @return the <code>Duration</code> based on seconds
     */
    public static Duration seconds(final int seconds) {
        return milliseconds(seconds * 1000L);
    }

    /**
     * Retrieves the given <code>long</code> as a <code>Duration</code>.
     * 
     * @param time
     *            the duration <code>long</code> value in milliseconds
     * @return the <code>Duration</code> value
     */
    public static Duration valueOf(final long time) {
        return new Duration(time);
    }

    /**
     * Converts the given <code>String</code> to a new <code>Duration</code> object. The string can
     * take the form of a floating point number followed by a number of milliseconds, seconds,
     * minutes, hours or days. For example "6 hours" or "3.4 days". Parsing is case-insensitive.
     * 
     * @param string
     *            a <code>String</code> to parse
     * @return the <code>Duration</code> value of the given <code>String</code>
     * @throws StringValueConversionException
     */
    public static Duration valueOf(final String string) throws StringValueConversionException {
        return valueOf(string, Locale.getDefault(Locale.Category.FORMAT));
    }

    /**
     * Converts the given <code>String</code> to a new <code>Duration</code> object. The string can
     * take the form of a floating point number followed by a number of milliseconds, seconds,
     * minutes, hours or days. For example "6 hours" or "3.4 days". Parsing is case-insensitive.
     * 
     * @param string
     *            a <code>String</code> to parse
     * @param locale
     *            the <code>Locale</code> used for parsing
     * @return the <code>Duration</code> value of the given <code>String</code>
     * @throws StringValueConversionException
     */
    public static Duration valueOf(final String string, final Locale locale) throws StringValueConversionException {
        final Matcher matcher = pattern.matcher(string);

        if (matcher.matches()) {
            final double value = StringValue.valueOf(matcher.group(1), locale).toDouble();
            final String units = matcher.group(3);

            if (units.equalsIgnoreCase("millisecond")) {
                return milliseconds(value);
            } else if (units.equalsIgnoreCase("second")) {
                return seconds(value);
            } else if (units.equalsIgnoreCase("minute")) {
                return minutes(value);
            } else if (units.equalsIgnoreCase("hour")) {
                return hours(value);
            } else if (units.equalsIgnoreCase("day")) {
                return days(value);
            } else {
                throw new StringValueConversionException("Unrecognized units: " + string);
            }
        } else {
            throw new StringValueConversionException("Unable to parse duration: " + string);
        }
    }

    /**
     * Private constructor forces use of static factory methods.
     * 
     * @param milliseconds
     *            number of milliseconds in this <code>Duration</code>
     */
    protected Duration(final long milliseconds) {
        super(milliseconds);
    }

    /**
     * Adds a given <code>Duration</code> to this <code>Duration</code>.
     * 
     * @param duration
     *            the <code>Duration</code> to add
     * @return the sum of the <code>Duration</code>s
     */
    public Duration add(final Duration duration) {
        return valueOf(getMilliseconds() + duration.getMilliseconds());
    }

    /**
     * Retrieves the number of days of the current <code>Duration</code>.
     * 
     * @return number of days of the current <code>Duration</code>
     */
    public final double days() {
        return hours() / 24.0;
    }

    /**
     * Retrieves the number of hours of the current <code>Duration</code>.
     * 
     * @return number of hours of the current <code>Duration</code>
     */
    public final double hours() {
        return minutes() / 60.0;
    }

    /**
     * Retrieves the number of minutes of the current <code>Duration</code>.
     * 
     * @return number of minutes of the current <code>Duration</code>
     */
    public final double minutes() {
        return seconds() / 60.0;
    }

    /**
     * Retrieves the number of seconds of the current <code>Duration</code>.
     * 
     * @return number of seconds of the current <code>Duration</code>
     */
    public final double seconds() {
        return getMilliseconds() / 1000.0;
    }

    /**
     * Sleeps for the current <code>Duration</code>.
     */
    public final void sleep() {
        if (getMilliseconds() > 0) {
            try {
                Thread.sleep(getMilliseconds());
            } catch (InterruptedException e) {
                // Ignored
            }
        }
    }

    /**
     * Subtracts a given <code>Duration</code> from this <code>Duration</code>.
     * 
     * @param that
     *            the <code>Duration</code> to subtract
     * @return this <code>Duration</code> minus that <code>Duration</code>
     */
    public Duration subtract(final Duration that) {
        return valueOf(getMilliseconds() - that.getMilliseconds());
    }

    /**
     * Wait for this duration on the given monitor
     * 
     * @param object
     *            The monitor to wait on
     */
    public void wait(final Object object) {
        try {
            object.wait(getMilliseconds());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieves the <code>String</code> representation of this <code>Duration</code> in days,
     * hours, minutes, seconds or milliseconds, as appropriate. Uses the default <code>Locale</code>
     * .
     * 
     * @return a <code>String</code> representation
     */
    @Override
    public String toString() {
        return toString(Locale.getDefault(Category.FORMAT));
    }

    /**
     * Retrieves the <code>String</code> representation of this <code>Duration</code> in days,
     * hours, minutes, seconds or milliseconds, as appropriate.
     * 
     * @param locale
     *            a <code>Locale</code>
     * @return a <code>String</code> representation
     */
    public String toString(final Locale locale) {
        if (getMilliseconds() >= 0) {
            if (days() >= 1.0) {
                return unitString(days(), "day", locale);
            }

            if (hours() >= 1.0) {
                return unitString(hours(), "hour", locale);
            }

            if (minutes() >= 1.0) {
                return unitString(minutes(), "minute", locale);
            }

            if (seconds() >= 1.0) {
                return unitString(seconds(), "second", locale);
            }

            return unitString(getMilliseconds(), "millisecond", locale);
        } else {
            return "N/A";
        }
    }

    /**
     * Converts a value to a unit-suffixed value, taking care of English singular/plural suffix.
     * 
     * @param value
     *            a <code>double</code> value to format
     * @param units
     *            the units to apply singular or plural suffix to
     * @param locale
     *            the <code>Locale</code>
     * @return a <code>String</code> representation
     */
    private String unitString(final double value, final String units, final Locale locale) {
        return StringValue.valueOf(value, locale) + " " + units + ((value > 1.0) ? "s" : "");
    }
}