com.microsoft.azure.management.servicebus.implementation.TimeSpan.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.azure.management.servicebus.implementation.TimeSpan.java

Source

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

package com.microsoft.azure.management.servicebus.implementation;

import org.joda.time.Period;
import rx.functions.Func0;

/**
 * Represents a time interval.
 */
public class TimeSpan {
    private static long ticksPerMillisecond = 10000;
    private static long ticksPerSecond = ticksPerMillisecond * 1000;
    private static long ticksPerMinute = ticksPerSecond * 60;
    private static long ticksPerHour = ticksPerMinute * 60;
    private static long ticksPerDay = ticksPerHour * 24;
    private static double millisecondsPerTick = 1.0 / ticksPerMillisecond;
    private static double secondsPerTick = 1.0 / ticksPerSecond;
    private static double minutesPerTick = 1.0 / ticksPerMinute;

    private int days;
    private int hours;
    private int minutes;
    private int seconds;
    private int milliseconds;

    /**
     * Creates TimeSpan.
     */
    public TimeSpan() {
    }

    /**
     * Specifies the number of days.
     *
     * @param days number of days
     * @return TimeSpan
     */
    public TimeSpan withDays(final int days) {
        this.days = days;
        return this;
    }

    /**
     * Specifies the number of hours.
     *
     * @param hours number of hours
     * @return TimeSpan
     */
    public TimeSpan withHours(final int hours) {
        this.hours = hours;
        return this;
    }

    /**
     * Specifies the number of minutes.
     *
     * @param minutes number of minutes
     * @return TimeSpan
     */
    public TimeSpan withMinutes(final int minutes) {
        this.minutes = minutes;
        return this;
    }

    /**
     * Specifies the number of seconds.
     *
     * @param seconds number of seconds
     * @return TimeSpan
     */
    public TimeSpan withSeconds(final int seconds) {
        this.seconds = seconds;
        return this;
    }

    /**
     * Specifies the number of milliseconds.
     *
     * @param milliseconds number of milliseconds
     * @return TimeSpan
     */
    public TimeSpan withMilliseconds(final int milliseconds) {
        this.milliseconds = milliseconds;
        return this;
    }

    /**
     * @return days value
     */
    public int days() {
        return this.days;
    }

    /**
     * @return hours value
     */
    public int hours() {
        return this.hours;
    }

    /**
     * @return minutes value
     */
    public int minutes() {
        return this.minutes;
    }

    /**
     * @return seconds value
     */
    public int seconds() {
        return this.seconds;
    }

    /**
     * @return mill-seconds value
     */
    public int milliseconds() {
        return this.milliseconds;
    }

    /**
     * @return total number of milliseconds represented by this instance
     */
    public double totalMilliseconds() {
        return totalTicks() * millisecondsPerTick;
    }

    /**
     * @return total number of seconds represented by this instance
     */
    public double totalSeconds() {
        return totalTicks() * secondsPerTick;
    }

    /**
     * @return total number of minutes represented by this instance
     */
    public double totalMinutes() {
        return totalTicks() * minutesPerTick;
    }

    /**
     * Gets TimeSpan from given period.
     *
     * @param period duration in period format
     * @return TimeSpan
     */
    public static TimeSpan fromPeriod(Period period) {
        // Normalize (e.g. move weeks to hour part)
        //
        Period p = new Period(period.toStandardDuration().getMillis());
        return TimeSpan
                .parse((new TimeSpan().withDays(p.getDays()).withHours(p.getHours()).withMinutes(p.getMinutes())
                        .withSeconds(p.getSeconds()).withMilliseconds(p.getMillis())).toString());
    }

    /**
     * Parses the TimeSpan in string format.
     *
     * Valid formats for TimeSpan are:
     * [-]d\0
     * [-]d.hh:mm\0
     * [-]hh:mm\0
     * [-]dd.hh:mm:ss\0
     * [-]hh:mm:ss\0
     * [-]dd.hh:mm:ss:ffffffff\0
     * [-]hh:mm:ss:ffffffff\0
     *
     * @param input the string representation of TimeSpan
     * @return TimeSpan
     */
    public static TimeSpan parse(String input) {
        if (input == null) {
            throw new IllegalArgumentException("input cannot be null");
        }
        final String str = input.trim();
        TimeSpan timeSpan = new TimeSpan();
        TokenParser parser = new TokenParser(str, str.charAt(0) == '-' ? 1 : 0);
        Token token = parser.nextToken();
        // Empty string not allowed
        //
        token.throwIfEmpty();
        int sign = str.charAt(0) == '-' ? -1 : 1;
        if (token.isTerminalCharNull()) {
            // 'dd\0'
            //
            timeSpan.withDays(toInt(token.getRawValue()) * sign);
            return timeSpan;
        }
        if (token.isTerminalMatched('.')) {
            // 'dd.hh'
            //
            timeSpan.withDays(toInt(token.getRawValue()) * sign);
            token = parser.nextToken();
            // If '.' follows 'dd' then there must be 'hh' token
            //
            token.throwIfEmpty();
            timeSpan.withHours(toInt(token.getRawValue()) * sign);
        } else {
            // 'hh'
            //
            timeSpan.withDays(0);
            timeSpan.withHours(toInt(token.getRawValue()) * sign);
        }
        if (timeSpan.hours() > 23) {
            parser.throwOutOfRange();
        }
        // there must be ':' followed by 'dd.hh' or 'hh'
        //
        token.throwIfTerminalCharNotMatch(':');
        token = parser.nextToken();
        // there must be 'mm' token
        //
        token.throwIfEmpty();
        timeSpan.withMinutes(toInt(token.getRawValue()) * sign);
        if (timeSpan.minutes() > 59) {
            parser.throwOutOfRange();
        }
        if (token.isTerminalCharNull()) {
            // 'dd.hh:mm\0' or 'hh:mm\0'
            //
            return timeSpan;
        }
        token.throwIfTerminalCharNotMatch(':');
        token = parser.nextToken();
        // There must be 'ss' token
        //
        token.throwIfEmpty();
        timeSpan.withSeconds(toInt(token.getRawValue()) * sign);
        if (timeSpan.seconds() > 59) {
            parser.throwOutOfRange();
        }
        if (token.isTerminalCharNull()) {
            // 'dd.hh:mm:ss\0' or 'hh:mm:ss\0'
            //
            return timeSpan;
        }
        token.throwIfTerminalCharNotMatch('.');
        token = parser.nextToken();
        // There must be 'fraction' token
        //
        token.throwIfEmpty();
        String milliStr = "." + token.getRawValue();
        if (milliStr.length() > 8) {
            parser.throwOutOfRange();
        }
        int milliSeconds = (int) (Double.parseDouble(milliStr) * 1000);
        timeSpan.withMilliseconds(milliSeconds * sign);
        // There should not be more tokens
        //
        if (!token.isTerminalCharNull()) {
            TokenParser.throwParseError();
        }
        return timeSpan;
    }

    /**
     * @return TimeSpan in [-][d.]hh:mm:ss[.fffffff] format
     */
    public String toString() {
        long totalTicks = totalTicks();
        int days = (int) Math.abs(totalTicks / ticksPerDay);
        StringBuilder stringBuilder = new StringBuilder();
        // Sign part
        //
        if (totalTicks < 0) {
            stringBuilder.append("-");
        }
        // Days part
        //
        if (days != 0) {
            stringBuilder.append(String.format("%d.", days));
        }
        long remainingTicks = Math.abs(totalTicks % ticksPerDay);
        int hours = (int) (remainingTicks / ticksPerHour % 24);
        int minutes = (int) (remainingTicks / ticksPerMinute % 60);
        int seconds = (int) (remainingTicks / ticksPerSecond % 60);
        // Hour, Minute, Second part
        //
        stringBuilder.append(String.format("%02d:%02d:%02d", hours, minutes, seconds));
        int fraction = (int) (remainingTicks % ticksPerSecond);
        // Fraction part
        //
        if (fraction != 0) {
            stringBuilder.append(String.format(".%07d", fraction));
        }
        return stringBuilder.toString();
    }

    /**
     * @return total number of ticks represented by this instance
     */
    private long totalTicks() {
        long totalMilliSeconds = ((long) days * 3600 * 24 + (long) hours * 3600 + (long) minutes * 60 + seconds)
                * 1000 + milliseconds;
        return totalMilliSeconds * ticksPerMillisecond;
    }

    private static int toInt(String intStr) {
        return Integer.parseInt(intStr);
    }
}

/**
 * Parses the TimeSpan in string format.
 */
class TokenParser {
    private final String str;
    private final int startIndex;
    private Func0<Token> nextTokenProvider;

    TokenParser(final String str, final int startIndex) {
        this.str = str;
        this.startIndex = startIndex;
        this.nextTokenProvider = nextTokenProvider();
    }

    /**
     * Gets the next token from the string represented in TimeSpan format.
     * Each token is a number followed by single char. For the last token-character
     * will be null. This method return an empty token if there is no more token left
     * in the string.
     *
     * @return next token
     */
    Token nextToken() {
        return nextTokenProvider.call();
    }

    static void throwParseError() {
        throw new IllegalArgumentException("String was not recognized as a valid TimeSpan");
    }

    static void throwOutOfRange() {
        throw new IllegalArgumentException(
                "The TimeSpan could not be parsed because at least one of the numeric components is out of range or contains too many digits");
    }

    private Func0<Token> nextTokenProvider() {
        return new Func0<Token>() {
            int currentIndex = startIndex;
            int length = str.length();

            @Override
            public Token call() {
                if (currentIndex >= length) {
                    return new Token(null, null);
                }
                StringBuilder builder = new StringBuilder();
                while (currentIndex < length && Character.isDigit(str.charAt(currentIndex))) {
                    builder.append(str.charAt(currentIndex));
                    currentIndex++;
                }
                String val = builder.toString();
                if (val == null) {
                    throwParseError();
                }
                try {
                    Integer.parseInt(val);
                } catch (Exception ex) {
                    throwOutOfRange();
                }
                if (currentIndex < length) {
                    return new Token(val, str.charAt(currentIndex++));
                }
                return new Token(val, null);
            }
        };
    }
}

/**
 * Represents a token (part of) a TimeSpan represented in string format.
 */
class Token {
    private String rawValue;
    private Character terminalChar;

    Token(String rawValue, Character terminalChar) {
        this.rawValue = rawValue;
        this.terminalChar = terminalChar;
    }

    String getRawValue() {
        return this.rawValue;
    }

    boolean isTerminalMatched(Character charToMatch) {
        return terminalChar == charToMatch;
    }

    boolean isTerminalCharNull() {
        return isTerminalMatched(null);
    }

    void throwIfTerminalCharNotMatch(Character matchChar) {
        if (!isTerminalMatched(matchChar)) {
            throw new IllegalArgumentException("String was not recognized as a valid TimeSpan");
        }
    }

    void throwIfEmpty() {
        if (isEmpty()) {
            throw new IllegalArgumentException("String was not recognized as a valid TimeSpan");
        }
    }

    boolean isEmpty() {
        return this.rawValue == null && this.terminalChar == null;
    }
}