Initial import of CSS source editor from GSoC project, see bug 250181: [Tooling][Theming] Integrate theme editor into RAP tooling
https://bugs.eclipse.org/bugs/show_bug.cgi?id=250181
diff --git a/bundles/org.eclipse.rap.themeeditor/.classpath b/bundles/org.eclipse.rap.themeeditor/.classpath
new file mode 100644
index 0000000..97bc37a
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="css"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.rap.themeeditor/.project b/bundles/org.eclipse.rap.themeeditor/.project
new file mode 100644
index 0000000..b4cfd6c
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.rap.themeeditor</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.rap.themeeditor/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.rap.themeeditor/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..60286e6
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Sat Dec 05 18:11:56 CET 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/bundles/org.eclipse.rap.themeeditor/META-INF/MANIFEST.MF b/bundles/org.eclipse.rap.themeeditor/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2f0b4d1
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: CSS Editor
+Bundle-SymbolicName: org.eclipse.rap.themeeditor;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.rap.themeeditor.ThemeEditorPlugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Import-Package: org.eclipse.core.filebuffers,
+ org.eclipse.jface.text,
+ org.eclipse.jface.text.contentassist,
+ org.eclipse.jface.text.presentation,
+ org.eclipse.jface.text.rules,
+ org.eclipse.jface.text.source,
+ org.eclipse.ui.editors.text,
+ org.eclipse.ui.texteditor,
+ org.eclipse.ui.views.contentoutline
diff --git a/bundles/org.eclipse.rap.themeeditor/build.properties b/bundles/org.eclipse.rap.themeeditor/build.properties
new file mode 100644
index 0000000..c8a64b4
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/build.properties
@@ -0,0 +1,7 @@
+source.. = src/,\
+           css/
+output.. = bin/
+bin.includes = .,\
+               META-INF/,\
+               plugin.xml
+
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSLexicalUnit.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSLexicalUnit.java
new file mode 100644
index 0000000..8ce213f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSLexicalUnit.java
@@ -0,0 +1,428 @@
+/*
+
+   Copyright 2000-2001,2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * This class implements the {@link LexicalUnit} interface.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: CSSLexicalUnit.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public abstract class CSSLexicalUnit implements LexicalUnit {
+
+         public static final String UNIT_TEXT_CENTIMETER  = "cm";
+         public static final String UNIT_TEXT_DEGREE      = "deg";
+         public static final String UNIT_TEXT_EM          = "em";
+         public static final String UNIT_TEXT_EX          = "ex";
+         public static final String UNIT_TEXT_GRADIAN     = "grad";
+         public static final String UNIT_TEXT_HERTZ       = "Hz";
+         public static final String UNIT_TEXT_INCH        = "in";
+         public static final String UNIT_TEXT_KILOHERTZ   = "kHz";
+         public static final String UNIT_TEXT_MILLIMETER  = "mm";
+         public static final String UNIT_TEXT_MILLISECOND = "ms";
+         public static final String UNIT_TEXT_PERCENTAGE  = "%";
+         public static final String UNIT_TEXT_PICA        = "pc";
+         public static final String UNIT_TEXT_PIXEL       = "px";
+         public static final String UNIT_TEXT_POINT       = "pt";
+         public static final String UNIT_TEXT_RADIAN      = "rad";
+         public static final String UNIT_TEXT_REAL        = "";
+         public static final String UNIT_TEXT_SECOND      = "s";
+
+
+    /**
+     * The lexical unit type.
+     */
+    protected short lexicalUnitType;
+
+    /**
+     * The next lexical unit.
+     */
+    protected LexicalUnit nextLexicalUnit;
+
+    /**
+     * The previous lexical unit.
+     */
+    protected LexicalUnit previousLexicalUnit;
+
+    /**
+     * Creates a new LexicalUnit.
+     */
+    protected CSSLexicalUnit(short t, LexicalUnit prev) {
+        lexicalUnitType = t;
+        previousLexicalUnit = prev;
+        if (prev != null) {
+            ((CSSLexicalUnit)prev).nextLexicalUnit = this;
+        }
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getLexicalUnitType()}.
+     */
+    public short getLexicalUnitType() {
+        return lexicalUnitType;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getNextLexicalUnit()}.
+     */
+    public LexicalUnit getNextLexicalUnit() {
+        return nextLexicalUnit;
+    }
+
+    /**
+     * Sets the next lexical unit.
+     */
+    public void setNextLexicalUnit(LexicalUnit lu) {
+        nextLexicalUnit = lu;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getPreviousLexicalUnit()}.
+     */
+    public LexicalUnit getPreviousLexicalUnit() {
+        return previousLexicalUnit;
+    }
+    
+    /**
+     * Sets the previous lexical unit.
+     */
+    public void setPreviousLexicalUnit(LexicalUnit lu) {
+        previousLexicalUnit = lu;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getIntegerValue()}.
+     */
+    public int getIntegerValue() {
+        throw new IllegalStateException();
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getFloatValue()}.
+     */
+    public float getFloatValue() {
+        throw new IllegalStateException();
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getDimensionUnitText()}.
+     */
+    public String getDimensionUnitText() {
+        switch (lexicalUnitType) {
+        case LexicalUnit.SAC_CENTIMETER:  return UNIT_TEXT_CENTIMETER;
+        case LexicalUnit.SAC_DEGREE:      return UNIT_TEXT_DEGREE;
+        case LexicalUnit.SAC_EM:          return UNIT_TEXT_EM;
+        case LexicalUnit.SAC_EX:          return UNIT_TEXT_EX;
+        case LexicalUnit.SAC_GRADIAN:     return UNIT_TEXT_GRADIAN;
+        case LexicalUnit.SAC_HERTZ:       return UNIT_TEXT_HERTZ;
+        case LexicalUnit.SAC_INCH:        return UNIT_TEXT_INCH;
+        case LexicalUnit.SAC_KILOHERTZ:   return UNIT_TEXT_KILOHERTZ;
+        case LexicalUnit.SAC_MILLIMETER:  return UNIT_TEXT_MILLIMETER;
+        case LexicalUnit.SAC_MILLISECOND: return UNIT_TEXT_MILLISECOND;
+        case LexicalUnit.SAC_PERCENTAGE:  return UNIT_TEXT_PERCENTAGE;
+        case LexicalUnit.SAC_PICA:        return UNIT_TEXT_PICA;
+        case LexicalUnit.SAC_PIXEL:       return UNIT_TEXT_PIXEL;
+        case LexicalUnit.SAC_POINT:       return UNIT_TEXT_POINT;
+        case LexicalUnit.SAC_RADIAN:      return UNIT_TEXT_RADIAN;
+        case LexicalUnit.SAC_REAL:        return UNIT_TEXT_REAL;
+        case LexicalUnit.SAC_SECOND:      return UNIT_TEXT_SECOND;
+        default:
+            throw new IllegalStateException("No Unit Text for type: " + 
+                                            lexicalUnitType);
+        }
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getFunctionName()}.
+     */
+    public String getFunctionName() {
+        throw new IllegalStateException();
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getParameters()}.
+     */
+    public LexicalUnit getParameters() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getStringValue()}.
+     */
+    public String getStringValue() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link LexicalUnit#getSubValues()}.
+     */
+    public LexicalUnit getSubValues() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Creates a new integer lexical unit.
+     */
+    public static CSSLexicalUnit createSimple(short t, LexicalUnit prev) {
+        return new SimpleLexicalUnit(t, prev);
+    }
+
+    /**
+     * This class represents a simple unit.
+     */
+    protected static class SimpleLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public SimpleLexicalUnit(short t, LexicalUnit prev) {
+            super(t, prev);
+        }
+    }
+
+    /**
+     * Creates a new integer lexical unit.
+     */
+    public static CSSLexicalUnit createInteger(int val, LexicalUnit prev) {
+        return new IntegerLexicalUnit(val, prev);
+    }
+
+    /**
+     * This class represents an integer unit.
+     */
+    protected static class IntegerLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The integer value.
+         */
+        protected int value;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public IntegerLexicalUnit(int val, LexicalUnit prev) {
+            super(LexicalUnit.SAC_INTEGER, prev);
+            value = val;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getIntegerValue()}.
+         */
+        public int getIntegerValue() {
+            return value;
+        }
+    }
+
+    /**
+     * Creates a new float lexical unit.
+     */
+    public static CSSLexicalUnit createFloat(short t, float val, LexicalUnit prev) {
+        return new FloatLexicalUnit(t, val, prev);
+    }
+
+    /**
+     * This class represents a float unit.
+     */
+    protected static class FloatLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The float value.
+         */
+        protected float value;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public FloatLexicalUnit(short t, float val, LexicalUnit prev) {
+            super(t, prev);
+            value = val;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getFloatValue()}.
+         */
+        public float getFloatValue() {
+            return value;
+        }
+    }
+
+    /**
+     * Creates a new float lexical unit.
+     */
+    public static CSSLexicalUnit createDimension(float val, String dim,
+                                                 LexicalUnit prev) {
+        return new DimensionLexicalUnit(val, dim, prev);
+    }
+
+    /**
+     * This class represents a dimension unit.
+     */
+    protected static class DimensionLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The float value.
+         */
+        protected float value;
+
+        /**
+         * The dimension.
+         */
+        protected String dimension;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public DimensionLexicalUnit(float val, String dim, LexicalUnit prev) {
+            super(SAC_DIMENSION, prev);
+            value = val;
+            dimension = dim;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getFloatValue()}.
+         */
+        public float getFloatValue() {
+            return value;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getDimensionUnitText()}.
+         */
+        public String getDimensionUnitText() {
+            return dimension;
+        }
+    }
+
+    /**
+     * Creates a new function lexical unit.
+     */
+    public static CSSLexicalUnit createFunction(String f, LexicalUnit params,
+                                                LexicalUnit prev) {
+        return new FunctionLexicalUnit(f, params, prev);
+    }
+
+    /**
+     * This class represents a function unit.
+     */
+    protected static class FunctionLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The function name.
+         */
+        protected String name;
+
+        /**
+         * The function parameters.
+         */
+        protected LexicalUnit parameters;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public FunctionLexicalUnit(String f, LexicalUnit params, LexicalUnit prev) {
+            super(SAC_FUNCTION, prev);
+            name = f;
+            parameters = params;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getFunctionName()}.
+         */
+        public String getFunctionName() {
+            return name;
+        }
+    
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getParameters()}.
+         */
+        public LexicalUnit getParameters() {
+            return parameters;
+        }
+
+    }
+
+    /**
+     * Creates a new function lexical unit.
+     */
+    public static CSSLexicalUnit createPredefinedFunction(short t, LexicalUnit params,
+                                                          LexicalUnit prev) {
+        return new PredefinedFunctionLexicalUnit(t, params, prev);
+    }
+
+    /**
+     * This class represents a function unit.
+     */
+    protected static class PredefinedFunctionLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The function parameters.
+         */
+        protected LexicalUnit parameters;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public PredefinedFunctionLexicalUnit(short t, LexicalUnit params,
+                                             LexicalUnit prev) {
+            super(t, prev);
+            parameters = params;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getParameters()}.
+         */
+        public LexicalUnit getParameters() {
+            return parameters;
+        }
+    }
+
+    /**
+     * Creates a new string lexical unit.
+     */
+    public static CSSLexicalUnit createString(short t, String val, LexicalUnit prev) {
+        return new StringLexicalUnit(t, val, prev);
+    }
+
+    /**
+     * This class represents a string unit.
+     */
+    protected static class StringLexicalUnit extends CSSLexicalUnit {
+
+        /**
+         * The string value.
+         */
+        protected String value;
+
+        /**
+         * Creates a new LexicalUnit.
+         */
+        public StringLexicalUnit(short t, String val, LexicalUnit prev) {
+            super(t, prev);
+            value = val;
+        }
+
+        /**
+         * <b>SAC</b>: Implements {@link LexicalUnit#getStringValue()}.
+         */
+        public String getStringValue() {
+            return value;
+        }
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSACMediaList.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSACMediaList.java
new file mode 100644
index 0000000..cd0b1e4
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSACMediaList.java
@@ -0,0 +1,71 @@
+/*
+
+   Copyright 2000-2001  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+import org.w3c.css.sac.SACMediaList;
+
+/**
+ * This class implements the {@link SACMediaList} interface.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: CSSSACMediaList.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class CSSSACMediaList implements SACMediaList {
+
+    /**
+     * The list.
+     */
+    protected String[] list = new String[3];
+
+    /**
+     * The list length.
+     */
+    protected int length;
+
+    /**
+     * <b>SAC</b>: Returns the length of this selector list
+     */    
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * <b>SAC</b>: Returns the selector at the specified index, or
+     * <code>null</code> if this is not a valid index.  
+     */
+    public String item(int index) {
+        if (index < 0 || index >= length) {
+            return null;
+        }
+        return list[index];
+    }
+
+    /**
+     * Appends an item to the list.
+     */
+    public void append(String item) {
+        if (length == list.length) {
+            String[] tmp = list;
+            list = new String[list.length * 3 / 2];
+            for (int i = 0; i < tmp.length; i++) {
+                list[i] = tmp[i];
+            }
+        }
+        list[length++] = item;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSelectorList.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSelectorList.java
new file mode 100644
index 0000000..c780162
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/CSSSelectorList.java
@@ -0,0 +1,99 @@
+/*
+
+   Copyright 2000-2001  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SelectorList;
+
+/**
+ * This class implements the {@link SelectorList} interface.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: CSSSelectorList.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class CSSSelectorList implements SelectorList {
+
+    /**
+     * The list.
+     */
+    protected Selector[] list = new Selector[3];
+
+    /**
+     * The list length.
+     */
+    protected int length;
+
+    /**
+     * <b>SAC</b>: Returns the length of this selector list
+     */    
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * <b>SAC</b>: Returns the selector at the specified index, or
+     * <code>null</code> if this is not a valid index.  
+     */
+    public Selector item(int index) {
+        if (index < 0 || index >= length) {
+            return null;
+        }
+        return list[index];
+    }
+
+    /**
+     * Appends an item to the list.
+     */
+    public void append(Selector item) {
+        if (length == list.length) {
+            Selector[] tmp = list;
+            list = new Selector[list.length * 3 / 2];
+            for (int i = 0; i < tmp.length; i++) {
+                list[i] = tmp[i];
+            }
+        }
+        list[length++] = item;
+    }
+    
+    /*
+     * BEGIN Modification for Theme Editor
+     * Removes an item from the list.
+     */
+    public void remove( final Selector item ) {
+      if (item==null) {
+        return;
+      }
+      Selector[] tmp = list;
+      list = new Selector[ tmp.length ];
+      int j = 0;
+      for (int i = 0; i < tmp.length; i++) {
+        if ( tmp[i] != null ) {
+          if ( !tmp[i].toString().equals( item.toString() ) ) {
+            list[j] = tmp[i];
+            j++;
+          }
+          else {
+            length--;
+          }
+        }
+      }
+    }
+    /*
+     * END Modification for Theme Editor
+     */
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/LexicalUnits.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/LexicalUnits.java
new file mode 100644
index 0000000..573ff1f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/LexicalUnits.java
@@ -0,0 +1,302 @@
+/*
+
+   Copyright 2000-2001  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+/**
+ * This interface defines the constants that represent CSS lexical units.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: LexicalUnits.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public interface LexicalUnits {
+
+    /**
+     * Represents the EOF lexical unit.
+     */
+    int EOF = 0;
+
+    /**
+     * Represents the '{' lexical unit.
+     */
+    int LEFT_CURLY_BRACE = 1;
+
+    /**
+     * Represents the '}' lexical unit.
+     */
+    int RIGHT_CURLY_BRACE = 2;
+
+    /**
+     * Represents the '=' lexical unit.
+     */
+    int EQUAL = 3;
+
+    /**
+     * Represents the '+' lexical unit.
+     */
+    int PLUS = 4;
+
+    /**
+     * Represents the '-' lexical unit.
+     */
+    int MINUS = 5;
+
+    /**
+     * Represents the ',' lexical unit.
+     */
+    int COMMA = 6;
+
+    /**
+     * Represents the '.' lexical unit.
+     */
+    int DOT = 7;
+
+    /**
+     * Represents the ';' lexical unit.
+     */
+    int SEMI_COLON = 8;
+
+    /**
+     * Represents the '&gt;' lexical unit.
+     */
+    int PRECEDE = 9;
+
+    /**
+     * Represents the '/' lexical unit.
+     */
+    int DIVIDE = 10;
+
+    /**
+     * Represents the '[' lexical unit.
+     */
+    int LEFT_BRACKET = 11;
+
+    /**
+     * Represents the ']' lexical unit.
+     */
+    int RIGHT_BRACKET = 12;
+
+    /**
+     * Represents the '*' lexical unit.
+     */
+    int ANY = 13;
+
+    /**
+     * Represents the '(' lexical unit.
+     */
+    int LEFT_BRACE = 14;
+
+    /**
+     * Represents the ')' lexical unit.
+     */
+    int RIGHT_BRACE = 15;
+
+    /**
+     * Represents the ':' lexical unit.
+     */
+    int COLON = 16;
+
+    /**
+     * Represents the white space lexical unit.
+     */
+    int SPACE = 17;
+
+    /**
+     * Represents the comment lexical unit.
+     */
+    int COMMENT = 18;
+
+    /**
+     * Represents the string lexical unit.
+     */
+    int STRING = 19;
+
+    /**
+     * Represents the identifier lexical unit.
+     */
+    int IDENTIFIER = 20;
+
+    /**
+     * Represents the '&lt;&#x21;--' lexical unit.
+     */
+    int CDO = 21;
+
+    /**
+     * Represents the '--&gt;' lexical unit.
+     */
+    int CDC = 22;
+
+    /**
+     * Represents the '&#x21;important' lexical unit.
+     */
+    int IMPORTANT_SYMBOL = 23;
+
+    /**
+     * Represents an integer.
+     */
+    int INTEGER = 24;
+
+    /**
+     * Represents the '|=' lexical unit.
+     */
+    int DASHMATCH = 25;
+
+    /**
+     * Represents the '~=' lexical unit.
+     */
+    int INCLUDES = 26;
+
+    /**
+     * Represents the '#name' lexical unit.
+     */
+    int HASH = 27;
+
+    /**
+     * Represents the '@import' lexical unit.
+     */
+    int IMPORT_SYMBOL = 28;
+
+    /**
+     * Represents the '@ident' lexical unit.
+     */
+    int AT_KEYWORD = 29;
+
+    /**
+     * Represents the '@charset' lexical unit.
+     */
+    int CHARSET_SYMBOL = 30;
+
+    /**
+     * Represents the '@font-face' lexical unit.
+     */
+    int FONT_FACE_SYMBOL = 31;
+
+    /**
+     * Represents the '@media' lexical unit.
+     */
+    int MEDIA_SYMBOL = 32;
+
+    /**
+     * Represents the '@page' lexical unit.
+     */
+    int PAGE_SYMBOL = 33;
+
+    /**
+     * Represents a dimension lexical unit.
+     */
+    int DIMENSION = 34;
+
+    /**
+     * Represents a ex lexical unit.
+     */
+    int EX = 35;
+
+    /**
+     * Represents a em lexical unit.
+     */
+    int EM = 36;
+
+    /**
+     * Represents a cm lexical unit.
+     */
+    int CM = 37;
+
+    /**
+     * Represents a mm lexical unit.
+     */
+    int MM = 38;
+
+    /**
+     * Represents a in lexical unit.
+     */
+    int IN = 39;
+
+    /**
+     * Represents a ms lexical unit.
+     */
+    int MS = 40;
+
+    /**
+     * Represents a hz lexical unit.
+     */
+    int HZ = 41;
+
+    /**
+     * Represents a % lexical unit.
+     */
+    int PERCENTAGE = 42;
+
+    /**
+     * Represents a s lexical unit.
+     */
+    int S = 43;
+
+    /**
+     * Represents a pc lexical unit.
+     */
+    int PC = 44;
+
+    /**
+     * Represents a pt lexical unit.
+     */
+    int PT = 45;
+
+    /**
+     * Represents a px lexical unit.
+     */
+    int PX = 46;
+
+    /**
+     * Represents a deg lexical unit.
+     */
+    int DEG = 47;
+
+    /**
+     * Represents a rad lexical unit.
+     */
+    int RAD = 48;
+
+    /**
+     * Represents a grad lexical unit.
+     */
+    int GRAD = 49;
+
+    /**
+     * Represents a khz lexical unit.
+     */
+    int KHZ = 50;
+
+    /**
+     * Represents a 'url(URI)' lexical unit.
+     */
+    int URI = 51;
+
+    /**
+     * Represents a 'ident(' lexical unit.
+     */
+    int FUNCTION = 52;
+
+    /**
+     * Represents a unicode range lexical unit.
+     */
+    int UNICODE_RANGE = 53;
+
+    /**
+     * represents a real number.
+     */
+    int REAL = 54;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.java
new file mode 100644
index 0000000..90786fe
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.apache.batik.css.parser;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Messages {
+
+  private static final String BUNDLE_NAME = "org.apache.batik.css.parser.Messages"; //$NON-NLS-1$
+
+  private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME );
+
+  private Messages() {
+  }
+
+  public static String getString( String key ) {
+    try {
+      return RESOURCE_BUNDLE.getString( key );
+    } catch( MissingResourceException e ) {
+      return '!' + key + '!';
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.properties b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.properties
new file mode 100644
index 0000000..db7688d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Messages.properties
@@ -0,0 +1,83 @@
+###########################################################################
+# Copyright 1999-2003,2005 The Apache Software Foundation.
+###########################################################################
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+#
+# The error messages for the CSS parser.
+#
+# Author: stephane@hillion.org
+#
+
+charset.string = \
+A string was expected in a @charset rule.
+
+semicolon = \
+';' expected.
+
+colon = \
+':' expected.
+
+eof = \
+Unexpected end of file.
+
+eof.expected = \
+End of file expected.
+
+character = \
+Unexpected character.
+
+string.or.uri = \
+String or URI expected.
+
+identifier = \
+Identifier expected.
+
+identifier.character = \
+Invalid identifier start character.
+
+identifier.or.string = \
+Identifier or string expected.
+
+left.curly.brace = \
+'{' expected.
+
+right.curly.brace = \
+'}' expected.
+
+right.brace = \
+')' expected.
+
+right.bracket = \
+']' expected.
+
+token = \
+Unexpected token: {0} (see LexicalUnits).
+
+rgb.color = \
+Invalid RGB color: {0}.
+
+encoding = \
+Invalid encoding: {0}.
+
+number.format = \
+Invalid number.
+
+duplicate.pseudo.element = \
+A pseudo element has already been defined.
+
+pseudo.function = \
+Invalid pseudo-function.
+
+empty.source = \
+Empty input source.
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ParseException.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ParseException.java
new file mode 100644
index 0000000..1ec53fe
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ParseException.java
@@ -0,0 +1,128 @@
+/*
+
+   Copyright 2000  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+/**
+ * This class encapsulates a general parse error or warning.
+ *
+ * <p>This class can contain basic error or warning information from
+ * either the parser or the application.
+ *
+ * <p>If the application needs to pass through other types of
+ * exceptions, it must wrap those exceptions in a ParseException.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: ParseException.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class ParseException extends RuntimeException {
+
+    /**
+     * @serial The embedded exception if tunnelling, or null.
+     */    
+    protected Exception exception;
+    
+    /**
+     * @serial The line number.
+     */
+    protected int lineNumber;
+
+    /**
+     * @serial The column number.
+     */
+    protected int columnNumber;
+
+    /**
+     * Creates a new ParseException.
+     * @param message The error or warning message.
+     * @param line The line of the last parsed character.
+     * @param column The column of the last parsed character.
+     */
+    public ParseException (String message, int line, int column) {
+	super(message);
+	exception = null;
+	lineNumber = line;
+	columnNumber = column;
+    }
+    
+    /**
+     * Creates a new ParseException wrapping an existing exception.
+     *
+     * <p>The existing exception will be embedded in the new
+     * one, and its message will become the default message for
+     * the ParseException.
+     * @param e The exception to be wrapped in a ParseException.
+     */
+    public ParseException (Exception e) {
+	exception = e;
+	lineNumber = -1;
+	columnNumber = -1;
+    }
+    
+    /**
+     * Creates a new ParseException from an existing exception.
+     *
+     * <p>The existing exception will be embedded in the new
+     * one, but the new exception will have its own message.
+     * @param message The detail message.
+     * @param e The exception to be wrapped in a SAXException.
+     */
+    public ParseException (String message, Exception e) {
+	super(message);
+	this.exception = e;
+    }
+    
+    /**
+     * Return a detail message for this exception.
+     *
+     * <p>If there is a embedded exception, and if the ParseException
+     * has no detail message of its own, this method will return
+     * the detail message from the embedded exception.
+     * @return The error or warning message.
+     */
+    public String getMessage () {
+	String message = super.getMessage();
+	
+	if (message == null && exception != null) {
+	    return exception.getMessage();
+	} else {
+	    return message;
+	}
+    }
+    
+    /**
+     * Return the embedded exception, if any.
+     * @return The embedded exception, or null if there is none.
+     */
+    public Exception getException () {
+	return exception;
+    }
+
+    /**
+     * Returns the line of the last parsed character.
+     */
+    public int getLineNumber() {
+	return lineNumber;
+    }
+
+    /**
+     * Returns the column of the last parsed character.
+     */
+    public int getColumnNumber() {
+	return columnNumber;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Parser.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Parser.java
new file mode 100644
index 0000000..3c05fcb
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Parser.java
@@ -0,0 +1,1733 @@
+/*
+
+   Copyright 2000-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+import java.io.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+import org.eclipse.rwt.internal.theme.css.ParserUtil;
+import org.w3c.css.sac.*;
+
+/**
+ * This class implements the {@link _org.w3c.css.sac.Parser} interface.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: Parser.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class Parser implements org.w3c.css.sac.Parser {
+
+    /**
+     * The default resource bundle base name.
+     */
+    public final static String BUNDLE_CLASSNAME =
+        "org.apache.batik.css.parser.resources.Messages";
+
+    /**
+     * The scanner used to scan the input source.
+     */
+    protected Scanner scanner;
+
+    /**
+     * The current lexical unit.
+     */
+    protected int current;
+
+    /**
+     * The document handler.
+     */
+    protected DocumentHandler documentHandler = null;
+
+    /**
+     * The selector factory.
+     */
+    protected SelectorFactory selectorFactory = null;
+
+    /**
+     * The condition factory.
+     */
+    protected ConditionFactory conditionFactory = null;
+
+    /**
+     * The error handler.
+     */
+    protected ErrorHandler errorHandler = null;
+
+    /**
+     * To store the current pseudo element.
+     */
+    protected String pseudoElement;
+
+    /**
+     * The document URI.
+     */
+    protected String documentURI;
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#getParserVersion()}.
+     * @return "http://www.w3.org/TR/REC-CSS2".
+     */
+    public String getParserVersion() {
+        return "http://www.w3.org/TR/REC-CSS2";
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link _org.w3c.css.sac.Parser#setLocale(Locale)}.
+     */
+    public void setLocale(Locale locale) throws CSSException {
+//        localizableSupport.setLocale(locale);
+    }
+    
+//    /**
+//     * Implements {@link org.apache.batik.i18n.Localizable#getLocale()}.
+//     */
+//    public Locale getLocale() {
+//        return localizableSupport.getLocale();
+//    }
+
+    /**
+     * Implements {@link
+     * org.apache.batik.i18n.Localizable#formatMessage(String,Object[])}.
+     */
+    public String formatMessage(String key, Object[] args)
+        throws MissingResourceException {
+        String pattern = Messages.getString( key );
+        return MessageFormat.format( pattern, args );
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#setDocumentHandler(DocumentHandler)}.
+     */
+    public void setDocumentHandler(DocumentHandler handler) {
+        documentHandler = handler;
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#setSelectorFactory(SelectorFactory)}.
+     */
+    public void setSelectorFactory(SelectorFactory factory) {
+        selectorFactory = factory;
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#setConditionFactory(ConditionFactory)}.
+     */
+    public void setConditionFactory(ConditionFactory factory) {
+        conditionFactory = factory;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#setErrorHandler(ErrorHandler)}.
+     */
+    public void setErrorHandler(ErrorHandler handler) {
+        errorHandler = handler;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#parseStyleSheet(InputSource)}.
+     */
+    public void parseStyleSheet(InputSource source) 
+        throws CSSException, IOException {
+        scanner = createScanner(source);
+
+        try {
+            documentHandler.startDocument(source);
+
+            current = scanner.next();
+            switch (current) {
+            case LexicalUnits.CHARSET_SYMBOL:
+                if (nextIgnoreSpaces() != LexicalUnits.STRING) {
+                    reportError("charset.string");
+                } else {
+                    if (nextIgnoreSpaces() != LexicalUnits.SEMI_COLON) {
+                        reportError("semicolon");
+                    }
+                    next();
+                }
+                break;
+            case LexicalUnits.COMMENT:
+                documentHandler.comment(scanner.getStringValue());
+            }
+
+            skipSpacesAndCDOCDC();
+            for (;;) {
+                if (current == LexicalUnits.IMPORT_SYMBOL) {
+                    nextIgnoreSpaces();
+                    parseImportRule();
+                    nextIgnoreSpaces();
+                } else {
+                    break;
+                }
+            }
+            
+            loop: for (;;) {
+                switch (current) {
+                case LexicalUnits.PAGE_SYMBOL:
+                    nextIgnoreSpaces();
+                    parsePageRule();
+                    break;
+                case LexicalUnits.MEDIA_SYMBOL:
+                    nextIgnoreSpaces();
+                    parseMediaRule();
+                    break;
+                case LexicalUnits.FONT_FACE_SYMBOL:
+                    nextIgnoreSpaces();
+                    parseFontFaceRule();
+                    break;
+                case LexicalUnits.AT_KEYWORD:
+                    nextIgnoreSpaces();
+                    parseAtRule();
+                    break;
+                case LexicalUnits.EOF:
+                    break loop;
+                default:
+                    parseRuleSet();
+                }
+                skipSpacesAndCDOCDC();
+            }
+        } finally {
+            documentHandler.endDocument(source);
+            scanner = null;
+        }
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#parseStyleSheet(String)}.
+     */
+    public void parseStyleSheet(String uri) throws CSSException, IOException {
+        parseStyleSheet(new InputSource(uri));
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#parseStyleDeclaration(InputSource)}.
+     */
+    public void parseStyleDeclaration(InputSource source) 
+        throws CSSException, IOException {
+
+        scanner = createScanner(source);
+	parseStyleDeclarationInternal();
+    }
+
+    /**
+     * Parses a style declaration using the current scanner.
+     */
+    protected void parseStyleDeclarationInternal() 
+	throws CSSException, IOException {
+        nextIgnoreSpaces();
+        try {
+            parseStyleDeclaration(false);
+        } catch (CSSParseException e) {
+            reportError(e);
+        } finally {
+            scanner = null;
+        }
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#parseRule(InputSource)}.
+     */
+    public void parseRule(InputSource source) 
+	throws CSSException, IOException {
+        scanner = createScanner(source);
+	parseRuleInternal();
+    }
+
+    /**
+     * Parses a rule using the current scanner.
+     */
+    protected void parseRuleInternal() throws CSSException, IOException {
+        nextIgnoreSpaces();
+        parseRule();
+        scanner = null;
+    }
+
+    /**
+     * <b>SAC</b>: Implements {@link
+     * _org.w3c.css.sac.Parser#parseSelectors(InputSource)}.
+     */    
+    public SelectorList parseSelectors(InputSource source)
+        throws CSSException, IOException {
+        scanner = createScanner(source);
+	return parseSelectorsInternal();
+    }
+
+    /**
+     * Parses selectors using the current scanner.
+     */
+    protected SelectorList parseSelectorsInternal() 
+	throws CSSException, IOException {
+        nextIgnoreSpaces();
+        SelectorList ret = parseSelectorList();
+        scanner = null;
+        return ret;
+    }
+
+    /**
+     * <b>SAC</b>: Implements
+     * {@link _org.w3c.css.sac.Parser#parsePropertyValue(InputSource)}.
+     */    
+    public LexicalUnit parsePropertyValue(InputSource source)
+        throws CSSException, IOException {
+        scanner = createScanner(source);
+	return parsePropertyValueInternal();
+    }
+
+    /**
+     * Parses property value using the current scanner.
+     */
+    protected LexicalUnit parsePropertyValueInternal() 
+	throws CSSException, IOException {
+        nextIgnoreSpaces();
+        
+        LexicalUnit exp = null;
+
+        try {
+            exp = parseExpression(false);
+        } catch (CSSParseException e) {
+            reportError(e);
+            throw e;
+        }
+
+        CSSParseException exception = null;
+        if (current != LexicalUnits.EOF)
+            exception = createCSSParseException("eof.expected");
+
+        scanner = null;
+
+        if (exception != null) {
+            errorHandler.fatalError(exception);
+        }
+        return exp;
+    }
+    
+    /**
+     * <b>SAC</b>: Implements
+     * {@link _org.w3c.css.sac.Parser#parsePriority(InputSource)}.
+     */    
+    public boolean parsePriority(InputSource source)
+        throws CSSException, IOException {
+        scanner = createScanner(source);
+	return parsePriorityInternal();
+    }
+
+    /**
+     * Parses the priority using the current scanner.
+     */
+    protected boolean parsePriorityInternal()
+        throws CSSException, IOException {
+        nextIgnoreSpaces();
+
+        scanner = null;
+
+        switch (current) {
+        case LexicalUnits.EOF:
+            return false;
+        case LexicalUnits.IMPORT_SYMBOL:
+            return true;
+        default:
+            reportError("token", new Object[] { new Integer(current) });
+            return false;
+        }
+    }
+
+    /**
+     * Parses a rule.
+     */
+    protected void parseRule() {
+        switch (scanner.getType()) {
+        case LexicalUnits.IMPORT_SYMBOL:
+            nextIgnoreSpaces();
+            parseImportRule();
+            break;
+        case LexicalUnits.AT_KEYWORD:
+            nextIgnoreSpaces();
+            parseAtRule();
+            break;
+        case LexicalUnits.FONT_FACE_SYMBOL:
+            nextIgnoreSpaces();
+            parseFontFaceRule();
+            break;
+        case LexicalUnits.MEDIA_SYMBOL:
+            nextIgnoreSpaces();
+            parseMediaRule();
+            break;
+        case LexicalUnits.PAGE_SYMBOL:
+            nextIgnoreSpaces();
+            parsePageRule();
+            break;
+        default:
+            parseRuleSet();
+        }
+    }
+
+    /**
+     * Parses an unknown rule.
+     */
+    protected void parseAtRule() {
+        scanner.scanAtRule();
+        documentHandler.ignorableAtRule(scanner.getStringValue());
+        nextIgnoreSpaces();
+    }
+
+    /**
+     * Parses an import rule. Assumes the current token is '@import'.
+     */
+    protected void parseImportRule() {
+        String uri = null;
+        switch (current) {
+        default:
+            reportError("string.or.uri");
+            return;
+        case LexicalUnits.STRING:
+        case LexicalUnits.URI:
+            uri = scanner.getStringValue();
+            nextIgnoreSpaces();
+        }
+
+        CSSSACMediaList ml;
+        if (current != LexicalUnits.IDENTIFIER) {
+            ml = new CSSSACMediaList();
+            ml.append("all");
+        } else {
+            ml = parseMediaList();
+        }
+
+        documentHandler.importStyle(uri, ml, null);
+
+        if (current != LexicalUnits.SEMI_COLON) {
+            reportError("semicolon");
+        } else {
+            next();
+        }
+    }
+
+    /**
+     * Parses a media list.
+     */
+    protected CSSSACMediaList parseMediaList() {
+        CSSSACMediaList result = new CSSSACMediaList();
+        result.append(scanner.getStringValue());
+        nextIgnoreSpaces();
+        
+        while (current == LexicalUnits.COMMA) {
+            nextIgnoreSpaces();
+
+            switch (current) {
+            default:
+                reportError("identifier");
+                break;
+            case LexicalUnits.IDENTIFIER:
+                result.append(scanner.getStringValue());
+                nextIgnoreSpaces();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Parses a font-face rule.
+     */
+    protected void parseFontFaceRule() {
+        try {
+            documentHandler.startFontFace();
+
+            if (current != LexicalUnits.LEFT_CURLY_BRACE) {
+                reportError("left.curly.brace");
+            } else {
+                nextIgnoreSpaces();
+        
+                try {
+                    parseStyleDeclaration(true);
+                } catch (CSSParseException e) {
+                    reportError(e);
+                }
+            }
+        } finally {
+            documentHandler.endFontFace();
+        }
+    }
+
+    /**
+     * Parses a page rule.
+     */
+    protected void parsePageRule() {
+        String page = null;
+        String ppage = null;
+
+        if (current == LexicalUnits.IDENTIFIER) {
+            page = scanner.getStringValue();
+            nextIgnoreSpaces();
+
+            if (current == LexicalUnits.COLON) {
+                nextIgnoreSpaces();
+
+                if (current != LexicalUnits.IDENTIFIER) {
+                    reportError("identifier");
+                    return;
+                }
+                ppage = scanner.getStringValue();
+                nextIgnoreSpaces();
+            }
+        }
+
+        try {
+            documentHandler.startPage(page, ppage);
+            
+            if (current != LexicalUnits.LEFT_CURLY_BRACE) {
+                reportError("left.curly.brace");
+            } else {
+                nextIgnoreSpaces();
+        
+                try {
+                    parseStyleDeclaration(true);
+                } catch (CSSParseException e) {
+                    reportError(e);
+                }
+            }
+        } finally {
+            documentHandler.endPage(page, ppage);
+        }
+    }
+    
+    /**
+     * Parses a media rule.
+     */
+    protected void parseMediaRule() {
+        if (current != LexicalUnits.IDENTIFIER) {
+            reportError("identifier");
+            return;
+        }
+
+        CSSSACMediaList ml = parseMediaList();
+        try {
+            documentHandler.startMedia(ml);
+
+            if (current != LexicalUnits.LEFT_CURLY_BRACE) {
+                reportError("left.curly.brace");
+            } else {
+                nextIgnoreSpaces();
+            
+                loop: for (;;) {
+                    switch (current) {
+                    case LexicalUnits.EOF:
+                    case LexicalUnits.RIGHT_CURLY_BRACE:
+                        break loop;
+                    default:
+                        parseRuleSet();
+                    }
+                }
+
+                nextIgnoreSpaces();
+            }
+        } finally {
+            documentHandler.endMedia(ml);
+        }
+    }
+
+    /**
+     * Parses a ruleset.
+     */
+    protected void parseRuleSet() {
+        SelectorList sl = null;
+
+        try {
+            sl = parseSelectorList();
+        } catch (CSSParseException e) {
+            reportError(e);
+            return;
+        }
+
+        /* BEGIN Modification for Theme Editor: handle comment reporting bug */
+        boolean callNextIgnoreSpaceAtFinally = false;
+        /* END Modification for Theme Editor */
+        try {
+            documentHandler.startSelector(sl);
+
+            if (current != LexicalUnits.LEFT_CURLY_BRACE) {
+                reportError("left.curly.brace");
+                if (current == LexicalUnits.RIGHT_CURLY_BRACE) {
+                  nextIgnoreSpaces();
+                }
+            } else {
+                nextIgnoreSpaces();
+        
+                try {
+                    parseStyleDeclaration(true);
+                    /* BEGIN Modification for Theme Editor: handle comment reporting bug */
+                    if ( current == LexicalUnits.RIGHT_CURLY_BRACE ) {
+                      callNextIgnoreSpaceAtFinally = true;
+                    }
+                    /* END Modification for Theme Editor */
+                } catch (CSSParseException e) {
+                    reportError(e);
+                }
+            }
+        } finally {
+            documentHandler.endSelector(sl);
+            /* BEGIN Modification for Theme Editor: handle comment reporting bug */
+            if ( callNextIgnoreSpaceAtFinally ) {
+              nextIgnoreSpaces();
+            }
+            /* END Modification for Theme Editor */
+        }
+    }
+
+    /**
+     * Parses a selector list
+     */
+    protected SelectorList parseSelectorList() {
+        CSSSelectorList result = new CSSSelectorList();
+        result.append(parseSelector());
+
+        for (;;) {
+            if (current != LexicalUnits.COMMA) {
+                return result;
+            }
+            nextIgnoreSpaces();
+            result.append(parseSelector());
+        }
+    }
+
+    /**
+     * Parses a selector.
+     */
+    protected Selector parseSelector() {
+        SimpleSelector ss = parseSimpleSelector();
+        Selector result = ss;
+
+        pseudoElement = null;
+
+        loop: for (;;) {
+            switch (current) {
+            default:
+                break loop;
+            case LexicalUnits.IDENTIFIER:
+            case LexicalUnits.ANY:
+            case LexicalUnits.HASH:
+            case LexicalUnits.DOT:
+            case LexicalUnits.LEFT_BRACKET:
+            case LexicalUnits.COLON:
+                result = selectorFactory.createDescendantSelector
+                    (result,
+                     parseSimpleSelector());
+                break;
+            case LexicalUnits.PLUS:
+                nextIgnoreSpaces();
+                result = selectorFactory.createDirectAdjacentSelector
+                    ((short)1,
+                     result, 
+                     parseSimpleSelector());
+                break;
+            case LexicalUnits.PRECEDE:
+                nextIgnoreSpaces();
+                result = selectorFactory.createChildSelector
+                    (result, 
+                     parseSimpleSelector());
+             }
+        }
+        if (pseudoElement != null) {
+            result = selectorFactory.createChildSelector
+                (result,
+                 selectorFactory.createPseudoElementSelector
+                 (null, pseudoElement));
+        }
+        return result;
+    }
+
+    /**
+     * Parses a simple selector.
+     */
+    protected SimpleSelector parseSimpleSelector() {
+        SimpleSelector result;
+
+        switch (current) {
+        case LexicalUnits.IDENTIFIER:
+            result = selectorFactory.createElementSelector
+                (null, scanner.getStringValue());
+            next();
+            break;
+        case LexicalUnits.ANY:
+            next();
+        default:
+            result = selectorFactory.createElementSelector(null, null);
+        }
+        Condition cond = null;
+        loop: for (;;) {
+            Condition c = null;
+            switch (current) {
+            case LexicalUnits.HASH:
+                c = conditionFactory.createIdCondition
+                    (scanner.getStringValue());
+                next();
+                break;
+            case LexicalUnits.DOT:
+                if (next() != LexicalUnits.IDENTIFIER) {
+                    throw createCSSParseException("identifier");
+                }
+                c = conditionFactory.createClassCondition
+                    (null, scanner.getStringValue());
+                next();
+                break;
+            case LexicalUnits.LEFT_BRACKET:
+                if (nextIgnoreSpaces() != LexicalUnits.IDENTIFIER) {
+                    throw createCSSParseException("identifier");
+                }
+                String name = scanner.getStringValue();
+                int op = nextIgnoreSpaces();
+                switch (op) {
+                default:
+                    throw createCSSParseException("right.bracket");
+                case LexicalUnits.RIGHT_BRACKET:
+                    nextIgnoreSpaces();
+                    c = conditionFactory.createAttributeCondition
+                        (name, null, false, null);
+                    break;
+                case LexicalUnits.EQUAL:
+                case LexicalUnits.INCLUDES:
+                case LexicalUnits.DASHMATCH:
+                    String val = null;
+                    switch (nextIgnoreSpaces()) {
+                    default:
+                        throw createCSSParseException("identifier.or.string");
+                    case LexicalUnits.STRING:
+                    case LexicalUnits.IDENTIFIER:
+                        val = scanner.getStringValue();
+                        nextIgnoreSpaces();
+                    }
+                    if (current != LexicalUnits.RIGHT_BRACKET) {
+                        throw createCSSParseException("right.bracket");
+                    }
+                    next();
+                    switch (op) {
+                    case LexicalUnits.EQUAL:
+                        c = conditionFactory.createAttributeCondition
+                            (name, null, false, val);
+                        break;
+                    case LexicalUnits.INCLUDES:
+                        c = conditionFactory.createOneOfAttributeCondition
+                            (name, null, false, val);
+                        break;
+                    default:
+                        c = conditionFactory.
+                            createBeginHyphenAttributeCondition
+                            (name, null, false, val);
+                    }
+                }
+                break;
+            case LexicalUnits.COLON:
+                switch (nextIgnoreSpaces()) {
+                case LexicalUnits.IDENTIFIER:
+                    String val = scanner.getStringValue();
+                    if (isPseudoElement(val)) {
+                        if (pseudoElement != null) {
+                            throw createCSSParseException
+                                ("duplicate.pseudo.element");
+                        }
+                        pseudoElement = val;
+                    } else {
+                        c = conditionFactory.createPseudoClassCondition
+                            (null, val);
+                    }
+                    next();
+                    break;
+                case LexicalUnits.FUNCTION:
+                    String func = scanner.getStringValue();
+                    if (nextIgnoreSpaces() != LexicalUnits.IDENTIFIER) {
+                        throw createCSSParseException("identifier");
+                    }
+                    String lang = scanner.getStringValue();
+                    if (nextIgnoreSpaces() != LexicalUnits.RIGHT_BRACE) {
+                        throw createCSSParseException("right.brace");
+                    }
+
+                    if (!func.equalsIgnoreCase("lang")) {
+                        throw createCSSParseException("pseudo.function");
+                    }
+
+                    c = conditionFactory.createLangCondition(lang);
+
+                    next();
+                    break;
+                default:
+                    throw createCSSParseException("identifier");
+                }
+                break;
+            default:
+                break loop;
+            }
+            if (c != null) {
+                if (cond == null) {
+                    cond = c;
+                } else {
+                    cond = conditionFactory.createAndCondition(cond, c);
+                }
+            }
+        }
+        skipSpaces();
+        if (cond != null) {
+            result = selectorFactory.createConditionalSelector(result, cond);
+        }
+        return result;
+    }
+
+    /**
+     * Tells whether or not the given string represents a pseudo-element.
+     */
+    protected boolean isPseudoElement(String s) {
+        switch (s.charAt(0)) {
+        case 'a':
+        case 'A':
+            return s.equalsIgnoreCase("after");
+        case 'b':
+        case 'B':
+            return s.equalsIgnoreCase("before");
+        case 'f':
+        case 'F':
+            return s.equalsIgnoreCase("first-letter") ||
+                   s.equalsIgnoreCase("first-line");
+        }
+        return false;
+    }
+
+    /**
+     * Parses the given reader.
+     */
+    protected void parseStyleDeclaration(boolean inSheet)
+        throws CSSException {
+        for (;;) {
+            switch (current) {
+            case LexicalUnits.EOF:
+                if (inSheet) {
+                    throw createCSSParseException("eof");
+                }
+                return;
+            case LexicalUnits.RIGHT_CURLY_BRACE:
+                if (!inSheet) {
+                    throw createCSSParseException("eof.expected");
+                }
+                /* BEGIN Modification for Theme Editor: handle comment reporting bug */
+                // nextIgnoreSpaces();
+                /* END Modification for Theme Editor */
+                return;
+            case LexicalUnits.SEMI_COLON:
+                nextIgnoreSpaces();
+                continue;
+            default:
+                throw createCSSParseException("identifier");
+            case LexicalUnits.IDENTIFIER:
+            }
+
+            String name = scanner.getStringValue();
+        
+            if (nextIgnoreSpaces() != LexicalUnits.COLON) {
+                throw createCSSParseException("colon");
+            }
+            /* BEGIN Modification for Theme Editor */
+            scanner.clearCustomBuffer();
+            String propertyString = null;
+            int propertyLine = scanner.getLine();
+            /* END Modification for Theme Editor */
+            nextIgnoreSpaces();
+        
+            LexicalUnit exp = null;
+            
+            try {
+                exp = parseExpression(false);
+                /* BEGIN Modification for Theme Editor */
+                propertyString = scanner.getCustomBufferData();
+                /* END Modification for Theme Editor */
+            } catch (CSSParseException e) {
+                reportError(e);
+            }
+
+            if (exp != null) {
+                boolean important = false;
+                if (current == LexicalUnits.IMPORTANT_SYMBOL) {
+                    important = true;
+                    nextIgnoreSpaces();
+                }
+                documentHandler.property(name, exp, important);
+                /* BEGIN Modification for Theme Editor */
+                ParserUtil.handlePropertyString( documentHandler,
+                                                 name,
+                                                 propertyString,
+                                                 propertyLine );
+                /* END Modification for Theme Editor */
+            }
+        }
+    }
+
+    /**
+     * Parses a CSS2 expression.
+     * @param param whether the expression to be parsed is a function parameter
+     */
+    protected LexicalUnit parseExpression(boolean param) {
+        LexicalUnit result = parseTerm(null);
+        LexicalUnit curr = result;
+
+        for (;;) {
+            boolean op = false;
+            switch (current) {
+            case LexicalUnits.COMMA:
+                op = true;
+                curr = CSSLexicalUnit.createSimple
+                    (LexicalUnit.SAC_OPERATOR_COMMA, curr);
+                nextIgnoreSpaces();
+                break;
+            case LexicalUnits.DIVIDE:
+                op = true;
+                curr = CSSLexicalUnit.createSimple
+                    (LexicalUnit.SAC_OPERATOR_SLASH, curr);
+                nextIgnoreSpaces();
+            }
+            if (param) {
+                if (current == LexicalUnits.RIGHT_BRACE) {
+                    if (op) {
+                        throw createCSSParseException
+                            ("token", new Object[] { new Integer(current) });
+                    }
+                    return result;
+                }
+                curr = parseTerm(curr);
+            } else {
+                switch (current) {
+                case LexicalUnits.IMPORTANT_SYMBOL:
+                case LexicalUnits.SEMI_COLON:
+                case LexicalUnits.RIGHT_CURLY_BRACE:
+                case LexicalUnits.EOF:
+                    if (op) {
+                        throw createCSSParseException
+                            ("token", new Object[] { new Integer(current) });
+                    }
+                    return result;
+                default:
+                    curr = parseTerm(curr);
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses a CSS2 term.
+     */
+    protected LexicalUnit parseTerm(LexicalUnit prev) {
+        boolean plus = true;
+        boolean sgn = false;
+
+        switch (current) {
+        case LexicalUnits.MINUS:
+            plus = false;
+        case LexicalUnits.PLUS:
+            next();
+            sgn = true;
+        default:
+            switch (current) {
+            case LexicalUnits.INTEGER:
+                String sval = scanner.getStringValue();
+                if (!plus) sval = "-"+sval;
+                int val = Integer.parseInt(sval);
+                nextIgnoreSpaces();
+                return CSSLexicalUnit.createInteger(val, prev);
+            case LexicalUnits.REAL:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_REAL,
+                                                  number(plus), prev);
+            case LexicalUnits.PERCENTAGE:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_PERCENTAGE,
+                                                  number(plus), prev);
+            case LexicalUnits.PT:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_POINT,
+                                                  number(plus), prev);
+            case LexicalUnits.PC:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_PICA,
+                                                  number(plus), prev);
+            case LexicalUnits.PX:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_PIXEL,
+                                                  number(plus), prev);
+            case LexicalUnits.CM:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_CENTIMETER,
+                                                  number(plus), prev);
+            case LexicalUnits.MM:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_MILLIMETER,
+                                                  number(plus), prev);
+            case LexicalUnits.IN:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_INCH,
+                                                  number(plus), prev);
+            case LexicalUnits.EM:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_EM,
+                                                  number(plus), prev);
+            case LexicalUnits.EX:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_EX,
+                                                  number(plus), prev);
+            case LexicalUnits.DEG:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_DEGREE,
+                                                  number(plus), prev);
+            case LexicalUnits.RAD:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_RADIAN,
+                                                  number(plus), prev);
+            case LexicalUnits.GRAD:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_GRADIAN,
+                                                  number(plus), prev);
+            case LexicalUnits.S:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_SECOND,
+                                                  number(plus), prev);
+            case LexicalUnits.MS:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_MILLISECOND,
+                                                  number(plus), prev);
+            case LexicalUnits.HZ:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_HERTZ,
+                                                  number(plus), prev);
+            case LexicalUnits.KHZ:
+                return CSSLexicalUnit.createFloat(LexicalUnit.SAC_KILOHERTZ,
+                                                  number(plus), prev);
+            case LexicalUnits.DIMENSION:
+                return dimension(plus, prev);
+            case LexicalUnits.FUNCTION:
+                return parseFunction(plus, prev);
+            }
+            if (sgn) {
+                throw createCSSParseException
+                    ("token",
+                     new Object[] { new Integer(current) });
+            }
+        }
+        switch (current) {
+        case LexicalUnits.STRING:
+            String val = scanner.getStringValue();
+            nextIgnoreSpaces();
+            return CSSLexicalUnit.createString(LexicalUnit.SAC_STRING_VALUE,
+                                               val, prev);
+        case LexicalUnits.IDENTIFIER:
+            val = scanner.getStringValue();
+            nextIgnoreSpaces();
+            if (val.equalsIgnoreCase("inherit")) {
+                return CSSLexicalUnit.createSimple(LexicalUnit.SAC_INHERIT,
+                                                   prev);
+            } else {
+                return CSSLexicalUnit.createString(LexicalUnit.SAC_IDENT,
+                                                   val, prev);
+            }
+        case LexicalUnits.URI:
+            val = scanner.getStringValue();
+            nextIgnoreSpaces();
+            return CSSLexicalUnit.createString(LexicalUnit.SAC_URI,
+                                               val, prev);
+        case LexicalUnits.HASH:
+            return hexcolor(prev);
+        default:
+            throw createCSSParseException
+                ("token",
+                 new Object[] { new Integer(current) });
+        }
+    }
+
+    /**
+     * Parses a CSS2 function.
+     */
+    protected LexicalUnit parseFunction(boolean positive, LexicalUnit prev) {
+        String name = scanner.getStringValue();
+        nextIgnoreSpaces();
+        
+        LexicalUnit params = parseExpression(true);
+
+        if (current != LexicalUnits.RIGHT_BRACE) {
+            throw createCSSParseException
+                ("token",
+                 new Object[] { new Integer(current) });
+        }
+        nextIgnoreSpaces();
+
+        predefined: switch (name.charAt(0)) {
+        case 'r':
+        case 'R':
+            LexicalUnit lu;
+            if (name.equalsIgnoreCase("rgb")) {
+                lu = params;
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu != null) {
+                    break;
+                }
+                return CSSLexicalUnit.createPredefinedFunction
+                    (LexicalUnit.SAC_RGBCOLOR, params, prev);
+            } else if (name.equalsIgnoreCase("rect")) {
+                lu = params;
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                    if (lu.getIntegerValue() != 0) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_IDENT:
+                    if (!lu.getStringValue().equalsIgnoreCase("auto")) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_EM:
+                case LexicalUnit.SAC_EX:
+                case LexicalUnit.SAC_PIXEL:
+                case LexicalUnit.SAC_CENTIMETER:
+                case LexicalUnit.SAC_MILLIMETER:
+                case LexicalUnit.SAC_INCH:
+                case LexicalUnit.SAC_POINT:
+                case LexicalUnit.SAC_PICA:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                    if (lu.getIntegerValue() != 0) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_IDENT:
+                    if (!lu.getStringValue().equalsIgnoreCase("auto")) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_EM:
+                case LexicalUnit.SAC_EX:
+                case LexicalUnit.SAC_PIXEL:
+                case LexicalUnit.SAC_CENTIMETER:
+                case LexicalUnit.SAC_MILLIMETER:
+                case LexicalUnit.SAC_INCH:
+                case LexicalUnit.SAC_POINT:
+                case LexicalUnit.SAC_PICA:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                    if (lu.getIntegerValue() != 0) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_IDENT:
+                    if (!lu.getStringValue().equalsIgnoreCase("auto")) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_EM:
+                case LexicalUnit.SAC_EX:
+                case LexicalUnit.SAC_PIXEL:
+                case LexicalUnit.SAC_CENTIMETER:
+                case LexicalUnit.SAC_MILLIMETER:
+                case LexicalUnit.SAC_INCH:
+                case LexicalUnit.SAC_POINT:
+                case LexicalUnit.SAC_PICA:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_INTEGER:
+                    if (lu.getIntegerValue() != 0) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_IDENT:
+                    if (!lu.getStringValue().equalsIgnoreCase("auto")) {
+                        break predefined;
+                    }
+                    lu = lu.getNextLexicalUnit();
+                    break;
+                case LexicalUnit.SAC_EM:
+                case LexicalUnit.SAC_EX:
+                case LexicalUnit.SAC_PIXEL:
+                case LexicalUnit.SAC_CENTIMETER:
+                case LexicalUnit.SAC_MILLIMETER:
+                case LexicalUnit.SAC_INCH:
+                case LexicalUnit.SAC_POINT:
+                case LexicalUnit.SAC_PICA:
+                case LexicalUnit.SAC_PERCENTAGE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu != null) {
+                    break;
+                }
+                return CSSLexicalUnit.createPredefinedFunction
+                    (LexicalUnit.SAC_RECT_FUNCTION, params, prev);
+            }
+            break;
+        case 'c':
+        case 'C':
+            if (name.equalsIgnoreCase("counter")) {
+                lu = params;
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_IDENT:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_IDENT:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu != null) {
+                    break;
+                }
+                return CSSLexicalUnit.createPredefinedFunction
+                    (LexicalUnit.SAC_COUNTER_FUNCTION, params, prev);
+            } else if (name.equalsIgnoreCase("counters")) {
+                lu = params;
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_IDENT:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_STRING_VALUE:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_OPERATOR_COMMA:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_IDENT:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu != null) {
+                    break;
+                }
+                return CSSLexicalUnit.createPredefinedFunction
+                    (LexicalUnit.SAC_COUNTERS_FUNCTION, params, prev);
+            }
+            break;
+        case 'a':
+        case 'A':
+            if (name.equalsIgnoreCase("attr")) {
+                lu = params;
+                if (lu == null) {
+                    break;
+                }
+                switch (lu.getLexicalUnitType()) {
+                default:
+                    break predefined;
+                case LexicalUnit.SAC_IDENT:
+                    lu = lu.getNextLexicalUnit();
+                }
+                if (lu != null) {
+                    break;
+                }
+                return CSSLexicalUnit.createString
+                    (LexicalUnit.SAC_ATTR, params.getStringValue(), prev);
+            }
+        }
+
+        return CSSLexicalUnit.createFunction(name, params, prev);
+    }
+
+    /**
+     * Converts a hash unit to a RGB color.
+     */
+    protected LexicalUnit hexcolor(LexicalUnit prev) {
+        String val = scanner.getStringValue();
+        int len = val.length();
+        LexicalUnit params = null;
+        switch (len) {
+        case 3:
+            char rc = Character.toLowerCase(val.charAt(0));
+            char gc = Character.toLowerCase(val.charAt(1));
+            char bc = Character.toLowerCase(val.charAt(2));
+            if (!ScannerUtilities.isCSSHexadecimalCharacter(rc) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(gc) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(bc)) {
+                throw createCSSParseException
+                    ("rgb.color", new Object[] { val });
+            }
+            int t;
+            int r = t = (rc >= '0' && rc <= '9') ? rc - '0' : rc - 'a' + 10;
+            t <<= 4;
+            r |= t;
+            int g = t = (gc >= '0' && gc <= '9') ? gc - '0' : gc - 'a' + 10;
+            t <<= 4;
+            g |= t;
+            int b = t = (bc >= '0' && bc <= '9') ? bc - '0' : bc - 'a' + 10;
+            t <<= 4;
+            b |= t;
+            params = CSSLexicalUnit.createInteger(r, null);
+            LexicalUnit tmp;
+            tmp = CSSLexicalUnit.createSimple
+                (LexicalUnit.SAC_OPERATOR_COMMA, params);
+            tmp = CSSLexicalUnit.createInteger(g, tmp);
+            tmp = CSSLexicalUnit.createSimple
+                (LexicalUnit.SAC_OPERATOR_COMMA, tmp);
+            tmp = CSSLexicalUnit.createInteger(b, tmp);
+            break;
+        case 6:
+            char rc1 = Character.toLowerCase(val.charAt(0));
+            char rc2 = Character.toLowerCase(val.charAt(1));
+            char gc1 = Character.toLowerCase(val.charAt(2));
+            char gc2 = Character.toLowerCase(val.charAt(3));
+            char bc1 = Character.toLowerCase(val.charAt(4));
+            char bc2 = Character.toLowerCase(val.charAt(5));
+            if (!ScannerUtilities.isCSSHexadecimalCharacter(rc1) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(rc2) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(gc1) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(gc2) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(bc1) ||
+                !ScannerUtilities.isCSSHexadecimalCharacter(bc2)) {
+                throw createCSSParseException("rgb.color");
+            }
+            r = (rc1 >= '0' && rc1 <= '9') ? rc1 - '0' : rc1 - 'a' + 10;
+            r <<= 4;
+            r |= (rc2 >= '0' && rc2 <= '9') ? rc2 - '0' : rc2 - 'a' + 10;
+            g = (gc1 >= '0' && gc1 <= '9') ? gc1 - '0' : gc1 - 'a' + 10;
+            g <<= 4;
+            g |= (gc2 >= '0' && gc2 <= '9') ? gc2 - '0' : gc2 - 'a' + 10;
+            b = (bc1 >= '0' && bc1 <= '9') ? bc1 - '0' : bc1 - 'a' + 10;
+            b <<= 4;
+            b |= (bc2 >= '0' && bc2 <= '9') ? bc2 - '0' : bc2 - 'a' + 10;
+            params = CSSLexicalUnit.createInteger(r, null);
+            tmp = CSSLexicalUnit.createSimple
+                (LexicalUnit.SAC_OPERATOR_COMMA, params);
+            tmp = CSSLexicalUnit.createInteger(g, tmp);
+            tmp = CSSLexicalUnit.createSimple
+                (LexicalUnit.SAC_OPERATOR_COMMA, tmp);
+            tmp = CSSLexicalUnit.createInteger(b, tmp);
+            break;
+        default:
+            throw createCSSParseException("rgb.color", new Object[] { val });
+        }
+        nextIgnoreSpaces();
+        return CSSLexicalUnit.createPredefinedFunction
+            (LexicalUnit.SAC_RGBCOLOR, params, prev);
+    }
+
+    /**
+     * Creates a scanner, given an InputSource.
+     */
+    protected Scanner createScanner(InputSource source) {
+        documentURI = source.getURI();
+        if (documentURI == null) {
+            documentURI = "";
+        }
+
+        Reader r = source.getCharacterStream();
+        if (r != null) {
+            return new Scanner(r);
+        }
+
+        InputStream is = source.getByteStream();
+        if (is != null) {
+            return new Scanner(is, source.getEncoding());
+        }
+
+        String uri = source.getURI();
+        if (uri == null) {
+            throw new CSSException(formatMessage("empty.source", null));
+        }
+
+//        try {
+//            ParsedURL purl = new ParsedURL(uri);
+//            is = purl.openStreamRaw(CSSConstants.CSS_MIME_TYPE);
+//            return new Scanner(is, source.getEncoding());
+//        } catch (IOException e) {
+//            throw new CSSException(e);
+//        }
+        return null;
+    }
+
+    /**
+     * Skips the white spaces.
+     */
+    protected int skipSpaces() {
+        int lex = scanner.getType();
+        while (lex == LexicalUnits.SPACE) {
+            lex = next();
+        }
+        return lex;
+    }
+
+    /**
+     * Skips the white spaces and CDO/CDC units.
+     */
+    protected int skipSpacesAndCDOCDC() {
+        loop: for (;;) {
+            switch (current) {
+            default:
+                break loop;
+            case LexicalUnits.COMMENT:
+            case LexicalUnits.SPACE:
+            case LexicalUnits.CDO:
+            case LexicalUnits.CDC:
+            }
+            scanner.clearBuffer();
+            next();
+        }
+        return current;
+    }
+
+    /**
+     * Converts the current lexical unit to a float.
+     */
+    protected float number(boolean positive) {
+        try {
+            float sgn = (positive) ? 1 : -1;
+            String val = scanner.getStringValue();
+            nextIgnoreSpaces();
+            return sgn * Float.parseFloat(val);
+        } catch (NumberFormatException e) {
+            throw createCSSParseException("number.format");
+        }
+    }
+
+    /**
+     * Converts the current lexical unit to a dimension.
+     */
+    protected LexicalUnit dimension(boolean positive, LexicalUnit prev) {
+        try {
+            float sgn = (positive) ? 1 : -1;
+            String val = scanner.getStringValue();
+            int i;
+            loop: for (i = 0; i < val.length(); i++) {
+                switch (val.charAt(i)) {
+                default:
+                    break loop;
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                case '.':
+                }
+            }
+            nextIgnoreSpaces();
+            return CSSLexicalUnit.createDimension
+                (sgn * Float.parseFloat(val.substring(0, i)),
+                 val.substring(i),
+                 prev);
+        } catch (NumberFormatException e) {
+            throw createCSSParseException("number.format");
+        }
+    }
+
+    /**
+     * Advances to the next token, ignoring comments.
+     */
+    protected int next() {
+        try {
+            for (;;) {
+                scanner.clearBuffer();
+                current = scanner.next();
+                if (current == LexicalUnits.COMMENT) {
+                    documentHandler.comment(scanner.getStringValue());
+                } else {
+                    break;
+                }
+            }
+            return current;
+        } catch (ParseException e) {
+            reportError(e.getMessage());
+            return current;
+        }
+    }
+
+    /**
+     * Advances to the next token and skip the spaces, ignoring comments.
+     */
+    protected int nextIgnoreSpaces() {
+        try {
+            loop: for (;;) {
+                scanner.clearBuffer();
+                current = scanner.next();
+                switch (current) {
+                case LexicalUnits.COMMENT:
+                    documentHandler.comment(scanner.getStringValue());
+                    break;
+                default:
+                    break loop;
+                case LexicalUnits.SPACE:
+                }
+            }
+            return current;
+        } catch (ParseException e) {
+            errorHandler.error(createCSSParseException(e.getMessage()));
+            return current;
+        }
+    }
+
+    /**
+     * Reports a parsing error.
+     */
+    protected void reportError(String key) {
+        reportError(key, null);
+    }
+
+    /**
+     * Reports a parsing error.
+     */
+    protected void reportError(String key, Object[] params) {
+        reportError(createCSSParseException(key, params));
+    }
+
+    /**
+     * Reports a parsing error.
+     */
+    protected void reportError(CSSParseException e) {
+        errorHandler.error(e);
+
+        int cbraces = 1;
+        for (;;) {
+            switch (current) {
+            case LexicalUnits.EOF:
+                return;
+            case LexicalUnits.SEMI_COLON:
+            case LexicalUnits.RIGHT_CURLY_BRACE:
+                if (--cbraces == 0) {
+                    nextIgnoreSpaces();
+                    return;
+                }
+            case LexicalUnits.LEFT_CURLY_BRACE:
+                cbraces++;
+            }
+            nextIgnoreSpaces();
+        }
+    }
+
+    /**
+     * Creates a parse exception.
+     */
+    protected CSSParseException createCSSParseException(String key) {
+        return createCSSParseException(key, null);
+    }
+
+    /**
+     * Creates a parse exception.
+     */
+    protected CSSParseException createCSSParseException(String key,
+                                                        Object[] params) {
+        return new CSSParseException(formatMessage(key, params),
+                                     documentURI,
+                                     scanner.getLine(),
+                                     scanner.getColumn());
+    }
+
+    // -----------------------------------------------------------------------
+    // Extended methods
+    // -----------------------------------------------------------------------
+    
+    /**
+     * Implements {@link ExtendedParser#parseStyleDeclaration(String)}.
+     */
+    public void parseStyleDeclaration(String source) 
+	throws CSSException, IOException {
+        scanner = new Scanner(source);
+	parseStyleDeclarationInternal();
+    }
+
+    /**
+     * Implements {@link ExtendedParser#parseRule(String)}.
+     */
+    public void parseRule(String source) throws CSSException, IOException {
+        scanner = new Scanner(source);
+	parseRuleInternal();
+    }
+    
+    /**
+     * Implements {@link ExtendedParser#parseSelectors(String)}.
+     */
+    public SelectorList parseSelectors(String source)
+        throws CSSException, IOException {
+        scanner = new Scanner(source);
+	return parseSelectorsInternal();
+    }
+
+    /**
+     * Implements {@link ExtendedParser#parsePropertyValue(String)}.
+     */
+    public LexicalUnit parsePropertyValue(String source)
+        throws CSSException, IOException {
+        scanner = new Scanner(source);
+	return parsePropertyValueInternal();
+    }
+
+    /**
+     * Implements {@link ExtendedParser#parsePriority(String)}.
+     */
+    public boolean parsePriority(String source)
+        throws CSSException, IOException {
+        scanner = new Scanner(source);
+	return parsePriorityInternal();
+    }
+
+    /**
+     * Implements {@link ExtendedParser#parseMedia(String)}.
+     */
+    public SACMediaList parseMedia(String mediaText)
+        throws CSSException, IOException {
+        CSSSACMediaList result = new CSSSACMediaList();
+        if (!"all".equalsIgnoreCase(mediaText)) {
+            StringTokenizer st = new StringTokenizer(mediaText, " ,");
+            while (st.hasMoreTokens()) {
+                result.append(st.nextToken());
+            }
+        }
+        return result;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Scanner.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Scanner.java
new file mode 100644
index 0000000..f2332f0
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/Scanner.java
@@ -0,0 +1,1319 @@
+/*
+
+   Copyright 2000-2004  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.css.parser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import org.apache.batik.util.io.NormalizingReader;
+import org.apache.batik.util.io.StreamNormalizingReader;
+import org.apache.batik.util.io.StringNormalizingReader;
+
+/**
+ * This class represents a CSS scanner - an object which decodes CSS lexical
+ * units.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: Scanner.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class Scanner {
+
+    /**
+     * The reader.
+     */
+    protected NormalizingReader reader;
+
+    /**
+     * The current char.
+     */
+    protected int current;
+
+    /**
+     * The recording buffer.
+     */
+    protected char[] buffer = new char[128];
+
+    /**
+     * The current position in the buffer.
+     */
+    protected int position;
+
+    /**
+     * The type of the current lexical unit.
+     */
+    protected int type;
+
+    /**
+     * The start offset of the last lexical unit.
+     */
+    protected int start;
+
+    /**
+     * The end offset of the last lexical unit.
+     */
+    protected int end;
+
+    /**
+     * The characters to skip to create the string which represents the
+     * current token.
+     */
+    protected int blankCharacters;
+
+    /**
+     * Creates a new Scanner object.
+     * @param r The reader to scan.
+     */
+    public Scanner(Reader r) throws ParseException {
+        try {
+            reader = new StreamNormalizingReader(r);
+            current = nextChar();
+        } catch (IOException e) {
+            throw new ParseException(e);
+        }
+    }
+
+    /**
+     * Creates a new Scanner object.
+     * @param is The input stream to scan.
+     * @param enc The encoding to use to decode the input stream, or null.
+     */
+    public Scanner(InputStream is, String enc) throws ParseException {
+        try {
+            reader = new StreamNormalizingReader(is, enc);
+            current = nextChar();
+        } catch (IOException e) {
+            throw new ParseException(e);
+        }
+    }
+
+    /**
+     * Creates a new Scanner object.
+     * @param s The string to scan.
+     */
+    public Scanner(String s) throws ParseException {
+        try {
+            reader = new StringNormalizingReader(s);
+            current = nextChar(); 
+        } catch (IOException e) {
+            throw new ParseException(e);
+        }
+    }
+
+    /**
+     * Returns the current line.
+     */
+    public int getLine() {
+        return reader.getLine();
+    }
+
+    /**
+     * Returns the current column.
+     */
+    public int getColumn() {
+        return reader.getColumn();
+    }
+
+    /**
+     * Returns the buffer used to store the chars.
+     */
+    public char[] getBuffer() {
+        return buffer;
+    }
+
+    /**
+     * Returns the start offset of the last lexical unit.
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Returns the end offset of the last lexical unit.
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Clears the buffer.
+     */
+    public void clearBuffer() {
+        if (position <= 0) {
+            position = 0;
+        } else {
+            buffer[0] = buffer[position-1];
+            position = 1;
+        }
+    }
+
+    /**
+     * The current lexical unit type like defined in LexicalUnits.
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Returns the string representation of the current lexical unit.
+     */
+    public String getStringValue() {
+        return new String(buffer, start, end - start);
+    }
+
+    /**
+     * Scans a @rule value. This method assumes that the current
+     * lexical unit is a at keyword.
+     */
+    public void scanAtRule() throws ParseException {
+        try {
+            // waiting for EOF, ';' or '{'
+            loop: for (;;) {
+                switch (current) {
+                case '{':
+                    int brackets = 1;
+                    for (;;) {
+                        nextChar();
+                        switch (current) {
+                        case '}':
+                            if (--brackets > 0) {
+                                break;
+                            }
+                        case -1:
+                            break loop;
+                        case '{':
+                            brackets++;
+                        }
+                    }
+                case -1:
+                case ';':
+                    break loop;
+                }
+                nextChar();
+            }
+            end = position;
+        } catch (IOException e) {
+            throw new ParseException(e);
+        }
+    }
+    
+    /**
+     * Returns the next token.
+     */
+    public int next() throws ParseException {
+        blankCharacters = 0;
+        start = position - 1;
+        nextToken();
+        end = position - endGap();
+        return type;
+    }
+
+    /**
+     * Returns the end gap of the current lexical unit.
+     */
+    protected int endGap() {
+        int result = (current == -1) ? 0 : 1;
+        switch (type) {
+        case LexicalUnits.FUNCTION:
+        case LexicalUnits.STRING:
+        case LexicalUnits.S:
+        case LexicalUnits.PERCENTAGE:
+            result += 1;
+            break;
+        case LexicalUnits.COMMENT:
+        case LexicalUnits.HZ:
+        case LexicalUnits.EM:
+        case LexicalUnits.EX:
+        case LexicalUnits.PC:
+        case LexicalUnits.PT:
+        case LexicalUnits.PX:
+        case LexicalUnits.CM:
+        case LexicalUnits.MM:
+        case LexicalUnits.IN:
+        case LexicalUnits.MS:
+            result += 2;
+            break;
+        case LexicalUnits.KHZ:
+        case LexicalUnits.DEG:
+        case LexicalUnits.RAD:
+            result += 3;
+            break;
+        case LexicalUnits.GRAD:
+            result += 4;
+        }
+        return result + blankCharacters;
+    }
+
+    /**
+     * Returns the next token.
+     */
+    protected void nextToken() throws ParseException {
+        try {
+            switch (current) {
+            case -1:
+                type = LexicalUnits.EOF;
+                return;
+            case '{':
+                nextChar();
+                type = LexicalUnits.LEFT_CURLY_BRACE;
+                return;
+            case '}':
+                nextChar();
+                type = LexicalUnits.RIGHT_CURLY_BRACE;
+                return;
+            case '=':
+                nextChar();
+                type = LexicalUnits.EQUAL;
+                return;
+            case '+':
+                nextChar();
+                type = LexicalUnits.PLUS;
+                return;
+            case ',':
+                nextChar();
+                type = LexicalUnits.COMMA;
+                return;
+            case ';':
+                nextChar();
+                type = LexicalUnits.SEMI_COLON;
+                return;
+            case '>':
+                nextChar();
+                type = LexicalUnits.PRECEDE;
+                return;
+            case '[':
+                nextChar();
+                type = LexicalUnits.LEFT_BRACKET;
+                return;
+            case ']':
+                nextChar();
+                type = LexicalUnits.RIGHT_BRACKET;
+                return;
+            case '*':
+                nextChar();
+                type = LexicalUnits.ANY;
+                return;
+            case '(':
+                nextChar();
+                type = LexicalUnits.LEFT_BRACE;
+                return;
+            case ')':
+                nextChar();
+                type = LexicalUnits.RIGHT_BRACE;
+                return;
+            case ':':
+                nextChar();
+                type = LexicalUnits.COLON;
+                return;
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+            case '\f':
+                do {
+                    nextChar();
+                } while (ScannerUtilities.isCSSSpace((char)current));
+                type = LexicalUnits.SPACE;
+                return;
+            case '/':
+                nextChar();
+                if (current != '*') {
+                    type = LexicalUnits.DIVIDE;
+                    return;
+                }
+                // Comment
+                nextChar();
+                start = position - 1;
+                do {
+                    while (current != -1 && current != '*') {
+                        nextChar();
+                    }
+                    do {
+                        nextChar();
+                    } while (current != -1 && current == '*');
+                } while (current != -1 && current != '/');
+                if (current == -1) {
+                    throw new ParseException("eof",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                }
+                nextChar();
+                type = LexicalUnits.COMMENT; 
+                return;
+            case '\'': // String1
+                type = string1();
+                return;
+            case '"': // String2
+                type = string2();
+                return;
+            case '<':
+                nextChar();
+                if (current != '!') {
+                    throw new ParseException("character",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                }
+                nextChar();
+                if (current == '-') {
+                    nextChar();
+                    if (current == '-') {
+                        nextChar();
+                        type = LexicalUnits.CDO;
+                        return;
+                    }
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '-':
+                nextChar();
+                if (current != '-') {
+                    type = LexicalUnits.MINUS;
+                    return;
+                }
+                nextChar();
+                if (current == '>') {
+                    nextChar();
+                    type = LexicalUnits.CDC;
+                    return;
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '|':
+                nextChar();
+                if (current == '=') {
+                    nextChar();
+                    type = LexicalUnits.DASHMATCH;
+                    return;
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '~':
+                nextChar();
+                if (current == '=') {
+                    nextChar();
+                    type = LexicalUnits.INCLUDES;
+                    return;
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '#':
+                nextChar();
+                if (ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    start = position - 1;
+                    do {
+                        nextChar();
+                        if (current == '\\') {
+                            nextChar();
+                            escape();
+                        }
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                                 ((char)current));
+                    type = LexicalUnits.HASH;
+                    return;
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '@':
+                nextChar();
+                switch (current) {
+                case 'c':
+                case 'C':
+                    start = position - 1;
+                    if (isEqualIgnoreCase(nextChar(), 'h') &&
+                        isEqualIgnoreCase(nextChar(), 'a') &&
+                        isEqualIgnoreCase(nextChar(), 'r') &&
+                        isEqualIgnoreCase(nextChar(), 's') &&
+                        isEqualIgnoreCase(nextChar(), 'e') &&
+                        isEqualIgnoreCase(nextChar(), 't')) {
+                        nextChar();
+                        type = LexicalUnits.CHARSET_SYMBOL;
+                        return;
+                    }
+                    break;
+                case 'f':
+                case 'F':
+                    start = position - 1;
+                    if (isEqualIgnoreCase(nextChar(), 'o') &&
+                        isEqualIgnoreCase(nextChar(), 'n') &&
+                        isEqualIgnoreCase(nextChar(), 't') &&
+                        isEqualIgnoreCase(nextChar(), '-') &&
+                        isEqualIgnoreCase(nextChar(), 'f') &&
+                        isEqualIgnoreCase(nextChar(), 'a') &&
+                        isEqualIgnoreCase(nextChar(), 'c') &&
+                        isEqualIgnoreCase(nextChar(), 'e')) {
+                        nextChar();
+                        type = LexicalUnits.FONT_FACE_SYMBOL;
+                        return;
+                    }
+                    break;
+                case 'i':
+                case 'I':
+                    start = position - 1;
+                    if (isEqualIgnoreCase(nextChar(), 'm') &&
+                        isEqualIgnoreCase(nextChar(), 'p') &&
+                        isEqualIgnoreCase(nextChar(), 'o') &&
+                        isEqualIgnoreCase(nextChar(), 'r') &&
+                        isEqualIgnoreCase(nextChar(), 't')) {
+                        nextChar();
+                        type = LexicalUnits.IMPORT_SYMBOL;
+                        return;
+                    }
+                    break;
+                case 'm':
+                case 'M':
+                    start = position - 1;
+                    if (isEqualIgnoreCase(nextChar(), 'e') &&
+                        isEqualIgnoreCase(nextChar(), 'd') &&
+                        isEqualIgnoreCase(nextChar(), 'i') &&
+                        isEqualIgnoreCase(nextChar(), 'a')) {
+                        nextChar();
+                        type = LexicalUnits.MEDIA_SYMBOL;
+                        return;
+                    }
+                    break;
+                case 'p':
+                case 'P':
+                    start = position - 1;
+                    if (isEqualIgnoreCase(nextChar(), 'a') &&
+                        isEqualIgnoreCase(nextChar(), 'g') &&
+                        isEqualIgnoreCase(nextChar(), 'e')) {
+                        nextChar();
+                        type = LexicalUnits.PAGE_SYMBOL;
+                        return;
+                    }
+                    break;
+                default:
+                    if (!ScannerUtilities.isCSSIdentifierStartCharacter
+                        ((char)current)) {
+                        throw new ParseException("identifier.character",
+                                                 reader.getLine(),
+                                                 reader.getColumn());
+                    }
+                    start = position - 1;
+                }
+                do {
+                    nextChar();
+                    if (current == '\\') {
+                        nextChar();
+                        escape();
+                    }
+                } while (current != -1 &&
+                         ScannerUtilities.isCSSNameCharacter((char)current));
+                type = LexicalUnits.AT_KEYWORD;
+                return;
+            case '!':
+                do {
+                    nextChar();
+                } while (current != -1 &&
+                         ScannerUtilities.isCSSSpace((char)current));
+                if (isEqualIgnoreCase(current, 'i') &&
+                    isEqualIgnoreCase(nextChar(), 'm') &&
+                    isEqualIgnoreCase(nextChar(), 'p') &&
+                    isEqualIgnoreCase(nextChar(), 'o') &&
+                    isEqualIgnoreCase(nextChar(), 'r') &&
+                    isEqualIgnoreCase(nextChar(), 't') &&
+                    isEqualIgnoreCase(nextChar(), 'a') &&
+                    isEqualIgnoreCase(nextChar(), 'n') &&
+                    isEqualIgnoreCase(nextChar(), 't')) {
+                    nextChar();
+                    type = LexicalUnits.IMPORTANT_SYMBOL;
+                    return;
+                }
+                if (current == -1) {
+                    throw new ParseException("eof",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                } else {
+                    throw new ParseException("character",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                }
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+                type = number();
+                return;
+            case '.':
+                switch (nextChar()) {
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                    type = dotNumber();
+                    return;
+                default:
+                    type = LexicalUnits.DOT;
+                    return;
+                }
+            case 'u':
+            case 'U':
+                nextChar();
+                switch (current) {
+                case '+':
+                    boolean range = false;
+                    for (int i = 0; i < 6; i++) {
+                        nextChar();
+                        switch (current) {
+                        case '?':
+                            range = true;
+                            break;
+                        default:
+                            if (range &&
+                            !ScannerUtilities.isCSSHexadecimalCharacter
+                                ((char)current)) {
+                                throw new ParseException("character",
+                                                         reader.getLine(),
+                                                         reader.getColumn());
+                            }
+                        }
+                    }
+                    nextChar();
+                    if (range) {
+                        type = LexicalUnits.UNICODE_RANGE;
+                        return;
+                    }
+                    if (current == '-') {
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            throw new ParseException("character",
+                                                     reader.getLine(),
+                                                     reader.getColumn());
+                        }
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            type = LexicalUnits.UNICODE_RANGE;
+                            return;
+                        }
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            type = LexicalUnits.UNICODE_RANGE;
+                            return;
+                        }
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            type = LexicalUnits.UNICODE_RANGE;
+                            return;
+                        }
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            type = LexicalUnits.UNICODE_RANGE;
+                            return;
+                        }
+                        nextChar();
+                        if (!ScannerUtilities.isCSSHexadecimalCharacter
+                            ((char)current)) {
+                            type = LexicalUnits.UNICODE_RANGE;
+                            return;
+                        }
+                        nextChar();
+                        type = LexicalUnits.UNICODE_RANGE;
+                        return;
+                    }
+                case 'r':
+                case 'R':
+                    nextChar();
+                    switch (current) {
+                    case 'l':
+                    case 'L':
+                        nextChar();
+                        switch (current) {
+                        case '(':
+                            do {
+                                nextChar();
+                            } while (current != -1 &&
+                                     ScannerUtilities.isCSSSpace
+                                     ((char)current));
+                            switch (current) {
+                            case '\'':
+                                string1();
+                                blankCharacters += 2;
+                                while (current != -1 &&
+                                       ScannerUtilities.isCSSSpace
+                                       ((char)current)) {
+                                    blankCharacters++;
+                                    nextChar();
+                                }
+                                if (current == -1) {
+                                    throw new ParseException
+                                        ("eof",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                if (current != ')') {
+                                    throw new ParseException
+                                        ("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                nextChar();
+                                type = LexicalUnits.URI;
+                                return;
+                            case '"':
+                                string2();
+                                blankCharacters += 2;
+                                while (current != -1 &&
+                                       ScannerUtilities.isCSSSpace
+                                       ((char)current)) {
+                                    blankCharacters++;
+                                    nextChar();
+                                }
+                                if (current == -1) {
+                                    throw new ParseException
+                                        ("eof",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                if (current != ')') {
+                                    throw new ParseException
+                                        ("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                nextChar();
+                                type = LexicalUnits.URI;
+                                return;
+                            case ')':
+                                throw new ParseException("character",
+                                                         reader.getLine(),
+                                                         reader.getColumn());
+                            default:
+                                if (!ScannerUtilities.isCSSURICharacter
+                                    ((char)current)) {
+                                    throw new ParseException
+                                        ("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                start = position - 1;
+                                do {
+                                    nextChar();
+                                } while (current != -1 &&
+                                      ScannerUtilities.isCSSURICharacter
+                                         ((char)current));
+                                blankCharacters++;
+                                while (current != -1 &&
+                                       ScannerUtilities.isCSSSpace
+                                       ((char)current)) {
+                                    blankCharacters++;
+                                    nextChar();
+                                }
+                                if (current == -1) {
+                                    throw new ParseException
+                                        ("eof",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                if (current != ')') {
+                                    throw new ParseException
+                                        ("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+                                }
+                                nextChar();
+                                type = LexicalUnits.URI;
+                                return;
+                            }
+                        }
+                    }
+                }
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                if (current == '(') {
+                    nextChar();
+                    type = LexicalUnits.FUNCTION;
+                    return;
+                }
+                type = LexicalUnits.IDENTIFIER;
+                return;
+            default:
+                if (ScannerUtilities.isCSSIdentifierStartCharacter
+                    ((char)current)) {
+                    // Identifier
+                    do {
+                        nextChar();
+                        if (current == '\\') {
+                            nextChar();
+                            escape();
+                        }
+                    } while (current != -1 && 
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    if (current == '(') {
+                        nextChar();
+                        type = LexicalUnits.FUNCTION;
+                        return;
+                    }
+                    type = LexicalUnits.IDENTIFIER;
+                    return;
+                }
+                nextChar();
+                throw new ParseException("identifier.character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            }
+        } catch (IOException e) {
+            throw new ParseException(e);
+        }
+    }
+
+    /**
+     * Scans a single quoted string.
+     */
+    protected int string1() throws IOException {
+        start = position;  // fix bug #29416
+        loop: for (;;) {
+            switch (nextChar()) {
+            case -1:
+                throw new ParseException("eof",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '\'':
+                break loop;
+            case '"':
+                break;
+            case '\\':
+                switch (nextChar()) {
+                case '\n':
+                case '\f':
+                    break;
+                default:
+                    escape();
+                }
+                break;
+            default:
+                if (!ScannerUtilities.isCSSStringCharacter((char)current)) {
+                    throw new ParseException("character",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                }
+            }
+        }
+        nextChar();
+        return LexicalUnits.STRING;
+    }
+
+    /**
+     * Scans a double quoted string.
+     */
+    protected int string2() throws IOException {
+        start = position;  // fix bug #29416
+        loop: for (;;) {
+            switch (nextChar()) {
+            case -1:
+                throw new ParseException("eof",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            case '\'':
+                break;
+            case '"':
+                break loop;
+            case '\\':
+                switch (nextChar()) {
+                case '\n':
+                case '\f':
+                    break;
+                default:
+                    escape();
+                }
+                break;
+            default:
+                if (!ScannerUtilities.isCSSStringCharacter((char)current)) {
+                    throw new ParseException("character",
+                                             reader.getLine(),
+                                             reader.getColumn());
+                }
+            }
+        }
+        nextChar();
+        return LexicalUnits.STRING;
+    }
+
+    /**
+     * Scans a number.
+     */
+    protected int number() throws IOException {
+        loop: for (;;) {
+            switch (nextChar()) {
+            case '.':
+                switch (nextChar()) {
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                    return dotNumber();
+                }
+                throw new ParseException("character",
+                                         reader.getLine(),
+                                         reader.getColumn());
+            default:
+                break loop;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+            }
+        }
+        return numberUnit(true);
+    }        
+
+    /**
+     * Scans the decimal part of a number.
+     */
+    protected int dotNumber() throws IOException {
+        loop: for (;;) {
+            switch (nextChar()) {
+            default:
+                break loop;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+            }
+        }
+        return numberUnit(false);
+    }
+
+    /**
+     * Scans the unit of a number.
+     */
+    protected int numberUnit(boolean integer) throws IOException {
+        switch (current) {
+        case '%':
+            nextChar();
+            return LexicalUnits.PERCENTAGE;
+        case 'c':
+        case 'C':
+            switch(nextChar()) {
+            case 'm':
+            case 'M':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.CM;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'd':
+        case 'D':
+            switch(nextChar()) {
+            case 'e':
+            case 'E':
+                switch(nextChar()) {
+                case 'g':
+                case 'G':
+                    nextChar();
+                    if (current != -1 &&
+                        ScannerUtilities.isCSSNameCharacter((char)current)) {
+                        do {
+                            nextChar();
+                        } while (current != -1 &&
+                                 ScannerUtilities.isCSSNameCharacter
+                                 ((char)current));
+                        return LexicalUnits.DIMENSION;
+                    }
+                    return LexicalUnits.DEG;
+                }
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'e':
+        case 'E':
+            switch(nextChar()) {
+            case 'm':
+            case 'M':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.EM;
+            case 'x':
+            case 'X':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.EX;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'g':
+        case 'G':
+            switch(nextChar()) {
+            case 'r':
+            case 'R':
+                switch(nextChar()) {
+                case 'a':
+                case 'A':
+                    switch(nextChar()) {
+                    case 'd':
+                    case 'D':
+                        nextChar();
+                        if (current != -1 &&
+                            ScannerUtilities.isCSSNameCharacter
+                            ((char)current)) {
+                            do {
+                                nextChar();
+                            } while (current != -1 &&
+                                     ScannerUtilities.isCSSNameCharacter
+                                     ((char)current));
+                            return LexicalUnits.DIMENSION;
+                        }
+                        return LexicalUnits.GRAD;
+                    }
+                }
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'h':
+        case 'H':
+            nextChar();
+            switch(current) {
+            case 'z':
+            case 'Z':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.HZ;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'i':
+        case 'I':
+            switch(nextChar()) {
+            case 'n':
+            case 'N':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.IN;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'k':
+        case 'K':
+            switch(nextChar()) {
+            case 'h':
+            case 'H':
+                switch(nextChar()) {
+                case 'z':
+                case 'Z':
+                    nextChar();
+                    if (current != -1 &&
+                        ScannerUtilities.isCSSNameCharacter((char)current)) {
+                        do {
+                            nextChar();
+                        } while (current != -1 &&
+                                 ScannerUtilities.isCSSNameCharacter
+                                 ((char)current));
+                        return LexicalUnits.DIMENSION;
+                    }
+                    return LexicalUnits.KHZ;
+                }
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'm':
+        case 'M':
+            switch(nextChar()) {
+            case 'm':
+            case 'M':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.MM;
+            case 's':
+            case 'S':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.MS;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 'p':
+        case 'P':
+            switch(nextChar()) {
+            case 'c':
+            case 'C':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.PC;
+            case 't':
+            case 'T':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.PT;
+            case 'x':
+            case 'X':
+                nextChar();
+                if (current != -1 &&
+                    ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    do {
+                        nextChar();
+                    } while (current != -1 &&
+                             ScannerUtilities.isCSSNameCharacter
+                             ((char)current));
+                    return LexicalUnits.DIMENSION;
+                }
+                return LexicalUnits.PX;
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }            
+        case 'r':
+        case 'R':
+            switch(nextChar()) {
+            case 'a':
+            case 'A':
+                switch(nextChar()) {
+                case 'd':
+                case 'D':
+                    nextChar();
+                    if (current != -1 &&
+                        ScannerUtilities.isCSSNameCharacter((char)current)) {
+                        do {
+                            nextChar();
+                        } while (current != -1 &&
+                                 ScannerUtilities.isCSSNameCharacter
+                                 ((char)current));
+                        return LexicalUnits.DIMENSION;
+                    }
+                    return LexicalUnits.RAD;
+                }
+            default:
+                while (current != -1 &&
+                       ScannerUtilities.isCSSNameCharacter((char)current)) {
+                    nextChar();
+                }
+                return LexicalUnits.DIMENSION;
+            }
+        case 's':
+        case 'S':
+            nextChar();
+            return LexicalUnits.S;
+        default:
+            if (current != -1 &&
+                ScannerUtilities.isCSSIdentifierStartCharacter
+                ((char)current)) {
+                do {
+                    nextChar();
+                } while (current != -1 &&
+                         ScannerUtilities.isCSSNameCharacter((char)current));
+                return LexicalUnits.DIMENSION;
+            }
+            return (integer) ? LexicalUnits.INTEGER : LexicalUnits.REAL;
+        }
+    }
+
+    /**
+     * Scans an escape sequence, if one.
+     */
+    protected void escape() throws IOException {
+        if (ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+            nextChar();
+            if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+                if (ScannerUtilities.isCSSSpace((char)current)) {
+                    nextChar();
+                }
+                return;
+            }
+            nextChar();
+            if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+                if (ScannerUtilities.isCSSSpace((char)current)) {
+                    nextChar();
+                }
+                return;
+            }
+            nextChar();
+            if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+                if (ScannerUtilities.isCSSSpace((char)current)) {
+                    nextChar();
+                }
+                return;
+            }
+            nextChar();
+            if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+                if (ScannerUtilities.isCSSSpace((char)current)) {
+                    nextChar();
+                }
+                return;
+            }
+            nextChar();
+            if (!ScannerUtilities.isCSSHexadecimalCharacter((char)current)) {
+                if (ScannerUtilities.isCSSSpace((char)current)) {
+                    nextChar();
+                }
+                return;
+            }
+        }
+        if ((current >= ' ' && current <= '~') || current >= 128) {
+            nextChar();
+            return;
+        }
+        throw new ParseException("character",
+                                 reader.getLine(),
+                                 reader.getColumn());
+    }
+
+    /**
+     * Compares the given int with the given character, ignoring case.
+     */
+    protected static boolean isEqualIgnoreCase(int i, char c) {
+        return (i == -1) ? false : Character.toLowerCase((char)i) == c;
+    }
+
+    /**
+     * Sets the value of the current char to the next character or -1 if the
+     * end of stream has been reached.
+     */
+    protected int nextChar() throws IOException {
+        current = reader.read();
+
+        if (current == -1) {
+            return current;
+        }
+
+        if (position == buffer.length) {
+            char[] t = new char[position * 3 / 2];
+            for (int i = 0; i < position; i++) {
+                t[i] = buffer[i];
+            }
+            buffer = t;
+        }
+
+        /* BEGIN Modification for Theme Editor */
+        customBuffer.append( (char)current );
+        /* END Modification for Theme Editor */
+
+        return buffer[position++] = (char)current;
+    }
+
+    /* BEGIN Modification for Theme Editor */
+    protected StringBuffer customBuffer = new StringBuffer();
+
+    public void clearCustomBuffer() {
+      customBuffer = new StringBuffer();
+    }
+
+    public String getCustomBufferData() {
+      return customBuffer.toString();
+    }
+    /* END Modification for Theme Editor */
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ScannerUtilities.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ScannerUtilities.java
new file mode 100644
index 0000000..d6e139a
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/css/parser/ScannerUtilities.java
@@ -0,0 +1,106 @@
+/*
+
+   Copyright 1999-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.batik.css.parser;
+
+/**
+ * A collection of utility functions for a CSS scanner.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: ScannerUtilities.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class ScannerUtilities {
+
+    /**
+     * The set of the valid identifier start characters.
+     */
+    protected final static int[] IDENTIFIER_START = { 0, 0, 134217726, 134217726 };
+
+    /**
+     * The set of the valid name characters.
+     */
+    protected final static int[] NAME = { 0, 67051520, 134217726, 134217726 };
+
+    /**
+     * The set of the valid hexadecimal characters.
+     */
+    protected final static int[] HEXADECIMAL = { 0, 67043328, 126, 126 };
+
+    /**
+     * The set of the valid string characters.
+     */
+    protected final static int[] STRING = { 512, -133, -1, 2147483647 };
+
+    /**
+     * The set of the valid uri characters.
+     */
+    protected final static int[] URI = { 0, -902, -1, 2147483647 };
+
+    /**
+     * This class does not need to be instantiated.
+     */
+    protected ScannerUtilities() {
+    }
+
+    /**
+     * Tests whether the given character is a valid space.
+     */
+    public static boolean isCSSSpace(char c) {
+      return (c <= 0x0020) &&
+             (((((1L << '\t') |
+                 (1L << '\n') |
+                 (1L << '\r') |
+                 (1L << '\f') |
+                 (1L << 0x0020)) >> c) & 1L) != 0);
+    }
+
+    /**
+     * Tests whether the given character is a valid identifier start character.
+     */
+    public static boolean isCSSIdentifierStartCharacter(char c) {
+	return c >= 128 || ((IDENTIFIER_START[c / 32] & (1 << (c % 32))) != 0);
+    }
+
+    /**
+     * Tests whether the given character is a valid name character.
+     */
+    public static boolean isCSSNameCharacter(char c) {
+	return c >= 128 || ((NAME[c / 32] & (1 << (c % 32))) != 0);
+    }
+
+    /**
+     * Tests whether the given character is a valid hexadecimal character.
+     */
+    public static boolean isCSSHexadecimalCharacter(char c) {
+	return c < 128 && ((HEXADECIMAL[c / 32] & (1 << (c % 32))) != 0);
+    }
+
+    /**
+     * Tests whether the given character is a valid string character.
+     */
+    public static boolean isCSSStringCharacter(char c) {
+	return c >= 128 || ((STRING[c / 32] & (1 << (c % 32))) != 0);
+    }
+
+    /**
+     * Tests whether the given character is a valid URI character.
+     */
+    public static boolean isCSSURICharacter(char c) {
+	return c >= 128 || ((URI[c / 32] & (1 << (c % 32))) != 0);
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ASCIIDecoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ASCIIDecoder.java
new file mode 100644
index 0000000..eae2e20
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ASCIIDecoder.java
@@ -0,0 +1,56 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class represents an object which decodes ASCII characters from
+ * a stream of bytes.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: ASCIIDecoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class ASCIIDecoder extends AbstractCharDecoder {
+    
+    /**
+     * Creates a new ASCIIDecoder.
+     */
+    public ASCIIDecoder(InputStream is) {
+        super(is);
+    }
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    public int readChar() throws IOException {
+        if (position == count) {
+            fillBuffer();
+        }
+        if (count == -1) {
+            return END_OF_STREAM;
+        }
+        int result = buffer[position++];
+        if (result < 0) {
+            charError("ASCII");
+        }
+        return result;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/AbstractCharDecoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/AbstractCharDecoder.java
new file mode 100644
index 0000000..7a79451
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/AbstractCharDecoder.java
@@ -0,0 +1,103 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+
+/**
+ * This class is the superclass of all the char decoders.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: AbstractCharDecoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public abstract class AbstractCharDecoder implements CharDecoder {
+
+    /**
+     * The buffer size.
+     */
+    protected final static int BUFFER_SIZE = 8192;
+
+    /**
+     * The input stream to read.
+     */
+    protected InputStream inputStream;
+
+    /**
+     * The input buffer.
+     */
+    protected byte[] buffer = new byte[BUFFER_SIZE];
+
+    /**
+     * The current position in the buffer.
+     */
+    protected int position;
+
+    /**
+     * The byte count in the buffer.
+     */
+    protected int count;
+
+    /**
+     * Creates a new CharDecoder object.
+     * @param is The stream to read.
+     */
+    protected AbstractCharDecoder(final InputStream is) {
+        inputStream = is;
+    }
+
+    /**
+     * Disposes the associated resources.
+     */
+    public void dispose() throws IOException {
+        inputStream.close();
+        inputStream = null;
+    }
+
+    /**
+     * Fills the input buffer.
+     */
+    protected void fillBuffer() throws IOException {
+        count = inputStream.read(buffer, 0, BUFFER_SIZE);
+        position = 0;
+    }
+
+    /**
+     * To throws an exception when the input stream contains an
+     * invalid character.
+     * @param encoding The encoding name.
+     */
+    protected void charError(final String encoding) throws IOException {
+        String pattern = "The input stream represents an invalid {0} stream.";
+        Object[] arguments = new Object[] { encoding };
+        String mesg = MessageFormat.format( pattern, arguments );
+        throw new IOException( mesg );
+    }
+
+    /**
+     * To throws an exception when the end of stream was unexpected.
+     * @param encoding The encoding name.
+     */
+    protected void endOfStreamError(final String encoding) throws IOException {
+      String pattern = "Unexpected end of stream while decoding a {0} stream.";
+      Object[] arguments = new Object[] { encoding };
+      String mesg = MessageFormat.format( pattern, arguments );
+      throw new IOException( mesg );
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/CharDecoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/CharDecoder.java
new file mode 100644
index 0000000..7da19b0
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/CharDecoder.java
@@ -0,0 +1,46 @@
+/*
+
+   Copyright 2002  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+
+/**
+ * This interface represents an object which decodes characters from a
+ * stream of bytes.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: CharDecoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public interface CharDecoder {
+    
+    /**
+     * This constant represents the end of stream character.
+     */
+    int END_OF_STREAM = -1;
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    int readChar() throws IOException;
+
+    /**
+     * Disposes the associated resources.
+     */
+    void dispose() throws IOException;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/GenericDecoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/GenericDecoder.java
new file mode 100644
index 0000000..f226f38
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/GenericDecoder.java
@@ -0,0 +1,75 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * This class delegates to a reader the decoding of an input stream.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: GenericDecoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class GenericDecoder implements CharDecoder {
+
+    /**
+     * The reader used to decode the stream.
+     */
+    protected Reader reader;
+
+    /**
+     * Creates a new GenericDecoder.
+     * @param is The input stream to decode.
+     * @param enc The Java encoding name.
+     */
+    public GenericDecoder(InputStream is, String enc) throws IOException {
+        reader = new InputStreamReader(is, enc);
+        reader = new BufferedReader(reader);
+    }
+
+    /**
+     * Creates a new GenericDecoder.
+     * @param r The reader to use.
+     */
+    public GenericDecoder(Reader r) {
+        reader = r;
+        if (!(r instanceof BufferedReader)) {
+            reader = new BufferedReader(reader);
+        }
+    }
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    public int readChar() throws IOException {
+        return reader.read();
+    }
+
+    /**
+     * Disposes the associated resources.
+     */
+    public void dispose() throws IOException {
+        reader.close();
+        reader = null;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ISO_8859_1Decoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ISO_8859_1Decoder.java
new file mode 100644
index 0000000..35af46f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/ISO_8859_1Decoder.java
@@ -0,0 +1,52 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class represents an object which decodes ISO-8859-1 characters from
+ * a stream of bytes.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: ISO_8859_1Decoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class ISO_8859_1Decoder extends AbstractCharDecoder {
+    
+    /**
+     * Creates a new ISO_8859_1Decoder.
+     */
+    public ISO_8859_1Decoder(InputStream is) {
+        super(is);
+    }
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    public int readChar() throws IOException {
+        if (position == count) {
+            fillBuffer();
+        }
+        if (count == -1) {
+            return -1;
+        }
+        return buffer[position++] & 0xff;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/NormalizingReader.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/NormalizingReader.java
new file mode 100644
index 0000000..c85a4ee
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/NormalizingReader.java
@@ -0,0 +1,69 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * This class represents a reader which normalizes the line break: \n,
+ * \r, \r\n are replaced by \n.  The methods of this reader are not
+ * synchronized.  The input is buffered.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: NormalizingReader.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public abstract class NormalizingReader extends Reader {
+
+    /**
+     * Read characters into a portion of an array.
+     * @param cbuf  Destination buffer
+     * @param off   Offset at which to start writing characters
+     * @param len   Maximum number of characters to read
+     * @return The number of characters read, or -1 if the end of the
+     * stream has been reached
+     */
+    public int read(char cbuf[], int off, int len) throws IOException {
+        if (len == 0) {
+            return 0;
+        }
+
+        int c = read();
+        if (c == -1) {
+            return -1;
+        }
+        int result = 0;
+        do {
+            cbuf[result + off] = (char)c;
+            result++;
+            c = read();
+        } while (c != -1 && result < len);
+        return result;
+    }
+
+    /**
+     * Returns the current line in the stream.
+     */
+    public abstract int getLine();
+
+    /**
+     * Returns the current column in the stream.
+     */
+    public abstract int getColumn();
+
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StreamNormalizingReader.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StreamNormalizingReader.java
new file mode 100644
index 0000000..a08d8c8
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StreamNormalizingReader.java
@@ -0,0 +1,216 @@
+/*
+
+   Copyright 2002-2003  The Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class represents a NormalizingReader which handles streams of
+ * bytes.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: StreamNormalizingReader.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class StreamNormalizingReader extends NormalizingReader {
+
+    /**
+     * The char decoder.
+     */
+    protected CharDecoder charDecoder;
+
+    /**
+     * The next char.
+     */
+    protected int nextChar = -1;
+
+    /**
+     * The current line in the stream.
+     */
+    protected int line = 1;
+
+    /**
+     * The current column in the stream.
+     */
+    protected int column;
+
+    /**
+     * Creates a new NormalizingReader. The encoding is assumed to be
+     * ISO-8859-1.
+     * @param is The input stream to decode.
+     */
+    public StreamNormalizingReader(final InputStream is) throws IOException {
+        this(is, null);
+    }
+
+    /**
+     * Creates a new NormalizingReader.
+     * @param is The input stream to decode.
+     * @param enc The standard encoding name. A null encoding means
+     * ISO-8859-1.
+     */
+    public StreamNormalizingReader(final InputStream is, String enc)
+        throws IOException {
+        if (enc == null) {
+            enc = "ISO-8859-1";
+        }
+        charDecoder = createCharDecoder(is, enc);
+    }
+
+    /**
+     * Creates a new NormalizingReader.
+     * @param r The reader to wrap.
+     */
+    public StreamNormalizingReader(final Reader r) throws IOException {
+        charDecoder = new GenericDecoder(r);
+    }
+
+    /**
+     * This constructor is intended for use by subclasses.
+     */
+    protected StreamNormalizingReader() {
+    }
+
+    /**
+     * Read a single character.  This method will block until a
+     * character is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     */
+    public int read() throws IOException {
+        int result = nextChar;
+        if (result != -1) {
+            nextChar = -1;
+            if (result == 13) {
+                column = 0;
+                line++;
+            } else {
+                column++;
+            }
+            return result;
+        }
+        result = charDecoder.readChar();
+        switch (result) {
+        case 13:
+            column = 0;
+            line++;
+            int c = charDecoder.readChar();
+            if (c == 10) {
+                return 10;
+            }
+            nextChar = c;
+            return 10;
+
+        case 10:
+            column = 0;
+            line++;
+        }
+        return result;
+    }
+
+    /**
+     * Returns the current line in the stream.
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /**
+     * Returns the current column in the stream.
+     */
+    public int getColumn() {
+        return column;
+    }
+
+    /**
+     * Close the stream.
+     */
+    public void close() throws IOException {
+        charDecoder.dispose();
+        charDecoder = null;
+    }
+
+    /**
+     * Creates the CharDecoder mapped with the given encoding name.
+     */
+    protected CharDecoder createCharDecoder(final InputStream is, final String enc)
+        throws IOException {
+        CharDecoderFactory cdf =
+            (CharDecoderFactory)charDecoderFactories.get(enc.toUpperCase());
+        if (cdf != null) {
+            return cdf.createCharDecoder(is);
+        }
+//        String e = EncodingUtilities.javaEncoding(enc);
+//        if (e == null) {
+//            e = enc;
+//        }
+        return new GenericDecoder(is, enc);
+    }
+
+    /**
+     * The CharDecoder factories map.
+     */
+    protected final static Map charDecoderFactories = new HashMap(11);
+    static {
+        CharDecoderFactory cdf = new ASCIIDecoderFactory();
+        charDecoderFactories.put("ASCII", cdf);
+        charDecoderFactories.put("US-ASCII", cdf);
+        charDecoderFactories.put("ISO-8859-1", new ISO_8859_1DecoderFactory());
+        charDecoderFactories.put("UTF-8", new UTF8DecoderFactory());
+    }
+
+    /**
+     * Represents a CharDecoder factory.
+     */
+    protected interface CharDecoderFactory {
+        CharDecoder createCharDecoder(InputStream is) throws IOException;
+    }
+
+    /**
+     * To create an ASCIIDecoder.
+     */
+    protected static class ASCIIDecoderFactory
+        implements CharDecoderFactory {
+        public CharDecoder createCharDecoder(final InputStream is)
+            throws IOException {
+            return new ASCIIDecoder(is);
+        }
+    }
+
+    /**
+     * To create an ISO_8859_1Decoder.
+     */
+    protected static class ISO_8859_1DecoderFactory
+        implements CharDecoderFactory {
+        public CharDecoder createCharDecoder(final InputStream is)
+            throws IOException {
+            return new ISO_8859_1Decoder(is);
+        }
+    }
+
+    /**
+     * To create a UTF8Decoder.
+     */
+    protected static class UTF8DecoderFactory
+        implements CharDecoderFactory {
+        public CharDecoder createCharDecoder(final InputStream is)
+            throws IOException {
+            return new UTF8Decoder(is);
+        }
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringDecoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringDecoder.java
new file mode 100644
index 0000000..43a9360
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringDecoder.java
@@ -0,0 +1,70 @@
+/*
+
+   Copyright 2002  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+
+/**
+ * This class reads a string.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: StringDecoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class StringDecoder implements CharDecoder {
+
+    /**
+     * The string which contains the decoded characters.
+     */
+    protected String string;
+
+    /**
+     * The number of chars in the string.
+     */
+    protected int length;
+
+    /**
+     * The next char index.
+     */
+    protected int next;
+
+    /**
+     * Creates a new StringDecoder.
+     */
+    public StringDecoder(String s) {
+        string = s;
+        length = s.length();
+    }
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    public int readChar() throws IOException {
+        if (next == length) {
+            return END_OF_STREAM;
+        }
+        return string.charAt(next++);
+    }
+
+    /**
+     * Disposes the associated resources.
+     */
+    public void dispose() throws IOException {
+        string = null;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringNormalizingReader.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringNormalizingReader.java
new file mode 100644
index 0000000..4ce4730
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/StringNormalizingReader.java
@@ -0,0 +1,110 @@
+/*
+
+   Copyright 2002  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+
+/**
+ * This class represents a NormalizingReader which handles Strings.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: StringNormalizingReader.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class StringNormalizingReader extends NormalizingReader {
+
+    /**
+     * The characters.
+     */
+    protected String string;
+
+    /**
+     * The length of the string.
+     */
+    protected int length;
+    
+    /**
+     * The index of the next character.
+     */
+    protected int next;
+
+    /**
+     * The current line in the stream.
+     */
+    protected int line = 1;
+
+    /**
+     * The current column in the stream.
+     */
+    protected int column;
+
+    /**
+     * Creates a new StringNormalizingReader.
+     * @param s The string to read.
+     */
+    public StringNormalizingReader(String s) {
+        string = s;
+        length = s.length();
+    }
+
+    /**
+     * Read a single character.  This method will block until a
+     * character is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     */
+    public int read() throws IOException {
+        int result = (length == next) ? -1 : string.charAt(next++);
+        if (result <= 13) {
+            switch (result) {
+            case 13:
+                column = 0;
+                line++;
+                int c = (length == next) ? -1 : string.charAt(next);
+                if (c == 10) {
+                    next++;
+                }
+                return 10;
+                
+            case 10:
+                column = 0;
+                line++;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the current line in the stream.
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /**
+     * Returns the current column in the stream.
+     */
+    public int getColumn() {
+        return column;
+    }
+
+    /**
+     * Close the stream.
+     */
+    public void close() throws IOException {
+        string = null;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/UTF8Decoder.java b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/UTF8Decoder.java
new file mode 100644
index 0000000..e85d891
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/apache/batik/util/io/UTF8Decoder.java
@@ -0,0 +1,148 @@
+/*
+
+   Copyright 1999-2003  The Apache Software Foundation 
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+*/
+
+package org.apache.batik.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class represents an object which decodes UTF-8 characters from
+ * a stream of bytes.
+ *
+ * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
+ * @version $Id: UTF8Decoder.java,v 1.1 2009/12/06 10:40:09 rsternber Exp $
+ */
+public class UTF8Decoder extends AbstractCharDecoder {
+    
+    /**
+     * The number of bytes of a UTF-8 sequence indexed by the first
+     * byte of the sequence.
+     */
+    protected final static byte[] UTF8_BYTES = {
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+        3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,
+    };
+
+    /**
+     * The next char, in case of a 4 bytes sequence.
+     */
+    protected int nextChar = -1;
+
+    /**
+     * Creates a new UTF8Decoder.
+     */
+    public UTF8Decoder(InputStream is) {
+        super(is);
+    }
+
+    /**
+     * Reads the next character.
+     * @return a character or END_OF_STREAM.
+     */
+    public int readChar() throws IOException {
+        if (nextChar != -1) {
+            int result = nextChar;
+            nextChar = -1;
+            return result;
+        }
+        if (position == count) {
+            fillBuffer();
+        }
+        if (count == -1) {
+            return END_OF_STREAM;
+        }
+        int b1 = buffer[position++] & 0xff;
+        switch (UTF8_BYTES[b1]) {
+        default:
+            charError("UTF-8");
+
+        case 1:
+            return b1;
+
+        case 2:
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            return ((b1 & 0x1f) << 6) | (buffer[position++] & 0x3f);
+
+        case 3:
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            int b2 = buffer[position++];
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            int b3 = buffer[position++];
+            if ((b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80) {
+                charError("UTF-8");
+            }
+            return ((b1 & 0x1f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x1f);
+
+        case 4:
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            b2 = buffer[position++];
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            b3 = buffer[position++];
+            if (position == count) {
+                fillBuffer();
+            }
+            if (count == -1) {
+                endOfStreamError("UTF-8");
+            }
+            int b4 = buffer[position++];
+            if ((b2 & 0xc0) != 0x80 ||
+                (b3 & 0xc0) != 0x80 ||
+                (b4 & 0xc0) != 0x80) {
+                charError("UTF-8");
+            }
+            int c = ((b1 & 0x1f) << 18)
+                | ((b2 & 0x3f) << 12)
+                | ((b3 & 0x1f) << 6)
+                | (b4 & 0x1f);
+            nextChar = (c - 0x10000) % 0x400 + 0xdc00;            
+            return (c - 0x10000) / 0x400 + 0xd800;
+        }
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/AttributeCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/AttributeCondition.java
new file mode 100644
index 0000000..56608df
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/AttributeCondition.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: AttributeCondition.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_ATTRIBUTE_CONDITION
+ * @see Condition#SAC_ONE_OF_ATTRIBUTE_CONDITION
+ * @see Condition#SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION
+ * @see Condition#SAC_ID_CONDITION
+ * @see Condition#SAC_CLASS_CONDITION
+ * @see Condition#SAC_PSEUDO_CLASS_CONDITION
+ */
+public interface AttributeCondition extends Condition {
+    
+    /**
+     * Returns the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#dt-NSName">namespace
+     * URI</a> of this attribute condition.
+     * <p><code>NULL</code> if :
+     * <ul>
+     * <li>this attribute condition can match any namespace.
+     * <li>this attribute is an id attribute.
+     * </ul>
+     */    
+    public String getNamespaceURI();
+
+    /**
+     * Returns the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+     * of the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+     * name</a> of this attribute.
+     * <p><code>NULL</code> if :
+     * <ul>
+     * <li><p>this attribute condition can match any attribute.
+     * <li><p>this attribute is a class attribute.
+     * <li><p>this attribute is an id attribute.
+     * <li><p>this attribute is a pseudo-class attribute.
+     * </ul>
+     */
+    public String getLocalName();
+
+    /**
+     * Returns <code>true</code> if the attribute must have an explicit value
+     * in the original document, <code>false</code> otherwise. If this is a
+     * pseudo class, the return value is unspecified.
+     * <p><code>false</code> if:
+     * <ul>
+     * <li>if this is an id attribute.
+     * <li>if this is a pseudo class a class attribute.
+     * </ul>
+     */
+    public boolean getSpecified();
+
+    /**
+     * Returns the value of the attribute.
+     * If this attribute is a class or a pseudo class attribute, you'll get
+     * the class name (or psedo class name) without the '.' or ':'.
+     */
+    public String getValue();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSException.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSException.java
new file mode 100644
index 0000000..bcf01b7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSException.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: CSSException.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public class CSSException extends RuntimeException {
+
+    protected String s;
+
+    /**
+     * this error is unspecified.
+     */    
+    public static short SAC_UNSPECIFIED_ERR   = 0;
+
+    /**
+     * If the operation is not supported
+     */    
+    public static short SAC_NOT_SUPPORTED_ERR = 1;
+
+    /**
+     * If an invalid or illegal string is specified
+     */    
+    public static short SAC_SYNTAX_ERR        = 2;
+
+    /**
+     * The internal exception.
+     */    
+    protected Exception e;
+
+    protected short     code;
+
+    /**
+     * Creates a new CSSException
+     */
+    public CSSException() {
+    }
+
+    /**
+     * Creates a new CSSException
+     */
+    public CSSException(String s) {
+	this.code = SAC_UNSPECIFIED_ERR;
+        this.s = s;
+    }
+    
+    /**
+     * Creates a new CSSException with an embeded exception.
+     * @param e the embeded exception.
+     */
+    public CSSException(Exception e) {
+	this.code = SAC_UNSPECIFIED_ERR;
+        this.e = e;
+    }
+
+    /**
+     * Creates a new CSSException with a specific code.
+     * @param code the embeded exception.
+     */
+    public CSSException(short code) {
+        this.code = code;
+    }
+
+    /**
+     * Creates a new CSSException with an embeded exception and a specified
+     * message.
+     * @param code the specified code.
+     * @param e the embeded exception.  
+     */
+    public CSSException(short code, String s, Exception e) {
+	this.code = code;
+	this.s = s;
+        this.e = e;
+    }
+
+    /**
+     * Returns the detail message of this throwable object. 
+     *
+     * @return the detail message of this Throwable, or null if this Throwable
+     *         does not have a detail message.  
+     */
+    public String getMessage() {
+	if (s != null) {
+	    return s;
+	} else if (e != null) {
+	    return e.getMessage();
+	} else {
+	    return null;
+	}
+    }
+
+    /**
+     * returns the error code for this exception.
+     */    
+    public short getCode() {
+	return code;
+    }
+
+    /**
+     * Returns the internal exception if any, null otherwise.
+     */    
+    public Exception getException() {
+	return e;
+    }
+
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSParseException.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSParseException.java
new file mode 100644
index 0000000..4d236e2
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CSSParseException.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: CSSParseException.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * Encapsulate a CSS parse error or warning.
+ *
+ * <p>This exception will include information for locating the error
+ * in the original CSS document.  Note that although the application
+ * will receive a CSSParseException as the argument to the handlers
+ * in the ErrorHandler interface, the application is not actually
+ * required to throw the exception; instead, it can simply read the
+ * information in it and take a different action.</p>
+ *
+ * <p>Since this exception is a subclass of CSSException, it
+ * inherits the ability to wrap another exception.</p>
+ *
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public class CSSParseException extends CSSException {
+    
+    private String uri;
+    private int lineNumber;
+    private int columnNumber;
+    
+    /**
+     * Create a new CSSParseException from a message and a Locator.
+     *
+     * <p>This constructor is especially useful when an application is
+     * creating its own exception from within a DocumentHandler
+     * callback.</p>
+     *
+     * @param message The error or warning message.
+     * @param locator The locator object for the error or warning.
+     * @see Locator
+     * @see Parser#setLocale 
+     */
+    public CSSParseException(String message, Locator locator) {
+	super(message);
+	this.code = SAC_SYNTAX_ERR;
+	this.uri = locator.getURI();
+	this.lineNumber = locator.getLineNumber();
+	this.columnNumber = locator.getColumnNumber();
+    }
+    
+    
+    /**
+
+     * Wrap an existing exception in a CSSParseException.
+     *
+     * <p>This constructor is especially useful when an application is
+     * creating its own exception from within a DocumentHandler
+     * callback, and needs to wrap an existing exception that is not a
+     * subclass of CSSException.</p>
+     *
+     * @param message The error or warning message, or null to
+     *                use the message from the embedded exception.
+     * @param locator The locator object for the error or warning.
+     * @param e Any exception
+     * @see Locator
+     * @see Parser#setLocale
+     */
+    public CSSParseException(String message, Locator locator,
+			     Exception e) {
+	super(SAC_SYNTAX_ERR, message, e);
+	this.uri = locator.getURI();
+	this.lineNumber = locator.getLineNumber();
+	this.columnNumber = locator.getColumnNumber();
+    }
+    
+    
+    /**
+     * Create a new CSSParseException.
+     *
+     * <p>This constructor is most useful for parser writers.</p>
+     *
+     * <p>the parser must resolve the URI fully before creating the exception.</p>
+     *
+     * @param message The error or warning message.
+     * @param uri The URI of the document that generated the error or warning.
+     * @param lineNumber The line number of the end of the text that
+     *                   caused the error or warning.
+     * @param columnNumber The column number of the end of the text that
+     *                     cause the error or warning.
+     * @see Parser#setLocale
+     */
+    public CSSParseException(String message, String uri,
+			     int lineNumber, int columnNumber) {
+	super(message);
+	this.code = SAC_SYNTAX_ERR;
+	this.uri = uri;
+	this.lineNumber = lineNumber;
+	this.columnNumber = columnNumber;
+    }
+        
+    /**
+     * Create a new CSSParseException with an embedded exception.
+     *
+     * <p>This constructor is most useful for parser writers who
+     * need to wrap an exception that is not a subclass of
+     * CSSException.</p>
+     *
+     * <p>The parser must resolve the URI fully before creating the
+     * exception.</p>
+     *
+     * @param message The error or warning message, or null to use
+     *                the message from the embedded exception.
+     * @param uri The URI of the document that generated
+     *                 the error or warning.
+     * @param lineNumber The line number of the end of the text that
+     *                   caused the error or warning.
+     * @param columnNumber The column number of the end of the text that
+     *                     cause the error or warning.
+     * @param e Another exception to embed in this one.
+     * @see Parser#setLocale 
+     */
+    public CSSParseException(String message, String uri,
+			     int lineNumber, int columnNumber, Exception e) {
+	super(SAC_SYNTAX_ERR, message, e);
+	this.uri = uri;
+	this.lineNumber = lineNumber;
+	this.columnNumber = columnNumber;
+    }
+    
+    /**
+     * Get the URI of the document where the exception occurred.
+     *
+     * <p>The URI will be resolved fully.</p>
+     *
+     * @return A string containing the URI, or null
+     *         if none is available.
+     * @see Locator#getURI
+     */
+    public String getURI() {
+	return this.uri;
+    }
+    
+    
+    /**
+     * The line number of the end of the text where the exception occurred.
+     *
+     * @return An integer representing the line number, or -1
+     *         if none is available.
+     * @see Locator#getLineNumber
+     */
+    public int getLineNumber() {
+	return this.lineNumber;
+    }
+    
+    
+    /**
+     * The column number of the end of the text where the exception occurred.
+     *
+     * <p>The first column in a line is position 1.</p>
+     *
+     * @return An integer representing the column number, or -1
+     *         if none is available.
+     * @see Locator#getColumnNumber
+     */
+    public int getColumnNumber() {
+	return this.columnNumber;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CharacterDataSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CharacterDataSelector.java
new file mode 100644
index 0000000..1b2ba5b
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CharacterDataSelector.java
@@ -0,0 +1,24 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: CharacterDataSelector.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_TEXT_NODE_SELECTOR
+ * @see Selector#SAC_CDATA_SECTION_NODE_SELECTOR
+ * @see Selector#SAC_COMMENT_NODE_SELECTOR
+ */
+public interface CharacterDataSelector extends SimpleSelector {
+
+    /**
+     * Returns the character data.
+     */    
+    public String getData();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CombinatorCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CombinatorCondition.java
new file mode 100644
index 0000000..1b7c248
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/CombinatorCondition.java
@@ -0,0 +1,28 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: CombinatorCondition.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_AND_CONDITION
+ * @see Condition#SAC_OR_CONDITION
+ */
+public interface CombinatorCondition extends Condition {
+
+    /**
+     * Returns the first condition.
+     */    
+    public Condition getFirstCondition();
+
+    /**
+     * Returns the second condition.
+     */    
+    public Condition getSecondCondition();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Condition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Condition.java
new file mode 100644
index 0000000..90f7ee8
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Condition.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: Condition.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public interface Condition {
+
+    /**
+     * This condition checks exactly two conditions.
+     * example:
+     * <pre class="example">
+     *   .part1:lang(fr)
+     * </pre>
+     * @see CombinatorCondition
+     */    
+    public static final short SAC_AND_CONDITION		        = 0;
+
+    /**
+     * This condition checks one of two conditions.
+     * @see CombinatorCondition
+     */    
+    public static final short SAC_OR_CONDITION		        = 1;
+
+    /**
+     * This condition checks that a condition can't be applied to a node.
+     * @see NegativeCondition
+     */    
+    public static final short SAC_NEGATIVE_CONDITION		= 2;
+
+    /**
+     * This condition checks a specified position.
+     * example:
+     * <pre class="example">
+     *   :first-child
+     * </pre>
+     * @see PositionalCondition
+     */    
+    public static final short SAC_POSITIONAL_CONDITION		= 3;
+
+    /**
+     * This condition checks an attribute.
+     * example:
+     * <pre class="example">
+     *   [simple]
+     *   [restart="never"]
+     * </pre>
+     * @see AttributeCondition
+     */    
+    public static final short SAC_ATTRIBUTE_CONDITION		= 4;
+    /**
+     * This condition checks an id attribute.
+     * example:
+     * <pre class="example">
+     *   #myId
+     * </pre>
+     * @see AttributeCondition
+     */    
+    public static final short SAC_ID_CONDITION		        = 5;
+    /**
+     * This condition checks the language of the node.
+     * example:
+     * <pre class="example">
+     *   :lang(fr)
+     * </pre>
+     * @see LangCondition
+     */    
+    public static final short SAC_LANG_CONDITION		= 6;
+    /**
+     * This condition checks for a value in a space-separated values in a
+     * specified attribute.
+     * example:
+     * <pre class="example">
+     *   [values~="10"]
+     * </pre>
+     * @see AttributeCondition
+     */
+    public static final short SAC_ONE_OF_ATTRIBUTE_CONDITION	= 7;
+    /**
+     * This condition checks if the value is in a hypen-separated list of values
+     * in a specified attribute.
+     * example:
+     * <pre class="example">
+     *   [languages|="fr"]
+     * </pre>
+     * @see AttributeCondition
+     */
+    public static final short SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION = 8;
+    /**
+     * This condition checks for a specified class.
+     * example:
+     * <pre class="example">
+     *   .example
+     * </pre>
+     * @see AttributeCondition
+     */
+    public static final short SAC_CLASS_CONDITION		= 9;
+    /**
+     * This condition checks for the link pseudo class.
+     * example:
+     * <pre class="example">
+     *   :link
+     *   :visited
+     *   :hover
+     * </pre>
+     * @see AttributeCondition
+     */
+    public static final short SAC_PSEUDO_CLASS_CONDITION	= 10;
+    /**
+     * This condition checks if a node is the only one in the node list.
+     */
+    public static final short SAC_ONLY_CHILD_CONDITION		= 11;
+    /**
+     * This condition checks if a node is the only one of his type.
+     */
+    public static final short SAC_ONLY_TYPE_CONDITION		= 12;
+    /**
+     * This condition checks the content of a node.
+     * @see ContentCondition
+     */
+    public static final short SAC_CONTENT_CONDITION		= 13;
+
+    /**
+     * An integer indicating the type of <code>Condition</code>.
+     */    
+    public short getConditionType();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionFactory.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionFactory.java
new file mode 100644
index 0000000..82de332
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionFactory.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: ConditionFactory.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public interface ConditionFactory {
+
+    /**
+     * Creates an and condition
+     *
+     * @param first the first condition
+     * @param second the second condition
+     * @return A combinator condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    CombinatorCondition createAndCondition(Condition first, Condition second)
+	throws CSSException;
+
+    /**
+     * Creates an or condition
+     *
+     * @param first the first condition
+     * @param second the second condition
+     * @return A combinator condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    CombinatorCondition createOrCondition(Condition first, Condition second)
+	throws CSSException;
+
+    /**
+     * Creates a negative condition
+     *
+     * @param condition the condition
+     * @return A negative condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    NegativeCondition createNegativeCondition(Condition condition)
+	throws CSSException;
+
+    /**
+     * Creates a positional condition
+     *
+     * @param position the position of the node in the list.
+     * @param typeNode <code>true</code> if the list should contain
+     *                 only nodes of the same type (element, text node, ...).
+     * @param type <code>true</code> true if the list should contain
+     *             only nodes of the same node (for element, same localName
+     *             and same namespaceURI).
+     * @return A positional condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    PositionalCondition createPositionalCondition(int position, 
+						  boolean typeNode, 
+						  boolean type)
+	throws CSSException;
+    
+    /**
+     * Creates an attribute condition
+     *
+     * @param localName the localName of the attribute
+     * @param namespaceURI the namespace URI of the attribute
+     * @param specified <code>true</code> if the attribute must be specified
+     *                  in the document.
+     * @param value the value of this attribute.
+     * @return An attribute condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createAttributeCondition(String localName,
+						String namespaceURI,
+						boolean specified,
+						String value)
+	throws CSSException;
+
+    /**
+     * Creates an id condition
+     *
+     * @param value the value of the id.
+     * @return An Id condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createIdCondition(String value)
+	throws CSSException;
+
+    /**
+     * Creates a lang condition
+     *
+     * @param lang the value of the language.
+     * @return A lang condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    LangCondition createLangCondition(String lang)
+	throws CSSException;
+
+    /**
+     * Creates a "one of" attribute condition
+     *
+     * @param localName the localName of the attribute
+     * @param namespaceURI the namespace URI of the attribute
+     * @param specified <code>true</code> if the attribute must be specified
+     *                  in the document.
+     * @param value the value of this attribute.
+     * @return A "one of" attribute condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createOneOfAttributeCondition(String localName,
+						     String namespaceURI,
+						     boolean specified,
+						     String value)
+	throws CSSException;
+
+    /**
+     * Creates a "begin hyphen" attribute condition
+     *
+     * @param localName the localName of the attribute
+     * @param namespaceURI the namespace URI of the attribute
+     * @param specified <code>true</code> if the attribute must be specified
+     *                  in the document.
+     * @param value the value of this attribute.
+     * @return A "begin hyphen" attribute condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createBeginHyphenAttributeCondition(String localName,
+							   String namespaceURI,
+							   boolean specified,
+							   String value)
+	throws CSSException;
+
+    /**
+     * Creates a class condition
+     *
+     * @param namespaceURI the namespace URI of the attribute
+     * @param value the name of the class.
+     * @return A class condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createClassCondition(String namespaceURI,
+					    String value)
+	throws CSSException;
+
+    /**
+     * Creates a pseudo class condition
+     *
+     * @param namespaceURI the namespace URI of the attribute
+     * @param value the name of the pseudo class
+     * @return A pseudo class condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    AttributeCondition createPseudoClassCondition(String namespaceURI,
+						  String value)
+	throws CSSException;
+
+    /**
+     * Creates a "only one" child condition
+     *
+     * @return A "only one" child condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    Condition createOnlyChildCondition() throws CSSException;
+
+
+    /**
+     * Creates a "only one" type condition
+     *
+     * @return A "only one" type condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    Condition createOnlyTypeCondition() throws CSSException;
+
+    /**
+     * Creates a content condition
+     *
+     * @param data the data in the content
+     * @return A content condition
+     * @exception CSSException if this exception is not supported.
+     */    
+    ContentCondition createContentCondition(String data)
+	throws CSSException;
+
+    
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionalSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionalSelector.java
new file mode 100644
index 0000000..266edc9
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ConditionalSelector.java
@@ -0,0 +1,28 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: ConditionalSelector.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_CONDITIONAL_SELECTOR
+ */
+public interface ConditionalSelector extends SimpleSelector {
+
+    /**
+     * Returns the simple selector.
+     * <p>The simple selector can't be a <code>ConditionalSelector</code>.</p>
+     */    
+    public SimpleSelector getSimpleSelector();
+
+    /**
+     * Returns the condition to be applied on the simple selector.
+     */    
+    public Condition getCondition();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ContentCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ContentCondition.java
new file mode 100644
index 0000000..ec72810
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ContentCondition.java
@@ -0,0 +1,21 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: ContentCondition.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_CONTENT_CONDITION
+ */
+public interface ContentCondition extends Condition {
+    /**
+     * Returns the content.
+     */
+    public String getData();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DescendantSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DescendantSelector.java
new file mode 100644
index 0000000..55c06d2
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DescendantSelector.java
@@ -0,0 +1,28 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: DescendantSelector.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_DESCENDANT_SELECTOR
+ * @see Selector#SAC_CHILD_SELECTOR
+ */
+public interface DescendantSelector extends Selector {
+    
+    /**
+     * Returns the parent selector.
+     */    
+    public Selector getAncestorSelector();
+
+    /*
+     * Returns the simple selector.
+     */    
+    public SimpleSelector getSimpleSelector();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DocumentHandler.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DocumentHandler.java
new file mode 100644
index 0000000..4f89756
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/DocumentHandler.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: DocumentHandler.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * This is the main interface that most CSS applications implement: if the
+ * application needs to be informed of basic parsing events, it implements this
+ * interface and registers an instance with the CSS parser using the
+ * setCSSHandler method.
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret 
+ */
+public interface DocumentHandler {
+    
+    /**
+     * Receive notification of the beginning of a style sheet.
+     *
+     * The CSS parser will invoke this method only once, before any other
+     * methods in this interface.
+     *
+     * @param source The source of the style sheet.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void startDocument(InputSource source)
+        throws CSSException;
+    
+    /**
+     * Receive notification of the end of a document. 
+     *
+     * The CSS parser will invoke this method only once, and it will be the
+     * last method invoked during the parse. The parser shall not invoke this
+     * method until it has either abandoned parsing (because of an
+     * unrecoverable error) or reached the end of input.  
+     *
+     * @param source The source of the style sheet.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void endDocument(InputSource source) throws CSSException;
+
+    /**
+     * Receive notification of a comment.
+     * If the comment appears in a declaration (e.g. color: /* comment * / blue;),
+     * the parser notifies the comment before the declaration.
+     *
+     * @param text The comment.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void comment(String text) throws CSSException;
+
+    /**
+     * Receive notification of an unknown rule t-rule not supported by this
+     * parser.
+     *
+     * @param atRule The complete ignored at-rule.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void ignorableAtRule(String atRule) throws CSSException;
+
+    /**
+     * Receive notification of an unknown rule t-rule not supported by this
+     * parser.
+     *
+     * @param prefix <code>null</code> if this is the default namespace
+     * @param uri The URI for this namespace.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void namespaceDeclaration(String prefix, String uri) 
+	throws CSSException;
+
+    /**
+     * Receive notification of a import statement in the style sheet.
+     *
+     * @param uri The URI of the imported style sheet.
+     * @param media The intended destination media for style information.
+     * @param defaultNamespaceURI The default namespace URI for the imported
+     *                            style sheet.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.
+     */
+    public void importStyle(String uri, SACMediaList media, 
+			    String defaultNamespaceURI)
+	throws CSSException;
+
+    /**
+     * Receive notification of the beginning of a media statement.
+     *
+     * The Parser will invoke this method at the beginning of every media
+     * statement in the style sheet. there will be a corresponding endMedia()
+     * event for every startElement() event.
+     *
+     * @param media The intended destination media for style information.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void startMedia(SACMediaList media) throws CSSException;
+
+    /**
+     * Receive notification of the end of a media statement.
+     *
+     * @param media The intended destination media for style information.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void endMedia(SACMediaList media) throws CSSException;
+
+    /**
+     * Receive notification of the beginning of a page statement.
+     *
+     * The Parser will invoke this method at the beginning of every page
+     * statement in the style sheet. there will be a corresponding endPage()
+     * event for every startPage() event.
+     *
+     * @param name the name of the page (if any, null otherwise)
+     * @param pseudo_page the pseudo page (if any, null otherwise)
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */    
+    public void startPage(String name, String pseudo_page) throws CSSException;
+
+    /**
+     * Receive notification of the end of a media statement.
+     *
+     * @param name the name of the page (if any, null otherwise)
+     * @param pseudo_page the pseudo page (if any, null otherwise)
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void endPage(String name, String pseudo_page) throws CSSException;
+
+    /**
+     * Receive notification of the beginning of a font face statement.
+     *
+     * The Parser will invoke this method at the beginning of every font face
+     * statement in the style sheet. there will be a corresponding endFontFace()
+     * event for every startFontFace() event.
+     *
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.
+     */
+    public void startFontFace() throws CSSException;
+
+    /**
+     * Receive notification of the end of a font face statement.
+     *
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.  
+     */
+    public void endFontFace() throws CSSException;
+
+    /**
+     * Receive notification of the beginning of a rule statement.
+     *
+     * @param selectors All intended selectors for all declarations.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.
+     */
+    public void startSelector(SelectorList selectors) throws CSSException;
+
+    /**
+     * Receive notification of the end of a rule statement.
+     *
+     * @param selectors All intended selectors for all declarations.
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.
+     */
+    public void endSelector(SelectorList selectors) throws CSSException;
+
+    /**
+     * Receive notification of a declaration.
+     *
+     * @param name the name of the property.
+     * @param value the value of the property. All whitespace are stripped.
+     * @param important is this property important ?
+     * @exception CSSException Any CSS exception, possibly wrapping another
+     *                         exception.
+     */
+    public void property(String name, LexicalUnit value, boolean important)
+        throws CSSException;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ElementSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ElementSelector.java
new file mode 100644
index 0000000..48b09cb
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ElementSelector.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: ElementSelector.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_ELEMENT_NODE_SELECTOR
+ */
+public interface ElementSelector extends SimpleSelector {
+
+    /**
+     * Returns the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#dt-NSName">namespace
+     * URI</a> of this element selector.
+     * <p><code>NULL</code> if this element selector can match any namespace.</p>
+     */
+    public String getNamespaceURI();
+
+    /**
+     * Returns the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+     * of the
+     * <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+     * name</a> of this element.
+     * <p><code>NULL</code> if this element selector can match any element.</p>
+     * </ul>
+     */
+    public String getLocalName();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ErrorHandler.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ErrorHandler.java
new file mode 100644
index 0000000..cd4ed4d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ErrorHandler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: ErrorHandler.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+  * Basic interface for CSS error handlers.
+  *
+  * <p>If a CSS application needs to implement customized error
+  * handling, it must implement this interface and then register an
+  * instance with the CSS parser using the parser's setErrorHandler
+  * method.  The parser will then report all errors and warnings
+  * through this interface.</p>
+  *
+  * <p> The parser shall use this interface instead of throwing an
+  * exception: it is up to the application whether to throw an
+  * exception for different types of errors and warnings.  Note,
+  * however, that there is no requirement that the parser continue to
+  * provide useful information after a call to fatalError (in other
+  * words, a CSS driver class could catch an exception and report a
+  * fatalError).</p>
+  *
+  * <p>The HandlerBase class provides a default implementation of this
+  * interface, ignoring warnings and recoverable errors and throwing a
+  * SAXParseException for fatal errors.  An application may extend
+  * that class rather than implementing the complete interface
+  * itself.</p>
+  *
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+  */
+public interface ErrorHandler {
+
+
+  /**
+    * Receive notification of a warning.
+    *
+    * <p>CSS parsers will use this method to report conditions that
+    * are not errors or fatal errors as defined by the XML 1.0
+    * recommendation.  The default behaviour is to take no action.</p>
+    *
+    * <p>The CSS parser must continue to provide normal parsing events
+    * after invoking this method: it should still be possible for the
+    * application to process the document through to the end.</p>
+    *
+    * @param exception The warning information encapsulated in a
+    *                  CSS parse exception.
+    * @exception CSSException Any CSS exception, possibly
+    *            wrapping another exception.
+    * @see CSSParseException 
+    */
+  public void warning(CSSParseException exception) throws CSSException;
+
+  /**
+    * Receive notification of a recoverable error.
+    *
+    * <p>This corresponds to the definition of "error" in section 1.2
+    * of the W3C XML 1.0 Recommendation.  For example, a validating
+    * parser would use this callback to report the violation of a
+    * validity constraint.  The default behaviour is to take no
+    * action.</p>
+    *
+    * <p>The CSS parser must continue to provide normal parsing events
+    * after invoking this method: it should still be possible for the
+    * application to process the document through to the end.  If the
+    * application cannot do so, then the parser should report a fatal
+    * error even if the XML 1.0 recommendation does not require it to
+    * do so.</p>
+    *
+    * @param exception The error information encapsulated in a
+    *                  CSS parse exception.
+    * @exception CSSException Any CSS exception, possibly
+    *            wrapping another exception.
+    * @see CSSParseException 
+    */
+  public void error(CSSParseException exception) throws CSSException;
+
+  /**
+    * Receive notification of a non-recoverable error.
+    *
+    * <p>This corresponds to the definition of "fatal error" in
+    * section 1.2 of the W3C XML 1.0 Recommendation.  For example, a
+    * parser would use this callback to report the violation of a
+    * well-formedness constraint.</p>
+    *
+    * <p>The application must assume that the document is unusable
+    * after the parser has invoked this method, and should continue
+    * (if at all) only for the sake of collecting addition error
+    * messages: in fact, CSS parsers are free to stop reporting any
+    * other events once this method has been invoked.</p>
+    *
+    * @param exception The error information encapsulated in a
+    *                  CSS parse exception.  
+    * @exception CSSException Any CSS exception, possibly
+    *            wrapping another exception.
+    * @see CSSParseException
+    */
+  public void fatalError(CSSParseException exception) throws CSSException;
+
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/InputSource.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/InputSource.java
new file mode 100644
index 0000000..aa5148e
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/InputSource.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: InputSource.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * A single input source for a CSS source.
+ *
+ * <p>This class allows a CSS application to encapsulate information about an
+ * input source in a single object, which may include a URI, a byte stream
+ * (possibly with a specified encoding), and/or a character stream.</p>
+ *
+ * <p>The CSS parser will use the InputSource object to determine how
+ * to read CSS input.  If there is a character stream available, the
+ * parser will read that stream directly; if not, the parser will use
+ * a byte stream, if available; if neither a character stream nor a
+ * byte stream is available, the parser will attempt to open a URI
+ * connection to the resource identified by the URI.</p>
+ *
+ * <p>An InputSource object belongs to the application: the CSS parser
+ * shall never modify it in any way (it may modify a copy if 
+ * necessary).</p>
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret 
+ */
+public class InputSource {
+    
+    private String      uri;
+    private InputStream byteStream;
+    private String      encoding;
+    private Reader      characterStream;
+    private String      title;
+    private String      media;
+    
+    /**
+     * Zero-argument default constructor.
+     *
+     * @see #setURI
+     * @see #setByteStream
+     * @see #setCharacterStream
+     * @see #setEncoding
+     */
+    public InputSource() {
+    }
+    
+    /**
+     * Create a new input source with a URI.
+     *
+     * <p>The URI must be full resolved.</p>
+     *
+     * @param uri The URI.
+     * @see #setURI
+     * @see #setByteStream
+     * @see #setEncoding
+     * @see #setCharacterStream
+     */
+    public InputSource(String uri) {
+	setURI(uri);
+    }
+    
+    /**
+     * Create a new input source with a character stream.
+     *
+     * <p>Application writers may use setURI() to provide a base 
+     * for resolving relative URIs, and setPublicId to include a 
+     * public identifier.</p>
+     *
+     * <p>The character stream shall not include a byte order mark.</p>
+     *
+     * @see #setURI
+     * @see #setByteStream
+     * @see #setCharacterStream
+     */
+    public InputSource(Reader characterStream) {
+	setCharacterStream(characterStream);
+    }
+    
+    /**
+     * Set the URI for this input source.
+     *
+     * <p>The URI is optional if there is a byte stream or a character stream,
+     * but it is still useful to provide one, since the application can use it
+     * to resolve relative URIs and can include it in error messages and
+     * warnings (the parser will attempt to open a connection to the URI only
+     * if there is no byte stream or character stream specified).</p>
+     *
+     * <p>If the application knows the character encoding of the
+     * object pointed to by the URI, it can register
+     * the encoding using the setEncoding method.</p>
+     *
+     * <p>The URI must be fully resolved.</p>
+     *
+     * @param uri The URI as a string.
+     * @see #setEncoding
+     * @see #getURI
+     * @see Locator#getURI
+     * @see CSSParseException#getURI 
+     */
+    public void setURI(String uri) {
+	this.uri = uri;
+    }
+    
+    /**
+     * Get the URI for this input source.
+     *
+     * <p>The getEncoding method will return the character encoding
+     * of the object pointed to, or null if unknown.</p>
+     *
+     * <p>The URI will be fully resolved.</p>
+     *
+     * @return The URI.
+     * @see #setURI
+     * @see #getEncoding
+     */
+    public String getURI() {
+	return uri;
+    }
+    
+    /**
+     * Set the byte stream for this input source.
+     *
+     * <p>The SAX parser will ignore this if there is also a character
+     * stream specified, but it will use a byte stream in preference
+     * to opening a URI connection itself.</p>
+     *
+     * <p>If the application knows the character encoding of the
+     * byte stream, it should set it with the setEncoding method.</p>
+     *
+     * @param byteStream A byte stream containing an CSS document or
+     *        other entity.
+     * @see #setEncoding
+     * @see #getByteStream
+     * @see #getEncoding
+     */
+    public void setByteStream(InputStream byteStream) {
+	this.byteStream = byteStream;
+    }
+    
+    /**
+     * Get the byte stream for this input source.
+     *
+     * <p>The getEncoding method will return the character
+     * encoding for this byte stream, or null if unknown.</p>
+     *
+     * @return The byte stream, or null if none was supplied.
+     * @see #getEncoding
+     * @see #setByteStream
+     */
+    public InputStream getByteStream() {
+	return byteStream;
+    }
+    
+    /** 
+     * Set the character encoding, if known.
+     *
+     * <p>The encoding must be a string acceptable for an
+     * CHARSET encoding declaration (see section 4.4 of the CSS
+     * recommendation Level 2).</p>
+     *
+     * <p>This method has no effect when the application provides a
+     * character stream.</p>
+     *
+     * @param encoding A string describing the character encoding.
+     * @see #setURI
+     * @see #setByteStream
+     * @see #getEncoding
+     */
+    public void setEncoding(String encoding) {
+	this.encoding = encoding;
+    }
+    
+    /**
+     * Get the character encoding for a byte stream or URI.
+     *
+     * @return The encoding, or null if none was supplied.
+     * @see #setByteStream
+     * @see #getURI
+     * @see #getByteStream
+     */
+    public String getEncoding() {
+	return encoding;
+    }
+    
+    /**
+     * Set the character stream for this input source.
+     *
+     * <p>If there is a character stream specified, the SAX parser
+     * will ignore any byte stream and will not attempt to open
+     * a URI connection to the URI.</p>
+     *
+     * @param characterStream The character stream containing the
+     *        CSS document or other entity.
+     * @see #getCharacterStream
+     */
+    public void setCharacterStream(Reader characterStream) {
+	this.characterStream = characterStream;
+    }
+    
+    /**
+     * Get the character stream for this input source.
+     *
+     * @return The character stream, or null if none was supplied.
+     * @see #setCharacterStream
+     */
+    public Reader getCharacterStream() {
+	return characterStream;
+    }
+
+    /**
+     * Set the title for this input source.
+     * @param title The advisory title. See the title attribute definition
+     *        for the <a href="http://www.w3.org/TR/REC-html40/struct/links.html#edef-LINK">LINK</A>
+     *        element in HTML 4.0, and the title pseudo-attribute for the XML
+     *        style sheet processing instruction.
+     */
+    public void setTitle(String title) {
+	this.title = title;
+    }
+
+    /**
+     * Returns the title for this input source.
+     */    
+    public String getTitle() {
+	return title;
+    }
+
+    /**
+     * Set the media for this input source.
+     * @param media A comma separated list with all media.
+     */    
+    public void setMedia(String media) {
+	this.media = media;
+    }
+
+    /**
+     * Returns the media associated to the input source or <code>null</code>
+     * if media are currently unknown.
+     * @return the media associated to this input source.
+     */    
+    public String getMedia() {
+	if (media == null) {
+	    return "all";
+	}
+	return media;
+    }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LangCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LangCondition.java
new file mode 100644
index 0000000..0a45206
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LangCondition.java
@@ -0,0 +1,21 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: LangCondition.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_LANG_CONDITION
+ */
+public interface LangCondition extends Condition {
+    /**
+     * Returns the language
+     */
+    public String getLang();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LexicalUnit.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LexicalUnit.java
new file mode 100644
index 0000000..28f8193
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/LexicalUnit.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: LexicalUnit.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * This is a lexical unit for CSS values.
+ * <p><b>Remarks</b>: Not all the following lexical units are supported (or
+ * will be supported) by CSS.
+ * <p>All examples are CSS2 compliant.
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret
+ */
+public interface LexicalUnit {
+    
+    /**
+     * ,
+     */
+    public static final short SAC_OPERATOR_COMMA	= 0;
+    /**
+     * +
+     */
+    public static final short SAC_OPERATOR_PLUS		= 1;
+    /**
+     * -
+     */
+    public static final short SAC_OPERATOR_MINUS	= 2;
+    /**
+     * *
+     */
+    public static final short SAC_OPERATOR_MULTIPLY	= 3;
+    /**
+     * /
+     */
+    public static final short SAC_OPERATOR_SLASH	= 4;
+    /**
+     * %
+     */
+    public static final short SAC_OPERATOR_MOD		= 5;
+    /**
+     * ^
+     */
+    public static final short SAC_OPERATOR_EXP		= 6;
+    /**
+     * <
+     */
+    public static final short SAC_OPERATOR_LT		= 7;
+    /**
+     * >
+     */
+    public static final short SAC_OPERATOR_GT		= 8;
+    /**
+     * <=
+     */
+    public static final short SAC_OPERATOR_LE		= 9;
+    /**
+     * >=
+     */
+    public static final short SAC_OPERATOR_GE		= 10;
+    /**
+     * ~
+     */
+    public static final short SAC_OPERATOR_TILDE	= 11;
+    
+    /**
+     * identifier <code>inherit</code>.
+     */
+    public static final short SAC_INHERIT		= 12;
+    /**
+     * Integers.
+     * @see #getIntegerValue
+     */
+    public static final short SAC_INTEGER		= 13;
+    /**
+     * reals.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_REAL		        = 14;
+    /**
+     * Relative length<code>em</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_EM		= 15;
+    /**
+     * Relative length<code>ex</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_EX		= 16;
+    /**
+     * Relative length <code>px</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_PIXEL		= 17;
+    /**
+     * Absolute length <code>in</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_INCH		= 18;
+    /**
+     * Absolute length <code>cm</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_CENTIMETER	= 19;
+    /**
+     * Absolute length <code>mm</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_MILLIMETER	= 20;
+    /**
+     * Absolute length <code>pt</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_POINT		= 21;
+    /**
+     * Absolute length <code>pc</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_PICA		= 22;
+    /**
+     * Percentage.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_PERCENTAGE		= 23;
+    /**
+     * URI: <code>uri(&#x2e;&#x2e;&#x2e;)</code>.
+     * @see #getStringValue
+     */
+    public static final short SAC_URI		        = 24;
+    /**
+     * function <code>counter</code>.
+     * @see #getFunctionName
+     * @see #getParameters
+     */
+    public static final short SAC_COUNTER_FUNCTION	= 25;
+    /**
+     * function <code>counters</code>.
+     * @see #getFunctionName
+     * @see #getParameters
+     */
+    public static final short SAC_COUNTERS_FUNCTION	= 26;
+    /**
+     * RGB Colors.
+     * <code>rgb(0, 0, 0)</code> and <code>#000</code>
+     * @see #getFunctionName
+     * @see #getParameters
+     */
+    public static final short SAC_RGBCOLOR		= 27;
+    /**
+     * Angle <code>deg</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_DEGREE		= 28;
+    /**
+     * Angle <code>grad</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_GRADIAN		= 29;
+    /**
+     * Angle <code>rad</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_RADIAN		= 30;
+    /**
+     * Time <code>ms</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_MILLISECOND		= 31;
+    /**
+     * Time <code>s</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_SECOND		= 32;
+    /**
+     * Frequency <code>Hz</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_HERTZ		        = 33;
+    /**
+     * Frequency <code>kHz</code>.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_KILOHERTZ		= 34;
+    
+    /**
+     * any identifier except <code>inherit</code>.
+     * @see #getStringValue
+     */
+    public static final short SAC_IDENT		        = 35;
+    /**
+     * A string.
+     * @see #getStringValue
+     */
+    public static final short SAC_STRING_VALUE		= 36;
+    /**
+     * Attribute: <code>attr(&#x2e;&#x2e;&#x2e;)</code>.
+     * @see #getStringValue
+     */
+    public static final short SAC_ATTR		        = 37;
+    /**
+     * function <code>rect</code>.
+     * @see #getFunctionName
+     * @see #getParameters
+     */
+    public static final short SAC_RECT_FUNCTION		= 38;
+    /**
+     * A unicode range. @@TO BE DEFINED
+     */
+    public static final short SAC_UNICODERANGE		= 39;
+    
+    /**
+     * sub expressions
+     * <code>(a)</code> <code>(a + b)</code> <code>(normal/none)</code>
+     * @see #getSubValues
+     */
+    public static final short SAC_SUB_EXPRESSION	= 40;
+    
+    /**
+     * unknown function.
+     * @see #getFunctionName
+     * @see #getParameters
+     */
+    public static final short SAC_FUNCTION		= 41;
+    /**
+     * unknown dimension.
+     * @see #getFloatValue
+     * @see #getDimensionUnitText
+     */
+    public static final short SAC_DIMENSION		= 42;
+    
+    /**
+     * An integer indicating the type of <code>LexicalUnit</code>.
+     */
+    public short       getLexicalUnitType();
+    
+    /**
+     * Returns the next value or <code>null</code> if any.
+     */
+    public LexicalUnit getNextLexicalUnit();
+    
+    /**
+     * Returns the previous value or <code>null</code> if any.
+     */
+    public LexicalUnit getPreviousLexicalUnit();
+    
+    /**
+     * Returns the integer value.
+     * @see #SAC_INTEGER
+     */
+    public int getIntegerValue();
+    
+    
+    /**
+     * Returns the float value.
+     * <p>If the type of <code>LexicalUnit</code> is one of SAC_DEGREE,
+     * SAC_GRADIAN, SAC_RADIAN, SAC_MILLISECOND, SAC_SECOND, SAC_HERTZ
+     * or SAC_KILOHERTZ, the value can never be negative.</p>
+     *
+     * @see #SAC_REAL
+     * @see #SAC_DIMENSION
+     * @see #SAC_EM
+     * @see #SAC_EX
+     * @see #SAC_PIXEL
+     * @see #SAC_INCH
+     * @see #SAC_CENTIMETER
+     * @see #SAC_MILLIMETER
+     * @see #SAC_POINT
+     * @see #SAC_PICA
+     * @see #SAC_PERCENTAGE
+     * @see #SAC_DEGREE
+     * @see #SAC_GRADIAN
+     * @see #SAC_RADIAN
+     * @see #SAC_MILLISECOND
+     * @see #SAC_SECOND
+     * @see #SAC_HERTZ
+     * @see #SAC_KILOHERTZ
+     */
+    public float getFloatValue();
+    
+    /**
+     * Returns the string representation of the unit.
+     * <p>if this lexical unit represents a float, the dimension is an empty
+     * string.</p>
+     * @see #SAC_REAL
+     * @see #SAC_DIMENSION
+     * @see #SAC_EM
+     * @see #SAC_EX
+     * @see #SAC_PIXEL
+     * @see #SAC_INCH
+     * @see #SAC_CENTIMETER
+     * @see #SAC_MILLIMETER
+     * @see #SAC_POINT
+     * @see #SAC_PICA
+     * @see #SAC_PERCENTAGE
+     * @see #SAC_DEGREE
+     * @see #SAC_GRADIAN
+     * @see #SAC_RADIAN
+     * @see #SAC_MILLISECOND
+     * @see #SAC_SECOND
+     * @see #SAC_HERTZ
+     * @see #SAC_KILOHERTZ 
+     */
+    public String getDimensionUnitText();
+    
+    /**
+     * Returns the name of the function.
+     * @see #SAC_COUNTER_FUNCTION
+     * @see #SAC_COUNTERS_FUNCTION
+     * @see #SAC_RECT_FUNCTION
+     * @see #SAC_FUNCTION
+     * @see #SAC_RGBCOLOR
+     */
+    public String      getFunctionName();
+    
+    /**
+     * The function parameters including operators (like the comma).
+     * <code>#000</code> is converted to <code>rgb(0, 0, 0)</code>
+     * can return <code>null</code> if <code>SAC_FUNCTION</code>.
+     * @see #SAC_COUNTER_FUNCTION
+     * @see #SAC_COUNTERS_FUNCTION
+     * @see #SAC_RECT_FUNCTION
+     * @see #SAC_FUNCTION
+     * @see #SAC_RGBCOLOR
+     */
+    public LexicalUnit getParameters();
+
+    /**
+     * Returns the string value.
+     * <p>If the type is <code>SAC_URI</code>, the return value doesn't contain
+     * <code>uri(....)</code> or quotes.
+     * <p>If the type is <code>SAC_ATTR</code>, the return value doesn't contain
+     * <code>attr(....)</code>.
+     *
+     * @see #SAC_URI
+     * @see #SAC_ATTR
+     * @see #SAC_IDENT
+     * @see #SAC_STRING_VALUE
+     * @see #SAC_UNICODERANGE @@TO BE DEFINED 
+     */
+    public String getStringValue();
+
+    /**
+     * Returns a list of values inside the sub expression.
+     * @see #SAC_SUB_EXPRESSION
+     */
+    public LexicalUnit getSubValues();
+    
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Locator.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Locator.java
new file mode 100644
index 0000000..15b0bed
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Locator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: Locator.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * Interface for associating a CSS event with a document location.
+ *
+ * <p>If a SAX parser provides location information to the SAX
+ * application, it does so by implementing this interface and then
+ * passing an instance to the application using the document
+ * handler's setDocumentLocator method.  The application can use the
+ * object to obtain the location of any other document handler event
+ * in the CSS source document.</p>
+ *
+ * <p>Note that the results returned by the object will be valid only
+ * during the scope of each document handler method: the application
+ * will receive unpredictable results if it attempts to use the
+ * locator at any other time.</p>
+ *
+ * <p>CSS parsers are not required to supply a locator, but they are
+ * very strong encouraged to do so.  If the parser supplies a
+ * locator, it must do so before reporting any other document events.
+ * If no locator has been set by the time the application receives
+ * the startDocument event, the application should assume that a
+ * locator is not available.</p>
+ *
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public interface Locator {
+    
+    /**
+     * Return the URI for the current document event.
+     *
+     * <p>The parser must resolve the URI fully before passing it to the
+     * application.</p>
+     *
+     * @return A string containing the URI, or null
+     *         if none is available.
+     */
+    public String getURI();
+    
+    /**
+     * Return the line number where the current document event ends.
+     * Note that this is the line position of the first character
+     * after the text associated with the document event.
+     * @return The line number, or -1 if none is available.
+     * @see #getColumnNumber
+     */
+    public int getLineNumber();
+    
+    /**
+     * Return the column number where the current document event ends.
+     * Note that this is the column number of the first
+     * character after the text associated with the document
+     * event.  The first column in a line is position 1.
+     * @return The column number, or -1 if none is available.
+     * @see #getLineNumber
+     */
+    public int getColumnNumber();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeCondition.java
new file mode 100644
index 0000000..c4c3e50
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeCondition.java
@@ -0,0 +1,22 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: NegativeCondition.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_NEGATIVE_CONDITION
+ */
+public interface NegativeCondition extends Condition {
+
+    /**
+     * Returns the condition.
+     */    
+    public Condition getCondition();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeSelector.java
new file mode 100644
index 0000000..742b059
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/NegativeSelector.java
@@ -0,0 +1,22 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: NegativeSelector.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_NEGATIVE_SELECTOR
+ */
+public interface NegativeSelector extends SimpleSelector {
+
+    /**
+     * Returns the simple selector.
+     */    
+    public SimpleSelector getSimpleSelector();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Parser.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Parser.java
new file mode 100644
index 0000000..c3dbec1
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Parser.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * The original version of this interface comes from SAX :
+ * http://www.megginson.com/SAX/
+ *
+ * $Id: Parser.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * Basic interface for CSS (Simple API for CSS) parsers.
+ *
+ * <p>All CSS parsers must implement this basic interface: it allows
+ * applications to register handlers for different types of events
+ * and to initiate a parse from a URI, or a character stream.</p>
+ *
+ * <p>All CSS parsers must also implement a zero-argument constructor
+ * (though other constructors are also allowed).</p>
+ *
+ * <p>CSS parsers are reusable but not re-entrant: the application
+ * may reuse a parser object (possibly with a different input source)
+ * once the first parse has completed successfully, but it may not
+ * invoke the parse() methods recursively within a parse.</p>
+ *
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see DocumentHandler
+ * @see ErrorHandler
+ * @see InputSource
+ */
+public interface Parser {
+    
+    /**
+     * Allow an application to request a locale for errors and warnings.
+     *
+     * <p>CSS parsers are not required to provide localisation for errors
+     * and warnings; if they cannot support the requested locale,
+     * however, they must throw a CSS exception.  Applications may
+     * not request a locale change in the middle of a parse.</p>
+     *
+     * @param locale A Java Locale object.
+     * @exception CSSException Throws an exception
+     *            (using the previous or default locale) if the 
+     *            requested locale is not supported.
+     * @see CSSException
+     * @see CSSParseException
+     */
+    public void setLocale(Locale locale) throws CSSException;
+    
+    /**
+     * Allow an application to register a document event handler.
+     *
+     * <p>If the application does not register a document handler, all
+     * document events reported by the CSS parser will be silently
+     * ignored (this is the default behaviour implemented by
+     * HandlerBase).</p>
+     *
+     * <p>Applications may register a new or different handler in the
+     * middle of a parse, and the CSS parser must begin using the new
+     * handler immediately.</p>
+     *
+     * @param handler The document handler.
+     * @see DocumentHandler
+     */
+    public void setDocumentHandler(DocumentHandler handler);
+
+    public void setSelectorFactory(SelectorFactory selectorFactory);
+    public void setConditionFactory(ConditionFactory conditionFactory);
+    
+    /**
+     * Allow an application to register an error event handler.
+     *
+     * <p>If the application does not register an error event handler,
+     * all error events reported by the CSS parser will be silently
+     * ignored, except for fatalError, which will throw a CSSException
+     * (this is the default behaviour implemented by HandlerBase).</p>
+     *
+     * <p>Applications may register a new or different handler in the
+     * middle of a parse, and the CSS parser must begin using the new
+     * handler immediately.</p>
+     *
+     * @param handler The error handler.
+     * @see ErrorHandler
+     * @see CSSException
+     */
+    public void setErrorHandler(ErrorHandler handler);
+    
+    /**
+     * Parse a CSS document.
+     *
+     * <p>The application can use this method to instruct the CSS parser
+     * to begin parsing an CSS document from any valid input
+     * source (a character stream, a byte stream, or a URI).</p>
+     *
+     * <p>Applications may not invoke this method while a parse is in
+     * progress (they should create a new Parser instead for each
+     * additional CSS document).  Once a parse is complete, an
+     * application may reuse the same Parser object, possibly with a
+     * different input source.</p>
+     *
+     * @param source The input source for the top-level of the
+     *        CSS document.
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     * @see InputSource
+     * @see #parseStyleSheet(java.lang.String)
+     * @see #setDocumentHandler
+     * @see #setErrorHandler
+     */
+    public void parseStyleSheet(InputSource source) 
+	throws CSSException, IOException;
+    
+    
+    /**
+     * Parse a CSS document from a URI.
+     *
+     * <p>This method is a shortcut for the common case of reading a document
+     * from a URI.  It is the exact equivalent of the following:</p>
+     *
+     * <pre>
+     * parse(new InputSource(uri));
+     * </pre>
+     *
+     * <p>The URI must be fully resolved by the application before it is passed
+     * to the parser.</p>
+     *
+     * @param uri The URI.
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     * @see #parseStyleSheet(InputSource) 
+     */
+    public void parseStyleSheet(String uri) throws CSSException, IOException;
+
+    /**
+     * Parse a CSS style declaration (without '{' and '}').
+     *
+     * @param source The source of the style sheet.
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     */
+    public void parseStyleDeclaration(InputSource source) 
+	throws CSSException, IOException;
+
+
+    /**
+     * Parse a CSS rule.
+     *
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     */
+    public void parseRule(InputSource source) throws CSSException, IOException;
+
+    /**
+     * Returns a string about which CSS language is supported by this
+     * parser. For CSS Level 1, it returns "CSS1", for CSS Level 2, it returns
+     * "CSS2". For CSS Level 3, it returns "CSS3", etc. Note that a "CSSx"
+     * parser can return lexical unit other than those allowed by CSS Level x
+     * but this usage is not recommended.
+     */
+    public String getParserVersion();
+    
+    /**
+     * Parse a comma separated list of selectors.
+     * 
+     * 
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     */    
+    public SelectorList parseSelectors(InputSource source)
+        throws CSSException, IOException;
+
+
+    /**
+     * Parse a CSS property value.
+     * 
+     * 
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     */    
+    public LexicalUnit parsePropertyValue(InputSource source)
+        throws CSSException, IOException;
+
+    
+    /**
+     * Parse a CSS priority value (e&#x2e;g&#x2e; "&#x21;important").
+     * 
+     * 
+     * @exception CSSException Any CSS exception, possibly
+     *            wrapping another exception.
+     * @exception java.io.IOException An IO exception from the parser,
+     *            possibly from a byte stream or character stream
+     *            supplied by the application.
+     */    
+    public boolean parsePriority(InputSource source)
+        throws CSSException, IOException;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/PositionalCondition.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/PositionalCondition.java
new file mode 100644
index 0000000..f8dd8c2
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/PositionalCondition.java
@@ -0,0 +1,36 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: PositionalCondition.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Condition#SAC_POSITIONAL_CONDITION
+ */
+public interface PositionalCondition extends Condition {
+
+    /**
+     * Returns the position in the tree.
+     * <p>A negative value means from the end of the child node list.
+     * <p>The child node list begins at 0.
+     */    
+    public int getPosition();
+
+    /**
+     * <code>true</code> if the child node list only shows nodes of the same
+     * type of the selector (only elements, only PIS, ...)
+     */
+    public boolean getTypeNode();
+
+    /**
+     * <code>true</code> if the node should have the same node type (for
+     *  element, same namespaceURI and same localName).
+     */
+    public boolean getType();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ProcessingInstructionSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ProcessingInstructionSelector.java
new file mode 100644
index 0000000..1604d0c
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/ProcessingInstructionSelector.java
@@ -0,0 +1,31 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: ProcessingInstructionSelector.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * This simple matches a
+ * <a href="http://www.w3.org/TR/REC-xml#sec-pi">processing instruction</a>.
+ *
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_PROCESSING_INSTRUCTION_NODE_SELECTOR
+ */
+public interface ProcessingInstructionSelector extends SimpleSelector {
+
+    /**
+     * Returns the <a href="http://www.w3.org/TR/REC-xml#NT-PITarget">target</a>
+     * of the processing instruction.
+     */    
+    public String getTarget();
+    
+    /**
+     * Returns the character data.
+     */
+    public String getData();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SACMediaList.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SACMediaList.java
new file mode 100644
index 0000000..72db2f7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SACMediaList.java
@@ -0,0 +1,27 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: SACMediaList.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ */
+public interface SACMediaList {
+
+    /**
+     * Returns the length of this media list
+     */    
+    public int getLength();
+
+    /**
+     * Returns the medium at the specified index, or <code>null</code> if this
+     * is not a valid index.  
+     */
+    public String item(int index);
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Selector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Selector.java
new file mode 100644
index 0000000..5ca82d5
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/Selector.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: Selector.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * This interface defines a selector.
+ * <p><b>Remarks</b>: Not all the following selectors are supported (or will be
+ * supported) by CSS.
+ * <p>All examples are CSS2 compliant.
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret
+ */
+public interface Selector {
+    
+    /* simple selectors */
+
+    /**
+     * This is a conditional selector.
+     * example:
+     * <pre class="example">
+     *   simple[role="private"]
+     *   .part1
+     *   H1#myId
+     *   P:lang(fr).p1
+     * </pre>
+     *
+     * @see ConditionalSelector
+     */
+    public static final short SAC_CONDITIONAL_SELECTOR		= 0;
+
+    /**
+     * This selector matches any node.
+     * @see SimpleSelector
+     */    
+    public static final short SAC_ANY_NODE_SELECTOR		= 1;
+
+    /**
+     * This selector matches the root node.
+     * @see SimpleSelector
+     */    
+    public static final short SAC_ROOT_NODE_SELECTOR		= 2;
+
+    /**
+     * This selector matches only node that are different from a specified one.
+     * @see NegativeSelector
+     */    
+    public static final short SAC_NEGATIVE_SELECTOR		= 3;
+
+    /**
+     * This selector matches only element node.
+     * example:
+     * <pre class="example">
+     *   H1
+     *   animate
+     * </pre>
+     * @see ElementSelector
+     */
+    public static final short SAC_ELEMENT_NODE_SELECTOR		= 4;
+
+    /**
+     * This selector matches only text node.
+     * @see CharacterDataSelector
+     */
+    public static final short SAC_TEXT_NODE_SELECTOR		= 5;
+
+    /**
+     * This selector matches only cdata node.
+     * @see CharacterDataSelector
+     */
+    public static final short SAC_CDATA_SECTION_NODE_SELECTOR	= 6;
+
+    /**
+     * This selector matches only processing instruction node.
+     * @see ProcessingInstructionSelector
+     */
+    public static final short SAC_PROCESSING_INSTRUCTION_NODE_SELECTOR	= 7;
+
+    /**
+     * This selector matches only comment node.
+     * @see CharacterDataSelector
+     */    
+    public static final short SAC_COMMENT_NODE_SELECTOR		= 8;
+    /**
+     * This selector matches the 'first line' pseudo element.
+     * example:
+     * <pre class="example">
+     *   :first-line
+     * </pre>
+     * @see ElementSelector
+     */
+    public static final short SAC_PSEUDO_ELEMENT_SELECTOR	= 9;
+
+    /* combinator selectors */
+
+    /**
+     * This selector matches an arbitrary descendant of some ancestor element.
+     * example:
+     * <pre class="example">
+     *   E F
+     * </pre>
+     * @see DescendantSelector
+     */    
+    public static final short SAC_DESCENDANT_SELECTOR		= 10;
+
+    /**
+     * This selector matches a childhood relationship between two elements.
+     * example:
+     * <pre class="example">
+     *   E > F
+     * </pre>
+     * @see DescendantSelector
+     */    
+    public static final short SAC_CHILD_SELECTOR		= 11;
+    /**
+     * This selector matches two selectors who shared the same parent in the
+     * document tree and the element represented by the first sequence
+     * immediately precedes the element represented by the second one.
+     * example:
+     * <pre class="example">
+     *   E + F
+     * </pre>
+     * @see SiblingSelector
+     */
+    public static final short SAC_DIRECT_ADJACENT_SELECTOR	= 12;
+
+    /**
+     * An integer indicating the type of <code>Selector</code>
+     */
+    public short getSelectorType();
+
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorFactory.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorFactory.java
new file mode 100644
index 0000000..fa2e713
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium,
+ * (Massachusetts Institute of Technology, Institut National de
+ * Recherche en Informatique et en Automatique, Keio University). All
+ * Rights Reserved. This program is distributed under the W3C's Software
+ * Intellectual Property License. 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 W3C License http://www.w3.org/Consortium/Legal/ for more details.
+ *
+ * $Id: SelectorFactory.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see org.w3c.css.sac.Selector
+ */
+public interface SelectorFactory {
+
+    /**
+     * Creates a conditional selector.
+     * 
+     * @param selector a selector.
+     * @param condition a condition
+     * @return the conditional selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    ConditionalSelector createConditionalSelector(SimpleSelector selector,
+						  Condition condition) 
+	throws CSSException;
+
+    /**
+     * Creates an any node selector.
+     * 
+     * @return the any node selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    SimpleSelector createAnyNodeSelector() throws CSSException;
+
+    /**
+     * Creates an root node selector.
+     * 
+     * @return the root node selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    SimpleSelector createRootNodeSelector() throws CSSException;
+
+    /**
+     * Creates an negative selector.
+     * 
+     * @param selector a selector.
+     * @return the negative selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    NegativeSelector createNegativeSelector(SimpleSelector selector) 
+	throws CSSException;
+
+    /**
+     * Creates an element selector.
+     * 
+     * @param namespaceURI the <a href="http://www.w3.org/TR/REC-xml-names/#dt-NSName">namespace
+     *                     URI</a> of the element selector.
+     * @param tagName the <a href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local
+     *        part</a> of the element name. <code>NULL</code> if this element
+     *        selector can match any element.</p>
+     * @return the element selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    ElementSelector createElementSelector(String namespaceURI, String tagName) 
+	throws CSSException;
+
+    /**
+     * Creates a text node selector.
+     * 
+     * @param data the data
+     * @return the text node selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    CharacterDataSelector createTextNodeSelector(String data)
+	throws CSSException;
+
+    /**
+     * Creates a cdata section node selector.
+     * 
+     * @param data the data
+     * @return the cdata section node selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    CharacterDataSelector createCDataSectionSelector(String data)
+	throws CSSException;
+
+    /**
+     * Creates a processing instruction node selector.
+     * 
+     * @param target the target
+     * @param data the data
+     * @return the processing instruction node selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    ProcessingInstructionSelector 
+	createProcessingInstructionSelector(String target,
+					    String data)
+	throws CSSException;
+
+    /**
+     * Creates a comment node selector.
+     * 
+     * @param data the data
+     * @return the comment node selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    CharacterDataSelector createCommentSelector(String data)
+	throws CSSException;
+
+    /**
+     * Creates a pseudo element selector.
+     * 
+     * @param pseudoName the pseudo element name. <code>NULL</code> if this
+     *                   element selector can match any pseudo element.</p>
+     * @return the element selector
+     * @exception CSSException If this selector is not supported.
+     */    
+    ElementSelector createPseudoElementSelector(String namespaceURI, 
+						String pseudoName) 
+	throws CSSException;
+
+    /**
+     * Creates a descendant selector.
+     *
+     * @param parent the parent selector
+     * @param descendant the descendant selector
+     * @return the combinator selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    DescendantSelector createDescendantSelector(Selector parent,
+					     SimpleSelector descendant)
+	throws CSSException;
+
+    /**
+     * Creates a child selector.
+     *
+     * @param parent the parent selector
+     * @param child the child selector
+     * @return the combinator selector.
+     * @exception CSSException If this selector is not supported.
+     */    
+    DescendantSelector createChildSelector(Selector parent,
+					   SimpleSelector child)
+	throws CSSException;
+
+    /**
+     * Creates a sibling selector.
+     *
+     * @param nodeType the type of nodes in the siblings list.
+     * @param child the child selector
+     * @param directAdjacent the direct adjacent selector
+     * @return the sibling selector with nodeType 
+     *         equals to org.w3c.dom.Node.ELEMENT_NODE
+     * @exception CSSException If this selector is not supported.
+     */
+    SiblingSelector createDirectAdjacentSelector(short nodeType,
+						 Selector child,
+						 SimpleSelector directAdjacent)
+	throws CSSException;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorList.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorList.java
new file mode 100644
index 0000000..bccffc0
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SelectorList.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: SelectorList.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * The SelectorList interface provides the abstraction of an ordered collection
+ * of selectors, without defining or constraining how this collection is
+ * implemented.
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret
+ */
+public interface SelectorList {
+
+    /**
+     * Returns the length of this selector list
+     */    
+    public int getLength();
+
+    /**
+     * Returns the selector at the specified index, or <code>null</code> if this
+     * is not a valid index.  
+     */
+    public Selector item(int index);
+}
+
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SiblingSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SiblingSelector.java
new file mode 100644
index 0000000..db4c955
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SiblingSelector.java
@@ -0,0 +1,36 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: SiblingSelector.java,v 1.1 2009/12/06 10:40:08 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * @version $Revision: 1.1 $
+ * @author  Philippe Le Hegaret
+ * @see Selector#SAC_DIRECT_ADJACENT_SELECTOR
+ */
+public interface SiblingSelector extends Selector {
+
+    public static final short ANY_NODE = 201;
+
+    /**
+     * The node type to considered in the siblings list.
+     * All DOM node types are supported. In order to support the "any" node
+     * type, the code ANY_NODE is added to the DOM node types.
+     */
+    public short getNodeType();
+    
+    /**
+     * Returns the first selector.
+     */    
+    public Selector getSelector();
+
+    /*
+     * Returns the second selector.
+     */    
+    public SimpleSelector getSiblingSelector();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SimpleSelector.java b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SimpleSelector.java
new file mode 100644
index 0000000..227caf3
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/css/org/w3c/css/sac/SimpleSelector.java
@@ -0,0 +1,21 @@
+/*
+ * (c) COPYRIGHT 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ *  en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: SimpleSelector.java,v 1.1 2009/12/06 10:40:07 rsternber Exp $
+ */
+package org.w3c.css.sac;
+
+/**
+ * This interface is only for constraints on selectors.
+ *
+ * <p>A <code>ConditionalSelector</code> can only accept a simple selector or a
+ * negative selector.</p>
+ *
+ * @version $Revision: 1.1 $
+ * @author Philippe Le Hegaret */
+public interface SimpleSelector extends Selector {
+    // empty
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/delete_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/delete_obj.gif
new file mode 100644
index 0000000..b6922ac
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/delete_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/error_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/error_obj.gif
new file mode 100644
index 0000000..0bc6068
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/error_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/field_private_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/field_private_obj.gif
new file mode 100644
index 0000000..1fe064e
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/field_private_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/file_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/file_obj.gif
new file mode 100644
index 0000000..061161a
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/file_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/font.gif b/bundles/org.eclipse.rap.themeeditor/icons/font.gif
new file mode 100644
index 0000000..8d187db
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/font.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/help.gif b/bundles/org.eclipse.rap.themeeditor/icons/help.gif
new file mode 100644
index 0000000..9d70301
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/help.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/image_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/image_obj.gif
new file mode 100644
index 0000000..830be0e
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/image_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/no-image.gif b/bundles/org.eclipse.rap.themeeditor/icons/no-image.gif
new file mode 100644
index 0000000..18c5200
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/no-image.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/pageedit.gif b/bundles/org.eclipse.rap.themeeditor/icons/pageedit.gif
new file mode 100644
index 0000000..9f84990
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/pageedit.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/public_co.gif b/bundles/org.eclipse.rap.themeeditor/icons/public_co.gif
new file mode 100644
index 0000000..a9af5d5
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/public_co.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/swt_launcher.gif b/bundles/org.eclipse.rap.themeeditor/icons/swt_launcher.gif
new file mode 100644
index 0000000..3c4c7c8
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/swt_launcher.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/transparent-banner.gif b/bundles/org.eclipse.rap.themeeditor/icons/transparent-banner.gif
new file mode 100644
index 0000000..c8f0c29
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/transparent-banner.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/transparent-large.gif b/bundles/org.eclipse.rap.themeeditor/icons/transparent-large.gif
new file mode 100644
index 0000000..4d59c33
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/transparent-large.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/transparent-small.gif b/bundles/org.eclipse.rap.themeeditor/icons/transparent-small.gif
new file mode 100644
index 0000000..188d532
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/transparent-small.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/icons/warning_obj.gif b/bundles/org.eclipse.rap.themeeditor/icons/warning_obj.gif
new file mode 100644
index 0000000..2b2e50f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/icons/warning_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.rap.themeeditor/plugin.xml b/bundles/org.eclipse.rap.themeeditor/plugin.xml
new file mode 100644
index 0000000..e10d156
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/plugin.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+
+   <extension
+         point="org.eclipse.ui.editors">
+      <editor
+            class="org.eclipse.rap.themeeditor.editor.source.CSSSourceEditor"
+            filenames="theme.css"
+            icon="icons/file_obj.gif"
+            id="org.eclipse.rap.themeeditor.editor.ThemeEditor"
+            name="RAP Theme Editor">
+      </editor>
+   </extension>
+
+   <extension
+         point="org.eclipse.core.filebuffers.documentSetup">
+      <participant
+         class="org.eclipse.rap.themeeditor.editor.source.CSSDocumentSetupParticipant"
+         extensions="css">
+      </participant>
+   </extension>
+</plugin>
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ColorRegistry.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ColorRegistry.java
new file mode 100644
index 0000000..8fc6da3
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ColorRegistry.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Manager for holding instance of SWT Color object, and has the responsibility
+ * to dispose them when method dispose() is called.
+ */
+public class ColorRegistry {
+
+  private Display display;
+  private Map colorMap;
+
+  public ColorRegistry( final Display display ) {
+    colorMap = new HashMap();
+    this.display = display;
+  }
+
+  public Color getColor( final RGB rgb ) {
+    Color result = ( Color )colorMap.get( rgb );
+    if( result == null ) {
+      result = new Color( display, rgb );
+      colorMap.put( rgb, result );
+    }
+    return result;
+  }
+
+  public void dispose() {
+    Iterator iterator = colorMap.values().iterator();
+    while( iterator.hasNext() ) {
+      Color color = ( Color )iterator.next();
+      color.dispose();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/SupportedKeywords.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/SupportedKeywords.java
new file mode 100644
index 0000000..2a7468d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/SupportedKeywords.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * All keywords that are supported by RAP, including selectors, styles, states
+ * and properties. Used for content assists and problem markers in source tab.
+ */
+public class SupportedKeywords {
+
+  public static final int UNDEFINED = 0;
+  public static final int SELECTOR_TYPE = 1;
+  public static final int PROPERTY_TYPE = 2;
+  public static final int STYLE_TYPE = 3;
+  public static final int STATE_TYPE = 4;
+  public static final String[] SELECTORS = {
+    "Button",
+    "Combo",
+    "CoolBar",
+    "CoolItem",
+    "CTabFolder",
+    "CTabItem",
+    "Group",
+    "Group-Frame",
+    "Group-Label",
+    "Label",
+    "Link",
+    "Link-Hyperlink",
+    "List",
+    "List-Item",
+    "Menu",
+    "MenuItem",
+    "ProgressBar",
+    "ProgressBar-Indicator",
+    "Shell",
+    "Shell-Titlebar",
+    "Shell-MinButton",
+    "Shell-MaxButton",
+    "Shell-CloseButton",
+    "Spinner",
+    "TabFolder",
+    "TabItem",
+    "Table",
+    "TableItem",
+    "TableColumn",
+    "TableColumn-SortIndicator",
+    "Table-GridLine",
+    "Table-Checkbox",
+    "Text",
+    "ToolBar",
+    "ToolItem",
+    "ToolTip",
+    "Tree",
+    "TreeItem",
+    "TreeColumn",
+    "TreeColumn-SortIndicator",
+    "*"
+  };
+  public static final String[] STYLES = {
+    "PUSH", "TOGGLE", "CHECK", "RADIO", "BORDER", "FLAT", "SINGLE", "MULTI"
+  };
+  public static final String[] STATES = {
+    "hover",
+    "pressed",
+    "up",
+    "down",
+    "disabled",
+    "selected",
+    "inactive",
+    "maximized",
+    "minimized"
+  };
+  public static final String[] PROPERTIES = {
+    "background-color",
+    "background-gradient-color",
+    "background-image",
+    "border",
+    "color",
+    "font",
+    "height",
+    "padding",
+    "margin",
+    "spacing",
+    "width",
+    "rwt-darkshadow-color",
+    "rwt-highlight-color",
+    "rwt-lightshadow-color",
+    "rwt-selectionmarker-color",
+    "rwt-shadow-color",
+    "rwt-thinborder-color"
+  };
+  public static final String[] COLOR_PROPERTIES = {
+    "background-color",
+    "background-gradient-color",
+    "border",
+    "color",
+    "rwt-darkshadow-color",
+    "rwt-highlight-color",
+    "rwt-lightshadow-color",
+    "rwt-selectionmarker-color",
+    "rwt-shadow-color",
+    "rwt-thinborder-color"
+  };
+  private static final String STARTS_WITH = "rwt";
+  private static final String ENDS_WITH = "color";
+
+  public static boolean isPropertySupported( final String name ) {
+    boolean result = false;
+    if( name.startsWith( STARTS_WITH ) && name.endsWith( ENDS_WITH ) ) {
+      result = true;
+    } else {
+      for( int i = 0; i < PROPERTIES.length; i++ ) {
+        if( name.equals( PROPERTIES[ i ] ) ) {
+          result = true;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static boolean isSelectorSupported( final String selector ) {
+    boolean result = false;
+    if( selector == null ) {
+      result = true;
+    } else {
+      for( int i = 0; i < SELECTORS.length; i++ ) {
+        if( selector.equals( SELECTORS[ i ] ) ) {
+          result = true;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static boolean isStyleSupported( final String style ) {
+    boolean result = false;
+    if( style == null ) {
+      result = true;
+    } else {
+      for( int i = 0; i < STYLES.length; i++ ) {
+        if( style.equals( STYLES[ i ] ) ) {
+          result = true;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static boolean isStateSupported( final String state ) {
+    boolean result = false;
+    if( state == null ) {
+      result = true;
+    } else {
+      for( int i = 0; i < STATES.length; i++ ) {
+        if( state.equals( STATES[ i ] ) ) {
+          result = true;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static String[] getKeywordsStartWith( final String start,
+                                               final int type )
+  {
+    String[] result = null;
+    String[] keywords = null;
+    switch( type ) {
+      case SELECTOR_TYPE:
+        keywords = SELECTORS;
+      break;
+      case PROPERTY_TYPE:
+        keywords = PROPERTIES;
+      break;
+      case STYLE_TYPE:
+        keywords = STYLES;
+      break;
+      case STATE_TYPE:
+        keywords = STATES;
+      break;
+    }
+    if( keywords != null && start != null ) {
+      List resultList = new ArrayList();
+      for( int i = 0; i < keywords.length; i++ ) {
+        String item = keywords[ i ];
+        if( item.toLowerCase().startsWith( start.toLowerCase() ) ) {
+          resultList.add( item );
+        }
+      }
+      result = new String[ resultList.size() ];
+      result = ( String[] )resultList.toArray( result );
+      Arrays.sort( result );
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ThemeEditorPlugin.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ThemeEditorPlugin.java
new file mode 100644
index 0000000..9ff7443
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/ThemeEditorPlugin.java
@@ -0,0 +1,68 @@
+package org.eclipse.rap.themeeditor;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+public class ThemeEditorPlugin extends AbstractUIPlugin {
+
+  public static final String IMG_WARNING = "warning_obj.gif";
+  public static final String IMG_ERROR = "error_obj.gif";
+  public static final String IMG_FIELD_PRIVATE = "field_private_obj.gif";
+  public static final String IMG_PUBLIC = "public_co.gif";
+
+  public static final String PLUGIN_ID = "org.eclipse.rap.themeeditor.csseditor"; //$NON-NLS-1$
+
+  private static ThemeEditorPlugin sharedInstance;
+
+  private ColorRegistry colorRegistry;
+
+  public void start( BundleContext context ) throws Exception {
+    super.start( context );
+    sharedInstance = this;
+  }
+
+  public void stop( BundleContext context ) throws Exception {
+    sharedInstance = null;
+    super.stop( context );
+  }
+
+  public static ThemeEditorPlugin getDefault() {
+    return sharedInstance;
+  }
+
+  public ColorRegistry getColorRegistry() {
+    if( colorRegistry == null ) {
+      colorRegistry = new ColorRegistry( Display.getDefault() );
+    }
+    return colorRegistry;
+  }
+
+  public Image getImage( String key ) {
+    return getImageRegistry().get( key );
+  }
+
+
+  public static ImageDescriptor getImageDescriptor( final String path ) {
+    return imageDescriptorFromPlugin( PLUGIN_ID, path );
+  }
+
+  protected void initializeImageRegistry( final ImageRegistry registry ) {
+    registerImage( registry, IMG_WARNING, IMG_WARNING );
+    registerImage( registry, IMG_ERROR, IMG_ERROR );
+    registerImage( registry, IMG_FIELD_PRIVATE, IMG_FIELD_PRIVATE );
+    registerImage( registry, IMG_PUBLIC, IMG_PUBLIC );
+  }
+
+  private void registerImage( final ImageRegistry registry,
+                              final String key,
+                              final String fileName )
+  {
+    String path = "icons/" + fileName;
+    ImageDescriptor descriptor = getImageDescriptor( path );
+    registry.put( key, descriptor );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/CSSContentOutlinePage.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/CSSContentOutlinePage.java
new file mode 100644
index 0000000..eab813d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/CSSContentOutlinePage.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.rap.themeeditor.editor.rule.DefaultContentProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
+
+/**
+ * Implementation of an Outline page showing StyleRules as items if the
+ * Rules-Tab is activated, or showing OutlineRegions as items if the Source-Tab
+ * is activated.
+ */
+public class CSSContentOutlinePage extends ContentOutlinePage {
+
+  protected Object[] input;
+  protected IDocumentProvider documentProvider;
+  protected ITextEditor textEditor;
+  private boolean isSetSelectionEnabled = true;
+  private int tabIndex = -1;
+  private Map listenerMap;
+
+  public CSSContentOutlinePage() {
+    super();
+    listenerMap = new HashMap();
+  }
+
+  public void createControl( final Composite parent ) {
+    super.createControl( parent );
+    TreeViewer viewer = getTreeViewer();
+    viewer.setContentProvider( new DefaultContentProvider() );
+    viewer.setLabelProvider( new ContentOutlineLabelProvider() );
+    viewer.addSelectionChangedListener( this );
+    if( input != null )
+      viewer.setInput( input );
+  }
+
+  /**
+   * Handles the SelectionChangedEvent if the Outline selection has been changed
+   * by the user, and so it calls a possibly registered listener.
+   */
+  public void selectionChanged( final SelectionChangedEvent event ) {
+    isSetSelectionEnabled = false;
+    super.selectionChanged( event );
+    int newIndex = -1;
+    Object item = null;
+    ISelection selection = event.getSelection();
+    if( !selection.isEmpty() ) {
+      item = ( ( IStructuredSelection )selection ).getFirstElement();
+    }
+    if( getTreeViewer().getTree().getSelectionCount() == 1 ) {
+      TreeItem treeItem = getTreeViewer().getTree().getSelection()[ 0 ];
+      newIndex = getTreeViewer().getTree().indexOf( treeItem );
+    }
+    IOutlineSelectionChangedListener listener = ( IOutlineSelectionChangedListener )listenerMap.get( new Integer( tabIndex ) );
+    if( listener != null ) {
+      listener.outlineSelectionChanged( newIndex, item );
+    }
+    isSetSelectionEnabled = true;
+  }
+
+  /**
+   * Forces a SelectionChangedEvent, and so it calls a possibly registered
+   * listener.
+   */
+  public void forceSelectionChanged( final int newIndex ) {
+    if( getTreeViewer() != null ) {
+      isSetSelectionEnabled = false;
+      Object item = null;
+      ISelection selection = getTreeViewer().getSelection();
+      if( !selection.isEmpty() ) {
+        item = ( ( IStructuredSelection )selection ).getFirstElement();
+      }
+      IOutlineSelectionChangedListener listener = ( IOutlineSelectionChangedListener )listenerMap.get( new Integer( tabIndex ) );
+      if( listener != null ) {
+        listener.outlineSelectionChanged( newIndex, item );
+      }
+      isSetSelectionEnabled = true;
+    }
+  }
+
+  /**
+   * Sets a new input array for the Outline.
+   */
+  public void setInput( final Object[] input, final int tabIndex ) {
+    this.input = input;
+    this.tabIndex = tabIndex;
+    update();
+  }
+
+  /**
+   * Updates the Outline. Called after the Outline input has changed.
+   */
+  private void update() {
+    TreeViewer viewer = getTreeViewer();
+    if( viewer != null ) {
+      Control control = viewer.getControl();
+      if( control != null && !control.isDisposed() ) {
+        control.setRedraw( false );
+        viewer.setInput( input );
+        viewer.expandAll();
+        control.setRedraw( true );
+      }
+    }
+  }
+
+  /**
+   * Sets the selected item in the Outline programmatically to the given index.
+   */
+  public void setSelection( final int index ) {
+    if( isSetSelectionEnabled && getTreeViewer() != null ) {
+      Tree tree = getTreeViewer().getTree();
+      if( index >= 0 && index < tree.getItemCount() ) {
+        tree.setSelection( tree.getItem( index ) );
+        tree.showSelection();
+      } else {
+        tree.deselectAll();
+      }
+    }
+  }
+
+  /**
+   * Sets a listener that will be notified of the selected item in the Outline
+   * changes.
+   */
+  public void setSelectionChangedListener( final IOutlineSelectionChangedListener listener,
+                                           final int tabIndex )
+  {
+    listenerMap.put( new Integer( tabIndex ), listener );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/ContentOutlineLabelProvider.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/ContentOutlineLabelProvider.java
new file mode 100644
index 0000000..e2f8de9
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/ContentOutlineLabelProvider.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.rap.themeeditor.ThemeEditorPlugin;
+import org.eclipse.rwt.internal.theme.css.StyleRule;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * JFace LabelProvider for elements displayed in Outline view.
+ */
+public class ContentOutlineLabelProvider extends LabelProvider {
+
+  public String getText( final Object element ) {
+    String result;
+    if( element instanceof StyleRule ) {
+      result = ( ( StyleRule )element ).getName();
+    } else {
+      result = element == null
+                              ? ""
+                              : element.toString();
+    }
+    return result;
+  }
+
+  public Image getImage( final Object element ) {
+    return ThemeEditorPlugin.getDefault()
+      .getImage( ThemeEditorPlugin.IMG_PUBLIC );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/IOutlineSelectionChangedListener.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/IOutlineSelectionChangedListener.java
new file mode 100644
index 0000000..8087805
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/IOutlineSelectionChangedListener.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor;
+
+/**
+ * Listener interface for GUI parts that want to be notified if the Outline
+ * selection has been changed.
+ */
+public interface IOutlineSelectionChangedListener {
+
+  public void outlineSelectionChanged( final int newIndex, final Object item );
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/rule/DefaultContentProvider.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/rule/DefaultContentProvider.java
new file mode 100644
index 0000000..b2aa13c
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/rule/DefaultContentProvider.java
@@ -0,0 +1,39 @@
+package org.eclipse.rap.themeeditor.editor.rule;
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+
+
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+
+/**
+ * A basic JFace ContentProvider with no parent element and no children
+ * elements.
+ */
+public class DefaultContentProvider extends ArrayContentProvider
+  implements ITreeContentProvider
+{
+
+  public DefaultContentProvider() {
+  }
+
+  public Object[] getChildren( final Object parentElement ) {
+    return new Object[ 0 ];
+  }
+
+  public Object getParent( final Object element ) {
+    return null;
+  }
+
+  public boolean hasChildren( final Object element ) {
+    return false;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSCompletionProcessor.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSCompletionProcessor.java
new file mode 100644
index 0000000..01b215c
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSCompletionProcessor.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.CompletionProposal;
+import org.eclipse.jface.text.contentassist.ContextInformation;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.ThemeEditorPlugin;
+import org.eclipse.rap.themeeditor.editor.source.region.IRegionExt;
+import org.eclipse.rwt.internal.theme.ThemeDefElementWrapper;
+import org.eclipse.rwt.internal.theme.ThemeDefProperty;
+import org.eclipse.rwt.internal.theme.ThemeDefinitionProvider;
+
+public class CSSCompletionProcessor implements IContentAssistProcessor {
+
+  private CSSTokenScanner tokenScanner;
+
+  public CSSCompletionProcessor( final CSSTokenScanner tokenScanner ) {
+    this.tokenScanner = tokenScanner;
+  }
+
+  public ICompletionProposal[] computeCompletionProposals( final ITextViewer viewer,
+                                                           final int offset )
+  {
+    IRegionExt regionExt = tokenScanner.getRegionExt( offset );
+    String currentContent = regionExt.getContent();
+    if( offset >= regionExt.getOffset()
+        && offset <= ( regionExt.getOffset() + regionExt.getLength() )
+        && ( offset - regionExt.getOffset() ) <= currentContent.length() )
+    {
+      currentContent = currentContent.substring( 0, offset
+                                                    - regionExt.getOffset() );
+    } else {
+      return null;
+    }
+    String[] proposalStrings = SupportedKeywords.getKeywordsStartWith( currentContent.trim(),
+                                                                       regionExt.getKeywordType() );
+    if( proposalStrings == null ) {
+      return null;
+    }
+    proposalStrings = filterProposalStrings( proposalStrings, regionExt );
+    if( proposalStrings.length == 0 ) {
+      return null;
+    }
+    // just a hack to perform trimStart only, as whitespace at end must remain
+    currentContent = currentContent + '#';
+    int beginIndex = currentContent.trim().length() - 1;
+    ICompletionProposal[] result = new ICompletionProposal[ proposalStrings.length ];
+    for( int i = 0; i < proposalStrings.length; i++ ) {
+      String additionalInfo = ThemeDefinitionProvider.getDescription( regionExt,
+                                                                      proposalStrings[ i ] );
+      IContextInformation info = new ContextInformation( proposalStrings[ i ],
+                                                         proposalStrings[ i ] );
+      result[ i ] = new CompletionProposal( proposalStrings[ i ],
+                                            offset - beginIndex,
+                                            beginIndex,
+                                            proposalStrings[ i ].length(),
+                                            ThemeEditorPlugin.getDefault()
+                                              .getImage( ThemeEditorPlugin.IMG_FIELD_PRIVATE ),
+                                            proposalStrings[ i ],
+                                            info,
+                                            additionalInfo );
+    }
+    return result;
+  }
+
+  public IContextInformation[] computeContextInformation( final ITextViewer viewer,
+                                                          final int offset )
+  {
+    return null;
+  }
+
+  public char[] getCompletionProposalAutoActivationCharacters() {
+    return new char[]{
+      '.', ':', '['
+    };
+  }
+
+  public char[] getContextInformationAutoActivationCharacters() {
+    return null;
+  }
+
+  public IContextInformationValidator getContextInformationValidator() {
+    return null;
+  }
+
+  public String getErrorMessage() {
+    return null;
+  }
+
+  private String[] filterProposalStrings( final String[] proposalStrings,
+                                          final IRegionExt regionExt )
+  {
+    String[] result;
+    switch( regionExt.getKeywordType() ) {
+      case SupportedKeywords.STYLE_TYPE:
+        result = filterProposalStyles( proposalStrings, regionExt );
+      break;
+      case SupportedKeywords.STATE_TYPE:
+        result = filterProposalStates( proposalStrings, regionExt );
+      break;
+      case SupportedKeywords.PROPERTY_TYPE:
+        result = filterProposalProperties( proposalStrings, regionExt );
+      break;
+      default:
+        result = proposalStrings;
+      break;
+    }
+    return result;
+  }
+
+  private String[] filterProposalStyles( final String[] proposalStrings,
+                                         final IRegionExt regionExt )
+  {
+    String[] result = new String[ 0 ];
+    if( regionExt.getTokenType() == CSSTokenProvider.STYLE_TOKEN ) {
+      ThemeDefElementWrapper wrapper = ThemeDefinitionProvider.getElementWrapper( regionExt );
+      if( wrapper != null ) {
+        List resultList = new ArrayList();
+        for( int i = 0; i < proposalStrings.length; i++ ) {
+          String text = proposalStrings[ i ];
+          if( wrapper.element.styleMap.containsKey( text ) ) {
+            resultList.add( text );
+          }
+        }
+        result = new String[ resultList.size() ];
+        result = ( String[] )resultList.toArray( result );
+      }
+    }
+    return result;
+  }
+
+  private String[] filterProposalStates( final String[] proposalStrings,
+                                         final IRegionExt regionExt )
+  {
+    String[] result = new String[ 0 ];
+    if( regionExt.getTokenType() == CSSTokenProvider.STATE_TOKEN ) {
+      ThemeDefElementWrapper wrapper = ThemeDefinitionProvider.getElementWrapper( regionExt );
+      if( wrapper != null ) {
+        List resultList = new ArrayList();
+        for( int i = 0; i < proposalStrings.length; i++ ) {
+          String text = proposalStrings[ i ];
+          if( wrapper.element.stateMap.containsKey( text ) ) {
+            resultList.add( text );
+          }
+        }
+        result = new String[ resultList.size() ];
+        result = ( String[] )resultList.toArray( result );
+      }
+    }
+    return result;
+  }
+
+  private String[] filterProposalProperties( final String[] proposalStrings,
+                                             final IRegionExt regionExt )
+  {
+    String[] result = new String[ 0 ];
+    if( regionExt.getTokenType() == CSSTokenProvider.PROPERTY_TOKEN ) {
+      OutlineRegion[] regions = tokenScanner.getOutlineRegionsArray();
+      OutlineRegion outlineRegion = null;
+      for( int i = 0; i < regions.length; i++ ) {
+        OutlineRegion next = regions[ i ];
+        if( next.getOffset() <= regionExt.getOffset() ) {
+          outlineRegion = next;
+        }
+      }
+      if( outlineRegion != null ) {
+        List resultList = new ArrayList();
+        for( int i = 0; i < outlineRegion.getElements().length; i++ ) {
+          ThemeDefElementWrapper wrapper = ThemeDefinitionProvider.getElementWrapper( outlineRegion.getElements()[ i ] );
+          if( wrapper != null ) {
+            for( int j = 0; j < proposalStrings.length; j++ ) {
+              String text = proposalStrings[ j ];
+              for( int k = 0; k < wrapper.element.properties.length; k++ ) {
+                ThemeDefProperty property = wrapper.element.properties[ k ];
+                if( property.name.equals( text ) ) {
+                  if( !resultList.contains( text ) ) {
+                    resultList.add( text );
+                  }
+                }
+              }
+            }
+          }
+        }
+        result = new String[ resultList.size() ];
+        result = ( String[] )resultList.toArray( result );
+        Arrays.sort( result );
+      }
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDamagerRepairer.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDamagerRepairer.java
new file mode 100644
index 0000000..7690455
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDamagerRepairer.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
+import org.eclipse.jface.text.rules.ITokenScanner;
+
+public class CSSDamagerRepairer extends DefaultDamagerRepairer {
+
+  public CSSDamagerRepairer( final ITokenScanner scanner ) {
+    super( scanner );
+  }
+
+  public void createPresentation( final TextPresentation presentation,
+                                  final ITypedRegion region )
+  {
+    ITypedRegion newRegion = new TypedRegion( 0, fDocument.getLength(), null );
+    super.createPresentation( presentation, newRegion );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDocumentSetupParticipant.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDocumentSetupParticipant.java
new file mode 100644
index 0000000..650d7ca
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSDocumentSetupParticipant.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.rules.FastPartitioner;
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
+
+public class CSSDocumentSetupParticipant implements IDocumentSetupParticipant {
+
+  public CSSDocumentSetupParticipant() {
+  }
+
+  public void setup( final IDocument document ) {
+    if( document instanceof IDocumentExtension3 ) {
+      IDocumentExtension3 extension3 = ( IDocumentExtension3 )document;
+      IDocumentPartitioner partitioner = new FastPartitioner( new RuleBasedPartitionScanner(),
+                                                              new String[]{} );
+      extension3.setDocumentPartitioner( CSSSourceViewerConfiguration.CSS_PARTITIONING,
+                                         partitioner );
+      partitioner.connect( document );
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceEditor.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceEditor.java
new file mode 100644
index 0000000..920f0a7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceEditor.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.ResourceBundle;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.IVerticalRuler;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.rap.themeeditor.editor.IOutlineSelectionChangedListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.texteditor.ContentAssistAction;
+import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
+
+/**
+ * The general editor in the Source Tab. Providing content assists, syntax
+ * coloring and Outline contribution.
+ */
+public class CSSSourceEditor extends TextEditor
+  implements IOutlineSelectionChangedListener
+{
+
+  private CSSTokenScanner tokenScanner;
+
+  public CSSSourceEditor() {
+    super();
+//    getCSSTokenScanner().setTokenChangedListener( editor );
+//    outline.setSelectionChangedListener( this, ThemeEditor.SOURCE_TAB );
+  }
+
+  protected void initializeEditor() {
+    super.initializeEditor();
+    setSourceViewerConfiguration( new CSSSourceViewerConfiguration( getCSSTokenScanner() ) );
+  }
+
+  public void createPartControl( Composite parent ) {
+    super.createPartControl( parent );
+    final IDocument doc = getDocument();
+    // add listener and call setRange manually, because it isnt't called
+    // automatically from jface if backspace key is performed
+    doc.addDocumentListener( new IDocumentListener() {
+
+      public void documentAboutToBeChanged( DocumentEvent event ) {
+        ;
+      }
+
+      public void documentChanged( DocumentEvent event ) {
+        getCSSTokenScanner().setRange( doc, 0, doc.getLength() );
+      }
+    } );
+  }
+
+  protected ISourceViewer createSourceViewer( final Composite parent,
+                                              final IVerticalRuler ruler,
+                                              final int styles )
+  {
+    return super.createSourceViewer( parent, ruler, styles );
+  }
+
+  public Object[] getSelectorTokens() {
+    return getCSSTokenScanner().getOutlineRegionsArray();
+  }
+
+  /**
+   * Returns the index of a rule in the StyleSheet at a given offset position
+   * within the document.
+   */
+  private int getRuleNumber( final int offset ) {
+    int result = -1;
+    Iterator it = getCSSTokenScanner().getOutlineRegionsList().iterator();
+    while( it.hasNext() ) {
+      IRegion regionExt = ( IRegion )it.next();
+      if( regionExt.getOffset() <= offset ) {
+        result++;
+      }
+    }
+    if( result < 0 ) {
+      result = 0;
+    }
+    return result;
+  }
+
+  protected void handleCursorPositionChanged() {
+    super.handleCursorPositionChanged();
+    ISelection selection = getSelectionProvider().getSelection();
+    if( selection instanceof ITextSelection ) {
+      int offset = ( ( ITextSelection )selection ).getOffset();
+      int index = getRuleNumber( offset );
+//      editor.updateOutlineSelection( index );
+    }
+  }
+
+  public void doCursorPositionChanged() {
+    handleCursorPositionChanged();
+  }
+
+  private CSSTokenScanner getCSSTokenScanner() {
+    if( tokenScanner == null ) {
+      tokenScanner = new CSSTokenScanner( this );
+    }
+    return tokenScanner;
+  }
+
+  public void dispose() {
+    tokenScanner.setTokenChangedListener( null );
+    super.dispose();
+  }
+
+  protected void createActions() {
+    super.createActions();
+    IAction action = new ContentAssistAction( getResourceBundle(),
+                                              "ContentAssistProposal.",
+                                              this );
+    action.setActionDefinitionId( ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS );
+    final String actionId = "RAP_THEME_EDITOR_CONTENT_ASSIST_PROPOSALS";
+    setAction( actionId, action );
+    markAsStateDependentAction( actionId, true );
+  }
+
+  /**
+   * Notification from the Outline when the user changed its selection.
+   */
+  public void outlineSelectionChanged( int newIndex, Object item ) {
+    if( item == null ) {
+      resetHighlightRange();
+    } else {
+      if( item instanceof OutlineRegion ) {
+        OutlineRegion region = ( OutlineRegion )item;
+        int start = region.getOffset();
+        int length = region.getLength();
+        try {
+          String regionText = getDocument().get( start, length );
+          int i = 0;
+          while( i < regionText.length()
+                 && Character.isWhitespace( regionText.charAt( i ) ) )
+          {
+            i++;
+          }
+          setHighlightRange( start + i, length - i, true );
+        } catch( IllegalArgumentException exception ) {
+          resetHighlightRange();
+        } catch( BadLocationException exception ) {
+          resetHighlightRange();
+        }
+      } else {
+        resetHighlightRange();
+      }
+    }
+  }
+
+  public IDocument getDocument() {
+    return getDocumentProvider().getDocument( getEditorInput() );
+  }
+
+  private ResourceBundle getResourceBundle() {
+    return new ResourceBundle() {
+      
+      protected Object handleGetObject( String key ) {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      public Enumeration getKeys() {
+        // TODO Auto-generated method stub
+        return null;
+      }
+    };
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceViewerConfiguration.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceViewerConfiguration.java
new file mode 100644
index 0000000..10ef026
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSSourceViewerConfiguration.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.contentassist.ContentAssistant;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
+
+public class CSSSourceViewerConfiguration extends TextSourceViewerConfiguration
+{
+
+  public final static String CSS_PARTITIONING = "__css_partitioning";
+  private CSSTokenScanner tokenScanner;
+
+  public CSSSourceViewerConfiguration( final CSSTokenScanner tokenScanner ) {
+    super();
+    this.tokenScanner = tokenScanner;
+  }
+
+  public String getConfiguredDocumentPartitioning( final ISourceViewer sourceViewer )
+  {
+    return CSS_PARTITIONING;
+  }
+
+  public String[] getConfiguredContentTypes( final ISourceViewer sourceViewer )
+  {
+    return new String[]{
+      IDocument.DEFAULT_CONTENT_TYPE
+    };
+  }
+
+  public IPresentationReconciler getPresentationReconciler( final ISourceViewer sourceViewer )
+  {
+    PresentationReconciler result = new PresentationReconciler();
+    result.setDocumentPartitioning( getConfiguredDocumentPartitioning( sourceViewer ) );
+    CSSDamagerRepairer repairer = new CSSDamagerRepairer( tokenScanner );
+    result.setDamager( repairer, IDocument.DEFAULT_CONTENT_TYPE );
+    result.setRepairer( repairer, IDocument.DEFAULT_CONTENT_TYPE );
+    return result;
+  }
+
+  public ITextHover getTextHover( final ISourceViewer sourceViewer,
+                                  final String contentType )
+  {
+    return new CSSTextHover( tokenScanner );
+  }
+
+  public IContentAssistant getContentAssistant( final ISourceViewer sourceViewer )
+  {
+    ContentAssistant assistant = new ContentAssistant();
+    assistant.setContentAssistProcessor( new CSSCompletionProcessor( tokenScanner ),
+                                         IDocument.DEFAULT_CONTENT_TYPE );
+//    assistant.setContentAssistProcessor( new JavaDocCompletionProcessor(),
+//                                         JavaPartitionScanner.JAVA_DOC );
+    assistant.enableAutoActivation( true );
+    assistant.setAutoActivationDelay( 500 );
+    assistant.setProposalPopupOrientation( IContentAssistant.PROPOSAL_OVERLAY );
+    assistant.setContextInformationPopupOrientation( IContentAssistant.CONTEXT_INFO_ABOVE );
+//    assistant.setContextInformationPopupBackground(JavaEditorEnvironment.getJavaColorProvider().getColor(new RGB(150, 150, 0)));
+    assistant.setInformationControlCreator( new IInformationControlCreator() {
+
+      public IInformationControl createInformationControl( Shell parent ) {
+        return new DefaultInformationControl( parent );
+      }
+      
+    });
+    return assistant;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTextHover.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTextHover.java
new file mode 100644
index 0000000..4a4c777
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTextHover.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+import org.eclipse.rap.themeeditor.editor.source.region.IRegionExt;
+import org.eclipse.swt.graphics.Point;
+
+public class CSSTextHover implements ITextHover {
+
+  private CSSTokenScanner tokenScanner;
+
+  public CSSTextHover( final CSSTokenScanner tokenScanner ) {
+    this.tokenScanner = tokenScanner;
+  }
+
+  public String getHoverInfo( final ITextViewer textViewer,
+                              final IRegion hoverRegion )
+  {
+    String result = "";
+    if( hoverRegion != null ) {
+      int offset = hoverRegion.getOffset();
+      IRegionExt regionExt = tokenScanner.getRegionExt( offset );
+      // TODO [rst] Implement pluggable descriptions to avoid tight coupling
+//      result = ThemeDefinitionProvider.getDescription( regionExt,
+//                                                       regionExt.getContent()
+//                                                         .trim() );
+    }
+    return result;
+  }
+
+  public IRegion getHoverRegion( final ITextViewer textViewer, final int offset )
+  {
+    IRegion result;
+    Point selection = textViewer.getSelectedRange();
+    if( selection.x <= offset && offset < selection.x + selection.y ) {
+      result = new Region( selection.x, selection.y );
+    } else {
+      result = new Region( offset, 0 );
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenProvider.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenProvider.java
new file mode 100644
index 0000000..96d1270
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenProvider.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.rap.themeeditor.ThemeEditorPlugin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * Holds information about the appearance (color and style) of all token types.
+ * This will be used for syntax coloring in source editor.
+ */
+public class CSSTokenProvider {
+
+  public static final int PROPERTY_TOKEN = 1;
+  public static final int SELECTOR_TOKEN = 2;
+  public static final int STYLE_TOKEN = 3;
+  public static final int STATE_TOKEN = 4;
+  public static final int VARIANT_TOKEN = 5;
+  public static final int STRING_TOKEN = 6;
+  public static final int COMMENT_TOKEN = 7;
+  public static final int DEFAULT_TOKEN = 8;
+  private Map tokenMap;
+
+  public CSSTokenProvider() {
+    tokenMap = new HashMap();
+    tokenMap.put( new Integer( PROPERTY_TOKEN ),
+                  new CSSTokenStyle( new RGB( 127, 0, 85 ), SWT.BOLD ) );
+    tokenMap.put( new Integer( SELECTOR_TOKEN ),
+                  new CSSTokenStyle( new RGB( 0, 0, 0 ), SWT.BOLD ) );
+    tokenMap.put( new Integer( STYLE_TOKEN ),
+                  new CSSTokenStyle( new RGB( 42, 0, 255 ), SWT.NORMAL ) );
+    tokenMap.put( new Integer( STATE_TOKEN ),
+                  new CSSTokenStyle( new RGB( 42, 0, 255 ), SWT.ITALIC ) );
+    tokenMap.put( new Integer( VARIANT_TOKEN ),
+                  new CSSTokenStyle( new RGB( 255, 0, 0 ), SWT.NORMAL ) );
+    tokenMap.put( new Integer( STRING_TOKEN ),
+                  new CSSTokenStyle( new RGB( 42, 0, 255 ), SWT.NORMAL ) );
+    tokenMap.put( new Integer( COMMENT_TOKEN ),
+                  new CSSTokenStyle( new RGB( 63, 127, 95 ), SWT.NORMAL ) );
+    tokenMap.put( new Integer( DEFAULT_TOKEN ),
+                  new CSSTokenStyle( new RGB( 0, 0, 0 ), SWT.NORMAL ) );
+  }
+
+  /**
+   * Returns a new token that can be used to style a found region appropriately
+   * to its type.
+   */
+  public IToken createToken( final int type ) {
+    CSSTokenStyle tokenStyle = ( CSSTokenStyle )tokenMap.get( new Integer( type ) );
+    if( tokenStyle == null ) {
+      tokenStyle = ( CSSTokenStyle )tokenMap.get( new Integer( DEFAULT_TOKEN ) );
+    }
+    return new Token( new TextAttribute( ThemeEditorPlugin.getDefault()
+      .getColorRegistry()
+      .getColor( tokenStyle.rgb ), null, tokenStyle.style ) );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenScanner.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenScanner.java
new file mode 100644
index 0000000..7bb294f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenScanner.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.ITokenScanner;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.rap.themeeditor.editor.source.region.IHasParentRegion;
+import org.eclipse.rap.themeeditor.editor.source.region.IRegionExt;
+import org.eclipse.rap.themeeditor.editor.source.region.SelectorRegion;
+
+/**
+ * Scans a document and splits its content into tokens, which are implementors
+ * of IRegionExt.
+ */
+public class CSSTokenScanner implements ITokenScanner {
+
+  private CSSTokenProvider provider;
+  private CSSSourceEditor editor;
+  private List outlineRegions;
+  private List tokenList;
+  private IRegionExt lastState;
+  private int currentListPosition;
+  private int lastListPosition;
+  private ITokenChangedListener listener = null;
+  private String oldContent;
+  private int oldListPosition;
+  private SelectorRegion lastSelector;
+
+  public CSSTokenScanner( final CSSSourceEditor editor ) {
+    this.editor = editor;
+    provider = new CSSTokenProvider();
+    tokenList = new ArrayList();
+    outlineRegions = new ArrayList();
+  }
+
+  public int getTokenLength() {
+    return lastState.getLength();
+  }
+
+  public int getTokenOffset() {
+    return lastState.getOffset();
+  }
+
+  public IToken nextToken() {
+    currentListPosition++;
+    if( currentListPosition >= tokenList.size()
+        || ( currentListPosition > lastListPosition ) )
+    {
+      return Token.EOF;
+    }
+    lastState = ( IRegionExt )tokenList.get( currentListPosition );
+    IToken result = provider.createToken( lastState.getTokenType() );
+    return result;
+  }
+
+  public void setRange( final IDocument document,
+                        final int offset,
+                        final int length )
+  {
+    if( document.get().equals( oldContent ) ) {
+      currentListPosition = oldListPosition;
+    } else {
+      oldContent = document.get();
+      tokenList.clear();
+      currentListPosition = -1;
+      lastListPosition = -1;
+      IRegionExt currentState = new SelectorRegion( -1 );
+      lastSelector = ( SelectorRegion )currentState;
+      for( int i = 0; i < document.getLength(); i++ ) {
+        try {
+          char character = document.getChar( i );
+          IRegionExt newState = currentState.getNextState( character );
+          if( newState != currentState ) {
+            tokenList.add( currentState );
+            if( currentState instanceof SelectorRegion ) {
+              SelectorRegion sr = ( SelectorRegion )currentState;
+              if( sr.getContent().trim().length() > 0 ) {
+                lastSelector = ( SelectorRegion )currentState;
+              }
+            } else if( currentState instanceof IHasParentRegion ) {
+              ( ( IHasParentRegion )currentState ).setParentRegion( lastSelector );
+            }
+            if( ( currentState.getOffset() + currentState.getLength() ) < offset )
+            {
+              currentListPosition++;
+            }
+            if( currentState.getOffset() < ( offset + length ) ) {
+              lastListPosition++;
+            }
+            currentState = newState;
+          }
+        } catch( BadLocationException e ) {
+          e.printStackTrace();
+        }
+      }
+      tokenList.add( currentState );
+      if( currentState instanceof SelectorRegion ) {
+        lastSelector = ( SelectorRegion )currentState;
+      } else if( currentState instanceof IHasParentRegion ) {
+        ( ( IHasParentRegion )currentState ).setParentRegion( lastSelector );
+      }
+      if( ( currentState.getOffset() + currentState.getLength() ) < offset ) {
+        currentListPosition++;
+      }
+      if( currentState.getOffset() < ( offset + length ) ) {
+        lastListPosition++;
+      }
+      oldListPosition = currentListPosition;
+      // notify token changed listener if one is registered
+      if( listener != null ) {
+        listener.tokensChanged();
+      }
+    }
+  }
+
+  /**
+   * Returns the token at a given offset position in the document.
+   */
+  public IRegionExt getRegionExt( final int offset ) {
+    IRegionExt result = null;
+    Iterator it = tokenList.iterator();
+    while( it.hasNext() ) {
+      IRegionExt regionExt = ( IRegionExt )it.next();
+      if( regionExt.getOffset() <= offset ) {
+        result = regionExt;
+      }
+    }
+    return result;
+  }
+
+  public List getTokenList() {
+    return tokenList;
+  }
+
+  public void setTokenChangedListener( final ITokenChangedListener listener ) {
+    this.listener = listener;
+  }
+
+  /**
+   * Returns the array of Outline regions that are directly used as the input
+   * items for the Outline.
+   */
+  public synchronized OutlineRegion[] getOutlineRegionsArray() {
+    outlineRegions.clear();
+    boolean bufferEnabled = false;
+    int offset = 0;
+    List outlineElements = new ArrayList();
+    Iterator it = getTokenList().iterator();
+    while( it.hasNext() ) {
+      IRegionExt regionExt = ( IRegionExt )it.next();
+      if( regionExt.getTokenType() == CSSTokenProvider.SELECTOR_TOKEN ) {
+        outlineElements.add( regionExt );
+      }
+      if( regionExt.getTokenType() == CSSTokenProvider.SELECTOR_TOKEN
+          && !bufferEnabled )
+      {
+        String temp = "";
+        try {
+          temp = editor.getDocument().get( regionExt.getOffset(),
+                                           regionExt.getLength() ).trim();
+        } catch( BadLocationException e ) {
+          temp = "";
+        }
+        if( temp.length() > 0 ) {
+          offset = regionExt.getOffset();
+          bufferEnabled = true;
+        }
+      } else if( regionExt.getTokenType() == CSSTokenProvider.PROPERTY_TOKEN
+                 && bufferEnabled )
+      {
+        bufferEnabled = false;
+        int length = regionExt.getOffset() - offset - 1;
+        String content = "";
+        try {
+          content = editor.getDocument().get( offset, length ).trim();
+        } catch( BadLocationException e ) {
+          e.printStackTrace();
+        }
+        if( content.length() > 0 ) {
+          // ignore comments and white spaces
+          content = content.replaceAll( "/\\*.*\\*/", " " );
+          content = content.replaceAll( "\\s+", " " );
+          OutlineRegion outlineRegion = new OutlineRegion( offset,
+                                                           length,
+                                                           content,
+                                                           outlineElements );
+          outlineRegions.add( outlineRegion );
+          outlineElements = new ArrayList();
+        }
+      }
+    }
+    OutlineRegion[] result = new OutlineRegion[ outlineRegions.size() ];
+    return ( org.eclipse.rap.themeeditor.editor.source.OutlineRegion[] )outlineRegions.toArray( result );
+  }
+
+  public List getOutlineRegionsList() {
+    return outlineRegions;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenStyle.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenStyle.java
new file mode 100644
index 0000000..1c06e10
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/CSSTokenStyle.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import org.eclipse.swt.graphics.RGB;
+
+public class CSSTokenStyle {
+
+  public RGB rgb;
+  public int style;
+
+  public CSSTokenStyle( final RGB rgb, final int style ) {
+    this.rgb = rgb;
+    this.style = style;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/DocumentUpdateTimer.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/DocumentUpdateTimer.java
new file mode 100644
index 0000000..443316d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/DocumentUpdateTimer.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+/**
+ * Timer that is used to buffer all occurring document change events. A document
+ * change event is only forwarded to the editor after a certain delay after the
+ * last occurring event. This will save the editor from updating Outline on
+ * every received key event.
+ */
+public class DocumentUpdateTimer implements Runnable {
+
+  private Thread thread;
+  private Object mutex = new Object();
+  private boolean isReset = false;
+  private boolean isStopped = false;
+  private IDocumentUpdateListener listener;
+  private int updateDelay = 1000;
+
+  public DocumentUpdateTimer( IDocumentUpdateListener listener ) {
+    this.listener = listener;
+  }
+
+  protected void start() {
+    thread = new Thread( this, "DocumentUpdateTimer.update_delay" );
+    thread.start();
+  }
+
+  public void run() {
+    isStopped = false;
+    try {
+      while( true ) {
+        synchronized( mutex ) {
+          if( updateDelay != 0 ) {
+            mutex.wait( updateDelay );
+          }
+          if( isReset ) {
+            isReset = false;
+            continue;
+          }
+        }
+        if( listener != null && !isStopped ) {
+          listener.updateDocument();
+        }
+        break;
+      }
+    } catch( InterruptedException exception ) {
+      exception.printStackTrace();
+    }
+    thread = null;
+  }
+
+  protected void reset() {
+    synchronized( mutex ) {
+      isReset = true;
+      mutex.notifyAll();
+    }
+  }
+
+  public void stop() {
+    synchronized( mutex ) {
+      // Thread threadToStop = thread;
+      // if( threadToStop != null && threadToStop.isAlive() ) {
+      // threadToStop.interrupt();
+      // }
+      isStopped = true;
+    }
+  }
+
+  public void documentChanged() {
+    if( thread != null && thread.isAlive() ) {
+      reset();
+    } else {
+      start();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/IDocumentUpdateListener.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/IDocumentUpdateListener.java
new file mode 100644
index 0000000..c364790
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/IDocumentUpdateListener.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+/**
+ * Listener that is notified when the time has elapsed after a document change
+ * event. So now the document change becomes visible to the editor.
+ */
+public interface IDocumentUpdateListener {
+
+  public void updateDocument();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/ITokenChangedListener.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/ITokenChangedListener.java
new file mode 100644
index 0000000..a337a91
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/ITokenChangedListener.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+/**
+ * Listener interface that is called whenever the token scanner was requested to
+ * split the document again into tokens.
+ */
+public interface ITokenChangedListener {
+
+  public void tokensChanged();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/OutlineRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/OutlineRegion.java
new file mode 100644
index 0000000..fedbec2
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/OutlineRegion.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.rap.themeeditor.editor.source.region.SelectorRegion;
+
+/**
+ * Special type of region for Outline items. It's basically the region in the
+ * text containing the whole selector list of a rule.
+ */
+public class OutlineRegion implements IRegion {
+
+  private int offset;
+  private int length;
+  private String content;
+  private List elements;
+
+  public OutlineRegion( final int offset,
+                        final int length,
+                        final String content,
+                        final List elements )
+  {
+    this.offset = offset;
+    this.length = length;
+    this.content = content;
+    this.elements = elements;
+  }
+
+  public int getLength() {
+    return length;
+  }
+
+  public int getOffset() {
+    return offset;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  public String toString() {
+    return content == null
+                          ? ""
+                          : content;
+  }
+
+  /**
+   * Returns the array of selectors this outline region consists of. Namely the
+   * selector list of a rule.
+   */
+  public SelectorRegion[] getElements() {
+    List resultList = new ArrayList();
+    Iterator it = elements.iterator();
+    while( it.hasNext() ) {
+      Object object = it.next();
+      if( object instanceof SelectorRegion ) {
+        resultList.add( ( SelectorRegion )object );
+      }
+    }
+    SelectorRegion[] result = new SelectorRegion[ resultList.size() ];
+    return ( SelectorRegion[] )resultList.toArray( result );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/AbstractRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/AbstractRegion.java
new file mode 100644
index 0000000..9cb9797
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/AbstractRegion.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+/**
+ * Super class of all supported region types.
+ */
+public abstract class AbstractRegion implements IRegionExt {
+
+  protected int offset;
+  protected int length;
+  protected char lastCharacter;
+
+  public AbstractRegion( final int offset ) {
+    this.offset = offset + 1;
+    this.length = 0;
+  }
+
+  public int getOffset() {
+    return offset;
+  }
+
+  public int getLength() {
+    return length - 1;
+  }
+
+  public String getContent() {
+    return "";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/CommentRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/CommentRegion.java
new file mode 100644
index 0000000..560c1a1
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/CommentRegion.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a comment section.
+ */
+public class CommentRegion extends AbstractRegion {
+
+  private IRegionExt lastState;
+
+  public CommentRegion( final int offset, final IRegionExt lastState ) {
+    super( offset - 1 );
+    length = 1;
+    this.lastState = lastState;
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    if( character == '/' && lastCharacter == '*' ) {
+      length++;
+      result = lastState.getCopy( offset + length );
+      length++;
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.COMMENT_TOKEN;
+  }
+
+  /* never called */
+  public IRegionExt getCopy( final int offset ) {
+    return null;
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.UNDEFINED;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IHasParentRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IHasParentRegion.java
new file mode 100644
index 0000000..4bea107
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IHasParentRegion.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+/**
+ * Interface implemented by Region Classes that provide access to a parent
+ * region. This parent region is basically the selector, which a style, state or
+ * variant belongs to.
+ */
+public interface IHasParentRegion {
+
+  public void setParentRegion( final SelectorRegion region );
+
+  public SelectorRegion getParentRegion();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IRegionExt.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IRegionExt.java
new file mode 100644
index 0000000..9c06ed7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/IRegionExt.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * An IRegionExt is a logic unit within the theme file, so that the editor can
+ * provide syntax color or content assists based on the structure of the regions
+ * of the theme file.
+ */
+public interface IRegionExt extends IRegion {
+
+  /**
+   * Returns the type of a region. Each region implementation has its own type.
+   */
+  public int getTokenType();
+
+  /**
+   * Returns the keyword type. These are the ones supported by RAP. So this type
+   * can be Selector, Property, Style, State, or default otherwise.
+   */
+  public int getKeywordType();
+
+  /**
+   * Returns the next state, namely the next instance of a region. Therefore an
+   * implementation has to evaluate the given character and decide if it shall
+   * switch the state (so instantiate an new region and return it) or if keeps
+   * the current state (so just return itself).
+   */
+  public IRegionExt getNextState( final char character );
+
+  /**
+   * Returns a new instance of a region that has got the same type as the
+   * current one. Used to continue with the same region type as before after a
+   * comment region has finished.
+   */
+  public IRegionExt getCopy( final int offset );
+
+  /**
+   * Returns a String containing all characters that were given to evaluate.
+   * This means the content is the part of text in the theme file from position
+   * offset until position offset+length.
+   */
+  public String getContent();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/PropertyRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/PropertyRegion.java
new file mode 100644
index 0000000..6ee7465
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/PropertyRegion.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a property inside a rule.
+ */
+public class PropertyRegion extends AbstractRegion {
+
+  private StringBuffer buffer;
+
+  public PropertyRegion( final int offset ) {
+    super( offset );
+    buffer = new StringBuffer();
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case ':':
+        result = new ValueRegion( offset + length );
+      break;
+      case '}':
+        result = new SelectorRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+          buffer.setCharAt( buffer.length() - 1, ' ' );
+        } else {
+          buffer.append( character );
+        }
+      break;
+      default:
+        buffer.append( character );
+      break;
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.PROPERTY_TOKEN;
+  }
+
+  public IRegionExt getCopy( final int offset ) {
+    return new PropertyRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.PROPERTY_TYPE;
+  }
+
+  public String getContent() {
+    return buffer.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/SelectorRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/SelectorRegion.java
new file mode 100644
index 0000000..7dc3f4b
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/SelectorRegion.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a selector.
+ */
+public class SelectorRegion extends AbstractRegion {
+
+  private StringBuffer buffer;
+
+  public SelectorRegion( final int offset ) {
+    super( offset );
+    buffer = new StringBuffer();
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case ',':
+      case ';':
+      case ' ':
+        result = new SelectorRegion( offset + length );
+        length++;
+      break;
+      case '[':
+        result = new StyleRegion( offset + length );
+      break;
+      case ':':
+        result = new StateRegion( offset + length );
+      break;
+      case '.':
+        result = new VariantRegion( offset + length );
+      break;
+      case '{':
+        result = new PropertyRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+          buffer.setCharAt( buffer.length() - 1, ' ' );
+        } else {
+          buffer.append( character );
+        }
+      break;
+      default:
+        buffer.append( character );
+      break;
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.SELECTOR_TOKEN;
+  }
+
+  public IRegionExt getCopy( final int offset ) {
+    return new SelectorRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.SELECTOR_TYPE;
+  }
+
+  public String getContent() {
+    return buffer.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StateRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StateRegion.java
new file mode 100644
index 0000000..3e9c96f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StateRegion.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a state belonging to a selector.
+ */
+public class StateRegion extends AbstractRegion implements IHasParentRegion {
+
+  private StringBuffer buffer;
+  private SelectorRegion parent;
+
+  public StateRegion( final int offset ) {
+    super( offset );
+    buffer = new StringBuffer();
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case '[':
+        result = new StyleRegion( offset + length );
+      break;
+      case ';':
+        result = new SelectorRegion( offset + length );
+      break;
+      case ',':
+        result = new SelectorRegion( offset + length );
+      break;
+      case '.':
+        result = new VariantRegion( offset + length );
+      break;
+      case ':':
+        result = new StateRegion( offset + length );
+        length++;
+      break;
+      case '{':
+        result = new PropertyRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+          buffer.setCharAt( buffer.length() - 1, ' ' );
+        } else {
+          buffer.append( character );
+        }
+      break;
+      default:
+        buffer.append( character );
+      break;
+    }
+    if( Character.isWhitespace( character ) ) {
+      result = new SelectorRegion( offset + length );
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.STATE_TOKEN;
+  }
+
+  public IRegionExt getCopy( final int offset ) {
+    return new StateRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.STATE_TYPE;
+  }
+
+  public String getContent() {
+    return buffer.toString();
+  }
+
+  public SelectorRegion getParentRegion() {
+    return parent;
+  }
+
+  public void setParentRegion( final SelectorRegion region ) {
+    parent = region;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StringRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StringRegion.java
new file mode 100644
index 0000000..8e1f752
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StringRegion.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a string value.
+ */
+public class StringRegion extends AbstractRegion {
+
+  private char quoteCharacter;
+  private IRegionExt lastState;
+
+  public StringRegion( final int offset,
+                       final char quoteCharacter,
+                       final IRegionExt lastState )
+  {
+    super( offset );
+    this.quoteCharacter = quoteCharacter;
+    this.lastState = lastState;
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    if( quoteCharacter == character ) {
+      result = lastState.getCopy( offset + length );
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.STRING_TOKEN;
+  }
+
+  /* never called */
+  public IRegionExt getCopy( final int offset ) {
+    return null;
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.UNDEFINED;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StyleRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StyleRegion.java
new file mode 100644
index 0000000..1766ea5
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/StyleRegion.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a style belonging to a selector.
+ */
+public class StyleRegion extends AbstractRegion implements IHasParentRegion {
+
+  private StringBuffer buffer;
+  private SelectorRegion parent;
+
+  public StyleRegion( final int offset ) {
+    super( offset );
+    buffer = new StringBuffer();
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case ';':
+        result = new SelectorRegion( offset + length );
+      break;
+      case ',':
+        result = new SelectorRegion( offset + length );
+      break;
+      case ']':
+        result = new SelectorRegion( offset + length );
+      break;
+      case '.':
+        result = new VariantRegion( offset + length );
+      break;
+      case ':':
+        result = new StateRegion( offset + length );
+      break;
+      case '{':
+        result = new PropertyRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+          buffer.setCharAt( buffer.length() - 1, ' ' );
+        } else {
+          buffer.append( character );
+        }
+      break;
+      default:
+        buffer.append( character );
+      break;
+    }
+    if( Character.isWhitespace( character ) ) {
+      result = new SelectorRegion( offset + length );
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.STYLE_TOKEN;
+  }
+
+  public IRegionExt getCopy( final int offset ) {
+    return new StyleRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.STYLE_TYPE;
+  }
+
+  public String getContent() {
+    return buffer.toString();
+  }
+
+  public SelectorRegion getParentRegion() {
+    return parent;
+  }
+
+  public void setParentRegion( final SelectorRegion region ) {
+    parent = region;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/ValueRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/ValueRegion.java
new file mode 100644
index 0000000..0b860ab
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/ValueRegion.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing the value after a property.
+ */
+public class ValueRegion extends AbstractRegion {
+
+  public ValueRegion( final int offset ) {
+    super( offset );
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case ';':
+        result = new PropertyRegion( offset + length );
+      break;
+      case '}':
+        result = new SelectorRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+        }
+      break;
+      default:
+      break;
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.DEFAULT_TOKEN;
+  }
+
+  public IRegionExt getCopy( final int offset ) {
+    return new ValueRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.UNDEFINED;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/VariantRegion.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/VariantRegion.java
new file mode 100644
index 0000000..fd62857
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rap/themeeditor/editor/source/region/VariantRegion.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.themeeditor.editor.source.region;
+
+import org.eclipse.rap.themeeditor.SupportedKeywords;
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+
+/**
+ * A region in the text file representing a variant belonging to a selector.
+ */
+public class VariantRegion extends AbstractRegion {
+
+  public VariantRegion( final int offset ) {
+    super( offset );
+  }
+
+  public IRegionExt getNextState( final char character ) {
+    IRegionExt result = this;
+    switch( character ) {
+      case '[':
+        result = new StyleRegion( offset + length );
+      break;
+      case ';':
+        result = new SelectorRegion( offset + length );
+      break;
+      case ',':
+        result = new SelectorRegion( offset + length );
+      break;
+      case ':':
+        result = new StateRegion( offset + length );
+      break;
+      case '.':
+        result = new VariantRegion( offset + length );
+        length++;
+      break;
+      case '{':
+        result = new PropertyRegion( offset + length );
+      break;
+      case '"':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '\'':
+        result = new StringRegion( offset + length, character, this );
+      break;
+      case '*':
+        if( lastCharacter == '/' ) {
+          result = new CommentRegion( offset + length - 1, this );
+          length--;
+        }
+      break;
+      default:
+      break;
+    }
+    if( Character.isWhitespace( character ) ) {
+      result = new SelectorRegion( offset + length );
+    }
+    lastCharacter = character;
+    length++;
+    return result;
+  }
+
+  public int getTokenType() {
+    return CSSTokenProvider.VARIANT_TOKEN;
+  }
+
+  public IRegionExt getCopy( int offset ) {
+    return new VariantRegion( offset );
+  }
+
+  public int getKeywordType() {
+    return SupportedKeywords.UNDEFINED;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/NewThemeDefinitionReader.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/NewThemeDefinitionReader.java
new file mode 100644
index 0000000..aaacead
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/NewThemeDefinitionReader.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.*;
+
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+/**
+ * Reader for theme definition files. These are the "*.theme.xml" files that
+ * define themeable properties of a certain widget.
+ */
+public class NewThemeDefinitionReader {
+
+  private static final String[] EMPTY_STRING_ARRAY = new String[ 0 ];
+  public static interface ThemeDefHandler {
+
+    public abstract void readThemeProperty( ThemeProperty def );
+  }
+  private static final String NODE_ROOT = "theme";
+  private static final String ATTR_NAME = "name";
+  private static final String ATTR_DESCRIPTION = "description";
+  private static final String THEME_DEF_SCHEMA = "themedef.xsd";
+  private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+  private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
+  private final InputStream inputStream;
+  private final String fileName;
+
+  /**
+   * An instance of this class reads theme definitions from an XML resource.
+   * 
+   * @param inputStream input stream from a theme definition XML
+   */
+  public NewThemeDefinitionReader( final InputStream inputStream,
+                                   final String fileName )
+  {
+    this.fileName = fileName;
+    if( inputStream == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    this.inputStream = inputStream;
+  }
+
+  public ThemeDefElement[] read() throws SAXException, IOException {
+    Document document;
+    document = parseThemeDefinition( inputStream );
+    Node root = document.getElementsByTagName( NODE_ROOT ).item( 0 );
+    NodeList childNodes = root.getChildNodes();
+    List elementList = new ArrayList();
+    for( int i = 0; i < childNodes.getLength(); i++ ) {
+      Node node = childNodes.item( i );
+      if( node.getNodeType() == Node.ELEMENT_NODE ) {
+        if( "element".equals( node.getNodeName() ) ) {
+          ThemeDefElement element = readThemeDefElement( node );
+          elementList.add( element );
+        }
+      }
+    }
+    ThemeDefElement[] result = new ThemeDefElement[ elementList.size() ];
+    elementList.toArray( result );
+    return result;
+  }
+
+  private ThemeDefElement readThemeDefElement( final Node node ) {
+    String name = getAttributeValue( node, ATTR_NAME );
+    String description = getAttributeValue( node, ATTR_DESCRIPTION );
+    NodeList childNodes = node.getChildNodes();
+    List propertyList = new ArrayList();
+    Map styleMap = new HashMap();
+    Map stateMap = new HashMap();
+    for( int i = 0; i < childNodes.getLength(); i++ ) {
+      Node childNode = childNodes.item( i );
+      if( childNode.getNodeType() == Node.ELEMENT_NODE ) {
+        if( "property".equals( childNode.getNodeName() ) ) {
+          ThemeDefProperty property = readThemeDefProperty( childNode );
+          propertyList.add( property );
+        } else if( "style".equals( childNode.getNodeName() ) ) {
+          styleMap.put( getAttributeValue( childNode, ATTR_NAME ),
+                        getAttributeValue( childNode, ATTR_DESCRIPTION ) );
+        } else if( "state".equals( childNode.getNodeName() ) ) {
+          stateMap.put( getAttributeValue( childNode, ATTR_NAME ),
+                        getAttributeValue( childNode, ATTR_DESCRIPTION ) );
+        }
+      }
+    }
+    ThemeDefProperty[] properties = new ThemeDefProperty[ propertyList.size() ];
+    propertyList.toArray( properties );
+    return new ThemeDefElement( name,
+                                description,
+                                properties,
+                                styleMap,
+                                stateMap );
+  }
+
+  private ThemeDefProperty readThemeDefProperty( final Node node ) {
+    String name = getAttributeValue( node, ATTR_NAME );
+    String description = getAttributeValue( node, ATTR_DESCRIPTION );
+    String stylesStr = getAttributeValue( node, "styles" );
+    String[] styles;
+    if( stylesStr != null ) {
+      styles = stylesStr.split( "\\s+" );
+    } else {
+      styles = EMPTY_STRING_ARRAY;
+    }
+    String statesStr = getAttributeValue( node, "states" );
+    String[] states = null;
+    if( statesStr != null ) {
+      states = statesStr.split( "\\s+" );
+    } else {
+      states = EMPTY_STRING_ARRAY;
+    }
+    return new ThemeDefProperty( name, description, styles, states );
+  }
+
+  private static String getAttributeValue( final Node node, final String name )
+  {
+    String result = null;
+    NamedNodeMap attributes = node.getAttributes();
+    if( attributes != null ) {
+      Node namedItem = attributes.getNamedItem( name );
+      if( namedItem != null ) {
+        result = namedItem.getNodeValue();
+      }
+    }
+    return result;
+  }
+
+  private Document parseThemeDefinition( final InputStream is )
+    throws SAXException, IOException
+  {
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+    factory.setNamespaceAware( true );
+    ClassLoader loader = NewThemeDefinitionReader.class.getClassLoader();
+    final URL schema = loader.getResource( THEME_DEF_SCHEMA );
+    factory.setValidating( schema != null );
+    try {
+      factory.setAttribute( JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA );
+    } catch( final IllegalArgumentException iae ) {
+      // XML-Processing does not support JAXP 1.2 or greater
+      factory.setNamespaceAware( false );
+      factory.setValidating( false );
+    }
+    DocumentBuilder builder;
+    try {
+      builder = factory.newDocumentBuilder();
+    } catch( final ParserConfigurationException e ) {
+      String message = "Failed to initialize parser for theme definition files";
+      throw new RuntimeException( message, e );
+    }
+    // builder.setEntityResolver( new EntityResolver() {
+    // public InputSource resolveEntity( final String publicID,
+    // final String systemID )
+    // throws IOException, SAXException
+    // {
+    // InputSource result = null;
+    // if( schema != null && systemID.endsWith( THEME_DEF_SCHEMA ) ) {
+    // URLConnection connection = schema.openConnection();
+    // connection.setUseCaches( false );
+    // result = new InputSource( connection.getInputStream() );
+    // }
+    // return result;
+    // }
+    // } );
+    builder.setErrorHandler( new ThemeDefinitionErrorHandler() );
+    return builder.parse( is );
+  }
+  // TODO: Logging instead of sysout
+  private class ThemeDefinitionErrorHandler implements ErrorHandler {
+
+    public void error( final SAXParseException spe ) throws SAXException {
+      System.err.println( "Error parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+
+    public void fatalError( final SAXParseException spe ) throws SAXException {
+      System.err.println( "Fatal error parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+
+    public void warning( final SAXParseException spe ) throws SAXException {
+      System.err.println( "Warning parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+
+    private String getPosition( final SAXParseException spe ) {
+      return "in file '"
+             + fileName
+             + "' at line "
+             + spe.getLineNumber()
+             + ", col "
+             + spe.getColumnNumber();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoolean.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoolean.java
new file mode 100644
index 0000000..efe9527
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoolean.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.text.MessageFormat;
+
+
+public class QxBoolean implements QxType {
+
+  public static final QxBoolean TRUE = new QxBoolean( true );
+
+  public static final QxBoolean FALSE = new QxBoolean( false );
+
+  private static final String[] VALID_TRUE_STRINGS = new String[] {
+    "true", "yes", "on"
+  };
+
+  private static final String[] VALID_FALSE_STRINGS = new String[] {
+    "false", "no", "off"
+  };
+
+  public final boolean value;
+
+  private QxBoolean( final boolean value ) {
+    this.value = value;
+  }
+
+  public static QxBoolean valueOf( final String input ) {
+    return evalInput( input ) ? TRUE : FALSE;
+  }
+
+  public String toDefaultString() {
+    return value ? VALID_TRUE_STRINGS[ 0 ] : VALID_FALSE_STRINGS[ 0 ];
+  }
+
+  public String toString () {
+    return "QxBoolean{ "
+           + String.valueOf( value )
+           + " }";
+  }
+
+  private static boolean evalInput( final String input ) {
+    boolean result = false;
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    boolean found = false;
+    for( int i = 0; i < VALID_TRUE_STRINGS.length && !found; i++ ) {
+      if( VALID_TRUE_STRINGS[ i ].equals( input ) ) {
+        result = true;
+        found = true;
+      }
+    }
+    for( int i = 0; i < VALID_FALSE_STRINGS.length && !found; i++ ) {
+      if( VALID_FALSE_STRINGS[ i ].equals( input ) ) {
+        found = true;
+      }
+    }
+    if( !found ) {
+      String mesg = "Illegal boolean value: ''{0}''";
+      Object[] arguments = new Object[] { input };
+      String message = MessageFormat.format( mesg, arguments );
+      throw new IllegalArgumentException( message );
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBorder.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBorder.java
new file mode 100644
index 0000000..c4d6d53
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBorder.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+public class QxBorder implements QxType {
+
+  public static final QxBorder NONE = new QxBorder( 0, null, null );
+
+  public static final String[] VALID_STYLES = new String[] {
+    "none",
+    "hidden",
+    "dotted",
+    "dashed",
+    "solid",
+    "double",
+    "groove",
+    "ridge",
+    "inset",
+    "outset"
+  };
+
+  // TODO [rst] Implement properties for left, right, etc.
+
+  private static final String DARKSHADOW_LIGHTSHADOW
+    = getBorderColors( "widget.darkshadow", "widget.lightshadow" );
+
+  private static final String LIGHTSHADOW_DARKSHADOW
+    = getBorderColors( "widget.lightshadow", "widget.darkshadow" );
+
+  private static final String SHADOW_HIGHLIGHT
+    = getBorderColors( "widget.shadow", "widget.highlight" );
+
+  private static final String HIGHLIGHT_SHADOW
+    = getBorderColors( "widget.highlight", "widget.shadow" );
+
+  public final int width;
+
+  public final String style;
+
+  // TODO [rst] Color is either a valid color string or a named color from the
+  //            color theme. Check for valid colors.
+  public final String color;
+
+  private QxBorder( final int width, final String style, final String color ) {
+    this.width = width;
+    this.style = style;
+    this.color = color;
+  }
+
+  public static QxBorder create( final int width,
+                                 final String style,
+                                 final String color )
+  {
+    QxBorder result;
+    if( width == 0 || "none".equals( style ) || "hidden".equals( style ) ) {
+      result = NONE;
+    } else {
+      result = new QxBorder( width, style == null ? "solid" : style, color );
+    }
+    return result;
+  }
+
+  public static QxBorder valueOf( final String input ) {
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    String[] parts = input.split( "\\s+" );
+    if( input.trim().length() == 0 ) {
+      throw new IllegalArgumentException( "Empty border definition" );
+    }
+    if( parts.length > 3 ) {
+      throw new IllegalArgumentException( "Illegal number of arguments for border" );
+    }
+    int width = -1;
+    String style = null;
+    String color = null;
+    for( int i = 0; i < parts.length; i++ ) {
+      String part = parts[ i ];
+      boolean consumed = "".equals( part );
+      // parse width
+      if( !consumed && width == -1 ) {
+        Integer parsedWidth = QxDimension.parseLength( part );
+        if( parsedWidth != null ) {
+          if( parsedWidth.intValue() < 0 ) {
+            throw new IllegalArgumentException( "Negative width: " + part );
+          }
+          width = parsedWidth.intValue();
+          consumed = true;
+        }
+      }
+      // parse style
+      if( !consumed && style == null ) {
+        String parsedStyle = parseStyle( part );
+        if( parsedStyle != null ) {
+          style = parsedStyle;
+          consumed = true;
+        }
+      }
+      // parse color
+      if( !consumed && color == null ) {
+        color = part;
+        consumed = true;
+      }
+      if( !consumed ) {
+        throw new IllegalArgumentException( "Illegal parameter for color: "
+                                            + part );
+      }
+    }
+    if( width == -1 ) {
+      width = 1;
+    }
+    return QxBorder.create( width, style, color );
+  }
+
+  public String getQxStyle() {
+    String result = style;
+    if( color == null ) {
+      if( ( "outset".equals( style ) || "inset".equals( style ) )
+          && ( width == 1 || width == 2 ) )
+      {
+        result = "solid";
+      } else if( ( "ridge".equals( style ) || "groove".equals( style ) )
+                 && width == 2 )
+      {
+        result = "solid";
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Returns the colors to pass to qx for use default widget colors for 3d
+   * borders. When a 3d border style is used and no color has been set, this
+   * method returns an array of the default widget colors for the style.
+   */
+  public String getQxColors() {
+    String result = null;
+    if( color == null && width == 2 ) {
+      if( "outset".equals( style ) ) {
+        result = LIGHTSHADOW_DARKSHADOW;
+      } else if( "inset".equals( style ) ) {
+        result = SHADOW_HIGHLIGHT;
+      } else if( "ridge".equals( style ) ) {
+        result = HIGHLIGHT_SHADOW;
+      } else if( "groove".equals( style ) ) {
+        result = SHADOW_HIGHLIGHT;
+      }
+    } else if( color == null && width == 1 ) {
+      if( "outset".equals( style ) ) {
+        result = HIGHLIGHT_SHADOW;
+      } else if( "inset".equals( style ) ) {
+        result = SHADOW_HIGHLIGHT;
+      }
+    }
+    if( result == null ) {
+      result = color == null ? null : "\"" + color + "\"";
+    }
+    return result;
+  }
+
+  /**
+   * Returns the inner colors to pass to qx for use default widget colors for 3d
+   * borders. When a 3d border style is used and no color has been set, this
+   * method returns an array of the default widget colors for the style.
+   */
+  public String getQxInnerColors() {
+    String result = null;
+    if( color == null && width == 2 ) {
+      if( "outset".equals( style ) ) {
+        result = HIGHLIGHT_SHADOW;
+      } else if( "inset".equals( style ) ) {
+        result = DARKSHADOW_LIGHTSHADOW;
+      } else if( "ridge".equals( style ) ) {
+        result = SHADOW_HIGHLIGHT;
+      } else if( "groove".equals( style ) ) {
+        result = HIGHLIGHT_SHADOW;
+      }
+    }
+    return result;
+  }
+
+  public String toDefaultString() {
+    StringBuffer result = new StringBuffer();
+    if( width == 0 ) {
+      result.append( "none" );
+    } else {
+      result.append( width + "px" );
+      result.append( " " );
+      result.append( style );
+      if( color != null ) {
+        result.append( " " );
+        result.append( color );
+      }
+    }
+    return result.toString();
+  }
+
+  public boolean equals( final Object object ) {
+    // TODO [rst] Adapt this method as soon as properties for left, right, etc. exist
+    boolean result = false;
+    if( object == this ) {
+      result = true;
+    } else if( object instanceof QxBorder ) {
+      QxBorder other = ( QxBorder )object;
+      result = other.width == this.width
+               && ( style == null
+                    ? other.style == null
+                    : style.equals( other.style ) )
+               && ( color == null
+                    ? other.color == null
+                    : color.equals( other.color ) );
+    }
+    return result;
+  }
+
+  public int hashCode() {
+    // TODO [rst] Adapt this method as soon as properties for left, right, etc.
+    //            exist
+    int result = 23;
+    result += 37 * result + width;
+    if( style != null ) {
+      result += 37 * result + style.hashCode();
+    }
+    if( color != null ) {
+      result += 37 * result + color.hashCode();
+    }
+    return result;
+  }
+
+  public String toString() {
+    // TODO [rst] Adapt this method as soon as properties for left, right, etc.
+    //            exist
+    return "QxBorder{ " + width + ", " + style + ", " + color + " }";
+  }
+
+  private static String getBorderColors( final String color1, final String color2 ) {
+    StringBuffer result = new StringBuffer();
+    result.append( "[ \"");
+    result.append( color1 );
+    result.append( "\", \"");
+    result.append( color2 );
+    result.append( "\", \"");
+    result.append( color2 );
+    result.append( "\", \"");
+    result.append( color1 );
+    result.append( "\" ]");
+    return result.toString();
+  }
+
+  private static String parseStyle( final String part ) {
+    String result = null;
+    for( int j = 0; j < VALID_STYLES.length && result == null; j++ ) {
+      if( VALID_STYLES[ j ].equalsIgnoreCase( part ) ) {
+        result = VALID_STYLES[ j ];
+      }
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoxDimensions.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoxDimensions.java
new file mode 100644
index 0000000..dbc38d9
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxBoxDimensions.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+public class QxBoxDimensions implements QxType {
+
+  public static final QxBoxDimensions ZERO = new QxBoxDimensions( 0, 0, 0, 0 );
+
+  public final int top;
+
+  public final int right;
+
+  public final int bottom;
+
+  public final int left;
+
+  private QxBoxDimensions( final int top,
+                           final int right,
+                           final int bottom,
+                           final int left )
+  {
+    this.top = top;
+    this.right = right;
+    this.bottom = bottom;
+    this.left = left;
+  }
+
+  public static QxBoxDimensions create( final int top,
+                                        final int right,
+                                        final int bottom,
+                                        final int left )
+  {
+    QxBoxDimensions result;
+    if( top == 0 && right == 0 && bottom == 0 && left == 0 ) {
+      result = ZERO;
+    } else {
+      result = new QxBoxDimensions( top, right, bottom, left );
+    }
+    return result;
+  }
+
+  public static QxBoxDimensions valueOf( final String input ) {
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    String[] parts = input.split( "\\s+" );
+    if( parts.length == 0 || parts.length > 4 ) {
+      String msg = "Illegal number of arguments for box dimensions";
+      throw new IllegalArgumentException( msg );
+    }
+    int top, right, left, bottom;
+    top = right = bottom = left = parsePxValue( parts[ 0 ] );
+    if( parts.length >= 2 ) {
+      right = left = parsePxValue( parts[ 1 ] );
+    }
+    if( parts.length >= 3 ) {
+      bottom = parsePxValue( parts[ 2 ] );
+    }
+    if( parts.length == 4 ) {
+      left = parsePxValue( parts[ 3 ] );
+    }
+    return create( top, right, bottom, left );
+  }
+
+  /**
+   * Returns <code>left + right</code> for convenience.
+   */
+  public int getWidth() {
+    return left + right;
+  }
+
+  /**
+   * Returns <code>top + bottom</code> for convenience.
+   */
+  public int getHeight() {
+    return top + bottom;
+  }
+
+  public String toJsArray() {
+    return "[ " + top + ", " + right + ", " + bottom + ", " + left + " ]";
+  }
+
+  public String toDefaultString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.append( top + "px" );
+    if( right != top || bottom != top || left != top ) {
+      buffer.append( " " + right + "px" );
+    }
+    if( bottom != top || left != right ) {
+      buffer.append( " " + bottom + "px" );
+    }
+    if( left != right ) {
+      buffer.append( " " + left + "px" );
+    }
+    return buffer.toString();
+  }
+
+  public boolean equals( final Object object ) {
+    boolean result = false;
+    if( object == this ) {
+      result = true;
+    } else if( object instanceof QxBoxDimensions ) {
+      QxBoxDimensions other = (QxBoxDimensions)object;
+      result = ( other.top == this.top )
+               && ( other.right == this.right )
+               && ( other.bottom == this.bottom )
+               && ( other.left == this.left );
+    }
+    return result;
+}
+
+  public int hashCode () {
+    int result = 911;
+    result += 23 * result + top;
+    result += 23 * result + right;
+    result += 23 * result + bottom;
+    result += 23 * result + left;
+    return result;
+  }
+
+  public String toString () {
+    return "QxBoxDimensions{ "
+           + top
+           + ", "
+           + right
+           + ", "
+           + bottom
+           + ", "
+           + left
+           + " }";
+  }
+
+  private static int parsePxValue( final String part ) {
+    Integer result = QxDimension.parseLength( part );
+    if( result == null ) {
+      throw new IllegalArgumentException( "Illegal parameter: " + part );
+    }
+    return result.intValue();
+  }
+
+  public static Rectangle createRectangle( final QxBoxDimensions boxdim ) {
+    return new Rectangle( boxdim.left,
+                          boxdim.top,
+                          boxdim.left + boxdim.right,
+                          boxdim.top + boxdim.bottom );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxColor.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxColor.java
new file mode 100644
index 0000000..2866522
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxColor.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+public class QxColor implements QxType {
+
+  private static final String TRANSPARENT_STR = "transparent";
+
+  private static final Map NAMED_COLORS = new HashMap();
+
+  public static final QxColor BLACK = new QxColor( 0, 0, 0 );
+
+  public static final QxColor WHITE = new QxColor( 255, 255, 255 );
+
+  public static final QxColor TRANSPARENT = new QxColor();
+
+  public final int red;
+
+  public final int green;
+
+  public final int blue;
+
+  public final boolean transparent;
+
+  static {
+    // register 16 standard HTML colors
+    NAMED_COLORS.put( "black", new int[] { 0, 0, 0 } );
+    NAMED_COLORS.put( "gray", new int[] { 128, 128, 128 } );
+    NAMED_COLORS.put( "silver", new int[] { 192, 192, 192 } );
+    NAMED_COLORS.put( "white", new int[] { 255, 255, 255 } );
+    NAMED_COLORS.put( "maroon", new int[] { 128, 0, 0 } );
+    NAMED_COLORS.put( "red", new int[] { 255, 0, 0 } );
+    NAMED_COLORS.put( "purple", new int[] { 128, 0, 128 } );
+    NAMED_COLORS.put( "fuchsia", new int[] { 255, 0, 255 } );
+    NAMED_COLORS.put( "green", new int[] { 0, 128, 0 } );
+    NAMED_COLORS.put( "lime", new int[] { 0, 255, 0 } );
+    NAMED_COLORS.put( "navy", new int[] { 0, 0, 128 } );
+    NAMED_COLORS.put( "blue", new int[] { 0, 0, 255 } );
+    NAMED_COLORS.put( "olive", new int[] { 128, 128, 0 } );
+    NAMED_COLORS.put( "yellow", new int[] { 255, 255, 0 } );
+    NAMED_COLORS.put( "teal", new int[] { 0, 128, 128 } );
+    NAMED_COLORS.put( "aqua", new int[] { 0, 255, 255 } );
+  }
+
+  private QxColor() {
+    this.red = 0;
+    this.green = 0;
+    this.blue = 0;
+    this.transparent = true;
+  }
+
+  private QxColor( final int red, final int green, final int blue ) {
+    this.red = red;
+    this.green = green;
+    this.blue = blue;
+    this.transparent = false;
+  }
+
+  public static QxColor create( final int red, final int green, final int blue )
+  {
+    QxColor result;
+    if( red == 0 && green == 0 && blue == 0 ) {
+      result = BLACK;
+    } else if( red == 255 && green == 255 && blue == 255 ) {
+      result = WHITE;
+    } else {
+      result = new QxColor( red, green, blue );
+    }
+    return result;
+  }
+
+  public static QxColor valueOf( final String input ) {
+    QxColor result;
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    if( TRANSPARENT_STR.equals( input ) ) {
+      result = TRANSPARENT;
+    } else {
+      int red, green, blue;
+      if( input.startsWith( "#" ) ) {
+        try {
+          if( input.length() == 7 ) {
+            red = Integer.parseInt( input.substring( 1, 3 ), 16 );
+            green = Integer.parseInt( input.substring( 3, 5 ), 16 );
+            blue = Integer.parseInt( input.substring( 5, 7 ), 16 );
+          } else if( input.length() == 4 ) {
+            red = Integer.parseInt( input.substring( 1, 2 ), 16 ) * 17;
+            green = Integer.parseInt( input.substring( 2, 3 ), 16 ) * 17;
+            blue = Integer.parseInt( input.substring( 3, 4 ), 16 ) * 17;
+          } else {
+            String pattern = "Illegal number of characters in color definition ''{0}''";
+            Object[] arguments = new Object[] { input };
+            String message = MessageFormat.format( pattern, arguments );
+            throw new IllegalArgumentException( message );
+          }
+        } catch( final NumberFormatException e ) {
+          String pattern = "Illegal number format in color definition ''{0}''";
+          Object[] arguments = new Object[] { input };
+          String message = MessageFormat.format( pattern, arguments );
+          throw new IllegalArgumentException( message );
+        }
+      } else if( NAMED_COLORS.containsKey( input.toLowerCase() ) ) {
+        int[] values = ( int[] )NAMED_COLORS.get( input.toLowerCase() );
+        red = values[ 0 ];
+        green = values[ 1 ];
+        blue = values[ 2 ];
+      } else {
+        String[] parts = input.split( "\\s*,\\s*" );
+        if( parts.length == 3 ) {
+          try {
+            red = Integer.parseInt( parts[ 0 ] );
+            green = Integer.parseInt( parts[ 1 ] );
+            blue = Integer.parseInt( parts[ 2 ] );
+          } catch( final NumberFormatException e ) {
+            String pattern = "Illegal number format in color definition ''{0}''";
+            Object[] arguments = new Object[] { input };
+            String message = MessageFormat.format( pattern, arguments );
+            throw new IllegalArgumentException( message );
+          }
+        } else {
+          String pattern = "Invalid color name ''{0}''";
+          Object[] arguments = new Object[] { input };
+          String message = MessageFormat.format( pattern, arguments );
+          throw new IllegalArgumentException( message );
+        }
+      }
+      result = create( red, green, blue );
+    }
+    return result;
+  }
+
+  public String toDefaultString() {
+    return transparent ? TRANSPARENT_STR : toHtmlString( red, green, blue );
+  }
+
+  public boolean equals( final Object obj ) {
+    boolean result = false;
+    if( obj == this ) {
+      result = true;
+    } else if( obj instanceof QxColor ) {
+      QxColor other = ( QxColor )obj;
+      result =  other.red == red
+             && other.green == green
+             && other.blue == blue;
+    }
+    return result;
+  }
+
+  public int hashCode() {
+    return transparent ? -1 : red + green * 256 + blue * 65536;
+  }
+
+  public String toString() {
+    String colors = red + ", " + green + ", " + blue;
+    return "QxColor{ " + ( transparent ? TRANSPARENT_STR : colors ) + " }";
+  }
+
+  public static String toHtmlString( final int red,
+                                     final int green,
+                                     final int blue )
+  {
+    StringBuffer sb = new StringBuffer();
+    sb.append( "#" );
+    sb.append( getHexStr( red ) );
+    sb.append( getHexStr( green ) );
+    sb.append( getHexStr( blue ) );
+    return sb.toString();
+  }
+
+  private static String getHexStr( final int value ) {
+    String hex = Integer.toHexString( value );
+    return hex.length() == 1 ? "0" + hex : hex;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxDimension.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxDimension.java
new file mode 100644
index 0000000..58283bd
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxDimension.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class QxDimension implements QxType {
+
+  public static final QxDimension ZERO = new QxDimension( 0 );
+
+  private static final Pattern LENGTH_PATTERN
+    = Pattern.compile( "((\\+|-)?\\d+)(em|ex|px|pt|pc|in|cm|mm|%)?" );
+
+  public final int value;
+
+  private QxDimension( final int value ) {
+    this.value = value;
+  }
+
+  public static QxDimension create( final int value ) {
+    QxDimension result;
+    if( value == 0 ) {
+      result = ZERO;
+    } else {
+      result = new QxDimension( value );
+    }
+    return result;
+  }
+
+  public static QxDimension valueOf( final String input ) {
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    Integer parsed = parseLength( input );
+    if( parsed == null ) {
+      throw new IllegalArgumentException( "Illegal dimension parameter: " + input );
+    }
+    return create( parsed.intValue() );
+  }
+
+  public String toDefaultString() {
+    return value + "px";
+  }
+
+  public boolean equals( final Object object ) {
+    boolean result = false;
+    if( object == this ) {
+      result = true;
+    } else if( object instanceof QxDimension ) {
+      QxDimension other = (QxDimension)object;
+      result = ( other.value == this.value );
+    }
+    return result;
+  }
+
+  public int hashCode () {
+    return value * 47;
+  }
+
+  public String toString () {
+    return "QxDimension{ "
+           + value
+           + " }";
+  }
+
+  /**
+   * Tries to interpret a string as length parameter.
+   *
+   * @return the parsed length as integer, or <code>null</code> if the string
+   *         could not be parsed.
+   * @throws IllegalArgumentException if the string is valid CSS length
+   *             parameter that is a percentage value or has an unsupported
+   *             unit.
+   */
+  static Integer parseLength( final String input ) {
+    // TODO [rst] Also catch values with fractional digits
+    Integer result = null;
+    Matcher matcher = LENGTH_PATTERN.matcher( input );
+    if( matcher.matches() ) {
+      result = Integer.valueOf( matcher.group( 1 ) );
+      String unit = matcher.group( 3 );
+      if( unit != null && "%".equals( unit ) ) {
+        throw new IllegalArgumentException( "Percentages not supported: " + input );
+      }
+      if( unit != null && !"px".equals( unit ) ) {
+        throw new IllegalArgumentException( "Unit not supported: " + input );
+      }
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxFont.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxFont.java
new file mode 100644
index 0000000..9a410f2
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxFont.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class QxFont implements QxType {
+
+  private static final Pattern FONT_DEF_PATTERN
+    = Pattern.compile( "(\".+?\"|'.+?'|\\S[^\\s,]+)(\\s*,)?" );
+
+  public final String[] family;
+
+  public final int size;
+
+  public final boolean bold;
+
+  public final boolean italic;
+
+  private String familyAsString;
+
+  private QxFont( final String[] family,
+                  final int size,
+                  final boolean bold,
+                  final boolean italic )
+  {
+    this.family = family;
+    this.size = size;
+    this.bold = bold;
+    this.italic = italic;
+  }
+
+  public static QxFont create( final String[] families,
+                               final int size,
+                               final boolean bold,
+                               final boolean italic )
+  {
+    if( size < 0 ) {
+      throw new IllegalArgumentException( "Negative width: " + size );
+    }
+    return new QxFont( families, size, bold, italic );
+  }
+
+  public static QxFont valueOf( final String input ) {
+    if( input == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    if( input.trim().length() == 0 ) {
+      throw new IllegalArgumentException( "Empty font definition" );
+    }
+    List family = new ArrayList();
+    int size = 0;
+    boolean bold = false;
+    boolean italic = false;
+
+    Matcher matcher = FONT_DEF_PATTERN.matcher( input );
+    while( matcher.find() ) {
+      String part = matcher.group( 1 );
+      char c = part.charAt( 0 );
+      if( c == '"' || c == '\'' ) {
+        part = part.substring( 1, part.length() - 1 );
+      }
+//      boolean hasComma = matcher.group( 2 ) != null;
+      if( "bold".equalsIgnoreCase( part ) ) {
+        bold = true;
+      } else if( "italic".equalsIgnoreCase( part ) ) {
+        italic = true;
+      } else {
+        Integer parsedSize = QxDimension.parseLength( part );
+        if( parsedSize != null ) {
+          size = parsedSize.intValue();
+        } else {
+          // TODO [rst] Check commas
+          family.add( part );
+        }
+      }
+    }
+    // TODO [rst] Check for illegal input and throw exception
+    String[] familyArr = ( String[] )family.toArray( new String[ family.size() ] );
+    return new QxFont( familyArr, size, bold, italic ) ;
+  }
+
+  public String getFamilyAsString() {
+    if( familyAsString == null ) {
+      StringBuffer buffer = new StringBuffer();
+      for( int i = 0; i < family.length; i++ ) {
+        if( i > 0 ) {
+          buffer.append( ", " );
+        }
+        boolean hasSpace = family[ i ].indexOf( ' ' ) != -1;
+        if( hasSpace ) {
+          buffer.append( "\"" );
+        }
+        buffer.append( family[ i ] );
+        if( hasSpace ) {
+          buffer.append( "\"" );
+        }
+      }
+      familyAsString = buffer.toString();
+    }
+    return familyAsString;
+  }
+
+  public String toDefaultString() {
+    StringBuffer result = new StringBuffer();
+    if( bold ) {
+      result.append( "bold " );
+    }
+    if( italic ) {
+      result.append( "italic " );
+    }
+    result.append( size );
+    result.append( "px " );
+    result.append( getFamilyAsString() );
+    return result.toString();
+  }
+
+  public boolean equals( final Object obj ) {
+    boolean result = false;
+    if( obj == this ) {
+      result = true;
+    } else if( obj instanceof QxFont ) {
+      QxFont other = ( QxFont )obj;
+      result =  Arrays.equals( other.family, family )
+             && other.size == size
+             && other.bold == bold
+             && other.italic == italic;
+    }
+    return result;
+  }
+
+  public int hashCode() {
+    int result = 23;
+    for( int i = 0; i < family.length; i++ ) {
+      result += 37 * result + family[ i ].hashCode();
+    }
+    result += 37 * result + size;
+    result += bold ? 0 : 37 * result + 41;
+    result += italic ? 0 : 37 * result + 43;
+    return result;
+  }
+
+  public String toString() {
+    StringBuffer result = new StringBuffer();
+    result.append( "QxFont{ " );
+    if( bold ) {
+      result.append( "bold " );
+    }
+    if( italic ) {
+      result.append( "italic " );
+    }
+    result.append( size );
+    result.append( "px " );
+    result.append( getFamilyAsString() );
+    result.append( " }" );
+    return result.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxImage.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxImage.java
new file mode 100644
index 0000000..ed11700
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxImage.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+public final class QxImage implements QxType {
+
+  private static final String NONE_INPUT = "none";
+
+  public static final QxImage NONE = new QxImage( true, null, null );
+
+  public final boolean none;
+
+  public final String path;
+
+  public final ResourceLoader loader;
+
+  /**
+   * Creates a new image from the given value.
+   *
+   * @param path the definition string to create the image from. Either
+   *            <code>none</code> or a path to an image
+   * @param loader a resource loader which is able to load the image from the
+   *            given path
+   */
+  private QxImage( final boolean none,
+                   final String path,
+                   final ResourceLoader loader )
+  {
+    this.none = none;
+    this.path = path;
+    this.loader = loader;
+  }
+
+  public static QxImage valueOf( final String input, final ResourceLoader loader )
+  {
+    QxImage result;
+    if( NONE_INPUT.equals( input ) ) {
+      result = NONE;
+    } else {
+      if( input == null || loader == null ) {
+        throw new NullPointerException( "null argument" );
+      }
+      if( input.length() == 0 ) {
+        throw new IllegalArgumentException( "Empty image path" );
+      }
+      result = new QxImage( false, input, loader );
+    }
+    return result;
+  }
+
+  public String toDefaultString() {
+    // returns an empty string, because the default resource path is only valid
+    // for the bundle that specified it
+    return none ? NONE_INPUT : "";
+  }
+
+  public boolean equals( final Object object ) {
+    boolean result = false;
+    if( object == this ) {
+      result = true;
+    } else if( object instanceof QxImage ) {
+      QxImage other = ( QxImage )object;
+      result = path != null
+               && path.equals( other.path )
+               && loader != null
+               && loader.equals( other.loader );
+    }
+    return result;
+  }
+
+  public int hashCode() {
+    return path.hashCode();
+  }
+
+  public String toString() {
+    return "QxImage{ "
+           + ( none ? NONE_INPUT : path )
+           + " }";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxTheme.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxTheme.java
new file mode 100644
index 0000000..b8f9c8b
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxTheme.java
@@ -0,0 +1,319 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+
+/**
+ * Instances of this class represent a qooxdoo theme of a certain type. Used to
+ * assemble the Javascript code for qooxdoo themes.
+ */
+public class QxTheme {
+
+  private final String id;
+
+  private final String title;
+
+  private final int type;
+
+  private final String base;
+
+  private final StringBuffer code;
+
+  private boolean headWritten;
+
+  private boolean tailWritten;
+
+  private boolean valueWritten;
+
+  /** Type for qooxdoo meta themes */
+  public static final int META = 1;
+
+  /** Type for qooxdoo font themes */
+  public static final int FONT = 2;
+
+  /** Type for qooxdoo color themes */
+  public static final int COLOR = 3;
+
+  /** Type for qooxdoo border themes */
+  public static final int BORDER = 4;
+
+  /** Type for qooxdoo icon themes */
+  public static final int ICON = 5;
+
+  /** Type for qooxdoo widget themes */
+  public static final int WIDGET = 6;
+
+  /** Type for qooxdoo apearance themes */
+  public static final int APPEARANCE = 7;
+
+  /**
+   * Creates a new qooxdoo theme with the given, id, name, and type.
+   *
+   * @param id the fully qualified qx class name for the theme
+   * @param title the name of the theme
+   * @param type the type of the theme
+   */
+  public QxTheme( final String id, final String title, final int type ) {
+    this( id, title, type, null );
+  }
+
+  /**
+   * Creates a new qooxdoo theme with the given, id, name, type, and base class.
+   *
+   * @param id the fully qualified qx class name for the theme
+   * @param title the name of the theme
+   * @param type the type of the theme
+   * @param base the fully qualified name of the qx theme to extend
+   */
+  public QxTheme( final String id,
+                  final String title,
+                  final int type,
+                  final String base )
+  {
+    this.id = id;
+    this.title = title;
+    this.type = checkType( type );
+    this.base = base;
+    this.code = new StringBuffer();
+    headWritten = false;
+    tailWritten = false;
+    valueWritten = false;
+  }
+
+  /**
+   * Appends a number of key-value pairs to the generated theme. The given
+   * Javascript code is appended as is, without any checks being performed.
+   *
+   * @param values Javascript code that adds the additional values
+   */
+  public void appendValues( final String values ) {
+    beforeWriteValue();
+    code.append( values );
+    afterWriteValue();
+  }
+
+  /**
+   * Appends a key-value pair to the generated font theme. Only applicable for
+   * instances with type FONT.
+   *
+   * @param key the key to append
+   * @param font the value for the key
+   */
+  public void appendFont( final String key, final QxFont font ) {
+    beforeWriteValue();
+    code.append( "    \"" + key + "\" : { " );
+    code.append( "family: [" );
+    for( int i = 0; i < font.family.length; i++ ) {
+      if( i > 0 ) {
+        code.append( " ," );
+      }
+      code.append( "\"" );
+      code.append( font.family[ i ] );
+      code.append( "\"" );
+    }
+    code.append( "]" );
+    code.append( ", size: " );
+    code.append( font.size );
+    if( font.bold ) {
+      code.append( ", bold: true" );
+    }
+    if( font.italic ) {
+      code.append( ", italic: true" );
+    }
+    code.append( " }" );
+    afterWriteValue();
+  }
+
+  /**
+   * Appends a key-value pair to the generated color theme. Only applicable for
+   * instances with type COLOR.
+   *
+   * @param key the key to append
+   * @param color the value for the key
+   */
+  public void appendColor( final String key, final QxColor color ) {
+    beforeWriteValue();
+    code.append( "    \"" + key + "\" : " );
+    code.append( "[ " );
+    code.append( color.red );
+    code.append( ", " );
+    code.append( color.green );
+    code.append( ", " );
+    code.append( color.blue );
+    code.append( " ]" );
+    afterWriteValue();
+  }
+
+  /**
+   * Appends a key-value pair to the generated border theme. Only applicable for
+   * instances with type BORDER.
+   *
+   * @param key the key to append
+   * @param border the value for the key
+   */
+  public void appendBorder( final String key, final QxBorder border ) {
+    beforeWriteValue();
+    code.append( "    \"" + key + "\" : " );
+    // none
+    code.append( "{ width : " );
+    code.append( border.width );
+    String style = border.getQxStyle();
+    if( style != null && !"solid".equals( style ) ) {
+      code.append( ", style : \"" );
+      code.append( style );
+      code.append( "\"" );
+    }
+    String colors = border.getQxColors();
+    if( colors != null ) {
+      code.append( ", color : " );
+      code.append( colors );
+    }
+    String innerColor = border.getQxInnerColors();
+    if( innerColor != null ) {
+      code.append( ", innerColor : " );
+      code.append( innerColor );
+    }
+    code.append( " }" );
+    afterWriteValue();
+  }
+
+  /**
+   * Appends the single uri entry to the generated widget or icon theme. Only
+   * applicable for instances with type WIDGET or ICON.
+   *
+   * @param pathPrefix the prefix to map "widget/" or "icon/" to
+   */
+  public void appendUri( final String pathPrefix ) {
+    beforeWriteValue();
+    code.append( "    \"uri\" : \"" );
+    code.append( pathPrefix );
+    code.append( "\"" );
+    afterWriteValue();
+  }
+
+  /**
+   * Appends a key-value pair to the generated theme. Only applicable for
+   * META theme writers.
+   *
+   * @param key the key to append
+   * @param theme the value for the key
+   */
+  public void appendTheme( final String key, final String theme ) {
+    beforeWriteValue();
+    code.append( "    \"" + key + "\" : " );
+    code.append( theme );
+    afterWriteValue();
+  }
+
+  /**
+   * Returns the Javascript code that represents this theme. Once this method
+   * has been called, no values can be appended anymore.
+   *
+   * @return the generated theme code.
+   */
+  public String getJsCode() {
+    if( !headWritten ) {
+      writeHead();
+    }
+    if( !tailWritten ) {
+      writeTail();
+    }
+    return code.toString();
+  }
+
+  private void beforeWriteValue() {
+    if( !headWritten ) {
+      writeHead();
+    }
+    if( tailWritten ) {
+      throw new IllegalStateException( "Tail already written" );
+    }
+    if( valueWritten ) {
+      code.append( ",\n" );
+    }
+  }
+
+  private void afterWriteValue() {
+    valueWritten = true;
+  }
+
+  private void writeHead() {
+    code.append( "/* RAP theme file generated by QxTheme. */\n" );
+    code.append( "qx.Theme.define( \"" + id + getNameSuffix() + "\",\n" );
+    code.append( "{\n" );
+    code.append( "  title : \"" + title + "\",\n" );
+    if( base != null ) {
+      code.append( "  extend : " + base + ",\n" );
+    }
+    code.append( "  " + getThemeKey() + " : {\n" );
+    headWritten = true;
+  }
+
+  private void writeTail() {
+    code.append( "\n" );
+    code.append( "  }\n" );
+    code.append( "} );\n" );
+    tailWritten = true;
+  }
+
+  private int checkType( final int type ) {
+    if( type != META
+        && type != FONT
+        && type != COLOR
+        && type != BORDER
+        && type != ICON
+        && type != WIDGET
+        && type != APPEARANCE )
+    {
+      throw new IllegalArgumentException( "illegal type" );
+    }
+    return type;
+  }
+
+  private String getNameSuffix() {
+    String result = "";
+    if( type == FONT ) {
+      result = "Fonts";
+    } else if( type == COLOR ) {
+      result = "Colors";
+    } else if( type == BORDER ) {
+      result = "Borders";
+    } else if( type == ICON ) {
+      result = "Icons";
+    } else if( type == WIDGET ) {
+      result = "Widgets";
+    } else if( type == APPEARANCE ) {
+      result = "Appearances";
+    }
+    return result;
+  }
+
+  private String getThemeKey() {
+    String result = null;
+    if( type == META ) {
+      result = "meta";
+    } else if( type == FONT ) {
+      result = "fonts";
+    } else if( type == COLOR ) {
+      result = "colors";
+    } else if( type == BORDER ) {
+      result = "borders";
+    } else if( type == ICON ) {
+      result = "icons";
+    } else if( type == WIDGET ) {
+      result = "widgets";
+    } else if( type == APPEARANCE ) {
+      result = "appearances";
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxType.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxType.java
new file mode 100644
index 0000000..641a4fd
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/QxType.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+public interface QxType {
+
+  /**
+   * Returns a default representation of the value, that can also be applied to
+   * the constructor. The returned value is also a valid notation for RAP theme
+   * files.
+   */
+  public abstract String toDefaultString();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ResourceLoader.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ResourceLoader.java
new file mode 100644
index 0000000..9c210e4
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ResourceLoader.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface ResourceLoader {
+
+  InputStream getResourceAsStream( String resourceName ) throws IOException;
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElement.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElement.java
new file mode 100644
index 0000000..f4db40f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElement.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.util.Map;
+
+public class ThemeDefElement {
+
+  public final String name;
+  public final String description;
+  public final ThemeDefProperty[] properties;
+  public final Map styleMap;
+  public final Map stateMap;
+
+  public ThemeDefElement( final String name,
+                          final String description,
+                          final ThemeDefProperty[] properties,
+                          final Map styleMap,
+                          final Map stateMap )
+  {
+    this.name = name;
+    this.description = description;
+    this.properties = properties;
+    this.styleMap = styleMap;
+    this.stateMap = stateMap;
+  }
+  
+  
+  
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElementWrapper.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElementWrapper.java
new file mode 100644
index 0000000..19a9344
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefElementWrapper.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rwt.internal.theme;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ThemeDefElementWrapper {
+
+  public final ThemeDefElement element;
+  public final ThemeDefElementWrapper parent;
+  private List children;
+
+  public ThemeDefElementWrapper( final ThemeDefElement element,
+                                 final ThemeDefElementWrapper parent )
+  {
+    this.element = element;
+    this.parent = parent;
+    children = new ArrayList();
+  }
+
+  public void addChildElement( final ThemeDefElementWrapper childElement ) {
+    children.add( childElement );
+  }
+
+  public Object[] getChildren() {
+    return children.toArray();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefProperty.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefProperty.java
new file mode 100644
index 0000000..3be0846
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+public class ThemeDefProperty {
+
+  public final String name;
+  public final String description;
+  public final String[] styles;
+  public final String[] states;
+
+  public ThemeDefProperty( final String name,
+                           final String description,
+                           final String[] styles,
+                           final String[] states )
+  {
+    this.name = name;
+    this.description = description;
+    this.styles = styles;
+    this.states = states;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionProvider.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionProvider.java
new file mode 100644
index 0000000..1c96b49
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionProvider.java
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rwt.internal.theme;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.rap.themeeditor.editor.source.CSSTokenProvider;
+import org.eclipse.rap.themeeditor.editor.source.region.IRegionExt;
+import org.eclipse.rap.themeeditor.editor.source.region.SelectorRegion;
+import org.eclipse.rap.themeeditor.editor.source.region.StateRegion;
+import org.eclipse.rap.themeeditor.editor.source.region.StyleRegion;
+import org.xml.sax.SAXException;
+
+public class ThemeDefinitionProvider {
+
+  private static final String[] THEME_FILES = {
+    "org/eclipse/swt/internal/widgets/widgetkit/Widget.theme.xml",
+    "org/eclipse/swt/internal/widgets/buttonkit/Button.theme.xml",
+    "org/eclipse/swt/internal/widgets/combokit/Combo.theme.xml",
+    "org/eclipse/swt/internal/widgets/coolbarkit/CoolBar.theme.xml",
+    "org/eclipse/swt/internal/custom/ctabfolderkit/CTabFolder.theme.xml",
+    "org/eclipse/swt/internal/widgets/groupkit/Group.theme.xml",
+    "org/eclipse/swt/internal/widgets/labelkit/Label.theme.xml",
+    "org/eclipse/swt/internal/widgets/linkkit/Link.theme.xml",
+    "org/eclipse/swt/internal/widgets/listkit/List.theme.xml",
+    "org/eclipse/swt/internal/widgets/menukit/Menu.theme.xml",
+    "org/eclipse/swt/internal/widgets/progressbarkit/ProgressBar.theme.xml",
+    "org/eclipse/swt/internal/widgets/shellkit/Shell.theme.xml",
+    "org/eclipse/swt/internal/widgets/spinnerkit/Spinner.theme.xml",
+    "org/eclipse/swt/internal/widgets/tabfolderkit/TabFolder.theme.xml",
+    "org/eclipse/swt/internal/widgets/tablekit/Table.theme.xml",
+    "org/eclipse/swt/internal/widgets/textkit/Text.theme.xml",
+    "org/eclipse/swt/internal/widgets/toolbarkit/ToolBar.theme.xml",
+    "org/eclipse/swt/internal/widgets/treekit/Tree.theme.xml"
+  };
+  private ThemeDefElementWrapper[] content;
+  private Map widgetMap;
+  private static ThemeDefinitionProvider provider = new ThemeDefinitionProvider();
+
+  private ThemeDefinitionProvider() {
+    widgetMap = new HashMap();
+    content = new ThemeDefElementWrapper[ THEME_FILES.length ];
+    for( int i = 0; i < THEME_FILES.length; i++ ) {
+      ThemeDefElementWrapper wrapper = readFile( THEME_FILES[ i ] );
+      content[ i ] = wrapper;
+      widgetMap.put( wrapper.element.name, wrapper );
+      for( int j = 0; j < wrapper.getChildren().length; j++ ) {
+        ThemeDefElementWrapper child = ( ThemeDefElementWrapper )wrapper.getChildren()[ j ];
+        widgetMap.put( child.element.name, child );
+      }
+    }
+  }
+
+  public static ThemeDefElementWrapper[] getContent() {
+    return provider.content;
+  }
+
+  public static String getDescription( final IRegionExt regionExt,
+                                       final String content )
+  {
+    String result = "";
+    if( regionExt != null && content != null ) {
+      ThemeDefElementWrapper wrapper = getElementWrapper( regionExt );
+      if( wrapper == null ) {
+        wrapper = ( ThemeDefElementWrapper )provider.widgetMap.get( content );
+      }
+      if( wrapper != null ) {
+        String text;
+        switch( regionExt.getTokenType() ) {
+          case CSSTokenProvider.SELECTOR_TOKEN:
+            result = wrapper.element.description;
+          break;
+          case CSSTokenProvider.STATE_TOKEN:
+            text = ( String )wrapper.element.stateMap.get( content );
+            if( text != null ) {
+              result = text;
+            } else {
+              result = "This state is not supported for "
+                       + wrapper.element.name
+                       + " widgets.";
+            }
+          break;
+          case CSSTokenProvider.STYLE_TOKEN:
+            text = ( String )wrapper.element.styleMap.get( content );
+            if( text != null ) {
+              result = text;
+            } else {
+              result = "This style is not supported for "
+                       + wrapper.element.name
+                       + " widgets.";
+            }
+          break;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static String getPropertyDescription( final String widgetName,
+                                               final String propertyName )
+  {
+    String result = null;
+    ThemeDefElementWrapper wrapper = ( ThemeDefElementWrapper )provider.widgetMap.get( widgetName );
+    if( wrapper != null ) {
+      for( int i = 0; i < wrapper.element.properties.length; i++ ) {
+        ThemeDefProperty property = wrapper.element.properties[ i ];
+        if( property.name.equals( propertyName ) ) {
+          result = property.description;
+        }
+      }
+    }
+    return result;
+  }
+
+  public static ThemeDefElementWrapper getElementWrapper( final IRegionExt regionExt )
+  {
+    ThemeDefElementWrapper result = null;
+    if( regionExt != null ) {
+      SelectorRegion parent;
+      switch( regionExt.getTokenType() ) {
+        case CSSTokenProvider.SELECTOR_TOKEN:
+          result = ( ThemeDefElementWrapper )provider.widgetMap.get( regionExt.getContent()
+            .trim() );
+        break;
+        case CSSTokenProvider.STATE_TOKEN:
+          parent = ( ( StateRegion )regionExt ).getParentRegion();
+          result = ( ThemeDefElementWrapper )provider.widgetMap.get( parent.getContent()
+            .trim() );
+        break;
+        case CSSTokenProvider.STYLE_TOKEN:
+          parent = ( ( StyleRegion )regionExt ).getParentRegion();
+          result = ( ThemeDefElementWrapper )provider.widgetMap.get( parent.getContent()
+            .trim() );
+        break;
+      }
+    }
+    return result;
+  }
+
+  private ThemeDefElementWrapper readFile( final String filename ) {
+    ClassLoader loader = this.getClass().getClassLoader();
+    InputStream is = loader.getResourceAsStream( filename );
+    NewThemeDefinitionReader reader = new NewThemeDefinitionReader( is, "test" );
+    ThemeDefElement[] elements = null;
+    try {
+      elements = reader.read();
+      is.close();
+    } catch( SAXException e ) {
+      e.printStackTrace();
+    } catch( IOException e ) {
+      e.printStackTrace();
+    }
+    ThemeDefElementWrapper result = new ThemeDefElementWrapper( elements[ 0 ],
+                                                                null );
+    for( int i = 1; i < elements.length; i++ ) {
+      result.addChildElement( new ThemeDefElementWrapper( elements[ i ], result ) );
+    }
+    return result;
+  }
+
+  // TODO [mschaeff] do this only once and store in map
+  public static boolean isStyleSupported( final String selector,
+                                          final String styleName )
+  {
+    ThemeDefElementWrapper[] wrappers = getContent();
+    for( int i = 0; i < wrappers.length; i++ ) {
+      ThemeDefElementWrapper wrapper = wrappers[ i ];
+      if( wrapper.element.name.equals( selector ) ) {
+        for( int j = 0; j < wrapper.element.properties.length; j++ ) {
+          String[] styles = wrapper.element.properties[ j ].styles;
+          for( int k = 0; k < styles.length; k++ ) {
+            if( styles[ k ].equals( styleName ) ) {
+              return true;
+            }
+          }
+        }
+        return false;
+      } else {
+        // check child elements
+        for( int m = 0; m < wrapper.getChildren().length; m++ ) {
+          ThemeDefElementWrapper child = ( ThemeDefElementWrapper )wrapper.getChildren()[ m ];
+          if( child.element.name.equals( selector ) ) {
+            for( int j = 0; j < child.element.properties.length; j++ ) {
+              String[] styles = child.element.properties[ j ].styles;
+              for( int k = 0; k < styles.length; k++ ) {
+                if( styles[ k ].equals( styleName ) ) {
+                  return true;
+                }
+              }
+            }
+            return false;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  // TODO [mschaeff] do this only once and store in map
+  // TODO [mschaeff] only supports direct child elements on level 1
+  public static boolean isStateSupported( String selector, String stateName ) {
+    ThemeDefElementWrapper[] wrappers = getContent();
+    for( int i = 0; i < wrappers.length; i++ ) {
+      ThemeDefElementWrapper wrapper = wrappers[ i ];
+      if( wrapper.element.name.equals( selector ) ) {
+        for( int j = 0; j < wrapper.element.properties.length; j++ ) {
+          String[] states = wrapper.element.properties[ j ].states;
+          for( int k = 0; k < states.length; k++ ) {
+            if( states[ k ].equals( stateName ) ) {
+              return true;
+            }
+          }
+        }
+        return false;
+      } else {
+        // check child elements
+        for( int m = 0; m < wrapper.getChildren().length; m++ ) {
+          ThemeDefElementWrapper child = ( ThemeDefElementWrapper )wrapper.getChildren()[ m ];
+          if( child.element.name.equals( selector ) ) {
+            for( int j = 0; j < child.element.properties.length; j++ ) {
+              String[] states = child.element.properties[ j ].states;
+              for( int k = 0; k < states.length; k++ ) {
+                if( states[ k ].equals( stateName ) ) {
+                  return true;
+                }
+              }
+            }
+            return false;
+          }
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionReader.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionReader.java
new file mode 100644
index 0000000..0bcfc3f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeDefinitionReader.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.xml.parsers.*;
+
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+
+/**
+ * Reader for theme definition files. These are the "*.theme.xml" files
+ * that define themeable properties of a certain widget.
+ */
+public class ThemeDefinitionReader {
+
+  public static interface ThemeDefHandler {
+    public abstract void readThemeProperty( ThemeProperty def );
+  }
+
+  private static final String NODE_ROOT = "theme";
+
+  private static final String ATTR_NAME = "name";
+
+  private static final String ATTR_DESCRIPTION = "description";
+
+  private static final String ATTR_DEFAULT = "default";
+
+  private static final String ATTR_INHERIT = "inherit";
+
+  private static final String ATTR_TARGET_PATH = "targetPath";
+
+  private static final String ATTR_TRANSPARENT_ALLOWED = "transparentAllowed";
+
+  private static final String ATTR_CSS_ELEMENTS = "css-elements";
+
+  private static final String ATTR_CSS_PROPERTY = "css-property";
+
+  private static final String ATTR_CSS_SELECTORS = "css-selectors";
+
+  private static final String TYPE_BOOLEAN = "boolean";
+
+  private static final String TYPE_BORDER = "border";
+
+  private static final String TYPE_DIMENSION = "dimension";
+
+  private static final String TYPE_BOXDIMENSION = "boxdim";
+
+  private static final String TYPE_COLOR = "color";
+
+  private static final String TYPE_FONT = "font";
+
+  private static final String TYPE_IMAGE = "image";
+
+  private static final String THEME_DEF_SCHEMA = "themedef.xsd";
+
+  private static final String JAXP_SCHEMA_LANGUAGE
+    = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+  private static final String W3C_XML_SCHEMA
+    = "http://www.w3.org/2001/XMLSchema";
+
+  private final InputStream inputStream;
+
+  private final ResourceLoader loader;
+
+  private final String fileName;
+
+  /**
+   * An instance of this class reads theme definitions from an XML resource.
+   *
+   * @param inputStream input stream from a theme definition XML
+   */
+  public ThemeDefinitionReader( final InputStream inputStream,
+                                final String fileName,
+                                final ResourceLoader loader )
+  {
+    this.fileName = fileName;
+    if( inputStream == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    this.inputStream = inputStream;
+    this.loader = loader;
+  }
+
+  /**
+   * Reads a theme definition from the specified stream. The stream is kept open
+   * after reading.
+   *
+   * @param callback an implementation of the ThemeDefHandler interface that
+   *            handles parsing events
+   * @throws IOException if a I/O error occurs
+   * @throws SAXException if a parse error occurs
+   */
+  public void read( final ThemeDefHandler callback )
+    throws SAXException, IOException
+  {
+    Document document;
+    document = parseThemeDefinition( inputStream );
+    Node root = document.getElementsByTagName( NODE_ROOT ).item( 0 );
+    NodeList childNodes = root.getChildNodes();
+    for( int i = 0; i < childNodes.getLength(); i++ ) {
+      Node node = childNodes.item( i );
+      if( node.getNodeType() == Node.ELEMENT_NODE ) {
+        ThemeProperty property = readElement( node );
+        if( property != null ) {
+          callback.readThemeProperty( property );
+        }
+      }
+    }
+  }
+
+  private ThemeProperty readElement( final Node node ) {
+    String type = node.getNodeName();
+    String name = getAttributeValue( node, ATTR_NAME );
+    String description = getAttributeValue( node, ATTR_DESCRIPTION );
+    String inherit = getAttributeValue( node, ATTR_INHERIT );
+    String defaultStr = getAttributeValue( node, ATTR_DEFAULT );
+    String cssElements = getAttributeValue( node, ATTR_CSS_ELEMENTS );
+    if( cssElements == null ) {
+      cssElements = getAttributeValue( node.getParentNode(), ATTR_CSS_ELEMENTS );
+    }
+    String cssProperty = getAttributeValue( node, ATTR_CSS_PROPERTY );
+    String cssSelectors = getAttributeValue( node, ATTR_CSS_SELECTORS );
+    QxType value;
+    String targetPath = null;
+    boolean transparentAllowed = false;
+    ThemeProperty result = null;
+    if( "property".equals( type ) || "element".equals( type ) ) {
+      // new syntax, ignore for now
+    } else {
+      if( TYPE_FONT.equals( type ) ) {
+        value = QxFont.valueOf( defaultStr );
+      } else if( TYPE_COLOR.equals( type ) ) {
+        String transpValue = getAttributeValue( node, ATTR_TRANSPARENT_ALLOWED );
+        transparentAllowed = Boolean.valueOf( transpValue ).booleanValue();
+        value = QxColor.valueOf( defaultStr );
+      } else if( TYPE_BOOLEAN.equals( type ) ) {
+        value = QxBoolean.valueOf( defaultStr );
+      } else if( TYPE_BORDER.equals( type ) ) {
+        value = QxBorder.valueOf( defaultStr );
+      } else if( TYPE_BOXDIMENSION.equals( type ) ) {
+        value = QxBoxDimensions.valueOf( defaultStr );
+      } else if( TYPE_DIMENSION.equals( type ) ) {
+        value = QxDimension.valueOf( defaultStr );
+      } else if( TYPE_IMAGE.equals( type ) ) {
+        targetPath = getAttributeValue( node, ATTR_TARGET_PATH );
+        value = QxImage.valueOf( defaultStr, loader );
+      } else {
+        // TODO [rst] Remove when XML validation is active
+        throw new IllegalArgumentException( "Illegal type: " + type );
+      }
+      result = new ThemeProperty( name, inherit, value, description );
+      result.targetPath = targetPath;
+      result.transparentAllowed = transparentAllowed;
+      if( cssElements != null && cssProperty != null ) {
+        result.cssElements = cssElements.split( "\\s+" );
+        result.cssProperty = cssProperty;
+        if( cssSelectors != null ) {
+          result.cssSelectors = cssSelectors.split( "\\s+" );
+        }
+      }
+    }
+    return result;
+  }
+
+  private static String getAttributeValue( final Node node, final String name )
+  {
+    String result = null;
+    NamedNodeMap attributes = node.getAttributes();
+    if( attributes != null ) {
+      Node namedItem = attributes.getNamedItem( name );
+      if( namedItem != null ) {
+        result = namedItem.getNodeValue();
+      }
+    }
+    return result;
+  }
+
+  private Document parseThemeDefinition( final InputStream is )
+    throws SAXException, IOException
+  {
+    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+    factory.setNamespaceAware( true );
+    ClassLoader loader = ThemeDefinitionReader.class.getClassLoader();
+    final URL schema = loader.getResource( THEME_DEF_SCHEMA );
+    factory.setValidating( schema != null );
+    try {
+      factory.setAttribute( JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA );
+    } catch( final IllegalArgumentException iae ) {
+      // XML-Processing does not support JAXP 1.2 or greater
+      factory.setNamespaceAware( false );
+      factory.setValidating( false );
+    }
+    DocumentBuilder builder;
+    try {
+      builder = factory.newDocumentBuilder();
+    } catch( final ParserConfigurationException e ) {
+      String message = "Failed to initialize parser for theme definition files";
+      throw new RuntimeException( message, e );
+    }
+//    builder.setEntityResolver( new EntityResolver() {
+//      public InputSource resolveEntity( final String publicID,
+//                                        final String systemID )
+//        throws IOException, SAXException
+//      {
+//        InputSource result = null;
+//        if( schema != null && systemID.endsWith( THEME_DEF_SCHEMA ) ) {
+//          URLConnection connection = schema.openConnection();
+//          connection.setUseCaches( false );
+//          result = new InputSource( connection.getInputStream() );
+//        }
+//        return result;
+//      }
+//    } );
+    builder.setErrorHandler( new ThemeDefinitionErrorHandler() );
+    return builder.parse( is );
+  }
+
+  // TODO: Logging instead of sysout
+  private class ThemeDefinitionErrorHandler implements ErrorHandler {
+    public void error( final SAXParseException spe ) throws SAXException {
+      System.err.println( "Error parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+    
+    public void fatalError( final SAXParseException spe )
+    throws SAXException
+    {
+      System.err.println( "Fatal error parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+    
+    public void warning( final SAXParseException spe )
+    throws SAXException
+    {
+      System.err.println( "Warning parsing theme definition "
+                          + getPosition( spe )
+                          + ":" );
+      System.err.println( spe.getMessage() );
+    }
+    
+    private String getPosition( final SAXParseException spe ) {
+      return "in file '"
+      + fileName
+      + "' at line "
+      + spe.getLineNumber()
+      + ", col "
+      + spe.getColumnNumber();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeProperty.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeProperty.java
new file mode 100644
index 0000000..3768ae3
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/ThemeProperty.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme;
+
+
+/**
+ * Holds all data read from a single property of a theme definition
+ * (*.theme.xml) file.
+ */
+// TODO [rst] Make immutable?
+// TODO [rst] implements equals and hashcode
+public class ThemeProperty {
+
+  private static final String[] EMPTY = new String[ 0 ];
+
+  public final String name;
+
+  public final String inherit;
+
+  public final QxType defValue;
+
+  public final String description;
+
+  public String targetPath;
+
+  public boolean transparentAllowed;
+  
+  public String cssProperty;
+
+  public String[] cssElements;
+
+  public String[] cssSelectors;
+
+  public ThemeProperty( final String name,
+                        final String inherit,
+                        final QxType defValue,
+                        final String description )
+  {
+    this.name = name;
+    this.inherit = inherit;
+    this.defValue = defValue;
+    this.description = description;
+    this.cssElements = EMPTY;
+    this.cssSelectors = EMPTY;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AndConditionImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AndConditionImpl.java
new file mode 100644
index 0000000..9cf49a7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AndConditionImpl.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.CombinatorCondition;
+import org.w3c.css.sac.Condition;
+
+
+public class AndConditionImpl
+  implements CombinatorCondition, ConditionExt
+{
+
+  private final Condition first;
+
+  private final Condition second;
+
+  public AndConditionImpl( final Condition first, final Condition second ) {
+    this.first = first;
+    this.second = second;
+  }
+
+  public Condition getFirstCondition() {
+    return first;
+  }
+
+  public Condition getSecondCondition() {
+    return second;
+  }
+
+  public short getConditionType() {
+    return SAC_AND_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    ElementMatcher firstMatcher = ( ElementMatcher )first;
+    ElementMatcher secondMatcher = ( ElementMatcher )second;
+    return firstMatcher.matches( element ) && secondMatcher.matches( element );
+  }
+
+  public int getSpecificity() {
+    Specific specificFirst = ( Specific )first;
+    Specific specificSecond = ( Specific )second;
+    return specificFirst.getSpecificity() + specificSecond.getSpecificity();
+  }
+
+  public String[] getClasses() {
+    String[] classes1 = ( ( ConditionExt )first ).getClasses();
+    String[] classes2 = ( ( ConditionExt )second ).getClasses();
+    String[] result = null;
+    if( classes1 == null ) {
+      result = classes2;
+    } else if( classes2 == null ) {
+      result = classes1;
+    } else {
+      result = new String[ classes1.length + classes2.length ];
+      System.arraycopy( classes1, 0, result, 0, classes1.length );
+      System.arraycopy( classes2, 0, result, classes1.length, classes2.length );
+    }
+    return result;
+  }
+
+  public String toString() {
+    return first.toString() + second.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AttributeConditionImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AttributeConditionImpl.java
new file mode 100644
index 0000000..fba8050
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/AttributeConditionImpl.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.AttributeCondition;
+
+
+public class AttributeConditionImpl implements AttributeCondition, ConditionExt
+{
+
+  private final String localName;
+  private final String value;
+  private final boolean specified;
+
+  public AttributeConditionImpl( final String localName,
+                                 final String value,
+                                 final boolean specified )
+  {
+    this.localName = localName;
+    this.value = value;
+    this.specified = specified;
+  }
+
+  public String getLocalName() {
+    return localName;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public boolean getSpecified() {
+    return specified;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public short getConditionType() {
+    return SAC_ATTRIBUTE_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    boolean result = false;
+    if( localName != null ) {
+      String attr = element.getAttribute( localName );
+      if( value != null ) {
+        result = value.equals( attr );
+      } else if(attr != null ) {
+        result = !attr.equals( "" );
+      }
+    }
+    return result;
+  }
+
+  public int getSpecificity() {
+    return ATTR_SPEC;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String toString() {
+    String result;
+    if( value != null ) {
+      result = "[" + localName + "=\"" + value + "\"]";
+    } else {
+      result = "[" + localName + "]";
+    }
+    return result;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ChildSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ChildSelectorImpl.java
new file mode 100644
index 0000000..0b60afd
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ChildSelectorImpl.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+
+public class ChildSelectorImpl implements DescendantSelector, SelectorExt {
+
+  private final Selector parent;
+  private final SimpleSelector child;
+
+  public ChildSelectorImpl( final Selector parent, final SimpleSelector child )
+  {
+    this.parent = parent;
+    this.child = child;
+  }
+
+  public Selector getAncestorSelector() {
+    return parent;
+  }
+
+  public SimpleSelector getSimpleSelector() {
+    return child;
+  }
+
+  public short getSelectorType() {
+    return SAC_CHILD_SELECTOR;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String getElementName() {
+    return ( ( SelectorExt )child ).getElementName();
+  }
+
+  public boolean matches( final Element element ) {
+    return ( ( ElementMatcher )child ).matches( element )
+           && ( ( ElementMatcher )parent ).matches( element.getParent() );
+  }
+
+  public int getSpecificity() {
+    return ( ( Specific )parent ).getSpecificity()
+           + ( ( Specific )child ).getSpecificity();
+  }
+
+  public String toString() {
+    return parent.toString() + " > " + child.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ClassConditionImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ClassConditionImpl.java
new file mode 100644
index 0000000..a83578a
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ClassConditionImpl.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.AttributeCondition;
+
+
+public class ClassConditionImpl implements AttributeCondition, ConditionExt {
+
+  private final String value;
+
+  public ClassConditionImpl( final String value ) {
+    this.value = value;
+  }
+
+  public String getLocalName() {
+    return null;
+  }
+
+  public boolean getSpecified() {
+    return true;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public short getConditionType() {
+    return SAC_CLASS_CONDITION;
+  }
+
+  public boolean matches( final Element e ) {
+    return e.hasClass( value );
+  }
+
+  public int getSpecificity() {
+    return ATTR_SPEC;
+  }
+
+  public String[] getClasses() {
+    return new String[] { value };
+  }
+
+  public String toString() {
+    return "." + value;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionExt.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionExt.java
new file mode 100644
index 0000000..cff9526
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionExt.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+
+public interface ConditionExt extends ElementMatcher, Specific {
+
+  abstract String[] getClasses();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionFactoryImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionFactoryImpl.java
new file mode 100644
index 0000000..408cb62
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionFactoryImpl.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+/**
+ * ConditionFactory implementation for parsing RAP theme files. All returned
+ * conditions implement the interface {@link ConditionExt}.
+ */
+public class ConditionFactoryImpl implements ConditionFactory {
+
+  private final CssFileReader reader;
+
+  public ConditionFactoryImpl( final CssFileReader reader ) {
+    this.reader = reader;
+  }
+
+  public AttributeCondition createClassCondition( final String namespaceURI,
+                                                  final String value )
+    throws CSSException
+  {
+    return new ClassConditionImpl( value );
+  }
+
+  public AttributeCondition createPseudoClassCondition( final String namespaceURI,
+                                                        final String value )
+    throws CSSException
+  {
+    return new PseudoClassConditionImpl( value );
+  }
+
+  public AttributeCondition createAttributeCondition( final String localName,
+                                                      final String namespaceURI,
+                                                      final boolean specified,
+                                                      final String value )
+    throws CSSException
+  {
+    return new AttributeConditionImpl( localName, value, specified );
+  }
+
+  public AttributeCondition createOneOfAttributeCondition( final String localName,
+                                                           final String namespaceURI,
+                                                           final boolean specified,
+                                                           final String value )
+    throws CSSException
+  {
+    return new OneOfAttributeCondition( localName, value, specified );
+  }
+
+  public CombinatorCondition createAndCondition( final Condition first,
+                                                 final Condition second )
+    throws CSSException
+  {
+    return new AndConditionImpl( first, second );
+  }
+
+  // ==========================================================================
+  // Not supported by RAP
+
+  public LangCondition createLangCondition( final String lang )
+    throws CSSException
+  {
+    String mesg = "Lang conditions not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    return new NullLangCondition();
+  }
+
+  public AttributeCondition createIdCondition( final String value )
+    throws CSSException
+  {
+    String mesg = "Id conditions not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    return new NullAttributeCondition();
+  }
+
+  public AttributeCondition createBeginHyphenAttributeCondition( final String localName,
+                                                                 final String namespaceURI,
+                                                                 final boolean specified,
+                                                                 final String value )
+    throws CSSException
+  {
+    String mesg = "Begin hyphen attribute conditions not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    return new NullAttributeCondition();
+  }
+
+  // ==========================================================================
+  // Not supported by CSS 2
+
+  public CombinatorCondition createOrCondition( final Condition first,
+                                                final Condition second )
+    throws CSSException
+  {
+    throw new CSSException( "Or conditions not supported by CSS2" );
+  }
+
+  public NegativeCondition createNegativeCondition( final Condition condition )
+    throws CSSException
+  {
+    throw new CSSException( "Negative conditions not supported by CSS2" );
+  }
+
+  public PositionalCondition createPositionalCondition( final int position,
+                                                        final boolean typeNode,
+                                                        final boolean type )
+    throws CSSException
+  {
+    throw new CSSException( "Positional conditions not supported by CSS2" );
+  }
+
+  public Condition createOnlyChildCondition() throws CSSException {
+    throw new CSSException( "Only-one-child conditions not supported by CSS2" );
+  }
+
+  public Condition createOnlyTypeCondition() throws CSSException {
+    throw new CSSException( "Only-one-type conditions not supported by CSS2" );
+  }
+
+  public ContentCondition createContentCondition( final String data )
+    throws CSSException
+  {
+    throw new CSSException( "Content conditions not supported by CSS2" );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionalSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionalSelectorImpl.java
new file mode 100644
index 0000000..e04db51
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ConditionalSelectorImpl.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+
+public class ConditionalSelectorImpl
+  implements ConditionalSelector, SelectorExt
+{
+
+  private final SimpleSelector selector;
+  private final Condition condition;
+
+  public ConditionalSelectorImpl( final SimpleSelector selector,
+                                  final Condition condition )
+  {
+    this.selector = selector;
+    this.condition = condition;
+  }
+
+  public Condition getCondition() {
+    return condition;
+  }
+
+  public SimpleSelector getSimpleSelector() {
+    return selector;
+  }
+
+  public short getSelectorType() {
+    return SAC_CONDITIONAL_SELECTOR;
+  }
+
+  public boolean matches( final Element element ) {
+    ElementMatcher conditionMatcher = ( ElementMatcher )condition;
+    ElementMatcher selectorMatcher = ( ElementMatcher )selector;
+    return selectorMatcher.matches( element )
+           && conditionMatcher.matches( element );
+  }
+
+  public int getSpecificity() {
+    Specific specificSelector = (Specific)selector;
+    Specific specificCondition = (Specific)condition;
+    return specificSelector.getSpecificity()
+           + specificCondition.getSpecificity();
+  }
+
+  public String getElementName() {
+    return ( ( SelectorExt )selector ).getElementName();
+  }
+
+  public String[] getClasses() {
+    return ( ( ConditionExt )condition ).getClasses();
+  }
+
+  public String toString() {
+    return selector.toString() + condition.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileReader.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileReader.java
new file mode 100644
index 0000000..3dd9e5b
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileReader.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.css.sac.*;
+
+
+public class CssFileReader {
+
+  private static final String CSS_ENCODING = "UTF-8";
+
+  private List problems;
+
+  private Parser parser;
+
+  /* BEGIN Modification for Theme Editor */
+  private List errors;
+  private List warnings;
+  private String uri;
+  /* END Modification for Theme Editor */
+
+  public CssFileReader() {
+    try {
+      parser = new org.apache.batik.css.parser.Parser();
+    } catch( Exception e ) {
+      throw new RuntimeException( "Failed to instantiate CSS parser", e );
+    }
+    problems = new ArrayList();
+    /* BEGIN Modification for Theme Editor */
+    errors = new ArrayList();
+    warnings = new ArrayList();
+    /* END Modification for Theme Editor */
+  }
+
+  public StyleSheet parse( final InputStream inputStream, final String uri )
+    throws CSSException, IOException
+  {
+    /* BEGIN Modification for Theme Editor */
+    this.uri = uri;
+    /* END Modification for Theme Editor */
+    InputSource source = new InputSource();
+    source.setByteStream( inputStream );
+    source.setEncoding( CSS_ENCODING );
+    source.setURI( uri );
+    parser.setConditionFactory( new ConditionFactoryImpl( this ) );
+    parser.setSelectorFactory( new SelectorFactoryImpl( this ) );
+    DocumentHandlerImpl documentHandler = new DocumentHandlerImpl( this );
+    parser.setDocumentHandler( documentHandler );
+    parser.setErrorHandler( new ErrorHandlerImpl( this ) );
+    parser.parseStyleSheet( source );
+    StyleRule[] styleRules = documentHandler.getStyleRules();
+    StyleSheet result = new StyleSheet( styleRules );
+    /* BEGIN Modification for Theme Editor */
+    result.setHeaderComment( documentHandler.getHeaderComment() );
+    /* END Modification for Theme Editor */
+    return result;
+  }
+
+  public CSSException[] getProblems() {
+    CSSException[] result = new CSSException[ problems.size() ];
+    problems.toArray( result );
+    return result;
+  }
+
+  void addProblem( final CSSException exception ) {
+//    TODO [rst] Logging instead of sysout
+    System.err.println( exception );
+    problems.add( exception );
+  /* BEGIN Modification for Theme Editor */
+    if( exception instanceof CSSParseException ) {
+      addWarning( ( CSSParseException )exception );
+    } else if( parser instanceof org.apache.batik.css.parser.Parser ) {
+      CSSParseException parseException = new CSSParseException( exception.getMessage(),
+                                                                uri,
+                                                                getCurrentLine(),
+                                                                0 );
+      addWarning( parseException );
+    }
+  /* END Modification for Theme Editor */
+  }
+
+  public CSSParseException[] getErrors() {
+    CSSParseException[] result = new CSSParseException[ errors.size() ];
+    errors.toArray( result );
+    return result;
+  }
+
+  public CSSParseException[] getWarnings() {
+    CSSParseException[] result = new CSSParseException[ warnings.size() ];
+    warnings.toArray( result );
+    return result;
+  }
+
+  private void addError( final CSSParseException exception ) {
+    errors.add( exception );
+  }
+
+  private void addWarning( final CSSParseException exception ) {
+    warnings.add( exception );
+  }
+
+  public int getCurrentLine() {
+    int result = -1;
+    if( parser instanceof org.apache.batik.css.parser.Parser ) {
+      //result = ( ( org.apache.batik.css.parser.Parser )parser ).getLine();
+    }
+    return result;
+  }
+  /* END Modification for Theme Editor */
+
+
+  private static String createProblemDescription( final String type,
+                                                  final CSSParseException exception )
+  {
+    String pattern = "{0}: {1} in {2} at pos [{3}:{4}]";
+    Object[] arguments = new Object[] {
+      type,
+      exception.getMessage(),
+      exception.getURI(),
+      String.valueOf( exception.getLineNumber() ),
+      String.valueOf( exception.getColumnNumber() )
+    };
+    return MessageFormat.format( pattern, arguments );
+  }
+
+  private static class ErrorHandlerImpl implements ErrorHandler {
+
+//    private final List problems;
+    private final CssFileReader reader;
+
+    public ErrorHandlerImpl( final CssFileReader reader ) {
+//      this.problems = reader.problems;
+      this.reader = reader;
+    }
+
+    // TODO [rst] decent logging instead of sysout
+    public void warning( final CSSParseException exception ) throws CSSException {
+//      String problem = createProblemDescription( "WARNING: ", exception );
+//      System.err.println( problem );
+//      problems.add( exception );
+      reader.addWarning( exception );
+    }
+
+    public void error( final CSSParseException exception ) throws CSSException {
+//      String problem = createProblemDescription( "ERROR: ", exception );
+//      System.err.println( problem );
+//      problems.add( exception );
+      reader.addError( exception );
+    }
+
+    public void fatalError( final CSSParseException exception ) throws CSSException {
+//      String problem = createProblemDescription( "FATAL ERROR: ", exception );
+//      System.err.println( problem );
+//      problems.add( exception );
+      reader.addError( exception );
+      throw exception;
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileWriter.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileWriter.java
new file mode 100644
index 0000000..1aeafaa
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/CssFileWriter.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+
+public class CssFileWriter {
+
+  private BufferedWriter out;
+
+  public CssFileWriter() {
+  }
+
+  public void write( StyleSheet styleSheet, String filename ) {
+    ClassLoader classLoader = CssFileWriter.class.getClassLoader();
+    URL url = classLoader.getResource( filename );
+    try {
+      File file = new File( new URI( url.toString() ) );
+      out = new BufferedWriter( new FileWriter( file ) );
+      writeRules( styleSheet );
+      out.flush();
+      out.close();
+    } catch( URISyntaxException e ) {
+      e.printStackTrace();
+    } catch( IOException e ) {
+      e.printStackTrace();
+    }
+  }
+
+  public String getAsString( StyleSheet styleSheet ) {
+    String result;
+    if( styleSheet == null ) {
+      result = "";
+    } else {
+      StringWriter stringWriter = new StringWriter();
+      try {
+        out = new BufferedWriter( stringWriter );
+        writeRules( styleSheet );
+        out.flush();
+        out.close();
+      } catch( IOException e ) {
+        e.printStackTrace();
+      }
+      result = stringWriter.toString();
+    }
+    return result;
+  }
+
+  private void writeRules( StyleSheet styleSheet ) throws IOException {
+    // write header comment first
+    String headerComment = styleSheet.getHeaderComment();
+    if( headerComment != null ) {
+      out.write( "/*" + headerComment + "*/" );
+      out.newLine();
+    }
+    // write each rule with comments
+    StyleRule[] rules = styleSheet.getStyleRules();
+    for( int i = 0; i < rules.length; i++ ) {
+      StyleRule rule = rules[ i ];
+      List comments = rule.getComments();
+      for( int j = 0; j < comments.size(); j++ ) {
+        String comment = ( String )comments.get( j );
+        out.write( "/*" + comment + "*/" );
+        out.newLine();
+      }
+      StylePropertyMap properties = rule.getProperties();
+      out.write( rule.getSelectorText() );
+      out.write( " {" );
+      out.newLine();
+      String[] keys = properties.getKeys();
+      for( int j = 0; j < keys.length; j++ ) {
+        String cssProperty = keys[ j ];
+        
+        String property = properties.getPropertyString( cssProperty );
+        if ( property != null ) {
+          out.write( "  " + cssProperty + ": " + property + ";" );
+          out.newLine();
+        }
+        
+        /*
+        LexicalUnit property = properties.getProperty( cssProperty );
+        if( property != null ) {
+          out.write( getPropertyAsString( cssProperty ) );
+          IPropertyWrapper wrapper = PropertyWrapperFactory.createPropertyWrapper( cssProperty,
+                                                                                   property,
+                                                                                   rule );
+          if( wrapper != null ) {
+            out.write( wrapper.getDefaultString() );
+          } else {
+            // handle unsupported properties
+            if( property.getLexicalUnitType() == LexicalUnit.SAC_IDENT ) {
+              out.write( property.getStringValue() );
+            } else {
+              out.write( "0" );
+            }
+          }
+          out.write( ";" );
+          out.newLine();
+        }
+        */
+        
+        
+      }
+      out.write( "}" );
+      out.newLine();
+      out.newLine();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerExt.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerExt.java
new file mode 100644
index 0000000..5cc2225
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerExt.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Mathias Schaeffner and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Mathias Schaeffner - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+public interface DocumentHandlerExt {
+
+  public void propertyString( final String name, final String value );
+
+  public void propertyLine( final String name, final int line );
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerImpl.java
new file mode 100644
index 0000000..fa6d12e
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/DocumentHandlerImpl.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.css.sac.*;
+
+
+public class DocumentHandlerImpl implements DocumentHandler, DocumentHandlerExt {
+
+  private String uri;
+  private final List rules;
+  private final CssFileReader reader;
+  private StylePropertyMap currentStyleProperties = null;
+
+  /* BEGIN Modification for Theme Editor */
+  private int ruleLineNumber = -1;
+  private List comments = new ArrayList();
+  private boolean firstSelectorStarted = false;
+  private boolean insideRule = false;
+  private String headerComment;
+
+  public String getHeaderComment() {
+    return headerComment;
+  }
+  /* END Modification for Theme Editor */
+
+  public DocumentHandlerImpl( final CssFileReader reader ) {
+    this.reader = reader;
+    this.rules = new ArrayList();
+  }
+
+  public void startDocument( final InputSource source ) throws CSSException {
+    uri = source.getURI();
+    log( "=== startDocument " + uri + "===" );
+  }
+
+  public void endDocument( final InputSource source ) throws CSSException {
+    log( "___ endDocument ___" );
+  }
+
+  public void startSelector( final SelectorList patterns ) throws CSSException {
+    log( "startSelector " + toString( patterns ) );
+    currentStyleProperties = new StylePropertyMap();
+    /* BEGIN Modification for Theme Editor */
+    ruleLineNumber = reader.getCurrentLine() - 1;
+    firstSelectorStarted = true;
+    insideRule = true;
+    /* END Modification for Theme Editor */
+  }
+
+  public void endSelector( final SelectorList patterns ) throws CSSException {
+    log( "endSelector " + toString( patterns ) );
+  /* BEGIN Modification for Theme Editor */
+    StyleRule newRule = new StyleRule( patterns, currentStyleProperties );
+    newRule.setComments( comments );
+    newRule.setLineNumber( ruleLineNumber );
+    rules.add( newRule );
+    comments = new ArrayList();
+    insideRule = false;
+  /* END Modification for Theme Editor */
+    currentStyleProperties = null;
+  }
+
+  public void property( final String name,
+                        final LexicalUnit value,
+                        final boolean important ) throws CSSException
+  {
+    log( "  property "
+         + name
+         + " := "
+         + toString( value )
+         + ( important
+                      ? ", important"
+                      : "" ) );
+    if( currentStyleProperties != null ) {
+      currentStyleProperties.setProperty( name, value );
+    }
+  }
+
+  /* BEGIN Modification for Theme Editor */
+  public void propertyString( final String name, final String value ) {
+    if( currentStyleProperties != null ) {
+      currentStyleProperties.setPropertyString( name, value );
+    }
+  }
+
+  public void propertyLine( final String name, final int line ) {
+    if( currentStyleProperties != null ) {
+      currentStyleProperties.setLineNumber( name, line );
+    }
+  }
+  /* END Modification for Theme Editor */
+
+  // -- ignored, but used for Theme Editor --
+  public void comment( final String text ) throws CSSException {
+    log( "    /*" + text + "*/" );
+    if ( !insideRule ) {
+      if ( !firstSelectorStarted && headerComment == null ) {
+        headerComment = text;
+      } else {
+        comments.add( text );
+      }
+    }
+  }
+
+  // -- unsupported --
+  public void importStyle( final String uri,
+                           final SACMediaList media,
+                           final String defaultNamespaceURI )
+    throws CSSException
+  {
+    log( "importStyle " + uri + ", " + media + ", " + defaultNamespaceURI );
+    reader.addProblem( new CSSException( "import rules not supported - ignored" ) );
+  }
+
+  public void namespaceDeclaration( final String prefix, final String uri )
+    throws CSSException
+  {
+    log( "namespaceDeclaration " + prefix + ", " + uri );
+    reader.addProblem( new CSSException( "unsupported namespace declaration '"
+                                         + prefix
+                                         + ":"
+                                         + uri
+                                         + "' - ignored" ) );
+  }
+
+  public void ignorableAtRule( final String atRule ) throws CSSException {
+    log( "ignorableAtRule " + atRule );
+    reader.addProblem( new CSSException( "unsupported at rule '"
+                                         + atRule
+                                         + "' - ignored" ) );
+  }
+
+  public void startPage( final String name, final String pseudo_page )
+    throws CSSException
+  {
+    log( "startPage " + name + ", " + pseudo_page );
+    reader.addProblem( new CSSException( "page rules not supported - ignored" ) );
+  }
+
+  public void endPage( final String name, final String pseudo_page )
+    throws CSSException
+  {
+    log( "endPage " + name + ", " + pseudo_page );
+  }
+
+  public void startMedia( final SACMediaList media ) throws CSSException {
+    log( "startMedia " + media );
+    reader.addProblem( new CSSException( "media rules not supported - ignored" ) );
+  }
+
+  public void endMedia( final SACMediaList media ) throws CSSException {
+    log( "endMedia " + media );
+  }
+
+  public void startFontFace() throws CSSException {
+    log( "startFontFace" );
+    reader.addProblem( new CSSException( "font face rules not supported - ignored" ) );
+  }
+
+  public void endFontFace() throws CSSException {
+    log( "end FontFace" );
+  }
+
+  public StyleRule[] getStyleRules() {
+    StyleRule[] result = new StyleRule[ rules.size() ];
+    rules.toArray( result );
+    return result;
+  }
+
+  private void log( final String message ) {
+//    System.out.println( message );
+  }
+
+  private static String toString( final SelectorList patterns ) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.append( "[" );
+    int length = patterns.getLength();
+    for( int i = 0; i < length; i++ ) {
+      buffer.append( " " );
+      Selector selector = patterns.item( i );
+      buffer.append( selector.toString() );
+    }
+    buffer.append( " ]" );
+    return buffer.toString();
+  }
+
+  private static String toString( final LexicalUnit value ) {
+    StringBuffer buffer = new StringBuffer();
+    short type = value.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_ATTR ) {
+      buffer.append( "ATTR " + value.getStringValue() );
+    } else if( type == LexicalUnit.SAC_CENTIMETER
+               || type == LexicalUnit.SAC_DEGREE
+               || type == LexicalUnit.SAC_EM
+               || type == LexicalUnit.SAC_EX
+               || type == LexicalUnit.SAC_GRADIAN
+               || type == LexicalUnit.SAC_HERTZ
+               || type == LexicalUnit.SAC_INCH
+               || type == LexicalUnit.SAC_KILOHERTZ
+               || type == LexicalUnit.SAC_MILLIMETER
+               || type == LexicalUnit.SAC_MILLISECOND
+               || type == LexicalUnit.SAC_PERCENTAGE
+               || type == LexicalUnit.SAC_PICA
+               || type == LexicalUnit.SAC_POINT
+               || type == LexicalUnit.SAC_PIXEL
+               || type == LexicalUnit.SAC_RADIAN
+               || type == LexicalUnit.SAC_SECOND
+               || type == LexicalUnit.SAC_DIMENSION )
+    {
+      buffer.append( "DIM "
+                     + value.getFloatValue()
+                     + value.getDimensionUnitText() );
+    } else if( type == LexicalUnit.SAC_RGBCOLOR ) {
+      LexicalUnit parameters = value.getParameters();
+      buffer.append( "RGBCOLOR " + toString( parameters ) );
+    } else if( type == LexicalUnit.SAC_STRING_VALUE ) {
+      buffer.append( "STRING " + value.getStringValue() );
+    } else if( type == LexicalUnit.SAC_IDENT ) {
+      buffer.append( "IDENT " + value.getStringValue() );
+    } else if( type == LexicalUnit.SAC_PIXEL ) {
+      buffer.append( "PIXEL " + value.getFloatValue() );
+    } else if( type == LexicalUnit.SAC_INTEGER ) {
+      buffer.append( "INT " + value.getIntegerValue() );
+    } else if( type == LexicalUnit.SAC_OPERATOR_COMMA ) {
+      buffer.append( "COMMA" );
+    } else if( type == LexicalUnit.SAC_ATTR ) {
+      buffer.append( "ATTR " + value.getStringValue() );
+    } else if( type == LexicalUnit.SAC_FUNCTION ) {
+      buffer.append( "UNKNOWN FUNCTION " + value.getFunctionName() );
+    } else if( type == LexicalUnit.SAC_DIMENSION ) {
+      buffer.append( "UNKNOWN DIMENSION " + value );
+    } else {
+      buffer.append( "unsupported unit " + value.getLexicalUnitType() );
+    }
+    LexicalUnit next = value.getNextLexicalUnit();
+    if( next != null ) {
+      buffer.append( ", " );
+      buffer.append( toString( next ) );
+    }
+    return buffer.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Element.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Element.java
new file mode 100644
index 0000000..c0f5247
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Element.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+
+public interface Element {
+
+  abstract boolean hasName( String name );
+
+  abstract boolean hasClass( String name );
+
+  abstract boolean hasPseudoClass( String name );
+
+  abstract boolean hasAttribute( String name );
+
+  abstract String getAttribute( String name );
+
+  abstract Element getParent();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementMatcher.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementMatcher.java
new file mode 100644
index 0000000..308d79a
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementMatcher.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+
+public interface ElementMatcher {
+
+  abstract boolean matches( Element element );
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementSelectorImpl.java
new file mode 100644
index 0000000..1fc3025
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ElementSelectorImpl.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.ElementSelector;
+
+
+public class ElementSelectorImpl implements ElementSelector, SelectorExt {
+
+  private final String tagName;
+
+  public ElementSelectorImpl( final String tagName ) {
+    this.tagName = tagName;
+  }
+
+  public String getLocalName() {
+    return tagName;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public short getSelectorType() {
+    return SAC_ELEMENT_NODE_SELECTOR;
+  }
+
+  public boolean matches( final Element element ) {
+    boolean result = false;
+    if( element != null ) {
+      result = tagName == null || element.hasName( tagName );
+    }
+    return result;
+  }
+
+  public int getSpecificity() {
+    return tagName != null ? ELEMENT_SPEC : 0;
+  }
+
+  public String getElementName() {
+    return tagName;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String toString() {
+    return tagName != null ? tagName : "*";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyChildSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyChildSelectorImpl.java
new file mode 100644
index 0000000..e99c9c9
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyChildSelectorImpl.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+public class EmptyChildSelectorImpl implements DescendantSelector, SelectorExt {
+
+  private final Selector parent;
+  private final SimpleSelector child;
+
+  public EmptyChildSelectorImpl( final Selector parent,
+                                 final SimpleSelector child )
+  {
+    this.parent = parent;
+    this.child = child;
+  }
+
+  public Selector getAncestorSelector() {
+    return parent;
+  }
+
+  public SimpleSelector getSimpleSelector() {
+    return child;
+  }
+
+  public short getSelectorType() {
+    return SAC_CHILD_SELECTOR;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String getElementName() {
+    return ( ( SelectorExt )child ).getElementName();
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return parent.toString() + " > " + child.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyDescendantSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyDescendantSelectorImpl.java
new file mode 100644
index 0000000..d00d461
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyDescendantSelectorImpl.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+public class EmptyDescendantSelectorImpl
+  implements DescendantSelector, SelectorExt
+{
+
+  private final Selector parent;
+  private final SimpleSelector child;
+
+  public EmptyDescendantSelectorImpl( final Selector parent,
+                                      final SimpleSelector child )
+  {
+    this.parent = parent;
+    this.child = child;
+  }
+
+  public Selector getAncestorSelector() {
+    return parent;
+  }
+
+  public SimpleSelector getSimpleSelector() {
+    return child;
+  }
+
+  public short getSelectorType() {
+    return SAC_DESCENDANT_SELECTOR;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String getElementName() {
+    return ( ( SelectorExt )child ).getElementName();
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return parent.toString() + " " + child.toString();
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyElementSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyElementSelectorImpl.java
new file mode 100644
index 0000000..574df71
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptyElementSelectorImpl.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.ElementSelector;
+
+public class EmptyElementSelectorImpl implements ElementSelector, SelectorExt {
+
+  private final String tagName;
+
+  public EmptyElementSelectorImpl( final String tagName ) {
+    this.tagName = tagName;
+  }
+
+  public String getLocalName() {
+    return null;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public short getSelectorType() {
+    return SAC_ELEMENT_NODE_SELECTOR;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String getElementName() {
+    return null;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String toString() {
+    return tagName != null
+                          ? tagName
+                          : "*";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptySiblingSelectorImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptySiblingSelectorImpl.java
new file mode 100644
index 0000000..99e6bea
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/EmptySiblingSelectorImpl.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+public class EmptySiblingSelectorImpl implements SiblingSelector, SelectorExt {
+
+  private final SimpleSelector directAdjacent;
+  private final Selector child;
+  private short nodeType;
+
+  public EmptySiblingSelectorImpl( final short nodeType,
+                                   final Selector child,
+                                   final SimpleSelector directAdjacent )
+  {
+    this.directAdjacent = directAdjacent;
+    this.child = child;
+    this.nodeType = nodeType;
+  }
+
+  public Selector getSelector() {
+    return child;
+  }
+
+  public SimpleSelector getSiblingSelector() {
+    return directAdjacent;
+  }
+
+  public short getSelectorType() {
+    return SAC_DIRECT_ADJACENT_SELECTOR;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String getElementName() {
+    return ( ( SelectorExt )child ).getElementName();
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return child.toString() + " + " + directAdjacent.toString();
+  }
+
+  public short getNodeType() {
+    return nodeType;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullAttributeCondition.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullAttributeCondition.java
new file mode 100644
index 0000000..9ebfc58
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullAttributeCondition.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.AttributeCondition;
+
+
+public class NullAttributeCondition implements AttributeCondition, ConditionExt
+{
+
+  public String getLocalName() {
+    return null;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public boolean getSpecified() {
+    return false;
+  }
+
+  public String getValue() {
+    return null;
+  }
+
+  public short getConditionType() {
+    return SAC_ATTRIBUTE_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return "null condition";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullDescendantSelector.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullDescendantSelector.java
new file mode 100644
index 0000000..0a1b66f
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullDescendantSelector.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+
+public class NullDescendantSelector implements DescendantSelector, SelectorExt {
+
+  public Selector getAncestorSelector() {
+    return null;
+  }
+
+  public SimpleSelector getSimpleSelector() {
+    return null;
+  }
+
+  public short getSelectorType() {
+    return SAC_DESCENDANT_SELECTOR;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public String getElementName() {
+    return null;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return "null selector";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullElementSelector.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullElementSelector.java
new file mode 100644
index 0000000..c4dc0da
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullElementSelector.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.ElementSelector;
+
+
+public class NullElementSelector implements ElementSelector, SelectorExt {
+
+  public String getLocalName() {
+    return null;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public short getSelectorType() {
+    return SAC_ELEMENT_NODE_SELECTOR;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public String getElementName() {
+    return null;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return "null selector";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullLangCondition.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullLangCondition.java
new file mode 100644
index 0000000..ee5322d
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullLangCondition.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.LangCondition;
+
+
+public class NullLangCondition implements LangCondition, ConditionExt {
+
+  public String getLang() {
+    return null;
+  }
+
+  public short getConditionType() {
+    return SAC_LANG_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return "null condition";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullSiblingSelector.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullSiblingSelector.java
new file mode 100644
index 0000000..05f939e
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/NullSiblingSelector.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+
+public class NullSiblingSelector implements SiblingSelector, SelectorExt {
+
+  public Selector getSelector() {
+    return null;
+  }
+
+  public SimpleSelector getSiblingSelector() {
+    return null;
+  }
+
+  public short getSelectorType() {
+    return SAC_DIRECT_ADJACENT_SELECTOR;
+  }
+
+  public short getNodeType() {
+    return ANY_NODE;
+  }
+
+  public boolean matches( final Element element ) {
+    return false;
+  }
+
+  public String getElementName() {
+    return null;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public int getSpecificity() {
+    return 0;
+  }
+
+  public String toString() {
+    return "null selector";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/OneOfAttributeCondition.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/OneOfAttributeCondition.java
new file mode 100644
index 0000000..3565bcc
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/OneOfAttributeCondition.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.AttributeCondition;
+
+
+public class OneOfAttributeCondition implements AttributeCondition, ConditionExt
+{
+
+  private final String localName;
+  private final String value;
+  private final boolean specified;
+
+  public OneOfAttributeCondition( final String localName,
+                                  final String value,
+                                  final boolean specified )
+  {
+    this.localName = localName;
+    this.value = value;
+    this.specified = specified;
+  }
+
+  public String getLocalName() {
+    return localName;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public boolean getSpecified() {
+    return specified;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public short getConditionType() {
+    return SAC_ONE_OF_ATTRIBUTE_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    boolean result = false;
+    if( localName != null && value != null ) {
+      String attr = element.getAttribute( localName );
+      // TODO improve this
+      if( attr != null ) {
+        String[] parts = attr.split( "\\s+" );
+        for( int i = 0; i < parts.length && !result; i++ ) {
+          result |= parts[ i ].equals( value );
+        }
+      }
+    }
+    return result;
+  }
+
+  public int getSpecificity() {
+    return ATTR_SPEC;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String toString() {
+    return "[" + getLocalName() + "~=\"" + getValue() + "\"]";
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ParserUtil.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ParserUtil.java
new file mode 100644
index 0000000..8fb7fe7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/ParserUtil.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.DocumentHandler;
+
+public class ParserUtil {
+
+  public static void handlePropertyString( final DocumentHandler documentHandler,
+                                           final String propertyName,
+                                           final String propertyString,
+                                           final int propertyLine )
+  {
+    if( propertyString != null ) {
+      String trimmedString = propertyString.trim();
+      if( documentHandler instanceof DocumentHandlerExt ) {
+        int length = trimmedString.length();
+        if( trimmedString.charAt( length - 1 ) == ';' ) {
+          trimmedString = trimmedString.substring( 0, length - 1 );
+        }
+        ( ( DocumentHandlerExt )documentHandler ).propertyString( propertyName,
+                                                                  trimmedString );
+        ( ( DocumentHandlerExt )documentHandler ).propertyLine( propertyName,
+                                                                propertyLine );
+      }
+    }
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PropertyResolver.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PropertyResolver.java
new file mode 100644
index 0000000..a4f2c06
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PropertyResolver.java
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.*;
+
+import org.eclipse.rwt.internal.theme.*;
+
+import org.w3c.css.sac.LexicalUnit;
+
+
+public class PropertyResolver {
+
+  private static final String BOLD = "bold";
+
+  private static final String ITALIC = "italic";
+
+  private static final String NORMAL = "normal";
+
+  private static final String NONE = "none";
+
+  private static final String HIDDEN = "hidden";
+
+  private static final String DOTTED = "dotted";
+
+  private static final String DASHED = "dashed";
+
+  private static final String SOLID = "solid";
+
+  private static final String DOUBLE = "double";
+
+  private static final String GROOVE = "groove";
+
+  private static final String RIDGE = "ridge";
+
+  private static final String INSET = "inset";
+
+  private static final String OUTSET = "outset";
+
+  /** A thin border. */
+  private static final String THIN = "thin";
+
+  /** A medium border. */
+  private static final String MEDIUM = "medium";
+
+  /** A thick border. */
+  private static final String THICK = "thick";
+
+  private static final String TRANSPARENT = "transparent";
+
+  private static final Map NAMED_COLORS = new HashMap();
+
+  private static final List BORDER_STYLES = new ArrayList();
+
+  /** Width value for "thin" identifier. */
+  public static final int THIN_VALUE = 1;
+
+  /** Width value for "medium" identifier. */
+  public static final int MEDIUM_VALUE = 3;
+
+  /** Width value for "thick" identifier. */
+  public static final int THICK_VALUE = 5;
+
+  static {
+    // register 16 standard HTML colors
+    NAMED_COLORS.put( "black", new int[] { 0, 0, 0 } );
+    NAMED_COLORS.put( "gray", new int[] { 128, 128, 128 } );
+    NAMED_COLORS.put( "silver", new int[] { 192, 192, 192 } );
+    NAMED_COLORS.put( "white", new int[] { 255, 255, 255 } );
+    NAMED_COLORS.put( "maroon", new int[] { 128, 0, 0 } );
+    NAMED_COLORS.put( "red", new int[] { 255, 0, 0 } );
+    NAMED_COLORS.put( "purple", new int[] { 128, 0, 128 } );
+    NAMED_COLORS.put( "fuchsia", new int[] { 255, 0, 255 } );
+    NAMED_COLORS.put( "green", new int[] { 0, 128, 0 } );
+    NAMED_COLORS.put( "lime", new int[] { 0, 255, 0 } );
+    NAMED_COLORS.put( "navy", new int[] { 0, 0, 128 } );
+    NAMED_COLORS.put( "blue", new int[] { 0, 0, 255 } );
+    NAMED_COLORS.put( "olive", new int[] { 128, 128, 0 } );
+    NAMED_COLORS.put( "yellow", new int[] { 255, 255, 0 } );
+    NAMED_COLORS.put( "teal", new int[] { 0, 128, 128 } );
+    NAMED_COLORS.put( "aqua", new int[] { 0, 255, 255 } );
+    // register border styles
+    BORDER_STYLES.add( NONE ); // No border; the computed border width is zero.
+    BORDER_STYLES.add( HIDDEN ); // Same as 'none', except in terms of border conflict resolution for table elements.
+    BORDER_STYLES.add( DOTTED ); // The border is a series of dots.
+    BORDER_STYLES.add( DASHED ); // The border is a series of short line segments.
+    BORDER_STYLES.add( SOLID ); // The border is a single line segment.
+    BORDER_STYLES.add( DOUBLE ); // The border is two solid lines. The sum of the two lines and the space between them equals the value of 'border-width'.
+    BORDER_STYLES.add( GROOVE ); // The border looks as though it were carved into the canvas.
+    BORDER_STYLES.add( RIDGE ); // The opposite of 'groove': the border looks as though it were coming out of the canvas.
+    BORDER_STYLES.add( INSET ); // The border makes the box look as though it were embedded in the canvas.
+    BORDER_STYLES.add( OUTSET ); // The opposite of 'inset': the border makes the box look as though it were coming out of the canvas.
+  }
+
+  public static QxColor readColor( final LexicalUnit input ) {
+    QxColor result = null;
+    short type = input.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_RGBCOLOR ) {
+      LexicalUnit redParam = input.getParameters();
+      LexicalUnit greenParam = redParam.getNextLexicalUnit().getNextLexicalUnit();
+      LexicalUnit blueParam = greenParam.getNextLexicalUnit().getNextLexicalUnit();
+      short valueType = redParam.getLexicalUnitType();
+      if( greenParam.getLexicalUnitType() == valueType
+          || blueParam.getLexicalUnitType() == valueType )
+      {
+        if( valueType == LexicalUnit.SAC_INTEGER ) {
+          int red = normalizeRGBValue( redParam.getIntegerValue() );
+          int green = normalizeRGBValue( greenParam.getIntegerValue() );
+          int blue = normalizeRGBValue( blueParam.getIntegerValue() );
+          result = QxColor.create( red, green, blue );
+        } else if( valueType == LexicalUnit.SAC_PERCENTAGE ) {
+          float redPercent = normalizePercentValue( redParam.getFloatValue() );
+          float greenPercent = normalizePercentValue( greenParam.getFloatValue() );
+          float bluePercent = normalizePercentValue( blueParam.getFloatValue() );
+          int red = ( int )( 255 * redPercent / 100 );
+          int green = ( int )( 255 * greenPercent / 100 );
+          int blue = ( int )( 255 * bluePercent / 100 );
+          result = QxColor.create( red, green, blue );
+        }
+      }
+    } else if( type == LexicalUnit.SAC_IDENT ) {
+      String string = input.getStringValue();
+      if( TRANSPARENT.equals( string ) ) {
+        result = QxColor.TRANSPARENT;
+      } else if( NAMED_COLORS.containsKey( string.toLowerCase() ) ) {
+        int[] values = ( int[] )NAMED_COLORS.get( string.toLowerCase() );
+        result = QxColor.create( values[ 0 ], values[ 1 ], values[ 2 ] );
+      }
+    }
+    return result;
+  }
+
+  public static QxDimension readDimension( final LexicalUnit unit ) {
+    QxDimension result = null;
+    Integer length = readSingleLengthUnit( unit );
+    if( length != null ) {
+      result = QxDimension.create( length.intValue() );
+    }
+    return result;
+  }
+
+  public static QxBoxDimensions readBoxDimensions( final LexicalUnit unit ) {
+    QxBoxDimensions result = null;
+    Integer value1 = readSingleLengthUnit( unit );
+    if( value1 != null ) {
+      int top, right, left, bottom;
+      top = right = bottom = left = value1.intValue();
+      LexicalUnit nextUnit = unit.getNextLexicalUnit();
+      boolean ok = true;
+      int pos = 1;
+      while( nextUnit != null && ok ) {
+        pos++;
+        Integer nextValue = readSingleLengthUnit( nextUnit );
+        ok &= nextValue != null && pos <= 4;
+        if( ok ) {
+          if( pos == 2 ) {
+            right = left = nextValue.intValue();
+          } else if( pos == 3 ) {
+            bottom = nextValue.intValue();
+          } else if( pos == 4 ) {
+            left = nextValue.intValue();
+          }
+        }
+        nextUnit = nextUnit.getNextLexicalUnit();
+      }
+      ok &= nextUnit == null;
+      if( ok  ) {
+        result = QxBoxDimensions.create( top, right, bottom, left );
+      }
+    }
+    return result;
+  }
+
+  public static String readBorderStyle( final LexicalUnit unit ) {
+    String result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_IDENT ) {
+      String string = unit.getStringValue();
+      if( BORDER_STYLES.contains( string ) ) {
+        result = string;
+      }
+    }
+    return result;
+  }
+
+  public static int readBorderWidth( final LexicalUnit unit ) {
+    int result = -1;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_IDENT ) {
+      String string = unit.getStringValue();
+      if( THIN.equals( string ) ) {
+        result = THIN_VALUE;
+      } else if( MEDIUM.equals( string ) ) {
+        result = MEDIUM_VALUE;
+      } else if( THICK.equals( string ) ) {
+        result = THICK_VALUE;
+      }
+    } else if( type == LexicalUnit.SAC_PIXEL ) {
+      float value = unit.getFloatValue();
+      if( value >= 0f ) {
+        result = Math.round( value );
+      }
+    }
+    return result;
+  }
+
+  public static QxBorder readBorder( final LexicalUnit unit ) {
+    QxBorder result = null;
+    QxColor color = null;
+    String style = null;
+    int width = -1;
+    LexicalUnit nextUnit = unit;
+    boolean consumed = false;
+    while( nextUnit != null ) {
+      consumed = false;
+      if( !consumed && width == -1 ) {
+        width = readBorderWidth( nextUnit );
+        consumed |= width != -1;
+      }
+      if( !consumed && style == null ) {
+        style = readBorderStyle( nextUnit );
+        consumed |= style != null;
+      }
+      if( !consumed && color == null ) {
+        color = readColor( nextUnit );
+        consumed |= color != null;
+      }
+      nextUnit = consumed ? nextUnit.getNextLexicalUnit() : null;
+    }
+    if( consumed ) {
+      // TODO [rst] create should take a QxColor
+      result = QxBorder.create( width == -1 ? 0 : width,
+                                style,
+                                color != null ? color.toDefaultString() : null );
+    }
+    return result;
+  }
+
+  public static String readFontStyle( final LexicalUnit unit ) {
+    String result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_IDENT ) {
+      String value = unit.getStringValue();
+      if( NORMAL.equals( value ) ) {
+        result = value;
+      } else if( ITALIC.equals( value ) ) {
+        result = value;
+      }
+    }
+    return result;
+  }
+
+  public static String readFontWeight( final LexicalUnit unit ) {
+    String result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_IDENT ) {
+      String value = unit.getStringValue();
+      if( NORMAL.equals( value ) ) {
+        result = value;
+      } else if( BOLD.equals( value ) ) {
+        result = value;
+      }
+    }
+    return result;
+  }
+
+  public static int readFontSize( final LexicalUnit unit ) {
+    int result = -1;
+    Integer length = readSingleLengthUnit( unit );
+    if( length != null ) {
+      int value = length.intValue();
+      if( value >= 0 ) {
+        result = value;
+      }
+    }
+    return result;
+  }
+
+  public static String[] readFontFamily( final LexicalUnit unit ) {
+    List list = new ArrayList();
+    LexicalUnit nextUnit = unit;
+    boolean ok = true;
+    String buffer = "";
+    while( nextUnit != null && ok ) {
+      short type = nextUnit.getLexicalUnitType();
+      if( type == LexicalUnit.SAC_STRING_VALUE
+          || type == LexicalUnit.SAC_IDENT )
+      {
+        if( buffer.length() > 0 ) {
+          buffer += " ";
+        }
+        buffer += nextUnit.getStringValue();
+      } else if( type == LexicalUnit.SAC_OPERATOR_COMMA ) {
+        if( buffer.length() > 0 ) {
+          list.add( buffer );
+        } else {
+          ok = false;
+        }
+        buffer = "";
+      }
+      nextUnit = nextUnit.getNextLexicalUnit();
+    }
+    String[] result = null;
+    if( buffer.length() > 0 ) {
+      list.add( buffer );
+      result = new String[ list.size() ];
+      list.toArray( result );
+    }
+    return result;
+  }
+
+  // The format of a URI value is 'url(' followed by optional whitespace
+  // followed by an optional single quote (') or double quote (") character
+  // followed by the URI itself, followed by an optional single quote (') or
+  // double quote (") character followed by optional whitespace followed by ')'.
+  // The two quote characters must be the same.
+
+  public static QxFont readFont( final LexicalUnit unit ) {
+    QxFont result = null;
+    String[] family = null;
+    String style = null;
+    String weight = null;
+    int size = -1;
+    boolean consumed = false;
+    boolean consumedSize = false;
+    boolean consumedFamily = false;
+    LexicalUnit nextUnit = unit;
+    while( nextUnit != null && !consumedFamily ) {
+      consumed = false;
+      if( !consumed && !consumedSize && style == null ) {
+        style = readFontStyle( nextUnit );
+        consumed |= style != null;
+      }
+      if( !consumed && !consumedSize && weight == null ) {
+        weight = readFontWeight( nextUnit );
+        consumed |= weight != null;
+      }
+      if( !consumed && !consumedFamily && size == -1 ) {
+        size = readFontSize( nextUnit );
+        consumedSize = size != -1;
+        consumed |= consumedSize;
+      }
+      if( !consumed && consumedSize && family == null ) {
+        family = readFontFamily( nextUnit );
+        consumedFamily = family != null;
+        consumed |= consumedFamily;
+      }
+      nextUnit = consumed ? nextUnit.getNextLexicalUnit() : null;
+    }
+    if( consumed && consumedSize && consumedFamily ) {
+      boolean bold = BOLD.equals( weight );
+      boolean italic = ITALIC.equals( style );
+      result = QxFont.create( family, size, bold, italic );
+    }
+    return result;
+  }
+
+  public static String readUrl( final LexicalUnit unit ) {
+    String result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_URI ) {
+      result = unit.getStringValue();
+    }
+    return result;
+  }
+
+  public static QxImage readBackgroundImage( final LexicalUnit unit,
+                                             final ResourceLoader loader ) {
+    QxImage result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_URI ) {
+      String value = unit.getStringValue();
+      result = QxImage.valueOf( value, loader );
+    } else if( type == LexicalUnit.SAC_IDENT ) {
+      String value = unit.getStringValue();
+      if( NONE.equals( value ) ) {
+        result = QxImage.NONE;
+      }
+    }
+    return result;
+  }
+
+  private static Integer readSingleLengthUnit( final LexicalUnit unit ) {
+    Integer result = null;
+    short type = unit.getLexicalUnitType();
+    if( type == LexicalUnit.SAC_PIXEL ) {
+      result = new Integer( ( int )unit.getFloatValue() );
+    }
+    return result;
+  }
+
+  private static int normalizeRGBValue( final int input ) {
+    int result = input;
+    if( input < 0 ) {
+      result = 0;
+    } else if( input > 255 ) {
+      result = 255;
+    }
+    return result;
+  }
+
+  private static float normalizePercentValue( final float input ) {
+    float result = input;
+    if( input < 0f ) {
+      result = 0f;
+    } else if( input > 100f ) {
+      result = 100f;
+    }
+    return result;
+  }
+  
+  /*
+   * BEGIN Modification for Theme Editor
+   * Returns an QxType out of a LexicalUnit.
+   */
+  public static QxType getQxType( final String cssProperty,
+                                  final LexicalUnit unit )
+  {
+    QxType result = null;
+    if( unit != null ) {
+//      IPropertyWrapper wrapper = PropertyWrapperFactory.createPropertyWrapper( cssProperty,
+//                                                                               unit,
+//                                                                               null );
+//      if ( wrapper != null ) {
+//        result = wrapper.getQxType();
+//      }
+    }
+    return result;
+  }
+
+  /*
+   * Returns the color value of a border unit.
+   */
+  public static QxColor readBorderColor( final LexicalUnit unit ) {
+    QxColor result = null;
+    String style = null;
+    int width = -1;
+    LexicalUnit nextUnit = unit;
+    boolean consumed = false;
+    while( nextUnit != null ) {
+      consumed = false;
+      if( !consumed && width == -1 ) {
+        width = readBorderWidth( nextUnit );
+        consumed |= width != -1;
+      }
+      if( !consumed && style == null ) {
+        style = readBorderStyle( nextUnit );
+        consumed |= style != null;
+      }
+      if( !consumed && result == null ) {
+        result = readColor( nextUnit );
+        consumed |= result != null;
+      }
+      nextUnit = consumed
+                         ? nextUnit.getNextLexicalUnit()
+                         : null;
+    }
+    return result;
+  }
+  /*
+   * END Modification for Theme Editor
+   */
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PseudoClassConditionImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PseudoClassConditionImpl.java
new file mode 100644
index 0000000..86a4fe7
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/PseudoClassConditionImpl.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.AttributeCondition;
+
+
+public class PseudoClassConditionImpl
+  implements AttributeCondition, ConditionExt
+{
+
+  private final String value;
+
+  public PseudoClassConditionImpl( final String value ) {
+    this.value = value;
+  }
+
+  public String getLocalName() {
+    return null;
+  }
+
+  public String getNamespaceURI() {
+    return null;
+  }
+
+  public boolean getSpecified() {
+    return false;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public short getConditionType() {
+    return SAC_PSEUDO_CLASS_CONDITION;
+  }
+
+  public boolean matches( final Element element ) {
+    return element.hasPseudoClass( value );
+  }
+
+  public int getSpecificity() {
+    return ATTR_SPEC;
+  }
+
+  public String[] getClasses() {
+    return null;
+  }
+
+  public String toString() {
+    return ":" + value;
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorExt.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorExt.java
new file mode 100644
index 0000000..8506a43
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorExt.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+
+public interface SelectorExt extends ElementMatcher, Specific {
+
+  abstract String getElementName();
+
+  abstract String[] getClasses();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorFactoryImpl.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorFactoryImpl.java
new file mode 100644
index 0000000..6e28530
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/SelectorFactoryImpl.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import org.w3c.css.sac.*;
+
+/**
+ * SelectorFacory implementation for parsing RAP theme files. All returned
+ * selectors implement the interface {@link SelectorExt}.
+ */
+public class SelectorFactoryImpl implements SelectorFactory {
+
+  private final CssFileReader reader;
+
+  public SelectorFactoryImpl( final CssFileReader reader ) {
+    this.reader = reader;
+  }
+
+  public ElementSelector createElementSelector( final String namespaceURI,
+                                                final String tagName )
+    throws CSSException
+  {
+    return new ElementSelectorImpl( tagName );
+  }
+
+  public ConditionalSelector createConditionalSelector( final SimpleSelector selector,
+                                                        final Condition condition )
+    throws CSSException
+  {
+    return new ConditionalSelectorImpl( selector, condition );
+  }
+
+  // ==========================================================================
+  // Not supported by RAP, but used by Theme Editor
+  
+  public DescendantSelector createChildSelector( final Selector parent,
+                                                 final SimpleSelector child )
+  throws CSSException
+  {
+//    return new ChildSelectorImpl( parent, child );
+    String mesg = "Child selectors not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    /* BEGIN Modification for Theme Editor */
+    return new EmptyChildSelectorImpl( parent, child );
+    /* END Modification for Theme Editor */
+  }
+
+  public ElementSelector createPseudoElementSelector( final String namespaceURI,
+                                                      final String pseudoName )
+    throws CSSException
+  {
+    String mesg = "Pseudo element selectors not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    /* BEGIN Modification for Theme Editor */
+    return new EmptyElementSelectorImpl( pseudoName );
+    /* END Modification for Theme Editor */
+  }
+
+  public DescendantSelector createDescendantSelector( final Selector parent,
+                                                      final SimpleSelector descendant )
+    throws CSSException
+  {
+    String mesg = "Descendant selectors not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    /* BEGIN Modification for Theme Editor */
+    return new EmptyDescendantSelectorImpl( parent, descendant );
+    /* END Modification for Theme Editor */
+  }
+
+  public SiblingSelector createDirectAdjacentSelector( final short nodeType,
+                                                       final Selector child,
+                                                       final SimpleSelector directAdjacent )
+    throws CSSException
+  {
+    String mesg = "Sibling selectors not supported by RAP - ignored";
+    reader.addProblem( new CSSException( mesg ) );
+    /* BEGIN Modification for Theme Editor */
+    return new EmptySiblingSelectorImpl( nodeType, child, directAdjacent );
+    /* END Modification for Theme Editor */
+  }
+
+  // ==========================================================================
+  // Not implemented in CSS 2
+
+  public SimpleSelector createRootNodeSelector() throws CSSException {
+    throw new CSSException( "Root node selectors not supported by CSS2" );
+  }
+
+  public CharacterDataSelector createTextNodeSelector( final String data )
+    throws CSSException
+  {
+    throw new CSSException( "Text node selectors not supported by CSS2" );
+  }
+
+  public CharacterDataSelector createCDataSectionSelector( final String data )
+    throws CSSException
+  {
+    throw new CSSException( "CData section selectors not supported by CSS2" );
+  }
+
+  public ProcessingInstructionSelector createProcessingInstructionSelector( final String target,
+                                                                            final String data )
+    throws CSSException
+  {
+    throw new CSSException( "Processing instruction selectors not supported by CSS2" );
+  }
+
+  public CharacterDataSelector createCommentSelector( final String data )
+    throws CSSException
+  {
+    throw new CSSException( "Comment selectors not supported by CSS2" );
+  }
+
+  public SimpleSelector createAnyNodeSelector() throws CSSException {
+    throw new CSSException( "Any-node selectors not supported by CSS2" );
+  }
+
+  public NegativeSelector createNegativeSelector( final SimpleSelector selector )
+    throws CSSException
+  {
+    throw new CSSException( "Negative selectors not supported by CSS2" );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Specific.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Specific.java
new file mode 100644
index 0000000..e4f4998
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/Specific.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+/**
+ * See {@link http://www.w3.org/TR/CSS21/cascade.html#specificity}
+ */
+public interface Specific {
+
+  /**
+   * Factor for b variable in specificity algorithm, see <a
+   * href="http://www.w3.org/TR/CSS21/cascade.html#specificity">http://www.w3.org/TR/CSS21/cascade.html#specificity</a>.
+   */
+  public static int ID_SPEC = 16 << 1;
+
+  /**
+   * Factor for c variable in specificity algorithm, see <a
+   * href="http://www.w3.org/TR/CSS21/cascade.html#specificity">http://www.w3.org/TR/CSS21/cascade.html#specificity</a>.
+   */
+  public static int ATTR_SPEC = 8 << 1;
+
+  /**
+   * Factor for d variable in specificity algorithm, see <a
+   * href="http://www.w3.org/TR/CSS21/cascade.html#specificity">http://www.w3.org/TR/CSS21/cascade.html#specificity</a>.
+   */
+  public static int ELEMENT_SPEC = 1;
+
+  abstract int getSpecificity();
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylableElement.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylableElement.java
new file mode 100644
index 0000000..c7409c3
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylableElement.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.*;
+
+
+public class StylableElement implements Element {
+
+  private Map attributes = new HashMap();
+
+  private List classes = new ArrayList();
+
+  private List pseudoClasses = new ArrayList();
+
+  private String name;
+
+  private Element parent;
+
+  public StylableElement( final String name ) {
+    this( null, name );
+  }
+
+  public StylableElement( final Element parent, final String name ) {
+    this.name = name;
+    this.parent = parent;
+  }
+
+  public boolean hasName( final String value ) {
+    return name == null || name.equals( value );
+  }
+
+  public Element getParent() {
+    return parent;
+  }
+
+  public boolean hasClass( final String value ) {
+    return value != null && classes.contains( value );
+  }
+
+  public boolean hasPseudoClass( final String value ) {
+    return value != null && pseudoClasses.contains( value );
+  }
+
+  public boolean hasAttribute( final String name ) {
+    String value = ( String )attributes.get( name );
+    return value != null && value.length() > 0;
+  }
+
+  public String getAttribute( final String name ) {
+    return ( String )attributes.get( name );
+  }
+
+  public void setClass( final String className ) {
+    if( className != null ) {
+      classes.add( className );
+    }
+  }
+
+  public void resetClass( final String className ) {
+    if( className != null && classes.contains( className ) ) {
+      classes.remove( className );
+    }
+  }
+
+  public void setPseudoClass( final String pseudoName ) {
+    if( pseudoName != null ) {
+      pseudoClasses.add( pseudoName );
+    }
+  }
+
+  public void resetPseudoClass( final String pseudoName ) {
+    if( pseudoName != null && pseudoClasses.contains( pseudoName ) ) {
+      pseudoClasses.remove( pseudoName );
+    }
+  }
+
+  public void setAttribute( final String name, final String value ) {
+    if( name != null ) {
+      if( value != null ) {
+        attributes.put( name, value );
+      } else {
+        attributes.remove( name );
+      }
+    }
+  }
+
+  public void setAttribute( final String name ) {
+    setAttribute( name, "true" );
+  }
+
+  public void resetAttribute( final String name ) {
+    setAttribute( name, null );
+  }
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylePropertyMap.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylePropertyMap.java
new file mode 100644
index 0000000..d9f9e06
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StylePropertyMap.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.*;
+
+import org.eclipse.rwt.internal.theme.QxType;
+import org.w3c.css.sac.LexicalUnit;
+
+public class StylePropertyMap {
+
+  private final Map properties = new HashMap();
+
+  /* BEGIN Modification for Theme Editor */
+  private final Map lineNumbers = new HashMap();
+  private final Map propertyStrings = new HashMap();
+  /* END Modification for Theme Editor */
+
+  public LexicalUnit getProperty( final String key ) {
+    return ( LexicalUnit )properties.get( key );
+  }
+
+  public void setProperty( final String key, final LexicalUnit value ) {
+    if( key == null || value == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    properties.put( key, value );
+    /* BEGIN Modification for Theme Editor */
+    QxType qxType = PropertyResolver.getQxType( key, value );
+    if( qxType != null ) {
+      setPropertyString( key, qxType.toDefaultString() );
+    }
+    /* END Modification for Theme Editor */
+  }
+
+  public String[] getKeys() {
+    Set keySet = properties.keySet();
+    String[] result = new String[ keySet.size() ];
+    keySet.toArray( result );
+    return result;
+  }
+
+  public void merge( final StylePropertyMap styles ) {
+    String[] keys = styles.getKeys();
+    for( int i = 0; i < keys.length; i++ ) {
+      String key = keys[ i ];
+      properties.put( key, styles.getProperty( key ) );
+      propertyStrings.put( key, styles.getPropertyString( key ) );
+    }
+  }
+
+  /*
+   * BEGIN Modification for Theme Editor
+   */
+  public void removeProperty( final String key ) {
+    if( key != null ) {
+      properties.remove( key );
+      propertyStrings.remove( key );
+    }
+  }
+
+  public int size() {
+    return properties.size();
+  }
+
+  public int getLineNumber( final String key ) {
+    int result = -1;
+    Integer value = ( Integer )lineNumbers.get( key );
+    if( value != null ) {
+      result = value.intValue();
+    }
+    return result;
+  }
+
+  public void setLineNumber( final String key, final int lineNumber ) {
+    if( key != null ) {
+      lineNumbers.put( key, new Integer( lineNumber ) );
+    }
+  }
+
+  public String getPropertyString( final String key ) {
+    return ( String )propertyStrings.get( key );
+  }
+
+  public void setPropertyString( final String key, final String value ) {
+    if( key == null || value == null ) {
+      throw new NullPointerException( "null argument" );
+    }
+    if ( properties.containsKey( key ) ) {
+      propertyStrings.put( key, value );
+    }
+  }
+  /*
+   * END Modification for Theme Editor
+   */
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleRule.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleRule.java
new file mode 100644
index 0000000..6c0eb7b
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleRule.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SelectorList;
+
+
+public class StyleRule implements ElementMatcher {
+
+  private final SelectorList selectors;
+
+  private final StylePropertyMap properties;
+
+  /* BEGIN Modification for Theme Editor */
+  private List comments;
+  private int namePosition;
+  private String name;
+  private int lineNumber = -1;
+  /* END Modification for Theme Editor */
+
+  public StyleRule( final SelectorList selectors,
+                    final StylePropertyMap properties )
+  {
+    this.selectors = selectors;
+    this.properties = properties;
+  /* BEGIN Modification for Theme Editor */
+  comments = new ArrayList();
+  /* END Modification for Theme Editor */
+  }
+
+  public SelectorList getSelectors() {
+    return selectors;
+  }
+
+  public StylePropertyMap getProperties() {
+    return properties;
+  }
+
+  public Selector getMatchingSelector( final Element element ) {
+    Selector result = null;
+    int maxSpecificity = -1;
+    int length = selectors.getLength();
+    for( int i = 0; i < length; i++ ) {
+      Selector selector = selectors.item( i );
+      ElementMatcher matcher = ( ElementMatcher )selector;
+      if( matcher.matches( element ) ) {
+        int specificity = ( ( Specific )selector ).getSpecificity();
+        if( specificity > maxSpecificity  ) {
+          result = selector;
+          maxSpecificity = specificity;
+        }
+      }
+    }
+    return result;
+  }
+
+  public boolean matches( final Element element ) {
+    return getMatchingSelector( element ) != null;
+  }
+
+  public String getSelectorText() {
+    StringBuffer buffer = new StringBuffer();
+    int length = selectors.getLength();
+    for( int i = 0; i < length; i++ ) {
+      Selector selector = selectors.item( i );
+      if( i > 0 ) {
+        buffer.append( ", " );
+      }
+      buffer.append( selector.toString() );
+    }
+    return buffer.toString();
+  }
+
+  /*
+   * BEGIN Modification for Theme Editor
+   */
+  public List getComments() {
+    return comments;
+  }
+
+  public void setComments( final List comments ) {
+    if ( comments != null ) {
+      this.comments = comments;
+      parseComments();
+    }
+  }
+
+  private void parseComments() {
+    for( int i = 0; i < comments.size(); i++ ) {
+      String comment = ( ( String )comments.get( i ) ).trim();
+      if( comment.startsWith( "@name " ) ) {
+        name = comment.substring( 6 );
+        namePosition = i;
+      }
+    }
+  }
+
+  public String getName() {
+    if( name == null ) {
+      return getSelectorText();
+    } else {
+      return name;
+    }
+  }
+
+  public void setName( final String name ) {
+    if( this.name == null ) {
+      comments.add( " @name " + name + " " );
+      namePosition = comments.size() - 1;
+    } else {
+      comments.set( namePosition, " @name " + name + " " );
+    }
+    this.name = name;
+  }
+
+  public int getLineNumber() {
+    return lineNumber;
+  }
+
+  public void setLineNumber( int lineNumber ) {
+    this.lineNumber = lineNumber;
+  }
+  /*
+   * END Modification for Theme Editor
+   */
+}
diff --git a/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleSheet.java b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleSheet.java
new file mode 100644
index 0000000..b5b9b77
--- /dev/null
+++ b/bundles/org.eclipse.rap.themeeditor/src/org/eclipse/rwt/internal/theme/css/StyleSheet.java
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Innoopract Informationssysteme GmbH.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Innoopract Informationssysteme GmbH - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.rwt.internal.theme.css;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.rwt.internal.theme.QxBorder;
+import org.eclipse.rwt.internal.theme.QxBoxDimensions;
+import org.eclipse.rwt.internal.theme.QxColor;
+import org.eclipse.rwt.internal.theme.QxDimension;
+import org.eclipse.rwt.internal.theme.QxFont;
+import org.eclipse.rwt.internal.theme.QxImage;
+import org.eclipse.rwt.internal.theme.QxType;
+import org.eclipse.rwt.internal.theme.ResourceLoader;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SelectorList;
+
+
+public class StyleSheet {
+
+
+  private static final MatchedStyleRuleComparator COMPARATOR
+    = new MatchedStyleRuleComparator();
+
+  private final StyleRule[] styleRules;
+
+  private String[] variants;
+  
+  private String headerComment;
+
+  public StyleSheet( final StyleRule[] styleRules ) {
+    this.styleRules = styleRules;
+    findClasses();
+  }
+
+  public StyleRule[] getStyleRules() {
+    return styleRules;
+  }
+
+  public StyleRule[] getMatchingStyleRules( final Element element ) {
+    List buffer = new ArrayList();
+    for( int i = 0; i < styleRules.length; i++ ) {
+      StyleRule rule = styleRules[ i ];
+      Selector selector = rule.getMatchingSelector( element );
+      if( selector != null ) {
+        int specificity = ( ( Specific )selector ).getSpecificity();
+        buffer.add( new MatchedStyleRule( rule, specificity, i ) );
+      }
+    }
+    Collections.sort( buffer, COMPARATOR );
+    StyleRule[] result = new StyleRule[ buffer.size() ];
+    for( int i = 0; i < result.length; i++ ) {
+      result[ i ] = ( ( MatchedStyleRule )buffer.get( i ) ).rule;
+    }
+    return result;
+  }
+
+  public QxType getValue( final String cssProperty,
+                          final StylableElement element,
+                          final ResourceLoader loader )
+  {
+    QxType result = null;
+    StyleRule[] rules = getMatchingStyleRules( element );
+    for( int i = 0; i < rules.length; i++ ) {
+      StyleRule rule = rules[ i ];
+      StylePropertyMap properties = rule.getProperties();
+      LexicalUnit property = properties.getProperty( cssProperty );
+      if( property != null ) {
+        if( "color".equals( cssProperty ) ) {
+          result = readColor( properties );
+        } else if( "background-color".equals( cssProperty ) ) {
+          result = readBackgroundColor( properties );
+        } else if( "background-image".equals( cssProperty ) ) {
+          result = readBackgroundImage( properties, loader );
+        } else if( "border".equals( cssProperty ) ) {
+          result = readBorder( properties );
+        } else if( "padding".equals( cssProperty ) ) {
+          result = readPadding( properties );
+        } else if( "margin".equals( cssProperty ) ) {
+          result = readMargin( properties );
+        } else if( "spacing".equals( cssProperty ) ) {
+          result = readSpacing( properties );
+        } else if( "height".equals( cssProperty ) ) {
+          result = readHeight( properties );
+        } else if( "width".equals( cssProperty ) ) {
+          result = readWidth( properties );
+        } else if( "font".equals( cssProperty ) ) {
+          result = readFont( properties );
+        } else if( cssProperty.startsWith( "rwt" )
+                   && cssProperty.endsWith( "color" ) )
+        {
+          result = readColor( properties, cssProperty );
+        } else if( "background-gradient-color".equals( cssProperty ) ) {
+          result = readBackgroundColor( properties, cssProperty );
+        } else {
+//          TODO [rst] Logging instead of sysout
+          System.err.println( "unsupported css property: " + cssProperty );
+        }
+      }
+    }
+    return result;
+  }
+
+  public String[] getVariants( final String elementName ) {
+    return variants;
+  }
+
+  private void findClasses() {
+    List list = new ArrayList();
+    for( int i = 0; i < styleRules.length; i++ ) {
+      StyleRule rule = styleRules[ i ];
+      SelectorList selectors = rule.getSelectors();
+      int length = selectors.getLength();
+      for( int j = 0; j < length; j++ ) {
+        SelectorExt selector = ( SelectorExt )selectors.item( j );
+        String[] classes = selector.getClasses();
+        if( classes != null ) {
+          for( int k = 0; k < classes.length; k++ ) {
+            String variant = classes[ k ];
+            if( !list.contains( variant ) ) {
+              list.add( variant );
+//              System.out.println( "found variant: " + variant );
+            }
+          }
+        }
+      }
+    }
+    variants = new String[ list.size() ];
+    list.toArray( variants );
+  }
+
+  private static QxColor readColor( final StylePropertyMap properties ) {
+    return readColor( properties, "color" );
+  }
+
+  private static QxColor readColor( final StylePropertyMap properties,
+                             final String property )
+  {
+    LexicalUnit unit = properties.getProperty( property );
+    QxColor result = PropertyResolver.readColor( unit );
+    return result != QxColor.TRANSPARENT ? result : null;
+  }
+
+  private static QxColor readBackgroundColor( final StylePropertyMap properties )
+  {
+    return readBackgroundColor( properties, "background-color" );
+  }
+  
+  private static QxColor readBackgroundColor( final StylePropertyMap properties,
+                                              final String property )
+  {
+    LexicalUnit unit = properties.getProperty( property );
+    return PropertyResolver.readColor( unit );
+  }
+
+  private QxFont readFont( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "font" );
+    return PropertyResolver.readFont( property );
+  }
+
+  private QxImage readBackgroundImage( final StylePropertyMap properties,
+                                       final ResourceLoader loader ) {
+    LexicalUnit property = properties.getProperty( "background-image" );
+    return PropertyResolver.readBackgroundImage( property, loader );
+  }
+
+  private QxBorder readBorder( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "border" );
+    return PropertyResolver.readBorder( property );
+  }
+
+  private QxBoxDimensions readPadding( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "padding" );
+    return PropertyResolver.readBoxDimensions( property );
+  }
+
+  private QxBoxDimensions readMargin( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "margin" );
+    return PropertyResolver.readBoxDimensions( property );
+  }
+
+  private QxDimension readSpacing( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "spacing" );
+    return PropertyResolver.readDimension( property );
+  }
+
+  private QxDimension readHeight( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "height" );
+    return PropertyResolver.readDimension( property );
+  }
+  
+  private QxDimension readWidth( final StylePropertyMap properties ) {
+    LexicalUnit property = properties.getProperty( "width" );
+    return PropertyResolver.readDimension( property );
+  }
+
+  static class MatchedStyleRuleComparator implements Comparator {
+
+    public int compare( final Object object1, final Object object2 ) {
+      MatchedStyleRule rule1 = ( MatchedStyleRule )object1;
+      MatchedStyleRule rule2 = ( MatchedStyleRule )object2;
+      int result = 0;
+      if( rule1.specificity > rule2.specificity ) {
+        result = 1;
+      } else if( rule1.specificity < rule2.specificity ) {
+        result = -1;
+      } else if( rule1.position > rule2.position ) {
+        result = 1;
+      } else if( rule1.position < rule2.position ) {
+        result = -1;
+      }
+      return result;
+    }
+  }
+
+  static class MatchedStyleRule {
+
+    public final StyleRule rule;
+
+    public final int specificity;
+
+    public final int position;
+
+    public MatchedStyleRule( final StyleRule rule,
+                             final int specificity,
+                             final int position )
+    {
+      this.rule = rule;
+      this.specificity = specificity;
+      this.position = position;
+    }
+  }
+
+  /*
+   * BEGIN Modification for Theme Editor
+   */
+  public void setHeaderComment( final String headerComment ) {
+    this.headerComment = headerComment;
+  }
+
+  public String getHeaderComment() {
+    return headerComment;
+  }
+
+  public int getStyleRulePoition( StyleRule rule ) {
+    int result = -1;
+    for( int i = 0; i < styleRules.length; i++ ) {
+      if( styleRules[ i ] == rule ) {
+        result = i;
+      }
+    }
+    return result;
+  }
+
+//  public IPropertyWrapper getValueWrapper( final String cssProperty,
+//                                           final StylableElement element )
+//  {
+//    IPropertyWrapper result = null;
+//    StyleRule[] rules = getMatchingStyleRules( element );
+//    for( int i = 0; i < rules.length; i++ ) {
+//      StyleRule rule = rules[ i ];
+//      StylePropertyMap properties = rule.getProperties();
+//      LexicalUnit property = properties.getProperty( cssProperty );
+//      if( property != null ) {
+//        result = PropertyWrapperFactory.createPropertyWrapper( cssProperty,
+//                                                               property,
+//                                                               rule );
+//      }
+//    }
+//    if ( result == null ) {
+//      result = PropertyWrapperFactory.createPropertyWrapper( cssProperty );
+//    }
+//    return result;
+//  }
+
+  public String[] getVariants() {
+    return variants;
+  }
+  /*
+   * END Modification for Theme Editor
+   */
+}