001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.perseus.parser;
028:
029: import com.sun.perseus.platform.MathSupport;
030:
031: import java.util.Hashtable;
032:
033: import com.sun.perseus.util.SVGConstants;
034:
035: import com.sun.perseus.j2d.RGB;
036:
037: /**
038: * The <code>ColorParser</code> class parses CSS 2 color values as
039: * defined in the SVG specification for SVG Tiny:<br />
040: * <pre>
041: * color: hex3Color | hex6Color | rgbColor | predefinedColor
042: * hex3Color: '#' hexDigit hexDigit hexDigit
043: * hexDigit: 0|1|2|3|4|5|6|7|8|9|A|a|B|b|C|c|D|d|E|e|F|f
044: * hex6Color: '#' hexDigit hexDigit hexDigi hexDigit hexDigit hexDigit
045: * rgbColor: rgbColorInt | rgbColorPercent
046: * rgbColorInt: rgbPrefix wsp* colInt wsp* comma wsp* colInt wsp*
047: * comma wsp* colInt wsp* rgbSuffix
048: * rgbPrefix: 'rgb('
049: * wsp: ' '
050: * comma: ','
051: * colInt: int[0-255]
052: * rgbColorPercent: rgbPrefix wsp* colPct wsp* comma wsp* colPct wsp*
053: * comma wsp* colPct wsp* rgbSuffix
054: * colPct: pctInt '%'
055: * pctInt: int[0-100]
056: * predefinedColor: 'black' | 'silver' | 'gray' | 'white' | 'maroon' | 'red'
057: * | 'purple' | 'fuchia' | 'green' | 'lime' | 'olive'
058: * | 'yellow' | 'navy' | 'blue' | 'teal' | 'aqua'
059: * </pre>
060: *
061: * The above captures the verbal description of the CSS 2 specification
062: * for <a href="http://www.w3.org/TR/REC-CSS2/syndata.html#color-units">CSS 2
063: * Color Values</a>.
064: *
065: * @version $Id: ColorParser.java,v 1.2 2006/04/21 06:40:26 st125089 Exp $
066: */
067: public class ColorParser extends NumberParser implements SVGConstants {
068: /**
069: * Contains the 16 predefined XHTML color values supported by
070: * SVG Tiny
071: * @see <a href="http://www.w3.org/TR/SVGMobile/#sec-data-types">SVG
072: * Tiny Data Types</a>
073: */
074: private static Hashtable predefinedCssColors = new Hashtable();
075:
076: /**
077: * Initialize the predefined color map
078: * @see #predefinedCssColors
079: */
080: static {
081: predefinedCssColors.put(CSS_BLACK_VALUE, RGB.black);
082: predefinedCssColors.put(CSS_SILVER_VALUE,
083: new RGB(192, 192, 192));
084: predefinedCssColors.put(CSS_GRAY_VALUE, new RGB(128, 128, 128));
085: predefinedCssColors.put(CSS_WHITE_VALUE, RGB.white);
086: predefinedCssColors.put(CSS_MAROON_VALUE, new RGB(128, 0, 0));
087: predefinedCssColors.put(CSS_RED_VALUE, RGB.red);
088: predefinedCssColors.put(CSS_PURPLE_VALUE, new RGB(128, 0, 128));
089: predefinedCssColors
090: .put(CSS_FUCHSIA_VALUE, new RGB(255, 0, 255));
091: predefinedCssColors.put(CSS_GREEN_VALUE, new RGB(0, 128, 0));
092: predefinedCssColors.put(CSS_LIME_VALUE, RGB.green);
093: predefinedCssColors.put(CSS_OLIVE_VALUE, new RGB(128, 128, 0));
094: predefinedCssColors.put(CSS_YELLOW_VALUE, RGB.yellow);
095: predefinedCssColors.put(CSS_NAVY_VALUE, new RGB(0, 0, 128));
096: predefinedCssColors.put(CSS_BLUE_VALUE, RGB.blue);
097: predefinedCssColors.put(CSS_TEAL_VALUE, new RGB(0, 128, 128));
098: predefinedCssColors.put(CSS_AQUA_VALUE, new RGB(0, 255, 255));
099: }
100:
101: /**
102: * Converts the input value to a Color object. See the class documentation
103: * for a description of the expected input syntax.
104: *
105: * @param colorString Should be a string which is not null, not empty and
106: * interned. If the input value does not meet these
107: * criterias, the behavior is unspecified. An
108: * IllegalArgumentException exception is thrown if
109: * the input value's syntax does not conform to the color
110: * syntax for SVG Tiny.
111: * @return A <code>Color</code> object corresponding to the input string
112: * value.
113: */
114: public RGB parseColor(final String colorString) {
115: setString(colorString);
116: current = read();
117: RGB c = null;
118:
119: if (current == '#') {
120: c = parseHexCSSColor();
121: } else if (current == 'r' && (current = read()) == 'g'
122: && (current = read()) == 'b'
123: && (current = read()) == '(') {
124: c = parseRgbCSSColor();
125: } else if (current == 'n' && (current = read()) == 'o'
126: && (current = read()) == 'n'
127: && (current = read()) == 'e') {
128: return null;
129: } else {
130: c = (RGB) predefinedCssColors.get(s);
131: }
132:
133: if (c == null) {
134: throw new IllegalArgumentException();
135: }
136:
137: return c;
138: }
139:
140: /**
141: * This helper method assumes the input 's' value is:
142: * - trimmed, interned and lower case
143: * - not 'inherit' nor 'none' nor 'currentColor'
144: * - starts with '#' (it is a CSS Hex value)
145: *
146: * @return parsed <code>Color</code> value.
147: */
148: private RGB parseHexCSSColor() {
149: if (s.length() == 4) {
150: int r = hexCharToInt(s.charAt(1));
151: int g = hexCharToInt(s.charAt(2));
152: int b = hexCharToInt(s.charAt(3));
153: return new RGB(r << 4 | r, g << 4 | g, b << 4 | b);
154: } else if (s.length() == 7) {
155: int r = hexCharToInt(s.charAt(1));
156: int r2 = hexCharToInt(s.charAt(2));
157: int g = hexCharToInt(s.charAt(3));
158: int g2 = hexCharToInt(s.charAt(4));
159: int b = hexCharToInt(s.charAt(5));
160: int b2 = hexCharToInt(s.charAt(6));
161: return new RGB(r << 4 | r2, g << 4 | g2, b << 4 | b2);
162: }
163:
164: throw new IllegalArgumentException();
165: }
166:
167: /**
168: * Converts an hex character to its integer value
169: *
170: * @param c the character to be mapped to an int value.
171: * @return int value corresponding to the input 'char' value.
172: */
173: static int hexCharToInt(final char c) {
174: switch (c) {
175: case '0':
176: return 0;
177: case '1':
178: return 1;
179: case '2':
180: return 2;
181: case '3':
182: return 3;
183: case '4':
184: return 4;
185: case '5':
186: return 5;
187: case '6':
188: return 6;
189: case '7':
190: return 7;
191: case '8':
192: return 8;
193: case '9':
194: return 9;
195: case 'a':
196: case 'A':
197: return 0xA;
198: case 'b':
199: case 'B':
200: return 0xB;
201: case 'c':
202: case 'C':
203: return 0xC;
204: case 'd':
205: case 'D':
206: return 0xD;
207: case 'e':
208: case 'E':
209: return 0xE;
210: case 'f':
211: case 'F':
212: return 0xF;
213: default:
214: throw new IllegalArgumentException();
215: }
216: }
217:
218: /**
219: * This helper method assumes the input value
220: * starts with 'rgb(' (it is a CSS rgb() function value)
221: *
222: * @return parsed <code>Color</code> value
223: */
224: private RGB parseRgbCSSColor() {
225: // Read the next character to move
226: // to the first char after 'rgb('
227: current = read();
228:
229: // Parse the red component
230: float r = parseNumber();
231: boolean isPercent = false;
232:
233: if (current == '%') {
234: isPercent = true;
235: current = read();
236: }
237:
238: skipSpacesCommaSpaces();
239:
240: float g = parseNumber();
241:
242: if (current == '%') {
243: if (!isPercent) {
244: throw new IllegalArgumentException();
245: }
246: current = read();
247: } else {
248: if (isPercent) {
249: throw new IllegalArgumentException();
250: }
251: }
252:
253: skipSpacesCommaSpaces();
254:
255: float b = parseNumber();
256:
257: if (current == '%') {
258: if (!isPercent) {
259: throw new IllegalArgumentException();
260: }
261: current = read();
262: } else {
263: if (isPercent) {
264: throw new IllegalArgumentException();
265: }
266: }
267:
268: skipSpaces();
269: if (current != ')') {
270: String msg = ">";
271: if (current == -1) {
272: msg += "-1";
273: } else {
274: msg += ((char) current) + "< " + r + " " + g + " " + b;
275: }
276: throw new IllegalArgumentException(msg);
277: }
278:
279: if (isPercent) {
280: r = r < 0 ? 0 : r;
281: r = r > 100 ? 100 : r;
282: g = g < 0 ? 0 : g;
283: g = g > 100 ? 100 : g;
284: b = b < 0 ? 0 : b;
285: b = b > 100 ? 100 : b;
286:
287: r = MathSupport.round(r * 2.55f);
288: g = MathSupport.round(g * 2.55f);
289: b = MathSupport.round(b * 2.55f);
290:
291: return new RGB((int) r, (int) g, (int) b);
292: } else {
293: r = r < 0 ? 0 : r;
294: r = r > 255 ? 255 : r;
295: g = g < 0 ? 0 : g;
296: g = g > 255 ? 255 : g;
297: b = b < 0 ? 0 : b;
298: b = b > 255 ? 255 : b;
299:
300: return new RGB((int) r, (int) g, (int) b);
301: }
302: }
303: }
|