add CSS core styles

The core styles are used to define build in styles for elements like
processing instructions, that need a stylesheet to be displayed
correctly. The core styles have a lower priority the plugin provided
stylesheets.

Change-Id: I08d85d57e9e10941403d0c1df2a32df60020817a
Signed-off-by: Carsten Hiesserich <carsten.hie@gmail.com>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/CssTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/CssTest.java
index 3bb1741..25b7850 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/CssTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/CssTest.java
@@ -20,8 +20,10 @@
 import org.eclipse.vex.core.internal.core.DisplayDevice;
 import org.eclipse.vex.core.internal.dom.Document;
 import org.eclipse.vex.core.internal.dom.Element;
+import org.eclipse.vex.core.internal.dom.ProcessingInstruction;
 import org.eclipse.vex.core.provisional.dom.IDocument;
 import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -542,6 +544,24 @@
 
 	}
 
+	@Test
+	public void testCoreStyles() throws Exception {
+		final StyleSheet styleSheet = StyleSheet.NULL;
+		final INode pi = new ProcessingInstruction("target");
+		final Styles styles = styleSheet.getStyles(pi);
+
+		assertEquals(new Color(0, 0, 255), styles.get("color"));
+	}
+
+	@Test
+	public void testCoreStylesOverrule() throws Exception {
+		final StyleSheet styleSheet = parseStyleSheetResource("testCoreStylesOverrule.css");
+		final INode pi = new ProcessingInstruction("target");
+		final Styles styles = styleSheet.getStyles(pi);
+
+		assertEquals(new Color(0x01, 0x23, 0x45), styles.get("color"));
+	}
+
 	private StyleSheet parseStyleSheetResource(final String resource) throws java.io.IOException {
 
 		final URL url = this.getClass().getResource(resource);
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/testCoreStylesOverrule.css b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/testCoreStylesOverrule.css
new file mode 100644
index 0000000..9a444ac
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/css/testCoreStylesOverrule.css
@@ -0,0 +1,6 @@
+/* Stylesheet for unit testing */
+
+vex|processing-instruction {
+  color: #012345;
+}
+
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheet.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheet.java
index 0312c95..b106d28 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheet.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheet.java
@@ -18,9 +18,11 @@
  *                          WeekReference. PseudoElements are cached.
  *     Carsten Hiesserich - Added OutlineContent property
  *     Carsten Hiesserich - New handling for pseudo elements
+ *     Carsten Hiesserich - Added core styles
  *******************************************************************************/
 package org.eclipse.vex.core.internal.css;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -88,6 +90,21 @@
 	private final List<Rule> rules;
 
 	/**
+	 * The VEX core styles
+	 */
+	private final static List<Rule> coreRules;
+	static {
+		List<Rule> rules;
+		try {
+			rules = new StyleSheetReader().readRules(StyleSheet.class.getResource("vex-core-styles.css"));
+		} catch (final IOException e) {
+			rules = Collections.<Rule> emptyList();
+			e.printStackTrace();
+		}
+		coreRules = rules;
+	}
+
+	/**
 	 * Computing styles can be expensive, e.g. we have to calculate the styles of all parents of an element. We
 	 * therefore cache styles in a map of element => styles. We use a WeakHashMap here that does not prevent the INode's
 	 * from being GC'ed. Note that the entries of the Map are only collected, when one of the Maps method is called the
@@ -276,12 +293,17 @@
 	 *         for pseudo elements.
 	 */
 	private Map<String, Map<String, LexicalUnit>> getApplicableDeclarations(final INode node) {
-		final List<PropertyDecl> rawDeclarationsForElement = findAllDeclarationsFor(node);
+		final List<PropertyDecl> coreDeclarationsForElement = findCoreDeclarationsFor(node);
+		Collections.sort(coreDeclarationsForElement, PROPERTY_CASCADE_ORDERING);
 
-		// Sort in cascade order. We can then just stuff them into a
-		// map and get the right values since higher-priority values
-		// come later and overwrite lower-priority ones.
-		Collections.sort(rawDeclarationsForElement, PROPERTY_CASCADE_ORDERING);
+		final List<PropertyDecl> stylesheetDeclarationsForElement = findAllDeclarationsFor(node);
+		Collections.sort(stylesheetDeclarationsForElement, PROPERTY_CASCADE_ORDERING);
+
+		// Both lists are sorted in cascade order. We can then just stuff them into a map and get the right values
+		// since higher-priority values come later and overwrite lower-priority ones.
+		// Core styles are at the list's begin, so they are ruled out by stylesheet definitions.
+		final List<PropertyDecl> rawDeclarationsForElement = coreDeclarationsForElement;
+		rawDeclarationsForElement.addAll(stylesheetDeclarationsForElement);
 
 		final Map<String, Map<String, PropertyDecl>> distilledDeclarations = new HashMap<String, Map<String, PropertyDecl>>();
 		final Map<String, Map<String, LexicalUnit>> values = new HashMap<String, Map<String, LexicalUnit>>();
@@ -319,6 +341,7 @@
 
 	private List<PropertyDecl> findAllDeclarationsFor(final INode node) {
 		final List<PropertyDecl> rawDeclarations = new ArrayList<PropertyDecl>();
+
 		for (final Rule rule : rules) {
 			if (rule.matches(node)) {
 				final PropertyDecl[] ruleDecls = rule.getPropertyDecls();
@@ -330,6 +353,20 @@
 		return rawDeclarations;
 	}
 
+	private List<PropertyDecl> findCoreDeclarationsFor(final INode node) {
+		final List<PropertyDecl> rawDeclarations = new ArrayList<PropertyDecl>();
+
+		for (final Rule rule : coreRules) {
+			if (rule.matches(node)) {
+				final PropertyDecl[] ruleDecls = rule.getPropertyDecls();
+				for (final PropertyDecl ruleDecl : ruleDecls) {
+					rawDeclarations.add(ruleDecl);
+				}
+			}
+		}
+		return rawDeclarations;
+	}
+
 	/**
 	 * This method is only public to be available for unit testing. It is not meant to be used in an implementation.
 	 * 
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheetReader.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheetReader.java
index 1d8b515..bd72c57 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheetReader.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/StyleSheetReader.java
@@ -139,12 +139,38 @@
 	 *            which case @import rules are ignored.
 	 */
 	public StyleSheet read(final InputSource inputSource, final URL url) throws CSSException, IOException {
+		final List<Rule> rules = readRules(inputSource, url);
+		return new StyleSheet(rules);
+	}
+
+	/**
+	 * Parse a stylesheet file from a URL and return the list of rules.
+	 * 
+	 * @param url
+	 *            URL from which to read the style sheet.
+	 * @return The List of rules.
+	 */
+	public List<Rule> readRules(final URL url) throws IOException {
+		return this.readRules(new InputSource(url.toString()), url);
+	}
+
+	/**
+	 * Parse a stylesheet file from an input source and return the list of rules.
+	 * 
+	 * @param inputSource
+	 *            InputSource from which to read the stylesheet.
+	 * @param url
+	 *            URL representing the input source, used to resolve @import rules with relative URIs. May be null, in
+	 *            which case @import rules are ignored.
+	 * @return The List of rules.
+	 */
+	public List<Rule> readRules(final InputSource inputSource, final URL url) throws CSSException, IOException {
 		final Parser parser = createParser();
 		final List<Rule> rules = new ArrayList<Rule>();
 		final StyleSheetBuilder styleSheetBuilder = new StyleSheetBuilder(rules, url);
 		parser.setDocumentHandler(styleSheetBuilder);
 		parser.parseStyleSheet(inputSource);
-		return new StyleSheet(rules);
+		return rules;
 	}
 
 	// ======================================================== PRIVATE
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css
new file mode 100644
index 0000000..cfb5e37
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ * 		Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+
+/*
+This file defines a set of core styles that are always added when creating a StyleSheet.
+*/
+
+vex|processing-instruction {
+	display: inherit;
+	color: blue;
+}
+
+vex|processing-instruction:before {
+	display: inline;
+	color: navy;
+	content: '<?',attr(target),' ';
+}
+
+vex|processing-instruction:after {
+	display: inline;
+	color: navy;
+	content: '?>';
+}
\ No newline at end of file
diff --git a/org.eclipse.vex.docbook/styles/docbook-plain.css b/org.eclipse.vex.docbook/styles/docbook-plain.css
index 3bd78eb..5540054 100644
--- a/org.eclipse.vex.docbook/styles/docbook-plain.css
+++ b/org.eclipse.vex.docbook/styles/docbook-plain.css
@@ -13,23 +13,6 @@
 
 @import url(vex-outline.css);
 
-vex|processing-instruction {
-	display: inherit;
-	color: blue;
-}
-
-vex|processing-instruction:before {
-	display: inline;
-	color: navy;
-	content: '<?',attr(target),' ';
-}
-
-vex|processing-instruction:after {
-	display: inline;
-	color: navy;
-	content: '?>';
-}
-
 abbrev {
 	display: inline;
 }