summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/javax/swing/text/html/css
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/gnu/javax/swing/text/html/css')
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/BorderStyle.java64
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/BorderWidth.java78
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSColor.java170
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSLexicalException.java60
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSParser.java503
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSParserCallback.java81
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSParserException.java62
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/CSSScanner.java718
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/FontSize.java273
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/FontStyle.java80
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/FontWeight.java84
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/Length.java283
-rw-r--r--libjava/classpath/gnu/javax/swing/text/html/css/Selector.java245
13 files changed, 2701 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/BorderStyle.java b/libjava/classpath/gnu/javax/swing/text/html/css/BorderStyle.java
new file mode 100644
index 000000000..3ccd38491
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/BorderStyle.java
@@ -0,0 +1,64 @@
+/* BorderStyle.java -- Utility for dealing with border styles
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+/**
+ * Utility class for handling border styles.
+ */
+public class BorderStyle
+{
+
+ /**
+ * Determines if a given value makes up a valid border style value.
+ *
+ * @param value the value to check
+ *
+ * @return <code>true</code> when this is a valid border style,
+ * <code>false</code> otherwise
+ */
+ public static boolean isValidStyle(String value)
+ {
+ return value.equals("none") || value.equals("hidden")
+ || value.equals("dotted") || value.equals("dashed")
+ || value.equals("solid") || value.equals("double")
+ || value.equals("groove") || value.equals("ridge")
+ || value.equals("inset") || value.equals("outset");
+
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/BorderWidth.java b/libjava/classpath/gnu/javax/swing/text/html/css/BorderWidth.java
new file mode 100644
index 000000000..ae64c2110
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/BorderWidth.java
@@ -0,0 +1,78 @@
+/* BorderWidth.java -- A CSS metric for border widths
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+/**
+ * A special CSS metric for border widths. It basically understands everything
+ * as Length, and in addition to that provides a mapping for the border-width's
+ * thin, medium and think values.
+ */
+public class BorderWidth
+ extends Length
+{
+
+ /**
+ * Creates a new BorderWidth instance.
+ *
+ * @param val the CSS value to be interpreted
+ */
+ public BorderWidth(String val)
+ {
+ super(val);
+ if (val.equals("thin"))
+ floatValue = 1.F;
+ else if (val.equals("medium"))
+ floatValue = 2.F;
+ else if (val.equals("thick"))
+ floatValue = 3.F;
+ }
+
+ /**
+ * Checks if the specified value makes up a valid border-width value.
+ *
+ * @param value the value to check
+ *
+ * @return <code>true</code> if the value is a valid border-width
+ */
+ public static boolean isValid(String value)
+ {
+ return value.equals("thin") || value.equals("medium")
+ || value.equals("thick") || Length.isValid(value);
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSColor.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSColor.java
new file mode 100644
index 000000000..ea4b94ae0
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSColor.java
@@ -0,0 +1,170 @@
+/* CSSColor.java -- Converts CSS color values
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Converts CSS color values into AWT Color values.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class CSSColor
+{
+
+ private static final HashMap COLOR_MAP;
+ static
+ {
+ COLOR_MAP = new HashMap();
+ COLOR_MAP.put("maroon", "#800000");
+ COLOR_MAP.put("red", "#ff0000");
+ COLOR_MAP.put("orange", "#ffa500");
+ COLOR_MAP.put("yellow", "#ffff00");
+ COLOR_MAP.put("olive", "#808000");
+ COLOR_MAP.put("purple", "#800080");
+ COLOR_MAP.put("fuchsia", "#ff00ff");
+ COLOR_MAP.put("white", "#ffffff");
+ COLOR_MAP.put("lime", "#00ff00");
+ COLOR_MAP.put("green", "#008000");
+ COLOR_MAP.put("navy", "#000080");
+ COLOR_MAP.put("blue", "#0000ff");
+ COLOR_MAP.put("aqua", "#00ffff");
+ COLOR_MAP.put("teal", "#008080");
+ COLOR_MAP.put("black", "#000000");
+ COLOR_MAP.put("silver", "#c0c0c0");
+ COLOR_MAP.put("gray", "#808080");
+ }
+
+ /**
+ * The CSS value.
+ */
+ private String value;
+
+ /**
+ * The converted color.
+ */
+ private Color color;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param val the CSS value
+ */
+ public CSSColor(String val)
+ {
+ value = val;
+ color = convertValue(value);
+ }
+
+ /**
+ * Converts a CSS color value to an AWT color.
+ *
+ * @param value the CSS color value
+ *
+ * @return the converted color value
+ */
+ public static Color convertValue(String value)
+ {
+ Color color;
+ String val1 = value.toLowerCase();
+ if (val1.charAt(0) != '#')
+ val1 = (String) COLOR_MAP.get(val1);
+ if (val1 != null)
+ {
+ String hexVal = val1.substring(1).trim();
+ try
+ {
+ int rgb = Integer.parseInt(hexVal, 16);
+ color = new Color(rgb);
+ }
+ catch (NumberFormatException ex)
+ {
+ color = Color.BLACK;
+ }
+ }
+ else
+ color = null;
+ return color;
+ }
+
+ /**
+ * Returns the converted color.
+ *
+ * @return the converted color
+ */
+ public Color getValue()
+ {
+ return color;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified value is a valid color value,
+ * <code>false</code> otherwise.
+ *
+ * @param val the value to check
+ *
+ * @return <code>true</code> if the specified value is a valid color value,
+ * <code>false</code> otherwise
+ */
+ public static boolean isValidColor(String val)
+ {
+ boolean ret = false;
+ if (val.charAt(0) == '#')
+ ret = true;
+ else
+ {
+ Set colors = COLOR_MAP.keySet();
+ for (Iterator i = colors.iterator(); i.hasNext() && ret == false;)
+ {
+ String color = (String) i.next();
+ if (color.equalsIgnoreCase(val))
+ ret = true;
+ }
+ }
+ return ret;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSLexicalException.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSLexicalException.java
new file mode 100644
index 000000000..13968e4d2
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSLexicalException.java
@@ -0,0 +1,60 @@
+/* CSSLexicalException.java -- Indicates a failure in the lexical analyser
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.io.IOException;
+
+/**
+ * Indicates a failure in the lexical analyser of the CSS parser.
+ */
+public class CSSLexicalException
+ extends IOException
+{
+
+ public CSSLexicalException()
+ {
+ super();
+ }
+
+ public CSSLexicalException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSParser.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParser.java
new file mode 100644
index 000000000..4be315e37
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParser.java
@@ -0,0 +1,503 @@
+/* CSSParser.java -- A parser for CSS stylesheets
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.StringTokenizer;
+
+/**
+ * A parser for CSS stylesheets.
+ *
+ * This parser is based on the simple CSS grammar describe in
+ *
+ * http://www.w3.org/TR/CSS21/syndata.html .
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+// TODO: Maybe use more restrictive grammar:
+// http://www.w3.org/TR/CSS21/grammar.html#q1
+public class CSSParser
+{
+
+ /**
+ * The scanner used to read the input streams into more usable tokens.
+ */
+ private CSSScanner scanner;
+
+ /**
+ * The parser callback.
+ */
+ private CSSParserCallback callback;
+
+ /**
+ * One lookahead token.
+ */
+ private int lookahead;
+
+ /**
+ * The parse error.
+ */
+ private String error;
+
+ /**
+ * Creates a new CSSParser that parses the specified input.
+ *
+ * @param in the source to parse
+ */
+ public CSSParser(Reader in, CSSParserCallback cb)
+ {
+ scanner = new CSSScanner(in);
+ callback = cb;
+ lookahead = -1;
+ }
+
+ /**
+ * Parses the input source specified in the constructor.
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ public void parse()
+ throws IOException
+ {
+ boolean success = parseStylesheet();
+ if (! success)
+ {
+ throw new CSSParserException(error);
+ }
+ }
+
+ /**
+ * Parses a stylesheet.
+ *
+ * @return <code>true</code> if the stylesheet could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseStylesheet()
+ throws IOException
+ {
+ int token = peekToken();
+ while (token != CSSScanner.EOF && (token == CSSScanner.CDC
+ || token == CSSScanner.CDO || token == CSSScanner.S
+ || parseStatement()))
+ {
+ if (token == CSSScanner.CDC || token == CSSScanner.CDO
+ || token == CSSScanner.S)
+ readToken();
+ token = peekToken();
+ }
+ // Last token must be EOF for valid stylesheets, I'd think.
+ return token == CSSScanner.EOF;
+ }
+
+ /**
+ * Parses a CSS statement.
+ * @return <code>true</code> if the stylesheet could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseStatement()
+ throws IOException
+ {
+ return parseRuleset() || parseAtRule();
+ }
+
+ /**
+ * Parses a CSS rule set.
+ *
+ * @return <code>true</code> if the ruleset could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseRuleset()
+ throws IOException
+ {
+ StringBuilder selector = new StringBuilder();
+ parseSelector(selector);
+ StringTokenizer selSplitter =
+ new StringTokenizer(selector.toString(), ",");
+ Selector[] sels = new Selector[selSplitter.countTokens()];
+ for (int i = 0; selSplitter.hasMoreTokens(); i++)
+ {
+ String sel = selSplitter.nextToken().trim();
+ sels[i] = new Selector(sel);
+ }
+ callback.startStatement(sels);
+ // Read any number of whitespace.
+ int token;
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+ boolean ret = true;
+
+ if (token == CSSScanner.CURLY_LEFT)
+ {
+ // Read any number of whitespace.
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+ lookahead = token;
+
+ // Maybe read declaration.
+ boolean decl = parseDeclaration();
+ token = peekToken();
+ while (token == CSSScanner.SEMICOLON)
+ {
+ readToken(); // Read the semicolon.
+ // Read any number of whitespace.
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+ lookahead = token;
+
+ // Maybe read declaration.
+ parseDeclaration();
+ token = peekToken();
+ }
+ if (token != CSSScanner.CURLY_RIGHT)
+ {
+ error = "Expected right curly brace";
+ ret = false;
+ }
+ else
+ {
+ readToken();
+ // Read any number of whitespace.
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+ lookahead = token;
+ callback.endStatement();
+ }
+ }
+ else
+ {
+ ret = false;
+ error = "Expected left curly brace";
+ }
+ return ret;
+ }
+
+ /**
+ * Parses a CSS declaration.
+ *
+ * @return <code>true</code> if the ruleset could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseDeclaration()
+ throws IOException
+ {
+ // Maybe fetch one DELIM.
+ int token = readToken();
+ if (token == CSSScanner.DELIM)
+ token = readToken();
+
+ boolean ret = true;
+
+ // Parse property
+ String property = null;
+ if (token == CSSScanner.IDENT)
+ {
+ property = new String(scanner.parseBuffer, 0, scanner.tokenEnd);
+ // Read any number of whitespace.
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+
+ // Read ':'.
+ if (token == CSSScanner.DELIM && scanner.parseBuffer[0] == ':')
+ {
+ // Read any number of whitespace.
+ do
+ {
+ token = readToken();
+ } while (token == CSSScanner.S);
+ lookahead = token;
+
+ StringBuilder value = new StringBuilder();
+ if (parseValue(value))
+ {
+ callback.declaration(property, value.toString().trim());
+ }
+ else
+ {
+ ret = false;
+ error = "Error while reading the property value";
+ }
+ }
+ else
+ {
+ ret = false;
+ error = "Expected colon to separate property and value";
+ }
+
+ }
+ else
+ {
+ lookahead = token;
+ ret = false;
+ error = "Expected IDENT token for property";
+ }
+ return ret;
+ }
+
+ /**
+ * Parses a property value.
+ *
+ * @param s the string builder to read the value into
+ *
+ * @return <code>true</code> if the ruleset could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseValue(StringBuilder s)
+ throws IOException
+ {
+ // FIXME: Handle block and ATKEYWORD.
+ boolean success = parseAny(s);
+ while (parseAny(s))
+ ;
+
+ return success;
+ }
+
+ /**
+ * Parses a selector.
+ *
+ * @param sel the string buffer to put the selector into
+ *
+ * @return <code>true</code> if the ruleset could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseSelector(StringBuilder sel)
+ throws IOException
+ {
+ // At least one any needs to be parsed.
+ boolean ret = parseAny(sel);
+ if (ret)
+ {
+ while (parseAny(sel))
+ ;
+ }
+ return ret;
+ }
+
+ /**
+ * Parses the any rule. If s is not null, then the contents of the
+ * tokens is appended verbatim.
+ *
+ * @param s the string builder to append to
+ *
+ * @return <code>true</code> if the ruleset could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseAny(StringBuilder s)
+ throws IOException
+ {
+ int token = peekToken();
+ boolean ret = false;
+ if (token == CSSScanner.IDENT || token == CSSScanner.NUMBER
+ || token == CSSScanner.PERCENTAGE || token == CSSScanner.DIMENSION
+ || token == CSSScanner.STRING || token == CSSScanner.DELIM
+ || token == CSSScanner.URI || token == CSSScanner.HASH
+ || token == CSSScanner.UNICODE_RANGE || token == CSSScanner.INCLUDES
+ || token == CSSScanner.DASHMATCH)
+ {
+ if (s != null)
+ s.append(scanner.parseBuffer, 0, scanner.tokenEnd);
+ readToken();
+ ret = true;
+ }
+ else if (token == CSSScanner.FUNCTION)
+ System.err.println("Implement parseAny for FUNCTION");
+ else if (token == CSSScanner.PAREN_LEFT)
+ System.err.println("Implement parseAny for (");
+ else if (token == CSSScanner.BRACE_LEFT)
+ System.err.println("Implement parseAny for [");
+
+ // Parse any following whitespace too.
+ token = peekToken();
+ while (token == CSSScanner.S)
+ {
+ if (s != null)
+ s.append(scanner.parseBuffer, 0, scanner.tokenEnd);
+ readToken();
+ token = peekToken();
+ }
+ return ret;
+ }
+
+ /**
+ * Parses a CSS at-rule.
+ *
+ * @return <code>true</code> if the at-rule could be parsed successfully,
+ * <code>false</code> otherwise
+ *
+ * @throws IOException if an IO or parse error occurs
+ */
+ private boolean parseAtRule()
+ throws IOException
+ {
+ // FIXME: Implement.
+ return false;
+ }
+
+ /**
+ * Reads the next token, and skips the comments.
+ *
+ * @return the next non-comment token
+ */
+ private int readToken()
+ throws IOException
+ {
+ int token;
+ if (lookahead == -1)
+ {
+ do
+ {
+ token = scanner.nextToken();
+ } while (token == CSSScanner.COMMENT);
+ }
+ else
+ {
+ token = lookahead;
+ lookahead = -1;
+ }
+ return token;
+ }
+
+ /**
+ * Returns the next token to be read, without really reading it. The next
+ * call to readToken() will return the same token again.
+ *
+ * @return the next token to be read, without really reading it
+ */
+ private int peekToken()
+ throws IOException
+ {
+ int token;
+ if (lookahead == -1)
+ {
+ do
+ {
+ token = scanner.nextToken();
+ } while (token == CSSScanner.COMMENT);
+ lookahead = token;
+ }
+ else
+ token = lookahead;
+ return token;
+ }
+
+ /**
+ * For testing, we read in the default.css in javax/swing/text/html
+ *
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ InputStream in;
+ if (args.length > 0)
+ {
+ File file = new File(args[0]);
+ in = new FileInputStream(file);
+ }
+ else
+ {
+ String name = "/javax/swing/text/html/default.css";
+ in = CSSScanner.class.getResourceAsStream(name);
+ }
+ BufferedInputStream bin = new BufferedInputStream(in);
+ InputStreamReader r = new InputStreamReader(bin);
+ CSSParserCallback cb = new CSSParserCallback()
+ {
+ public void startStatement(Selector[] selector)
+ {
+ System.out.print("startStatement: ");
+ for (int i = 0; i < selector.length; i++)
+ {
+ System.out.print(selector[i]);
+ if (i < selector.length - 1)
+ System.out.print(',');
+ else
+ System.out.println();
+ }
+ }
+ public void endStatement()
+ {
+ System.out.println("endStatement");
+ }
+ public void declaration(String property, String value)
+ {
+ System.out.println("declaration: " + property + ", " + value);
+ }
+ };
+ CSSParser p = new CSSParser(r, cb);
+ p.parse();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserCallback.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserCallback.java
new file mode 100644
index 000000000..f49ffa232
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserCallback.java
@@ -0,0 +1,81 @@
+/* CSSParserCallback.java -- Callback for parsing CSS
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+/**
+ * Defines the callback that is used by the CSSParser to notify the
+ * backend of the parsing process.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public interface CSSParserCallback
+{
+
+ /**
+ * Signals the beginning of a statement.
+ *
+ * A CSS statement is build up like follows:
+ * <pre>
+ * <selector> {
+ * ... declarations...
+ * }
+ * </pre>
+ *
+ * After startStatement(), the callback will receive zero to n callbacks
+ * to declaration, followed by an endStatement() call.
+ *
+ * @param selector the selector of the statement.
+ */
+ void startStatement(Selector[] selector);
+
+ /**
+ * Signals the end of a statement.
+ */
+ void endStatement();
+
+ /**
+ * Signals the parsing of one declaration, which defines a mapping
+ * from a property to a value.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ void declaration(String property, String value);
+
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserException.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserException.java
new file mode 100644
index 000000000..2328d5398
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSParserException.java
@@ -0,0 +1,62 @@
+/* CSSParserException.java -- The CSS parser exception
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.io.IOException;
+
+/**
+ * This exception is raised when the CSS parser hits a syntax error.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class CSSParserException
+ extends IOException
+{
+
+ /**
+ * Creates a new CSSParserException.
+ *
+ * @param message the exception message
+ */
+ public CSSParserException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/CSSScanner.java b/libjava/classpath/gnu/javax/swing/text/html/css/CSSScanner.java
new file mode 100644
index 000000000..37d544641
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/CSSScanner.java
@@ -0,0 +1,718 @@
+/* CSSScanner.java -- A parser for CSS stylesheets
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * A tokenizer for CSS stylesheets. This is based on the scanner definition
+ * from:
+ *
+ * http://www.w3.org/TR/CSS21/syndata.html#tokenization
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+// TODO: Maybe implement more restrictive scanner:
+// http://www.w3.org/TR/CSS21/grammar.html#q2
+class CSSScanner
+{
+
+ // The tokens. This list is taken from:
+ // http://www.w3.org/TR/CSS21/syndata.html#tokenization
+ static final int IDENT = 1;
+ static final int ATKEYWORD = 2;
+ static final int STRING = 3;
+ static final int INVALID = 4;
+ static final int HASH = 5;
+ static final int NUMBER = 6;
+ static final int PERCENTAGE = 7;
+ static final int DIMENSION = 8;
+ static final int URI = 9;
+ static final int UNICODE_RANGE = 10;
+ static final int CDO = 11;
+ static final int CDC = 12;
+ static final int SEMICOLON = 13;
+ static final int CURLY_LEFT = 14;
+ static final int CURLY_RIGHT = 15;
+ static final int PAREN_LEFT = 16;
+ static final int PAREN_RIGHT = 17;
+ static final int BRACE_LEFT = 16;
+ static final int BRACE_RIGHT = 17;
+ static final int S = 18;
+ static final int COMMENT = 19;
+ static final int FUNCTION = 20;
+ static final int INCLUDES = 21;
+ static final int DASHMATCH = 22;
+ static final int DELIM = 23;
+
+ // Additional tokens defined for convenience.
+ static final int EOF = -1;
+
+ /**
+ * The input source.
+ */
+ private Reader in;
+
+ /**
+ * The parse buffer.
+ */
+ char[] parseBuffer;
+
+ /**
+ * The end index in the parseBuffer of the current token.
+ */
+ int tokenEnd;
+
+ /**
+ * The lookahead 'buffer'.
+ */
+ private int[] lookahead;
+
+ CSSScanner(Reader r)
+ {
+ lookahead = new int[2];
+ lookahead[0] = -1;
+ lookahead[1] = -1;
+ parseBuffer = new char[2048];
+ in = r;
+ }
+
+ /**
+ * Fetches the next token. The actual character data is in the parseBuffer
+ * afterwards with the tokenStart at index 0 and the tokenEnd field
+ * pointing to the end of the token.
+ *
+ * @return the next token
+ */
+ int nextToken()
+ throws IOException
+ {
+ tokenEnd = 0;
+ int token = -1;
+ int next = read();
+ if (next != -1)
+ {
+ switch (next)
+ {
+ case ';':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = SEMICOLON;
+ break;
+ case '{':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = CURLY_LEFT;
+ break;
+ case '}':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = CURLY_RIGHT;
+ break;
+ case '(':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = PAREN_LEFT;
+ break;
+ case ')':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = PAREN_RIGHT;
+ break;
+ case '[':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = BRACE_LEFT;
+ break;
+ case ']':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = BRACE_RIGHT;
+ break;
+ case '@':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ readIdent();
+ token = ATKEYWORD;
+ break;
+ case '#':
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ readName();
+ token = HASH;
+ break;
+ case '\'':
+ case '"':
+ lookahead[0] = next;
+ readString();
+ token = STRING;
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case '\f':
+ lookahead[0] = next;
+ readWhitespace();
+ token = S;
+ break;
+ // FIXME: Detecting an URI involves several characters lookahead.
+// case 'u':
+// lookahead[0] = ch;
+// readURI();
+// token = URI;
+// break;
+ case '<':
+ parseBuffer[0] = (char) next;
+ parseBuffer[1] = (char) read();
+ parseBuffer[2] = (char) read();
+ parseBuffer[3] = (char) read();
+ if (parseBuffer[1] == '!' && parseBuffer[2] == '-'
+ && parseBuffer[3] == '-')
+ {
+ token = CDO;
+ tokenEnd = 4;
+ }
+ else
+ throw new CSSLexicalException("expected CDO token");
+ break;
+ case '/':
+ lookahead[0] = next;
+ readComment();
+ token = COMMENT;
+ break;
+ case '~':
+ parseBuffer[0] = (char) next;
+ parseBuffer[1] = (char) read();
+ if (parseBuffer[1] == '=')
+ token = INCLUDES;
+ else
+ throw new CSSLexicalException("expected INCLUDES token");
+ break;
+ case '|':
+ parseBuffer[0] = (char) next;
+ parseBuffer[1] = (char) read();
+ if (parseBuffer[1] == '=')
+ token = DASHMATCH;
+ else
+ throw new CSSLexicalException("expected DASHMATCH token");
+ break;
+ case '-':
+ int ch2 = read();
+ if (ch2 == '-')
+ {
+ int ch3 = read();
+ if (ch3 == '>')
+ {
+ parseBuffer[0] = (char) next;
+ parseBuffer[1] = (char) ch2;
+ parseBuffer[2] = (char) ch3;
+ tokenEnd = 3;
+ token = CDC;
+ }
+ else
+ throw new CSSLexicalException("expected CDC token");
+ }
+ else
+ {
+ lookahead[0] = next;
+ lookahead[1] = ch2;
+ readIdent();
+ int ch3 = read();
+ if (ch3 == -1 || ch3 != '(')
+ {
+ lookahead[0] = ch3;
+ token = IDENT;
+ }
+ else
+ {
+ parseBuffer[tokenEnd] = (char) ch3;
+ tokenEnd++;
+ token = FUNCTION;
+ }
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ lookahead[0] = next;
+ readNum();
+ int ch3 = read();
+ if (ch3 == '%')
+ {
+ parseBuffer[tokenEnd] = (char) ch3;
+ tokenEnd++;
+ token = PERCENTAGE;
+ }
+ else if (ch3 == -1 || (! (ch3 == '_'
+ || (ch3 >= 'a' && ch3 <= 'z')
+ || (ch3 >= 'A' && ch3 <= 'Z')
+ || ch3 == '\\' || ch3 > 177)))
+ {
+ lookahead[0] = ch3;
+ token = NUMBER;
+ }
+ else
+ {
+ lookahead[0] = ch3;
+ readIdent();
+ token = DIMENSION;
+ }
+ break;
+ default:
+ // Handle IDENT that don't begin with '-'.
+ if (next == '_' || (next >= 'a' && next <= 'z')
+ || (next >= 'A' && next <= 'Z') || next == '\\' || next > 177)
+ {
+ lookahead[0] = next;
+ readIdent();
+ int ch4 = read();
+ if (ch4 == -1 || ch4 != '(')
+ {
+ lookahead[0] = ch4;
+ token = IDENT;
+ }
+ else
+ {
+ parseBuffer[tokenEnd] = (char) ch4;
+ tokenEnd++;
+ token = FUNCTION;
+ }
+ }
+ else
+ {
+ parseBuffer[0] = (char) next;
+ tokenEnd = 1;
+ token = DELIM;
+ }
+ break;
+ }
+ }
+ return token;
+ }
+
+ String currentTokenString()
+ {
+ return new String(parseBuffer, 0, tokenEnd);
+ }
+
+ /**
+ * Reads one character from the input stream or from the lookahead
+ * buffer, if it contains one character.
+ *
+ * @return the next character
+ *
+ * @throws IOException if problems occur on the input source
+ */
+ private int read()
+ throws IOException
+ {
+ int ret;
+ if (lookahead[0] != -1)
+ {
+ ret = lookahead[0];
+ lookahead[0] = -1;
+ }
+ else if (lookahead[1] != -1)
+ {
+ ret = lookahead[1];
+ lookahead[1] = -1;
+ }
+ else
+ {
+ ret = in.read();
+ }
+ return ret;
+ }
+
+ /**
+ * Reads and identifier.
+ *
+ * @throws IOException if something goes wrong in the input source or if
+ * the lexical analyser fails to read an identifier
+ */
+ private void readIdent()
+ throws IOException
+ {
+ int ch1 = read();
+ // Read possibly leading '-'.
+ if (ch1 == '-')
+ {
+ parseBuffer[tokenEnd] = (char) ch1;
+ tokenEnd++;
+ ch1 = read();
+ }
+ // What follows must be '_' or a-z or A-Z or nonascii (>177) or an
+ // escape.
+ if (ch1 == '_' || (ch1 >= 'a' && ch1 <= 'z')
+ || (ch1 >= 'A' && ch1 <= 'Z') || ch1 > 177)
+ {
+ parseBuffer[tokenEnd] = (char) ch1;
+ tokenEnd++;
+ }
+ else if (ch1 == '\\')
+ {
+ // Try to read an escape.
+ lookahead[0] = ch1;
+ readEscape();
+ }
+ else
+ throw new CSSLexicalException("First character of identifier incorrect");
+
+ // Read any number of [_a-zA-Z0-9-] chars.
+ int ch = read();
+ while (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z')
+ || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ }
+
+ // Push back last read character since it doesn't belong to the IDENT.
+ lookahead[0] = ch;
+ }
+
+ /**
+ * Reads an escape.
+ *
+ * @throws IOException if something goes wrong in the input source or if
+ * the lexical analyser fails to read an escape
+ */
+ private void readEscape()
+ throws IOException
+ {
+ int ch = read();
+ if (ch != -1 && ch == '\\')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f'))
+ {
+ // Read unicode escape.
+ // Zero to five 0-9a-f chars can follow.
+ int hexcount = 0;
+ ch = read();
+ while (((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f'))
+ && hexcount < 5)
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ hexcount++;
+ ch = read();
+ }
+ // Now we can have a \r\n or any whitespace character following.
+ if (ch == '\r')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ if (ch == '\n')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ else
+ {
+ lookahead[0] = ch;
+ }
+ }
+ else if (ch == ' ' || ch == '\n' || ch == '\f' || ch == '\t')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ else
+ {
+ lookahead[0] = ch;
+ }
+ }
+ else if (ch != '\n' && ch != '\r' && ch != '\f')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ else
+ throw new CSSLexicalException("Can't read escape");
+ }
+ else
+ throw new CSSLexicalException("Escape must start with '\\'");
+
+ }
+
+ private void readName()
+ throws IOException
+ {
+ // Read first name character.
+ int ch = read();
+ if (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z')
+ || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ else
+ throw new CSSLexicalException("Invalid name");
+
+ // Read any number (at least one) of [_a-zA-Z0-9-] chars.
+ ch = read();
+ while (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z')
+ || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ }
+
+ // Push back last read character since it doesn't belong to the IDENT.
+ lookahead[0] = ch;
+ }
+
+ /**
+ * Reads in a string.
+ *
+ * @throws IOException
+ */
+ private void readString()
+ throws IOException
+ {
+ int ch1 = read();
+ if (ch1 != -1 && (ch1 == '\'' || ch1 == '\"'))
+ {
+ parseBuffer[tokenEnd] = (char) ch1;
+ tokenEnd++;
+
+ // Read any number of chars until we hit another chc1 char.
+ // Reject newlines, except if prefixed with \.
+ int ch = read();
+ while (ch != -1 && ch != ch1)
+ {
+ // Every non-newline and non-\ char should be ok.
+ if (ch != '\n' && ch != '\r' && ch != '\f' && ch != '\\')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ // Ok when followed by newline or as part of escape.
+ else if (ch == '\\')
+ {
+ int ch2 = read();
+ if (ch2 == '\n' || ch2 == '\r')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ parseBuffer[tokenEnd + 1] = (char) ch2;
+ tokenEnd += 2;
+ }
+ else
+ {
+ // Try to parse an escape.
+ lookahead[0] = ch;
+ lookahead[1] = ch2;
+ readEscape();
+ }
+ }
+ else
+ throw new CSSLexicalException("Invalid string");
+
+ ch = read();
+ }
+ if (ch != -1)
+ {
+ // Push the final char on the buffer.
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ }
+ else
+ throw new CSSLexicalException("Unterminated string");
+ }
+ else
+ throw new CSSLexicalException("Invalid string");
+ }
+
+ /**
+ * Reads a chunk of whitespace.
+ *
+ * @throws IOException
+ */
+ private void readWhitespace()
+ throws IOException
+ {
+ int ch = read();
+ while (ch != -1 && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'
+ || ch == '\f'))
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ }
+ // Push back last character read.
+ lookahead[0] = ch;
+
+ }
+
+ private void readURI()
+ throws IOException
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Reads a comment block.
+ *
+ * @throws IOException
+ */
+ private void readComment()
+ throws IOException
+ {
+ // First we need a / and a *
+ int ch = read();
+ if (ch != -1 && ch == '/')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ if (ch != -1 && ch == '*')
+ {
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ boolean finished = false;
+ int lastChar = ch;
+ ch = read();
+ while (! finished && ch != -1)
+ {
+ if (lastChar == '*' && ch == '/')
+ finished = true;
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ lastChar = ch;
+ ch = read();
+ }
+ }
+ }
+ if (ch == -1)
+ throw new CSSLexicalException("Unterminated comment");
+
+ // Push back last character read.
+ lookahead[0] = ch;
+ }
+
+ /**
+ * Reads a number.
+ *
+ * @throws IOException
+ */
+ private void readNum()
+ throws IOException
+ {
+ boolean hadDot = false;
+ // First char must be number or .
+ int ch = read();
+ if (ch != -1 && ((ch >= '0' && ch <= '9') || ch == '.'))
+ {
+ if (ch == '.')
+ hadDot = true;
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ // Now read in any number of digits afterwards, and maybe one dot,
+ // if we hadn't one already.
+ ch = read();
+ while (ch != -1 && ((ch >= '0' && ch <= '9')
+ || (ch == '.' && ! hadDot)))
+ {
+ if (ch == '.')
+ hadDot = true;
+ parseBuffer[tokenEnd] = (char) ch;
+ tokenEnd++;
+ ch = read();
+ }
+ }
+ else
+ throw new CSSLexicalException("Invalid number");
+
+ // Check if we haven't accidentally finished with a dot.
+ if (parseBuffer[tokenEnd - 1] == '.')
+ throw new CSSLexicalException("Invalid number");
+
+ // Push back last character read.
+ lookahead[0] = ch;
+ }
+
+ /**
+ * For testing, we read in the default.css in javax/swing/text/html
+ *
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ String name = "/javax/swing/text/html/default.css";
+ InputStream in = CSSScanner.class.getResourceAsStream(name);
+ BufferedInputStream bin = new BufferedInputStream(in);
+ InputStreamReader r = new InputStreamReader(bin);
+ CSSScanner s = new CSSScanner(r);
+ int token;
+ do
+ {
+ token = s.nextToken();
+ System.out.println("token: " + token + ": "
+ + s.currentTokenString());
+ } while (token != -1);
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/FontSize.java b/libjava/classpath/gnu/javax/swing/text/html/css/FontSize.java
new file mode 100644
index 000000000..203eadc40
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/FontSize.java
@@ -0,0 +1,273 @@
+/* FontSize.java -- Converts CSS font size values into real values
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+/**
+ * Converts CSS font-size values into real (point) values.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class FontSize
+{
+
+ /**
+ * The CSS value.
+ */
+ private String value;
+
+ /**
+ * The actual font size.
+ */
+ private int size;
+
+ /**
+ * The index of one of the standard sizes that this font size maps to.
+ * This is -1 if this fontsize doesn't map to one of the standard sizes.
+ *
+ * @see #SCALE
+ */
+ private int sizeIndex;
+
+ /**
+ * True when this font size is relative.
+ */
+ private boolean isRelative;
+
+ /**
+ * The default size for 'medium' absolute size. The other absolute sizes
+ * are calculated from this.
+ */
+ public static final int DEFAULT_FONT_SIZE = 12;
+
+ /**
+ * The scaling factors relative to the medium size. Medium is at index 2.
+ */
+ private static final double[] SCALE = {0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8 };
+
+ /**
+ * Creates a new FontSize for the specified value.
+ *
+ * @param val the value to convert
+ */
+ public FontSize(String val)
+ {
+ value = val;
+ sizeIndex = -1;
+ isRelative = false;
+ size = mapValue();
+ }
+
+ /**
+ * Returns the font size value.
+ *
+ * @return the font size value
+ */
+ public int getValue(int p)
+ {
+ if (isRelative)
+ mapRelative(p);
+ return size;
+ }
+
+ public int getValue()
+ {
+ assert ! isRelative;
+ return size;
+ }
+
+ /**
+ * Returns the converted real value in point.
+ *
+ * @return the converted real value in point
+ */
+ private int mapValue()
+ {
+ int intVal;
+ if (value.contains("pt"))
+ intVal = mapPoints();
+ else if (value.contains("px"))
+ intVal = mapPixels();
+ else if (value.contains("em") || value.contains("%")
+ || value.contains("larger") || value.contains("smaller"))
+ {
+ intVal = -1;
+ isRelative = true;
+ }
+ else
+ intVal = mapAbsolute();
+ return intVal;
+ }
+
+ /**
+ * Maps point values ('XXXpt').
+ *
+ * @return the real font size
+ */
+ private int mapPoints()
+ {
+ int end = value.indexOf("pt");
+ String number = value.substring(0, end);
+ int intVal = (int) Double.parseDouble(number);
+ return intVal;
+ }
+
+ /**
+ * Maps pixel values ('XXXpx').
+ *
+ * @return the real font size
+ */
+ private int mapPixels()
+ {
+ int end = value.indexOf("px");
+ if (end == -1)
+ end = value.length();
+ String number = value.substring(0, end);
+ try
+ {
+ int intVal = (int) Double.parseDouble(number);
+ return intVal;
+ }
+ catch (NumberFormatException ex)
+ {
+ return DEFAULT_FONT_SIZE;
+ }
+ }
+
+ private int mapPercent(int par)
+ {
+ int end = value.indexOf("%");
+ if (end == -1)
+ end = value.length();
+ String number = value.substring(0, end);
+ try
+ {
+ int intVal = (int) Double.parseDouble(number);
+ return intVal * par / 100;
+ }
+ catch (NumberFormatException ex)
+ {
+ System.err.println("couldn't map value: '" + value + "'");
+ return DEFAULT_FONT_SIZE;
+ }
+ }
+
+ private int mapEM(int par)
+ {
+ int end = value.indexOf("em");
+ if (end == -1)
+ end = value.length();
+ String number = value.substring(0, end);
+ try
+ {
+ float factor = Float.parseFloat(number);
+ // FIXME: Should be relative to the parent element's size.
+ return (int) (factor * par);
+ }
+ catch (NumberFormatException ex)
+ {
+ return DEFAULT_FONT_SIZE;
+ }
+ }
+
+ private int mapSmaller(int par)
+ {
+ return (int) (par * 0.9);
+ }
+
+ private int mapLarger(int par)
+ {
+ return (int) (par * 0.9);
+ }
+
+ /**
+ * Maps absolute font-size values.
+ *
+ * @return the real value
+ */
+ private int mapAbsolute()
+ {
+ int index;
+ if (value.equals("xx-small") || value.equals("x-small"))
+ index = 0;
+ else if (value.equals("small"))
+ index = 1;
+ else if (value.equals("medium"))
+ index = 2;
+ else if (value.equals("large"))
+ index = 3;
+ else if (value.equals("x-large"))
+ index = 4;
+ else if (value.equals("xx-large"))
+ index = 5;
+ else
+ index = 2;
+ double scale = SCALE[index];
+ // FIXME: Scale the real medium size of the document, rather than the
+ // constant here.
+ int intVal = (int) (scale * DEFAULT_FONT_SIZE);
+ sizeIndex = index;
+ return intVal;
+ }
+
+ /**
+ * Returns the string representation.
+ */
+ public String toString()
+ {
+ return value;
+ }
+
+ private int mapRelative(int par)
+ {
+ if (value.indexOf('%') != -1)
+ size = mapPercent(par);
+ else if (value.indexOf("em") != -1)
+ size = mapEM(par);
+ else if (value.indexOf("larger") != -1)
+ size = mapLarger(par);
+ else if (value.indexOf("smaller") != -1)
+ size = mapSmaller(par);
+ return size;
+ }
+
+ public boolean isRelative()
+ {
+ return isRelative;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/FontStyle.java b/libjava/classpath/gnu/javax/swing/text/html/css/FontStyle.java
new file mode 100644
index 000000000..e52893193
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/FontStyle.java
@@ -0,0 +1,80 @@
+/* FontStyle.java -- Converts font-size CSS values
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.awt.Font;
+
+/**
+ * Converts font-size CSS values to a form to be used by {@link java.awt.Font}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class FontStyle
+{
+
+ /**
+ * The real value.
+ */
+ private String value;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param val the CSS value
+ */
+ public FontStyle(String val)
+ {
+ value = val;
+ }
+
+ /**
+ * Returns the converted value.
+ *
+ * @return the converted value
+ */
+ public int getValue()
+ {
+ int intVal;
+ if (value.equals("italic") || value.equals("oblique"))
+ intVal = Font.ITALIC;
+ else
+ intVal = Font.PLAIN;
+ return intVal;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/FontWeight.java b/libjava/classpath/gnu/javax/swing/text/html/css/FontWeight.java
new file mode 100644
index 000000000..d338c6f55
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/FontWeight.java
@@ -0,0 +1,84 @@
+/* FontWeight.java -- Converts font-weight values
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import java.awt.Font;
+
+/**
+ * Converts font-weight CSS values to the constants defined for
+ * {@link java.awt.Font}
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class FontWeight
+{
+
+ /**
+ * The value to convert.
+ */
+ private String value;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param val the value to convert
+ */
+ public FontWeight(String val)
+ {
+ value = val;
+ }
+
+ /**
+ * Returns the converted value.
+ *
+ * @return the converted value
+ */
+ public int getValue()
+ {
+ int intVal;
+ if (value.equals("normal"))
+ intVal = Font.PLAIN;
+ else if (value.equals("bold"))
+ intVal = Font.BOLD;
+ else
+ // FIXME: Implement finer-grained weights.
+ intVal = Font.PLAIN;
+ return intVal;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/Length.java b/libjava/classpath/gnu/javax/swing/text/html/css/Length.java
new file mode 100644
index 000000000..06fa36e3d
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/Length.java
@@ -0,0 +1,283 @@
+/* Length.java -- Converts CSS length values
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+/**
+ * Converts CSS length values to usable length values.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class Length
+{
+
+ /**
+ * The original value.
+ */
+ private String value;
+
+ /**
+ * The converted value.
+ */
+ protected float floatValue;
+
+ /**
+ * Indicates when the value is a percentage value.
+ */
+ private boolean isPercentage;
+
+ /**
+ * Indicates a length value that is relative to the font size (em).
+ */
+ private boolean isFontEMRelative;
+
+ /**
+ * Indicates a length value that is relative to the font size (ex).
+ */
+ private boolean isFontEXRelative;
+
+ /**
+ * The EM base size.
+ */
+ private float emBase;
+
+ /**
+ * The EX base size.
+ */
+ private float exBase;
+
+ /**
+ * Creates a new length converter instance.
+ *
+ * @param val the CSS value
+ */
+ public Length(String val)
+ {
+ isFontEMRelative = false;
+ isFontEXRelative = false;
+ isPercentage = false;
+ value = val;
+ int i = value.indexOf("px");
+ int percent = value.indexOf("%");
+ int em = value.indexOf("em");
+ int ex = value.indexOf("ex");
+ try
+ {
+ floatValue = 0.0F;
+ if (i != -1)
+ {
+ String sub = value.substring(0, i);
+ floatValue = Float.parseFloat(sub);
+ }
+ else if (percent != -1)
+ {
+ isPercentage = true;
+ String sub = value.substring(0, percent);
+ floatValue = Float.parseFloat(sub) / 100;
+ }
+ else if (em != -1)
+ {
+ isFontEMRelative = true;
+ String sub = value.substring(0, em);
+ floatValue = Float.parseFloat(sub);
+ }
+ else if (ex != -1)
+ {
+ isFontEXRelative = true;
+ String sub = value.substring(0, ex);
+ floatValue = Float.parseFloat(sub);
+ }
+ else
+ {
+ floatValue = Float.parseFloat(value);
+ }
+ }
+ catch (NumberFormatException exc)
+ {
+ // Don't let such small problems interrupt CSS parsing.
+ System.err.println("couldn't parse: " + val);
+ }
+ }
+
+ /**
+ * Returns the value converted to pixels.
+ *
+ * @return the value converted to pixels
+ */
+ public float getValue()
+ {
+ return floatValue;
+ }
+
+ /**
+ * Returns the absolute span for the case when this length value is
+ * a relative value.
+ *
+ * @param base the base span
+ *
+ * @return the absolute span
+ */
+ public float getValue(float base)
+ {
+ float span = floatValue;
+ if (isPercentage)
+ span *= base;
+ else if (isFontEMRelative)
+ span *= emBase;
+ else if (isFontEXRelative)
+ span *= exBase;
+ return span;
+ }
+
+ /**
+ * Sets the font relative EM base.
+ *
+ * @param base the font relative EM base
+ */
+ public void setEMBase(float base)
+ {
+ emBase = base;
+ }
+
+ /**
+ * Sets the font relative EX base.
+ *
+ * @param base the font relative EX base
+ */
+ public void setEXBase(float base)
+ {
+ exBase = base;
+ }
+
+ /**
+ * Sets the font relative base values.
+ *
+ * @param emBase the EM base
+ * @param exBase the EX base
+ */
+ public void setFontBases(float emBase, float exBase)
+ {
+ setEMBase(emBase);
+ setEXBase(exBase);
+ }
+
+ /**
+ * Returns true when this length value is an em font relative value. In
+ * order to get correct results, you need the exBase property set up
+ * correctly.
+ *
+ * @return true when this length value is an ex font relative value
+ */
+ public boolean isFontEMRelative()
+ {
+ return isFontEMRelative;
+ }
+
+ /**
+ * Returns true when this length value is an ex font relative value. In
+ * order to get correct results, you need the emBase property set up
+ * correctly.
+ *
+ * @return true when this length value is an ex font relative value
+ */
+ public boolean isFontEXRelative()
+ {
+ return isFontEXRelative;
+ }
+
+ /**
+ * Returns <code>true</code> when the length value is a percentage
+ * value, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> when the length value is a percentage
+ * value, <code>false</code> otherwise
+ */
+ public boolean isPercentage()
+ {
+ return isPercentage;
+ }
+
+ /**
+ * Checks if the specified value makes up a valid length value.
+ *
+ * @param value the value to check
+ *
+ * @return <code>true</code> if the value is a valid length
+ */
+ public static boolean isValid(String value)
+ {
+ boolean isValid = true;
+ int px = value.indexOf("px");
+ int em = value.indexOf("em");
+ int ex = value.indexOf("ex");
+ int pc = value.indexOf('%');
+ try
+ {
+ if (px != -1)
+ {
+ Integer.parseInt(value.substring(0, px));
+ }
+ else if (em != -1)
+ {
+ Integer.parseInt(value.substring(0, em));
+ }
+ else if (ex != -1)
+ {
+ Integer.parseInt(value.substring(0, ex));
+ }
+ else if (pc != -1)
+ {
+ Integer.parseInt(value.substring(0, ex));
+ }
+ else
+ {
+ Integer.parseInt(value);
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ isValid = false;
+ }
+ return isValid;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+}
diff --git a/libjava/classpath/gnu/javax/swing/text/html/css/Selector.java b/libjava/classpath/gnu/javax/swing/text/html/css/Selector.java
new file mode 100644
index 000000000..97e582194
--- /dev/null
+++ b/libjava/classpath/gnu/javax/swing/text/html/css/Selector.java
@@ -0,0 +1,245 @@
+/* Selector.java -- A CSS selector
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.swing.text.html.css;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * A CSS selector. This provides methods to interpret a selector and
+ * query matches with an actual HTML element tree.
+ */
+public class Selector
+{
+
+ /**
+ * The actual selector. The selector tokens are stored backwards, that
+ * is the last token first. This makes matching easier.
+ */
+ private String[] selector;
+
+ private String[] elements;
+ private String[] ids;
+ private String[] classes;
+
+ /**
+ * The specificity of the selector.
+ */
+ private int specificity;
+
+ /**
+ * An implicit selector has true here. This is the case for CSS rules that
+ * are attached to HTML elements directly via style="<CSS rule>".
+ */
+ private boolean implicit;
+
+ /**
+ * Creates a new Selector instance for the specified selector string.
+ *
+ * @param sel the selector
+ */
+ public Selector(String sel)
+ {
+ StringTokenizer selectorTokens = new StringTokenizer(sel, " ");
+ selector = new String[selectorTokens.countTokens()];
+ for (int i = selector.length - 1; selectorTokens.hasMoreTokens(); i--)
+ {
+ selector[i] = selectorTokens.nextToken();
+ }
+ calculateSpecificity();
+ }
+
+ /**
+ * Determines if this selector matches the element path specified in the
+ * arguments. The arguments hold the element names as well as class
+ * and id attibutes of the HTML element to be queried. The first item
+ * in the array is the deepest element and the last on the highest up (for
+ * instance, the html tag).
+ *
+ * @param tags
+ *
+ * @return <code>true</code> when this selector matches the element path,
+ * <code>false</code> otherwise
+ */
+ public boolean matches(String[] tags, List<Map<String,String>> attributes)
+ {
+ // TODO: This implements class, id and descendent matching. These are
+ // the most commonly used selector matchers in CSS together with HTML.
+ // However, the CSS spec defines a couple of more sophisticated matches
+ // which should be implemented.
+ // http://www.w3.org/TR/CSS21/selector.html
+
+ // All parts of the selector must match at some point.
+ boolean match = false;
+ int numTags = tags.length;
+ int numSel = selector.length;
+ if (numSel <= numTags)
+ {
+ match = true;
+ int tagIndex = 0;
+ for (int j = 0; j < numSel && match; j++)
+ {
+ boolean tagMatch = false;
+ for (; tagIndex < numTags && tagMatch == false; tagIndex++)
+ {
+ Object pathClass = attributes.get(tagIndex).get("class");
+ // Try pseudo class too.
+ Object pseudoClass = attributes.get(tagIndex).get("_pseudo");
+ Object dynClass = attributes.get(tagIndex).get("_dynamic");
+ Object pathId = attributes.get(tagIndex).get("id");
+ String tag = elements[j];
+ String clazz = classes[j];
+ String id = ids[j];
+ tagMatch = tag.equals("") || tag.equals("*")
+ || tag.equals(tags[tagIndex]);
+ tagMatch = tagMatch && (clazz.equals("*")
+ || clazz.equals(dynClass)
+ || clazz.equals(pseudoClass)
+ || clazz.equals(pathClass));
+ tagMatch = tagMatch && (id.equals("*")
+ || id.equals(pathId));
+ // For the last element in the selector we must not look
+ // further.
+ if (j == 0)
+ break;
+ }
+ // If we don't come out here with a matching tag, then we're
+ // not matching at all.
+ match = tagMatch;
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Returns the specificity of the selector. This is calculated according
+ * to:
+ * http://www.w3.org/TR/CSS21/cascade.html#specificity
+ *
+ * @return the specificity of the selector
+ */
+ public int getSpecificity()
+ {
+ return specificity;
+ }
+
+ /**
+ * Returns a string representation of the selector. This tries to reconstruct
+ * the original selector as closely as possible.
+ *
+ * @return a string representation of the selector
+ */
+ public String toString()
+ {
+ CPStringBuilder b = new CPStringBuilder();
+ for (int i = selector.length - 1; i >= 0; i--)
+ {
+ b.append(selector[i]);
+ if (i > 0)
+ b.append(' ');
+ }
+ return b.toString();
+ }
+
+ /**
+ * Calculates the specificity of the selector. This is calculated according
+ * to:
+ * http://www.w3.org/TR/CSS21/cascade.html#specificity
+ */
+ private void calculateSpecificity()
+ {
+ int a = implicit ? 1 : 0;
+ int b = 0;
+ int c = 0;
+ int d = 0;
+ int numSel = selector.length;
+ elements = new String[numSel];
+ ids = new String[numSel];
+ classes = new String[numSel];
+ for (int i = 0; i < numSel; i++)
+ {
+ String sel = selector[i];
+ int clazzIndex = sel.indexOf('.');
+ // Try pseudo class too.
+ if (clazzIndex == -1)
+ clazzIndex = sel.indexOf(':');
+ int idIndex = sel.indexOf('#');
+ String clazz;
+ if (clazzIndex == -1)
+ {
+ clazz = "*";
+ clazzIndex = sel.length();
+ }
+ else
+ {
+ c++;
+ clazz = sel.substring(clazzIndex + 1,
+ idIndex > 0 ? Math.min(idIndex, sel.length())
+ : sel.length());
+ }
+ String id;
+ if (idIndex == -1)
+ {
+ id = "*";
+ idIndex = sel.length();
+ }
+ else
+ {
+ b++;
+ id = sel.substring(idIndex + 1,
+ clazzIndex > 0 ? Math.min(clazzIndex, sel.length())
+ : sel.length());
+ }
+ String tag = sel.substring(0,
+ Math.min(Math.min(clazzIndex, idIndex),
+ sel.length()));
+ if (! tag.equals("") && ! tag.equals("*"))
+ d++;
+
+ elements[i] = tag;
+ ids[i] = id;
+ classes[i] = clazz;
+ }
+ // An order of 20 should be enough for everybody.
+ specificity = a * 20 ^ 3 + b * 20 ^ 2 + c * 20 + d;
+ }
+}