001: /*
002: *
003: *
004: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
005: * Reserved. Use is subject to license terms.
006: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License version
010: * 2 only, as published by the Free Software Foundation.
011: *
012: * This program is distributed in the hope that it will be useful, but
013: * WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * General Public License version 2 for more details (a copy is
016: * included at /legal/license.txt).
017: *
018: * You should have received a copy of the GNU General Public License
019: * version 2 along with this work; if not, write to the Free Software
020: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
021: * 02110-1301 USA
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
024: * Clara, CA 95054 or visit www.sun.com if you need additional
025: * information or have any questions.
026: */
027:
028: /*****************************************************************************
029: * Copyright (C) The Apache Software Foundation. All rights reserved. *
030: * ------------------------------------------------------------------------- *
031: * This software is published under the terms of the Apache Software License *
032: * version 1.1, a copy of which has been included with this distribution in *
033: * the LICENSE file. *
034: *****************************************************************************/package com.sun.perseus.parser;
035:
036: /**
037: * Parser for SVG Clock values, as originally defined in the SMIL spec:
038: * <pre>
039: * Clock-val ::= Full-clock-val | Partial-clock-val
040: * | Timecount-val
041: * Full-clock-val ::= Hours ":" Minutes ":" Seconds ("." Fraction)?
042: * Partial-clock-val ::= Minutes ":" Seconds ("." Fraction)?
043: * Timecount-val ::= Timecount ("." Fraction)? (Metric)?
044: * Metric ::= "h" | "min" | "s" | "ms"
045: * Hours ::= DIGIT+; any positive number
046: * Minutes ::= 2DIGIT; range from 00 to 59
047: * Seconds ::= 2DIGIT; range from 00 to 59
048: * Fraction ::= DIGIT+
049: * Timecount ::= DIGIT+
050: * 2DIGIT ::= DIGIT DIGIT
051: * DIGIT ::= [0-9]
052: * </pre>
053: *
054: * @author <a href="mailto:christopher.campbell@sun.com">Chris Campbell</a>
055: * @version $Id: ClockParser.java,v 1.3 2006/04/21 06:40:23 st125089 Exp $
056: */
057: public class ClockParser extends AbstractParser {
058:
059: /** Number of milliseconds in a second */
060: public static final int MILLIS_PER_SECOND = 1000;
061:
062: /** Number of seconds in a minute */
063: public static final int SECONDS_PER_MINUTE = 60;
064:
065: /** Number of milliseconds in a minute */
066: public static final int MILLIS_PER_MINUTE = SECONDS_PER_MINUTE
067: * MILLIS_PER_SECOND;
068:
069: /** Number of minutes in an hour */
070: public static final int MINUTES_PER_HOUR = 60;
071:
072: /** Number of milliseconds in an hour */
073: public static final int MILLIS_PER_HOUR = MINUTES_PER_HOUR
074: * MILLIS_PER_MINUTE;
075:
076: /**
077: * Total number of milliseconds represented by this clock value.
078: */
079: private long millis;
080:
081: /**
082: * Parses a clock value. This method throws an
083: * <code>IllegalArgumentException</code> if the input argument's
084: * syntax does not conform to that of a clock value, as defined
085: * by the SMIL specification.
086: *
087: * @param clockString the value to convert to a long offset value.
088: * @return long offset value corresponding to the input argument.
089: */
090: public long parseClock(final String clockString) {
091: setString(clockString);
092: current = read();
093: return parseClock(true);
094: }
095:
096: /**
097: * Parses a clock value, beginning at the current character.
098: *
099: * @param eos if true, then there should be no more
100: * characters at the end of the string (excess characters will produce
101: * an <code>IllegalArgumentException</code>); if false, the parser will
102: * treat whitespace and ';' characters as if it marked the end of string.
103: * @return a long offset value.
104: */
105: protected long parseClock(final boolean eos) {
106: millis = 0L;
107: int[] wholeParts = new int[3];
108: float fractionPart = 0.0f;
109: int numWholeParts = 0;
110: int tmp = 0;
111: boolean isTimeCountVal = true;
112: boolean isFirstDigitInferiorToSix = false;
113: boolean hasFractionPart = false;
114:
115: // first digit (could be part of full, partial, or count)
116: m1: switch (current) {
117: default:
118: throw new IllegalArgumentException();
119: case '0':
120: case '1':
121: case '2':
122: case '3':
123: case '4':
124: case '5':
125: isFirstDigitInferiorToSix = true;
126: break m1;
127: case '6':
128: case '7':
129: case '8':
130: case '9':
131: break m1;
132: }
133:
134: tmp = tmp * 10 + (current - '0');
135:
136: current = read();
137:
138: m2: switch (current) {
139: default:
140: throw new IllegalArgumentException();
141: case 0x20:
142: case 0x09:
143: case 0x0D:
144: case 0x0A:
145: case ';':
146: if (eos) {
147: throw new IllegalArgumentException();
148: }
149: // FALLTHROUGH
150: case -1:
151: wholeParts[numWholeParts++] = tmp;
152: break m2;
153: case ':':
154: if (isFirstDigitInferiorToSix) {
155: isTimeCountVal = false;
156: wholeParts[numWholeParts++] = tmp;
157: tmp = 0;
158: current = read();
159: switch (current) {
160: default:
161: throw new IllegalArgumentException();
162: case '0':
163: case '1':
164: case '2':
165: case '3':
166: case '4':
167: case '5':
168: tmp = tmp * 10 + (current - '0');
169: current = read();
170: switch (current) {
171: default:
172: throw new IllegalArgumentException();
173: case '0':
174: case '1':
175: case '2':
176: case '3':
177: case '4':
178: case '5':
179: case '6':
180: case '7':
181: case '8':
182: case '9':
183: tmp = tmp * 10 + (current - '0');
184: current = read();
185: switch (current) {
186: default:
187: throw new IllegalArgumentException();
188: case ':':
189: wholeParts[numWholeParts++] = tmp;
190: tmp = 0;
191: current = read();
192: switch (current) {
193: default:
194: throw new IllegalArgumentException();
195: case '0':
196: case '1':
197: case '2':
198: case '3':
199: case '4':
200: case '5':
201: tmp = tmp * 10 + (current - '0');
202: current = read();
203: switch (current) {
204: default:
205: throw new IllegalArgumentException();
206: case '0':
207: case '1':
208: case '2':
209: case '3':
210: case '4':
211: case '5':
212: case '6':
213: case '7':
214: case '8':
215: case '9':
216: tmp = tmp * 10 + (current - '0');
217: current = read();
218: switch (current) {
219: default:
220: throw new IllegalArgumentException();
221: case '.':
222: break m2;
223: case 0x20:
224: case 0x09:
225: case 0x0D:
226: case 0x0A:
227: case ';':
228: if (eos) {
229: throw new IllegalArgumentException();
230: }
231: // FALLTHROUGH
232: case -1:
233: wholeParts[numWholeParts++] = tmp;
234: break m2;
235: }
236: }
237: }
238: }
239: }
240: }
241: }
242: break m2;
243: case '.':
244: case 'h':
245: case 'm':
246: case 's':
247: break m2;
248: case '0':
249: case '1':
250: case '2':
251: case '3':
252: case '4':
253: case '5':
254: case '6':
255: case '7':
256: case '8':
257: case '9':
258: if (isFirstDigitInferiorToSix) {
259: tmp = tmp * 10 + (current - '0');
260: current = read();
261: switch (current) {
262: default:
263: throw new IllegalArgumentException();
264: case 0x20:
265: case 0x09:
266: case 0x0D:
267: case 0x0A:
268: case ';':
269: if (eos) {
270: throw new IllegalArgumentException();
271: }
272: // FALLTHROUGH
273: case -1:
274: wholeParts[numWholeParts++] = tmp;
275: break m2;
276: case '.':
277: case 'h':
278: case 'm':
279: case 's':
280: case '0':
281: case '1':
282: case '2':
283: case '3':
284: case '4':
285: case '5':
286: case '6':
287: case '7':
288: case '8':
289: case '9':
290: break m2;
291: case ':':
292: isTimeCountVal = false;
293: wholeParts[numWholeParts++] = tmp;
294: tmp = 0;
295: current = read();
296: switch (current) {
297: default:
298: throw new IllegalArgumentException();
299: case '0':
300: case '1':
301: case '2':
302: case '3':
303: case '4':
304: case '5':
305: tmp = tmp * 10 + (current - '0');
306: current = read();
307: switch (current) {
308: default:
309: throw new IllegalArgumentException();
310: case '0':
311: case '1':
312: case '2':
313: case '3':
314: case '4':
315: case '5':
316: case '6':
317: case '7':
318: case '8':
319: case '9':
320: tmp = tmp * 10 + (current - '0');
321: current = read();
322: switch (current) {
323: default:
324: throw new IllegalArgumentException();
325: case '.':
326: break m2;
327: case 0x20:
328: case 0x09:
329: case 0x0D:
330: case 0x0A:
331: case ';':
332: if (eos) {
333: throw new IllegalArgumentException();
334: }
335: // FALLTHROUGH
336: case -1:
337: wholeParts[numWholeParts++] = tmp;
338: break m2;
339: case ':':
340: wholeParts[numWholeParts++] = tmp;
341: tmp = 0;
342: current = read();
343: switch (current) {
344: default:
345: throw new IllegalArgumentException();
346: case '0':
347: case '1':
348: case '2':
349: case '3':
350: case '4':
351: case '5':
352: tmp = tmp * 10 + (current - '0');
353: current = read();
354: switch (current) {
355: default:
356: throw new IllegalArgumentException();
357: case '0':
358: case '1':
359: case '2':
360: case '3':
361: case '4':
362: case '5':
363: case '6':
364: case '7':
365: case '8':
366: case '9':
367: tmp = tmp * 10
368: + (current - '0');
369: current = read();
370: switch (current) {
371: default:
372: throw new IllegalArgumentException();
373: case '.':
374: break m2;
375: case 0x20:
376: case 0x09:
377: case 0x0D:
378: case 0x0A:
379: case ';':
380: if (eos) {
381: throw new IllegalArgumentException();
382: }
383: // FALLTHROUGH
384: case -1:
385: wholeParts[numWholeParts++] = tmp;
386: break m2;
387: }
388: }
389: }
390: }
391: }
392: }
393: }
394: }
395: }
396:
397: m3: switch (current) {
398: default:
399: throw new IllegalArgumentException();
400: case 0x20:
401: case 0x09:
402: case 0x0D:
403: case 0x0A:
404: case ';':
405: if (eos) {
406: throw new IllegalArgumentException();
407: }
408: // FALLTHROUGH
409: case -1:
410: break m3;
411: case ':':
412: case '.':
413: case 'h':
414: case 'm':
415: case 's':
416: break m3;
417: case '0':
418: case '1':
419: case '2':
420: case '3':
421: case '4':
422: case '5':
423: case '6':
424: case '7':
425: case '8':
426: case '9':
427: // must be in the hours field; loop until we reach the colon
428: for (;;) {
429: tmp = tmp * 10 + (current - '0');
430: current = read();
431: switch (current) {
432: default:
433: throw new IllegalArgumentException();
434: case 0x20:
435: case 0x09:
436: case 0x0D:
437: case 0x0A:
438: case ';':
439: if (eos) {
440: throw new IllegalArgumentException();
441: }
442: // FALLTHROUGH
443: case -1:
444: wholeParts[numWholeParts++] = tmp;
445: break m3;
446: case ':':
447: case '.':
448: case 'h':
449: case 'm':
450: case 's':
451: break m3;
452: case '0':
453: case '1':
454: case '2':
455: case '3':
456: case '4':
457: case '5':
458: case '6':
459: case '7':
460: case '8':
461: case '9':
462: }
463: }
464: }
465:
466: m4: switch (current) {
467: default:
468: throw new IllegalArgumentException();
469: case 0x20:
470: case 0x09:
471: case 0x0D:
472: case 0x0A:
473: case ';':
474: if (eos) {
475: throw new IllegalArgumentException();
476: }
477: // FALLTHROUGH
478: case -1:
479: break m4;
480: case '.':
481: case 'h':
482: case 'm':
483: case 's':
484: break m4;
485: case ':':
486: isTimeCountVal = false;
487: wholeParts[numWholeParts++] = tmp;
488: tmp = 0;
489: current = read();
490: switch (current) {
491: default:
492: throw new IllegalArgumentException();
493: case '0':
494: case '1':
495: case '2':
496: case '3':
497: case '4':
498: case '5':
499: tmp = tmp * 10 + (current - '0');
500: current = read();
501: switch (current) {
502: default:
503: throw new IllegalArgumentException();
504: case '0':
505: case '1':
506: case '2':
507: case '3':
508: case '4':
509: case '5':
510: case '6':
511: case '7':
512: case '8':
513: case '9':
514: tmp = tmp * 10 + (current - '0');
515: current = read();
516: switch (current) {
517: default:
518: throw new IllegalArgumentException();
519: case ':':
520: wholeParts[numWholeParts++] = tmp;
521: tmp = 0;
522: current = read();
523: switch (current) {
524: default:
525: throw new IllegalArgumentException();
526: case '0':
527: case '1':
528: case '2':
529: case '3':
530: case '4':
531: case '5':
532: tmp = tmp * 10 + (current - '0');
533: current = read();
534: switch (current) {
535: default:
536: throw new IllegalArgumentException();
537: case '0':
538: case '1':
539: case '2':
540: case '3':
541: case '4':
542: case '5':
543: case '6':
544: case '7':
545: case '8':
546: case '9':
547: tmp = tmp * 10 + (current - '0');
548: current = read();
549: switch (current) {
550: default:
551: throw new IllegalArgumentException();
552: case '.':
553: break m4;
554: case 0x20:
555: case 0x09:
556: case 0x0D:
557: case 0x0A:
558: case ';':
559: if (eos) {
560: throw new IllegalArgumentException();
561: }
562: // FALLTHROUGH
563: case -1:
564: wholeParts[numWholeParts++] = tmp;
565: break m4;
566: }
567: }
568: }
569: }
570: }
571: }
572: }
573:
574: m5: switch (current) {
575: default:
576: throw new IllegalArgumentException();
577: case '.':
578: wholeParts[numWholeParts++] = tmp;
579: hasFractionPart = true;
580: String frac = "0.";
581: current = read();
582: if (isTimeCountVal) {
583: switch (current) {
584: default:
585: throw new IllegalArgumentException();
586: case '0':
587: case '1':
588: case '2':
589: case '3':
590: case '4':
591: case '5':
592: case '6':
593: case '7':
594: case '8':
595: case '9':
596: for (;;) {
597: frac = frac + (current - '0');
598: current = read();
599: switch (current) {
600: default:
601: break m5;
602: case 0x20:
603: case 0x09:
604: case 0x0D:
605: case 0x0A:
606: case ';':
607: if (eos) {
608: throw new IllegalArgumentException();
609: }
610: // FALLTHROUGH
611: case 'h':
612: case 'm':
613: case 's':
614: case -1:
615: fractionPart = Float.parseFloat(frac);
616: break m5;
617: case '0':
618: case '1':
619: case '2':
620: case '3':
621: case '4':
622: case '5':
623: case '6':
624: case '7':
625: case '8':
626: case '9':
627: }
628: }
629: }
630: } else {
631: switch (current) {
632: default:
633: throw new IllegalArgumentException();
634: case '0':
635: case '1':
636: case '2':
637: case '3':
638: case '4':
639: case '5':
640: case '6':
641: case '7':
642: case '8':
643: case '9':
644: for (;;) {
645: frac = frac + (current - '0');
646: current = read();
647: switch (current) {
648: default:
649: throw new IllegalArgumentException();
650: case 0x20:
651: case 0x09:
652: case 0x0D:
653: case 0x0A:
654: case ';':
655: if (eos) {
656: throw new IllegalArgumentException();
657: }
658: // FALLTHROUGH
659: case -1:
660: fractionPart = Float.parseFloat(frac);
661: break m5;
662: case '0':
663: case '1':
664: case '2':
665: case '3':
666: case '4':
667: case '5':
668: case '6':
669: case '7':
670: case '8':
671: case '9':
672: }
673: }
674: }
675: }
676: case 0x20:
677: case 0x09:
678: case 0x0D:
679: case 0x0A:
680: case ';':
681: if (eos) {
682: throw new IllegalArgumentException();
683: }
684: // FALLTHROUGH
685: case 'h':
686: case 'm':
687: case 's':
688: case -1:
689: break m5;
690: }
691:
692: switch (current) {
693: default:
694: throw new IllegalArgumentException();
695: case 'h':
696: numWholeParts = 0; // so we don't fall into the seconds case below
697: addHours(tmp);
698: addMillis((int) (fractionPart * MILLIS_PER_HOUR));
699: current = read();
700: break;
701: case 'm':
702: numWholeParts = 0; // so we don't fall into the seconds case below
703: current = read();
704: switch (current) {
705: case 'i':
706: current = read();
707: switch (current) {
708: case 'n':
709: addMinutes(tmp);
710: addMillis((int) (fractionPart * MILLIS_PER_MINUTE));
711: current = read();
712: break;
713: default:
714: throw new IllegalArgumentException();
715: }
716: break;
717: case 's':
718: addMillis(tmp);
719: current = read();
720: break;
721: default:
722: throw new IllegalArgumentException();
723: }
724: break;
725: case 's':
726: // seconds case is handled in the time count case below
727: if (!hasFractionPart) {
728: wholeParts[numWholeParts++] = tmp;
729: }
730: current = read();
731: break;
732: case 0x20:
733: case 0x09:
734: case 0x0D:
735: case 0x0A:
736: case ';':
737: if (eos) {
738: throw new IllegalArgumentException();
739: }
740: // FALLTHROUGH
741: case -1:
742: break;
743: }
744:
745: if (eos) {
746: skipSpaces();
747: if (current != -1) {
748: throw new IllegalArgumentException();
749: }
750: }
751:
752: switch (numWholeParts) {
753: case 0: // time count was already handled above, just break
754: break;
755: case 1: // time count (seconds)
756: addSeconds(wholeParts[0]);
757: addMillis((int) (fractionPart * MILLIS_PER_SECOND));
758: break;
759: case 2: // partial clock value
760: addMinutes(wholeParts[0]);
761: addSeconds(wholeParts[1]);
762: addMillis((int) (fractionPart * MILLIS_PER_SECOND));
763: break;
764: case 3: // full clock value
765: addHours(wholeParts[0]);
766: addMinutes(wholeParts[1]);
767: addSeconds(wholeParts[2]);
768: addMillis((int) (fractionPart * MILLIS_PER_SECOND));
769: break;
770: default:
771: throw new IllegalArgumentException(
772: "wrong number of whole parts");
773: }
774:
775: return millis;
776: }
777:
778: /**
779: * Adds the given number of hours to the total clock value.
780: *
781: * @param hours number of hours to add to the total clock value
782: */
783: private void addHours(final long hours) {
784: millis += (hours * MILLIS_PER_HOUR);
785: }
786:
787: /**
788: * Adds the given number of minutes to the total clock value.
789: *
790: * @param minutes number of minutes to add to the total clock value
791: */
792: private void addMinutes(final long minutes) {
793: millis += (minutes * MILLIS_PER_MINUTE);
794: }
795:
796: /**
797: * Adds the given number of seconds to the total clock value.
798: *
799: * @param seconds number of seconds to add to the total clock value
800: */
801: private void addSeconds(final long seconds) {
802: millis += (seconds * MILLIS_PER_SECOND);
803: }
804:
805: /**
806: * Adds the given number of milliseconds to the total clock value.
807: *
808: * @param ms number of milliseconds to add to the total clock value
809: */
810: private void addMillis(final long ms) {
811: millis += ms;
812: }
813: }
|