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

ColorParser.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * 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.
 */

package com.sun.perseus.parser;

import com.sun.perseus.platform.MathSupport;

import java.util.Hashtable;

import com.sun.perseus.util.SVGConstants;

import com.sun.perseus.j2d.RGB;

/**
 * The <code>ColorParser</code> class parses CSS 2 color values as
 * defined in the SVG specification for SVG Tiny:<br />
 * <pre>
 * color: hex3Color | hex6Color | rgbColor | predefinedColor
 * hex3Color: '#' hexDigit hexDigit hexDigit
 * hexDigit: 0|1|2|3|4|5|6|7|8|9|A|a|B|b|C|c|D|d|E|e|F|f
 * hex6Color: '#' hexDigit hexDigit hexDigi hexDigit hexDigit hexDigit
 * rgbColor: rgbColorInt | rgbColorPercent
 * rgbColorInt: rgbPrefix wsp* colInt wsp* comma wsp* colInt wsp*
 *              comma wsp* colInt wsp* rgbSuffix
 * rgbPrefix: 'rgb('
 * wsp: ' '
 * comma: ','
 * colInt: int[0-255]
 * rgbColorPercent: rgbPrefix wsp* colPct wsp* comma wsp* colPct wsp*
 *                  comma wsp* colPct wsp* rgbSuffix
 * colPct: pctInt '%'
 * pctInt: int[0-100]
 * predefinedColor: 'black' | 'silver' | 'gray' | 'white' | 'maroon' | 'red'
 *                  | 'purple' | 'fuchia' | 'green' | 'lime' | 'olive'
 *                  | 'yellow' | 'navy' | 'blue' | 'teal' | 'aqua'
 * </pre>
 *
 * The above captures the verbal description of the CSS 2 specification
 * for <a href="http://www.w3.org/TR/REC-CSS2/syndata.html#color-units">CSS 2
 * Color Values</a>.
 *
 * @version $Id: ColorParser.java,v 1.2 2006/04/21 06:40:26 st125089 Exp $
 */
public class ColorParser extends NumberParser implements SVGConstants {
    /**
     * Contains the 16 predefined XHTML color values supported by
     * SVG Tiny
     * @see <a href="http://www.w3.org/TR/SVGMobile/#sec-data-types">SVG
     *      Tiny Data Types</a>
     */
    private static Hashtable predefinedCssColors = new Hashtable();

    /**
     * Initialize the predefined color map
     * @see #predefinedCssColors
     */
    static {
        predefinedCssColors.put(CSS_BLACK_VALUE,
                                RGB.black);
        predefinedCssColors.put(CSS_SILVER_VALUE,
                                new RGB(192, 192, 192));
        predefinedCssColors.put(CSS_GRAY_VALUE,
                                new RGB(128, 128, 128));
        predefinedCssColors.put(CSS_WHITE_VALUE,
                                RGB.white);
        predefinedCssColors.put(CSS_MAROON_VALUE,
                                new RGB(128, 0, 0));
        predefinedCssColors.put(CSS_RED_VALUE,
                                RGB.red);
        predefinedCssColors.put(CSS_PURPLE_VALUE,
                                new RGB(128, 0, 128));
        predefinedCssColors.put(CSS_FUCHSIA_VALUE,
                                new RGB(255, 0, 255));
        predefinedCssColors.put(CSS_GREEN_VALUE,
                                new RGB(0, 128, 0));
        predefinedCssColors.put(CSS_LIME_VALUE,
                                RGB.green);
        predefinedCssColors.put(CSS_OLIVE_VALUE,
                                new RGB(128, 128, 0));
        predefinedCssColors.put(CSS_YELLOW_VALUE,
                                RGB.yellow);
        predefinedCssColors.put(CSS_NAVY_VALUE,
                                new RGB(0, 0, 128));
        predefinedCssColors.put(CSS_BLUE_VALUE,
                                RGB.blue);
        predefinedCssColors.put(CSS_TEAL_VALUE,
                                new RGB(0, 128, 128));
        predefinedCssColors.put(CSS_AQUA_VALUE,
                                new RGB(0, 255, 255));
    }

    /**
     * Converts the input value to a Color object. See the class documentation
     * for a description of the expected input syntax.
     *
     * @param colorString Should be a string which is not null, not empty and
     *              interned. If the input value does not meet these
     *              criterias, the behavior is unspecified. An
     *              IllegalArgumentException exception is thrown if
     *              the input value's syntax does not conform to the color
     *              syntax for SVG Tiny.
     * @return A <code>Color</code> object corresponding to the input string
     *         value.
     */
    public RGB parseColor(final String colorString) {
        setString(colorString);
        current = read();
        RGB c = null;

        if (current == '#') {
            c = parseHexCSSColor();
        } else if (current == 'r'
                   &&
                   (current = read()) == 'g'
                   &&
                   (current = read()) == 'b'
                   &&
                   (current = read()) == '(') {
            c = parseRgbCSSColor();
        } else if (current == 'n'
                   &&
                   (current = read()) == 'o'
                   &&
                   (current = read()) == 'n'
                   &&
                   (current = read()) == 'e') {
            return null;
        } else {
            c = (RGB) predefinedCssColors.get(s);
        }

        if (c == null) {
            throw new IllegalArgumentException();
        }

        return c;
    }

    /**
     * This helper method assumes the input 's' value is:
     * - trimmed, interned and lower case
     * - not 'inherit' nor 'none' nor 'currentColor'
     * - starts with '#' (it is a CSS Hex value)
     *
     * @return parsed <code>Color</code> value.
     */
    private RGB parseHexCSSColor() {
        if (s.length() == 4) {
            int r = hexCharToInt(s.charAt(1));
            int g = hexCharToInt(s.charAt(2));
            int b = hexCharToInt(s.charAt(3));
            return new RGB(r << 4 | r,
                             g << 4 | g,
                             b << 4 | b);
        } else if (s.length() == 7) {
            int r = hexCharToInt(s.charAt(1));
            int r2 = hexCharToInt(s.charAt(2));
            int g = hexCharToInt(s.charAt(3));
            int g2 = hexCharToInt(s.charAt(4));
            int b = hexCharToInt(s.charAt(5));
            int b2 = hexCharToInt(s.charAt(6));
            return new RGB(r << 4 | r2,
                             g << 4 | g2,
                             b << 4 | b2);
        }

        throw new IllegalArgumentException();
    }

    /**
     * Converts an hex character to its integer value
     *
     * @param c the character to be mapped to an int value.
     * @return int value corresponding to the input 'char' value.
     */
    static int hexCharToInt(final char c) {
        switch(c) {
        case '0': return 0;
        case '1': return 1;
        case '2': return 2;
        case '3': return 3;
        case '4': return 4;
        case '5': return 5;
        case '6': return 6;
        case '7': return 7;
        case '8': return 8;
        case '9': return 9;
        case 'a': case 'A': return 0xA;
        case 'b': case 'B': return 0xB;
        case 'c': case 'C': return 0xC;
        case 'd': case 'D': return 0xD;
        case 'e': case 'E': return 0xE;
        case 'f': case 'F': return 0xF;
        default:
            throw new IllegalArgumentException();
        }
    }

    /**
     * This helper method assumes the input value
     * starts with 'rgb(' (it is a CSS rgb() function value)
     *
     * @return parsed <code>Color</code> value
     */
    private RGB parseRgbCSSColor() {
        // Read the next character to move
        // to the first char after 'rgb('
        current = read();

        // Parse the red component
        float r = parseNumber();
        boolean isPercent = false;

        if (current == '%') {
            isPercent = true;
            current = read();
        }

        skipSpacesCommaSpaces();

        float g = parseNumber();

        if (current == '%') {
            if (!isPercent) {
                throw new IllegalArgumentException();
            }
            current = read();
        } else {
            if (isPercent) {
                throw new IllegalArgumentException();
            }
        }

        skipSpacesCommaSpaces();

        float b = parseNumber();

        if (current == '%') {
            if (!isPercent) {
                throw new IllegalArgumentException();
            }
            current = read();
        } else {
            if (isPercent) {
                throw new IllegalArgumentException();
            }
        }

        skipSpaces();
        if (current != ')') {
            String msg = ">";
            if (current == -1) {
                msg += "-1";
            } else {
                msg += ((char) current) + "< " + r + " " + g + " " + b;
            }
            throw new IllegalArgumentException (msg);
        }

        if (isPercent) {
            r = r < 0 ? 0 : r;
            r = r > 100 ? 100 : r;
            g = g < 0 ? 0 : g;
            g = g > 100 ? 100 : g;
            b = b < 0 ? 0 : b;
            b = b > 100 ? 100 : b;

            r = MathSupport.round(r * 2.55f);
            g = MathSupport.round(g * 2.55f);
            b = MathSupport.round(b * 2.55f);

            return new RGB((int) r, (int) g, (int) b);
        } else {
            r = r < 0 ? 0 : r;
            r = r > 255 ? 255 : r;
            g = g < 0 ? 0 : g;
            g = g > 255 ? 255 : g;
            b = b < 0 ? 0 : b;
            b = b > 255 ? 255 : b;

            return new RGB((int) r, (int) g, (int) b);
        }
    }
}