/* Copyright (c) 2008 Google Inc. * * Licensed 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 com.google.gdata.data.media.mediarss; import java.text.ParseException; /** * Time specification object which tries to conform to section 3.6 * of RFC 2326 (Normal Play Time). * * It does not support ranges. * * It only supports a millisecond precision. Any time more precise than * that will be lost when parsing. * * */ public class NormalPlayTime { /** The NormalPlayType 'now' */ public static final NormalPlayTime NOW = new NormalPlayTime(true, -1l); private boolean isNow; private long ms; /** * Creates a NormalPlayTime object. * * If you want 'NOW', use the constant {@link #NOW}. * * @param ms time offset, in milliseconds */ public NormalPlayTime(long ms) { this(false, ms); } private NormalPlayTime(boolean now, long ms) { isNow = now; this.ms = ms; } /** Check whether this time is the special 'now' time. */ public boolean isNow() { return isNow; } /** Returns the offset it milliseconds, -1l for NOW. */ public long getTimeOffsetMs() { return ms; } /** * Parses a NormalPlayTime object and return it. * * @param stringRep string representation * @return a NormalPlayTime, null if and only if stringRep == null * @throws ParseException if the string representation could not * be parsed */ public static NormalPlayTime parse(String stringRep) throws ParseException { if (stringRep == null) { return null; } if ("now".equals(stringRep)) { return NOW; } NPTParser parser = new NPTParser(stringRep); return new NormalPlayTime(parser.parse()); } /** Gets a valid string representation (seconds "." fraction). */ @Override public String toString() { return getNptSecondsRepresentation(); } /** * Gets the standard {@code seconds.fraction } representation * for this object. * * @return {@code seconds.fraction} or {@code "now"} */ public String getNptSecondsRepresentation() { if (isNow) { return "now"; } long seconds = ms / 1000l; long fraction = ms % 1000l; if (fraction == 0) { return Long.toString(seconds); } return String.format("%1$d.%2$03d", seconds, fraction); } /** * Gets the standard {@code hh:mm:ss.fraction } representation * for this object. * * @return {@code hh:mm:ss.fraction} or {@code "now"} */ public String getNptHhmmssRepresentation() { if (isNow) { return "now"; } long fraction = ms % 1000l; long totalseconds = ms / 1000l; long seconds = totalseconds % 60l; long totalminutes = totalseconds / 60l; long minutes = totalminutes % 60l; long hours = totalminutes / 60l; if (fraction > 0) { return String.format("%1$02d:%2$02d:%3$02d.%4$03d", hours, minutes, seconds, fraction); } else { return String.format("%1$02d:%2$02d:%3$02d", hours, minutes, seconds); } } /** * Parser class for a NormalPlayTime that supports both time representations. */ private static class NPTParser { private final String text; private final int length; private int currentIndex; /** Current character, 0 when the end is reached. */ private char current; private static final char EOF = '\0'; public NPTParser(String text) { this.text = text; this.length = text.length(); currentIndex = -1; next(); } private long parse() throws ParseException { long ms; int first = parseNumber(); if (current == ':') { int hours = first; next(); long minutes = parseNumber(); assertCurrentIs(':'); next(); long seconds = parseNumber(); ms = ((((hours * 60l) + minutes) * 60l) + seconds) * 1000l; } else { ms = first * 1000l; } if (current == '.') { next(); int exp = 100; for (int i = 0; i <= 3 && isDigit(); next(), i++, exp /= 10) { ms += exp * digitValue(); } // Ignore extra fraction which can't be stored parseNumber(); } assertCurrentIs(EOF); return ms; } private int parseNumber() { int retval; for (retval = 0; isDigit(); next()) { retval *= 10; retval += digitValue(); } return retval; } private int digitValue() { return current - '0'; } private boolean isDigit() { return current >= '0' && current <= '9'; } private void assertCurrentIs(char c) throws ParseException { if (c != current) { throw new ParseException("Unexpected character", currentIndex); } } private void next() { currentIndex ++; if (currentIndex >= length) { current = EOF; } else { current = text.charAt(currentIndex); } } } }