Home | History | Annotate | Download | only in smil
      1 /*
      2  * Copyright (C) 2007-2008 Esmertec AG.
      3  * Copyright (C) 2007-2008 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mms.dom.smil;
     19 
     20 import org.w3c.dom.DOMException;
     21 import org.w3c.dom.Element;
     22 import org.w3c.dom.smil.Time;
     23 
     24 public class TimeImpl implements Time {
     25     static final int ALLOW_INDEFINITE_VALUE = (1 << 0);
     26     static final int ALLOW_OFFSET_VALUE     = (1 << 1);
     27     static final int ALLOW_SYNCBASE_VALUE   = (1 << 2);
     28     static final int ALLOW_SYNCTOPREV_VALUE = (1 << 3);
     29     static final int ALLOW_EVENT_VALUE      = (1 << 4);
     30     static final int ALLOW_MARKER_VALUE     = (1 << 5);
     31     static final int ALLOW_WALLCLOCK_VALUE  = (1 << 6);
     32     static final int ALLOW_NEGATIVE_VALUE   = (1 << 7);
     33     static final int ALLOW_ALL              = 0xFF;
     34 
     35     short mTimeType;
     36     boolean mResolved;
     37     double mResolvedOffset;
     38 
     39     /**
     40      * Creates a TimeImpl representation of a time-value represented as a String.
     41      * Time-values have the following syntax:
     42      * <p>
     43      * <pre>
     44      * Time-val ::= ( smil-1.0-syncbase-value
     45      *                          | "indefinite"
     46      *                          | offset-value
     47      *                          | syncbase-value
     48      *                          | syncToPrev-value
     49      *                          | event-value
     50      *                          | media-marker-value
     51      *                          | wallclock-sync-value )
     52      * Smil-1.0-syncbase-value ::=
     53      *          "id(" id-ref ")" ( "(" ( "begin" | "end" | clock-value ) ")" )?
     54      * Offset-value         ::= ( "+" | "-" )? clock-value
     55      * Syncbase-value       ::= ( id-ref "." ( "begin" | "end" ) ) ( ( "+" | "-" ) clock-value )?
     56      * SyncToPrev-value     ::= ( "prev.begin" | "prev.end" ) ( ( "+" | "-" ) clock-value )?
     57      * Event-value          ::= ( id-ref "." )? ( event-ref  ) ( ( "+" | "-" ) clock-value )?
     58      * Media-marker-value   ::= id-ref ".marker(" marker-name ")"
     59      * Wallclock-sync-value ::= "wallclock(" wallclock-value ")"
     60      * </pre>
     61      *
     62      * @param timeValue A String in the representation specified above
     63      * @param constraints Any combination of the #ALLOW_* flags
     64      * @return  A TimeImpl instance representing
     65      * @exception java.lang.IllegalArgumentException if the timeValue input
     66      *          parameter does not comply with the defined syntax
     67      * @exception java.lang.NullPointerException if the timekValue string is
     68      *          <code>null</code>
     69      */
     70     TimeImpl(String timeValue, int constraints) {
     71         /*
     72          * We do not support yet:
     73          *      - smil-1.0-syncbase-value
     74          *      - syncbase-value
     75          *      - syncToPrev-value
     76          *      - event-value
     77          *      - Media-marker-value
     78          *      - Wallclock-sync-value
     79          */
     80         // Will throw NullPointerException if timeValue is null
     81         if (timeValue.equals("indefinite")
     82                 && ((constraints & ALLOW_INDEFINITE_VALUE) != 0) ) {
     83             mTimeType = SMIL_TIME_INDEFINITE;
     84         } else if ((constraints & ALLOW_OFFSET_VALUE) != 0) {
     85             int sign = 1;
     86             if (timeValue.startsWith("+")) {
     87                 timeValue = timeValue.substring(1);
     88             } else if (timeValue.startsWith("-")) {
     89                 timeValue = timeValue.substring(1);
     90                 sign = -1;
     91             }
     92             mResolvedOffset = sign*parseClockValue(timeValue)/1000.0;
     93             mResolved = true;
     94             mTimeType = SMIL_TIME_OFFSET;
     95         } else {
     96             throw new IllegalArgumentException("Unsupported time value");
     97         }
     98     }
     99 
    100     /**
    101      * Converts a String representation of a clock value into the float
    102      * representation used in this API.
    103      * <p>
    104      * Clock values have the following syntax:
    105      * </p>
    106      * <p>
    107      * <pre>
    108      * Clock-val         ::= ( Full-clock-val | Partial-clock-val | Timecount-val )
    109      * Full-clock-val    ::= Hours ":" Minutes ":" Seconds ("." Fraction)?
    110      * Partial-clock-val ::= Minutes ":" Seconds ("." Fraction)?
    111      * Timecount-val     ::= Timecount ("." Fraction)? (Metric)?
    112      * Metric            ::= "h" | "min" | "s" | "ms"
    113      * Hours             ::= DIGIT+; any positive number
    114      * Minutes           ::= 2DIGIT; range from 00 to 59
    115      * Seconds           ::= 2DIGIT; range from 00 to 59
    116      * Fraction          ::= DIGIT+
    117      * Timecount         ::= DIGIT+
    118      * 2DIGIT            ::= DIGIT DIGIT
    119      * DIGIT             ::= [0-9]
    120      * </pre>
    121      *
    122      * @param clockValue A String in the representation specified above
    123      * @return  A float value in milliseconds that matches the string
    124      *          representation given as the parameter
    125      * @exception java.lang.IllegalArgumentException if the clockValue input
    126      *          parameter does not comply with the defined syntax
    127      * @exception java.lang.NullPointerException if the clockValue string is
    128      *          <code>null</code>
    129      */
    130     public static float parseClockValue(String clockValue) {
    131         try {
    132             float result = 0;
    133 
    134             // Will throw NullPointerException if clockValue is null
    135             clockValue = clockValue.trim();
    136 
    137             // Handle first 'Timecount-val' cases with metric
    138             if (clockValue.endsWith("ms")) {
    139                 result = parseFloat(clockValue, 2, true);
    140             } else if (clockValue.endsWith("s")) {
    141                 result = 1000*parseFloat(clockValue, 1, true);
    142             } else if (clockValue.endsWith("min")) {
    143                 result = 60000*parseFloat(clockValue, 3, true);
    144             } else if (clockValue.endsWith("h")) {
    145                 result = 3600000*parseFloat(clockValue, 1, true);
    146             } else {
    147                 // Handle Timecount-val without metric
    148                 try {
    149                     return parseFloat(clockValue, 0, true) * 1000;
    150                 } catch (NumberFormatException _) {
    151                     // Ignore
    152                 }
    153 
    154                 // Split in {[Hours], Minutes, Seconds}
    155                 String[] timeValues = clockValue.split(":");
    156 
    157                 // Read Hours if present and remember location of Minutes
    158                 int indexOfMinutes;
    159                 if (timeValues.length == 2) {
    160                     indexOfMinutes = 0;
    161                 } else if (timeValues.length == 3) {
    162                     result = 3600000*(int)parseFloat(timeValues[0], 0, false);
    163                     indexOfMinutes = 1;
    164                 } else {
    165                     throw new IllegalArgumentException();
    166                 }
    167 
    168                 // Read Minutes
    169                 int minutes = (int)parseFloat(timeValues[indexOfMinutes], 0, false);
    170                 if ((minutes >= 00) && (minutes <= 59)) {
    171                     result += 60000*minutes;
    172                 } else {
    173                     throw new IllegalArgumentException();
    174                 }
    175 
    176                 // Read Seconds
    177                 float seconds = parseFloat(timeValues[indexOfMinutes + 1], 0, true);
    178                 if ((seconds >= 00) && (seconds < 60)) {
    179                     result += 60000*seconds;
    180                 } else {
    181                     throw new IllegalArgumentException();
    182                 }
    183 
    184             }
    185             return result;
    186         } catch (NumberFormatException e) {
    187             throw new IllegalArgumentException();
    188         }
    189     }
    190 
    191     /**
    192      * Parse a value formatted as follows:
    193      * <p>
    194      * <pre>
    195      * Value    ::= Number ("." Decimal)? (Text)?
    196      * Number   ::= DIGIT+; any positive number
    197      * Decimal  ::= DIGIT+; any positive number
    198      * Text     ::= CHAR*;   any sequence of chars
    199      * DIGIT    ::= [0-9]
    200      * </pre>
    201      * @param value The Value to parse
    202      * @param ignoreLast The size of Text to ignore
    203      * @param parseDecimal Whether Decimal is expected
    204      * @return The float value without Text, rounded to 3 digits after '.'
    205      * @throws IllegalArgumentException if Decimal was not expected but encountered
    206      */
    207     private static float parseFloat(String value, int ignoreLast, boolean parseDecimal) {
    208         // Ignore last characters
    209         value = value.substring(0, value.length() - ignoreLast);
    210 
    211         float result;
    212         int indexOfComma = value.indexOf('.');
    213         if (indexOfComma != -1) {
    214             if (!parseDecimal) {
    215                 throw new IllegalArgumentException("int value contains decimal");
    216             }
    217             // Ensure that there are at least 3 decimals
    218             value = value + "000";
    219             // Read value up to 3 decimals and cut the rest
    220             result = Float.parseFloat(value.substring(0, indexOfComma));
    221             result += Float.parseFloat(
    222                     value.substring(indexOfComma + 1, indexOfComma + 4))/1000;
    223         } else {
    224             result = Integer.parseInt(value);
    225         }
    226 
    227         return result;
    228     }
    229 
    230     /*
    231      * Time Interface
    232      */
    233 
    234     public boolean getBaseBegin() {
    235         // TODO Auto-generated method stub
    236         return false;
    237     }
    238 
    239     public Element getBaseElement() {
    240         // TODO Auto-generated method stub
    241         return null;
    242     }
    243 
    244     public String getEvent() {
    245         // TODO Auto-generated method stub
    246         return null;
    247     }
    248 
    249     public String getMarker() {
    250         // TODO Auto-generated method stub
    251         return null;
    252     }
    253 
    254     public double getOffset() {
    255         // TODO Auto-generated method stub
    256         return 0;
    257     }
    258 
    259     public boolean getResolved() {
    260         return mResolved;
    261     }
    262 
    263     public double getResolvedOffset() {
    264         return mResolvedOffset;
    265     }
    266 
    267     public short getTimeType() {
    268         return mTimeType;
    269     }
    270 
    271     public void setBaseBegin(boolean baseBegin) throws DOMException {
    272         // TODO Auto-generated method stub
    273 
    274     }
    275 
    276     public void setBaseElement(Element baseElement) throws DOMException {
    277         // TODO Auto-generated method stub
    278 
    279     }
    280 
    281     public void setEvent(String event) throws DOMException {
    282         // TODO Auto-generated method stub
    283 
    284     }
    285 
    286     public void setMarker(String marker) throws DOMException {
    287         // TODO Auto-generated method stub
    288 
    289     }
    290 
    291     public void setOffset(double offset) throws DOMException {
    292         // TODO Auto-generated method stub
    293 
    294     }
    295 }
    296