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

PathParser.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;

import com.sun.perseus.j2d.Path;

/**
 * The <code>PathParser</code> class converts attributes conforming to the
 * SVG <a href="http://www.w3.org/TR/SVG11/paths.html#PathDataBNF">path
 * syntax</a> with the
 * <a href="http://www.w3.org/TR/SVGMobile/#sec-shapes">limitation</a> of SVG
 * Tiny which says that SVG Tiny does not support <code>arc to</code> commands.
 *
 * @version $Id: PathParser.java,v 1.4 2006/04/21 06:40:37 st125089 Exp $
 */
public class PathParser extends NumberParser {
    /**
     * Current x and y positions in the path, set by
     * commands such as moveTo or lineTo.
     */
    private float currentX, currentY;

    /**
     * Last moveTo command.
     */
    private float lastMoveToX, lastMoveToY;

    /**
     * The smoothQCenter point is used for smootg quad curves
     */
    private float smoothQCenterX, smoothQCenterY;

    /**
     * The smoothQCenter point is used for smooth cubic curves
     */
    private float smoothCCenterX, smoothCCenterY;

    /**
     * The GeneralPath under construction
     */
    private Path p;

    /**
     * Returns the current working path. This can be used,
     * for example, when the parsePath method throws an error
     * to retrieve the state of the path at the time the error
     * occured
     * @return the <code>Path</code> built from the parsed
     *         <code>String</code>
     */
    public Path getPath() {
        return p;
    }

    /**
     * Parses the input <code>String</code> and returns the corresponding
     * <code>Path</code>. 
     *
     * @param s the <code>String</code> to parse.
     * @return the <code>GeneralPath</code> built from the parsed
     * <code>String</code>.
     */
    public Path parsePoints(final String s) {
        setString(s);
        p = preparePath();

        setString(s);
        
        current = read();

        skipSpaces();
        if (current == -1) {
            // No coordinate pair
            return p;
        }

        // Initial moveTo
        float x = parseNumber();
        skipCommaSpaces();
        float y = parseNumber();
        p.moveTo(x, y);
        lastMoveToX = x;
        lastMoveToY = y;

        while (current != -1) {
            skipSpaces();
            if (current != -1) {
                skipCommaSpaces();
                x = parseNumber();
                skipCommaSpaces();
                y = parseNumber();
                p.lineTo(x, y);
            }
        }

        return p;
    }

    /**
     * Pre-parses the path data to allocate the optimal number of commands and data
     * for the path.
     *
     * @return p a path with the proper capacity for the comming path.
     */
    protected Path preparePath() {
        int commandCapacity = 0;
        int dataCapacity = 0;

        current = read();
        
        while (current != -1) {
            skipCommaSpaces();
            switch (current) {
            case 'z':
            case 'Z':
                commandCapacity++;
                break;
            case 'm':
            case 'l':
            case 'M':
            case 'L':
            case 'h':
            case 'H':
            case 'v':
            case 'V':
                commandCapacity++;
                dataCapacity += 1;
                break;
            case 'c':
            case 'C':
            case 's':
            case 'S':
                commandCapacity++;
                dataCapacity += 3;
                break;
            case 'q':
            case 'Q':
            case 't':
            case 'T':
                commandCapacity++;
                dataCapacity += 2;
                break;
            default:
                break;
            }
            current = read();
        }

        return new Path(commandCapacity, dataCapacity);
    }

    /**
     * Parses the input <code>String</code> and returns the corresponding
     * <code>Path</code> if no error is found. If an error occurs,
     * this method throws an <code>IllegalArgumentException</code>.
     *
     * @param s the <code>String</code> to parse.
     * @return the <code>Path</code> built from the parsed
     *         <code>String</code>
     */
    public Path parsePath(final String s) {
        setString(s);
        p = preparePath();

        setString(s);

        currentX = 0;
        currentY = 0;
        smoothQCenterX = 0;
        smoothQCenterY = 0;
        smoothCCenterX = 0;
        smoothCCenterY = 0;

        current = read();
        skipSpaces();

        // Multiple coordinate pairs after a moveto
        // are like a moveto followed by lineto
        switch(current) {
        case 'm':
            parsem();
            parsel();
            break;
        case 'M':
            parseM();
            parseL();
            break;
	case -1:
 	    //an empty path is valid.
 	    break;        
        default:
            throw new IllegalArgumentException();
        }

        loop: for (;;) {
            switch (current) {
            case 0xD:
            case 0xA: 
            case 0x20:
            case 0x9:
                current = read();
                break;
            case 'z':
            case 'Z':
                current = read();
                p.close();
                currentX = lastMoveToX;
                currentY = lastMoveToY;
                break;
            case 'm':
                parsem();
            case 'l':
                parsel();
                break;
            case 'M':
                parseM();
            case 'L':
                parseL();
                break;
            case 'h':
                parseh();
                break;
            case 'H':
                parseH();
                break;
            case 'v':
                parsev();
                break;
            case 'V':
                parseV();
                break;
            case 'c':
                parsec();
                break;
            case 'C':
                parseC();
                break;
            case 'q':
                parseq();
                break;
            case 'Q':
                parseQ();
                break;
            case 's':
                parses();
                break;
            case 'S':
                parseS();
                break;
            case 't':
                parset();
                break;
            case 'T':
                parseT();
                break;
            case -1:
                break loop;
            default:
                throw new IllegalArgumentException();
            }
          
        }

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

        return p;
    }

    /**
     * Parses a 'm' command.
     */
    protected final void parsem() {
        current = read();
        skipSpaces();

        final float x = parseNumber();
        skipCommaSpaces();
        final float y = parseNumber();

        currentX += x;
        smoothQCenterX = currentX;
        smoothCCenterX = currentX;
        currentY += y;
        smoothQCenterY = currentY;
        smoothCCenterY = currentY;
        p.moveTo(smoothCCenterX, smoothCCenterY);
        lastMoveToX = smoothCCenterX;
        lastMoveToY = smoothCCenterY;

        skipCommaSpaces();
    }

    /**
     * Parses a 'l' command.
     */
    protected final void parsel() {
        if (current == 'l') {
            current = read();
        }
        skipSpaces();
        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float x = parseNumber();
                skipCommaSpaces();
                float y = parseNumber();

                currentX += x;
                smoothQCenterX = currentX;
                smoothCCenterX = currentX;

                currentY += y;
                smoothQCenterY = currentY;
                smoothCCenterY = currentY;
                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'M' command.
     */
    protected final void parseM() {
        current = read();
        skipSpaces();

        float x = parseNumber();
        skipCommaSpaces();
        float y = parseNumber();
          
        currentX = x;
        smoothQCenterX = x;
        smoothCCenterX = x;

        currentY = y;
        smoothQCenterY = y;
        smoothCCenterY = y;
        p.moveTo(x, y);
        lastMoveToX = x;
        lastMoveToY = y;

        skipCommaSpaces();
    }

    /**
     * Parses a 'L' command.
     */
    protected final void parseL() {
        if (current == 'L') {
            current = read();
        }
        skipSpaces();
        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float x = parseNumber();
                skipCommaSpaces();
                float y = parseNumber();

                currentX = x;
                smoothQCenterX = x;
                smoothCCenterX = x;

                currentY = y;
                smoothQCenterY = y;
                smoothCCenterY = y;

                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'h' command.
     */
    protected final void parseh() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float x = parseNumber();
                currentX += x;
                smoothQCenterX = currentX;
                smoothCCenterX = currentX;

                smoothQCenterY = currentY;
                smoothCCenterY = currentY;
                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'H' command.
     */
    protected final void parseH() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float x = parseNumber();
                currentX = x;
                smoothQCenterX = x;
                smoothCCenterX = x;

                smoothQCenterY = currentY;
                smoothCCenterY = currentY;
                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'v' command.
     */
    protected final void parsev() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float y = parseNumber();
                smoothQCenterX = currentX;
                smoothCCenterX = currentX;

                currentY += y;
                smoothQCenterY = currentY;
                smoothCCenterY = currentY;
                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'V' command.
     */
    protected final void parseV() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                float y = parseNumber();
                smoothQCenterX = currentX;
                smoothCCenterX = currentX;

                currentY = y;
                smoothQCenterY = y;
                smoothCCenterY = y;
                p.lineTo(smoothCCenterX, smoothCCenterY);
                break;
            default:
                return;
            }
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'c' command.
     */
    protected final void parsec() {
        current = read();
        skipSpaces();
       
        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x1 = parseNumber();
            skipCommaSpaces();
            float y1 = parseNumber();
            skipCommaSpaces();
            float x2 = parseNumber();
            skipCommaSpaces();
            float y2 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            smoothCCenterX = currentX + x2;
            smoothCCenterY = currentY + y2;
            smoothQCenterX = currentX + x;
            smoothQCenterY = currentY + y;
            p.curveTo(currentX + x1, currentY + y1,
                      smoothCCenterX, smoothCCenterY,
                      smoothQCenterX, smoothQCenterY);
            currentX = smoothQCenterX;
            currentY = smoothQCenterY;
            skipCommaSpaces();
        }
    }               

    /**
     * Parses a 'C' command.
     */
    protected final void parseC() {
        current = read();
        skipSpaces();
       
        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x1 = parseNumber();
            skipCommaSpaces();
            float y1 = parseNumber();
            skipCommaSpaces();
            float x2 = parseNumber();
            skipCommaSpaces();
            float y2 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            smoothCCenterX = x2;
            smoothCCenterY = y2;
            currentX = x;
            currentY = y;
            p.curveTo(x1, y1, smoothCCenterX, smoothCCenterY, 
                      currentX, currentY);
            smoothQCenterX = currentX;
            smoothQCenterY = currentY;
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'q' command.
     */
    protected final void parseq() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x1 = parseNumber();
            skipCommaSpaces();
            float y1 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            smoothQCenterX = currentX + x1;
            smoothQCenterY = currentY + y1;
            currentX += x; 
            currentY += y;
            p.quadTo(smoothQCenterX, smoothQCenterY, currentX, currentY);
            smoothCCenterX = currentX;
            smoothCCenterY = currentY;

            skipCommaSpaces();
        }
    }

    /**
     * Parses a 'Q' command.
     */
    protected final void parseQ() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x1 = parseNumber();
            skipCommaSpaces();
            float y1 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            smoothQCenterX = x1;
            smoothQCenterY = y1;
            currentX = x;
            currentY = y;
            p.quadTo(smoothQCenterX, smoothQCenterY, currentX, currentY);
            smoothCCenterX = currentX;
            smoothCCenterY = currentY;
            skipCommaSpaces();
        }
    }

    /**
     * Parses a 's' command.
     */
    protected final void parses() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }
          
            float x2 = parseNumber();
            skipCommaSpaces();
            float y2 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            float smoothX = currentX * 2 - smoothCCenterX;
            float smoothY = currentY * 2 - smoothCCenterY;
            smoothCCenterX = currentX + x2;
            smoothCCenterY = currentY + y2;
            currentX += x;
            currentY += y;

            p.curveTo(smoothX, smoothY,
                      smoothCCenterX, smoothCCenterY,
                      currentX, currentY);

            smoothQCenterX = currentX;
            smoothQCenterY = currentY;
            skipCommaSpaces();
        }
    }               

    /**
     * Parses a 'S' command.
     */
    protected final void parseS() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }
          
            float x2 = parseNumber();
            skipCommaSpaces();
            float y2 = parseNumber();
            skipCommaSpaces();
            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            float smoothX = currentX * 2 - smoothCCenterX;
            float smoothY = currentY * 2 - smoothCCenterY;
            currentX = x;
            currentY = y;
            p.curveTo(smoothX, smoothY,
                      x2, y2,
                      currentX, currentY);
            smoothCCenterX = x2;
            smoothCCenterY = y2;
            smoothQCenterX = currentX;
            smoothQCenterY = currentY;

            skipCommaSpaces();
        }
    }               

    /**
     * Parses a 't' command.
     */
    protected final void parset() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();
          
            smoothQCenterX = currentX * 2 - smoothQCenterX;
            smoothQCenterY = currentY * 2 - smoothQCenterY;
            currentX += x;
            currentY += y;
            p.quadTo(smoothQCenterX, smoothQCenterY, currentX, currentY);
            smoothCCenterX = currentX;
            smoothCCenterY = currentY;
            skipCommaSpaces();
        }               
    }

    /**
     * Parses a 'T' command.
     */
    protected final void parseT() {
        current = read();
        skipSpaces();

        for (;;) {
            switch (current) {
            default:
                return;
            case '+': case '-': case '.':
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            }

            float x = parseNumber();
            skipCommaSpaces();
            float y = parseNumber();

            smoothQCenterX = currentX * 2 - smoothQCenterX;
            smoothQCenterY = currentY * 2 - smoothQCenterY;
            currentX = x;
            currentY = y;
            p.quadTo(smoothQCenterX, smoothQCenterY,
                     currentX, currentY);
            smoothCCenterX = currentX;
            smoothCCenterY = currentY;
            skipCommaSpaces();
        }               
    }
}