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