/* * Copyright (C) 2007-2008 Esmertec AG. * Copyright (C) 2007-2008 The Android Open Source Project * * 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.android.mms.dom.smil; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.smil.Time; public class TimeImpl implements Time { static final int ALLOW_INDEFINITE_VALUE = (1 << 0); static final int ALLOW_OFFSET_VALUE = (1 << 1); static final int ALLOW_SYNCBASE_VALUE = (1 << 2); static final int ALLOW_SYNCTOPREV_VALUE = (1 << 3); static final int ALLOW_EVENT_VALUE = (1 << 4); static final int ALLOW_MARKER_VALUE = (1 << 5); static final int ALLOW_WALLCLOCK_VALUE = (1 << 6); static final int ALLOW_NEGATIVE_VALUE = (1 << 7); static final int ALLOW_ALL = 0xFF; short mTimeType; boolean mResolved; double mResolvedOffset; /** * Creates a TimeImpl representation of a time-value represented as a String. * Time-values have the following syntax: *

*

     * Time-val ::= ( smil-1.0-syncbase-value
     *                          | "indefinite"
     *                          | offset-value
     *                          | syncbase-value
     *                          | syncToPrev-value
     *                          | event-value
     *                          | media-marker-value
     *                          | wallclock-sync-value )
     * Smil-1.0-syncbase-value ::=
     *          "id(" id-ref ")" ( "(" ( "begin" | "end" | clock-value ) ")" )?
     * Offset-value         ::= ( "+" | "-" )? clock-value
     * Syncbase-value       ::= ( id-ref "." ( "begin" | "end" ) ) ( ( "+" | "-" ) clock-value )?
     * SyncToPrev-value     ::= ( "prev.begin" | "prev.end" ) ( ( "+" | "-" ) clock-value )?
     * Event-value          ::= ( id-ref "." )? ( event-ref  ) ( ( "+" | "-" ) clock-value )?
     * Media-marker-value   ::= id-ref ".marker(" marker-name ")"
     * Wallclock-sync-value ::= "wallclock(" wallclock-value ")"
     * 
* * @param timeValue A String in the representation specified above * @param constraints Any combination of the #ALLOW_* flags * @return A TimeImpl instance representing * @exception java.lang.IllegalArgumentException if the timeValue input * parameter does not comply with the defined syntax * @exception java.lang.NullPointerException if the timekValue string is * null */ TimeImpl(String timeValue, int constraints) { /* * We do not support yet: * - smil-1.0-syncbase-value * - syncbase-value * - syncToPrev-value * - event-value * - Media-marker-value * - Wallclock-sync-value */ // Will throw NullPointerException if timeValue is null if (timeValue.equals("indefinite") && ((constraints & ALLOW_INDEFINITE_VALUE) != 0) ) { mTimeType = SMIL_TIME_INDEFINITE; } else if ((constraints & ALLOW_OFFSET_VALUE) != 0) { int sign = 1; if (timeValue.startsWith("+")) { timeValue = timeValue.substring(1); } else if (timeValue.startsWith("-")) { timeValue = timeValue.substring(1); sign = -1; } mResolvedOffset = sign*parseClockValue(timeValue)/1000.0; mResolved = true; mTimeType = SMIL_TIME_OFFSET; } else { throw new IllegalArgumentException("Unsupported time value"); } } /** * Converts a String representation of a clock value into the float * representation used in this API. *

* Clock values have the following syntax: *

*

*

     * Clock-val         ::= ( Full-clock-val | Partial-clock-val | Timecount-val )
     * Full-clock-val    ::= Hours ":" Minutes ":" Seconds ("." Fraction)?
     * Partial-clock-val ::= Minutes ":" Seconds ("." Fraction)?
     * Timecount-val     ::= Timecount ("." Fraction)? (Metric)?
     * Metric            ::= "h" | "min" | "s" | "ms"
     * Hours             ::= DIGIT+; any positive number
     * Minutes           ::= 2DIGIT; range from 00 to 59
     * Seconds           ::= 2DIGIT; range from 00 to 59
     * Fraction          ::= DIGIT+
     * Timecount         ::= DIGIT+
     * 2DIGIT            ::= DIGIT DIGIT
     * DIGIT             ::= [0-9]
     * 
* * @param clockValue A String in the representation specified above * @return A float value in milliseconds that matches the string * representation given as the parameter * @exception java.lang.IllegalArgumentException if the clockValue input * parameter does not comply with the defined syntax * @exception java.lang.NullPointerException if the clockValue string is * null */ public static float parseClockValue(String clockValue) { try { float result = 0; // Will throw NullPointerException if clockValue is null clockValue = clockValue.trim(); // Handle first 'Timecount-val' cases with metric if (clockValue.endsWith("ms")) { result = parseFloat(clockValue, 2, true); } else if (clockValue.endsWith("s")) { result = 1000*parseFloat(clockValue, 1, true); } else if (clockValue.endsWith("min")) { result = 60000*parseFloat(clockValue, 3, true); } else if (clockValue.endsWith("h")) { result = 3600000*parseFloat(clockValue, 1, true); } else { // Handle Timecount-val without metric try { return parseFloat(clockValue, 0, true) * 1000; } catch (NumberFormatException _) { // Ignore } // Split in {[Hours], Minutes, Seconds} String[] timeValues = clockValue.split(":"); // Read Hours if present and remember location of Minutes int indexOfMinutes; if (timeValues.length == 2) { indexOfMinutes = 0; } else if (timeValues.length == 3) { result = 3600000*(int)parseFloat(timeValues[0], 0, false); indexOfMinutes = 1; } else { throw new IllegalArgumentException(); } // Read Minutes int minutes = (int)parseFloat(timeValues[indexOfMinutes], 0, false); if ((minutes >= 00) && (minutes <= 59)) { result += 60000*minutes; } else { throw new IllegalArgumentException(); } // Read Seconds float seconds = parseFloat(timeValues[indexOfMinutes + 1], 0, true); if ((seconds >= 00) && (seconds < 60)) { result += 60000*seconds; } else { throw new IllegalArgumentException(); } } return result; } catch (NumberFormatException e) { throw new IllegalArgumentException(); } } /** * Parse a value formatted as follows: *

*

     * Value    ::= Number ("." Decimal)? (Text)?
     * Number   ::= DIGIT+; any positive number
     * Decimal  ::= DIGIT+; any positive number
     * Text     ::= CHAR*;   any sequence of chars
     * DIGIT    ::= [0-9]
     * 
* @param value The Value to parse * @param ignoreLast The size of Text to ignore * @param parseDecimal Whether Decimal is expected * @return The float value without Text, rounded to 3 digits after '.' * @throws IllegalArgumentException if Decimal was not expected but encountered */ private static float parseFloat(String value, int ignoreLast, boolean parseDecimal) { // Ignore last characters value = value.substring(0, value.length() - ignoreLast); float result; int indexOfComma = value.indexOf('.'); if (indexOfComma != -1) { if (!parseDecimal) { throw new IllegalArgumentException("int value contains decimal"); } // Ensure that there are at least 3 decimals value = value + "000"; // Read value up to 3 decimals and cut the rest result = Float.parseFloat(value.substring(0, indexOfComma)); result += Float.parseFloat( value.substring(indexOfComma + 1, indexOfComma + 4))/1000; } else { result = Integer.parseInt(value); } return result; } /* * Time Interface */ public boolean getBaseBegin() { // TODO Auto-generated method stub return false; } public Element getBaseElement() { // TODO Auto-generated method stub return null; } public String getEvent() { // TODO Auto-generated method stub return null; } public String getMarker() { // TODO Auto-generated method stub return null; } public double getOffset() { // TODO Auto-generated method stub return 0; } public boolean getResolved() { return mResolved; } public double getResolvedOffset() { return mResolvedOffset; } public short getTimeType() { return mTimeType; } public void setBaseBegin(boolean baseBegin) throws DOMException { // TODO Auto-generated method stub } public void setBaseElement(Element baseElement) throws DOMException { // TODO Auto-generated method stub } public void setEvent(String event) throws DOMException { // TODO Auto-generated method stub } public void setMarker(String marker) throws DOMException { // TODO Auto-generated method stub } public void setOffset(double offset) throws DOMException { // TODO Auto-generated method stub } }