FileDocCategorySizeDatePackage
ClockParser.javaAPI DocphoneME MR2 API (J2ME)24565Wed May 02 18:00:36 BST 2007com.sun.perseus.parser

ClockParser.java

/*
 *
 *
 * Portions Copyright  2000-2007 Sun Microsystems, Inc. All Rights
 * Reserved.  Use is subject to license terms.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */

/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package com.sun.perseus.parser;

/**
 * Parser for SVG Clock values, as originally defined in the SMIL spec:
 * <pre>
 * 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]
 * </pre>
 *
 * @author <a href="mailto:christopher.campbell@sun.com">Chris Campbell</a>
 * @version $Id: ClockParser.java,v 1.3 2006/04/21 06:40:23 st125089 Exp $
 */
public class ClockParser extends AbstractParser {

    /** Number of milliseconds in a second */
    public static final int MILLIS_PER_SECOND = 1000;

    /** Number of seconds in a minute */
    public static final int SECONDS_PER_MINUTE = 60;

    /** Number of milliseconds in a minute */
    public static final int MILLIS_PER_MINUTE =
        SECONDS_PER_MINUTE * MILLIS_PER_SECOND;

    /** Number of minutes in an hour */
    public static final int MINUTES_PER_HOUR = 60;

    /** Number of milliseconds in an hour */
    public static final int MILLIS_PER_HOUR =
        MINUTES_PER_HOUR * MILLIS_PER_MINUTE;

    /**
     * Total number of milliseconds represented by this clock value.
     */
    private long millis;

    /**
     * Parses a clock value.  This method throws an
     * <code>IllegalArgumentException</code> if the input argument's
     * syntax does not conform to that of a clock value, as defined
     * by the SMIL specification.
     *
     * @param clockString the value to convert to a long offset value.
     * @return long offset value corresponding to the input argument.
     */
    public long parseClock(final String clockString) {
        setString(clockString);
        current = read();
        return parseClock(true);
    }

    /**
     * Parses a clock value, beginning at the current character.
     *
     * @param eos if true, then there should be no more
     * characters at the end of the string (excess characters will produce
     * an <code>IllegalArgumentException</code>); if false, the parser will
     * treat whitespace and ';' characters as if it marked the end of string.
     * @return a long offset value.
     */
    protected long parseClock(final boolean eos) {
        millis = 0L;
        int[] wholeParts = new int[3];
        float fractionPart = 0.0f;
        int numWholeParts = 0;
        int tmp = 0;
        boolean isTimeCountVal = true;
        boolean isFirstDigitInferiorToSix = false;
        boolean hasFractionPart = false;
        
        // first digit (could be part of full, partial, or count)
        m1: switch (current) {
        default:
            throw new IllegalArgumentException();
        case '0': case '1': case '2': case '3': case '4': case '5':
            isFirstDigitInferiorToSix = true;
            break m1;
        case '6': case '7': case '8': case '9':
            break m1;
        }
        
        tmp = tmp * 10 + (current - '0');
        
        current = read();

        m2: switch (current) {
        default:
            throw new IllegalArgumentException();
        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
            if (eos) {
                throw new IllegalArgumentException();
            }
            // FALLTHROUGH
        case -1:
            wholeParts[numWholeParts++] = tmp;
            break m2;
        case ':':
            if (isFirstDigitInferiorToSix) {
                isTimeCountVal = false;
                wholeParts[numWholeParts++] = tmp;
                tmp = 0;
                current = read();
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case '0': case '1': case '2': case '3': case '4': case '5':
                    tmp = tmp * 10 + (current - '0');
                    current = read();
                    switch (current) {
                    default:
                        throw new IllegalArgumentException();
                    case '0': case '1': case '2': case '3': case '4':
                    case '5': case '6': case '7': case '8': case '9':
                        tmp = tmp * 10 + (current - '0');
                        current = read();
                        switch (current) {
                        default:
                            throw new IllegalArgumentException();
                        case ':':
                            wholeParts[numWholeParts++] = tmp;
                            tmp = 0;
                            current = read();
                            switch (current) {
                            default:
                                throw new IllegalArgumentException();
                            case '0': case '1': case '2': 
                            case '3': case '4': case '5':
                                tmp = tmp * 10 + (current - '0');
                                current = read();
                                switch (current) {
                                default:
                                    throw new IllegalArgumentException();
                                case '0': case '1': case '2': case '3': 
                                case '4': case '5': case '6': case '7':
                                case '8': case '9':
                                    tmp = tmp * 10 + (current - '0');
                                    current = read();
                                    switch (current) {
                                    default:
                                        throw new IllegalArgumentException();
                                    case '.':
                                        break m2;
                                    case 0x20: case 0x09:
                                    case 0x0D: case 0x0A:
                                    case ';':
                                        if (eos) {
                                            throw
                                                new IllegalArgumentException();
                                        }
                                        // FALLTHROUGH
                                    case -1:
                                        wholeParts[numWholeParts++] = tmp;
                                        break m2;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            break m2;
        case '.': case 'h': case 'm': case 's':
            break m2;
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            if (isFirstDigitInferiorToSix) {
                tmp = tmp * 10 + (current - '0');
                current = read();
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
                    if (eos) {
                        throw new IllegalArgumentException();
                    }
                    // FALLTHROUGH
                case -1:
                    wholeParts[numWholeParts++] = tmp;
                    break m2;
                case '.': case 'h': case 'm': case 's':
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    break m2;
                case ':':
                    isTimeCountVal = false;
                    wholeParts[numWholeParts++] = tmp;
                    tmp = 0;
                    current = read();
                    switch (current) {
                    default:
                        throw new IllegalArgumentException();
                    case '0': case '1': case '2': case '3': case '4': case '5':
                        tmp = tmp * 10 + (current - '0');
                        current = read();
                        switch (current) {
                        default:
                            throw new IllegalArgumentException();
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                            tmp = tmp * 10 + (current - '0');
                            current = read();
                            switch (current) {
                            default:
                                throw new IllegalArgumentException();
                            case '.':
                                break m2;
                            case 0x20: case 0x09: case 0x0D: case 0x0A:
                            case ';':
                                if (eos) {
                                    throw new IllegalArgumentException();
                                }
                                // FALLTHROUGH
                            case -1:
                                wholeParts[numWholeParts++] = tmp;
                                break m2;
                            case ':':
                                wholeParts[numWholeParts++] = tmp;
                                tmp = 0;
                                current = read();
                                switch (current) {
                                default:
                                    throw new IllegalArgumentException();
                                case '0': case '1': case '2': 
                                case '3': case '4': case '5':
                                    tmp = tmp * 10 + (current - '0');
                                    current = read();
                                    switch (current) {
                                    default:
                                        throw new IllegalArgumentException();
                                    case '0': case '1': case '2': case '3': 
                                    case '4': case '5': case '6': case '7':
                                    case '8': case '9':
                                        tmp = tmp * 10 + (current - '0');
                                        current = read();
                                        switch (current) {
                                        default:
                                            throw
                                                new IllegalArgumentException();
                                        case '.':
                                            break m2;
                                        case 0x20: case 0x09:
                                        case 0x0D: case 0x0A:
                                        case ';':
                                            if (eos) {
                                                throw
                                                    new
                                                    IllegalArgumentException();
                                            }
                                            // FALLTHROUGH
                                        case -1:
                                            wholeParts[numWholeParts++] = tmp;
                                            break m2;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        m3: switch (current) {
        default:
            throw new IllegalArgumentException();
        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
            if (eos) {
                throw new IllegalArgumentException();
            }
            // FALLTHROUGH
        case -1:
            break m3;
        case ':': case '.': case 'h': case 'm': case 's':
            break m3;
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            // must be in the hours field; loop until we reach the colon
            for (;;) {
                tmp = tmp * 10 + (current - '0');
                current = read();
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
                    if (eos) {
                        throw new IllegalArgumentException();
                    }
                    // FALLTHROUGH
                case -1:
                    wholeParts[numWholeParts++] = tmp;
                    break m3;
                case ':': case '.': case 'h': case 'm': case 's':
                    break m3;
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                }
            }
        }

        m4: switch (current) {
        default:
            throw new IllegalArgumentException();
        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
            if (eos) {
                throw new IllegalArgumentException();
            }
            // FALLTHROUGH
        case -1:
            break m4;
        case '.': case 'h': case 'm': case 's':
            break m4;
        case ':':
            isTimeCountVal = false;
            wholeParts[numWholeParts++] = tmp;
            tmp = 0;
            current = read();
            switch (current) {
            default:
                throw new IllegalArgumentException();
            case '0': case '1': case '2': case '3': case '4': case '5':
                tmp = tmp * 10 + (current - '0');
                current = read();
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    tmp = tmp * 10 + (current - '0');
                    current = read();
                    switch (current) {
                    default:
                        throw new IllegalArgumentException();
                    case ':':
                        wholeParts[numWholeParts++] = tmp;
                        tmp = 0;
                        current = read();
                        switch (current) {
                        default:
                            throw new IllegalArgumentException();
                        case '0': case '1': case '2':case '3': 
                        case '4': case '5':
                            tmp = tmp * 10 + (current - '0');
                            current = read();
                            switch (current) {
                            default:
                                throw new IllegalArgumentException();
                            case '0': case '1': case '2': case '3': case '4':
                            case '5': case '6': case '7': case '8': case '9':
                                tmp = tmp * 10 + (current - '0');
                                current = read();
                                switch (current) {
                                default:
                                    throw new IllegalArgumentException();
                                case '.':
                                    break m4;
                                case 0x20: case 0x09: case 0x0D: case 0x0A:
                                case ';':
                                    if (eos) {
                                        throw new IllegalArgumentException();
                                    }
                                    // FALLTHROUGH
                                case -1:
                                    wholeParts[numWholeParts++] = tmp;
                                    break m4;
                                }
                            }
                        }
                    }
                }
            }
        }

        m5: switch (current) {
        default:
            throw new IllegalArgumentException();
        case '.':
            wholeParts[numWholeParts++] = tmp;
            hasFractionPart = true;
            String frac = "0.";
            current = read();
            if (isTimeCountVal) {
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    for (;;) {
                        frac = frac + (current - '0');
                        current = read();
                        switch (current) {
                        default:
                            break m5;
                        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
                            if (eos) {
                                throw new IllegalArgumentException();
                            }
                            // FALLTHROUGH
                        case 'h': case 'm': case 's': case -1:
                            fractionPart = Float.parseFloat(frac);
                            break m5;
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                        }
                    }
                }
            } else {
                switch (current) {
                default:
                    throw new IllegalArgumentException();
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    for (;;) {
                        frac = frac + (current - '0');
                        current = read();
                        switch (current) {
                        default:
                            throw new IllegalArgumentException();
                        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
                            if (eos) {
                                throw new IllegalArgumentException();
                            }
                            // FALLTHROUGH
                        case -1:
                            fractionPart = Float.parseFloat(frac);
                            break m5;
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9':
                        }
                    }
                }
            }
        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
            if (eos) {
                throw new IllegalArgumentException();
            }
            // FALLTHROUGH
        case 'h': case 'm': case 's': case -1:
            break m5;
        }

        switch (current) {
        default:
            throw new IllegalArgumentException();
        case 'h':
            numWholeParts = 0; // so we don't fall into the seconds case below
            addHours(tmp);
            addMillis((int) (fractionPart * MILLIS_PER_HOUR));
            current = read();
            break;
        case 'm':
            numWholeParts = 0; // so we don't fall into the seconds case below
            current = read();
            switch (current) {
            case 'i':
                current = read();
                switch (current) {
                case 'n':
                    addMinutes(tmp);
                    addMillis((int) (fractionPart * MILLIS_PER_MINUTE));
                    current = read();
                    break;
                default:
                    throw new IllegalArgumentException();
                }
                break;
            case 's':
                addMillis(tmp);
                current = read();
                break;
            default:
                throw new IllegalArgumentException();
            }
            break;
        case 's':
            // seconds case is handled in the time count case below
            if (!hasFractionPart) {
                wholeParts[numWholeParts++] = tmp;
            }
            current = read();
            break;
        case 0x20: case 0x09: case 0x0D: case 0x0A: case ';':
            if (eos) {
                throw new IllegalArgumentException();
            }
            // FALLTHROUGH
        case -1:
            break;
        }

        if (eos) {
            skipSpaces();
            if (current != -1) {
                throw new IllegalArgumentException();
            }
        }

        switch (numWholeParts) {
        case 0: // time count was already handled above, just break
            break;
        case 1: // time count (seconds)
            addSeconds(wholeParts[0]);
            addMillis((int) (fractionPart * MILLIS_PER_SECOND));
            break;
        case 2: // partial clock value
            addMinutes(wholeParts[0]);
            addSeconds(wholeParts[1]);
            addMillis((int) (fractionPart * MILLIS_PER_SECOND));
            break;
        case 3: // full clock value
            addHours(wholeParts[0]);
            addMinutes(wholeParts[1]);
            addSeconds(wholeParts[2]);
            addMillis((int) (fractionPart * MILLIS_PER_SECOND));
            break;
        default:
            throw new IllegalArgumentException("wrong number of whole parts");
        }

        return millis;
    }

    /**
     * Adds the given number of hours to the total clock value.
     *
     * @param hours number of hours to add to the total clock value
     */
    private void addHours(final long hours) {
        millis += (hours * MILLIS_PER_HOUR);
    }

    /**
     * Adds the given number of minutes to the total clock value.
     *
     * @param minutes number of minutes to add to the total clock value
     */
    private void addMinutes(final long minutes) {
        millis += (minutes * MILLIS_PER_MINUTE);
    }

    /**
     * Adds the given number of seconds to the total clock value.
     *
     * @param seconds number of seconds to add to the total clock value
     */
    private void addSeconds(final long seconds) {
        millis += (seconds * MILLIS_PER_SECOND);
    }

    /**
     * Adds the given number of milliseconds to the total clock value.
     *
     * @param ms number of milliseconds to add to the total clock value
     */
    private void addMillis(final long ms) {
        millis += ms;
    }
}