rename bundles, projects and adopt the poms
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/.classpath b/org.eclipse.vex.core/.classpath
new file mode 100644
index 0000000..fe28682
--- /dev/null
+++ b/org.eclipse.vex.core/.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.5"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry exported="true" kind="lib" path="lib/flute.jar"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/org.eclipse.vex.core/.cvsignore b/org.eclipse.vex.core/.cvsignore
new file mode 100644
index 0000000..fc08203
--- /dev/null
+++ b/org.eclipse.vex.core/.cvsignore
@@ -0,0 +1,3 @@
+bin
+net.sf.vex.toolkit_1.0.0.jar
+vex-toolkit.jar
diff --git a/org.eclipse.vex.core/.gitignore b/org.eclipse.vex.core/.gitignore
new file mode 100644
index 0000000..fe07c27
--- /dev/null
+++ b/org.eclipse.vex.core/.gitignore
@@ -0,0 +1,2 @@
+bin

+/target
diff --git a/org.eclipse.vex.core/.project b/org.eclipse.vex.core/.project
new file mode 100644
index 0000000..fb62b4d
--- /dev/null
+++ b/org.eclipse.vex.core/.project
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>org.eclipse.vex.core</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>

+		<buildCommand>

+			<name>net.sourceforge.metrics.builder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.pde.PluginNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+		<nature>net.sourceforge.metrics.nature</nature>

+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>

+	</natures>

+</projectDescription>

diff --git a/org.eclipse.vex.core/.settings/org.eclipse.core.resources.prefs b/org.eclipse.vex.core/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..3a4d5b4
--- /dev/null
+++ b/org.eclipse.vex.core/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Feb 25 19:52:05 CET 2011

+eclipse.preferences.version=1

+encoding/<project>=UTF-8

diff --git a/org.eclipse.vex.core/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.vex.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..f23f0b0
--- /dev/null
+++ b/org.eclipse.vex.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Fri Oct 24 20:14:12 GMT 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/org.eclipse.vex.core/.settings/org.eclipse.pde.prefs b/org.eclipse.vex.core/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..e1a4f4c
--- /dev/null
+++ b/org.eclipse.vex.core/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,22 @@
+#Fri Oct 31 01:34:22 EDT 2008
+compilers.s.create-docs=false
+compilers.p.internal=1
+compilers.p.unresolved-ex-points=0
+compilers.p.missing-packages=1
+eclipse.preferences.version=1
+compilers.p.unresolved-import=0
+compilers.f.unresolved-features=1
+compilers.p.unknown-identifier=1
+compilers.p.unknown-resource=0
+compilers.s.open-tags=1
+compilers.p.not-externalized-att=1
+compilers.p.no-required-att=0
+compilers.p.discouraged-class=1
+compilers.incompatible-environment=1
+compilers.p.deprecated=1
+compilers.f.unresolved-plugins=1
+compilers.p.build=0
+compilers.p.unknown-element=1
+compilers.p.unknown-class=1
+compilers.s.doc-folder=doc
+compilers.p.unknown-attribute=0
diff --git a/org.eclipse.vex.core/.settings/org.eclipse.wst.html.core.prefs b/org.eclipse.vex.core/.settings/org.eclipse.wst.html.core.prefs
new file mode 100644
index 0000000..5b7d208
--- /dev/null
+++ b/org.eclipse.vex.core/.settings/org.eclipse.wst.html.core.prefs
@@ -0,0 +1,36 @@
+#Fri Oct 31 01:34:24 EDT 2008
+elemEndInvalidCase=1
+refInvalidContent=2
+elemUnknownName=2
+elemCoexistence=2
+piUndefined=2
+attrUndefName=2
+eclipse.preferences.version=1
+docDuplicateTag=1
+attrInvalidValue=2
+piInvalidContent=2
+elemInvalidDirective=1
+attrUndefValue=2
+elemInvalidContent=2
+elemUnclosedEndTag=1
+attrValueMismatch=1
+elemMissingStart=1
+elemUnclosedStartTag=1
+elemInvalidName=1
+docDoctypeUnclosed=1
+cdataUnclosed=1
+commentInvalidContent=2
+attrDuplicate=2
+docInvalidChar=2
+elemMissingEnd=2
+elemDuplicate=2
+piUnclosed=1
+commentUnclosed=1
+elemStartInvalidCase=2
+cdataInvalidContent=2
+elemUnnecessaryEnd=2
+attrInvalidName=2
+attrValueUnclosed=2
+docInvalidContent=2
+attrNameMismatch=2
+elemInvalidEmptyTag=2
diff --git a/org.eclipse.vex.core/.template b/org.eclipse.vex.core/.template
new file mode 100644
index 0000000..d65e0f4
--- /dev/null
+++ b/org.eclipse.vex.core/.template
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form>
+   <p/><p><b>Tips on working with this plug-in project</b></p><li>For the view of the new plug-in at a glance, go to the <img href="pageImage"/><a href="OverviewPage">Overview</a>.</li><li>You can test the contributions of this plug-in by launching another instance of the workbench. On the <b>Run</b> menu, click <b>Run As</b> and choose <img href="runTimeWorkbenchImage"/><a href="action.run">Run-time Workbench</a> from the available choices.</li><li>You can add more functionality to this plug-in by adding extensions using the <a href="action.newExtension">New Extension Wizard</a>.</li><li>The plug-in project contains Java code that you can debug. Place breakpoints in Java classes. On the <b>Run</b> menu, select <b>Debug As</b> and choose <img href="runTimeWorkbenchImage"/><a href="action.debug">Run-time Workbench</a> from the available choices.</li>
+</form>
diff --git a/org.eclipse.vex.core/META-INF/MANIFEST.MF b/org.eclipse.vex.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1205d5d
--- /dev/null
+++ b/org.eclipse.vex.core/META-INF/MANIFEST.MF
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-SymbolicName: org.eclipse.vex.core;singleton:=true
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.ui;bundle-version="[3.4.0,4.0.0)",
+ org.w3c.css.sac;bundle-version="[1.3.0,2.0.0)",
+ org.apache.xerces;bundle-version="[2.9.0,3.0.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.dtd.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.emf.ecore;bundle-version="[2.4.1,3.0.0)",
+ org.eclipse.wst.common.emf;bundle-version="[1.1.0,2.0.0)"
+Export-Package: org.eclipse.wst.xml.vex.core.internal;x-friends:="org.eclipse.wst.xml.vex.ui",
+ org.eclipse.wst.xml.vex.core.internal.core;x-friends:="org.eclipse.wst.xml.vex.ui,org.eclipse.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.css;x-friends:="org.eclipse.wst.xml.vex.ui,org.eclipse.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.dom;x-friends:="org.eclipse.wst.xml.vex.ui,org.eclipse.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.layout;x-friends:="org.eclipse.wst.xml.vex.ui,org.eclipse.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.undo;x-internal:=true,
+ org.eclipse.wst.xml.vex.core.internal.validator;x-friends:="org.eclipse.wst.xml.vex.ui",
+ org.eclipse.wst.xml.vex.core.internal.widget;x-friends:="org.eclipse.wst.xml.vex.ui,org.eclipse.vex.core.tests",
+ org.w3c.flute.parser,
+ org.w3c.flute.parser.selectors,
+ org.w3c.flute.util
+Bundle-Activator: org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin
+Bundle-Localization: plugin
+Bundle-ClassPath: lib/flute.jar,
+ .
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/org.eclipse.vex.core/build.properties b/org.eclipse.vex.core/build.properties
new file mode 100644
index 0000000..49a4958
--- /dev/null
+++ b/org.eclipse.vex.core/build.properties
@@ -0,0 +1,11 @@
+bin.includes = plugin.properties,\
+               META-INF/,\
+               .,\
+               lib/,\
+               lib/flute.jar
+src.includes = lib/
+source.. = src/
+der = .
+bin.excludes = lib/.cvsignore
+src.excludes = lib/.cvsignore
+output.. = bin/
diff --git a/org.eclipse.vex.core/lib/.cvsignore b/org.eclipse.vex.core/lib/.cvsignore
new file mode 100644
index 0000000..8f7e8a6
--- /dev/null
+++ b/org.eclipse.vex.core/lib/.cvsignore
@@ -0,0 +1 @@
+dtdparser120.jar
diff --git a/org.eclipse.vex.core/lib/flute.jar b/org.eclipse.vex.core/lib/flute.jar
new file mode 100644
index 0000000..39a55d4
--- /dev/null
+++ b/org.eclipse.vex.core/lib/flute.jar
Binary files differ
diff --git a/org.eclipse.vex.core/plugin.properties b/org.eclipse.vex.core/plugin.properties
new file mode 100644
index 0000000..da60e09
--- /dev/null
+++ b/org.eclipse.vex.core/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2009 John Krasnay 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:
+#     John Krasnay - initial API and implementation
+###############################################################################
+pluginName= Vex Core
+providerName= Eclipse.org
diff --git a/org.eclipse.vex.core/pom.xml b/org.eclipse.vex.core/pom.xml
new file mode 100644
index 0000000..7c7b86f
--- /dev/null
+++ b/org.eclipse.vex.core/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>org.eclipse.vex.core</artifactId>
+	<packaging>eclipse-plugin</packaging>
+	<version>1.0.0-SNAPSHOT</version>
+	<name>Vex Core</name>
+
+	<parent>
+		<artifactId>org.eclipse.vex-releng</artifactId>
+		<groupId>org.eclipse.vex</groupId>
+		<version>1.0.0-SNAPSHOT</version>
+		<relativePath>../org.eclipse.vex.releng/pom.xml</relativePath>
+	</parent>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>findbugs-maven-plugin</artifactId>
+				<version>2.3.2</version>
+				<configuration>
+					<findbugsXmlOutput>true</findbugsXmlOutput>
+					<failOnError>false</failOnError>
+				</configuration>
+				<executions>
+					<execution>
+						<goals>
+							<goal>check</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-pmd-plugin</artifactId>
+				<configuration>
+					<sourceEncoding>utf-8</sourceEncoding>
+					<minimumTokens>100</minimumTokens>
+					<targetJdk>1.5</targetJdk>
+					<format>xml</format>
+					<failOnViolation>false</failOnViolation>
+				</configuration>
+				<executions>
+					<execution>
+						<goals>
+							<goal>cpd-check</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/VEXCorePlugin.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/VEXCorePlugin.java
new file mode 100644
index 0000000..0b7de0c
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/VEXCorePlugin.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     David Carver (STAR) - initial renaming
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+public class VEXCorePlugin extends AbstractUIPlugin {
+
+	public static final String ID = "org.eclipse.wst.xml.vex.core"; //$NON-NLS-1$
+
+	private static VEXCorePlugin instance;
+
+	public VEXCorePlugin() {
+		if (instance != null)
+			throw new IllegalStateException("This plug-in must be a singleton."); //$NON-NLS-1$
+		instance = this;
+	}
+
+	public static VEXCorePlugin getInstance() {
+		return instance;
+	}
+
+	public void start(BundleContext bundleContext) throws Exception {
+		super.start(bundleContext);
+	}
+
+	public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Caret.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Caret.java
new file mode 100644
index 0000000..dbb41e2
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Caret.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Represents the caret, a line that indicates an insertion point in the
+ * document.
+ */
+public abstract class Caret {
+
+	private int x;
+	private int y;
+
+	/**
+	 * Class constructor
+	 * 
+	 * @param x
+	 *            x-coordinate of the top left corner of the caret
+	 * @param y
+	 *            y-coordinate of the top left corner of the caret
+	 */
+	public Caret(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	/**
+	 * Draws the caret in the given Graphics context.
+	 * 
+	 * @param g
+	 *            Graphics within which the caret should be drawn.
+	 * @param color
+	 *            Color with which the caret should be drawn.
+	 */
+	public abstract void draw(Graphics g, Color color);
+
+	/**
+	 * Returns the smallest rectangle that completely encloses the caret.
+	 */
+	public abstract Rectangle getBounds();
+
+	/**
+	 * Returns the x-coordinate of the top left corner of the caret
+	 */
+	public int getX() {
+		return this.x;
+	}
+
+	/**
+	 * Returns the y-coordinate of the top left corner of the caret
+	 */
+	public int getY() {
+		return this.y;
+	}
+
+	/**
+	 * Moves the caret by the given x and y distance.
+	 * 
+	 * @param x
+	 *            amount by which to move the caret to the right
+	 * @param y
+	 *            amount by which to move the caret down
+	 */
+	public void translate(int x, int y) {
+		this.x += x;
+		this.y += y;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Color.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Color.java
new file mode 100644
index 0000000..d4d01b9
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Color.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent representation of a color. Colors consist of three
+ * integers in the range 0..255 representing red, green, and blue components.
+ * Objects of this class are immutable.
+ */
+public class Color {
+
+	public static final Color BLACK = new Color(0, 0, 0);
+
+	private final int red;
+	private final int green;
+	private final int blue;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param red
+	 *            red value, 0..255
+	 * @param green
+	 *            green value, 0..255
+	 * @param blue
+	 *            blue value, 0..255
+	 */
+	public Color(int red, int green, int blue) {
+		this.red = red;
+		this.green = green;
+		this.blue = blue;
+	}
+
+	/**
+	 * Returns the blue component of the color, in the range 0..255
+	 */
+	public int getBlue() {
+		return blue;
+	}
+
+	/**
+	 * Returns the green component of the color, in the range 0..255
+	 */
+	public int getGreen() {
+		return green;
+	}
+
+	/**
+	 * Returns the red component of the color, in the range 0..255
+	 */
+	public int getRed() {
+		return red;
+	}
+
+	public boolean equals(Object obj) {
+		if (this == obj) return true;
+		if (obj == null) return false;
+		if (getClass() != obj.getClass()) return false;
+		
+		Color other = (Color) obj;
+		return    red == other.red
+		       && green == other.green
+		       && blue == other.blue;
+	}
+
+	public int hashCode() {
+		return this.red + this.green << 16 + this.blue << 24;
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer(20);
+		sb.append("Color[r=");
+		sb.append(this.red);
+		sb.append(",g=");
+		sb.append(this.green);
+		sb.append(",b=");
+		sb.append(this.blue);
+		sb.append("]");
+		return sb.toString();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ColorResource.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ColorResource.java
new file mode 100644
index 0000000..914173e
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ColorResource.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Wrapper for a toolkit-defined color. Color objects are system resources. They
+ * should be retrieved with the Graphics.createColor method and should be
+ * disposed when no longer needed.
+ */
+public interface ColorResource {
+
+	public static final int SELECTION_BACKGROUND = 0;
+	public static final int SELECTION_FOREGROUND = 1;
+
+	public void dispose();
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/DisplayDevice.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/DisplayDevice.java
new file mode 100644
index 0000000..c958acb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/DisplayDevice.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Represents a device that can display graphics. This class is subclassed for
+ * each target system.
+ */
+public abstract class DisplayDevice {
+
+	public static final DisplayDevice NULL = new DisplayDevice() {
+		@Override
+		public int getHorizontalPPI() {
+			return 0;
+		}
+
+		@Override
+		public int getVerticalPPI() {
+			return 0;
+		}
+	};
+	
+	/**
+	 * Class constructor.
+	 */
+	public DisplayDevice() {
+	}
+
+	/**
+	 * Returns the current display device.
+	 */
+	public static DisplayDevice getCurrent() {
+		return current;
+	}
+
+	/**
+	 * Returns the horizontal resolution of the device, in pixels-per-inch.
+	 */
+	public abstract int getHorizontalPPI();
+
+	/**
+	 * Returns the horizontal resolution of the device, in pixels-per-inch.
+	 */
+	public abstract int getVerticalPPI();
+
+	/**
+	 * Sets the current display device. This is typically called by the
+	 * platform-specific widget;
+	 * 
+	 * @param current
+	 *            The device to use as the current device.
+	 */
+	public static void setCurrent(DisplayDevice current) {
+		DisplayDevice.current = current;
+	}
+
+	// ======================================================= PRIVATE
+
+	private static DisplayDevice current = NULL;
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Drawable.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Drawable.java
new file mode 100644
index 0000000..2ba63d8
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Drawable.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * An object that can be drawn into a Graphics.
+ */
+public interface Drawable {
+
+	/**
+	 * Draw the object.
+	 * 
+	 * @param g
+	 *            Graphics into which to draw the object.
+	 * @param x
+	 *            x-coordinate where the object should be drawn
+	 * @param y
+	 *            y-coordinate where the object should be drawn
+	 */
+	public void draw(Graphics g, int x, int y);
+
+	/**
+	 * Returns the smallest rectangle that completely encloses the drawn shape.
+	 */
+	public Rectangle getBounds();
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontMetrics.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontMetrics.java
new file mode 100644
index 0000000..e46771a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontMetrics.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent font metrics.
+ */
+public interface FontMetrics {
+	public int getAscent();
+
+	public int getDescent();
+
+	public int getHeight();
+
+	public int getLeading();
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontResource.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontResource.java
new file mode 100644
index 0000000..037f4da
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontResource.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Wrapper for a toolkit-defined font. Fonts are system-defined resources. They
+ * must be retrieved from the Graphics.createFont method, and must be disposed
+ * when no longer needed.
+ */
+public interface FontResource {
+
+	public void dispose();
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontSpec.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontSpec.java
new file mode 100644
index 0000000..8cb4474
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/FontSpec.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Dave Holroyd - Implement text decoration
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent specifier of a font. This class does not encapsulate an
+ * actual font, but simply the information needed for the toolkit to find an
+ * actual font.
+ * 
+ * <p>
+ * An array of font family names may be specified. If more than one name is
+ * specified, the toolkit should select the first name that matches an actual
+ * font on the platform.
+ * </p>
+ */
+public class FontSpec {
+
+	public static final int PLAIN = 0x0;
+	public static final int BOLD = 1 << 0;
+	public static final int ITALIC = 1 << 1;
+	public static final int UNDERLINE = 1 << 2;
+	public static final int OVERLINE = 1 << 3;
+	public static final int LINE_THROUGH = 1 << 4;
+
+	private String[] names;
+	private float size;
+	private int style;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param names
+	 *            Array of names of the font family.
+	 * @param style
+	 *            Bitwise-OR of the applicable style flages, e.g. BOLD | ITALIC
+	 * @param size
+	 *            Size of the font, in points.
+	 */
+	public FontSpec(String[] names, int style, float size) {
+		this.names = names;
+		this.style = style;
+		this.size = size;
+	}
+
+	/**
+	 * Returns the names of the font families that match the font.
+	 */
+	public String[] getNames() {
+		return names;
+	}
+
+	/**
+	 * Returns the size of the font in points.
+	 */
+	public float getSize() {
+		return size;
+	}
+
+	/**
+	 * Returns a bitwise-OR of the style flags. The following sample checks if
+	 * the font is bold.
+	 * 
+	 * <pre>
+	 * if (font.getStyle | VexFont.BOLD) {
+	 * 	// do something bold...
+	 * }
+	 * </pre>
+	 */
+	public int getStyle() {
+		return style;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Graphics.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Graphics.java
new file mode 100644
index 0000000..4e64962
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Graphics.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Mohamadou Nassourou - Bug 298912 - rudimentary support for images 
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+import java.net.URL;
+
+/**
+ * Interface through which Vex performs graphics operations. Implemented by
+ * adapters to the java.awt.Graphics and org.eclipse.swt.graphics.GC classes.
+ */
+public interface Graphics {
+
+	public static final int LINE_SOLID = 0;
+	public static final int LINE_DASH = 1;
+	public static final int LINE_DOT = 2;
+
+	public int charsWidth(char[] data, int offset, int length);
+
+	public ColorResource createColor(Color rgb);
+
+	public FontResource createFont(FontSpec fontSpec);
+
+	public void dispose();
+
+	public void drawChars(char[] chars, int offset, int length, int x, int y);
+
+	public void drawLine(int x1, int y1, int x2, int y2);
+
+	/**
+	 * Draw the given string at the given point using the current font.
+	 * 
+	 * @param s
+	 *            string to draw
+	 * @param x
+	 *            x-coordinate of the top left corner of the text box
+	 * @param y
+	 *            y-coordinate of the top left corner of the text box
+	 */
+	public void drawString(String s, int x, int y);
+
+	public void drawOval(int x, int y, int width, int height);
+
+	public void drawRect(int x, int y, int width, int height);
+
+	public void drawImage(Image image, int x, int y, int width, int height); 
+	
+	public void fillOval(int x, int y, int width, int height);
+
+	public void fillRect(int x, int y, int width, int height);
+
+	public Rectangle getClipBounds();
+
+	public ColorResource getColor();
+
+	public FontResource getFont();
+
+	public int getLineStyle();
+
+	public int getLineWidth();
+
+	public ColorResource getSystemColor(int id);
+
+	public FontMetrics getFontMetrics();
+	
+	public Image getImage(URL url);
+
+	public boolean isAntiAliased();
+
+	public void setAntiAliased(boolean antiAliased);
+
+	public ColorResource setColor(ColorResource color);
+
+	public FontResource setFont(FontResource font);
+
+	public void setLineStyle(int style);
+
+	public void setLineWidth(int width);
+
+	public int stringWidth(String s);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Image.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Image.java
new file mode 100644
index 0000000..f9622eb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Image.java
@@ -0,0 +1,19 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.core;

+

+/**

+ * @author Florian Thienel

+ */

+public interface Image {

+	int getHeight();

+	int getWidth();

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Insets.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Insets.java
new file mode 100644
index 0000000..d2f762a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Insets.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent insets.
+ */
+public class Insets {
+
+	private int top;
+	private int left;
+	private int bottom;
+	private int right;
+
+	/** Zero insets */
+	public static final Insets ZERO_INSETS = new Insets(0, 0, 0, 0);
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param top
+	 *            Top inset.
+	 * @param left
+	 *            Left inset.
+	 * @param bottom
+	 *            Bottom inset.
+	 * @param right
+	 *            Right inset.
+	 */
+	public Insets(int top, int left, int bottom, int right) {
+		this.top = top;
+		this.left = left;
+		this.bottom = bottom;
+		this.right = right;
+	}
+
+	/**
+	 * @return Returns the top.
+	 */
+	public int getTop() {
+		return top;
+	}
+
+	/**
+	 * @return Returns the left.
+	 */
+	public int getLeft() {
+		return left;
+	}
+
+	/**
+	 * @return Returns the bottom.
+	 */
+	public int getBottom() {
+		return bottom;
+	}
+
+	/**
+	 * Returns the right inset.
+	 */
+	public int getRight() {
+		return right;
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer(80);
+		sb.append(Insets.class.getName());
+		sb.append("[top=");
+		sb.append(this.getTop());
+		sb.append(",left=");
+		sb.append(this.getLeft());
+		sb.append(",bottom=");
+		sb.append(this.getBottom());
+		sb.append(",right=");
+		sb.append(this.getRight());
+		sb.append("]");
+		return sb.toString();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/IntRange.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/IntRange.java
new file mode 100644
index 0000000..c680f16
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/IntRange.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Represents a range of integers. Zero-length ranges (i.e. ranges where start
+ * == end) are permitted. This class is immutable.
+ */
+public class IntRange {
+
+	/**
+	 * Class constuctor.
+	 * 
+	 * @param start
+	 *            Start of the range.
+	 * @param end
+	 *            End of the range. Must be >= start.
+	 */
+	public IntRange(int start, int end) {
+		if (start > end) {
+			throw new IllegalArgumentException("start (" + start
+					+ ") is greater than end (" + end + ")");
+		}
+		this.start = start;
+		this.end = end;
+	}
+
+	/**
+	 * Returns the start of the range.
+	 */
+	public int getStart() {
+		return this.start;
+	}
+
+	/**
+	 * Returns the end of the range.
+	 */
+	public int getEnd() {
+		return this.end;
+	}
+
+	/**
+	 * Returns the range that represents the intersection of this range and the
+	 * given range. If the ranges do not intersect, returns null. May return an
+	 * empty range.
+	 * 
+	 * @param range
+	 *            Range with which to perform an intersection.
+	 */
+	public IntRange intersection(IntRange range) {
+		if (this.intersects(range)) {
+			return new IntRange(Math.max(this.start, range.start), Math.min(
+					this.end, range.end));
+		} else {
+			return null;
+		}
+
+	}
+
+	/**
+	 * Returns true if this range intersects the given range, even if the result
+	 * would be an empty range.
+	 * 
+	 * @param range
+	 *            Range with which to intersect.
+	 */
+	public boolean intersects(IntRange range) {
+		return this.start <= range.end && this.end >= range.start;
+	}
+
+	/**
+	 * Returns true if start and end are equal.
+	 */
+	public boolean isEmpty() {
+		return start == end;
+	}
+
+	/**
+	 * Returns a range that is the union of this range and the given range. If
+	 * the ranges are disjoint, the gap between the ranges is included in the
+	 * result.
+	 * 
+	 * @param range
+	 *            Rnage with which to perform the union
+	 */
+	public IntRange union(IntRange range) {
+		return new IntRange(Math.min(this.start, range.start), Math.min(
+				this.end, range.end));
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer();
+		sb.append("IntRange(");
+		sb.append(start);
+		sb.append(",");
+		sb.append(end);
+		sb.append(")");
+		return sb.toString();
+	}
+
+	// ============================================================= PRIVATE
+
+	private int start;
+	private int end;
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ListenerList.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ListenerList.java
new file mode 100644
index 0000000..c7eb120
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/ListenerList.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Florian Thienel - generics inferred
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A collection of listener objects. The main point of this class is the
+ * fireEvent method, which takes care of the tedium of iterating over the
+ * collection and catching exceptions generated by listeners.
+ *
+ * @param <L> the type of listeners maintained by this list
+ * @param <E> the type of events to fire
+ */
+public class ListenerList<L, E extends EventObject> {
+
+    private final Class<L> listenerClass;
+	private final List<L> listeners = new ArrayList<L>();
+
+    /** Mapping: method name => method object */
+    private Map<String, Method> methods = new HashMap<String, Method>();
+    
+	/**
+	 * Class constructor.
+	 * 
+	 * @param listenerClass Class of the listener interface.
+	 */
+	public ListenerList(Class<L> listenerClass) {
+		this.listenerClass = listenerClass;
+	}
+
+    /**
+     * Adds a listener to the list. Rejects listeners that are not subclasses of
+     * the listener class passed to the constructor.
+     *
+     * @param listener Listener to be added.
+     */
+    public void add(L listener) {
+        this.listeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from the list.
+     *
+     * @param listener
+     *            Listener to remove.
+     */
+    public void remove(L listener) {
+    	this.listeners.remove(listener);
+    }
+    
+    /**
+     * Called from fireEvent whenever a called listener method throws an
+     * exception, or if there is a problem looking up the listener method by
+     * reflection. By default, simply prints the stack trace to stdout. Clients
+     * may override this method to provide a more suitable implementation.
+     *
+     * @param e Exception thrown by the listener method.
+     */
+    public void handleException(Exception e) {
+        e.printStackTrace();
+    }
+
+    /**
+     * Calls the given method on each registered listener. Any exception thrown
+     * from one of the called methods is passed to handleException, as is any
+     * introspection error, e.g. if the given method doesn't exist.
+     *
+     * @param methodName Listener method to call.
+     * @param event Event to be passed to each call.
+     */
+    public void fireEvent(String methodName, E event) {
+    	final Method method = getMethod(methodName, event.getClass());
+    	if (method == null)
+    		return; // Exception handling already done by getMethod
+
+    	for (L listener : listeners) {
+    		try {
+    			method.invoke(listener, event);
+    		} catch (IllegalArgumentException e) {
+    			handleException(e);
+    		} catch (IllegalAccessException e) {
+    			handleException(e);
+    		} catch (InvocationTargetException e) {
+    			handleException(e);
+    		}
+    	}
+    }
+
+    private Method getMethod(String methodName, Class<?> eventClass) {
+        if (this.methods.containsKey(methodName))
+            return this.methods.get(methodName);
+        
+		try {
+			Method method = listenerClass.getMethod(methodName, eventClass);
+			this.methods.put(methodName, method);
+			return method;
+		} catch (SecurityException e) {
+			handleException(e);
+		} catch (NoSuchMethodException e) {
+			handleException(e);
+		}
+		return null;
+    }
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Point.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Point.java
new file mode 100644
index 0000000..d3fdd29
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Point.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent point.
+ */
+public class Point {
+
+	private int x;
+	private int y;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param x
+	 *            X-coordinate.
+	 * @param y
+	 *            Y-coordinate.
+	 */
+	public Point(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer(80);
+		sb.append(Point.class.getName());
+		sb.append("[x=");
+		sb.append(this.getX());
+		sb.append(",y=");
+		sb.append(this.getY());
+		sb.append("]");
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the x-coordinate.
+	 */
+	public int getX() {
+		return x;
+	}
+
+	/**
+	 * Returns the y-coordinate.
+	 */
+	public int getY() {
+		return y;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/QualifiedNameComparator.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/QualifiedNameComparator.java
new file mode 100644
index 0000000..97360f7
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/QualifiedNameComparator.java
@@ -0,0 +1,25 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.core;

+

+import java.util.Comparator;

+

+import org.eclipse.core.runtime.QualifiedName;

+

+/**

+ * @author Florian Thienel

+ */

+public class QualifiedNameComparator implements Comparator<QualifiedName> {

+

+	public int compare(QualifiedName name1, QualifiedName name2) {

+		return name1.toString().compareTo(name2.toString());

+	}

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Rectangle.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Rectangle.java
new file mode 100644
index 0000000..b996dcb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/core/Rectangle.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.core;
+
+/**
+ * Toolkit-independent rectangle.
+ */
+public class Rectangle {
+
+	private int x;
+	private int y;
+	private int width;
+	private int height;
+
+	public Rectangle(int x, int y, int width, int height) {
+		this.x = x;
+		this.y = y;
+		this.width = width;
+		this.height = height;
+	}
+
+	public boolean intersects(Rectangle rect) {
+		return rect.x < this.x + this.width && rect.x + rect.width > this.x
+				&& rect.y < this.y + this.height
+				&& rect.y + rect.height > this.y;
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer(80);
+		sb.append(Rectangle.class.getName());
+		sb.append("[x=");
+		sb.append(this.getX());
+		sb.append(",y=");
+		sb.append(this.getY());
+		sb.append(",width=");
+		sb.append(this.getWidth());
+		sb.append(",height=");
+		sb.append(this.getHeight());
+		sb.append("]");
+		return sb.toString();
+	}
+
+	/**
+	 * @return Returns the x.
+	 */
+	public int getX() {
+		return x;
+	}
+
+	/**
+	 * @return Returns the y.
+	 */
+	public int getY() {
+		return y;
+	}
+
+	/**
+	 * @return Returns the width.
+	 */
+	public int getWidth() {
+		return width;
+	}
+
+	/**
+	 * @return Returns the height.
+	 */
+	public int getHeight() {
+		return height;
+	}
+
+	/**
+	 * Returns a Rectangle that is the union of this rectangle with another.
+	 * 
+	 * @param rect
+	 *            Rectangle with which to union this one.
+	 */
+	public Rectangle union(Rectangle rect) {
+		int left = Math.min(this.x, rect.x);
+		int top = Math.min(this.y, rect.y);
+		int right = Math.max(this.x + this.width, rect.x + rect.width);
+		int bottom = Math.max(this.y + this.height, rect.y + rect.height);
+		return new Rectangle(left, top, right - left, bottom - top);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/AbstractProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/AbstractProperty.java
new file mode 100644
index 0000000..31b6662
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/AbstractProperty.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Abstract base class for property classes. Implements the <code>name</code>
+ * property but leaves the implementation of <code>calculate</code> to the
+ * subclass.
+ */
+public abstract class AbstractProperty implements IProperty {
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param name
+	 *            Name of the property.
+	 */
+	public AbstractProperty(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents the token "inherit".
+	 */
+	public static boolean isInherit(LexicalUnit lu) {
+		return lu != null && lu.getLexicalUnitType() == LexicalUnit.SAC_INHERIT;
+	}
+
+	public String getName() {
+		return this.name;
+	}
+
+	public static boolean isPercentage(LexicalUnit lu) {
+		return lu != null
+				&& lu.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE;
+	}
+
+	public static boolean isLength(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		}
+
+		short type = lu.getLexicalUnitType();
+
+		if (type == LexicalUnit.SAC_INTEGER && lu.getIntegerValue() == 0) {
+			return true;
+		}
+
+		return type == LexicalUnit.SAC_CENTIMETER
+				|| type == LexicalUnit.SAC_DIMENSION
+				|| type == LexicalUnit.SAC_EM || type == LexicalUnit.SAC_EX
+				|| type == LexicalUnit.SAC_INCH
+				|| type == LexicalUnit.SAC_MILLIMETER
+				|| type == LexicalUnit.SAC_PICA
+				|| type == LexicalUnit.SAC_PIXEL
+				|| type == LexicalUnit.SAC_POINT;
+	}
+
+	public static int getIntLength(LexicalUnit lu, float fontSize, int ppi) {
+		return Math.round(getFloatLength(lu, fontSize, ppi));
+	}
+
+	public static float getFloatLength(LexicalUnit lu, float fontSize, int ppi) {
+
+		float value = 0f;
+
+		switch (lu.getLexicalUnitType()) {
+
+		case LexicalUnit.SAC_CENTIMETER:
+			value = lu.getFloatValue() * ppi / 2.54f;
+			break;
+		case LexicalUnit.SAC_EM:
+			value = lu.getFloatValue() * fontSize;
+			break;
+		case LexicalUnit.SAC_EX:
+			value = lu.getFloatValue() * fontSize * EX_FACTOR;
+			break;
+		case LexicalUnit.SAC_INCH:
+			value = lu.getFloatValue() * ppi;
+			break;
+		case LexicalUnit.SAC_INTEGER:
+			value = 0; // 0 is the only valid length w/o a dimension
+			break;
+		case LexicalUnit.SAC_MILLIMETER:
+			value = lu.getFloatValue() * ppi / 25.4f;
+			break;
+		case LexicalUnit.SAC_PICA:
+			value = lu.getFloatValue() * ppi / 6;
+			break;
+		case LexicalUnit.SAC_PIXEL:
+			value = lu.getFloatValue();
+			break;
+		case LexicalUnit.SAC_POINT:
+			value = lu.getFloatValue() * ppi / 72;
+			break;
+		}
+		return value;
+	}
+
+	// ============================================================== PRIVATE
+
+	public static boolean isNumber(LexicalUnit lu) {
+		return lu != null
+				&& (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER || lu
+						.getLexicalUnitType() == LexicalUnit.SAC_REAL);
+	}
+
+	public static float getNumber(LexicalUnit lu) {
+		if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
+			return lu.getIntegerValue();
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_REAL) {
+			return lu.getFloatValue();
+		} else {
+			throw new RuntimeException("LexicalUnit type "
+					+ lu.getLexicalUnitType() + " is not a numeric type.");
+		}
+	}
+
+	private String name;
+
+	private static final float EX_FACTOR = 0.6f;
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BackgroundImageProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BackgroundImageProperty.java
new file mode 100644
index 0000000..c79e12b
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BackgroundImageProperty.java
@@ -0,0 +1,52 @@
+/*******************************************************************************

+ * Copyright (c) 2010  Mohamadou Nassourou 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:

+ * 		 Mohamadou Nassourou - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.css;

+

+import java.text.MessageFormat;

+

+import org.eclipse.core.runtime.IStatus;

+import org.eclipse.core.runtime.Status;

+import org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin;

+import org.eclipse.wst.xml.vex.core.internal.dom.Element;

+import org.w3c.css.sac.LexicalUnit;

+

+/**

+ * @author Mohamadou Nassourou

+ */

+public class BackgroundImageProperty extends AbstractProperty {

+

+	public static final String DEFAULT = null;

+

+	public BackgroundImageProperty() {

+		super(CSS.BACKGROUND_IMAGE);

+	}

+

+	public Object calculate(final LexicalUnit lexicalUnit, final Styles parentStyles, final Styles styles, final Element element) {

+		if (lexicalUnit == null)

+			return DEFAULT;

+		switch (lexicalUnit.getLexicalUnitType()) {

+		case LexicalUnit.SAC_STRING_VALUE:

+			return lexicalUnit.getStringValue();

+		case LexicalUnit.SAC_ATTR:

+			final String attributeValue = element.getAttributeValue(lexicalUnit.getStringValue());

+			if (attributeValue != null)

+				return attributeValue;

+			return DEFAULT;

+		default:

+			VEXCorePlugin

+					.getInstance()

+					.getLog()

+					.log(new Status(IStatus.WARNING, VEXCorePlugin.ID, MessageFormat.format(

+							"Unsupported lexical unit type in ''background-image: {0}'' (type: {1})", lexicalUnit.toString(), lexicalUnit.getLexicalUnitType())));

+			return DEFAULT;

+		}

+	}

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderSpacingProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderSpacingProperty.java
new file mode 100644
index 0000000..9bd90c6
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderSpacingProperty.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.core.DisplayDevice;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS 'border-spacing' property.
+ */
+public class BorderSpacingProperty extends AbstractProperty {
+
+	/**
+	 * Represents the computed value of border-spacing, which is a pair of
+	 * values representing vertical and horizontal spacing.
+	 */
+	public static class Value {
+
+		private int horizontal;
+		private int vertical;
+
+		public static final Value ZERO = new Value(0, 0);
+
+		public Value(int horizontal, int vertical) {
+			this.horizontal = horizontal;
+			this.vertical = vertical;
+		}
+
+		/**
+		 * Returns the horizontal spacing, in pixels.
+		 */
+		public int getHorizontal() {
+			return this.horizontal;
+		}
+
+		/**
+		 * Returns the vertical spacing, in pixels.
+		 */
+		public int getVertical() {
+			return this.vertical;
+		}
+	}
+
+	/**
+	 * Class constructor.
+	 */
+	public BorderSpacingProperty() {
+		super(CSS.BORDER_SPACING);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+
+		int horizontal = 0;
+		int vertical = 0;
+
+		DisplayDevice device = DisplayDevice.getCurrent();
+
+		if (isLength(lu)) {
+			horizontal = getIntLength(lu, styles.getFontSize(), device
+					.getHorizontalPPI());
+			lu = lu.getNextLexicalUnit();
+			if (isLength(lu)) {
+				vertical = getIntLength(lu, styles.getFontSize(), device
+						.getVerticalPPI());
+			} else {
+				vertical = horizontal;
+			}
+			return new Value(horizontal, vertical);
+		} else {
+			// 'inherit' or an invalid value
+			if (parentStyles == null) {
+				return Value.ZERO;
+			} else {
+				return parentStyles.getBorderSpacing();
+			}
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderStyleProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderStyleProperty.java
new file mode 100644
index 0000000..7865f3a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderStyleProperty.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The border-XXX-style CSS property.
+ */
+public class BorderStyleProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param name
+	 *            Name of the property.
+	 */
+	public BorderStyleProperty(String name) {
+		super(name);
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a border style.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isBorderStyle(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NONE) || s.equals(CSS.HIDDEN)
+					|| s.equals(CSS.DOTTED) || s.equals(CSS.DASHED)
+					|| s.equals(CSS.SOLID) || s.equals(CSS.DOUBLE)
+					|| s.equals(CSS.GROOVE) || s.equals(CSS.RIDGE)
+					|| s.equals(CSS.INSET) || s.equals(CSS.OUTSET);
+		}
+
+		return false;
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isBorderStyle(lu)) {
+			return lu.getStringValue();
+		} else if (isInherit(lu) && parentStyles != null) {
+			return parentStyles.get(this.getName());
+		} else {
+			return CSS.NONE;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderWidthProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderWidthProperty.java
new file mode 100644
index 0000000..884568d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/BorderWidthProperty.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.core.DisplayDevice;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The border-XXX-width CSS property. Since the value of this property depends
+ * on the corresponding border-XXX-style property, the style property must be
+ * calculated first and placed in the styles, and its name given to the
+ * constructor of this class.
+ */
+public class BorderWidthProperty extends AbstractProperty {
+
+	// Name of the corresponding border style property
+	private String borderStyleName;
+
+	// Axis along which the border width is measured.
+	private Axis axis;
+
+	// named border widths
+	private static final int BORDER_WIDTH_THIN = 1;
+	private static final int BORDER_WIDTH_MEDIUM = 3;
+	private static final int BORDER_WIDTH_THICK = 5;
+
+	private static int getBorderWidth(LexicalUnit lu, float fontSize, int ppi) {
+		if (isLength(lu)) {
+			return getIntLength(lu, fontSize, ppi);
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			if (s.equals(CSS.THIN)) {
+				return BORDER_WIDTH_THIN;
+			} else if (s.equals(CSS.MEDIUM)) {
+				return BORDER_WIDTH_MEDIUM;
+			} else if (s.equals(CSS.THICK)) {
+				return BORDER_WIDTH_THICK;
+			} else {
+				return 0;
+			}
+		} else {
+			return 0;
+		}
+	}
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param name
+	 *            Name of the property.
+	 * @param borderStyleName
+	 *            Name of the corresponding border style property. For example,
+	 *            if name is CSS.BORDER_TOP_WIDTH, then borderStyleName should
+	 *            be CSS.BORDER_TOP_STYLE.
+	 * @param axis
+	 *            AXIS_HORIZONTAL (for left and right borders) or AXIS_VERTICAL
+	 *            (for top and bottom borders).
+	 */
+	public BorderWidthProperty(String name, String borderStyleName, Axis axis) {
+		super(name);
+		this.borderStyleName = borderStyleName;
+		this.axis = axis;
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a border width.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isBorderWidth(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (isLength(lu)) {
+			return true;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.THIN) || s.equals(CSS.MEDIUM)
+					|| s.equals(CSS.THICK);
+		} else {
+			return false;
+		}
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		return Integer.valueOf(calculateInternal(lu, parentStyles, styles));
+	}
+
+	private int calculateInternal(LexicalUnit lu, Styles parentStyles,
+			Styles styles) {
+
+		DisplayDevice device = DisplayDevice.getCurrent();
+		int ppi = this.axis == Axis.HORIZONTAL ? device.getHorizontalPPI()
+				: device.getVerticalPPI();
+
+		String borderStyle = (String) styles.get(this.borderStyleName);
+
+		if (borderStyle.equals(CSS.NONE) || borderStyle.equals(CSS.HIDDEN)) {
+			return 0;
+		} else if (isBorderWidth(lu)) {
+			return getBorderWidth(lu, styles.getFontSize(), ppi);
+		} else if (isInherit(lu) && parentStyles != null) {
+			return ((Integer) parentStyles.get(this.getName())).intValue();
+		} else {
+			// not specified, "none", or other unknown value
+			return BORDER_WIDTH_MEDIUM;
+		}
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/CSS.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/CSS.java
new file mode 100644
index 0000000..88c7a37
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/CSS.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Dave Holroyd - Implement text decoration
+ *     John Austin - More complete CSS constants.  Add the colour "orange".
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+/**
+ * CSS constants.
+ */
+public interface CSS {
+
+	// property names
+	public static final String AZIMUTH = "azimuth";
+	public static final String BACKGROUND = "background";
+	public static final String BACKGROUND_ATTACHMENT = "background-attachment";
+	public static final String BACKGROUND_COLOR = "background-color";
+	public static final String BACKGROUND_IMAGE = "background-image";
+	public static final String BACKGROUND_POSITION = "background-position";
+	public static final String BACKGROUND_REPEAT = "background-repeat";
+	public static final String BORDER = "border";
+	public static final String BORDER_BOTTOM = "border-bottom";
+	public static final String BORDER_BOTTOM_COLOR = "border-bottom-color";
+	public static final String BORDER_BOTTOM_STYLE = "border-bottom-style";
+	public static final String BORDER_BOTTOM_WIDTH = "border-bottom-width";
+	public static final String BORDER_COLOR = "border-color";
+	public static final String BORDER_LEFT = "border-left";
+	public static final String BORDER_LEFT_COLOR = "border-left-color";
+	public static final String BORDER_LEFT_STYLE = "border-left-style";
+	public static final String BORDER_LEFT_WIDTH = "border-left-width";
+	public static final String BORDER_RIGHT = "border-right";
+	public static final String BORDER_RIGHT_COLOR = "border-right-color";
+	public static final String BORDER_RIGHT_STYLE = "border-right-style";
+	public static final String BORDER_RIGHT_WIDTH = "border-right-width";
+	public static final String BORDER_SPACING = "border-spacing";
+	public static final String BORDER_STYLE = "border-style";
+	public static final String BORDER_TOP = "border-top";
+	public static final String BORDER_TOP_COLOR = "border-top-color";
+	public static final String BORDER_TOP_STYLE = "border-top-style";
+	public static final String BORDER_TOP_WIDTH = "border-top-width";
+	public static final String BORDER_WIDTH = "border-width";
+	public static final String BOTTOM = "bottom";
+	public static final String CAPTION_SIDE = "caption-side";
+	public static final String CLEAR = "clear";
+	public static final String CLIP = "clip";
+	public static final String COLOR = "color";
+	public static final String CONTENT = "content";
+	public static final String COUNTER_INCREMENT = "counter-increment";
+	public static final String COUNTER_RESET = "counter-reset";
+	public static final String CUE = "cue";
+	public static final String CUE_AFTER = "cue-after";
+	public static final String CUE_BEFORE = "cue-before";
+	public static final String CURSOR = "cursor";
+	public static final String DIRECTION = "direction";
+	public static final String DISPLAY = "display";
+	public static final String ELEVATION = "elevation";
+	public static final String EMPTY_CELLS = "empty-cells";
+	public static final String FLOAT = "float";
+	public static final String FONT = "font";
+	public static final String FONT_FAMILY = "font-family";
+	public static final String FONT_SIZE = "font-size";
+	public static final String FONT_STYLE = "font-style";
+	public static final String FONT_VARIANT = "font-variant";
+	public static final String FONT_WEIGHT = "font-weight";
+	public static final String HEIGHT = "height";
+	public static final String HIDDEN = "hidden";
+	public static final String LETTER_SPACING = "letter-spacing";
+	public static final String LINE_HEIGHT = "line-height";
+	public static final String LIST_STYLE = "list-style";
+	public static final String LIST_STYLE_IMAGE = "list-style-image";
+	public static final String LIST_STYLE_POSITION = "list-style-position";
+	public static final String LIST_STYLE_TYPE = "list-style-type";
+	public static final String MARGIN = "margin";
+	public static final String MARGIN_BOTTOM = "margin-bottom";
+	public static final String MARGIN_LEFT = "margin-left";
+	public static final String MARGIN_RIGHT = "margin-right";
+	public static final String MARGIN_TOP = "margin-top";
+	public static final String MAX_HEIGHT = "max-height";
+	public static final String MAX_WIDTH = "max-width";
+	public static final String MIN_HEIGHT = "min-height";
+	public static final String MIN_WIDTH = "min-width";
+	public static final String ORPHANS = "orphans";
+	public static final String OUTLINE = "outline";
+	public static final String OVERFLOW = "overflow";
+	public static final String OUTLINE_COLOR = "outline-color";
+	public static final String OUTLINE_STYLE = "outline-style";
+	public static final String OUTLINE_WIDTH = "outline-width";
+	public static final String PADDING = "padding";
+	public static final String PADDING_BOTTOM = "padding-bottom";
+	public static final String PADDING_LEFT = "padding-left";
+	public static final String PADDING_RIGHT = "padding-right";
+	public static final String PADDING_TOP = "padding-top";
+	public static final String PAUSE = "pause";
+	public static final String PAUSE_AFTER = "pause-after";
+	public static final String PAUSE_BEFORE = "pause-before";
+	public static final String PITCH = "pitch";
+	public static final String PITCH_RANGE = "pitch-range";
+	public static final String PLAY_DURING = "play-during";
+	public static final String POSITION = "position";
+	public static final String QUOTES = "quotes";
+	public static final String RICHNESS = "richness";
+	public static final String SPEAK = "speak";
+	public static final String SPEAK_HEADER = "speak-header";
+	public static final String SPEAK_NUMERAL = "speak-numeral";
+	public static final String SPEAK_PUNCTUATION = "speak-punctuation";
+	public static final String SPEECH_RATE = "speech-rate";
+	public static final String STRESS = "stress";
+	public static final String TABLE_LAYOUT = "table-layout";
+	public static final String TEXT_ALIGN = "text-align";
+	public static final String TEXT_DECORATION = "text-decoration";
+	public static final String TEXT_INDENT = "text-indent";
+	public static final String TEXT_TRANSFORM = "text-transform";
+	public static final String UNICODE_BIDI = "unicode-bidi";
+	public static final String VERTICAL_ALIGN = "vertical-align";
+	public static final String VISIBILITY = "visibility";
+	public static final String VOICE_FAMILY = "voice-family";
+	public static final String VOLUME = "volume";
+	public static final String WHITE_SPACE = "white-space";
+	public static final String WIDOWS = "widows";
+	public static final String WIDTH = "width";
+	public static final String WORD_SPACING = "word-spacing";
+	public static final String Z_SPACING = "z-spacing";
+
+	// suffixes to BORDER_XXX
+	public static final String COLOR_SUFFIX = "-color";
+	public static final String STYLE_SUFFIX = "-style";
+	public static final String WIDTH_SUFFIX = "-width";
+
+	// color values
+	public static final String AQUA = "aqua";
+	public static final String BLACK = "black";
+	public static final String BLUE = "blue";
+	public static final String FUCHSIA = "fuchsia";
+	public static final String GRAY = "gray";
+	public static final String GREEN = "green";
+	public static final String LIME = "lime";
+	public static final String MAROON = "maroon";
+	public static final String NAVY = "navy";
+	public static final String OLIVE = "olive";
+	public static final String ORANGE = "orange";
+	public static final String PURPLE = "purple";
+	public static final String RED = "red";
+	public static final String SILVER = "silver";
+	public static final String TEAL = "teal";
+	public static final String WHITE = "white";
+	public static final String YELLOW = "yellow";
+
+	// list-style values
+	public static final String ARMENIAN = "armenian";
+	public static final String CIRCLE = "circle";
+	public static final String CJK_IDEOGRAPHIC = "cjk-ideographic";
+	public static final String DECIMAL = "decimal";
+	public static final String DECIMAL_LEADING_ZERO = "decimal-leading-zero";
+	public static final String DISC = "disc";
+	public static final String GEORGIAN = "georgian";
+	public static final String HEBREW = "hebrew";
+	public static final String HIRAGANA = "hiragana";
+	public static final String HIRAGANA_IROHA = "hiragana-iroha";
+	public static final String KATAKANA = "katakana";
+	public static final String KATAKANA_IROHA = "katakana-iroha";
+	public static final String LOWER_ALPHA = "lower-alpha";
+	public static final String LOWER_GREEK = "lower-greek";
+	public static final String LOWER_LATIN = "lower-latin";
+	public static final String LOWER_ROMAN = "lower-roman";
+	public static final String SQUARE = "square";
+	public static final String UPPER_ALPHA = "upper-alpha";
+	public static final String UPPER_LATIN = "upper-latin";
+	public static final String UPPER_ROMAN = "upper-roman";
+
+	// other values
+	public static final String BLINK = "blink";
+	public static final String BLOCK = "block";
+	public static final String BOLD = "bold";
+	public static final String BOLDER = "bolder";
+	public static final String CENTER = "center";
+	public static final String DASHED = "dashed";
+	public static final String DOTTED = "dotted";
+	public static final String DOUBLE = "double";
+	public static final String GROOVE = "groove";
+	public static final String INLINE = "inline";
+	public static final String INLINE_BLOCK = "inline-block";
+	public static final String INLINE_TABLE = "inline-table";
+	public static final String INSET = "inset";
+	public static final String ITALIC = "italic";
+	public static final String JUSTIFY = "justify";
+	public static final String LARGE = "large";
+	public static final String LARGER = "larger";
+	public static final String LEFT = "left";
+	public static final String LIGHTER = "lighter";
+	public static final String LINE_THROUGH = "line-through";
+	public static final String LIST_ITEM = "list-item";
+	public static final String MEDIUM = "medium";
+	public static final String NONE = "none";
+	public static final String NORMAL = "normal";
+	public static final String NOWRAP = "nowrap";
+	public static final String OBLIQUE = "oblique";
+	public static final String OUTSET = "outset";
+	public static final String OVERLINE = "overline";
+	public static final String PRE = "pre";
+	public static final String RIDGE = "ridge";
+	public static final String RIGHT = "right";
+	public static final String RUN_IN = "run-in";
+	public static final String SOLID = "solid";
+	public static final String SMALL = "small";
+	public static final String SMALL_CAPS = "small-caps";
+	public static final String SMALLER = "smaller";
+	public static final String TABLE = "table";
+	public static final String TABLE_CAPTION = "table-caption";
+	public static final String TABLE_CELL = "table-cell";
+	public static final String TABLE_COLUMN = "table-column";
+	public static final String TABLE_COLUMN_GROUP = "table-column-group";
+	public static final String TABLE_FOOTER_GROUP = "table-footer-group";
+	public static final String TABLE_HEADER_GROUP = "table-header-group";
+	public static final String TABLE_ROW = "table-row";
+	public static final String TABLE_ROW_GROUP = "table-row-group";
+	public static final String THICK = "thick";
+	public static final String THIN = "thin";
+	public static final String UNDERLINE = "underline";
+	public static final String X_LARGE = "x-large";
+	public static final String X_SMALL = "x-small";
+	public static final String XX_LARGE = "xx-large";
+	public static final String XX_SMALL = "xx-small";
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ColorProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ColorProperty.java
new file mode 100644
index 0000000..1454d4a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ColorProperty.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Color-valued properties.
+ */
+public class ColorProperty extends AbstractProperty {
+
+	private static Map<String, Color> colorNames = new HashMap<String, Color>();
+
+	static {
+		colorNames.put(CSS.AQUA, new Color(0, 255, 255));
+		colorNames.put(CSS.BLACK, new Color(0, 0, 0));
+		colorNames.put(CSS.BLUE, new Color(0, 0, 255));
+		colorNames.put(CSS.FUCHSIA, new Color(255, 0, 255));
+		colorNames.put(CSS.GRAY, new Color(128, 128, 128));
+		colorNames.put(CSS.GREEN, new Color(0, 128, 0));
+		colorNames.put(CSS.LIME, new Color(0, 255, 0));
+		colorNames.put(CSS.MAROON, new Color(128, 0, 0));
+		colorNames.put(CSS.NAVY, new Color(0, 0, 128));
+		colorNames.put(CSS.OLIVE, new Color(128, 128, 0));
+		colorNames.put(CSS.ORANGE, new Color(255, 165, 0));
+		colorNames.put(CSS.PURPLE, new Color(128, 0, 128));
+		colorNames.put(CSS.RED, new Color(255, 0, 0));
+		colorNames.put(CSS.SILVER, new Color(192, 192, 192));
+		colorNames.put(CSS.TEAL, new Color(0, 128, 128));
+		colorNames.put(CSS.WHITE, new Color(255, 255, 255));
+		colorNames.put(CSS.YELLOW, new Color(255, 255, 0));
+	}
+
+	/**
+	 * Class constructor. The names CSS.COLOR and CSS.BACKGROUND_COLOR are
+	 * treated specially, as follows.
+	 * 
+	 * <ul>
+	 * <li>If name is CSS.COLOR, it is inherited and defaults to black.</li>
+	 * <li>If name is CSS.BACKGROUND_COLOR, it is not inherited and defaults to
+	 * transparent (null).</li>
+	 * <li>Otherwise, it is not inherited and defaults to the current color.</li>
+	 * </ul>
+	 * 
+	 * <p>
+	 * Because of the default in the third case, the ColorProperty for CSS.COLOR
+	 * must be processed before any others.
+	 * </p>
+	 * 
+	 * @param name
+	 *            Name of the element.
+	 */
+	public ColorProperty(String name) {
+		super(name);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+
+		boolean inherit = isInherit(lu) || this.getName().equals(CSS.COLOR);
+
+		if (isColor(lu)) {
+			return getColor(lu);
+		} else if (inherit && parentStyles != null) {
+			return parentStyles.get(this.getName());
+		} else {
+			if (this.getName().equals(CSS.COLOR)) {
+				return Color.BLACK;
+			} else if (this.getName().equals(CSS.BACKGROUND_COLOR)) {
+				return null; // transparent
+			} else {
+				return styles.getColor();
+			}
+		}
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a color.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isColor(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT
+				&& colorNames.containsKey(lu.getStringValue())) {
+			return true;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	// ========================================================== PRIVATE
+
+	private static Color getColor(LexicalUnit lu) {
+		if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			if (colorNames.containsKey(s)) {
+				return (Color) colorNames.get(s);
+			} else {
+				return null;
+			}
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR) {
+			lu = lu.getParameters();
+			int red = getColorPart(lu);
+			lu = lu.getNextLexicalUnit(); // gobble comma
+			lu = lu.getNextLexicalUnit();
+			int green = getColorPart(lu);
+			lu = lu.getNextLexicalUnit(); // gobble comma
+			lu = lu.getNextLexicalUnit();
+			int blue = getColorPart(lu);
+
+			if (red == -1 || green == -1 || blue == -1) {
+				return null;
+			} else {
+				return new Color(red, green, blue);
+			}
+		} else {
+			System.out.println("WARNING: unsupported color type: " + lu);
+			return null;
+		}
+	}
+
+	/**
+	 * Converts one of the color channels into an int from 0-255, or -1 if
+	 * there's an error.
+	 */
+	private static int getColorPart(LexicalUnit lu) {
+		int value;
+		if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
+			value = lu.getIntegerValue();
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
+			value = Math.round(lu.getFloatValue() * 255);
+		} else {
+			System.out.println("WARNING: unsupported color part: " + lu);
+			return -1;
+		}
+		return Math.max(0, Math.min(255, value));
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/DisplayProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/DisplayProperty.java
new file mode 100644
index 0000000..f51a661
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/DisplayProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS 'display' property.
+ */
+public class DisplayProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public DisplayProperty() {
+		super(CSS.DISPLAY);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+
+		if (isDisplay(lu)) {
+			return lu.getStringValue();
+		} else if (isInherit(lu) && parentStyles != null) {
+			return parentStyles.getDisplay();
+		} else {
+			// not specified or other unknown value
+			return CSS.INLINE;
+		}
+	}
+
+	// ======================================================== PRIVATE
+
+	/**
+	 * Returns true if the value of the given LexicalUnit represents a valid
+	 * value for this property.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to inspect.
+	 */
+	private static boolean isDisplay(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.BLOCK) || s.equals(CSS.INLINE)
+					|| s.equals(CSS.INLINE_BLOCK) || s.equals(CSS.INLINE_TABLE)
+					|| s.equals(CSS.LIST_ITEM) || s.equals(CSS.NONE)
+					|| s.equals(CSS.RUN_IN) || s.equals(CSS.TABLE)
+					|| s.equals(CSS.TABLE_CAPTION) || s.equals(CSS.TABLE_CELL)
+					|| s.equals(CSS.TABLE_COLUMN)
+					|| s.equals(CSS.TABLE_COLUMN_GROUP)
+					|| s.equals(CSS.TABLE_FOOTER_GROUP)
+					|| s.equals(CSS.TABLE_HEADER_GROUP)
+					|| s.equals(CSS.TABLE_ROW) || s.equals(CSS.TABLE_ROW_GROUP);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontFamilyProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontFamilyProperty.java
new file mode 100644
index 0000000..93b5ae7
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontFamilyProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Florian Thienel - bug 304413 - fix immutable array issue.
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS 'font-family' property.
+ */
+public class FontFamilyProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public FontFamilyProperty() {
+		super(CSS.FONT_FAMILY);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isFontFamily(lu)) {
+			return getFontFamilies(lu);
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getFontFamilies();
+			} else {
+				String[] fonts = new String[DEFAULT_FONT_FAMILY.length];
+				System.arraycopy(DEFAULT_FONT_FAMILY, 0, fonts, 0, DEFAULT_FONT_FAMILY.length);
+				return fonts;
+			}
+		}
+	}
+	
+	private static final String[] DEFAULT_FONT_FAMILY = new String[] { "sans-serif" };
+
+	private static boolean isFontFamily(LexicalUnit lu) {
+		return lu != null
+				&& (lu.getLexicalUnitType() == LexicalUnit.SAC_STRING_VALUE || lu
+						.getLexicalUnitType() == LexicalUnit.SAC_IDENT);
+	}
+
+	private static String[] getFontFamilies(LexicalUnit lu) {
+		List<String> list = new ArrayList<String>();
+		while (lu != null) {
+			if (lu.getLexicalUnitType() == LexicalUnit.SAC_STRING_VALUE
+					|| lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+
+				list.add(lu.getStringValue());
+			}
+			lu = lu.getNextLexicalUnit();
+		}
+		return list.toArray(new String[list.size()]);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontSizeProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontSizeProperty.java
new file mode 100644
index 0000000..bd3112b
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontSizeProperty.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.core.DisplayDevice;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS font-size property. Note that other lengths depend on the computed
+ * value of this property, so this should be evaluated early on in the
+ * stylesheet, before any other lengths.
+ */
+public class FontSizeProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor,
+	 */
+	public FontSizeProperty() {
+		super(CSS.FONT_SIZE);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		return new Float(this.calculateInternal(lu, parentStyles, styles));
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a font size.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isFontSize(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (isLength(lu)) {
+			return true;
+		} else if (isPercentage(lu)) {
+			return true;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.XX_SMALL) || s.equals(CSS.X_SMALL)
+					|| s.equals(CSS.SMALL) || s.equals(CSS.MEDIUM)
+					|| s.equals(CSS.LARGE) || s.equals(CSS.X_LARGE)
+					|| s.equals(CSS.XX_LARGE) || s.equals(CSS.SMALLER)
+					|| s.equals(CSS.LARGER);
+		} else {
+			return false;
+		}
+	}
+
+	// ======================================================== PRIVATE
+
+	private float calculateInternal(LexicalUnit lu, Styles parentStyles,
+			Styles styles) {
+
+		DisplayDevice device = DisplayDevice.getCurrent();
+		float baseFontSize = DEFAULT_FONT_SIZE_POINTS * device.getVerticalPPI()
+				/ 72;
+
+		if (parentStyles != null) {
+			baseFontSize = parentStyles.getFontSize();
+		}
+
+		if (lu == null) {
+			return baseFontSize;
+		} else if (isLength(lu)) {
+			return getFloatLength(lu, baseFontSize, device.getVerticalPPI());
+		} else if (isPercentage(lu)) {
+			return baseFontSize * lu.getFloatValue() / 100;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+
+			if (s.equals(CSS.XX_SMALL)) {
+				return baseFontSize * FONT_FACTOR_XX_SMALL;
+
+			} else if (s.equals(CSS.X_SMALL)) {
+				return baseFontSize * FONT_FACTOR_X_SMALL;
+
+			} else if (s.equals(CSS.SMALL)) {
+				return baseFontSize * FONT_FACTOR_SMALL;
+
+			} else if (s.equals(CSS.MEDIUM)) {
+				return baseFontSize * FONT_FACTOR_MEDIUM;
+
+			} else if (s.equals(CSS.LARGE)) {
+				return baseFontSize * FONT_FACTOR_LARGE;
+
+			} else if (s.equals(CSS.X_LARGE)) {
+				return baseFontSize * FONT_FACTOR_X_LARGE;
+
+			} else if (s.equals(CSS.XX_LARGE)) {
+				return baseFontSize * FONT_FACTOR_XX_LARGE;
+
+			} else if (s.equals(CSS.SMALLER)) {
+				return baseFontSize / FONT_SIZE_FACTOR;
+
+			} else if (s.equals(CSS.LARGER)) {
+				return baseFontSize * FONT_SIZE_FACTOR;
+
+			} else {
+				return baseFontSize;
+			}
+		} else {
+			return baseFontSize;
+		}
+
+	}
+
+	private static final float DEFAULT_FONT_SIZE_POINTS = 12;
+
+	// relative size of adjacent font size names
+	private static final float FONT_SIZE_FACTOR = 1.2f;
+
+	// Sizes of named font sizes, relative to "medium"
+	private static final float FONT_FACTOR_MEDIUM = 1.0f;
+
+	private static final float FONT_FACTOR_SMALL = FONT_FACTOR_MEDIUM
+			/ FONT_SIZE_FACTOR;
+
+	private static final float FONT_FACTOR_X_SMALL = FONT_FACTOR_SMALL
+			/ FONT_SIZE_FACTOR;
+
+	private static final float FONT_FACTOR_XX_SMALL = FONT_FACTOR_X_SMALL
+			/ FONT_SIZE_FACTOR;
+
+	private static final float FONT_FACTOR_LARGE = FONT_FACTOR_MEDIUM
+			* FONT_SIZE_FACTOR;
+
+	private static final float FONT_FACTOR_X_LARGE = FONT_FACTOR_LARGE
+			* FONT_SIZE_FACTOR;
+
+	private static final float FONT_FACTOR_XX_LARGE = FONT_FACTOR_X_LARGE
+			* FONT_SIZE_FACTOR;
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontStyleProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontStyleProperty.java
new file mode 100644
index 0000000..8daa8eb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontStyleProperty.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS font-style property.
+ */
+public class FontStyleProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public FontStyleProperty() {
+		super(CSS.FONT_STYLE);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isFontStyle(lu)) {
+			return lu.getStringValue();
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getFontStyle();
+			} else {
+				return CSS.NORMAL;
+			}
+		}
+
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a font style.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isFontStyle(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NORMAL) || s.equals(CSS.ITALIC)
+					|| s.equals(CSS.OBLIQUE);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontVariantProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontVariantProperty.java
new file mode 100644
index 0000000..19f1382
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontVariantProperty.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS font-variant property.
+ */
+public class FontVariantProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public FontVariantProperty() {
+		super(CSS.FONT_VARIANT);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isFontVariant(lu)) {
+			return lu.getStringValue();
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getFontStyle();
+			} else {
+				return CSS.NORMAL;
+			}
+		}
+
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a font variant.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isFontVariant(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NORMAL) || s.equals(CSS.SMALL_CAPS);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontWeightProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontWeightProperty.java
new file mode 100644
index 0000000..2502ff3
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/FontWeightProperty.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS font-weight property.
+ */
+public class FontWeightProperty extends AbstractProperty {
+
+	private static final int FONT_WEIGHT_NORMAL = 400;
+	private static final int FONT_WEIGHT_BOLD = 700;
+
+	/**
+	 * Class constructor.
+	 */
+	public FontWeightProperty() {
+		super(CSS.FONT_WEIGHT);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		return Integer.valueOf(calculateInternal(lu, parentStyles, styles));
+	}
+
+	public int calculateInternal(LexicalUnit lu, Styles parentStyles,
+			Styles styles) {
+		if (isFontWeight(lu)) {
+			return getFontWeight(lu, parentStyles);
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getFontWeight();
+			} else {
+				return FONT_WEIGHT_NORMAL;
+			}
+		}
+
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a font weight.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isFontWeight(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
+			return true;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NORMAL) || s.equals(CSS.BOLD)
+					|| s.equals(CSS.BOLDER) || s.equals(CSS.LIGHTER);
+		} else {
+			return false;
+		}
+	}
+
+	private static int getFontWeight(LexicalUnit lu, Styles parentStyles) {
+		if (lu == null) {
+			return FONT_WEIGHT_NORMAL;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
+			return lu.getIntegerValue();
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			if (s.equals(CSS.NORMAL)) {
+				return FONT_WEIGHT_NORMAL;
+			} else if (s.equals(CSS.BOLD)) {
+				return FONT_WEIGHT_BOLD;
+			} else if (s.equals(CSS.BOLDER)) {
+				if (parentStyles != null) {
+					return parentStyles.getFontWeight() + 151;
+				} else {
+					return FONT_WEIGHT_BOLD;
+				}
+			} else if (s.equals(CSS.LIGHTER)) {
+				if (parentStyles != null) {
+					return parentStyles.getFontWeight() - 151;
+				} else {
+					return FONT_WEIGHT_NORMAL;
+				}
+			} else {
+				return FONT_WEIGHT_NORMAL;
+			}
+		} else {
+			return FONT_WEIGHT_NORMAL;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/IProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/IProperty.java
new file mode 100644
index 0000000..d50ede7
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/IProperty.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Represents a CSS property.
+ */
+public interface IProperty {
+
+	public static enum Axis {
+		HORIZONTAL, VERTICAL
+	};
+
+	/**
+	 * Returns the name of the property.
+	 */
+	public String getName();
+
+	/**
+	 * Calculates the value of a property given a LexicalUnit.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to interpret.
+	 * @param parentStyles
+	 *            Styles of the parent element. These are used when the property
+	 *            inherits a value.
+	 * @param styles
+	 *            Styles currently in effect. Often, the calculated value
+	 *            depends on previously calculated styles such as font size and
+	 *            color.
+	 * @param element
+	 *            The current element for which this property is calculated. May
+	 *            be null.
+	 */
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LengthProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LengthProperty.java
new file mode 100644
index 0000000..bf76a38
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LengthProperty.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin;
+import org.eclipse.wst.xml.vex.core.internal.core.DisplayDevice;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.Parser;
+
+/**
+ * A property that represents lengths, such as a margin or padding.
+ */
+public class LengthProperty extends AbstractProperty {
+
+	private final Axis axis;
+
+	public LengthProperty(final String name, final Axis axis) {
+		super(name);
+		this.axis = axis;
+	}
+
+	public Object calculate(final LexicalUnit lu, final Styles parentStyles, final Styles styles, final Element element) {
+		final int ppi = getPpi();
+		if (isAttr(lu))
+			return calculate(parseAttribute(lu, element), parentStyles, styles, element);
+		if (isLength(lu)) {
+			final int length = getIntLength(lu, styles.getFontSize(), ppi);
+			return RelativeLength.createAbsolute(length);
+		} else if (isPercentage(lu))
+			return RelativeLength.createRelative(lu.getFloatValue() / 100);
+		else if (isInherit(lu) && parentStyles != null)
+			return parentStyles.get(getName());
+		else
+			// not specified, "auto", or other unknown value
+			return RelativeLength.createAbsolute(0);
+	}
+
+	private static boolean isAttr(final LexicalUnit lexicalUnit) {
+		return lexicalUnit != null && lexicalUnit.getLexicalUnitType() == LexicalUnit.SAC_ATTR;
+	}
+
+	private static LexicalUnit parseAttribute(final LexicalUnit lexicalUnit, final Element element) {
+		final String attributeName = lexicalUnit.getStringValue();
+		final String attributeValue = element.getAttributeValue(attributeName);
+		if (attributeValue == null)
+			return null;
+		final Parser parser = StyleSheetReader.createParser();
+		try {
+			return parser.parsePropertyValue(new InputSource(new StringReader(attributeValue)));
+		} catch (final CSSException e) {
+			VEXCorePlugin.getInstance().getLog().log(new Status(IStatus.ERROR, VEXCorePlugin.ID, e.getMessage(), e));
+			return null;
+		} catch (final IOException e) {
+			VEXCorePlugin.getInstance().getLog().log(new Status(IStatus.ERROR, VEXCorePlugin.ID, e.getMessage(), e));
+			return null;
+		}
+	}
+
+	private int getPpi() {
+		final DisplayDevice device = DisplayDevice.getCurrent();
+		final int ppi = axis == Axis.HORIZONTAL ? device.getHorizontalPPI() : device.getVerticalPPI();
+		return ppi;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LineHeightProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LineHeightProperty.java
new file mode 100644
index 0000000..f9173fb
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/LineHeightProperty.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.core.DisplayDevice;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS line-height property.
+ */
+public class LineHeightProperty extends AbstractProperty {
+
+	private static final float LINE_HEIGHT_NORMAL = 1.2f;
+
+	/**
+	 * Class constructor.
+	 */
+	public LineHeightProperty() {
+		super(CSS.LINE_HEIGHT);
+	}
+
+	/**
+	 * Calculates the value of the property given a LexicalUnit. Returns a
+	 * RelativeLength that is relative to the current font size.
+	 */
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+
+		int ppi = DisplayDevice.getCurrent().getVerticalPPI();
+
+		if (isLength(lu)) {
+			return RelativeLength.createAbsolute(Math.round(getIntLength(lu,
+					styles.getFontSize(), ppi)
+					/ styles.getFontSize()));
+		} else if (isNumber(lu)) {
+			if (getNumber(lu) <= 0) {
+				return RelativeLength.createRelative(LINE_HEIGHT_NORMAL);
+			} else {
+				return RelativeLength.createRelative(getNumber(lu));
+			}
+		} else if (isPercentage(lu)) {
+			if (lu.getFloatValue() <= 0) {
+				return RelativeLength.createRelative(LINE_HEIGHT_NORMAL);
+			} else {
+				return RelativeLength.createRelative(lu.getFloatValue() / 100);
+			}
+		} else {
+			// not specified, "inherit", or other unknown value
+			if (parentStyles == null) {
+				return RelativeLength.createRelative(LINE_HEIGHT_NORMAL);
+			} else {
+				return (RelativeLength) parentStyles.get(CSS.LINE_HEIGHT);
+			}
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ListStyleTypeProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ListStyleTypeProperty.java
new file mode 100644
index 0000000..dcea615
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/ListStyleTypeProperty.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS list-style-type property.
+ */
+public class ListStyleTypeProperty extends AbstractProperty {
+
+	public ListStyleTypeProperty() {
+		super(CSS.LIST_STYLE_TYPE);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isListStyleType(lu)) {
+			return lu.getStringValue();
+		} else {
+			if (parentStyles == null) {
+				return CSS.DISC;
+			} else {
+				return parentStyles.getListStyleType();
+			}
+		}
+
+	}
+
+	private static boolean isListStyleType(LexicalUnit lu) {
+
+		if (lu == null || lu.getLexicalUnitType() != LexicalUnit.SAC_IDENT) {
+			return false;
+		}
+
+		String s = lu.getStringValue();
+		return s.equals(CSS.ARMENIAN) || s.equals(CSS.CIRCLE)
+				|| s.equals(CSS.CJK_IDEOGRAPHIC) || s.equals(CSS.DECIMAL)
+				|| s.equals(CSS.DECIMAL_LEADING_ZERO) || s.equals(CSS.DISC)
+				|| s.equals(CSS.GEORGIAN) || s.equals(CSS.HEBREW)
+				|| s.equals(CSS.HIRAGANA) || s.equals(CSS.HIRAGANA_IROHA)
+				|| s.equals(CSS.KATAKANA) || s.equals(CSS.KATAKANA_IROHA)
+				|| s.equals(CSS.LOWER_ALPHA) || s.equals(CSS.LOWER_GREEK)
+				|| s.equals(CSS.LOWER_LATIN) || s.equals(CSS.LOWER_ROMAN)
+				|| s.equals(CSS.NONE) || s.equals(CSS.SQUARE)
+				|| s.equals(CSS.UPPER_ALPHA) || s.equals(CSS.UPPER_LATIN)
+				|| s.equals(CSS.UPPER_ROMAN);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PropertyDecl.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PropertyDecl.java
new file mode 100644
index 0000000..e28b922
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PropertyDecl.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Florian Thienel - bug 306639 - remove serializability from StyleSheet
+ *                       and dependend classes
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Represents a particular CSS property declaration.
+ */
+public class PropertyDecl {
+
+	private final Rule rule;
+	private final String property;
+	private final LexicalUnit value;
+	private final boolean important;
+	
+	/**
+	 * Class constructor.
+	 */
+	public PropertyDecl(Rule rule, String property, LexicalUnit value,
+			boolean important) {
+		this.rule = rule;
+		this.property = property;
+		this.value = value;
+		this.important = important;
+	}
+
+	/**
+	 * Return the value of the <code>important</code> property.
+	 */
+	public boolean isImportant() {
+		return this.important;
+	}
+
+	/**
+	 * Return the value of the <code>property</code> property.
+	 */
+	public String getProperty() {
+		return this.property;
+	}
+
+	/**
+	 * Return the value of the <code>rule</code> property.
+	 */
+	public Rule getRule() {
+		return this.rule;
+	}
+
+	/**
+	 * Return the value of the <code>value</code> property.
+	 */
+	public LexicalUnit getValue() {
+		return this.value;
+	}
+	
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PseudoElement.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PseudoElement.java
new file mode 100644
index 0000000..0c73081
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/PseudoElement.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Represents a :before or :after pseudo-element.
+ */
+public class PseudoElement extends Element {
+
+	public static final String AFTER = "after";
+	public static final String BEFORE = "before";
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param parent
+	 *            Parent element to this pseudo-element.
+	 * @param name
+	 *            Name of this pseudo-element, e.g. PseudoElement.BEFORE.
+	 */
+	public PseudoElement(Element parent, String name) {
+		super(name);
+		this.setParent(parent);
+	}
+
+	/**
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object o) {
+		if (o == null || o.getClass() != this.getClass()) {
+			return false;
+		}
+		PseudoElement other = (PseudoElement) o;
+		return this.getParent() == other.getParent()
+				&& this.getQualifiedName().equals(other.getQualifiedName());
+	}
+
+	/**
+	 * @see java.lang.Object#hashCode()
+	 */
+	public int hashCode() {
+		return this.getParent().hashCode() + this.getQualifiedName().hashCode();
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/RelativeLength.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/RelativeLength.java
new file mode 100644
index 0000000..d88f4cc
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/RelativeLength.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+
+/**
+ * A length that may be expressed as an absolute or relative value.
+ */
+public class RelativeLength {
+
+	private float percentage;
+	private int absolute;
+	boolean isAbsolute;
+
+	private static RelativeLength ZERO = new RelativeLength(0, 0, true);
+
+	/**
+	 * Create a relative length representing an absolute value.
+	 * 
+	 * @return the new RelativeLength value.
+	 */
+	public static RelativeLength createAbsolute(int value) {
+		if (value == 0) {
+			return ZERO;
+		} else {
+			return new RelativeLength(0, value, true);
+		}
+	}
+
+	/**
+	 * Create a relative length representing a relative value.
+	 * 
+	 * @return the new RelativeLength value.
+	 */
+	public static RelativeLength createRelative(float percentage) {
+		return new RelativeLength(percentage, 0, false);
+	}
+
+	/**
+	 * Return the value of the length given a reference value. If this object
+	 * represents an absolute value, that value is simply returned. Otherwise,
+	 * returns the given reference length multiplied by the given percentage and
+	 * rounded to the nearest integer.
+	 * 
+	 * @param referenceLength
+	 *            reference length by which percentage lengths will by
+	 *            multiplied.
+	 * @return the actual value
+	 */
+	public int get(int referenceLength) {
+		if (this.isAbsolute) {
+			return this.absolute;
+		} else {
+			return Math.round(this.percentage * referenceLength);
+		}
+	}
+
+	// ==================================================== PRIVATE
+
+	private RelativeLength(float percentage, int absolute, boolean isAbsolute) {
+		this.percentage = percentage;
+		this.absolute = absolute;
+		this.isAbsolute = isAbsolute;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Rule.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Rule.java
new file mode 100644
index 0000000..cfd81e0
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Rule.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Dave Holroyd - Proper specificity for wildcard selector
+ *     John Austin - Implement sibling selectors
+ *     Florian Thienel - bug 306639 - remove serializability from StyleSheet
+ *                       and dependend classes
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.AttributeCondition;
+import org.w3c.css.sac.CombinatorCondition;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.ConditionalSelector;
+import org.w3c.css.sac.DescendantSelector;
+import org.w3c.css.sac.ElementSelector;
+import org.w3c.css.sac.NegativeSelector;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SiblingSelector;
+
+/**
+ * Represents a pairing of a selector with a list of styles. This does not
+ * exactly correspond to a rule in a style sheet; there is only one selector
+ * associated with an instance of this class, whereas multiple selectors may be
+ * associated with a style sheet rule.
+ * 
+ * Note: <code>Rule</code> implements the <code>Comparable</code> interface in
+ * order to be sorted by "specificity" as defined by the CSS spec. However, this
+ * ordering is <em>not</em> consistent with <code>equals</code> (rules with the
+ * same specificity may not be equal). Therefore, <code>Rule</code> objects
+ * should not be used with sorted collections or maps in the
+ * <code>java.util</code> package, unless a suitable <code>Comparator</code> is
+ * also used.
+ */
+public class Rule {
+
+	private final Selector selector;
+	private final List<PropertyDecl> propertyDecls = new ArrayList<PropertyDecl>();
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param selector
+	 *            Selector for the rule.
+	 */
+	public Rule(final Selector selector) {
+		this.selector = selector;
+	}
+
+	/**
+	 * Adds a property declaration to the rule.
+	 * 
+	 * @param decl
+	 *            new property declaration to add
+	 */
+	public void add(final PropertyDecl decl) {
+		propertyDecls.add(decl);
+	}
+
+	/**
+	 * Returns the selector for the rule.
+	 */
+	public Selector getSelector() {
+		return selector;
+	}
+
+	/**
+	 * Returns an array of the property declarations in this rule.
+	 */
+	public PropertyDecl[] getPropertyDecls() {
+		return propertyDecls.toArray(new PropertyDecl[propertyDecls.size()]);
+	}
+
+	/**
+	 * Calculates the specificity for the selector associated with this rule.
+	 * The specificity is represented as an integer whose base-10
+	 * representation, xxxyyyzzz, can be decomposed into the number of "id"
+	 * selectors (xxx), "class" selectors (yyy), and "element" selectors (zzz).
+	 * Composite selectors result in a recursive call.
+	 */
+	public int getSpecificity() {
+		return specificity(getSelector());
+	}
+
+	/**
+	 * Returns true if the given element matches this rule's selector.
+	 * 
+	 * @param element
+	 *            Element to check.
+	 */
+	public boolean matches(final Element element) {
+		return matches(selector, element);
+	}
+
+	// ==================================================== PRIVATE
+
+	/**
+	 * Returns true if the given element matches the given selector.
+	 */
+	private static boolean matches(final Selector selector, final Element element) {
+
+		if (element == null)
+			// This can happen when, e.g., with the rule "foo > *".
+			// Since the root element matches the "*", we check if
+			// its parent matches "foo", but of course its parent
+			// is null
+			return false;
+
+		final int selectorType = selector.getSelectorType();
+
+		switch (selectorType) {
+
+		case Selector.SAC_ANY_NODE_SELECTOR:
+			// You'd think we land here if we have a * rule, but instead
+			// it appears we get a SAC_ELEMENT_NODE_SELECTOR with
+			// localName==null
+			return true;
+
+		case Selector.SAC_CONDITIONAL_SELECTOR:
+			// This little wart is the product of a mismatch btn the CSS
+			// spec an the Flute parser. CSS treats pseudo-elements as elements
+			// attached to their parents, while Flute treats them like
+			// attributes
+			final ConditionalSelector cs = (ConditionalSelector) selector;
+			if (cs.getCondition().getConditionType() == Condition.SAC_PSEUDO_CLASS_CONDITION) {
+				if (element instanceof PseudoElement) {
+					final AttributeCondition ac = (AttributeCondition) cs.getCondition();
+					return ac.getValue().equals(element.getLocalName()) && matches(cs.getSimpleSelector(), element.getParent());
+				} else
+					return false;
+			} else
+				return matches(cs.getSimpleSelector(), element) && matchesCondition(cs.getCondition(), element);
+
+		case Selector.SAC_ELEMENT_NODE_SELECTOR:
+			final String elementName = element.getLocalName();
+			final String selectorName = ((ElementSelector) selector).getLocalName();
+			if (selectorName == null)
+				// We land here if we have a wildcard selector (*) or
+				// a pseudocondition w/o an element name (:before)
+				// Probably other situations too (conditional w/o element
+				// name? e.g. [attr=value])
+				return true;
+			if (selectorName.equals(elementName))
+				return true;
+			break;
+
+		case Selector.SAC_DESCENDANT_SELECTOR:
+			final DescendantSelector ds = (DescendantSelector) selector;
+			return matches(ds.getSimpleSelector(), element) && matchesAncestor(ds.getAncestorSelector(), element.getParent());
+
+		case Selector.SAC_CHILD_SELECTOR:
+			final DescendantSelector ds2 = (DescendantSelector) selector;
+			Element parent = element.getParent();
+			if (element instanceof PseudoElement)
+				parent = parent.getParent(); // sigh - this looks inelegant, but
+												// whatcha gonna do?
+			return matches(ds2.getSimpleSelector(), element) && matches(ds2.getAncestorSelector(), parent);
+
+		case Selector.SAC_DIRECT_ADJACENT_SELECTOR:
+
+			final SiblingSelector ss = (SiblingSelector) selector;
+
+			if (element != null && element.getParent() != null && matches(ss.getSiblingSelector(), element)) {
+
+				// find next sibling
+				final Iterator<Element> i = element.getParent().getChildElements().iterator();
+				Element e = null;
+				Element f = null;
+				while (i.hasNext() && e != element) {
+					f = e;
+					e = i.next();
+				}
+
+				if (e == element)
+					return matches(ss.getSelector(), f);
+			}
+			return false;
+
+		default:
+			// System.out.println("DEBUG: selector type not supported");
+			// TODO: warning: selector not supported
+		}
+		return false;
+	}
+
+	/**
+	 * Returns true if some ancestor of the given element matches the given
+	 * selector.
+	 */
+	private static boolean matchesAncestor(final Selector selector, final Element element) {
+		Element e = element;
+		while (e != null) {
+			if (matches(selector, e))
+				return true;
+			e = e.getParent();
+		}
+		return false;
+	}
+
+	private static boolean matchesCondition(final Condition condition, final Element element) {
+
+		AttributeCondition attributeCondition;
+		String attributeName;
+		String value;
+
+		switch (condition.getConditionType()) {
+		case Condition.SAC_PSEUDO_CLASS_CONDITION:
+			return false;
+
+		case Condition.SAC_ATTRIBUTE_CONDITION:
+			attributeCondition = (AttributeCondition) condition;
+			value = element.getAttributeValue(attributeCondition.getLocalName());
+			if (attributeCondition.getValue() != null)
+				return attributeCondition.getValue().equals(value);
+			else
+				return value != null;
+
+		case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION:
+		case Condition.SAC_CLASS_CONDITION:
+
+			attributeCondition = (AttributeCondition) condition;
+
+			if (condition.getConditionType() == Condition.SAC_CLASS_CONDITION)
+				attributeName = "class";
+			else
+				attributeName = attributeCondition.getLocalName();
+
+			value = element.getAttributeValue(attributeName);
+			if (value == null)
+				return false;
+			final StringTokenizer st = new StringTokenizer(value);
+			while (st.hasMoreTokens())
+				if (st.nextToken().equals(attributeCondition.getValue()))
+					return true;
+			return false;
+
+		case Condition.SAC_AND_CONDITION:
+			final CombinatorCondition ccon = (CombinatorCondition) condition;
+			return matchesCondition(ccon.getFirstCondition(), element) && matchesCondition(ccon.getSecondCondition(), element);
+
+		default:
+			// TODO: warning: condition not supported
+			System.out.println("Unsupported condition type: " + condition.getConditionType());
+		}
+		return false;
+	}
+
+	/**
+	 * Calculates the specificity for a selector.
+	 */
+	private static int specificity(final Selector sel) {
+		if (sel instanceof ElementSelector) {
+			if (((ElementSelector) sel).getLocalName() == null)
+				// actually wildcard selector -- see comment in matches()
+				return 0;
+			else
+				return 1;
+		} else if (sel instanceof DescendantSelector) {
+			final DescendantSelector ds = (DescendantSelector) sel;
+			return specificity(ds.getAncestorSelector()) + specificity(ds.getSimpleSelector());
+		} else if (sel instanceof SiblingSelector) {
+			final SiblingSelector ss = (SiblingSelector) sel;
+			return specificity(ss.getSelector()) + specificity(ss.getSiblingSelector());
+		} else if (sel instanceof NegativeSelector) {
+			final NegativeSelector ns = (NegativeSelector) sel;
+			return specificity(ns.getSimpleSelector());
+		} else if (sel instanceof ConditionalSelector) {
+			final ConditionalSelector cs = (ConditionalSelector) sel;
+			return specificity(cs.getCondition()) + specificity(cs.getSimpleSelector());
+		} else
+			return 0;
+	}
+
+	/**
+	 * Calculates the specificity for a condition.
+	 */
+	private static int specificity(final Condition cond) {
+		if (cond instanceof CombinatorCondition) {
+			final CombinatorCondition cc = (CombinatorCondition) cond;
+			return specificity(cc.getFirstCondition()) + specificity(cc.getSecondCondition());
+		} else if (cond instanceof AttributeCondition) {
+			if (cond.getConditionType() == Condition.SAC_ID_CONDITION)
+				return 1000000;
+			else
+				return 1000;
+		} else
+			return 0;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheet.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheet.java
new file mode 100644
index 0000000..8b6296d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheet.java
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Dave Holroyd - Implement font-weight:bolder
+ *     Dave Holroyd - Implement text decoration
+ *     John Austin - More complete CSS constants. Add the colour "orange".
+ *     Travis Haagen - bug 260806 - enhanced support for 'content' CSS property
+ *     Florian Thienel - bug 306639 - remove serializability from StyleSheet
+ *                       and dependend classes
+ *     Mohamadou Nassourou - Bug 298912 - rudimentary support for images 
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.eclipse.wst.xml.vex.core.internal.core.FontSpec;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Represents a CSS style sheet.
+ */
+public class StyleSheet {
+	
+	public static final StyleSheet NULL = new StyleSheet(Collections.<Rule> emptyList());
+	
+	private static final Comparator<PropertyDecl> PROPERTY_CASCADE_ORDERING =
+		new Comparator<PropertyDecl>() {
+		public int compare(PropertyDecl propertyDecl1,
+				           PropertyDecl propertyDecl2) {
+			if (propertyDecl1.isImportant() != propertyDecl2.isImportant()) {
+				return    (propertyDecl1.isImportant() ? 1 : 0)
+				        - (propertyDecl2.isImportant() ? 1 : 0);
+			}
+
+			return   propertyDecl1.getRule().getSpecificity()
+			       - propertyDecl2.getRule().getSpecificity();
+		}
+	};
+
+	/**
+	 * Standard CSS properties.
+	 */
+	private static final IProperty[] CSS_PROPERTIES = new IProperty[] {
+			new DisplayProperty(),
+			new LineHeightProperty(),
+			new ListStyleTypeProperty(),
+			new TextAlignProperty(),
+			new WhiteSpaceProperty(),
+
+			new FontFamilyProperty(),
+			new FontSizeProperty(),
+			new FontStyleProperty(),
+			new FontWeightProperty(),
+			new TextDecorationProperty(),
+
+			new ColorProperty(CSS.COLOR),
+			new ColorProperty(CSS.BACKGROUND_COLOR),
+
+			new LengthProperty(CSS.MARGIN_BOTTOM, IProperty.Axis.VERTICAL),
+			new LengthProperty(CSS.MARGIN_LEFT, IProperty.Axis.HORIZONTAL),
+			new LengthProperty(CSS.MARGIN_RIGHT, IProperty.Axis.HORIZONTAL),
+			new LengthProperty(CSS.MARGIN_TOP, IProperty.Axis.VERTICAL),
+
+			new LengthProperty(CSS.PADDING_BOTTOM, IProperty.Axis.VERTICAL),
+			new LengthProperty(CSS.PADDING_LEFT, IProperty.Axis.HORIZONTAL),
+			new LengthProperty(CSS.PADDING_RIGHT, IProperty.Axis.HORIZONTAL),
+			new LengthProperty(CSS.PADDING_TOP, IProperty.Axis.VERTICAL),
+
+			new ColorProperty(CSS.BORDER_BOTTOM_COLOR),
+			new ColorProperty(CSS.BORDER_LEFT_COLOR),
+			new ColorProperty(CSS.BORDER_RIGHT_COLOR),
+			new ColorProperty(CSS.BORDER_TOP_COLOR),
+			new BorderStyleProperty(CSS.BORDER_BOTTOM_STYLE),
+			new BorderStyleProperty(CSS.BORDER_LEFT_STYLE),
+			new BorderStyleProperty(CSS.BORDER_RIGHT_STYLE),
+			new BorderStyleProperty(CSS.BORDER_TOP_STYLE),
+			new BorderWidthProperty(CSS.BORDER_BOTTOM_WIDTH,
+					CSS.BORDER_BOTTOM_STYLE, IProperty.Axis.VERTICAL),
+			new BorderWidthProperty(CSS.BORDER_LEFT_WIDTH,
+					CSS.BORDER_LEFT_STYLE, IProperty.Axis.HORIZONTAL),
+			new BorderWidthProperty(CSS.BORDER_RIGHT_WIDTH,
+					CSS.BORDER_RIGHT_STYLE, IProperty.Axis.HORIZONTAL),
+			new BorderWidthProperty(CSS.BORDER_TOP_WIDTH, CSS.BORDER_TOP_STYLE,
+					IProperty.Axis.VERTICAL), new BorderSpacingProperty(), 
+			new LengthProperty(CSS.HEIGHT, IProperty.Axis.VERTICAL),
+			new LengthProperty(CSS.WIDTH, IProperty.Axis.HORIZONTAL),
+			new BackgroundImageProperty()
+		};
+
+	/**
+	 * The rules that comprise the stylesheet.
+	 */
+	private final List<Rule> 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 => WeakReference(styles). A weak hash map is used to avoid
+	 * leaking memory as elements are deleted. By using weak references to the
+	 * values, we also ensure the cache is memory-sensitive.
+	 * 
+	 * This must be transient to prevent it from being serialized, as
+	 * WeakHashMaps are not serializable.
+	 */
+	private transient Map<Element, WeakReference<Styles>> styleMap;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param rules
+	 *            Rules that constitute the style sheet.
+	 */
+	public StyleSheet(final Collection<Rule> rules) {
+		this.rules = new ArrayList<Rule>(rules);
+	}
+
+	/**
+	 * Flush any cached styles for the given element.
+	 * 
+	 * @param element
+	 *            IVEXElement for which styles are to be flushed.
+	 */
+	public void flushStyles(Element element) {
+		this.getStyleMap().remove(element);
+	}
+
+	/**
+	 * Returns a pseudo-element representing content to be displayed after the
+	 * given element, or null if there is no such content.
+	 * 
+	 * @param element
+	 *            Parent element of the pseudo-element.
+	 */
+	public Element getAfterElement(Element element) {
+		PseudoElement pe = new PseudoElement(element, PseudoElement.AFTER);
+		Styles styles = this.getStyles(pe);
+		if (styles == null) {
+			return null;
+		} else {
+			return pe;
+		}
+	}
+
+	/**
+	 * Returns a pseudo-element representing content to be displayed before the
+	 * given element, or null if there is no such content.
+	 * 
+	 * @param element
+	 *            Parent element of the pseudo-element.
+	 */
+	public Element getBeforeElement(Element element) {
+		PseudoElement pe = new PseudoElement(element, PseudoElement.BEFORE);
+		Styles styles = this.getStyles(pe);
+		if (styles == null) {
+			return null;
+		} else {
+			return pe;
+		}
+	}
+
+	/**
+	 * Returns the styles for the given element. The styles are cached to ensure
+	 * reasonable performance.
+	 * 
+	 * @param element
+	 *            Element for which to calculate the styles.
+	 */
+	public Styles getStyles(Element element) {
+
+		Styles styles;
+		WeakReference<Styles> ref = getStyleMap().get(element);
+
+		if (ref != null) {
+			// can't combine these tests, since calling ref.get() twice
+			// (once to query for null, once to get the value) would
+			// cause a race condition should the GC happen btn the two.
+			styles = ref.get();
+			if (styles != null) {
+				return styles;
+			}
+		} else if (getStyleMap().containsKey(element)) {
+			// this must be a pseudo-element with no content
+			return null;
+		}
+
+		styles = calculateStyles(element);
+
+		if (styles == null) {
+			// Yes, they can be null if element is a PseudoElement with no
+			// content property
+			getStyleMap().put(element, null);
+		} else {
+			getStyleMap().put(element, new WeakReference<Styles>(styles));
+		}
+
+		return styles;
+	}
+
+	private Styles calculateStyles(Element element) {
+
+		Styles styles = new Styles();
+		Styles parentStyles = null;
+		if (element.getParent() != null) {
+			parentStyles = this.getStyles(element.getParent());
+		}
+
+		Map<String, LexicalUnit> decls = getApplicableDeclarations(element);
+
+		LexicalUnit lexicalUnit;
+
+		// If we're finding a pseudo-element, look at the 'content' property
+		// first, since most of the time it'll be empty and we'll return null.
+		if (element instanceof PseudoElement) {
+			lexicalUnit = decls.get(CSS.CONTENT);
+			if (lexicalUnit == null)
+				return null;
+
+			List<String> content = new ArrayList<String>();
+			while (lexicalUnit != null) {
+				switch (lexicalUnit.getLexicalUnitType()) {
+				case LexicalUnit.SAC_STRING_VALUE :
+					// content: "A String"
+					content.add(lexicalUnit.getStringValue());
+					break;
+				case LexicalUnit.SAC_ATTR :
+					// content: attr(attributeName)
+					final String attributeValue = element.getParent().getAttributeValue(lexicalUnit.getStringValue());
+					if (attributeValue != null)
+						content.add(attributeValue);
+					break;
+				}
+				lexicalUnit = lexicalUnit.getNextLexicalUnit();
+			}
+			styles.setContent(content);
+		}
+
+		for (final IProperty property : CSS_PROPERTIES) {
+			lexicalUnit = decls.get(property.getName());
+			final Object value = property.calculate(lexicalUnit, parentStyles, styles, element);
+			styles.put(property.getName(), value);
+		}
+
+		// Now, map font-family, font-style, font-weight, and font-size onto
+		// an AWT font.
+
+		int styleFlags = FontSpec.PLAIN;
+		String fontStyle = styles.getFontStyle();
+		if (fontStyle.equals(CSS.ITALIC) || fontStyle.equals(CSS.OBLIQUE)) {
+			styleFlags |= FontSpec.ITALIC;
+		}
+		if (styles.getFontWeight() > 550) {
+			// 550 is halfway btn normal (400) and bold (700)
+			styleFlags |= FontSpec.BOLD;
+		}
+		String textDecoration = styles.getTextDecoration();
+		if (textDecoration.equals(CSS.UNDERLINE)) {
+			styleFlags |= FontSpec.UNDERLINE;
+		} else if (textDecoration.equals(CSS.OVERLINE)) {
+			styleFlags |= FontSpec.OVERLINE;
+		} else if (textDecoration.equals(CSS.LINE_THROUGH)) {
+			styleFlags |= FontSpec.LINE_THROUGH;
+		}
+
+		styles.setFont(new FontSpec(styles.getFontFamilies(), styleFlags, Math
+				.round(styles.getFontSize())));
+
+		return styles;
+	}
+	
+	/**
+	 * Returns the rules comprising this stylesheet.
+	 */
+	public List<Rule> getRules() {
+		return Collections.unmodifiableList(this.rules);
+	}
+	
+	/**
+	 * Returns all the declarations that apply to the given element.
+	 */
+	private Map<String, LexicalUnit> getApplicableDeclarations(final Element element) {
+		final List<PropertyDecl> rawDeclarationsForElement = findAllDeclarationsFor(element);
+
+		// 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 Map<String, PropertyDecl> distilledDeclarations = new HashMap<String, PropertyDecl>();
+		final Map<String, LexicalUnit> values = new HashMap<String, LexicalUnit>();
+		for (final PropertyDecl declaration : rawDeclarationsForElement) {
+			final PropertyDecl previousDeclaration = distilledDeclarations.get(declaration.getProperty());
+			if (previousDeclaration == null || !previousDeclaration.isImportant()
+					|| declaration.isImportant()) {
+				distilledDeclarations.put(declaration.getProperty(), declaration);
+				values.put(declaration.getProperty(), declaration.getValue());
+			}
+		}
+
+		return values;
+	}
+	
+	private List<PropertyDecl> findAllDeclarationsFor(final Element element) {
+		final List<PropertyDecl> rawDeclarations = new ArrayList<PropertyDecl>();
+		for (final Rule rule : rules) {
+			if (rule.matches(element)) {
+				PropertyDecl[] ruleDecls = rule.getPropertyDecls();
+				for (final PropertyDecl ruleDecl : ruleDecls) {
+					rawDeclarations.add(ruleDecl);
+				}
+			}
+		}
+		return rawDeclarations;
+	}
+
+	private Map<Element, WeakReference<Styles>> getStyleMap() {
+		if (styleMap == null) {
+			styleMap = new WeakHashMap<Element, WeakReference<Styles>>();
+		}
+		return styleMap;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheetReader.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheetReader.java
new file mode 100644
index 0000000..8255cf3
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/StyleSheetReader.java
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Florian Thienel - bug 306639 - remove serializability from StyleSheet
+ *                       and dependend classes
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.DocumentHandler;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.Parser;
+import org.w3c.css.sac.SACMediaList;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SelectorList;
+
+/**
+ * Driver for the creation of StyleSheet objects.
+ */
+public class StyleSheetReader {
+
+	/**
+	 * Class constructor.
+	 */
+	public StyleSheetReader() {
+
+	}
+
+	public static Parser createParser() {
+		// return new org.apache.batik.css.parser.Parser();
+		return new org.w3c.flute.parser.Parser();
+	}
+
+	/**
+	 * Creates a new StyleSheet object from a URL.
+	 * 
+	 * @param url
+	 *            URL from which to read the style sheet.
+	 */
+	public StyleSheet read(URL url) throws IOException {
+		return this.read(new InputSource(url.toString()), url);
+	}
+
+	/**
+	 * Creates a style sheet from a string. This is mainly used for small style
+	 * sheets within unit tests.
+	 * 
+	 * @param s
+	 *            String containing the style sheet.
+	 */
+	public StyleSheet read(String s) throws CSSException, IOException {
+		Reader reader = new CharArrayReader(s.toCharArray());
+		return this.read(new InputSource(reader), null);
+	}
+
+	/**
+	 * Creates a new Stylesheet from an input source.
+	 * 
+	 * @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.
+	 */
+	public StyleSheet read(InputSource inputSource, 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);
+	}
+
+	// ======================================================== PRIVATE
+
+	private static class StyleSheetBuilder implements DocumentHandler {
+
+		// The rules that will be added to the stylesheet
+		private final List<Rule> rules;
+
+		// The rules to which decls are currently being added
+		private List<Rule> currentRules;
+
+		// URL from which @import rules relative URIs are resolved.
+		// May be null!
+		private final URL url;
+
+		public StyleSheetBuilder(List<Rule> rules, URL url) {
+			this.rules = rules;
+			this.url = url;
+		}
+
+		// -------------------------------------------- DocumentHandler methods
+
+		public void comment(java.lang.String text) {
+		}
+
+		public void endDocument(InputSource source) {
+		}
+
+		public void endFontFace() {
+		}
+
+		public void endMedia(SACMediaList media) {
+		}
+
+		public void endPage(String name, String pseudo_page) {
+		}
+
+		public void endSelector(SelectorList selectors) {
+			this.rules.addAll(this.currentRules);
+			this.currentRules = null;
+		}
+
+		public void ignorableAtRule(String atRule) {
+		}
+
+		public void importStyle(String uri, SACMediaList media,
+				String defaultNamespaceURI) {
+
+			if (this.url == null) {
+				return;
+			}
+
+			try {
+				Parser parser = createParser();
+				URL importUrl = new URL(this.url, uri);
+				StyleSheetBuilder ssBuilder = new StyleSheetBuilder(rules,
+						importUrl);
+				parser.setDocumentHandler(ssBuilder);
+				parser.parseStyleSheet(new InputSource(importUrl.toString()));
+			} catch (CSSException e) {
+			} catch (IOException e) {
+			}
+
+		}
+
+		public void namespaceDeclaration(String prefix, String uri) {
+		}
+
+		public void property(String name, LexicalUnit value, boolean important) {
+			if (name.equals(CSS.BORDER)) {
+				this.expandBorder(value, important);
+			} else if (name.equals(CSS.BORDER_BOTTOM)) {
+				this.expandBorder(value, CSS.BORDER_BOTTOM, important);
+			} else if (name.equals(CSS.BORDER_LEFT)) {
+				this.expandBorder(value, CSS.BORDER_LEFT, important);
+			} else if (name.equals(CSS.BORDER_RIGHT)) {
+				this.expandBorder(value, CSS.BORDER_RIGHT, important);
+			} else if (name.equals(CSS.BORDER_TOP)) {
+				this.expandBorder(value, CSS.BORDER_TOP, important);
+			} else if (name.equals(CSS.BORDER_COLOR)) {
+				this.expandBorderColor(value, important);
+			} else if (name.equals(CSS.BORDER_STYLE)) {
+				this.expandBorderStyle(value, important);
+			} else if (name.equals(CSS.BORDER_WIDTH)) {
+				this.expandBorderWidth(value, important);
+			} else if (name.equals(CSS.FONT)) {
+				this.expandFont(value, important);
+			} else if (name.equals(CSS.MARGIN)) {
+				this.expandMargin(value, important);
+			} else if (name.equals(CSS.PADDING)) {
+				this.expandPadding(value, important);
+			} else {
+				this.addDecl(name, value, important);
+			}
+		}
+
+		public void startDocument(InputSource source) {
+		}
+
+		public void startFontFace() {
+		}
+
+		public void startMedia(SACMediaList media) {
+		}
+
+		public void startPage(String name, String pseudo_page) {
+		}
+
+		public void startSelector(SelectorList selectors) {
+			this.currentRules = new ArrayList<Rule>();
+			for (int i = 0; i < selectors.getLength(); i++) {
+				final Selector selector = selectors.item(i);
+				this.currentRules.add(new Rule(selector));
+			}
+		}
+
+		// ----------------------------------------- DocumentHandler methods end
+
+		// ======================================================= PRIVATE
+
+		/**
+		 * Adds a PropertyDecl to the current set of rules.
+		 */
+		private void addDecl(String name, LexicalUnit value, boolean important) {
+			for (Rule rule : this.currentRules) {
+				rule.add(new PropertyDecl(rule, name, value, important));
+			}
+		}
+
+		/**
+		 * Expand the "border" shorthand property.
+		 */
+		private void expandBorder(LexicalUnit value, boolean important) {
+			this.expandBorder(value, CSS.BORDER_BOTTOM, important);
+			this.expandBorder(value, CSS.BORDER_LEFT, important);
+			this.expandBorder(value, CSS.BORDER_RIGHT, important);
+			this.expandBorder(value, CSS.BORDER_TOP, important);
+		}
+
+		/**
+		 * Expand one of the the "border-xxx" shorthand properties. whichBorder
+		 * must be one of CSS.BORDER_BOTTOM, CSS.BORDER_LEFT, CSS.BORDER_RIGHT,
+		 * CSS.BORDER_TOP.
+		 */
+		private void expandBorder(LexicalUnit value, String whichBorder,
+				boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(whichBorder + CSS.COLOR_SUFFIX, value, important);
+				this.addDecl(whichBorder + CSS.STYLE_SUFFIX, value, important);
+				this.addDecl(whichBorder + CSS.WIDTH_SUFFIX, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			int i = 0;
+			if (BorderWidthProperty.isBorderWidth(lus[i])) {
+				this.addDecl(whichBorder + CSS.WIDTH_SUFFIX, lus[i], important);
+				i++;
+			}
+
+			if (i < lus.length && BorderStyleProperty.isBorderStyle(lus[i])) {
+				this.addDecl(whichBorder + CSS.STYLE_SUFFIX, lus[i], important);
+				i++;
+			}
+
+			if (i < lus.length && ColorProperty.isColor(lus[i])) {
+				this.addDecl(whichBorder + CSS.COLOR_SUFFIX, lus[i], important);
+				i++;
+			}
+
+		}
+
+		/**
+		 * Expand the "border-color" shorthand property.
+		 */
+		private void expandBorderColor(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.BORDER_TOP_COLOR, value, important);
+				this.addDecl(CSS.BORDER_LEFT_COLOR, value, important);
+				this.addDecl(CSS.BORDER_RIGHT_COLOR, value, important);
+				this.addDecl(CSS.BORDER_BOTTOM_COLOR, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			if (lus.length >= 4) {
+				this.addDecl(CSS.BORDER_TOP_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_COLOR, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_COLOR, lus[2], important);
+				this.addDecl(CSS.BORDER_LEFT_COLOR, lus[3], important);
+			} else if (lus.length == 3) {
+				this.addDecl(CSS.BORDER_TOP_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_COLOR, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_COLOR, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_COLOR, lus[2], important);
+			} else if (lus.length == 2) {
+				this.addDecl(CSS.BORDER_TOP_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_COLOR, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_COLOR, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_COLOR, lus[0], important);
+			} else if (lus.length == 1) {
+				this.addDecl(CSS.BORDER_TOP_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_COLOR, lus[0], important);
+				this.addDecl(CSS.BORDER_BOTTOM_COLOR, lus[0], important);
+			}
+		}
+
+		/**
+		 * Expand the "border-style" shorthand property.
+		 */
+		private void expandBorderStyle(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.BORDER_TOP_STYLE, value, important);
+				this.addDecl(CSS.BORDER_LEFT_STYLE, value, important);
+				this.addDecl(CSS.BORDER_RIGHT_STYLE, value, important);
+				this.addDecl(CSS.BORDER_BOTTOM_STYLE, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			if (lus.length >= 4) {
+				this.addDecl(CSS.BORDER_TOP_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_STYLE, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_STYLE, lus[2], important);
+				this.addDecl(CSS.BORDER_LEFT_STYLE, lus[3], important);
+			} else if (lus.length == 3) {
+				this.addDecl(CSS.BORDER_TOP_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_STYLE, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_STYLE, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_STYLE, lus[2], important);
+			} else if (lus.length == 2) {
+				this.addDecl(CSS.BORDER_TOP_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_STYLE, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_STYLE, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_STYLE, lus[0], important);
+			} else if (lus.length == 1) {
+				this.addDecl(CSS.BORDER_TOP_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_STYLE, lus[0], important);
+				this.addDecl(CSS.BORDER_BOTTOM_STYLE, lus[0], important);
+			}
+		}
+
+		/**
+		 * Expand the "border-width" shorthand property.
+		 */
+		private void expandBorderWidth(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.BORDER_TOP_WIDTH, value, important);
+				this.addDecl(CSS.BORDER_LEFT_WIDTH, value, important);
+				this.addDecl(CSS.BORDER_RIGHT_WIDTH, value, important);
+				this.addDecl(CSS.BORDER_BOTTOM_WIDTH, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			if (lus.length >= 4) {
+				this.addDecl(CSS.BORDER_TOP_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_WIDTH, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_WIDTH, lus[2], important);
+				this.addDecl(CSS.BORDER_LEFT_WIDTH, lus[3], important);
+			} else if (lus.length == 3) {
+				this.addDecl(CSS.BORDER_TOP_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_WIDTH, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_WIDTH, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_WIDTH, lus[2], important);
+			} else if (lus.length == 2) {
+				this.addDecl(CSS.BORDER_TOP_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_WIDTH, lus[1], important);
+				this.addDecl(CSS.BORDER_RIGHT_WIDTH, lus[1], important);
+				this.addDecl(CSS.BORDER_BOTTOM_WIDTH, lus[0], important);
+			} else if (lus.length == 1) {
+				this.addDecl(CSS.BORDER_TOP_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_LEFT_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_RIGHT_WIDTH, lus[0], important);
+				this.addDecl(CSS.BORDER_BOTTOM_WIDTH, lus[0], important);
+			}
+		}
+
+		/**
+		 * Expand the "font" shorthand property.
+		 */
+		private void expandFont(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.FONT_STYLE, value, important);
+				this.addDecl(CSS.FONT_VARIANT, value, important);
+				this.addDecl(CSS.FONT_WEIGHT, value, important);
+				this.addDecl(CSS.FONT_SIZE, value, important);
+				this.addDecl(CSS.FONT_FAMILY, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			int n = lus.length;
+			int i = 0;
+			if (i < n && FontStyleProperty.isFontStyle(lus[i])) {
+				this.addDecl(CSS.FONT_STYLE, lus[i], important);
+				i++;
+			}
+
+			if (i < n && FontVariantProperty.isFontVariant(lus[i])) {
+				this.addDecl(CSS.FONT_VARIANT, lus[i], important);
+				i++;
+			}
+
+			if (i < n && FontWeightProperty.isFontWeight(lus[i])) {
+				this.addDecl(CSS.FONT_WEIGHT, lus[i], important);
+				i++;
+			}
+
+			if (i < n && FontSizeProperty.isFontSize(lus[i])) {
+				this.addDecl(CSS.FONT_SIZE, lus[i], important);
+				i++;
+			}
+
+			if (i < n
+					&& lus[i].getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_SLASH) {
+				i++; // gobble slash
+				if (i < n) {
+					this.addDecl(CSS.LINE_HEIGHT, lus[i], important);
+				}
+				i++;
+			}
+
+			if (i < n) {
+				this.addDecl(CSS.FONT_FAMILY, lus[i], important);
+			}
+		}
+
+		/**
+		 * Expand the "margin" shorthand property.
+		 */
+		private void expandMargin(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.MARGIN_TOP, value, important);
+				this.addDecl(CSS.MARGIN_RIGHT, value, important);
+				this.addDecl(CSS.MARGIN_BOTTOM, value, important);
+				this.addDecl(CSS.MARGIN_LEFT, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			if (lus.length >= 4) {
+				this.addDecl(CSS.MARGIN_TOP, lus[0], important);
+				this.addDecl(CSS.MARGIN_RIGHT, lus[1], important);
+				this.addDecl(CSS.MARGIN_BOTTOM, lus[2], important);
+				this.addDecl(CSS.MARGIN_LEFT, lus[3], important);
+			} else if (lus.length == 3) {
+				this.addDecl(CSS.MARGIN_TOP, lus[0], important);
+				this.addDecl(CSS.MARGIN_LEFT, lus[1], important);
+				this.addDecl(CSS.MARGIN_RIGHT, lus[1], important);
+				this.addDecl(CSS.MARGIN_BOTTOM, lus[2], important);
+			} else if (lus.length == 2) {
+				this.addDecl(CSS.MARGIN_TOP, lus[0], important);
+				this.addDecl(CSS.MARGIN_LEFT, lus[1], important);
+				this.addDecl(CSS.MARGIN_RIGHT, lus[1], important);
+				this.addDecl(CSS.MARGIN_BOTTOM, lus[0], important);
+			} else if (lus.length == 1) {
+				this.addDecl(CSS.MARGIN_TOP, lus[0], important);
+				this.addDecl(CSS.MARGIN_LEFT, lus[0], important);
+				this.addDecl(CSS.MARGIN_RIGHT, lus[0], important);
+				this.addDecl(CSS.MARGIN_BOTTOM, lus[0], important);
+			}
+		}
+
+		/**
+		 * Expand the "padding" shorthand property.
+		 */
+		private void expandPadding(LexicalUnit value, boolean important) {
+
+			if (AbstractProperty.isInherit(value)) {
+				this.addDecl(CSS.PADDING_TOP, value, important);
+				this.addDecl(CSS.PADDING_LEFT, value, important);
+				this.addDecl(CSS.PADDING_RIGHT, value, important);
+				this.addDecl(CSS.PADDING_BOTTOM, value, important);
+				return;
+			}
+
+			LexicalUnit[] lus = getLexicalUnitList(value);
+			if (lus.length >= 4) {
+				this.addDecl(CSS.PADDING_TOP, lus[0], important);
+				this.addDecl(CSS.PADDING_RIGHT, lus[1], important);
+				this.addDecl(CSS.PADDING_BOTTOM, lus[2], important);
+				this.addDecl(CSS.PADDING_LEFT, lus[3], important);
+			} else if (lus.length == 3) {
+				this.addDecl(CSS.PADDING_TOP, lus[0], important);
+				this.addDecl(CSS.PADDING_LEFT, lus[1], important);
+				this.addDecl(CSS.PADDING_RIGHT, lus[1], important);
+				this.addDecl(CSS.PADDING_BOTTOM, lus[2], important);
+			} else if (lus.length == 2) {
+				this.addDecl(CSS.PADDING_TOP, lus[0], important);
+				this.addDecl(CSS.PADDING_LEFT, lus[1], important);
+				this.addDecl(CSS.PADDING_RIGHT, lus[1], important);
+				this.addDecl(CSS.PADDING_BOTTOM, lus[0], important);
+			} else if (lus.length == 1) {
+				this.addDecl(CSS.PADDING_TOP, lus[0], important);
+				this.addDecl(CSS.PADDING_LEFT, lus[0], important);
+				this.addDecl(CSS.PADDING_RIGHT, lus[0], important);
+				this.addDecl(CSS.PADDING_BOTTOM, lus[0], important);
+			}
+		}
+
+		/**
+		 * Returns an array of <code>LexicalUnit</code> objects, the first of
+		 * which is given.
+		 */
+		private static LexicalUnit[] getLexicalUnitList(LexicalUnit lu) {
+			List<LexicalUnit> lus = new ArrayList<LexicalUnit>();
+			while (lu != null) {
+				lus.add(lu);
+				lu = lu.getNextLexicalUnit();
+			}
+			return lus.toArray(new LexicalUnit[lus.size()]);
+		}
+
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Styles.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Styles.java
new file mode 100644
index 0000000..4aa08a2
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/Styles.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Dave Holroyd - Implement text decoration
+ *     Mohamadou Nassourou - Bug 298912 - rudimentary support for images 
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.core.FontSpec;
+
+/**
+ * Represents the computed style properties for a particular element.
+ */
+public class Styles {
+
+	/** Maps property name (String) => value (Object) */
+	private Map<String, Object> values = new HashMap<String, Object>();
+
+	private List<String> content;
+	private FontSpec font;
+
+	/**
+	 * Returns the value of the given property, or null if the property does not
+	 * have a value.
+	 * 
+	 * @param propertyName
+	 * @return
+	 */
+	public Object get(String propertyName) {
+		return this.values.get(propertyName);
+	}
+
+	/**
+	 * Returns the value of the <code>backgroundColor</code> property.
+	 */
+	public Color getBackgroundColor() {
+		return (Color) this.values.get(CSS.BACKGROUND_COLOR);
+	}
+
+	/**
+	 * Returns the value of the <code>borderBottomColor</code> property.
+	 */
+	public Color getBorderBottomColor() {
+		return (Color) this.values.get(CSS.BORDER_BOTTOM_COLOR);
+	}
+
+	/**
+	 * Returns the value of the <code>borderBottomStyle</code> property.
+	 */
+	public String getBorderBottomStyle() {
+		return (String) this.values.get(CSS.BORDER_BOTTOM_STYLE);
+	}
+
+	/**
+	 * Returns the value of the <code>borderLeftColor</code> property.
+	 */
+	public Color getBorderLeftColor() {
+		return (Color) this.values.get(CSS.BORDER_LEFT_COLOR);
+	}
+
+	/**
+	 * Returns the value of the <code>borderLeftStyle</code> property.
+	 */
+	public String getBorderLeftStyle() {
+		return (String) this.values.get(CSS.BORDER_LEFT_STYLE);
+	}
+
+	/**
+	 * Returns the value of the <code>borderRightColor</code> property.
+	 */
+	public Color getBorderRightColor() {
+		return (Color) this.values.get(CSS.BORDER_RIGHT_COLOR);
+	}
+
+	/**
+	 * Returns the value of the <code>borderRightStyle</code> property.
+	 */
+	public String getBorderRightStyle() {
+		return (String) this.values.get(CSS.BORDER_RIGHT_STYLE);
+	}
+
+	/**
+	 * Returns the value of the <code>borderSpacing</code> property.
+	 */
+	public BorderSpacingProperty.Value getBorderSpacing() {
+		return (BorderSpacingProperty.Value) this.values
+				.get(CSS.BORDER_SPACING);
+	}
+
+	/**
+	 * Returns the value of the <code>borderTopColor</code> property.
+	 */
+	public Color getBorderTopColor() {
+		return (Color) this.values.get(CSS.BORDER_TOP_COLOR);
+	}
+
+	/**
+	 * Returns the value of the <code>borderTopStyle</code> property.
+	 */
+	public String getBorderTopStyle() {
+		return (String) this.values.get(CSS.BORDER_TOP_STYLE);
+	}
+
+	/**
+	 * Returns the value of the <code>color</code> property.
+	 */
+	public Color getColor() {
+		return (Color) this.values.get(CSS.COLOR);
+	}
+
+	/**
+	 * Returns a <code>List</code> of <code>ContentPart</code> objects
+	 * representing the <code>content</code> property.
+	 */
+	public List<String> getContent() {
+		return content;
+	}
+
+	/**
+	 * Returns the value of the <code>display</code> property.
+	 */
+	public String getDisplay() {
+		return (String) this.values.get(CSS.DISPLAY);
+	}
+
+	/**
+	 * @return true if the <code>display</code> property is not 'none'.
+	 */
+	public boolean isDisplayed() {
+		return !CSS.NONE.equals(getDisplay());
+	}
+	
+	/**
+	 * Returns the value of the <code>font</code> property.
+	 */
+	public FontSpec getFont() {
+		return font;
+	}
+
+	/**
+	 * Returns the value of the <code>fontFamily</code> property.
+	 */
+	public String[] getFontFamilies() {
+		return (String[]) this.values.get(CSS.FONT_FAMILY);
+	}
+
+	/**
+	 * Returns the value of the <code>fontSize</code> property.
+	 */
+	public float getFontSize() {
+		return ((Float) this.values.get(CSS.FONT_SIZE)).floatValue();
+	}
+
+	/**
+	 * Returns the value of the <code>fontStyle</code> property.
+	 */
+	public String getFontStyle() {
+		return (String) this.values.get(CSS.FONT_STYLE);
+	}
+
+	/**
+	 * Returns the value of the <code>fontWeight</code> property.
+	 */
+	public int getFontWeight() {
+		return ((Integer) this.values.get(CSS.FONT_WEIGHT)).intValue();
+	}
+
+	/**
+	 * Returns the value of the <code>lineHeight</code> property.
+	 */
+	public int getLineHeight() {
+		return ((RelativeLength) this.values.get(CSS.LINE_HEIGHT)).get(Math
+				.round(this.getFontSize()));
+	}
+
+	/**
+	 * Returns the value of the <code>listStyleType</code> property.
+	 */
+	public String getListStyleType() {
+		return (String) this.values.get(CSS.LIST_STYLE_TYPE);
+	}
+
+	/**
+	 * Returns the value of the <code>textAlign</code> property.
+	 */
+	public String getTextAlign() {
+		return (String) this.values.get(CSS.TEXT_ALIGN);
+	}
+
+	/**
+	 * Returns the value of the <code>textDecoration</code> property.
+	 */
+	public String getTextDecoration() {
+		return (String) this.values.get(CSS.TEXT_DECORATION);
+	}
+
+	/**
+	 * Returns the value of the <code>whiteSpace</code> property.
+	 */
+	public String getWhiteSpace() {
+		return (String) this.values.get(CSS.WHITE_SPACE);
+	}
+
+	/**
+	 * Returns true if this element is block-formatted, or false if it is
+	 * inline-formatted.
+	 */
+	public boolean isBlock() {
+		return this.getDisplay().equals(CSS.BLOCK)
+				|| this.getDisplay().equals(CSS.LIST_ITEM)
+				|| this.getDisplay().equals(CSS.TABLE)
+				|| this.getDisplay().equals(CSS.TABLE_CAPTION)
+				|| this.getDisplay().equals(CSS.TABLE_CELL)
+				|| this.getDisplay().equals(CSS.TABLE_COLUMN)
+				|| this.getDisplay().equals(CSS.TABLE_COLUMN_GROUP)
+				|| this.getDisplay().equals(CSS.TABLE_FOOTER_GROUP)
+				|| this.getDisplay().equals(CSS.TABLE_HEADER_GROUP)
+				|| this.getDisplay().equals(CSS.TABLE_ROW)
+				|| this.getDisplay().equals(CSS.TABLE_ROW_GROUP);
+	}
+
+	/**
+	 * Sets the value of a property in this stylesheet.
+	 * 
+	 * @param propertyName
+	 *            Name of the property being set.
+	 * @param value
+	 *            Value of the property.
+	 */
+	public void put(String propertyName, Object value) {
+		this.values.put(propertyName, value);
+	}
+
+	/**
+	 * Sets the vale of the <code>content</code> property.
+	 * 
+	 * @param content
+	 *            <code>List</code> of <code>ContentPart</code> objects
+	 *            representing the content.
+	 */
+	public void setContent(List<String> content) {
+		this.content = content;
+	}
+
+	/**
+	 * Sets the value of the <code>font</code> property.
+	 * 
+	 * @param font
+	 *            new value for the <code>font</code> property.
+	 */
+	public void setFont(FontSpec font) {
+		this.font = font;
+	}
+
+	public RelativeLength getElementWidth() {
+		return (RelativeLength) this.values.get(CSS.WIDTH);
+	}
+
+	public RelativeLength getElementHeight() {
+		return (RelativeLength) this.values.get(CSS.HEIGHT);
+	}
+	
+	public boolean hasBackgroundImage() {
+		return this.values.get(CSS.BACKGROUND_IMAGE) != null;
+	}
+	
+	public String getBackgroundImage() {
+		final Object value = this.values.get(CSS.BACKGROUND_IMAGE);
+		if (value == null) 
+			return BackgroundImageProperty.DEFAULT;
+		return value.toString();
+	}
+	
+	/**
+	 * @return the value of border-bottom-width
+	 */
+	public int getBorderBottomWidth() {
+		return ((Integer) this.values.get(CSS.BORDER_BOTTOM_WIDTH)).intValue();
+	}
+
+	/**
+	 * @return the value of border-left-width
+	 */
+	public int getBorderLeftWidth() {
+		return ((Integer) this.values.get(CSS.BORDER_LEFT_WIDTH)).intValue();
+	}
+
+	/**
+	 * @return the value of border-right-width
+	 */
+	public int getBorderRightWidth() {
+		return ((Integer) this.values.get(CSS.BORDER_RIGHT_WIDTH)).intValue();
+	}
+
+	/**
+	 * @return the value of border-top-width
+	 */
+	public int getBorderTopWidth() {
+		return ((Integer) this.values.get(CSS.BORDER_TOP_WIDTH)).intValue();
+	}
+
+	/**
+	 * @return the value of margin-bottom
+	 */
+	public RelativeLength getMarginBottom() {
+		return (RelativeLength) this.values.get(CSS.MARGIN_BOTTOM);
+		// return marginBottom;
+	}
+
+	/**
+	 * @return the value of margin-left
+	 */
+	public RelativeLength getMarginLeft() {
+		return (RelativeLength) this.values.get(CSS.MARGIN_LEFT);
+	}
+
+	/**
+	 * @return the value of margin-right
+	 */
+	public RelativeLength getMarginRight() {
+		return (RelativeLength) this.values.get(CSS.MARGIN_RIGHT);
+	}
+
+	/**
+	 * @return the value of margin-top
+	 */
+	public RelativeLength getMarginTop() {
+		return (RelativeLength) this.values.get(CSS.MARGIN_TOP);
+	}
+
+	/**
+	 * @return the value of padding-bottom
+	 */
+	public RelativeLength getPaddingBottom() {
+		return (RelativeLength) this.values.get(CSS.PADDING_BOTTOM);
+	}
+
+	/**
+	 * @return the value of padding-left
+	 */
+	public RelativeLength getPaddingLeft() {
+		return (RelativeLength) this.values.get(CSS.PADDING_LEFT);
+	}
+
+	/**
+	 * @return the value of padding-right
+	 */
+	public RelativeLength getPaddingRight() {
+		return (RelativeLength) this.values.get(CSS.PADDING_RIGHT);
+	}
+
+	/**
+	 * @return the value of padding-top
+	 */
+	public RelativeLength getPaddingTop() {
+		return (RelativeLength) this.values.get(CSS.PADDING_TOP);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextAlignProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextAlignProperty.java
new file mode 100644
index 0000000..d04bc85
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextAlignProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS text-align property.
+ */
+public class TextAlignProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor
+	 */
+	public TextAlignProperty() {
+		super(CSS.TEXT_ALIGN);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (TextAlignProperty.isTextAlign(lu)) {
+			return lu.getStringValue();
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getTextAlign();
+			} else {
+				return CSS.LEFT;
+			}
+		}
+
+	}
+
+	// =================================================== PRIVATE
+
+	private static boolean isTextAlign(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextDecorationProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextDecorationProperty.java
new file mode 100644
index 0000000..d0eb3e3
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/TextDecorationProperty.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS text-decoration property.
+ */
+public class TextDecorationProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public TextDecorationProperty() {
+		super(CSS.TEXT_DECORATION);
+	}
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isTextDecoration(lu)) {
+			return lu.getStringValue();
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getTextDecoration();
+			} else {
+				return CSS.NONE;
+			}
+		}
+	}
+
+	// =================================================== PRIVATE
+
+	/**
+	 * Returns true if the given lexical unit represents a text decoration.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	private static boolean isTextDecoration(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NONE) || s.equals(CSS.UNDERLINE)
+					|| s.equals(CSS.OVERLINE) || s.equals(CSS.LINE_THROUGH)
+					|| s.equals(CSS.BLINK);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/WhiteSpaceProperty.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/WhiteSpaceProperty.java
new file mode 100644
index 0000000..fd98eab
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/WhiteSpaceProperty.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.css;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * The CSS white-space property.
+ */
+public class WhiteSpaceProperty extends AbstractProperty {
+
+	/**
+	 * Class constructor.
+	 */
+	public WhiteSpaceProperty() {
+		super(CSS.WHITE_SPACE);
+	}
+
+	/**
+     *
+     */
+
+	public Object calculate(LexicalUnit lu, Styles parentStyles, Styles styles, Element element) {
+		if (isWhiteSpace(lu)) {
+			return lu.getStringValue();
+		} else {
+			// not specified, "inherit", or some other value
+			if (parentStyles != null) {
+				return parentStyles.getWhiteSpace();
+			} else {
+				return CSS.NORMAL;
+			}
+		}
+
+	}
+
+	/**
+	 * Returns true if the given lexical unit represents a white space value.
+	 * 
+	 * @param lu
+	 *            LexicalUnit to check.
+	 */
+	public static boolean isWhiteSpace(LexicalUnit lu) {
+		if (lu == null) {
+			return false;
+		} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_IDENT) {
+			String s = lu.getStringValue();
+			return s.equals(CSS.NORMAL) || s.equals(CSS.PRE)
+					|| s.equals(CSS.NOWRAP);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/package.html b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/package.html
new file mode 100644
index 0000000..44d5ee2
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/css/package.html
@@ -0,0 +1,15 @@
+<html>
+  <head>
+    <title>net.sf.vex.css</title>
+  </head>
+  <body>
+  
+    <p>Classes implementing an object model for CSS stylesheets. Style
+    sheets can be built from an input stream using a
+    <code>StyleSheetBuilder</code> and a SAC (Simple API for CSS)
+    parser. The <code>StyleSheet</code> class encapsulates the
+    style sheet data and implements the the CSS algorithms for
+    calculating computed style values.</p>
+    
+  </body>
+</html>
\ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Attribute.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Attribute.java
new file mode 100644
index 0000000..eeeb99a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Attribute.java
@@ -0,0 +1,84 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.dom;

+

+import org.eclipse.core.runtime.QualifiedName;

+

+/**

+ * An immutable representation of an attribute within the start tag of an

+ * element.

+ * The attribute is Comparable by its qualified name.

+ * 

+ * @author Florian Thienel

+ */

+public class Attribute implements Comparable<Attribute> {

+

+	private final Element parent;

+

+	private final QualifiedName name;

+

+	private final String value;

+

+	public Attribute(final Element parent, final String localName, final String value) {

+		this(parent, new QualifiedName(null, localName), value);

+	}

+

+	public Attribute(final Element parent, final QualifiedName name, final String value) {

+		this.parent = parent;

+		this.name = name;

+		this.value = value;

+	}

+

+	public Element getParent() {

+		return parent;

+	}

+

+	public String getLocalName() {

+		return name.getLocalName();

+	}

+

+	public String getValue() {

+		return value;

+	}

+

+	/**

+	 * @return a pair of the namespace URI and the local name

+	 */

+	public QualifiedName getQualifiedName() {

+		return name;

+	}

+

+	/**

+	 * @return prefix:localName, or localName if prefix is null or this

+	 *         attribute is in the same namespace as the parent element.

+	 */

+	public String getPrefixedName() {

+		final String attributeQualifier = name.getQualifier();

+		if (parent == null || attributeQualifier == null)

+			return getLocalName();

+		final String elementQualifier = parent.getQualifiedName().getQualifier();

+		if (elementQualifier == null || attributeQualifier.equals(elementQualifier))

+			return getLocalName();

+		final String prefix = parent.getNamespacePrefix(attributeQualifier);

+		return (prefix == null ? "" : prefix + ":") + getLocalName();

+	}

+

+	/**

+	 * Compares two attributes by their name.

+	 * 

+	 * @param otherAttribute

+	 *            the other attribute

+	 * @see Comparable

+	 */

+	public int compareTo(final Attribute otherAttribute) {

+		return name.toString().compareTo(otherAttribute.name.toString());

+	}

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Content.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Content.java
new file mode 100644
index 0000000..58e3011
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Content.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+
+
+/**
+ * Interface for classes that manage a string of characters representing the
+ * content of a document.
+ * 
+ * @model
+ */
+public interface Content {
+
+	/**
+	 * Creates a new Position object at the given initial offset.
+	 * 
+	 * @param offset
+	 *            initial offset of the position
+	 * @model
+	 */
+	public Position createPosition(int offset);
+
+	/**
+	 * Insert a string into the content.
+	 * 
+	 * @param offset
+	 *            Offset at which to insert the string.
+	 * @param s
+	 *            String to insert.
+	 * @model
+	 */
+	public void insertString(int offset, String s);
+
+	/**
+	 * Deletes the given range of characters.
+	 * 
+	 * @param offset
+	 *            Offset from which characters should be deleted.
+	 * @param length
+	 *            Number of characters to delete.
+	 * @model
+	 */
+	public void remove(int offset, int length);
+
+	/**
+	 * Gets a substring of the content.
+	 * 
+	 * @param offset
+	 *            Offset at which the string begins.
+	 * @param length
+	 *            Number of characters to return.
+	 * @model
+	 */
+	public String getString(int offset, int length);
+
+	/**
+	 * Return the length of the content.
+	 * 
+	 * @model
+	 */
+	public int getLength();
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java
new file mode 100644
index 0000000..969c15a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java
@@ -0,0 +1,824 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.xml.vex.core.internal.core.ListenerList;
+import org.eclipse.wst.xml.vex.core.internal.undo.CannotRedoException;
+import org.eclipse.wst.xml.vex.core.internal.undo.CannotUndoException;
+import org.eclipse.wst.xml.vex.core.internal.undo.IUndoableEdit;
+
+/**
+ * Represents an XML document.
+ * 
+ */
+public class Document {
+
+	private final Content content;
+	private final RootElement rootElement;
+	private final ListenerList<DocumentListener, DocumentEvent> listeners = new ListenerList<DocumentListener, DocumentEvent>(DocumentListener.class);
+	private boolean undoEnabled = true;
+
+	private String publicID;
+	protected String systemID;
+	private String documentURI;
+
+	private String encoding;
+	private Validator validator;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param rootElement
+	 *            root element of the document. The document property of this
+	 *            RootElement is set by this constructor.
+	 * 
+	 */
+	public Document(final RootElement rootElement) {
+		content = new GapContent(100);
+		content.insertString(0, "\0\0");
+
+		this.rootElement = rootElement;
+		rootElement.setDocument(this);
+		rootElement.setContent(content, 0, 1);
+	}
+
+	/**
+	 * Class constructor. This constructor is used by the document builder and
+	 * assumes that the content and root element have bee properly set up.
+	 * 
+	 * @param content
+	 *            Content object used to store the document's content.
+	 * @param rootElement
+	 *            RootElement of the document.
+	 * 
+	 */
+	public Document(final Content content, final RootElement rootElement) {
+		this.content = content;
+		this.rootElement = rootElement;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#addDocumentListener
+	 * (org.eclipse.wst.xml.vex.core.internal.dom.DocumentListener)
+	 */
+	public void addDocumentListener(final DocumentListener listener) {
+		listeners.add(listener);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#canInsertFragment
+	 * (int, org.eclipse.wst.xml.vex.core.internal.dom.DocumentFragment)
+	 */
+	public boolean canInsertFragment(final int offset, final DocumentFragment fragment) {
+		if (validator == null)
+			return true;
+
+		final Element element = getElementAt(offset);
+		final List<QualifiedName> seq1 = getNodeNames(element.getStartOffset() + 1, offset);
+		final List<QualifiedName> seq2 = fragment.getNodeNames();
+		final List<QualifiedName> seq3 = getNodeNames(offset, element.getEndOffset());
+
+		return validator.isValidSequence(element.getQualifiedName(), seq1, seq2, seq3, true);
+	}
+
+	public boolean canInsertText(final int offset) {
+		if (validator == null)
+			return true;
+
+		final Element element = getElementAt(offset);
+		final List<QualifiedName> seq1 = getNodeNames(element.getStartOffset() + 1, offset);
+		final List<QualifiedName> seq2 = Collections.singletonList(Validator.PCDATA);
+		final List<QualifiedName> seq3 = getNodeNames(offset, element.getEndOffset());
+
+		return validator.isValidSequence(element.getQualifiedName(), seq1, seq2, seq3, true);
+	}
+
+	public Position createPosition(final int offset) {
+		return content.createPosition(offset);
+	}
+
+	public void delete(final int startOffset, final int endOffset) throws DocumentValidationException {
+
+		final Element e1 = getElementAt(startOffset);
+		final Element e2 = getElementAt(endOffset);
+		if (e1 != e2)
+			throw new IllegalArgumentException("Deletion from " + startOffset + " to " + endOffset + " is unbalanced");
+
+		final Validator validator = getValidator();
+		if (validator != null) {
+			final List<QualifiedName> seq1 = getNodeNames(e1.getStartOffset() + 1, startOffset);
+			final List<QualifiedName> seq2 = getNodeNames(endOffset, e1.getEndOffset());
+
+			if (!validator.isValidSequence(e1.getQualifiedName(), seq1, seq2, null, true))
+				throw new DocumentValidationException("Unable to delete from " + startOffset + " to " + endOffset);
+		}
+
+		// Grab the fragment for the undoable edit while it's still here
+		final DocumentFragment frag = getFragment(startOffset, endOffset);
+
+		fireBeforeContentDeleted(new DocumentEvent(this, e1, startOffset, endOffset - startOffset, null));
+
+		Iterator<Node> iter = e1.getChildNodes().iterator();
+		if (e1 instanceof Element)
+			iter = e1.getChildIterator();
+		while (iter.hasNext()) {
+			final Node child = iter.next();
+			if (startOffset <= child.getStartOffset() && child.getEndOffset() < endOffset)
+				iter.remove();
+		}
+
+		content.remove(startOffset, endOffset - startOffset);
+
+		final IUndoableEdit edit = undoEnabled ? new DeleteEdit(startOffset, endOffset, frag) : null;
+
+		fireContentDeleted(new DocumentEvent(this, e1, startOffset, endOffset - startOffset, edit));
+	}
+
+	public Element findCommonElement(final int offset1, final int offset2) {
+		Element element = rootElement;
+		for (;;) {
+			boolean tryAgain = false;
+			final List<Element> children = element.getChildElements();
+			for (int i = 0; i < children.size(); i++)
+				if (offset1 > children.get(i).getStartOffset() && offset2 > children.get(i).getStartOffset() && offset1 <= children.get(i).getEndOffset()
+						&& offset2 <= children.get(i).getEndOffset()) {
+
+					element = children.get(i);
+					tryAgain = true;
+					break;
+				}
+			if (!tryAgain)
+				break;
+		}
+		return element;
+	}
+
+	public char getCharacterAt(final int offset) {
+		return content.getString(offset, 1).charAt(0);
+	}
+
+	public Element getElementAt(final int offset) {
+		if (offset < 1 || offset >= getLength())
+			throw new IllegalArgumentException("Illegal offset: " + offset + ". Must be between 1 and n-1");
+		Element element = rootElement;
+		for (;;) {
+			boolean tryAgain = false;
+			final List<Element> children = element.getChildElements();
+			for (int i = 0; i < children.size(); i++) {
+				final Element child = children.get(i);
+				if (offset <= child.getStartOffset())
+					return element;
+				else if (offset <= child.getEndOffset()) {
+					element = child;
+					tryAgain = true;
+					break;
+				}
+			}
+			if (!tryAgain)
+				break;
+		}
+		return element;
+	}
+
+	public String getEncoding() {
+		return encoding;
+	}
+
+	public void setEncoding(final String encoding) {
+		this.encoding = encoding;
+	}
+
+	public DocumentFragment getFragment(final int startOffset, final int endOffset) {
+
+		assertOffset(startOffset, 0, content.getLength());
+		assertOffset(endOffset, 0, content.getLength());
+
+		if (endOffset <= startOffset)
+			throw new IllegalArgumentException("Invalid range (" + startOffset + ", " + endOffset + ")");
+
+		final Element e1 = getElementAt(startOffset);
+		final Element e2 = getElementAt(endOffset);
+		if (e1 != e2)
+			throw new IllegalArgumentException("Fragment from " + startOffset + " to " + endOffset + " is unbalanced");
+
+		final List<Element> children = e1.getChildElements();
+
+		final Content newContent = new GapContent(endOffset - startOffset);
+		final String s = content.getString(startOffset, endOffset - startOffset);
+		newContent.insertString(0, s);
+		final List<Element> newChildren = new ArrayList<Element>();
+		for (int i = 0; i < children.size(); i++) {
+			final Element child = children.get(i);
+			if (child.getEndOffset() <= startOffset)
+				continue;
+			else if (child.getStartOffset() >= endOffset)
+				break;
+			else
+				newChildren.add(cloneElement(child, newContent, -startOffset, null));
+		}
+
+		return new DocumentFragment(newContent, newChildren);
+	}
+
+	public int getLength() {
+		return content.getLength();
+	}
+
+	public List<QualifiedName> getNodeNames(final int startOffset, final int endOffset) {
+
+		final List<Node> nodes = getNodes(startOffset, endOffset);
+		final List<QualifiedName> names = new ArrayList<QualifiedName>(nodes.size());
+
+		for (int i = 0; i < nodes.size(); i++) {
+			final Node node = nodes.get(i);
+			if (node instanceof Element)
+				names.add(((Element) node).getQualifiedName());
+			else
+				names.add(Validator.PCDATA);
+		}
+
+		return names;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#getNodes(int,
+	 * int)
+	 */
+	public List<Node> getNodes(final int startOffset, final int endOffset) {
+		final Element element = getElementAt(startOffset);
+		if (element != getElementAt(endOffset))
+			throw new IllegalArgumentException(NLS.bind("Offsets are unbalanced: {0} is in {1}, {2} is in {3}.", new Object[] {startOffset, element.getPrefixedName(), endOffset,getElementAt(endOffset).getPrefixedName()}));
+
+		final List<Node> list = new ArrayList<Node>();
+		final List<Node> nodes = element.getChildNodes();
+		for (int i = 0; i < nodes.size(); i++) {
+			final Node node = nodes.get(i);
+			if (node.getEndOffset() <= startOffset)
+				continue;
+			else if (node.getStartOffset() >= endOffset)
+				break;
+			else if (node instanceof Element)
+				list.add(node);
+			else {
+				final Text text = (Text) node;
+				if (text.getStartOffset() < startOffset)
+					text.setContent(text.getContent(), startOffset, text.getEndOffset());
+				else if (text.getEndOffset() > endOffset)
+					text.setContent(text.getContent(), text.getStartOffset(), endOffset);
+				list.add(text);
+			}
+		}
+
+		return list;
+	}
+
+	/**
+	 * Creates an array of nodes for a given run of content. The returned array
+	 * includes the given child elements and <code>Text</code> objects where
+	 * text appears between elements.
+	 * 
+	 * @param content
+	 *            Content object containing the content
+	 * @param startOffset
+	 *            start offset of the run
+	 * @param endOffset
+	 *            end offset of the run
+	 * @param elements
+	 *            child elements that are within the run
+	 */
+	static List<Node> createNodeList(final Content content, final int startOffset, final int endOffset, final List<Node> elements) {
+
+		final List<Node> nodes = new ArrayList<Node>();
+		int offset = startOffset;
+		for (int i = 0; i < elements.size(); i++) {
+			final int start = elements.get(i).getStartOffset();
+			if (offset < start)
+				nodes.add(new Text(content, offset, start));
+			nodes.add(elements.get(i));
+			offset = elements.get(i).getEndOffset() + 1;
+		}
+
+		if (offset < endOffset)
+			nodes.add(new Text(content, offset, endOffset));
+
+		return nodes;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#getPublicID()
+	 */
+	public String getPublicID() {
+		return publicID;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#getRawText(int,
+	 * int)
+	 */
+	public String getRawText(final int startOffset, final int endOffset) {
+		return content.getString(startOffset, endOffset - startOffset);
+	}
+
+	public Element getRootElement() {
+		return rootElement;
+	}
+
+	public String getSystemID() {
+		return systemID;
+	}
+
+	public String getText(final int startOffset, final int endOffset) {
+		final String raw = content.getString(startOffset, endOffset - startOffset);
+		final StringBuffer sb = new StringBuffer(raw.length());
+		for (int i = 0; i < raw.length(); i++) {
+			final char c = raw.charAt(i);
+			if (c != '\0')
+				sb.append(c);
+		}
+		return sb.toString();
+	}
+
+	public Validator getValidator() {
+		return validator;
+	}
+
+	public void insertElement(final int offset, final Element element) throws DocumentValidationException {
+
+		if (offset < 1 || offset >= getLength())
+			throw new IllegalArgumentException("Error inserting element <" + element.getPrefixedName() + ">: offset is " + offset + ", but it must be between 1 and "
+					+ (getLength() - 1));
+
+		final Validator validator = getValidator();
+		if (validator != null) {
+			final Element parent = getElementAt(offset);
+			final List<QualifiedName> seq1 = getNodeNames(parent.getStartOffset() + 1, offset);
+			final List<QualifiedName> seq2 = Collections.singletonList(element.getQualifiedName());
+			final List<QualifiedName> seq3 = getNodeNames(offset, parent.getEndOffset());
+
+			if (!validator.isValidSequence(parent.getQualifiedName(), seq1, seq2, seq3, true))
+				throw new DocumentValidationException("Cannot insert element " + element.getPrefixedName() + " at offset " + offset);
+		}
+
+		// find the parent, and the index into its children at which
+		// this element should be inserted
+		Element parent = rootElement;
+		int childIndex = -1;
+		while (childIndex == -1) {
+			boolean tryAgain = false;
+			final List<Element> children = parent.getChildElements();
+			for (int i = 0; i < children.size(); i++) {
+				final Element child = children.get(i);
+				if (offset <= child.getStartOffset()) {
+					childIndex = i;
+					break;
+				} else if (offset <= child.getEndOffset()) {
+					parent = child;
+					tryAgain = true;
+					break;
+				}
+			}
+			if (!tryAgain && childIndex == -1) {
+				childIndex = children.size();
+				break;
+			}
+		}
+
+		fireBeforeContentInserted(new DocumentEvent(this, parent, offset, 2, null));
+
+		content.insertString(offset, "\0\0");
+
+		element.setContent(content, offset, offset + 1);
+		element.setParent(parent);
+		parent.insertChild(childIndex, element);
+
+		final IUndoableEdit edit = undoEnabled ? new InsertElementEdit(offset, element) : null;
+
+		fireContentInserted(new DocumentEvent(this, parent, offset, 2, edit));
+	}
+
+	public void insertFragment(final int offset, final DocumentFragment fragment) throws DocumentValidationException {
+
+		if (offset < 1 || offset >= getLength())
+			throw new IllegalArgumentException("Error inserting document fragment");
+
+		final Element parent = getElementAt(offset);
+
+		if (validator != null) {
+			final List<QualifiedName> seq1 = getNodeNames(parent.getStartOffset() + 1, offset);
+			final List<QualifiedName> seq2 = fragment.getNodeNames();
+			final List<QualifiedName> seq3 = getNodeNames(offset, parent.getEndOffset());
+
+			if (!validator.isValidSequence(parent.getQualifiedName(), seq1, seq2, seq3, true))
+				throw new DocumentValidationException("Cannot insert document fragment");
+		}
+
+		fireBeforeContentInserted(new DocumentEvent(this, parent, offset, 2, null));
+
+		final Content c = fragment.getContent();
+		final String s = c.getString(0, c.getLength());
+		content.insertString(offset, s);
+
+		final List<Element> children = parent.getChildElements();
+		int index = 0;
+		while (index < children.size() && children.get(index).getEndOffset() < offset)
+			index++;
+
+		final List<Element> elements = fragment.getElements();
+		for (int i = 0; i < elements.size(); i++) {
+			final Element newElement = cloneElement(elements.get(i), content, offset, parent);
+			parent.insertChild(index, newElement);
+			index++;
+		}
+
+		final IUndoableEdit edit = undoEnabled ? new InsertFragmentEdit(offset, fragment) : null;
+
+		fireContentInserted(new DocumentEvent(this, parent, offset, fragment.getContent().getLength(), edit));
+	}
+
+	public void insertText(final int offset, final String text) throws DocumentValidationException {
+
+		if (offset < 1 || offset >= getLength())
+			throw new IllegalArgumentException("Offset must be between 1 and n-1");
+
+		final Element parent = getElementAt(offset);
+
+		boolean isValid = false;
+		if (getCharacterAt(offset - 1) != '\0')
+			isValid = true;
+		else if (getCharacterAt(offset) != '\0')
+			isValid = true;
+		else {
+			final Validator validator = getValidator();
+			if (validator != null) {
+				final List<QualifiedName> seq1 = getNodeNames(parent.getStartOffset() + 1, offset);
+				final List<QualifiedName> seq2 = Collections.singletonList(Validator.PCDATA);
+				final List<QualifiedName> seq3 = getNodeNames(offset, parent.getEndOffset());
+
+				isValid = validator.isValidSequence(parent.getQualifiedName(), seq1, seq2, seq3, true);
+			} else
+				isValid = true;
+		}
+
+		if (!isValid)
+			throw new DocumentValidationException("Cannot insert text '" + text + "' at offset " + offset);
+
+		// Convert control chars to spaces
+		final StringBuffer sb = new StringBuffer(text);
+		for (int i = 0; i < sb.length(); i++)
+			if (Character.isISOControl(sb.charAt(i)) && sb.charAt(i) != '\n')
+				sb.setCharAt(i, ' ');
+
+		final String s = sb.toString();
+
+		fireBeforeContentInserted(new DocumentEvent(this, parent, offset, 2, null));
+
+		content.insertString(offset, s);
+
+		final IUndoableEdit edit = undoEnabled ? new InsertTextEdit(offset, s) : null;
+
+		fireContentInserted(new DocumentEvent(this, parent, offset, s.length(), edit));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#isUndoEnabled()
+	 */
+	public boolean isUndoEnabled() {
+		return undoEnabled;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#removeDocumentListener
+	 * (org.eclipse.wst.xml.vex.core.internal.dom.DocumentListener)
+	 */
+	public void removeDocumentListener(final DocumentListener listener) {
+		listeners.remove(listener);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#setPublicID(java
+	 * .lang.String)
+	 */
+	public void setPublicID(final String publicID) {
+		this.publicID = publicID;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#setSystemID(java
+	 * .lang.String)
+	 */
+	public void setSystemID(final String systemID) {
+		this.systemID = systemID;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#setUndoEnabled
+	 * (boolean)
+	 */
+	public void setUndoEnabled(final boolean undoEnabled) {
+		this.undoEnabled = undoEnabled;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocument#setValidator(org
+	 * .eclipse.wst.xml.vex.core.internal.dom.Validator)
+	 */
+	public void setValidator(final Validator validator) {
+		this.validator = validator;
+	}
+
+	// ==================================================== PRIVATE
+
+	/**
+	 * Represents a deletion from a document that can be undone and redone.
+	 */
+	private class DeleteEdit implements IUndoableEdit {
+
+		private final int startOffset;
+		private final int endOffset;
+		private final DocumentFragment frag;
+
+		public DeleteEdit(final int startOffset, final int endOffset, final DocumentFragment frag) {
+			this.startOffset = startOffset;
+			this.endOffset = endOffset;
+			this.frag = frag;
+		}
+
+		public boolean combine(final IUndoableEdit edit) {
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			try {
+				setUndoEnabled(false);
+				insertFragment(startOffset, frag);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			try {
+				setUndoEnabled(false);
+				delete(startOffset, endOffset);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+	}
+
+	/**
+	 * Represents an insertion of an element into the document.
+	 */
+	private class InsertElementEdit implements IUndoableEdit {
+
+		private final int offset;
+		private final Element element;
+
+		public InsertElementEdit(final int offset, final Element element2) {
+			this.offset = offset;
+			element = element2;
+		}
+
+		public boolean combine(final IUndoableEdit edit) {
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			try {
+				setUndoEnabled(false);
+				delete(offset, offset + 2);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			try {
+				setUndoEnabled(false);
+				insertElement(offset, element);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+	}
+
+	/**
+	 * Represents an insertion of a fragment into the document.
+	 */
+	private class InsertFragmentEdit implements IUndoableEdit {
+
+		private final int offset;
+		private final DocumentFragment frag;
+
+		public InsertFragmentEdit(final int offset, final DocumentFragment frag) {
+			this.offset = offset;
+			this.frag = frag;
+		}
+
+		public boolean combine(final IUndoableEdit edit) {
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			try {
+				setUndoEnabled(false);
+				final int length = frag.getContent().getLength();
+				delete(offset, offset + length);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			try {
+				setUndoEnabled(false);
+				insertFragment(offset, frag);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+	}
+
+	/**
+	 * Represents an insertion of text into the document.
+	 */
+	private class InsertTextEdit implements IUndoableEdit {
+
+		private final int offset;
+		private String text;
+
+		public InsertTextEdit(final int offset, final String text) {
+			this.offset = offset;
+			this.text = text;
+		}
+
+		public boolean combine(final IUndoableEdit edit) {
+			if (edit instanceof InsertTextEdit) {
+				final InsertTextEdit ite = (InsertTextEdit) edit;
+				if (ite.offset == offset + text.length()) {
+					text = text + ite.text;
+					return true;
+				}
+			}
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			try {
+				setUndoEnabled(false);
+				delete(offset, offset + text.length());
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			try {
+				setUndoEnabled(false);
+				insertText(offset, text);
+			} catch (final DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				setUndoEnabled(true);
+			}
+		}
+
+	}
+
+	/**
+	 * Assert that the given offset is within the given range, throwing
+	 * IllegalArgumentException if not.
+	 */
+	private static void assertOffset(final int offset, final int min, final int max) {
+		if (offset < min || offset > max)
+			throw new IllegalArgumentException("Bad offset " + offset + "must be between " + min + " and " + max);
+	}
+
+	/**
+	 * Clone an element tree, pointing to a new Content object.
+	 * 
+	 * @param original
+	 *            Element to be cloned
+	 * @param content
+	 *            new Content object to which the clone will point
+	 * @param shift
+	 *            amount to shift offsets to be valid in the new Content.
+	 * @param parent
+	 *            parent for the cloned Element
+	 */
+	private Element cloneElement(final Element original, final Content content, final int shift, final Element parent) {
+		final Element clone = new Element(original.getQualifiedName());
+		clone.setContent(content, original.getStartOffset() + shift, original.getEndOffset() + shift);
+		for (final Attribute attribute : original.getAttributes())
+			try {
+				clone.setAttribute(attribute.getQualifiedName(), attribute.getValue());
+			} catch (final DocumentValidationException ex) {
+				throw new RuntimeException("Unexpected exception: " + ex);
+			}
+		clone.setParent(parent);
+
+		final List<Element> children = original.getChildElements();
+		for (int i = 0; i < children.size(); i++) {
+			final Element cloneChild = cloneElement(children.get(i), content, shift, clone);
+			clone.insertChild(i, cloneChild);
+		}
+
+		return clone;
+	}
+
+	public void fireAttributeChanged(final DocumentEvent e) {
+		listeners.fireEvent("attributeChanged", e);
+	}
+
+	private void fireBeforeContentDeleted(final DocumentEvent e) {
+		listeners.fireEvent("beforeContentDeleted", e);
+	}
+
+	private void fireBeforeContentInserted(final DocumentEvent e) {
+		listeners.fireEvent("beforeContentInserted", e);
+	}
+
+	private void fireContentDeleted(final DocumentEvent e) {
+		listeners.fireEvent("contentDeleted", e);
+	}
+
+	private void fireContentInserted(final DocumentEvent e) {
+		listeners.fireEvent("contentInserted", e);
+	}
+
+	public void setDocumentURI(final String documentURI) {
+		this.documentURI = documentURI;
+	}
+
+	public String getDocumentURI() {
+		return documentURI;
+	}
+
+	public String getBaseURI() {
+		return getDocumentURI();
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentBuilder.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentBuilder.java
new file mode 100644
index 0000000..9a13e8f
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentBuilder.java
@@ -0,0 +1,358 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.QualifiedName;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * A SAX handler that builds a Vex document. This builder collapses whitespace
+ * as it goes, according to the following rules.
+ * 
+ * <ul>
+ * <li>Elements with style white-space: pre are left alone.</li>
+ * <li>Runs of whitespace are replaced with a single space.</li>
+ * <li>Space just inside the start and end of elements is removed.</li>
+ * <li>Space just outside the start and end of block-formatted elements is
+ * removed.</li>
+ * </ul>
+ */
+public class DocumentBuilder implements ContentHandler, LexicalHandler {
+	private final IWhitespacePolicyFactory policyFactory;
+	private IWhitespacePolicy policy;
+
+	// Holds pending characters until we see another element boundary.
+	// This is (a) so we can collapse spaces in multiple adjacent character
+	// blocks, and (b) so we can trim trailing whitespace, if necessary.
+	private final StringBuffer pendingChars = new StringBuffer();
+
+	// If true, trim the leading whitespace from the next received block of
+	// text.
+	private boolean trimLeading = false;
+
+	// Content object to hold document content
+	private final Content content = new GapContent(100);
+
+	// Stack of StackElement objects
+	private final LinkedList<StackEntry> stack = new LinkedList<StackEntry>();
+
+	private final NamespaceStack namespaceStack = new NamespaceStack();
+
+	private RootElement rootElement;
+
+	private String dtdPublicID;
+	private String dtdSystemID;
+	private Document doc;
+	private Locator locator;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param policyFactory
+	 *            Used to determine the WhitespacePolicy to use for a given
+	 *            document type.
+	 */
+	public DocumentBuilder(final IWhitespacePolicyFactory policyFactory) {
+		this.policyFactory = policyFactory;
+	}
+
+	/**
+	 * Returns the newly built <code>Document</code> object.
+	 */
+	public Document getDocument() {
+		return doc;
+	}
+
+	// ============================================= ContentHandler methods
+
+	public void characters(final char[] ch, final int start, final int length) throws SAXException {
+
+		// Convert nulls to spaces, since we use nulls for element delimiters
+		final char[] chars = new char[length];
+		System.arraycopy(ch, start, chars, 0, length);
+		for (int i = 0; i < chars.length; i++)
+			if (Character.isISOControl(chars[i]) && chars[i] != '\n' && chars[i] != '\r' && chars[i] != '\t')
+				chars[i] = ' ';
+		pendingChars.append(chars);
+	}
+
+	public void endDocument() {
+		if (rootElement == null)
+			return;
+
+		doc = new Document(content, rootElement);
+		doc.setPublicID(dtdPublicID);
+		doc.setSystemID(dtdSystemID);
+		rootElement.setDocument(doc);
+	}
+
+	public void endElement(final String namespaceURI, final String localName, final String qName) {
+		appendChars(true);
+
+		final StackEntry entry = stack.removeLast();
+
+		// we must insert the trailing sentinel first, else the insertion
+		// pushes the end position of the element to after the sentinel
+		content.insertString(content.getLength(), "\0");
+		entry.element.setContent(content, entry.offset, content.getLength() - 1);
+
+		if (isBlock(entry.element))
+			trimLeading = true;
+	}
+
+	public void endPrefixMapping(final String prefix) {
+		System.out.println("end prefix: '" + prefix + "'"); // TODO trace
+	}
+
+	public void ignorableWhitespace(final char[] ch, final int start, final int length) {
+	}
+
+	public void processingInstruction(final String target, final String data) {
+	}
+
+	public void setDocumentLocator(final Locator locator) {
+		this.locator = locator;
+	}
+
+	public void skippedEntity(final java.lang.String name) {
+	}
+
+	public void startDocument() {
+	}
+
+	public void startElement(final String namespaceURI, final String localName, final String qName, final Attributes attrs) throws SAXException {
+		System.out.println("element: '" + qName + "' namespaceUri: '" + namespaceURI + "' local: '" + localName + "'"); // TODO trace
+		final QualifiedName elementName;
+		if ("".equals(localName))
+			elementName = new QualifiedName(null, qName);
+		else
+		elementName = new QualifiedName(namespaceURI, localName);
+		Element element;
+		if (stack.isEmpty()) {
+			rootElement = new RootElement(elementName);
+			element = rootElement;
+			if (policyFactory != null)
+				policy = policyFactory.getPolicy(dtdPublicID);
+		} else {
+			element = new Element(elementName);
+
+			final Element parent = stack.getLast().element;
+			parent.addChild(element);
+		}
+
+		final String defaultNamespaceUri = namespaceStack.peekDefault();
+		if (defaultNamespaceUri != null)
+			element.declareDefaultNamespace(defaultNamespaceUri);
+		
+		for (final String prefix : namespaceStack.getPrefixes())
+			element.declareNamespace(prefix, namespaceStack.peek(prefix));
+		
+		final int n = attrs.getLength();
+		for (int i = 0; i < n; i++) {
+			System.out.println("attr: " + attrs.getQName(i)); // TODO trace
+			final QualifiedName attributeName;
+			if ("".equals(attrs.getLocalName(i)))
+				attributeName = new QualifiedName(null, attrs.getQName(i));
+			else if ("".equals(attrs.getURI(i)))
+				attributeName = new QualifiedName(elementName.getQualifier(), attrs.getLocalName(i));
+			else
+				attributeName = new QualifiedName(attrs.getURI(i), attrs.getLocalName(i));
+			try {
+				element.setAttribute(attributeName, attrs.getValue(i));
+			} catch (final DocumentValidationException e) {
+				throw new SAXParseException("DocumentValidationException", locator, e);
+			}
+		}
+
+		appendChars(isBlock(element));
+
+		stack.add(new StackEntry(element, content.getLength(), isPre(element)));
+		content.insertString(content.getLength(), "\0");
+
+		trimLeading = true;
+
+		namespaceStack.clear();
+	}
+
+	public void startPrefixMapping(final String prefix, final String uri) {
+		System.out.println("prefix: '" + prefix + "' uri: '" + uri + "'"); // TODO trace
+		checkPrefix(prefix);
+		if (isDefaultPrefix(prefix))
+			namespaceStack.pushDefault(uri);
+		else
+			namespaceStack.push(prefix, uri);
+	}
+
+	private static void checkPrefix(final String prefix) {
+		Assert.isNotNull(prefix, "null is not a valid namespace prefix.");
+	}
+
+	private static boolean isDefaultPrefix(final String prefix) {
+		return "".equals(prefix);
+	}
+
+	// ============================================== LexicalHandler methods
+
+	public void comment(final char[] ch, final int start, final int length) {
+	}
+
+	public void endCDATA() {
+	}
+
+	public void endDTD() {
+	}
+
+	public void endEntity(final String name) {
+	}
+
+	public void startCDATA() {
+	}
+
+	public void startDTD(final String name, final String publicId, final String systemId) {
+		dtdPublicID = publicId;
+		dtdSystemID = systemId;
+	}
+
+	public void startEntity(final java.lang.String name) {
+	}
+
+	// ======================================================== PRIVATE
+
+	// Append any pending characters to the content
+	private void appendChars(final boolean trimTrailing) {
+
+		StringBuffer sb;
+
+		sb = cleanUpTextContent(trimTrailing);
+
+		content.insertString(content.getLength(), sb.toString());
+
+		pendingChars.setLength(0);
+		trimLeading = false;
+	}
+
+	private StringBuffer cleanUpTextContent(final boolean trimTrailing) {
+		StringBuffer sb;
+		final StackEntry entry = stack.isEmpty() ? null : stack.getLast();
+
+		if (entry != null && entry.pre)
+			sb = pendingChars;
+		else {
+
+			// collapse the space in the pending characters
+			sb = new StringBuffer(pendingChars.length());
+			boolean ws = false; // true if we're in a run of whitespace
+			for (int i = 0; i < pendingChars.length(); i++) {
+				final char c = pendingChars.charAt(i);
+				if (Character.isWhitespace(c))
+					ws = true;
+				else {
+					if (ws) {
+						sb.append(' ');
+						ws = false;
+					}
+					sb.append(c);
+				}
+			}
+			if (ws)
+				sb.append(' ');
+			// trim leading and trailing space, if necessary
+			if (trimLeading && sb.length() > 0 && sb.charAt(0) == ' ')
+				sb.deleteCharAt(0);
+			if (trimTrailing && sb.length() > 0 && sb.charAt(sb.length() - 1) == ' ')
+				sb.setLength(sb.length() - 1);
+		}
+
+		normalizeNewlines(sb);
+		return sb;
+	}
+
+	private boolean isBlock(final Element element) {
+		return policy != null && policy.isBlock(element);
+	}
+
+	private boolean isPre(final Element element) {
+		return policy != null && policy.isPre(element);
+	}
+
+	/**
+	 * Convert lines that end in CR and CRLFs to plain newlines.
+	 * 
+	 * @param sb
+	 *            StringBuffer to be normalized.
+	 */
+	private void normalizeNewlines(final StringBuffer sb) {
+
+		// State machine states
+		final int START = 0;
+		final int SEEN_CR = 1;
+
+		int state = START;
+		int i = 0;
+		while (i < sb.length()) {
+			// No simple 'for' here, since we may delete chars
+
+			final char c = sb.charAt(i);
+
+			switch (state) {
+			case START:
+				if (c == '\r')
+					state = SEEN_CR;
+				i++;
+				break;
+
+			case SEEN_CR:
+				if (c == '\n') {
+					// CR-LF, just delete the previous CR
+					sb.deleteCharAt(i - 1);
+					state = START;
+					// no need to advance i, since it's done implicitly
+				} else if (c == '\r') {
+					// CR line ending followed by another
+					// Replace the first with a newline...
+					sb.setCharAt(i - 1, '\n');
+					i++;
+					// ...and stay in the SEEN_CR state
+				} else {
+					// CR line ending, replace it with a newline
+					sb.setCharAt(i - 1, '\n');
+					i++;
+					state = START;
+				}
+			}
+		}
+
+		if (state == SEEN_CR) {
+			// CR line ending, replace it with a newline
+		}
+	}
+
+	private static class StackEntry {
+		public Element element;
+		public int offset;
+		public boolean pre;
+
+		public StackEntry(final Element element, final int offset, final boolean pre) {
+			this.element = element;
+			this.offset = offset;
+			this.pre = pre;
+		}
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentEvent.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentEvent.java
new file mode 100644
index 0000000..26416db
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentEvent.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.EventObject;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.wst.xml.vex.core.internal.undo.IUndoableEdit;
+
+/**
+ * Encapsulation of the details of a document change
+ * @model
+ */
+public class DocumentEvent extends EventObject {
+
+	private static final long serialVersionUID = -9028980559838712720L;
+	
+	private Document document;
+	private Element parentElement;
+	private int offset;
+	private int length;
+	private QualifiedName attributeName;
+	private String oldAttributeValue;
+	private String newAttributeValue;
+	private IUndoableEdit undoableEdit;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param document
+	 *            Document that changed.
+	 * @param e1
+	 *            Element containing the change.
+	 * @param offset
+	 *            offset at which the change occurred.
+	 * @param length
+	 *            length of the change.
+	 * @param undoableEdit
+	 *            IUndoableEdit that can be used to undo the change.
+	 */
+	public DocumentEvent(Document document, Element e1, int offset,
+			int length, IUndoableEdit undoableEdit) {
+
+		super(document);
+		this.document = document;
+		this.parentElement = e1;
+		this.offset = offset;
+		this.length = length;
+		this.undoableEdit = undoableEdit;
+	}
+
+	/**
+	 * Class constructor used when firing an attributeChanged event.
+	 * 
+	 * @param document
+	 *            Document that changed.
+	 * @param parentElement
+	 *            element containing the attribute that changed
+	 * @param attributeName
+	 *            name of the attribute that changed
+	 * @param oldAttributeValue
+	 *            value of the attribute before the change.
+	 * @param newAttributeValue
+	 *            value of the attribute after the change.
+	 * @param undoableEdit
+	 *            IUndoableEdit that can be used to undo the change.
+	 */
+	public DocumentEvent(Document document, Element parentElement,
+			QualifiedName attributeName, String oldAttributeValue,
+			String newAttributeValue, IUndoableEdit undoableEdit) {
+
+		super(document);
+		this.document = document;
+		this.parentElement = parentElement;
+		this.attributeName = attributeName;
+		this.oldAttributeValue = oldAttributeValue;
+		this.newAttributeValue = newAttributeValue;
+		this.undoableEdit = undoableEdit;
+	}
+
+	/**
+	 * Returns the length of the change.
+	 * @model
+	 */
+	public int getLength() {
+		return this.length;
+	}
+
+	/**
+	 * Returns the offset at which the change occurred.
+	 * @model
+	 */
+	public int getOffset() {
+		return this.offset;
+	}
+
+	/**
+	 * Returns the element containing the change.
+	 * @model
+	 */
+	public Element getParentElement() {
+		return this.parentElement;
+	}
+
+	/**
+	 * @return the value of the attribute before the change. If null, indicates
+	 *         that the attribute was removed.
+	 * @model
+	 */
+	public String getNewAttributeValue() {
+		return newAttributeValue;
+	}
+
+	/**
+	 * @return the value of the attribute after the change. If null, indicates
+	 *         the attribute did not exist before the change.
+	 * @model
+	 */
+	public String getOldAttributeValue() {
+		return oldAttributeValue;
+	}
+
+	/**
+	 * @return the name of the attribute that was changed.
+	 * @model
+	 */
+	public QualifiedName getAttributeName() {
+		return attributeName;
+	}
+
+	/**
+	 * @return the document for which this event was generated
+	 * @model
+	 */
+	public Document getDocument() {
+		return document;
+	}
+
+	/**
+	 * Returns the undoable edit that can be used to undo the action. May be
+	 * null, in which case the action cannot be undone.
+	 * @model
+	 */
+	public IUndoableEdit getUndoableEdit() {
+		return undoableEdit;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentFragment.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentFragment.java
new file mode 100644
index 0000000..f2aacee
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentFragment.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * Represents a fragment of an XML document.
+ */
+public class DocumentFragment implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Mime type representing document fragments: "text/x-vex-document-fragment"
+	 * 
+	 * @model
+	 */
+	public static final String MIME_TYPE = "application/x-vex-document-fragment";
+
+	private Content content;
+	private List<Element> elements;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param content
+	 *            Content holding the fragment's content.
+	 * @param elements
+	 *            Elements that make up this fragment.
+	 */
+	public DocumentFragment(final Content content, final List<Element> elements) {
+		this.content = content;
+		this.elements = elements;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocumentFragment#getContent
+	 * ()
+	 */
+	public Content getContent() {
+		return content;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocumentFragment#getLength
+	 * ()
+	 */
+	public int getLength() {
+		return content.getLength();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocumentFragment#getElements
+	 * ()
+	 */
+	public List<Element> getElements() {
+		return elements;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocumentFragment#getNodeNames
+	 * ()
+	 */
+	public List<QualifiedName> getNodeNames() {
+		final List<Node> nodes = getNodes();
+		final List<QualifiedName> names = new ArrayList<QualifiedName>(nodes.size());
+		for (Node node : nodes)
+			if (node instanceof Text)
+				names.add(Validator.PCDATA);
+			else
+				names.add(((Element) node).getQualifiedName());
+
+		return names;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.dom.IVEXDocumentFragment#getNodes()
+	 */
+	public List<Node> getNodes() {
+		return Document.createNodeList(getContent(), 0, getContent().getLength(), getNodes(getElements()));
+	}
+
+	private List<Node> getNodes(final List<Element> elements) {
+		final List<Node> nodes = new ArrayList<Node>();
+		for (final Node node : elements)
+			if (node.getNodeType().equals("Element"))
+				nodes.add(node);
+		return nodes;
+	}
+
+	/*
+	 * Custom Serialization Methods
+	 */
+
+	private void writeObject(final ObjectOutputStream out) throws IOException {
+		out.writeUTF(content.getString(0, content.getLength()));
+		out.writeInt(elements.size());
+		for (int i = 0; i < elements.size(); i++)
+			writeElement(elements.get(i), out);
+	}
+
+	private void writeElement(final Element element, final ObjectOutputStream out) throws IOException {
+
+		out.writeObject(element.getQualifiedName());
+		out.writeInt(element.getStartOffset());
+		out.writeInt(element.getEndOffset());
+		final Collection<Attribute> attributes = element.getAttributes();
+		out.writeInt(attributes.size());
+		for (final Attribute attribute : attributes) {
+			out.writeObject(attribute.getQualifiedName());
+			out.writeObject(attribute.getValue());
+		}
+		final List<Element> children = element.getChildElements();
+		out.writeInt(children.size());
+		for (int i = 0; i < children.size(); i++)
+			writeElement(children.get(i), out);
+	}
+
+	private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+		final String s = in.readUTF();
+		content = new GapContent(s.length());
+		content.insertString(0, s);
+		final int n = in.readInt();
+		elements = new ArrayList<Element>(n);
+		for (int i = 0; i < n; i++)
+			elements.add(readElement(in));
+	}
+
+	private Element readElement(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+		final QualifiedName elementName = createQualifiedName(in.readObject());
+		final int startOffset = in.readInt();
+		final int endOffset = in.readInt();
+		final Element element = new Element(elementName);
+		element.setContent(content, startOffset, endOffset);
+
+		final int attrCount = in.readInt();
+		for (int i = 0; i < attrCount; i++) {
+			final QualifiedName attributeName = createQualifiedName(in.readObject());
+			final String value = (String) in.readObject();
+			try {
+				element.setAttribute(attributeName, value);
+			} catch (final DocumentValidationException e) {
+				// Should never happen; there ain't no document
+				e.printStackTrace();
+			}
+		}
+
+		final int childCount = in.readInt();
+		for (int i = 0; i < childCount; i++) {
+			final Element child = readElement(in);
+			child.setParent(element);
+			element.insertChild(i, child);
+		}
+
+		return element;
+	}
+
+	private static QualifiedName createQualifiedName(final Object object) {
+		final String serializedQualifiedName = object.toString();
+		final int localNameStartIndex = serializedQualifiedName.lastIndexOf(':') + 1;
+		if (localNameStartIndex == 0)
+			return new QualifiedName(null, serializedQualifiedName);
+		final String qualifier = serializedQualifiedName.substring(0, localNameStartIndex - 1);
+		final String localName = serializedQualifiedName.substring(localNameStartIndex);
+		return new QualifiedName(qualifier, localName);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentListener.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentListener.java
new file mode 100644
index 0000000..8776b36
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentListener.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * Receives notifications of document changes.
+ */
+public interface DocumentListener extends java.util.EventListener {
+
+	/**
+	 * Called when an attribute is changed in one of the document's elements.
+	 * 
+	 * @param e
+	 *            the document event.
+	 */
+	public void attributeChanged(DocumentEvent e);
+
+	/**
+	 * Called before content is deleted from a document.
+	 * 
+	 * @param e
+	 *            the document event
+	 */
+	public void beforeContentDeleted(DocumentEvent e);
+
+	/**
+	 * Called before content is inserted into a document.
+	 * 
+	 * @param e
+	 *            the document event
+	 */
+	public void beforeContentInserted(DocumentEvent e);
+
+	/**
+	 * Called when content is deleted from a document.
+	 * 
+	 * @param e
+	 *            the document event
+	 */
+	public void contentDeleted(DocumentEvent e);
+
+	/**
+	 * Called when content is inserted into a document.
+	 * 
+	 * @param e
+	 *            the document event
+	 */
+	public void contentInserted(DocumentEvent e);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentReader.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentReader.java
new file mode 100644
index 0000000..a94975c
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentReader.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * Class for creating documents given a URL.
+ */
+public class DocumentReader {
+
+	/**
+	 * Returns the debugging flag.
+	 */
+	public boolean isDebugging() {
+		return debugging;
+	}
+
+	/**
+	 * Returns the entity resolver for this reader.
+	 */
+	public EntityResolver getEntityResolver() {
+		return entityResolver;
+	}
+
+	/**
+	 * Returns the whitespace policy factory for this reader.
+	 */
+	public IWhitespacePolicyFactory getWhitespacePolicyFactory() {
+		return whitespacePolicyFactory;
+	}
+
+	/**
+	 * Reads a document given a URL.
+	 * 
+	 * @param url
+	 *            URL from which to load the document.
+	 */
+	public Document read(final URL url) throws IOException, ParserConfigurationException, SAXException {
+
+		return read(new InputSource(url.toString()));
+	}
+
+	/**
+	 * Reads a document from a string. This is mainly used for short documents
+	 * in unit tests.
+	 * 
+	 * @param s
+	 *            String containing the document to be read.
+	 */
+	public Document read(final String s) throws IOException, ParserConfigurationException, SAXException {
+
+		final Reader reader = new CharArrayReader(s.toCharArray());
+		return this.read(new InputSource(reader));
+	}
+
+	/**
+	 * Reads a document given a SAX InputSource.
+	 * 
+	 * @param is
+	 *            SAX InputSource from which to load the document.
+	 */
+	public Document read(final InputSource is) throws IOException, ParserConfigurationException, SAXException {
+
+		final SAXParserFactory factory = SAXParserFactory.newInstance();
+		factory.setValidating(false); // TODO: experimental--SWT implementation
+		factory.setNamespaceAware(true);
+		final XMLReader xmlReader = factory.newSAXParser().getXMLReader();
+		// xmlReader.setFeature("http://xml.org/sax/features/validation",
+		// false);
+		final org.eclipse.wst.xml.vex.core.internal.dom.DocumentBuilder builder = new org.eclipse.wst.xml.vex.core.internal.dom.DocumentBuilder(
+				getWhitespacePolicyFactory());
+
+		ContentHandler contentHandler = builder;
+		LexicalHandler lexicalHandler = builder;
+
+		if (isDebugging()) {
+			final Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { ContentHandler.class, LexicalHandler.class },
+					new InvocationHandler() {
+						public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+							try {
+								return method.invoke(builder, args);
+							} catch (final InvocationTargetException ex) {
+								ex.getCause().printStackTrace();
+								throw ex.getCause();
+							}
+						}
+					});
+
+			contentHandler = (ContentHandler) proxy;
+			lexicalHandler = (LexicalHandler) proxy;
+		}
+
+		xmlReader.setContentHandler(contentHandler);
+		xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
+		if (getEntityResolver() != null)
+			xmlReader.setEntityResolver(getEntityResolver());
+		xmlReader.parse(is);
+		final Document result = builder.getDocument();
+		if (result != null)
+			result.setDocumentURI(is.getSystemId());
+		return result;
+	}
+
+	/**
+	 * Sets the debugging flag.
+	 * 
+	 * @param debugging
+	 *            true if the component should log debugging info to stdout.
+	 */
+	public void setDebugging(final boolean debugging) {
+		this.debugging = debugging;
+	}
+
+	/**
+	 * Sets the entity resolver for this reader.
+	 * 
+	 * @param entityResolver
+	 *            The entityResolver to set.
+	 */
+	public void setEntityResolver(final EntityResolver entityResolver) {
+		this.entityResolver = entityResolver;
+	}
+
+	/**
+	 * Sets the whitespace policy factory for this reader. This factory is used
+	 * to obtain a whitespace policy once the public ID of the document is
+	 * known.
+	 * 
+	 * @param whitespacePolicyFactory
+	 *            The whitespacePolicyFactory to set.
+	 */
+	public void setWhitespacePolicyFactory(final IWhitespacePolicyFactory whitespacePolicyFactory) {
+		this.whitespacePolicyFactory = whitespacePolicyFactory;
+	}
+
+	// ======================================================= PRIVATE
+
+	private boolean debugging;
+	private EntityResolver entityResolver;
+	private IWhitespacePolicyFactory whitespacePolicyFactory;
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentValidationException.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentValidationException.java
new file mode 100644
index 0000000..fe613b1
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentValidationException.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * Exception thrown when an change would have compromised document validity.
+ */
+public class DocumentValidationException extends RuntimeException {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param message
+	 *            Message indicating the nature of the exception.
+	 */
+	public DocumentValidationException(String message) {
+		super(message);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentWriter.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentWriter.java
new file mode 100644
index 0000000..bf57aec
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/DocumentWriter.java
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.validator.AttributeDefinition;
+
+/**
+ * Writes a document to an output stream, using a stylesheet to provide
+ * formatting hints.
+ * 
+ * <ul>
+ * <li>Children of an element are indented by a configurable amount.</li>
+ * <li>Text is wrapped to fit within a configurable width.
+ * <li>
+ * </ul>
+ * 
+ * <p>
+ * Documents are currently saved UTF-8 encoding, with no encoding specified in
+ * the XML declaration.
+ * </p>
+ */
+public class DocumentWriter {
+
+	private IWhitespacePolicy whitespacePolicy;
+	private String indent;
+	private int wrapColumn;
+
+	/**
+	 * Class constructor.
+	 */
+	public DocumentWriter() {
+		indent = "  ";
+		wrapColumn = 72;
+	}
+
+/**
+     * Escapes special XML characters. Changes '<', '>', and '&' to
+     * '&lt;', '&gt;' and '&amp;', respectively.
+     *
+     * @param s the string to be escaped.
+     * @return the escaped string
+     */
+	public static String escape(final String s) {
+		final StringBuffer sb = new StringBuffer(s.length());
+
+		for (int i = 0; i < s.length(); i++) {
+			final char c = s.charAt(i);
+			if (c == '<')
+				sb.append("&lt;");
+			else if (c == '>')
+				sb.append("&gt;");
+			else if (c == '&')
+				sb.append("&amp;");
+			else if (c == '"')
+				sb.append("&quot;");
+			else if (c == '\'')
+				sb.append("&apos;");
+			else
+				sb.append(c);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Returns the indent string. By default this is two spaces.
+	 */
+	public String getIndent() {
+		return indent;
+	}
+
+	/**
+	 * Returns the whitespace policy used by this writer.
+	 */
+	public IWhitespacePolicy getWhitespacePolicy() {
+		return whitespacePolicy;
+	}
+
+	/**
+	 * Returns the column at which text should be wrapped. By default this is
+	 * 72.
+	 */
+	public int getWrapColumn() {
+		return wrapColumn;
+	}
+
+	/**
+	 * Sets the value of the indent string.
+	 * 
+	 * @param indent
+	 *            new value for the indent string.
+	 */
+	public void setIndent(final String indent) {
+		this.indent = indent;
+	}
+
+	/**
+	 * Sets the whitespace policy for this writer. The whitespace policy tells
+	 * the writer which elements are block-formatted and which are
+	 * pre-formatted.
+	 * 
+	 * @param whitespacePolicy
+	 *            The whitespacePolicy to set.
+	 */
+	public void setWhitespacePolicy(final IWhitespacePolicy whitespacePolicy) {
+		this.whitespacePolicy = whitespacePolicy;
+	}
+
+	/**
+	 * Sets the value of the wrap column.
+	 * 
+	 * @param wrapColumn
+	 *            new value for the wrap column.
+	 */
+	public void setWrapColumn(final int wrapColumn) {
+		this.wrapColumn = wrapColumn;
+	}
+
+	public void write(final Document doc, final OutputStream os) throws IOException {
+
+		final OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
+		final PrintWriter pw = new PrintWriter(osw);
+		pw.println("<?xml version='1.0'?>");
+
+		if ((doc).getSystemID() != null) {
+			final StringBuffer sb = new StringBuffer();
+			sb.append("<!DOCTYPE ");
+			sb.append(doc.getRootElement().getPrefixedName());
+			if (doc.getPublicID() != null) {
+				sb.append(" PUBLIC");
+				sb.append(" \"");
+				sb.append(doc.getPublicID());
+				sb.append("\"");
+			} else
+				sb.append(" SYSTEM");
+			sb.append(" \"");
+			sb.append((doc).getSystemID());
+			sb.append("\">");
+			pw.println(sb.toString());
+		}
+		writeNode(doc.getRootElement(), pw, "");
+		pw.flush();
+	}
+
+	// ====================================================== PRIVATE
+
+	private void writeNode(final Node node, final PrintWriter pw, final String indent) {
+
+		if (node instanceof Text) {
+			final TextWrapper wrapper = new TextWrapper();
+			wrapper.add(escape(node.getText()));
+
+			final String[] lines = wrapper.wrap(wrapColumn - indent.length());
+
+			for (final String line : lines) {
+				pw.print(indent);
+				pw.println(line);
+			}
+
+		} else {
+
+			final Element element = (Element) node;
+
+			if (whitespacePolicy != null && whitespacePolicy.isPre(element)) {
+				pw.print(indent);
+				writeNodeNoWrap(node, pw);
+				pw.println();
+				return;
+			}
+
+			boolean hasBlockChild = false;
+			final List<Element> children = element.getChildElements();
+			for (int i = 0; i < children.size(); i++)
+				if (whitespacePolicy != null && whitespacePolicy.isBlock(children.get(i))) {
+					hasBlockChild = true;
+					break;
+				}
+
+			if (hasBlockChild) {
+				pw.print(indent);
+				pw.print("<");
+				pw.print(element.getPrefixedName());
+
+				final TextWrapper wrapper = new TextWrapper();
+				wrapper.addNoSplit(getNamespaceDeclarationsString(element));
+				wrapper.addNoSplit(getAttributeString(element));
+				final int outdent = indent.length() + 1 + element.getPrefixedName().length();
+				final String[] lines = wrapper.wrap(wrapColumn - outdent);
+				final char[] bigIndent = new char[outdent];
+				Arrays.fill(bigIndent, ' ');
+				for (int i = 0; i < lines.length; i++) {
+					if (i > 0)
+						pw.print(bigIndent);
+					pw.print(lines[i]);
+					if (i < lines.length - 1)
+						pw.println();
+				}
+				pw.println(">");
+
+				final String childIndent = indent + this.indent;
+				final List<Node> content = element.getChildNodes();
+				for (int i = 0; i < content.size(); i++)
+					writeNode(content.get(i), pw, childIndent);
+				pw.print(indent);
+				pw.print("</");
+				pw.print(element.getPrefixedName());
+				pw.println(">");
+			} else {
+				final TextWrapper wrapper = new TextWrapper();
+				addNode(element, wrapper);
+				final String[] lines = wrapper.wrap(wrapColumn - indent.length());
+				for (final String line : lines) {
+					pw.print(indent);
+					pw.println(line);
+				}
+			}
+
+		}
+	}
+
+	private void writeNodeNoWrap(final Node node, final PrintWriter pw) {
+
+		if (node instanceof Text)
+			pw.print(escape(node.getText()));
+		else {
+
+			final Element element = (Element) node;
+
+			pw.print("<");
+			pw.print(element.getPrefixedName());
+			pw.print(getNamespaceDeclarationsString(element));
+			pw.print(getAttributeString(element));
+			pw.print(">");
+
+			for (Node child : element.getChildNodes())
+				writeNodeNoWrap(child, pw);
+
+			pw.print("</");
+			pw.print(element.getPrefixedName());
+			pw.print(">");
+		}
+	}
+	
+	private String getNamespaceDeclarationsString(final Element element) {
+		final StringBuilder result = new StringBuilder();
+		final String declaredNamespaceURI = element.getDeclaredDefaultNamespaceURI();
+		if (declaredNamespaceURI != null)
+			result.append(" xmlns=\"").append(declaredNamespaceURI).append("\"");
+		for (final String prefix : element.getDeclaredNamespacePrefixes()) {
+			result.append(" xmlns:")
+			.append(prefix)
+			.append("=\"")
+			.append(element.getNamespaceURI(prefix))
+			.append("\"");
+		}
+		return result.toString();
+	}
+
+	private String attrToString(final Attribute attribute) {
+		final StringBuffer result = new StringBuffer();
+		result.append(" ");
+		result.append(attribute.getPrefixedName());
+		result.append("=\"");
+		result.append(escape(attribute.getValue()));
+		result.append("\"");
+		return result.toString();
+	}
+
+	private void addNode(final Node node, final TextWrapper wrapper) {
+		if (node instanceof Text)
+			wrapper.add(escape(node.getText()));
+		else {
+			final Element element = (Element) node;
+			final List<Node> content = element.getChildNodes();
+			final Collection<Attribute> attributes = element.getAttributes();
+
+			if (attributes.isEmpty()) {
+				if (content.isEmpty())
+					wrapper.add("<" + element.getPrefixedName() + "/>");
+				else
+					wrapper.add("<" + element.getPrefixedName() + ">");
+			} else {
+				final Validator validator = element.getDocument().getValidator();
+				final StringBuffer stringBuffer = new StringBuffer();
+				stringBuffer.append("<" + element.getPrefixedName());
+				stringBuffer.append(getNamespaceDeclarationsString(element));
+				for (final Attribute attribute : attributes) {
+					if (!attrHasDefaultValue(validator, attribute))
+						stringBuffer.append(attrToString(attribute));
+				}
+				if (content.isEmpty())
+					stringBuffer.append("/>");
+				else
+					stringBuffer.append(">");
+				wrapper.addNoSplit(stringBuffer.toString());
+			}
+
+			for (int i = 0; i < content.size(); i++)
+				addNode(content.get(i), wrapper);
+
+			if (!content.isEmpty())
+				wrapper.add("</" + element.getPrefixedName() + ">");
+		}
+	}
+
+	private String getAttributeString(final Element element) {
+		final Validator validator = element.getDocument().getValidator();
+
+		final StringBuffer result = new StringBuffer();
+		for (final Attribute attribute : element.getAttributes())
+			if (!attrHasDefaultValue(validator, attribute)) {
+				result.append(" ");
+				result.append(attribute.getPrefixedName());
+				result.append("=\"");
+				result.append(escape(attribute.getValue()));
+				result.append("\"");
+			}
+		return result.toString();
+	}
+
+	private static boolean attrHasDefaultValue(final Validator validator, final Attribute attribute) {
+		if (validator != null) {
+			final AttributeDefinition attributeDefinition = validator.getAttributeDefinition(attribute);
+			if (attributeDefinition != null) {
+				final String currentValue = attribute.getValue();
+				final String defaultValue = attributeDefinition.getDefaultValue();
+				return currentValue.equals(defaultValue);
+			}
+		}
+		return false;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Element.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Element.java
new file mode 100644
index 0000000..b9293b0
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Element.java
@@ -0,0 +1,404 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.wst.xml.vex.core.internal.core.QualifiedNameComparator;
+import org.eclipse.wst.xml.vex.core.internal.undo.CannotRedoException;
+import org.eclipse.wst.xml.vex.core.internal.undo.CannotUndoException;
+import org.eclipse.wst.xml.vex.core.internal.undo.IUndoableEdit;
+
+/**
+ * Represents a tag in an XML document. Methods are
+ * available for managing the element's attributes and children.
+ */
+public class Element extends Node implements Cloneable {
+
+	private static final QualifiedName XML_BASE_ATTRIBUTE = new QualifiedName(Namespace.XML_NAMESPACE_URI, "base");
+	
+	private final QualifiedName name;
+	
+	private Element parent = null;
+	private List<Node> childNodes = new ArrayList<Node>();
+	private Map<QualifiedName, Attribute> attributes = new HashMap<QualifiedName, Attribute>();
+	private Map<String, String> namespaceDeclarations = new HashMap<String, String>();
+
+	public Element(final String localName) {
+		this(new QualifiedName(null, localName));
+	}
+	
+	public Element(final QualifiedName qualifiedName) {
+		this.name = qualifiedName;
+	}
+
+	public void addChild(Element child) {
+		childNodes.add(child);
+		child.setParent(this);
+	}
+
+	public Object clone() {
+		try {
+			final Element element = new Element(getQualifiedName());
+			//add the attributes to the element instance to be cloned
+			for (Map.Entry<QualifiedName, Attribute> attr : this.attributes.entrySet())
+				element.setAttribute(attr.getKey(), attr.getValue().getValue());
+			for (Map.Entry<String, String> namespaceDeclaration : this.namespaceDeclarations.entrySet()) {
+				if (namespaceDeclaration.getKey() == null)
+					element.declareDefaultNamespace(namespaceDeclaration.getValue());
+				else
+					element.declareNamespace(namespaceDeclaration.getKey(), namespaceDeclaration.getValue());
+			}
+			return element;
+		} catch (DocumentValidationException ex) {
+			ex.printStackTrace();
+			return null;
+		}
+	}
+
+	public Attribute getAttribute(String localName) {
+		return getAttribute(qualify(localName));
+	}
+	
+	public Attribute getAttribute(QualifiedName name) {
+		return attributes.get(name);
+	}
+	
+	public String getAttributeValue(String localName) {
+		return getAttributeValue(qualify(localName));
+	}
+
+	public String getAttributeValue(QualifiedName name) {
+		final Attribute attribute = getAttribute(name);
+		if (attribute == null || "".equals(attribute.getValue().trim()))
+			return null;
+		return attribute.getValue();
+	}
+	
+	public void removeAttribute(String localName) throws DocumentValidationException {
+		removeAttribute(qualify(localName));
+	}
+	
+	public void removeAttribute(QualifiedName name) throws DocumentValidationException {
+		final Attribute attribute = this.getAttribute(name);
+		if (attribute == null)
+			return;
+		final String oldValue = attribute.getValue();
+		final String newValue = null;
+		if (oldValue != null)
+			this.attributes.remove(name);
+
+		final Document document = getDocument();
+		if (document == null) // document may be null, e.g. when we're cloning an element to produce a document fragment
+			return;
+
+		final IUndoableEdit edit = document.isUndoEnabled() ? new AttributeChangeEdit(name, oldValue, newValue) : null;
+		document.fireAttributeChanged(new DocumentEvent(document, this, name, oldValue, newValue, edit));
+	}
+
+	public void setAttribute(String name, String value) throws DocumentValidationException {
+		setAttribute(qualify(name), value);
+	}
+	
+	private QualifiedName qualify(String localName) {
+		return new QualifiedName(name.getQualifier(), localName);
+	}
+	
+	public void setAttribute(QualifiedName name, String value) throws DocumentValidationException {
+		final Attribute oldAttribute = attributes.get(name);
+		final String oldValue = oldAttribute != null ? oldAttribute.getValue() : null;
+		
+		if (value == null && oldValue == null)
+			return;
+		
+		if (value == null) {
+			this.removeAttribute(name);
+		} else {
+			if (value.equals(oldValue)) {
+				return;
+			} else {
+				final Attribute newAttribute = new Attribute(this, name, value);
+				this.attributes.put(name, newAttribute);
+
+				final Document document = getDocument();
+				if (document == null) // doc may be null, e.g. when we're cloning an element to produce a document fragment
+					return;
+
+				final IUndoableEdit edit = document.isUndoEnabled() ? new AttributeChangeEdit(name, oldValue, value) : null;
+				document.fireAttributeChanged(new DocumentEvent(document, this, name, oldValue, value, edit));
+			}
+		}
+	}
+
+	public Collection<Attribute> getAttributes() {
+		final ArrayList<Attribute> result = new ArrayList<Attribute>(attributes.values());
+		Collections.sort(result);
+		return Collections.unmodifiableCollection(result);
+	}
+	
+	public List<QualifiedName> getAttributeNames() {
+		ArrayList<QualifiedName> result = new ArrayList<QualifiedName>();
+		for (Attribute attribute : attributes.values()) {
+			result.add(attribute.getQualifiedName());
+		}
+		Collections.sort(result, new QualifiedNameComparator());
+		return result;
+	}
+
+	public Iterator<Node> getChildIterator() {
+		return childNodes.iterator();
+	}
+
+	public List<Element> getChildElements() {
+		List<Node> nodes = getChildNodes();
+		Iterator<Node> iter = nodes.iterator();
+		List<Element> elements = new ArrayList<Element>();
+		while (iter.hasNext()) {
+			Node node = iter.next();
+			if (node.getNodeType().equals("Element")) {
+				elements.add((Element) node);
+			}
+		}
+		return elements;
+	}
+
+	public List<Node> getChildNodes() {
+		return Document.createNodeList(getContent(), getStartOffset() + 1, getEndOffset(), childNodes);
+	}
+
+	public Document getDocument() {
+		Element root = this;
+		while (root.getParent() != null) {
+			root = root.getParent();
+		}
+		if (root instanceof RootElement) {
+			return root.getDocument();
+		} else {
+			return null;
+		}
+	}
+
+	public String getLocalName() {
+		return name.getLocalName();
+	}
+	
+	public QualifiedName getQualifiedName() {
+		return name;
+	}
+	
+	public String getPrefix() {
+		return getNamespacePrefix(name.getQualifier());
+	}
+	
+	public String getPrefixedName() {
+		String prefix = getPrefix();
+		if (prefix == null)
+			return getLocalName();
+		return prefix + ":" + getLocalName();
+	}
+
+	public Element getParent() {
+		return this.parent;
+	}
+
+	public String getText() {
+		final String s = super.getText();
+		final StringBuilder sb = new StringBuilder(s.length());
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+			if (c != 0) {
+				sb.append(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Inserts the given element as a child at the given child index. Sets the
+	 * parent attribute of the given element to this element.
+	 */
+	public void insertChild(int index, Element child) {
+		childNodes.add(index, child);
+		child.setParent(this);
+	}
+
+	public boolean isEmpty() {
+		return this.getStartOffset() + 1 == this.getEndOffset();
+	}
+
+	public String toString() {
+
+		StringBuffer sb = new StringBuffer();
+		sb.append("<");
+		sb.append(this.getPrefixedName().toString());
+		
+		for (Attribute attribute : getAttributes()) {
+			sb.append(" ");
+			sb.append(attribute.getPrefixedName());
+			sb.append("=\"");
+			sb.append(attribute.getValue());
+			sb.append("\"");
+		}
+
+		sb.append("> (");
+		sb.append(this.getStartPosition());
+		sb.append(",");
+		sb.append(this.getEndPosition());
+		sb.append(")");
+
+		return sb.toString();
+	}
+
+	// ========================================================= PRIVATE
+
+	private class AttributeChangeEdit implements IUndoableEdit {
+
+		private QualifiedName name;
+		private String oldValue;
+		private String newValue;
+
+		public AttributeChangeEdit(QualifiedName name, String oldValue, String newValue) {
+			this.name = name;
+			this.oldValue = oldValue;
+			this.newValue = newValue;
+		}
+
+		public boolean combine(IUndoableEdit edit) {
+			return false;
+		}
+
+		public void undo() throws CannotUndoException {
+			Document doc = (Document) getDocument();
+			try {
+				doc.setUndoEnabled(false);
+				setAttribute(name, oldValue);
+			} catch (DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				doc.setUndoEnabled(true);
+			}
+		}
+
+		public void redo() throws CannotRedoException {
+			Document doc = (Document) getDocument();
+			try {
+				doc.setUndoEnabled(false);
+				setAttribute(name, newValue);
+			} catch (DocumentValidationException ex) {
+				throw new CannotUndoException();
+			} finally {
+				doc.setUndoEnabled(true);
+			}
+		}
+	}
+
+	public void setParent(Element parent) {
+		this.parent = parent;
+	}
+
+	public String getNamespaceURI(final String namespacePrefix) {
+		if (namespaceDeclarations.containsKey(namespacePrefix))
+			return namespaceDeclarations.get(namespacePrefix);
+		if (parent != null)
+			return parent.getNamespaceURI(namespacePrefix);
+		return null;
+	}
+	
+	public String getDefaultNamespaceURI() {
+		return getNamespaceURI(null);
+	}
+	
+	public String getDeclaredDefaultNamespaceURI() {
+		return namespaceDeclarations.get(null);
+	}
+
+	public String getNamespacePrefix(final String namespaceURI) {
+		if (namespaceURI == null)
+			return null;
+		for (Entry<String, String> entry: namespaceDeclarations.entrySet())
+			if (entry.getValue().equals(namespaceURI))
+				return entry.getKey();
+		if (parent != null) {
+			final String parentPrefix = parent.getNamespacePrefix(namespaceURI);
+			if (!namespaceDeclarations.containsKey(parentPrefix))
+				return parentPrefix;
+		}
+		return null;
+	}
+	
+	public Collection<String> getDeclaredNamespacePrefixes() {
+		final ArrayList<String> result = new ArrayList<String>();
+		for (final String prefix : namespaceDeclarations.keySet()) {
+			if (prefix != null)
+				result.add(prefix);
+		}
+		Collections.sort(result);
+		return result;
+	}
+	
+	public void declareNamespace(final String namespacePrefix, final String namespaceURI) {
+		if (namespaceURI == null || "".equals(namespaceURI.trim()))
+			return;
+		namespaceDeclarations.put(namespacePrefix, namespaceURI);
+	}
+	
+	public void removeNamespace(String namespacePrefix) {
+		namespaceDeclarations.remove(namespacePrefix);
+	}
+	
+	public void declareDefaultNamespace(final String namespaceURI) {
+		declareNamespace(null, namespaceURI);
+	}
+	
+	public void removeDefaultNamespace() {
+		removeNamespace(null);
+	}
+
+	@Override
+	public String getNodeType() {
+		return "Element";
+	}
+
+	/**
+	 * Sets the content of the node
+	 * 
+	 * @param content
+	 *            Content object holding the node's content
+	 * @param startOffset
+	 *            offset at which the node's content starts
+	 * @param endOffset
+	 *            offset at which the node's content ends
+	 */
+	public void setContent(Content content, int startOffset, int endOffset) {
+		super.setContent(content, startOffset, endOffset);
+	}
+
+	@Override
+	public String getBaseURI() {
+		final Attribute baseAttribute = getAttribute(XML_BASE_ATTRIBUTE);
+		if (baseAttribute != null)
+			return baseAttribute.getValue();
+		if (getParent() != null)
+			return getParent().getBaseURI();
+		if (getDocument() != null)
+			return getDocument().getBaseURI();
+		return null;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/GapContent.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/GapContent.java
new file mode 100644
index 0000000..e6b6a17
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/GapContent.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of the <code>Content</code> interface that manages changes
+ * efficiently. Implements a buffer that keeps its free space (the "gap") at the
+ * location of the last change. Insertions at the start of the gap require no
+ * other chars to be moved so long as the insertion is smaller than the gap.
+ * Deletions that end of the gap are also very efficent. Furthermore, changes
+ * near the gap require relatively few characters to be moved.
+ */
+public class GapContent implements Content {
+
+	private char[] content;
+	private int gapStart;
+	private int gapEnd;
+	private final Map<Position, Position> positions = new HashMap<Position, Position>();
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param initialCapacity
+	 *            initial capacity of the content.
+	 */
+	public GapContent(int initialCapacity) {
+		assertPositive(initialCapacity);
+
+		this.content = new char[initialCapacity];
+		this.gapStart = 0;
+		this.gapEnd = initialCapacity;
+	}
+
+	/**
+	 * Creates a new Position object at the given initial offset.
+	 * 
+	 * @param offset
+	 *            initial offset of the position
+	 */
+	public Position createPosition(int offset) {
+
+		assertOffset(offset, 0, this.getLength());
+
+		Position pos = new GapContentPosition(offset);
+		this.positions.put(pos, pos);
+
+		return pos;
+	}
+
+	/**
+	 * Insert a string into the content.
+	 * 
+	 * @param offset
+	 *            Offset at which to insert the string.
+	 * @param s
+	 *            String to insert.
+	 */
+	public void insertString(int offset, String s) {
+
+		assertOffset(offset, 0, this.getLength());
+
+		if (s.length() > (this.gapEnd - this.gapStart)) {
+			this.expandContent(this.getLength() + s.length());
+		}
+
+		//
+		// Optimization: no need to update positions if we're inserting
+		// after existing content (offset == this.getLength()) and if
+		// we don't have to move the gap to do it (offset == gapStart).
+		//
+		// This significantly improves document load speed.
+		//
+		boolean atEnd = (offset == this.getLength() && offset == gapStart);
+
+		this.moveGap(offset);
+		s.getChars(0, s.length(), this.content, offset);
+		this.gapStart += s.length();
+
+		if (!atEnd) {
+
+			// Update positions
+			for (Position pos : this.positions.keySet()) {
+				if (pos.getOffset() >= offset) {
+					pos.setOffset(pos.getOffset() + s.length());
+				}
+			}
+
+		}
+	}
+
+	/**
+	 * Deletes the given range of characters.
+	 * 
+	 * @param offset
+	 *            Offset from which characters should be deleted.
+	 * @param length
+	 *            Number of characters to delete.
+	 */
+	public void remove(int offset, int length) {
+
+		assertOffset(offset, 0, this.getLength() - length);
+		assertPositive(length);
+
+		this.moveGap(offset + length);
+		this.gapStart -= length;
+
+		for (Position pos : this.positions.keySet()) {
+			if (pos.getOffset() >= offset + length) {
+				pos.setOffset(pos.getOffset() - length);
+			} else if (pos.getOffset() >= offset) {
+				pos.setOffset(offset);
+			}
+		}
+	}
+
+	/**
+	 * Gets a substring of the content.
+	 * 
+	 * @param offset
+	 *            Offset at which the string begins.
+	 * @param length
+	 *            Number of characters to return.
+	 */
+	public String getString(int offset, int length) {
+
+		assertOffset(offset, 0, this.getLength() - length);
+		assertPositive(length);
+
+		if (offset + length <= this.gapStart) {
+			return new String(this.content, offset, length);
+		} else if (offset >= this.gapStart) {
+			return new String(this.content, offset - this.gapStart
+					+ this.gapEnd, length);
+		} else {
+			StringBuffer sb = new StringBuffer(length);
+			sb.append(this.content, offset, this.gapStart - offset);
+			sb.append(this.content, this.gapEnd, offset + length
+					- this.gapStart);
+			return sb.toString();
+		}
+	}
+
+	/**
+	 * Return the length of the content.
+	 */
+	public int getLength() {
+		return this.content.length - (this.gapEnd - this.gapStart);
+	}
+
+	// ====================================================== PRIVATE
+
+	private static final int GROWTH_SLOWDOWN_SIZE = 100000;
+	private static final int GROWTH_RATE_FAST = 2;
+	private static final float GROWTH_RATE_SLOW = 1.1f;
+
+	/**
+	 * Implementation of the Position interface.
+	 */
+	private static class GapContentPosition implements Position {
+
+		private int offset;
+		
+		public GapContentPosition(int offset) {
+			this.offset = offset;
+		}
+
+		public int getOffset() {
+			return offset;
+		}
+
+		public void setOffset(int offset) {
+			this.offset = offset;
+		}
+
+		public String toString() {
+			return Integer.toString(offset);
+		}
+	}
+
+	/**
+	 * Assert that the given offset is within the given range, throwing
+	 * IllegalArgumentException if not.
+	 */
+	private static void assertOffset(int offset, int min, int max) {
+		if (offset < min || offset > max) {
+			throw new IllegalArgumentException("Bad offset " + offset
+					+ "must be between " + min + " and " + max);
+		}
+	}
+
+	/**
+	 * Assert that the given value is zero or positive. throwing
+	 * IllegalArgumentException if not.
+	 */
+	private static void assertPositive(int value) {
+		if (value < 0) {
+			throw new IllegalArgumentException(
+					"Value should be zero or positive, but it was " + value);
+		}
+	}
+
+	/**
+	 * Expand the content array to fit at least the given length.
+	 */
+	private void expandContent(int newLength) {
+
+		// grow quickly when small, slower when large
+
+		int newCapacity;
+
+		if (newLength < GROWTH_SLOWDOWN_SIZE) {
+			newCapacity = Math.max((int) (newLength * GROWTH_RATE_FAST), 32);
+		} else {
+			newCapacity = (int) (newLength * GROWTH_RATE_SLOW);
+		}
+
+		char[] newContent = new char[newCapacity];
+
+		System.arraycopy(this.content, 0, newContent, 0, this.gapStart);
+
+		int tailLength = this.content.length - this.gapEnd;
+		System.arraycopy(this.content, this.gapEnd, newContent, newCapacity
+				- tailLength, tailLength);
+
+		this.content = newContent;
+		this.gapEnd = newCapacity - tailLength;
+	}
+
+	/**
+	 * Move the gap to the given offset.
+	 */
+	private void moveGap(int offset) {
+
+		assertOffset(offset, 0, this.getLength());
+
+		if (offset <= this.gapStart) {
+			int length = this.gapStart - offset;
+			System.arraycopy(this.content, offset, this.content, this.gapEnd
+					- length, length);
+			this.gapStart -= length;
+			this.gapEnd -= length;
+		} else {
+			int length = offset - this.gapStart;
+			System.arraycopy(this.content, this.gapEnd, this.content,
+					this.gapStart, length);
+			this.gapStart += length;
+			this.gapEnd += length;
+		}
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicy.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicy.java
new file mode 100644
index 0000000..ccc9c2f
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicy.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * Determines whitespace policy for document elements. For example, a CSS
+ * stylesheet implements a whitespace policy via its display and white-space
+ * properties.
+ */
+public interface IWhitespacePolicy {
+
+	/**
+	 * A NULL object of this type. No blocks and no pre elements.
+	 */
+	IWhitespacePolicy NULL = new IWhitespacePolicy() {
+		public boolean isBlock(Element element) {
+			return false;
+		}
+
+		public boolean isPre(Element element) {
+			return false;
+		}
+	};
+
+	/**
+	 * Returns true if the given element is normally block-formatted.
+	 * 
+	 * @param element
+	 *            Element to test.
+	 */
+	boolean isBlock(Element element);
+
+	/**
+	 * Returns true if the given element is pre-formatted, that is, all of its
+	 * contained whitespace should be preserved.
+	 * 
+	 * @param element
+	 *            Element to test.
+	 */
+	boolean isPre(Element element);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicyFactory.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicyFactory.java
new file mode 100644
index 0000000..6476cfd
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/IWhitespacePolicyFactory.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+
+/**
+ * Factory for returning a WhitespacePolicy object given a document type public
+ * ID. This is required by DocumentBuilder, since we don't know what
+ * WhitespacePolicy we need before we begin parsing the document.
+ */
+public interface IWhitespacePolicyFactory {
+	
+	/**
+	 * A factory that always returns the NULL whitespace policy.
+	 */
+	IWhitespacePolicyFactory NULL = new IWhitespacePolicyFactory() {
+		public IWhitespacePolicy getPolicy(String publicId) {
+			return IWhitespacePolicy.NULL;
+		}
+	};
+
+	/**
+	 * Return a WhitespacePolicy for documents with the given public ID.
+	 * 
+	 * @param publicId
+	 *            Public ID of the document type associated with the document.
+	 */
+	IWhitespacePolicy getPolicy(String publicId);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Namespace.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Namespace.java
new file mode 100644
index 0000000..664cd0e
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Namespace.java
@@ -0,0 +1,26 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.dom;

+

+/**

+ * @author Florian Thienel

+ */

+public class Namespace {

+	

+	public static final String XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";

+	

+	public static final String XML_NAMESPACE_PREFIX = "xml";

+	

+	public static final String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";

+	

+	public static final String XMLNS_NAMESPACE_PREFIX = "xmlns";

+

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/NamespaceStack.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/NamespaceStack.java
new file mode 100644
index 0000000..740202d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/NamespaceStack.java
@@ -0,0 +1,91 @@
+/*******************************************************************************

+ * Copyright (c) 2011 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.dom;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * @author Florian Thienel

+ */

+public class NamespaceStack {

+	private static final String DEFAULT_PREFIX = null;

+

+	private final Map<String, List<String>> stacks = new HashMap<String, List<String>>();

+

+	public String peekDefault() {

+		return peek(DEFAULT_PREFIX);

+	}

+

+	public String peek(final String prefix) {

+		return peek(getStack(prefix));

+	}

+

+	private List<String> getStack(final String prefix) {

+		final List<String> result = stacks.get(prefix);

+		if (result != null)

+			return result;

+		return putNewStack(prefix);

+	}

+

+	private List<String> putNewStack(final String prefix) {

+		final List<String> result = new ArrayList<String>();

+		stacks.put(prefix, result);

+		return result;

+	}

+

+	private String peek(final List<String> stack) {

+		if (stack.isEmpty())

+			return null;

+		return stack.get(stack.size() - 1);

+	}

+

+	private void removeTop(final List<String> stack) {

+		if (stack.isEmpty())

+			return;

+		stack.remove(stack.size() - 1);

+	}

+

+	public void pushDefault(final String namespaceUri) {

+		push(DEFAULT_PREFIX, namespaceUri);

+	}

+

+	public void push(final String prefix, final String namespaceUri) {

+		getStack(prefix).add(namespaceUri);

+	}

+

+	public String popDefault() {

+		return pop(DEFAULT_PREFIX);

+	}

+

+	public String pop(final String prefix) {

+		final List<String> stack = getStack(prefix);

+		final String result = peek(stack);

+		removeTop(stack);

+		return result;

+	}

+

+	public void clear() {

+		stacks.clear();

+	}

+	

+	public Collection<String> getPrefixes() {

+		final ArrayList<String> result = new ArrayList<String>();

+		for (String key : stacks.keySet())

+			if (key != DEFAULT_PREFIX)

+				result.add(key);

+		return result;

+	}

+

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Node.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Node.java
new file mode 100644
index 0000000..8caaf2f
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Node.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * <code>Node</code> represents a component of an XML document. .
+ *
+ */
+public abstract class Node {
+
+	private Content content;
+	private Position startPosition;
+	private Position endPosition;
+	
+	public Content getContent() {
+		return content;
+	}
+
+	public int getEndOffset() {
+		return endPosition.getOffset();
+	}
+
+	public Position getEndPosition() {
+		return endPosition;
+	}
+
+	public int getStartOffset() {
+		return startPosition.getOffset();
+	}
+
+	public Position getStartPosition() {
+		return startPosition;
+	}
+
+	public String getText() {
+		return content.getString(getStartOffset(), getEndOffset() - getStartOffset());
+	}
+
+	/**
+	 * Sets the content of this node
+	 * 
+	 * @param content
+	 *            Content object holding the node's content
+	 * @param startOffset
+	 *            offset at which the node's content starts
+	 * @param endOffset
+	 *            offset at which the node's content ends
+	 */
+	public void setContent(Content content, int startOffset, int endOffset) {
+		this.content = content;
+		startPosition = content.createPosition(startOffset);
+		endPosition = content.createPosition(endOffset);
+	}
+
+	public abstract String getNodeType();
+	
+	public abstract String getBaseURI();
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Position.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Position.java
new file mode 100644
index 0000000..4d5c931
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Position.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * Represents a logical location in a document. As the document is modified,
+ * existing <code>Position</code> objects are updated to reflect the appropriate
+ * character offset in the document.
+ */
+public interface Position {
+
+	/**
+	 * @return the character offset corresponding to the position.
+	 */
+	int getOffset();
+	
+	/**
+	 * @param offset the character offset corresponding to the position
+	 */
+	void setOffset(int offset);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/RootElement.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/RootElement.java
new file mode 100644
index 0000000..458dc32
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/RootElement.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * The root element of a document. Keeps track of the document to which it is
+ * associated. Any element can find the document to which it is associated by
+ * following its parents to this root. This would be done, for example, to
+ * notify document listeners that the document has changed when the element
+ * changes.
+ */
+public class RootElement extends Element {
+
+	private Document document;
+
+	public RootElement(final String localName) {
+		super(localName);
+	}
+	
+	public RootElement(final QualifiedName qualifiedName) {
+		super(qualifiedName);
+	}
+	
+	public Document getDocument() {
+		return document;
+	}
+
+	public void setDocument(Document document) {
+		this.document = document;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Text.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Text.java
new file mode 100644
index 0000000..187f845
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Text.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+/**
+ * <code>Text</code> represents a run of text in a document. Text objects are
+ * not used in the internal document structure; they are only returned as needed
+ * by the <code>Element.getContent</code> method.
+ */
+public class Text extends Node {
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param content
+	 *            Content object containing the text
+	 * @param startOffset
+	 *            character offset of the start of the run
+	 * @param endOffset
+	 *            character offset of the end of the run
+	 */
+	public Text(Content content, int startOffset, int endOffset) {
+		setContent(content, startOffset, endOffset);
+	}
+	
+	@Override
+	public String getNodeType() {
+		return "Text";
+	}	
+	
+	@Override
+	public String getBaseURI() {
+		return null;
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/TextWrapper.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/TextWrapper.java
new file mode 100644
index 0000000..4dc19e4
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/TextWrapper.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     David Carver unit tests fixes
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Wraps text to a given width.
+ */
+public class TextWrapper {
+
+	private List<String> parts = new ArrayList<String>();
+
+	private boolean lastIsWhite = true;
+
+	/**
+	 * Class constructor.
+	 */
+	public TextWrapper() {
+	}
+
+	/**
+	 * Adds text to the list of things to be wrapped.
+	 * 
+	 * @param s
+	 *            Text to be added.
+	 */
+	public void add(String s) {
+		int i = 0;
+		int j = 0;
+		boolean thisIsWhite = true;
+		while (j < s.length()) {
+
+			// skip non-whitespace
+			while (j < s.length() && !Character.isWhitespace(s.charAt(j))) {
+				j++;
+				thisIsWhite = false;
+			}
+
+			// skip whitespace
+			while (j < s.length() && Character.isWhitespace(s.charAt(j))) {
+				j++;
+				thisIsWhite = true;
+			}
+
+			if (lastIsWhite || parts.isEmpty()) {
+				this.parts.add(s.substring(i, j));
+			}
+			else if (!parts.isEmpty()) { 
+				
+				this.parts.add(((String) this.parts
+						.remove(this.parts.size() - 1))
+						+ s.substring(i, j));
+			} 
+			i = j;
+			lastIsWhite = thisIsWhite;
+		}
+	}
+
+	/**
+	 * Adds text to the list of things to be wrapped. The given text will be
+	 * treated as a single unit and will not be split across lines.
+	 * 
+	 * @param s
+	 *            Text to be added.
+	 */
+	public void addNoSplit(String s) {
+		this.parts.add(s);
+	}
+
+	/**
+	 * Clears any added text.
+	 */
+	public void clear() {
+		this.parts.clear();
+	}
+
+	/**
+	 * Wraps the text into the given width. The text is only broken at spaces,
+	 * meaning the returned lines will not necessarily fit within width.
+	 * 
+	 * @param width
+	 */
+	public String[] wrap(int width) {
+		List<String> lines = new ArrayList<String>();
+		StringBuffer line = new StringBuffer();
+
+		for (String s : this.parts) {
+			if (line.length() > 0 && line.length() + s.length() > width) {
+				// part won't fit on the current line
+				lines.add(line.toString());
+				line.setLength(0);
+
+				if (s.length() > width) {
+					lines.add(s);
+				} else {
+					line.append(s);
+				}
+			} else {
+				line.append(s);
+			}
+		}
+
+		if (line.length() > 0) {
+			lines.add(line.toString());
+		}
+
+		return (String[]) lines.toArray(new String[lines.size()]);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Validator.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Validator.java
new file mode 100644
index 0000000..d52ca1d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Validator.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.dom;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.wst.xml.vex.core.internal.validator.AttributeDefinition;
+
+/**
+ * Represents an object that can validate the structure of a document.
+ * Validators must be serializable.
+ * @model
+ */
+public interface Validator {
+
+	/**
+	 * QualifiedName indicating that character data is allowed at the given point in
+	 * the document.
+	 * @model
+	 */
+	public static final QualifiedName PCDATA = new QualifiedName(null, "#PCDATA");
+
+	/**
+	 * Returns the AttributeDefinition for a particular attribute.
+	 * 
+	 * @param element
+	 *            Name of the element.
+	 * @param attribute
+	 *            Name of the attribute.
+	 * @model
+	 */
+	public AttributeDefinition getAttributeDefinition(Attribute attribute);
+
+	/**
+	 * Returns the attribute definitions that apply to the given element.
+	 * 
+	 * @param element
+	 *            the element to check.
+	 * @model
+	 */
+	public List<AttributeDefinition> getAttributeDefinitions(Element element);
+
+	/**
+	 * Returns a set of QualifiedNames representing valid root elements for the given
+	 * document type.
+	 * @model 
+	 */
+	public Set<QualifiedName> getValidRootElements();
+
+	/**
+	 * Returns a set of QualifiedNames representing items that are valid at point in
+	 * the child nodes of a given element. Each string is either an element name
+	 * or Validator.PCDATA.
+	 * 
+	 * @param element
+	 *            the parent element.
+	 * @model 
+	 */
+	public Set<QualifiedName> getValidItems(final Element element);
+
+	/**
+	 * Returns true if the given sequence is valid for the given element.
+	 * Accepts three sequences, which will be concatenated before doing the
+	 * check.
+	 * 
+	 * @param element
+	 *            Name of the element being tested.
+	 * @param nodes
+	 *            Array of element names and Validator.PCDATA.
+	 * @param partial
+	 *            If true, an valid but incomplete sequence is acceptable.
+	 * @model
+	 */
+	public boolean isValidSequence(QualifiedName element, List<QualifiedName> nodes,
+			boolean partial);
+
+	/**
+	 * Returns true if the given sequence is valid for the given element.
+	 * Accepts three sequences, which will be concatenated before doing the
+	 * check.
+	 * 
+	 * @param element
+	 *            Name of the element being tested.
+	 * @param seq1
+	 *            List of element names and Validator.PCDATA.
+	 * @param seq2
+	 *            List of element names and Validator.PCDATA. May be null or
+	 *            empty.
+	 * @param seq3
+	 *            List of element names and Validator.PCDATA. May be null or
+	 *            empty.
+	 * @param partial
+	 *            If true, an valid but incomplete sequence is acceptable.
+	 * @model
+	 */
+	public boolean isValidSequence(QualifiedName element, List<QualifiedName> seq1,
+			List<QualifiedName> seq2, List<QualifiedName> seq3, boolean partial);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/package.html b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/package.html
new file mode 100644
index 0000000..2eca216
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/package.html
@@ -0,0 +1,22 @@
+<?xml version='1.0'?>
+<html>
+
+  <head>
+
+    <title>net.sf.vex.dom</title>
+
+  </head>
+
+  <body>
+
+    <p>Classes implementing an object model for XML documents. The 
+    classes in this package are designed to be similar to those in the 
+    JDOM library. However, this package is unique in that content is 
+    represented by one single string of characters for the entire 
+    document. Implementing the model in this way simplifies the editor 
+    implementation.</p>
+
+  </body>
+
+</html>
+
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java
new file mode 100644
index 0000000..57b1645
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java
@@ -0,0 +1,997 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.StyleSheet;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Document;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.eclipse.wst.xml.vex.core.internal.dom.Position;
+
+/**
+ * Base class of block boxes that can contain other block boxes. This class
+ * implements the layout method and various navigation methods. Subclasses must
+ * implement the createChildren method.
+ * 
+ * Subclasses can be anonymous or non-anonymous (i.e. generated by an element).
+ * Since the vast majority of instances will be non-anonymous, this class can
+ * manage the element and top and bottom margins without too much inefficiency.
+ * 
+ * <p>
+ * Subclasses that can be anonymous must override the getStartPosition and
+ * getEndPosition classes to return the range covered by the box.
+ * </p>
+ */
+public abstract class AbstractBlockBox extends AbstractBox implements BlockBox {
+
+	/**
+	 * Class constructor for non-anonymous boxes.
+	 * 
+	 * @param context
+	 *            LayoutContext being used.
+	 * @param parent
+	 *            Parent box.
+	 * @param element
+	 *            Element associated with this box. anonymous box.
+	 */
+	public AbstractBlockBox(LayoutContext context, BlockBox parent,
+			Element element) {
+
+		this.parent = parent;
+		this.element = element;
+
+		Styles styles = context.getStyleSheet().getStyles(element);
+		int parentWidth = parent.getWidth();
+		this.marginTop = styles.getMarginTop().get(parentWidth);
+		this.marginBottom = styles.getMarginBottom().get(parentWidth);
+
+	}
+
+	/**
+	 * Class constructor for anonymous boxes.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param parent
+	 *            Parent box.
+	 * @param startOffset
+	 *            Start of the range covered by the box.
+	 * @param endOffset
+	 *            End of the range covered by the box.
+	 */
+	public AbstractBlockBox(LayoutContext context, BlockBox parent,
+			int startOffset, int endOffset) {
+		this.parent = parent;
+		this.marginTop = 0;
+		this.marginBottom = 0;
+
+		Document doc = context.getDocument();
+		this.startPosition = doc.createPosition(startOffset);
+		this.endPosition = doc.createPosition(endOffset);
+	}
+
+	/**
+	 * Walks the box tree and returns the nearest enclosing element.
+	 */
+	protected Element findContainingElement() {
+		BlockBox box = this;
+		Element element = box.getElement();
+		while (element == null) {
+			box = box.getParent();
+			element = box.getElement();
+		}
+		return element;
+	}
+
+	/**
+	 * Returns this box's children as an array of BlockBoxes.
+	 */
+	protected BlockBox[] getBlockChildren() {
+		return (BlockBox[]) this.getChildren();
+	}
+
+	public Caret getCaret(LayoutContext context, int offset) {
+
+		// If we haven't yet laid out this block, estimate the caret.
+		if (this.getLayoutState() != LAYOUT_OK) {
+			int relative = offset - this.getStartOffset();
+			int size = this.getEndOffset() - this.getStartOffset();
+			int y = 0;
+			if (size > 0) {
+				y = this.getHeight() * relative / size;
+			}
+			return new HCaret(0, y, this.getHCaretWidth());
+		}
+
+		int y;
+
+		Box[] children = this.getContentChildren();
+		for (int i = 0; i < children.length; i++) {
+
+			if (offset < children[i].getStartOffset()) {
+				if (i > 0) {
+					y = (children[i - 1].getY() + children[i - 1].getHeight() + children[i]
+							.getY()) / 2;
+				} else {
+					y = 0;
+				}
+				return new HCaret(0, y, this.getHCaretWidth());
+			}
+
+			if (offset >= children[i].getStartOffset()
+					&& offset <= children[i].getEndOffset()) {
+
+				Caret caret = children[i].getCaret(context, offset);
+				caret.translate(children[i].getX(), children[i].getY());
+				return caret;
+			}
+		}
+
+		if (this.hasChildren()) {
+			y = this.getHeight();
+		} else {
+			y = this.getHeight() / 2;
+		}
+
+		return new HCaret(0, y, this.getHCaretWidth());
+	}
+
+	public Box[] getChildren() {
+		return this.children;
+	}
+
+	/**
+	 * Return an array of children that contain content.
+	 */
+	protected BlockBox[] getContentChildren() {
+		Box[] children = this.getChildren();
+		List<BlockBox> result = new ArrayList<BlockBox>(children.length);
+		for (Box child : children) {
+			if (child.hasContent()) {
+				result.add((BlockBox) child);
+			}
+		}
+		return result.toArray(new BlockBox[result.size()]);
+	}
+
+	public Element getElement() {
+		return element;
+	}
+
+	public int getEndOffset() {
+		Element element = this.getElement();
+		if (element != null) {
+			return element.getEndOffset();
+		} else if (this.getEndPosition() != null) {
+			return this.getEndPosition().getOffset();
+		} else {
+			throw new IllegalStateException();
+		}
+	}
+
+	/**
+	 * Returns the estimated size of the box, based on the the current font size
+	 * and the number of characters covered by the box. This is a utility method
+	 * that can be used in implementation of setInitialSize. It assumes the
+	 * width of the box has already been correctly set.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 */
+	protected int getEstimatedHeight(LayoutContext context) {
+
+		Element element = this.findContainingElement();
+		Styles styles = context.getStyleSheet().getStyles(element);
+		int charCount = this.getEndOffset() - this.getStartOffset();
+
+		float fontSize = styles.getFontSize();
+		float lineHeight = styles.getLineHeight();
+		float estHeight = lineHeight * fontSize * 0.6f * charCount
+				/ this.getWidth();
+
+		return Math.round(Math.max(estHeight, lineHeight));
+	}
+
+	public LineBox getFirstLine() {
+		if (this.hasChildren()) {
+			BlockBox firstChild = (BlockBox) this.getChildren()[0];
+			return firstChild.getFirstLine();
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the width of the horizontal caret. This is overridden by TableBox
+	 * to return a caret that is the full width of the table.
+	 */
+	protected int getHCaretWidth() {
+		return H_CARET_LENGTH;
+	}
+
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+
+		if (this.getElement() != null) {
+			Styles styles = context.getStyleSheet()
+					.getStyles(this.getElement());
+
+			int top = this.marginTop + styles.getBorderTopWidth()
+					+ styles.getPaddingTop().get(containerWidth);
+
+			int left = styles.getMarginLeft().get(containerWidth)
+					+ styles.getBorderLeftWidth()
+					+ styles.getPaddingLeft().get(containerWidth);
+
+			int bottom = this.marginBottom + styles.getBorderBottomWidth()
+					+ styles.getPaddingBottom().get(containerWidth);
+
+			int right = styles.getMarginRight().get(containerWidth)
+					+ styles.getBorderRightWidth()
+					+ styles.getPaddingRight().get(containerWidth);
+
+			return new Insets(top, left, bottom, right);
+		} else {
+			return new Insets(this.marginTop, 0, this.marginBottom, 0);
+		}
+	}
+
+	public LineBox getLastLine() {
+		if (this.hasChildren()) {
+			BlockBox lastChild = (BlockBox) this.getChildren()[this
+					.getChildren().length - 1];
+			return lastChild.getLastLine();
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the layout state of this box.
+	 */
+	protected byte getLayoutState() {
+		return this.layoutState;
+	}
+
+	public int getLineEndOffset(int offset) {
+		BlockBox[] children = this.getContentChildren();
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].containsOffset(offset)) {
+				return children[i].getLineEndOffset(offset);
+			}
+		}
+		return offset;
+	}
+
+	public int getLineStartOffset(int offset) {
+		BlockBox[] children = this.getContentChildren();
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].containsOffset(offset)) {
+				return children[i].getLineStartOffset(offset);
+			}
+		}
+		return offset;
+	}
+
+	public int getMarginBottom() {
+		return this.marginBottom;
+	}
+
+	public int getMarginTop() {
+		return this.marginTop;
+	}
+
+	public int getNextLineOffset(LayoutContext context, int offset, int x) {
+
+		//
+		// This algorithm works when this block owns the offsets between
+		// its children.
+		//
+
+		if (offset == this.getEndOffset()) {
+			return -1;
+		}
+
+		BlockBox[] children = this.getContentChildren();
+
+		if (offset < this.getStartOffset() && children.length > 0
+				&& children[0].getStartOffset() > this.getStartOffset()) {
+			//
+			// If there's an offset before the first child, put the caret there.
+			//
+			return this.getStartOffset();
+		}
+
+		for (int i = 0; i < children.length; i++) {
+			BlockBox child = children[i];
+			if (offset <= child.getEndOffset()) {
+				int newOffset = child.getNextLineOffset(context, offset, x
+						- child.getX());
+				if (newOffset < 0 /* && i < children.length-1 */) {
+					return child.getEndOffset() + 1;
+				} else {
+					return newOffset;
+				}
+			}
+		}
+
+		return this.getEndOffset();
+	}
+
+	public BlockBox getParent() {
+		return this.parent;
+	}
+
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x) {
+
+		if (offset == this.getStartOffset()) {
+			return -1;
+		}
+
+		BlockBox[] children = this.getContentChildren();
+
+		if (offset > this.getEndOffset()
+				&& children.length > 0
+				&& children[children.length - 1].getEndOffset() < this
+						.getEndOffset()) {
+			//
+			// If there's an offset after the last child, put the caret there.
+			//
+			return this.getEndOffset();
+		}
+
+		for (int i = children.length; i > 0; i--) {
+			BlockBox child = children[i - 1];
+			if (offset >= child.getStartOffset()) {
+				int newOffset = child.getPreviousLineOffset(context, offset, x
+						- child.getX());
+				if (newOffset < 0 && i > 0) {
+					return child.getStartOffset() - 1;
+				} else {
+					return newOffset;
+				}
+			}
+		}
+
+		return this.getStartOffset();
+	}
+
+	public int getStartOffset() {
+		Element element = this.getElement();
+		if (element != null) {
+			return element.getStartOffset() + 1;
+		} else if (this.getStartPosition() != null) {
+			return this.getStartPosition().getOffset();
+		} else {
+			throw new IllegalStateException();
+		}
+	}
+
+	public boolean hasContent() {
+		return true;
+	}
+
+	public void invalidate(boolean direct) {
+
+		if (direct) {
+			this.layoutState = LAYOUT_REDO;
+		} else {
+			this.layoutState = LAYOUT_PROPAGATE;
+		}
+
+		if (this.getParent() instanceof AbstractBlockBox) {
+			((AbstractBlockBox) this.getParent()).invalidate(false);
+		}
+	}
+
+	public boolean isAnonymous() {
+		return this.getElement() == null;
+	}
+
+	/**
+	 * Call the given callback for each child matching one of the given display
+	 * styles. Any nodes that do not match one of the given display types cause
+	 * the onRange callback to be called, with a range covering all such
+	 * contiguous nodes.
+	 * 
+	 * @param styleSheet
+	 *            StyleSheet from which to determine display styles.
+	 * @param displayStyles
+	 *            Display types to be explicitly recognized.
+	 * @param callback
+	 *            DisplayStyleCallback through which the caller is notified of
+	 *            matching elements and non-matching ranges.
+	 */
+	protected void iterateChildrenByDisplayStyle(StyleSheet styleSheet,
+			Set<String> displayStyles, ElementOrRangeCallback callback) {
+		LayoutUtils.iterateChildrenByDisplayStyle(styleSheet, displayStyles,
+				this.findContainingElement(), this.getStartOffset(), this
+						.getEndOffset(), callback);
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+
+		if (this.skipPaint(context, x, y)) {
+			return;
+		}
+
+		boolean drawBorders = !context.isElementSelected(this.getElement());
+
+		this.drawBox(context, x, y, this.getParent().getWidth(), drawBorders);
+
+		this.paintChildren(context, x, y);
+
+		this.paintSelectionFrame(context, x, y, true);
+	}
+
+	/**
+	 * Default implementation. Width is calculated as the parent's width minus
+	 * this box's insets. Height is calculated by getEstimatedHeight.
+	 */
+	public void setInitialSize(LayoutContext context) {
+		int parentWidth = this.getParent().getWidth();
+		Insets insets = this.getInsets(context, parentWidth);
+		this.setWidth(Math.max(0, parentWidth - insets.getLeft()
+				- insets.getRight()));
+		this.setHeight(this.getEstimatedHeight(context));
+	}
+
+	public int viewToModel(LayoutContext context, int x, int y) {
+		Box[] children = getChildren();
+
+		if (children == null) {
+			int charCount = getEndOffset() - getStartOffset() - 1;
+			if (charCount == 0 || getHeight() == 0) return getEndOffset();
+
+			return getStartOffset() + charCount * y / getHeight();
+		}
+
+		for (Box child : children) {
+			if (!child.hasContent()) continue;
+
+			if (y < child.getY()) return child.getStartOffset() - 1;
+
+			if (y < child.getY() + child.getHeight()) {
+				return child.viewToModel(context,
+						                 x - child.getX(),
+						                 y - child.getY());
+			}
+
+		}
+
+		return getEndOffset();
+	}
+
+	// ===================================================== PRIVATE
+
+	private BlockBox parent;
+	private Box[] children;
+
+	/**
+	 * Paint a frame that indicates a block element box has been selected.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param x
+	 *            x-coordinate at which to draw
+	 * @param y
+	 *            y-coordinate at which to draw.
+	 * @param selected
+	 */
+	protected void paintSelectionFrame(LayoutContext context, int x, int y,
+			boolean selected) {
+
+		Element element = this.getElement();
+		Element parent = element == null ? null : element.getParent();
+
+		boolean paintFrame = context.isElementSelected(element)
+				&& !context.isElementSelected(parent);
+
+		if (!paintFrame) {
+			return;
+		}
+
+		Graphics g = context.getGraphics();
+		ColorResource foreground;
+		ColorResource background;
+
+		if (selected) {
+			foreground = g.getSystemColor(ColorResource.SELECTION_FOREGROUND);
+			background = g.getSystemColor(ColorResource.SELECTION_BACKGROUND);
+		} else {
+			foreground = g.createColor(new Color(0, 0, 0));
+			background = g.createColor(new Color(0xcc, 0xcc, 0xcc));
+		}
+
+		FontMetrics fm = g.getFontMetrics();
+		ColorResource oldColor = g.setColor(background);
+		g.setLineStyle(Graphics.LINE_SOLID);
+		g.setLineWidth(1);
+		int tabWidth = g.stringWidth(this.getElement().getPrefixedName())
+				+ fm.getLeading();
+		int tabHeight = fm.getHeight();
+		int tabX = x + this.getWidth() - tabWidth;
+		int tabY = y + this.getHeight() - tabHeight;
+		g.drawRect(x, y, this.getWidth(), this.getHeight());
+		g.fillRect(tabX, tabY, tabWidth, tabHeight);
+		g.setColor(foreground);
+		g.drawString(this.getElement().getPrefixedName(), tabX + fm.getLeading() / 2,
+				tabY);
+
+		g.setColor(oldColor);
+		if (!selected) {
+			foreground.dispose();
+			background.dispose();
+		}
+	}
+
+	/** Layout is OK */
+	public static final byte LAYOUT_OK = 0;
+
+	/** My layout is OK, but one of my children needs to be laid out */
+	public static final byte LAYOUT_PROPAGATE = 1;
+
+	/** I need to be laid out */
+	public static final byte LAYOUT_REDO = 2;
+
+	private byte layoutState = LAYOUT_REDO;
+
+	public IntRange layout(LayoutContext context, int top, int bottom) {
+
+		int repaintStart = Integer.MAX_VALUE;
+		int repaintEnd = 0;
+		boolean repaintToBottom = false;
+		int originalHeight = this.getHeight();
+
+		if (this.layoutState == LAYOUT_REDO) {
+
+			// System.out.println("Redo layout of " +
+			// this.getElement().getName());
+
+			List<Box> childList = this.createChildren(context);
+			this.children = (BlockBox[]) childList
+					.toArray(new BlockBox[childList.size()]);
+
+			// Even though we position children after layout, we have to
+			// do a preliminary positioning here so we now which ones
+			// overlap our layout band
+			for (int i = 0; i < this.children.length; i++) {
+				BlockBox child = (BlockBox) this.children[i];
+				child.setInitialSize(context);
+			}
+			this.positionChildren(context);
+
+			// repaint everything
+			repaintToBottom = true;
+			repaintStart = 0;
+		}
+
+		Box[] children = this.getChildren();
+		for (int i = 0; i < children.length; i++) {
+			if (children[i] instanceof BlockBox) {
+				BlockBox child = (BlockBox) children[i];
+				if (top <= child.getY() + child.getHeight()
+						&& bottom >= child.getY()) {
+
+					IntRange repaintRange = child.layout(context, top
+							- child.getY(), bottom - child.getY());
+					if (repaintRange != null) {
+						repaintStart = Math.min(repaintStart, repaintRange
+								.getStart()
+								+ child.getY());
+						repaintEnd = Math.max(repaintEnd, repaintRange.getEnd()
+								+ child.getY());
+					}
+				}
+			}
+		}
+
+		int childRepaintStart = this.positionChildren(context);
+		if (childRepaintStart != -1) {
+			repaintToBottom = true;
+			repaintStart = Math.min(repaintStart, childRepaintStart);
+		}
+
+		this.layoutState = LAYOUT_OK;
+
+		if (repaintToBottom) {
+			repaintEnd = Math.max(originalHeight, this.getHeight());
+		}
+
+		if (repaintStart < repaintEnd) {
+			return new IntRange(repaintStart, repaintEnd);
+		} else {
+			return null;
+		}
+	}
+
+	protected abstract List<Box> createChildren(LayoutContext context);
+
+	/**
+	 * Creates a list of block boxes for a given document range. beforeInlines
+	 * and afterInlines are prepended/appended to the first/last block child,
+	 * and each may be null.
+	 */
+	protected List<Box> createBlockBoxes(LayoutContext context, int startOffset,
+			int endOffset, int width, List<InlineBox> beforeInlines, List<InlineBox> afterInlines) {
+		final List<Box> blockBoxes = new ArrayList<Box>();
+		final List<InlineBox> pendingInlines = new ArrayList<InlineBox>();
+
+		if (beforeInlines != null)
+			pendingInlines.addAll(beforeInlines);
+		
+		Document document = context.getDocument();
+		Element element = document.findCommonElement(startOffset,
+				endOffset);
+
+		if (startOffset == endOffset) {
+			int relOffset = startOffset - element.getStartOffset();
+			pendingInlines.add(new PlaceholderBox(context, element, relOffset));
+		} else {
+			BlockInlineIterator iter = new BlockInlineIterator(context,
+					element, startOffset, endOffset);
+			while (true) {
+				Object next = iter.next();
+				if (next == null)
+					break;
+
+				if (next instanceof IntRange) {
+					IntRange range = (IntRange) next;
+					InlineElementBox.InlineBoxes inlineBoxes = InlineElementBox
+							.createInlineBoxes(context, element, range
+									.getStart(), range.getEnd());
+					pendingInlines.addAll(inlineBoxes.boxes);
+					pendingInlines.add(new PlaceholderBox(context, element,
+							range.getEnd() - element.getStartOffset()));
+				} else {
+					if (!pendingInlines.isEmpty()) {
+						blockBoxes.add(ParagraphBox.create(context, element,
+								pendingInlines, width));
+						pendingInlines.clear();
+					}
+
+					if (isTableChild(context, next)) {
+						// Consume continguous table children and create an
+						// anonymous table.
+						int tableStartOffset = ((Element) next)
+								.getStartOffset();
+						int tableEndOffset = -1; // dummy to hide warning
+						while (isTableChild(context, next)) {
+							tableEndOffset = ((Element) next).getEndOffset() + 1;
+							next = iter.next();
+						}
+
+						// add anonymous table
+						blockBoxes.add(new TableBox(context, this,
+								tableStartOffset, tableEndOffset));
+						if (next == null) {
+							break;
+						} else {
+							iter.push(next);
+						}
+					} else { // next is a block box element
+						Element blockElement = (Element) next;
+						blockBoxes.add(context.getBoxFactory().createBox(
+								context, blockElement, this, width));
+					}
+				}
+			}
+		}
+
+		if (afterInlines != null)
+			pendingInlines.addAll(afterInlines);
+ 
+		if (!pendingInlines.isEmpty()) {
+			blockBoxes.add(ParagraphBox.create(context, element,
+					pendingInlines, width));
+			pendingInlines.clear();
+		}
+
+		return blockBoxes;
+	}
+
+	private static class BlockInlineIterator {
+
+		public BlockInlineIterator(LayoutContext context, Element element,
+				int startOffset, int endOffset) {
+			this.context = context;
+			this.element = element;
+			this.startOffset = startOffset;
+			this.endOffset = endOffset;
+		}
+
+		/**
+		 * Returns the next block element or inline range, or null if we're at
+		 * the end.
+		 */
+		public Object next() {
+			if (!this.pushStack.isEmpty()) {
+				return this.pushStack.removeLast();
+			} else if (startOffset == endOffset) {
+				return null;
+			} else {
+				Element blockElement = findNextBlockElement(this.context,
+						this.element, startOffset, endOffset);
+				if (blockElement == null) {
+					if (startOffset < endOffset) {
+						IntRange result = new IntRange(startOffset, endOffset);
+						startOffset = endOffset;
+						return result;
+					} else {
+						return null;
+					}
+				} else if (blockElement.getStartOffset() > startOffset) {
+					this.pushStack.addLast(blockElement);
+					IntRange result = new IntRange(startOffset, blockElement
+							.getStartOffset());
+					startOffset = blockElement.getEndOffset() + 1;
+					return result;
+				} else {
+					startOffset = blockElement.getEndOffset() + 1;
+					return blockElement;
+				}
+			}
+		}
+
+		public void push(Object pushed) {
+			this.pushStack.addLast(pushed);
+		}
+
+		private LayoutContext context;
+		private Element element;
+		private int startOffset;
+		private int endOffset;
+		private LinkedList<Object> pushStack = new LinkedList<Object>();
+	}
+
+	protected boolean hasChildren() {
+		return this.getChildren() != null && this.getChildren().length > 0;
+	}
+
+	/**
+	 * Positions the children of this box. Vertical margins are collapsed here.
+	 * Returns the vertical offset of the top of the first child to move, or -1
+	 * if not children were actually moved.
+	 */
+	protected int positionChildren(LayoutContext context) {
+
+		int childY = 0;
+		int prevMargin = 0;
+		BlockBox[] children = this.getBlockChildren();
+		int repaintStart = -1;
+
+		Styles styles = null;
+
+		if (!this.isAnonymous()) {
+			styles = context.getStyleSheet().getStyles(this.getElement());
+		}
+
+		if (styles != null && children.length > 0) {
+			if (styles.getBorderTopWidth()
+					+ styles.getPaddingTop().get(this.getWidth()) == 0) {
+				// first child's top margin collapses into ours
+				this.marginTop = Math.max(this.marginTop, children[0]
+						.getMarginTop());
+				childY -= children[0].getMarginTop();
+			}
+		}
+
+		for (int i = 0; i < children.length; i++) {
+
+			Insets insets = children[i].getInsets(context, this.getWidth());
+
+			childY += insets.getTop();
+
+			if (i > 0) {
+				childY -= Math.min(prevMargin, children[i].getMarginTop());
+			}
+
+			if (repaintStart == -1 && children[i].getY() != childY) {
+				repaintStart = Math.min(children[i].getY(), childY);
+			}
+
+			children[i].setX(insets.getLeft());
+			children[i].setY(childY);
+
+			childY += children[i].getHeight() + insets.getBottom();
+			prevMargin = children[i].getMarginBottom();
+		}
+
+		if (styles != null && children.length > 0) {
+			if (styles.getBorderBottomWidth()
+					+ styles.getPaddingBottom().get(this.getWidth()) == 0) {
+				// last child's bottom margin collapses into ours
+				this.marginBottom = Math.max(this.marginBottom, prevMargin);
+				childY -= prevMargin;
+			}
+		}
+
+		this.setHeight(childY);
+
+		return repaintStart;
+	}
+
+	/**
+	 * Sets the layout state of the box.
+	 * 
+	 * @param layoutState
+	 *            One of the LAYOUT_* constants
+	 */
+	protected void setLayoutState(byte layoutState) {
+		this.layoutState = layoutState;
+	}
+
+	// ========================================================= PRIVATE
+	/** The length, in pixels, of the horizontal caret between block boxes */
+	private static final int H_CARET_LENGTH = 20;
+
+	/**
+	 * Element with which we are associated. For anonymous boxes, this is null.
+	 */
+	private Element element;
+
+	/*
+	 * We cache the top and bottom margins, since they may be affected by our
+	 * children.
+	 */
+	private int marginTop;
+	private int marginBottom;
+
+	/**
+	 * Start position of an anonymous box. For non-anonymous boxes, this is
+	 * null.
+	 */
+	private Position startPosition;
+
+	/**
+	 * End position of an anonymous box. For non-anonymous boxes, this is null.
+	 */
+	private Position endPosition;
+
+	/**
+	 * Searches for the next block-formatted child.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param element
+	 *            Element within which to search.
+	 * @param startOffset
+	 *            The offset at which to start the search.
+	 * @param endOffset
+	 *            The offset at which to end the search.
+	 */
+	private static Element findNextBlockElement(LayoutContext context,
+			Element element, int startOffset, int endOffset) {
+
+		List<Element> children = element.getChildElements();
+		for (int i = 0; i < children.size(); i++) {
+			Element child = children.get(i);
+
+            // inside range?
+			if (child.getEndOffset() < startOffset) continue;
+			if (child.getStartOffset() >= endOffset) break;
+
+            // found?
+			if (!isInline(context, child, element)) return child;
+
+			// recursion
+			Element fromChild =
+				findNextBlockElement(context, child, startOffset, endOffset);
+			if (fromChild != null) {
+				return fromChild;
+			}
+		}
+
+		return null;
+	}
+	
+	private static boolean isInline(LayoutContext context,
+			                        Element child,
+			                        Element parent) {
+
+		String style = displayStyleOf(child, context);
+		String parentStyle = displayStyleOf(parent, context);
+		if (style.equals(CSS.INLINE)) {
+			return true;
+		}
+		
+		// invalid nested table elements have to be shown as 'inline': 
+		
+		// parent of 'table-cell': 'table-row'
+		if (   style.equals(CSS.TABLE_CELL)
+			&& !parentStyle.equals(CSS.TABLE_ROW)) {
+			return true;
+		}
+		
+		// parent of 'table-row': 'table', 'table-row-group', 
+		// 'table-header-group' or 'table-footer-group'
+		if (   style.equals(CSS.TABLE_ROW)
+			&& !parentStyle.equals(CSS.TABLE)
+			&& !parentStyle.equals(CSS.TABLE_ROW_GROUP)
+			&& !parentStyle.equals(CSS.TABLE_HEADER_GROUP)
+			&& !parentStyle.equals(CSS.TABLE_FOOTER_GROUP)) {
+			return true;
+		}
+		
+		// parent of 'table-row-group', table-header-group'
+		// or 'table-footer-group': 'table'
+		if (   (   style.equals(CSS.TABLE_ROW_GROUP)
+				|| style.equals(CSS.TABLE_HEADER_GROUP)
+				|| style.equals(CSS.TABLE_FOOTER_GROUP))
+			&& !parentStyle.equals(CSS.TABLE)) {
+			return true;
+		}
+		
+		// parent of 'table-column': 'table-column-group'
+		if (   style.equals(CSS.TABLE_COLUMN)
+			&& !parentStyle.equals(CSS.TABLE_COLUMN_GROUP)) {
+			return true;
+		}
+		
+		// parent of 'table-column-group': 'table'
+		if (   style.equals(CSS.TABLE_COLUMN_GROUP)
+			&& !parentStyle.equals(CSS.TABLE)) {
+			return true;
+		}
+		
+		// parent of 'table-caption': 'table'
+		if (   style.equals(CSS.TABLE_CAPTION)
+			&& !parentStyle.equals(CSS.TABLE)) {
+			return true;
+		}
+		
+		return false;
+	}
+	
+	private static String displayStyleOf(Element element,
+			                             LayoutContext context) {
+		return context.getStyleSheet().getStyles(element).getDisplay();
+	}
+
+	/**
+	 * Return the end position of an anonymous box. The default implementation
+	 * returns null.
+	 */
+	private Position getEndPosition() {
+		return this.endPosition;
+	}
+
+	/**
+	 * Return the start position of an anonymous box. The default implementation
+	 * returns null.
+	 */
+	private Position getStartPosition() {
+		return this.startPosition;
+	}
+
+	private boolean isTableChild(LayoutContext context, Object rangeOrElement) {
+		if (rangeOrElement != null && rangeOrElement instanceof Element) {
+			return LayoutUtils.isTableChild(context.getStyleSheet(),
+					(Element) rangeOrElement);
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBox.java
new file mode 100644
index 0000000..9f057c7
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBox.java
@@ -0,0 +1,458 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Base implementation of the <code>Box</code> interface, implementing some
+ * common methods.
+ */
+public abstract class AbstractBox implements Box {
+
+	private static final Box[] EMPTY_BOX_ARRAY = new Box[0];
+
+	private int x;
+	private int y;
+	private int width = -1;
+	private int height = -1;
+
+	/**
+	 * Class constructor.
+	 */
+	public AbstractBox() {
+	}
+
+	/**
+	 * Returns true if the given offset is between startOffset and endOffset,
+	 * inclusive.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#containsOffset(int)
+	 */
+	public boolean containsOffset(int offset) {
+		return offset >= this.getStartOffset() && offset <= this.getEndOffset();
+	}
+
+	/**
+	 * Throws <code>IllegalStateException</code>. Boxes with content must
+	 * provide an implementation of this method.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * Returns an empty array of children.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getChildren()
+	 */
+	public Box[] getChildren() {
+		return EMPTY_BOX_ARRAY;
+	}
+
+	/**
+	 * Returns null. Boxes associated with elements must provide an
+	 * implementation of this method.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getElement()
+	 */
+	public Element getElement() {
+		return null;
+	}
+
+	/**
+	 * Throws <code>IllegalStateException</code>. Boxes with content must
+	 * provide an implementation of this method.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * Returns the height set with <code>setHeight</code>.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getHeight()
+	 */
+	public int getHeight() {
+		return this.height;
+	}
+
+	/**
+	 * Throws <code>IllegalStateException</code>. Boxes with content must
+	 * provide an implementation of this method.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * Returns the insets of this box, which is the sum of the margin, border,
+	 * and padding on each side. If no element is associated with this box
+	 * returns all zeros.
+	 */
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+		Element element = this.getElement();
+		if (element == null) {
+			return Insets.ZERO_INSETS;
+		} else {
+			return getInsets(context.getStyleSheet().getStyles(element),
+					containerWidth);
+		}
+	}
+
+	/**
+	 * Returns false. Boxes with content must override this method and return
+	 * true, and must provide implementations for the following methods.
+	 * 
+	 * <ul>
+	 * <li>{@link Box#getCaretShapes}</li>
+	 * <li>{@link Box#getStartOffset}</li>
+	 * <li>{@link Box#getEndOffset}</li>
+	 * <li>{@link Box#viewToModel}</li>
+	 * </ul>
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#hasContent()
+	 */
+	public boolean hasContent() {
+		return false;
+	}
+
+	public boolean isAnonymous() {
+		return true;
+	}
+
+	/**
+	 * Returns the width set with <code>setWidth</code>.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getWidth()
+	 */
+	public int getWidth() {
+		return this.width;
+	}
+
+	/**
+	 * Returns the value set with <code>setX</code>.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getX()
+	 */
+	public int getX() {
+		return this.x;
+	}
+
+	/**
+	 * Returns the value set with <code>setY</code>.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getY()
+	 */
+	public int getY() {
+		return this.y;
+	}
+
+	/**
+	 * Paint all children of this box.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#paint(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+		if (this.skipPaint(context, x, y))
+			return;
+
+		this.paintChildren(context, x, y);
+	}
+
+	/**
+	 * Paint the children of this box.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param x
+	 *            x-coordinate at which to paint
+	 * @param y
+	 *            y-coordinate at which to paint
+	 */
+	protected void paintChildren(LayoutContext context, int x, int y) {
+		Box[] children = this.getChildren();
+		for (int i = 0; children != null && i < children.length; i++) {
+			Box child = children[i];
+			child.paint(context, x + child.getX(), y + child.getY());
+		}
+	}
+
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	/**
+	 * Returns true if this box is outside the clip region. Implementations of
+	 * <code>paint</code> should use this to avoid unnecessary painting.
+	 * 
+	 * @param context
+	 *            <code>LayoutContext</code> in effect.
+	 * @param x
+	 *            the x-coordinate at which the box is being painted
+	 * @param y
+	 *            the y-coordinate at which the box is being painted
+	 */
+	protected boolean skipPaint(LayoutContext context, int x, int y) {
+		Rectangle clipBounds = context.getGraphics().getClipBounds();
+
+		return clipBounds.getY() + clipBounds.getHeight() <= y
+				|| clipBounds.getY() >= y + this.getHeight();
+
+	}
+
+	/**
+	 * Throws <code>IllegalStateException</code>. Boxes with content must
+	 * provide an implementation of this method.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#viewToModel(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int viewToModel(LayoutContext context, int x, int y) {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * Draws the background and borders of a CSS-styled box.
+	 * 
+	 * @param context
+	 *            LayoutContext used for drawing.
+	 * @param x
+	 *            x-coordinate of the left side of the box
+	 * @param y
+	 *            y-coordinate of the top of the box
+	 * @param containerWidth
+	 *            width of the containing client area. Used for calculating
+	 *            padding expressed as a percentage.
+	 * @param drawBorders
+	 *            If true, the background is filled and the borders are drawn;
+	 *            otherwise, just the background is filled. This is handy when
+	 *            removing the borders when drawing the selection frame.
+	 */
+	protected void drawBox(LayoutContext context, int x, int y,
+			int containerWidth, boolean drawBorders) {
+		this.drawBox(context, this.getElement(), x, y, containerWidth,
+				drawBorders);
+	}
+
+	/**
+	 * Draws the background and borders of a CSS-styled box.
+	 * 
+	 * @param context
+	 *            LayoutContext used for drawing.
+	 * @param element
+	 *            Element to use when determining styles. This is used by
+	 *            TableBodyBox to specify the corresponding table element.
+	 * @param x
+	 *            x-coordinate of the left side of the box
+	 * @param y
+	 *            y-coordinate of the top of the box
+	 * @param containerWidth
+	 *            width of the containing client area. Used for calculating
+	 *            padding expressed as a percentage.
+	 * @param drawBorders
+	 *            If true, the background is filled and the borders are drawn;
+	 *            otherwise, just the background is filled. This is handy when
+	 *            removing the borders when drawing the selection frame.
+	 */
+	protected void drawBox(LayoutContext context, Element element, int x,
+			int y, int containerWidth, boolean drawBorders) {
+
+		if (element == null) {
+			return;
+		}
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(element);
+
+		boolean hasLeft = true;
+		boolean hasRight = true;
+		int left = x - styles.getPaddingLeft().get(containerWidth)
+				- styles.getBorderLeftWidth();
+		int top = y - styles.getPaddingTop().get(containerWidth)
+				- styles.getBorderTopWidth();
+		int right = x + this.getWidth()
+				+ styles.getPaddingRight().get(containerWidth)
+				+ styles.getBorderRightWidth();
+		int bottom = y + this.getHeight()
+				+ styles.getPaddingBottom().get(containerWidth)
+				+ styles.getBorderBottomWidth();
+
+		if (this instanceof InlineElementBox) {
+			// TODO fix boxes for inline elements
+			hasLeft = this.getStartOffset() == element.getStartOffset() + 1;
+			hasRight = this.getEndOffset() == element.getEndOffset();
+			if (hasLeft) {
+				// left += styles.getMarginLeft().get(0);
+			}
+			if (hasRight) {
+				// right -= styles.getMarginRight().get(0);
+			}
+			// top = y - styles.getPaddingTop().get(0) -
+			// styles.getBorderTopWidth();
+			// bottom = y + box.getHeight() + styles.getPaddingBottom().get(0) +
+			// styles.getBorderBottomWidth();
+		}
+
+		Color backgroundColor = styles.getBackgroundColor();
+
+		if (backgroundColor != null) {
+			ColorResource color = g.createColor(backgroundColor);
+			ColorResource oldColor = g.setColor(color);
+			g.fillRect(left, top, right - left, bottom - top);
+			g.setColor(oldColor);
+			color.dispose();
+		}
+
+		if (drawBorders) {
+			// Object oldAntiAlias =
+			// g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+			//
+			// g.setRenderingHint(
+			// RenderingHints.KEY_ANTIALIASING,
+			// RenderingHints.VALUE_ANTIALIAS_OFF);
+			boolean oldAntiAlias = g.isAntiAliased();
+			g.setAntiAliased(false);
+
+			int bw2 = styles.getBorderBottomWidth() / 2;
+			int lw2 = styles.getBorderLeftWidth() / 2;
+			int rw2 = styles.getBorderRightWidth() / 2;
+			int tw2 = styles.getBorderTopWidth() / 2;
+
+			// Bottom border
+			if (styles.getBorderBottomWidth() > 0) {
+				ColorResource color = g.createColor(styles
+						.getBorderBottomColor());
+				ColorResource oldColor = g.setColor(color);
+				g.setLineStyle(lineStyle(styles.getBorderBottomStyle()));
+				g.setLineWidth(styles.getBorderBottomWidth());
+				g.drawLine(left + bw2, bottom - bw2 - 1, right - bw2, bottom
+						- bw2 - 1);
+				g.setColor(oldColor);
+				color.dispose();
+			}
+
+			// Left border
+			if (hasLeft && styles.getBorderLeftWidth() > 0) {
+				ColorResource color = g
+						.createColor(styles.getBorderLeftColor());
+				ColorResource oldColor = g.setColor(color);
+				g.setLineStyle(lineStyle(styles.getBorderLeftStyle()));
+				g.setLineWidth(styles.getBorderLeftWidth());
+				g.drawLine(left + lw2, top + lw2, left + lw2, bottom - lw2 - 1);
+				g.setColor(oldColor);
+				color.dispose();
+			}
+
+			// Right border
+			if (hasRight && styles.getBorderRightWidth() > 0) {
+				ColorResource color = g.createColor(styles
+						.getBorderRightColor());
+				ColorResource oldColor = g.setColor(color);
+				g.setLineStyle(lineStyle(styles.getBorderRightStyle()));
+				g.setLineWidth(styles.getBorderRightWidth());
+				g.drawLine(right - rw2 - 1, top + rw2, right - rw2 - 1, bottom
+						- rw2 - 1);
+				g.setColor(oldColor);
+				color.dispose();
+			}
+
+			// Top border
+			if (styles.getBorderTopWidth() > 0) {
+				ColorResource color = g.createColor(styles.getBorderTopColor());
+				ColorResource oldColor = g.setColor(color);
+				g.setLineStyle(lineStyle(styles.getBorderTopStyle()));
+				g.setLineWidth(styles.getBorderTopWidth());
+				g.drawLine(left + tw2, top + tw2, right - tw2, top + tw2);
+				g.setColor(oldColor);
+				color.dispose();
+			}
+
+			// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+			// oldAntiAlias);
+			g.setAntiAliased(oldAntiAlias);
+
+		}
+	}
+
+	/**
+	 * Convert a CSS line style string (e.g. "dotted") to the corresponding
+	 * Graphics.LINE_XXX style.
+	 */
+	private static int lineStyle(String style) {
+		if (style.equals(CSS.DOTTED)) {
+			return Graphics.LINE_DOT;
+		} else if (style.equals(CSS.DASHED)) {
+			return Graphics.LINE_DASH;
+		} else {
+			return Graphics.LINE_SOLID;
+		}
+
+	}
+
+	/**
+	 * Returns the insets for a CSS box with the given styles.
+	 * 
+	 * @param styles
+	 *            Styles for the box.
+	 * @param containerWidth
+	 *            Content area of the containing box.
+	 */
+	public static Insets getInsets(Styles styles, int containerWidth) {
+
+		int top = styles.getMarginTop().get(containerWidth)
+				+ styles.getBorderTopWidth()
+				+ styles.getPaddingTop().get(containerWidth);
+
+		int left = styles.getMarginLeft().get(containerWidth)
+				+ styles.getBorderLeftWidth()
+				+ styles.getPaddingLeft().get(containerWidth);
+
+		int bottom = styles.getMarginBottom().get(containerWidth)
+				+ styles.getBorderBottomWidth()
+				+ styles.getPaddingBottom().get(containerWidth);
+
+		int right = styles.getMarginRight().get(containerWidth)
+				+ styles.getBorderRightWidth()
+				+ styles.getPaddingRight().get(containerWidth);
+
+		return new Insets(top, left, bottom, right);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractInlineBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractInlineBox.java
new file mode 100644
index 0000000..ef9c5b6
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractInlineBox.java
@@ -0,0 +1,21 @@
+/*******************************************************************************

+ * Copyright (c) 2010 Florian Thienel 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:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.wst.xml.vex.core.internal.layout;

+

+/**

+ * @author Florian Thienel

+ */

+public abstract class AbstractInlineBox extends AbstractBox implements InlineBox {

+

+	public void alignOnBaseline(int baseline) {

+		setY(baseline - getBaseline());

+	}

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AnonymousBlockBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AnonymousBlockBox.java
new file mode 100644
index 0000000..5021f0d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AnonymousBlockBox.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.List;
+
+/**
+ * A block box that is not associated with a particular element.
+ */
+public class AnonymousBlockBox extends AbstractBlockBox {
+
+	public AnonymousBlockBox(LayoutContext context, BlockBox parent,
+			int startOffset, int endOffset) {
+
+		super(context, parent, startOffset, endOffset);
+	}
+
+	protected List<Box> createChildren(LayoutContext context) {
+		return createBlockBoxes(context, this.getStartOffset(), this
+				.getEndOffset(), this.getWidth(), null, null);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockBox.java
new file mode 100644
index 0000000..7787476
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockBox.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+
+/**
+ * Represents a block box. Block boxes are stacked one on top of another.
+ */
+public interface BlockBox extends Box {
+
+	/**
+	 * Returns the first LineBox contained by this block, or null if the block
+	 * contains no lines.
+	 */
+	public LineBox getFirstLine();
+
+	/**
+	 * Returns the last LineBox contained by this block, or null if the block
+	 * contains no lines.
+	 */
+	public LineBox getLastLine();
+
+	/**
+	 * Returns the offset of the end of the line containing the given offset.
+	 * 
+	 * @param offset
+	 *            offset identifying the current line.
+	 */
+	public int getLineEndOffset(int offset);
+
+	/**
+	 * Returns the offset of the start of the line containing the given offset.
+	 * 
+	 * @param offset
+	 *            offset identifying the current line.
+	 */
+	public int getLineStartOffset(int offset);
+
+	/**
+	 * Returns the bottom margin of this box.
+	 */
+	public int getMarginBottom();
+
+	/**
+	 * Returns the top margin of this box.
+	 */
+	public int getMarginTop();
+
+	/**
+	 * Returns the offset on the next line that is closest to the given x
+	 * coordinate. The given offset may be before the start of this box in which
+	 * case this method should return the offset of the first line in this box.
+	 * 
+	 * @param context
+	 *            LayoutContext used for the layout
+	 * @param offset
+	 *            the current offset
+	 * @param x
+	 *            the x coordinate
+	 */
+	public int getNextLineOffset(LayoutContext context, int offset, int x);
+
+	/**
+	 * Returns the offset on the previous line that is closest to the given x
+	 * coordinate. The given offset may be after the end of this box in which
+	 * case this method should return the offset of the last line in this box.
+	 * 
+	 * @param context
+	 *            LayoutContext used for the layout
+	 * @param offset
+	 *            the current offset
+	 * @param x
+	 *            the x coordinate
+	 */
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x);
+
+	/**
+	 * Returns the parent box of this box.
+	 */
+	public BlockBox getParent();
+
+	/**
+	 * Informs this box that its layout has become invalid, and that it should
+	 * re-do it the next time layout is called.
+	 * 
+	 * @param direct
+	 *            If true, the box's content has changed and it must re-create
+	 *            it's children on the next call to layout. Otherwise, it should
+	 *            just propagate the next layout call to its children.
+	 */
+	public void invalidate(boolean direct);
+
+	/**
+	 * Layout this box. This method is responsible for the following.
+	 * 
+	 * <ul>
+	 * <li>Creating any child boxes.</li>
+	 * <li>Calling layout on the child boxes.</li>
+	 * <li>Positioning the child boxes (i.e. calling child.setX() and
+	 * child.setY())</li>
+	 * <li>Determining this box's height and width.</li>
+	 * </ul>
+	 * 
+	 * <p>
+	 * Boxes with no children should simply calculate their width and height
+	 * here
+	 * </p>
+	 * 
+	 * <p>
+	 * This method is passed a vertical range to be layed out. Children falling
+	 * outside this range need not be layed out.
+	 * </p>
+	 * 
+	 * <p>
+	 * This method returns an IntRange object representing the vertical range to
+	 * re-draw due to layout change. Null may be returned if there were no
+	 * changes that need to be re-drawn.
+	 * </p>
+	 * 
+	 * @param context
+	 *            The layout context to be used.
+	 * @param top
+	 *            Top of the range to lay out.
+	 * @param bottom
+	 *            Bottom of the range to lay out.
+	 */
+	public IntRange layout(LayoutContext context, int top, int bottom);
+
+	/**
+	 * Sets the initial size of the box.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 */
+	public void setInitialSize(LayoutContext context);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockElementBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockElementBox.java
new file mode 100644
index 0000000..df4f661
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockElementBox.java
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *     Mohamadou Nassourou - Bug 298912 - rudimentary support for images 
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin;
+import org.eclipse.wst.xml.vex.core.internal.core.Drawable;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.StyleSheet;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * A block box corresponding to a DOM Element. Block boxes lay their children
+ * out stacked top to bottom. Block boxes correspond to the
+ * <code>display: block;</code> CSS property.
+ */
+public class BlockElementBox extends AbstractBlockBox {
+
+	/** hspace btn. list-item bullet and block, as fraction of font-size */
+	static final float BULLET_SPACE = 0.5f;
+
+	/** vspace btn. list-item bullet and baseine, as fraction of font-size */
+	// private static final float BULLET_LIFT = 0.1f;
+	/** number of boxes created since VM startup, for profiling */
+	private static int boxCount;
+
+	BlockBox beforeMarker;
+
+	/**
+	 * Class constructor. This box's children are not created here but in the
+	 * first call to layout. Instead, we estimate the box's height here based on
+	 * the given width.
+	 * 
+	 * @param context
+	 *            LayoutContext used for this layout.
+	 * @param parent
+	 *            This box's parent box.
+	 * @param element
+	 *            Element to which this box corresponds.
+	 */
+	public BlockElementBox(LayoutContext context, BlockBox parent,
+			Element element) {
+		super(context, parent, element);
+	}
+
+	/**
+	 * Returns the number of boxes created since VM startup. Used for profiling.
+	 */
+	public static int getBoxCount() {
+		return boxCount;
+	}
+
+	public int getEndOffset() {
+		return this.getElement().getEndOffset();
+	}
+
+	public int getStartOffset() {
+		return this.getElement().getStartOffset() + 1;
+	}
+
+	public boolean hasContent() {
+		return true;
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+
+		super.paint(context, x, y);
+
+		if (this.beforeMarker != null) {
+			this.beforeMarker.paint(context, x + this.beforeMarker.getX(), y
+					+ this.beforeMarker.getY());
+		}
+	}
+
+	protected int positionChildren(LayoutContext context) {
+
+		int repaintStart = super.positionChildren(context);
+
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		if (this.beforeMarker != null) {
+			int x = -this.beforeMarker.getWidth()
+					- Math.round(BULLET_SPACE * styles.getFontSize());
+			int y = this.getFirstLineTop(context);
+			LineBox firstLine = this.getFirstLine();
+			if (firstLine != null) {
+				y += firstLine.getBaseline()
+						- this.beforeMarker.getFirstLine().getBaseline();
+			}
+
+			this.beforeMarker.setX(x);
+			this.beforeMarker.setY(y);
+		}
+
+		return repaintStart;
+	}
+
+	public String toString() {
+		return "BlockElementBox: <" + this.getElement().getPrefixedName() + ">" + "[x="
+				+ this.getX() + ",y=" + this.getY() + ",width="
+				+ this.getWidth() + ",height=" + this.getHeight() + "]";
+	}
+
+	// ===================================================== PRIVATE
+
+	/**
+	 * Lays out the children as vertically stacked blocks. Runs of text and
+	 * inline elements are wrapped in DummyBlockBox's.
+	 */
+	public List<Box> createChildren(LayoutContext context) {
+		long start = 0;
+		if (VEXCorePlugin.getInstance().isDebugging())
+			start = System.currentTimeMillis();
+
+		final Element element = this.getElement();
+		int width = this.getWidth();
+
+		List<Box> childList = new ArrayList<Box>();
+
+		StyleSheet styleSheet = context.getStyleSheet();
+
+		// element and styles for generated boxes
+		Element genElement;
+		Styles genStyles;
+
+		// :before content
+		List<InlineBox> beforeInlines = null;
+		genElement = context.getStyleSheet()
+				.getBeforeElement(this.getElement());
+		if (genElement != null) {
+			genStyles = styleSheet.getStyles(genElement);
+			if (genStyles.getDisplay().equals(CSS.INLINE)) {
+				beforeInlines = new ArrayList<InlineBox>();
+				beforeInlines.addAll(LayoutUtils.createGeneratedInlines(
+						context, genElement));
+			} else {
+				childList.add(new BlockPseudoElementBox(context, genElement,
+						this, width));
+			}
+		}
+
+		// background image
+		final Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		if (styles.hasBackgroundImage() && !styles.getDisplay().equalsIgnoreCase(CSS.NONE)) {
+			final InlineBox imageBox = ImageBox.create(getElement(), context, getWidth());
+			if (imageBox != null) {
+				if (beforeInlines == null)
+					beforeInlines = new ArrayList<InlineBox>();
+				beforeInlines.add(imageBox);
+			}
+		}
+		
+		// :after content
+		Box afterBlock = null;
+		List<InlineBox> afterInlines = null;
+		genElement = context.getStyleSheet().getAfterElement(this.getElement());
+		if (genElement != null) {
+			genStyles = context.getStyleSheet().getStyles(genElement);
+			if (genStyles.getDisplay().equals(CSS.INLINE)) {
+				afterInlines = new ArrayList<InlineBox>();
+				afterInlines.addAll(LayoutUtils.createGeneratedInlines(context,
+						genElement));
+			} else {
+				afterBlock = new BlockPseudoElementBox(context, genElement,
+						this, width);
+			}
+		}
+
+		final int startOffset = element.getStartOffset() + 1;
+		final int endOffset = element.getEndOffset();
+		List<Box> blockBoxes = createBlockBoxes(context, startOffset, endOffset,
+				width, beforeInlines, afterInlines);
+		childList.addAll(blockBoxes);
+
+		if (afterBlock != null)
+			childList.add(afterBlock);
+
+		if (styles.getDisplay().equals(CSS.LIST_ITEM)
+				&& !styles.getListStyleType().equals(CSS.NONE)) {
+			this.createListMarker(context);
+		}
+
+		if (VEXCorePlugin.getInstance().isDebugging()) {
+			long end = System.currentTimeMillis();
+			if (end - start > 10) {
+				System.out.println("BEB.layout for "
+						+ this.getElement().getPrefixedName() + " took "
+						+ (end - start) + "ms");
+			}
+		}
+
+		return childList;
+	}
+	
+	/**
+	 * Creates a marker box for this primary box and puts it in the beforeMarker
+	 * field.
+	 */
+	private void createListMarker(LayoutContext context) {
+
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+
+		InlineBox markerInline;
+		String type = styles.getListStyleType();
+		if (type.equals(CSS.NONE)) {
+			return;
+		} else if (type.equals(CSS.CIRCLE)) {
+			markerInline = createCircleBullet(this.getElement(), styles);
+		} else if (type.equals(CSS.SQUARE)) {
+			markerInline = createSquareBullet(this.getElement(), styles);
+		} else if (isEnumeratedListStyleType(type)) {
+			String item = this.getItemNumberString(type);
+			markerInline = new StaticTextBox(context, this.getElement(), item
+					+ ".");
+		} else {
+			markerInline = createDiscBullet(this.getElement(), styles);
+		}
+
+		this.beforeMarker = ParagraphBox.create(context, this.getElement(),
+				new InlineBox[] { markerInline }, Integer.MAX_VALUE);
+
+	}
+
+	/**
+	 * Returns a Drawable that draws a circle-style list item bullet.
+	 */
+	private static InlineBox createCircleBullet(Element element, Styles styles) {
+		final int size = Math.round(0.5f * styles.getFontSize());
+		final int lift = Math.round(0.1f * styles.getFontSize());
+		Drawable drawable = new Drawable() {
+			public void draw(Graphics g, int x, int y) {
+				g.setLineStyle(Graphics.LINE_SOLID);
+				g.setLineWidth(1);
+				g.drawOval(x, y - size - lift, size, size);
+			}
+
+			public Rectangle getBounds() {
+				return new Rectangle(0, -size - lift, size, size);
+			}
+		};
+		return new DrawableBox(drawable, element);
+	}
+
+	/**
+	 * Returns a Drawable that draws a disc-style list item bullet.
+	 */
+	private static InlineBox createDiscBullet(Element element, Styles styles) {
+		final int size = Math.round(0.5f * styles.getFontSize());
+		final int lift = Math.round(0.1f * styles.getFontSize());
+		Drawable drawable = new Drawable() {
+			public void draw(Graphics g, int x, int y) {
+				g.fillOval(x, y - size - lift, size, size);
+			}
+
+			public Rectangle getBounds() {
+				return new Rectangle(0, -size - lift, size, size);
+			}
+		};
+		return new DrawableBox(drawable, element);
+	}
+
+	/**
+	 * Returns a Drawable that draws a square-style list item bullet.
+	 */
+	private static InlineBox createSquareBullet(Element element, Styles styles) {
+		final int size = Math.round(0.5f * styles.getFontSize());
+		final int lift = Math.round(0.1f * styles.getFontSize());
+		Drawable drawable = new Drawable() {
+			public void draw(Graphics g, int x, int y) {
+				g.setLineStyle(Graphics.LINE_SOLID);
+				g.setLineWidth(1);
+				g.drawRect(x, y - size - lift, size, size);
+			}
+
+			public Rectangle getBounds() {
+				return new Rectangle(0, -size - lift, size, size);
+			}
+		};
+		return new DrawableBox(drawable, element);
+	}
+
+	/**
+	 * Returns the vertical distance from the top of this box to the top of its
+	 * first line.
+	 */
+	int getFirstLineTop(LayoutContext context) {
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		int top = styles.getBorderTopWidth() + styles.getPaddingTop().get(0);
+		Box[] children = this.getChildren();
+		if (children != null && children.length > 0
+				&& children[0] instanceof BlockElementBox) {
+			return top
+					+ ((BlockElementBox) children[0]).getFirstLineTop(context);
+		} else {
+			return top;
+		}
+	}
+
+	/**
+	 * Returns the item number of this box. The item number indicates the
+	 * ordinal number of the corresponding element amongst its siblings starting
+	 * with 1.
+	 */
+	private int getItemNumber() {
+		Element element = this.getElement();
+		Element parent = element.getParent();
+
+		if (parent == null) {
+			return 1;
+		}
+
+		int item = 1;
+		List<Element> children = parent.getChildElements();
+		for (int i = 0; i < children.size(); i++) {
+			if (children.get(i) == element) {
+				return item;
+			}
+			if (children.get(i).getQualifiedName().equals(element.getQualifiedName())) {
+				item++;
+			}
+		}
+
+		throw new IllegalStateException();
+	}
+
+	private String getItemNumberString(String style) {
+		int item = getItemNumber();
+		if (style.equals(CSS.DECIMAL_LEADING_ZERO)) {
+			if (item < 10) {
+				return "0" + Integer.toString(item);
+			} else {
+				return Integer.toString(item);
+			}
+		} else if (style.equals(CSS.LOWER_ALPHA)
+				|| style.equals(CSS.LOWER_LATIN)) {
+			return this.getAlpha(item);
+		} else if (style.equals(CSS.LOWER_ROMAN)) {
+			return this.getRoman(item);
+		} else if (style.equals(CSS.UPPER_ALPHA)
+				|| style.equals(CSS.UPPER_LATIN)) {
+			return this.getAlpha(item).toUpperCase();
+		} else if (style.equals(CSS.UPPER_ROMAN)) {
+			return this.getRoman(item).toUpperCase();
+		} else {
+			return Integer.toString(item);
+		}
+	}
+
+	private String getAlpha(int n) {
+		final String alpha = "abcdefghijklmnopqrstuvwxyz";
+		return String.valueOf(alpha.charAt((n - 1) % 26));
+	}
+
+	private String getRoman(int n) {
+		final String[] ones = { "", "i", "ii", "iii", "iv", "v", "vi", "vii",
+				"viii", "ix" };
+		final String[] tens = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx",
+				"lxxx", "xc" };
+		final String[] hundreds = { "", "c", "cc", "ccc", "cd", "d", "dc",
+				"dcc", "dccc", "cm" };
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < n / 1000; i++) {
+			sb.append("m");
+		}
+		sb.append(hundreds[(n / 100) % 10]);
+		sb.append(tens[(n / 10) % 10]);
+		sb.append(ones[n % 10]);
+		return sb.toString();
+	}
+
+	private static boolean isEnumeratedListStyleType(String s) {
+		return s.equals(CSS.ARMENIAN) || s.equals(CSS.CJK_IDEOGRAPHIC)
+				|| s.equals(CSS.DECIMAL) || s.equals(CSS.DECIMAL_LEADING_ZERO)
+				|| s.equals(CSS.GEORGIAN) || s.equals(CSS.HEBREW)
+				|| s.equals(CSS.HIRAGANA) || s.equals(CSS.HIRAGANA_IROHA)
+				|| s.equals(CSS.KATAKANA) || s.equals(CSS.KATAKANA_IROHA)
+				|| s.equals(CSS.LOWER_ALPHA) || s.equals(CSS.LOWER_GREEK)
+				|| s.equals(CSS.LOWER_LATIN) || s.equals(CSS.LOWER_ROMAN)
+				|| s.equals(CSS.UPPER_ALPHA) || s.equals(CSS.UPPER_LATIN)
+				|| s.equals(CSS.UPPER_ROMAN);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockPseudoElementBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockPseudoElementBox.java
new file mode 100644
index 0000000..cc23b54
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BlockPseudoElementBox.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Implements a Block
+ */
+public class BlockPseudoElementBox extends AbstractBox implements BlockBox {
+
+	private Element pseudoElement;
+	private BlockBox parent;
+	private ParagraphBox para;
+
+	private int marginTop;
+	private int marginBottom;
+
+	public BlockPseudoElementBox(LayoutContext context, Element pseudoElement,
+			BlockBox parent, int width) {
+
+		this.pseudoElement = pseudoElement;
+		this.parent = parent;
+
+		Styles styles = context.getStyleSheet().getStyles(pseudoElement);
+
+		this.marginTop = styles.getMarginTop().get(width);
+		this.marginBottom = styles.getMarginBottom().get(width);
+
+		int leftInset = styles.getMarginLeft().get(width)
+				+ styles.getBorderLeftWidth()
+				+ styles.getPaddingLeft().get(width);
+		int rightInset = styles.getMarginRight().get(width)
+				+ styles.getBorderRightWidth()
+				+ styles.getPaddingRight().get(width);
+
+		int childWidth = width - leftInset - rightInset;
+		List<InlineBox> inlines = LayoutUtils.createGeneratedInlines(context,
+				pseudoElement);
+		this.para = ParagraphBox.create(context, pseudoElement, inlines,
+				childWidth);
+
+		this.para.setX(0);
+		this.para.setY(0);
+		this.setWidth(width - leftInset - rightInset);
+		this.setHeight(this.para.getHeight());
+	}
+
+	/**
+	 * Provide children for {@link AbstractBox#paint}.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getChildren()
+	 */
+	public Box[] getChildren() {
+		return new Box[] { this.para };
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getElement()
+	 */
+	public Element getElement() {
+		return this.pseudoElement;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getFirstLine()
+	 */
+	public LineBox getFirstLine() {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getLastLine()
+	 */
+	public LineBox getLastLine() {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getLineEndOffset(int)
+	 */
+	public int getLineEndOffset(int offset) {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getLineStartOffset(int)
+	 */
+	public int getLineStartOffset(int offset) {
+		throw new IllegalStateException();
+	}
+
+	public int getMarginBottom() {
+		return this.marginBottom;
+	}
+
+	public int getMarginTop() {
+		return this.marginTop;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getNextLineOffset(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int getNextLineOffset(LayoutContext context, int offset, int x) {
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * Returns this box's parent.
+	 */
+	public BlockBox getParent() {
+		return this.parent;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getPreviousLineOffset(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x) {
+		throw new IllegalStateException();
+	}
+
+	public IntRange layout(LayoutContext context, int top, int bottom) {
+		return null;
+	}
+
+	public void invalidate(boolean direct) {
+		throw new IllegalStateException(
+				"invalidate called on a non-element BlockBox");
+	}
+
+	/**
+	 * Draw boxes before painting our child.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#paint(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+		this.drawBox(context, x, y, this.getParent().getWidth(), true);
+		super.paint(context, x, y);
+	}
+
+	public void setInitialSize(LayoutContext context) {
+		// NOP - size calculated in the ctor
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/Box.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/Box.java
new file mode 100644
index 0000000..8b90c0b
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/Box.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Represents a rectangular area in the layout. The height and width of the box
+ * are measured from the inner edges of the box's padding, as in CSS. Similarly,
+ * the (x, y) position of the box are with respect to the inner top-left corner
+ * of the box's padding, and are relative to the parent's (x, y) position.
+ */
+public interface Box {
+
+	/**
+	 * Returns true if this box contains the given offset.
+	 * 
+	 * @param offset
+	 *            the offset to test
+	 */
+	public boolean containsOffset(int offset);
+
+	/**
+	 * Returns a Caret object representing the given offset.
+	 * 
+	 * @param context
+	 *            LayoutContext to be used
+	 * @param offset
+	 *            offset for which to retrieve the caret
+	 */
+	public Caret getCaret(LayoutContext context, int offset);
+
+	/**
+	 * Returns an array of this box's children.
+	 */
+	public Box[] getChildren();
+
+	/**
+	 * Returns the Element with which this box is associated, or null if there
+	 * is no such box. The box may directly represent the Element, or simply use
+	 * it for formatting information.
+	 */
+	public Element getElement();
+
+	/**
+	 * Returns the offset of the end of the content that the box covers.
+	 */
+	public int getEndOffset();
+
+	/**
+	 * Returns the height of the box. For boxes subject to the CSS box model,
+	 * this is the inner height, exclusive of margins, borders, and padding.
+	 */
+	public int getHeight();
+
+	/**
+	 * Return an Insets object representing the total width of margins, borders,
+	 * and padding for this box.
+	 * 
+	 * @param context
+	 *            LayoutContext being used for this layout.
+	 * @param containerWidth
+	 *            Width of the containing box.
+	 */
+	public Insets getInsets(LayoutContext context, int containerWidth);
+
+	/**
+	 * Returns the offset of the start of the content that the box covers.
+	 */
+	public int getStartOffset();
+
+	/**
+	 * Returns the width of the box. For boxes subject to the CSS box model,
+	 * this is the inner width, exclusive of margins, borders, and padding.
+	 */
+	public int getWidth();
+
+	/**
+	 * Returns the x-coordinate of the box, relative to its parent. For boxes
+	 * subject to the CSS box model, this is the left edge of the box's content
+	 * area.
+	 */
+	public int getX();
+
+	/**
+	 * Returns the y-coordinate of the box, relative to its parent. For boxes
+	 * subject to the CSS box model, this is the top edge of the box's content
+	 * area.
+	 */
+	public int getY();
+
+	/**
+	 * Returns true if this box represents a portion of the XML document's
+	 * content. If false is returned, the following methods are not supported by
+	 * this box.
+	 * 
+	 * <ul>
+	 * <li>getCaretShapes()</li>
+	 * <li>getEndOffset()</li>
+	 * <li>getStartOffset()</li>
+	 * <li>viewToModel()</li>
+	 * </ul>
+	 */
+	public boolean hasContent();
+
+	/**
+	 * Returns true if the box is anonymous, that is, it is not directly
+	 * associated with an element.
+	 */
+	public boolean isAnonymous();
+
+	/**
+	 * Draws the box's content in the given Graphics context.
+	 * 
+	 * @param context
+	 *            <code>LayoutContext</code> containing the
+	 *            <code>Graphics</code> object into which the box should be
+	 *            painted
+	 * @param x
+	 *            the x-offset at which the box should be painted
+	 * @param y
+	 *            the y-offset at which the box should be painted
+	 */
+	public void paint(LayoutContext context, int x, int y);
+
+	/**
+	 * Sets the height of this box.
+	 * 
+	 * @param height
+	 *            new height of the box
+	 */
+	public void setHeight(int height);
+
+	/**
+	 * Sets the width of this box.
+	 * 
+	 * @param width
+	 *            new width of the box
+	 */
+	public void setWidth(int width);
+
+	/**
+	 * Sets the x-coordinate of the top-left corner of the box.
+	 * 
+	 * @param x
+	 *            the new x-coordinate
+	 */
+	public void setX(int x);
+
+	/**
+	 * Sets the y-coordinate of the top-left corner of the box.
+	 * 
+	 * @param y
+	 *            the new y-coordinate
+	 */
+	public void setY(int y);
+
+	/**
+	 * Returns the offset in the content closest to the given view position.
+	 * 
+	 * @param context
+	 *            <code>LayoutContext</code> for this box tree
+	 * @param x
+	 *            x offset of the view position for which the model offset is to
+	 *            be determined.
+	 * @param y
+	 *            y offset of the view position for which the model offset is to
+	 *            be determined.
+	 */
+	public int viewToModel(LayoutContext context, int x, int y);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BoxFactory.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BoxFactory.java
new file mode 100644
index 0000000..b2c762c
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/BoxFactory.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.io.Serializable;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Interface to an object that creates boxes from elements. Implementations of
+ * this interface must be serializable.
+ */
+public interface BoxFactory extends Serializable {
+
+	/**
+	 * Creates a box given an element.
+	 * 
+	 * @param context
+	 *            CSS styles for the new element
+	 * @param element
+	 *            Element for which the box should be created.
+	 * @param parent
+	 *            Parent box for the new box.
+	 * @param containerWidth
+	 *            Width of the box to be created.
+	 */
+	public Box createBox(LayoutContext context, Element element,
+			BlockBox parent, int containerWidth);
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CompositeInlineBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CompositeInlineBox.java
new file mode 100644
index 0000000..cbfa82c
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CompositeInlineBox.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+
+/**
+ * InlineBox consisting of several children. This is the parent class of
+ * InlineElementBox and LineBox, and implements the split method.
+ */
+public abstract class CompositeInlineBox extends AbstractInlineBox {
+
+	/**
+	 * Returns true if any of the children have content.
+	 */
+	public boolean hasContent() {
+		Box[] children = this.getChildren();
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].hasContent()) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public boolean isEOL() {
+		Box[] children = this.getChildren();
+		return children.length > 0
+				&& ((InlineBox) children[children.length - 1]).isEOL();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+
+		int x = 0;
+		Box[] children = this.getChildren();
+
+		// we want the caret to be to the right of any leading static boxes...
+		int start = 0;
+		while (start < children.length && !children[start].hasContent()) {
+			x += children[start].getWidth();
+			start++;
+		}
+
+		// ...and to the left of any trailing static boxes
+		int end = children.length;
+		while (end < 0 && !children[end - 1].hasContent()) {
+			end--;
+		}
+
+		for (int i = start; i < end; i++) {
+			Box child = children[i];
+			if (child.hasContent()) {
+				if (offset < child.getStartOffset()) {
+					break;
+				} else if (offset <= child.getEndOffset()) {
+					Caret caret = child.getCaret(context, offset);
+					caret.translate(child.getX(), child.getY());
+					return caret;
+				}
+			}
+			x += child.getWidth();
+		}
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		FontMetrics fm = g.getFontMetrics();
+		int height = fm.getAscent() + fm.getDescent();
+		g.setFont(oldFont);
+		font.dispose();
+
+		int lineHeight = styles.getLineHeight();
+		int y = (lineHeight - height) / 2;
+		return new TextCaret(x, y, height);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, boolean)
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force) {
+
+		// list of children that have yet to be added to the left side
+		LinkedList<Box> rights = new LinkedList<Box>(Arrays.asList(this.getChildren()));
+
+		// pending is a list of inlines we are trying to add to the left side
+		// but which cannot end at a split
+		List<InlineBox> pending = new ArrayList<InlineBox>();
+
+		// list of inlines that make up the left side
+		List<InlineBox> lefts = new ArrayList<InlineBox>();
+
+		int remaining = maxWidth;
+		boolean eol = false;
+
+		while (!rights.isEmpty() && remaining >= 0) {
+			InlineBox inline = (InlineBox) rights.removeFirst();
+			InlineBox.Pair pair = inline.split(context, remaining, force
+					&& lefts.isEmpty());
+
+			if (pair.getLeft() != null) {
+				lefts.addAll(pending);
+				pending.clear();
+				lefts.add(pair.getLeft());
+				remaining -= pair.getLeft().getWidth();
+			}
+
+			if (pair.getRight() != null) {
+				pending.add(pair.getRight());
+				remaining -= pair.getRight().getWidth();
+			}
+
+			if (pair.getLeft() != null && pair.getLeft().isEOL()) {
+				eol = true;
+				break;
+			}
+
+		}
+
+		if (((force && lefts.isEmpty()) || remaining >= 0) && !eol) {
+			lefts.addAll(pending);
+		} else {
+			rights.addAll(0, pending);
+		}
+
+		InlineBox[] leftKids = lefts.toArray(new InlineBox[lefts.size()]);
+		InlineBox[] rightKids = rights.toArray(new InlineBox[rights.size()]);
+		return this.split(context, leftKids, rightKids);
+	}
+
+	/**
+	 * Creates a Pair of InlineBoxes, each with its own set of children.
+	 * 
+	 * @param context
+	 *            LayoutContext used for this layout.
+	 * @param lefts
+	 *            Child boxes to be given to the left box.
+	 * @param rights
+	 *            Child boxes to be given to the right box.
+	 * @return
+	 */
+	protected abstract Pair split(LayoutContext context, InlineBox[] lefts,
+			InlineBox[] rights);
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#viewToModel(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int viewToModel(LayoutContext context, int x, int y) {
+
+		if (!this.hasContent()) {
+			throw new RuntimeException(
+					"Oops. Calling viewToModel on a line with no content");
+		}
+
+		Box closestContentChild = null;
+		int delta = Integer.MAX_VALUE;
+		Box[] children = this.getChildren();
+		for (int i = 0; i < children.length; i++) {
+			Box child = children[i];
+			if (child.hasContent()) {
+				int newDelta = 0;
+				if (x < child.getX()) {
+					newDelta = child.getX() - x;
+				} else if (x > child.getX() + child.getWidth()) {
+					newDelta = x - (child.getX() + child.getWidth());
+				}
+				if (newDelta < delta) {
+					delta = newDelta;
+					closestContentChild = child;
+				}
+			}
+		}
+
+		return closestContentChild.viewToModel(context, x
+				- closestContentChild.getX(), y - closestContentChild.getY());
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CssBoxFactory.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CssBoxFactory.java
new file mode 100644
index 0000000..088a6e1
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/CssBoxFactory.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Implementation of the BoxFactory interface that returns boxes that represent
+ * CSS semantics.
+ */
+public class CssBoxFactory implements BoxFactory {
+
+	private static final long serialVersionUID = -6882526795866485074L;
+
+	/**
+	 * Class constructor.
+	 */
+	public CssBoxFactory() {
+	}
+
+	public Box createBox(LayoutContext context, Element element,
+			BlockBox parent, int containerWidth) {
+		Styles styles = context.getStyleSheet().getStyles(element);
+		if (styles.getDisplay().equals(CSS.TABLE)) {
+			return new TableBox(context, parent, element);
+		} else if (styles.isBlock()) {
+			return new BlockElementBox(context, parent, element);
+		} else {
+			throw new RuntimeException("Unexpected display property: "
+					+ styles.getDisplay());
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DocumentTextBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DocumentTextBox.java
new file mode 100644
index 0000000..15b5fa9
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DocumentTextBox.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Document;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.eclipse.wst.xml.vex.core.internal.dom.Text;
+
+/**
+ * A TextBox that gets its text from the document. Represents text which is
+ * editable within the VexWidget.
+ */
+public class DocumentTextBox extends TextBox {
+
+	private int startRelative;
+	private int endRelative;
+
+	/**
+	 * Class constructor, accepting a Text object.
+	 * 
+	 * @param context
+	 *            LayoutContext in use
+	 * @param element
+	 *            Element being used
+	 * @param text
+	 */
+	public DocumentTextBox(LayoutContext context, Element element, Text text) {
+		this(context, element, text.getStartOffset(), text.getEndOffset());
+	}
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param context
+	 *            LayoutContext used to calculate the box's size.
+	 * @param element
+	 *            Element that directly contains the text.
+	 * @param startOffset
+	 *            start offset of the text
+	 * @param endOffset
+	 *            end offset of the text
+	 */
+	public DocumentTextBox(LayoutContext context, Element element,
+			int startOffset, int endOffset) {
+		super(element);
+
+		if (startOffset >= endOffset) {
+			throw new IllegalStateException("DocumentTextBox: startOffset ("
+					+ startOffset + ") >= endOffset (" + endOffset + ")");
+		}
+
+		this.startRelative = startOffset - element.getStartOffset();
+		this.endRelative = endOffset - element.getStartOffset();
+		this.calculateSize(context);
+
+		if (this.getText().length() < (endOffset - startOffset)) {
+			throw new IllegalStateException();
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		if (this.endRelative == -1) {
+			return -1;
+		} else {
+			return this.getElement().getStartOffset() + this.endRelative - 1;
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		if (this.startRelative == -1) {
+			return -1;
+		} else {
+			return this.getElement().getStartOffset() + this.startRelative;
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.TextBox#getText()
+	 */
+	public String getText() {
+		Document doc = this.getElement().getDocument();
+		return doc.getText(this.getStartOffset(), this.getEndOffset() + 1);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#hasContent()
+	 */
+	public boolean hasContent() {
+		return true;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#paint(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		Graphics g = context.getGraphics();
+
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		ColorResource foreground = g.createColor(styles.getColor());
+		ColorResource oldForeground = g.setColor(foreground);
+		// ColorResource background =
+		// g.createColor(styles.getBackgroundColor());
+		// ColorResource oldBackground = g.setBackgroundColor(background);
+
+		char[] chars = this.getText().toCharArray();
+
+		if (chars.length < this.getEndOffset() - this.getStartOffset()) {
+			throw new IllegalStateException();
+		}
+
+		if (chars.length == 0) {
+			throw new IllegalStateException();
+		}
+
+		int start = 0;
+		int end = chars.length;
+		if (chars[end - 1] == NEWLINE_CHAR) {
+			end--;
+		}
+		int selStart = context.getSelectionStart() - this.getStartOffset();
+		selStart = Math.min(Math.max(selStart, start), end);
+		int selEnd = context.getSelectionEnd() - this.getStartOffset();
+		selEnd = Math.min(Math.max(selEnd, start), end);
+
+		// text before selection
+		if (start < selStart) {
+			g.drawChars(chars, start, selStart - start, x, y);
+			String s = new String(chars, start, selStart - start);
+			paintTextDecoration(context, styles, s, x, y);
+		}
+
+		// text after selection
+		if (selEnd < end) {
+			int x1 = x + g.charsWidth(chars, 0, selEnd);
+			g.drawChars(chars, selEnd, end - selEnd, x1, y);
+			String s = new String(chars, selEnd, end - selEnd);
+			paintTextDecoration(context, styles, s, x1, y);
+		}
+
+		// text within selection
+		if (selStart < selEnd) {
+			String s = new String(chars, selStart, selEnd - selStart);
+			int x1 = x + g.charsWidth(chars, 0, selStart);
+			this.paintSelectedText(context, s, x1, y);
+			paintTextDecoration(context, styles, s, x1, y);
+		}
+
+		g.setFont(oldFont);
+		g.setColor(oldForeground);
+		// g.setBackgroundColor(oldBackground);
+		font.dispose();
+		foreground.dispose();
+		// background.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.TextBox#splitAt(int)
+	 */
+	public Pair splitAt(LayoutContext context, int offset) {
+
+		if (offset < 0 || offset > (this.endRelative - this.startRelative)) {
+			throw new IllegalStateException();
+		}
+
+		int split = this.getStartOffset() + offset;
+
+		DocumentTextBox left;
+		if (offset == 0) {
+			left = null;
+		} else {
+			left = new DocumentTextBox(context, this.getElement(), this
+					.getStartOffset(), split);
+		}
+
+		InlineBox right;
+		if (split == this.getEndOffset() + 1) {
+			right = null;
+		} else {
+			right = new DocumentTextBox(context, this.getElement(), split, this
+					.getEndOffset() + 1);
+		}
+		return new Pair(left, right);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#viewToModel(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int viewToModel(LayoutContext context, int x, int y) {
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		char[] chars = this.getText().toCharArray();
+
+		if (this.getWidth() <= 0) {
+			return this.getStartOffset();
+		}
+
+		// first, get an estimate based on x / width
+		int offset = (x / this.getWidth()) * chars.length;
+		offset = Math.max(0, offset);
+		offset = Math.min(chars.length, offset);
+
+		int delta = Math.abs(x - g.charsWidth(chars, 0, offset));
+
+		// Search backwards
+		while (offset > 0) {
+			int newDelta = Math.abs(x - g.charsWidth(chars, 0, offset - 1));
+			if (newDelta > delta) {
+				break;
+			}
+			delta = newDelta;
+			offset--;
+		}
+
+		// Search forwards
+		while (offset < chars.length - 1) {
+			int newDelta = Math.abs(x - g.charsWidth(chars, 0, offset + 1));
+			if (newDelta > delta) {
+				break;
+			}
+			delta = newDelta;
+			offset++;
+		}
+
+		g.setFont(oldFont);
+		font.dispose();
+		return this.getStartOffset() + offset;
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DrawableBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DrawableBox.java
new file mode 100644
index 0000000..42f7781
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/DrawableBox.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Drawable;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * An inline box that draws a Drawable object. The drawable is drawn relative to
+ * the text baseline, therefore it should draw using mostly negative
+ * y-coordinates.
+ */
+public class DrawableBox extends AbstractInlineBox {
+
+	public static final byte NO_MARKER = 0;
+	public static final byte START_MARKER = 1;
+	public static final byte END_MARKER = 2;
+
+	private Drawable drawable;
+	private Element element;
+	private byte marker;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param drawable
+	 *            Drawable to draw.
+	 * @param element2
+	 *            Element whose styles determine the color of the drawable.
+	 */
+	public DrawableBox(Drawable drawable, Element element2) {
+		this(drawable, element2, NO_MARKER);
+	}
+
+	/**
+	 * Class constructor. This constructor is called when creating a DrawableBox
+	 * that represents the start or end marker of an inline element.
+	 * 
+	 * @param drawable
+	 *            Drawable to draw.
+	 * @param element2
+	 *            Element whose styles determine the color of the drawable.
+	 * @param marker
+	 *            which marker should be drawn. Must be one of NO_MARKER,
+	 *            START_MARKER, or END_MARKER.
+	 */
+	public DrawableBox(Drawable drawable, Element element2, byte marker) {
+		this.drawable = drawable;
+		this.element = element2;
+		this.marker = marker;
+		Rectangle bounds = drawable.getBounds();
+		this.setWidth(bounds.getWidth());
+		this.setHeight(bounds.getHeight());
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return 0;
+	}
+
+	/**
+	 * Returns the element that controls the styling for this text element.
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	public boolean isEOL() {
+		return false;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, boolean)
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force) {
+		return new Pair(null, this);
+	}
+
+	/**
+	 * Draw the drawable. The foreground color of the context's Graphics is set
+	 * before calling the drawable's draw method.
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.element);
+
+		boolean drawSelected = false;
+		if (this.marker == START_MARKER) {
+			drawSelected = this.getElement().getStartOffset() >= context
+					.getSelectionStart()
+					&& this.getElement().getStartOffset() + 1 <= context
+							.getSelectionEnd();
+		} else if (this.marker == END_MARKER) {
+			drawSelected = this.getElement().getEndOffset() >= context
+					.getSelectionStart()
+					&& this.getElement().getEndOffset() + 1 <= context
+							.getSelectionEnd();
+		}
+
+		FontResource font = g.createFont(styles.getFont());
+		ColorResource color = g.createColor(styles.getColor());
+
+		FontResource oldFont = g.setFont(font);
+		ColorResource oldColor = g.setColor(color);
+
+		FontMetrics fm = g.getFontMetrics();
+
+		if (drawSelected) {
+			Rectangle bounds = this.drawable.getBounds();
+			g.setColor(g.getSystemColor(ColorResource.SELECTION_BACKGROUND));
+			g.fillRect(x + bounds.getX(), y - fm.getAscent(),
+					bounds.getWidth(), styles.getLineHeight());
+			g.setColor(g.getSystemColor(ColorResource.SELECTION_FOREGROUND));
+		}
+
+		this.drawable.draw(g, x, y);
+
+		g.setFont(oldFont);
+		g.setColor(oldColor);
+		font.dispose();
+		color.dispose();
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "[shape]";
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ElementOrRangeCallback.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ElementOrRangeCallback.java
new file mode 100644
index 0000000..b090cab
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ElementOrRangeCallback.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+public interface ElementOrRangeCallback {
+	public void onElement(Element child, String displayStyle);
+
+	public void onRange(Element parent, int startOffset, int endOffset);
+}
\ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/HCaret.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/HCaret.java
new file mode 100644
index 0000000..6e46709
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/HCaret.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+
+/**
+ * A horizontal caret representing the insertion point between two block boxes.
+ */
+public class HCaret extends Caret {
+
+	private static final int LINE_WIDTH = 2;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param x
+	 *            x-coordinate of the top left corner of the caret
+	 * @param y
+	 *            y-coordinate of the top left corner of the caret
+	 * @param length
+	 *            Horizontal length of the caret.
+	 */
+	public HCaret(int x, int y, int length) {
+		super(x, y);
+		this.length = length;
+	}
+
+	public void draw(Graphics g, Color color) {
+		ColorResource newColor = g.createColor(color);
+		ColorResource oldColor = g.setColor(newColor);
+		g.fillRect(this.getX(), this.getY(), this.length, LINE_WIDTH);
+		g.setColor(oldColor);
+		newColor.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.core.Caret#getBounds()
+	 */
+	public Rectangle getBounds() {
+		return new Rectangle(this.getX(), this.getY(), this.length, LINE_WIDTH);
+	}
+
+	// ====================================================== PRIVATE
+
+	private int length;
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ImageBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ImageBox.java
new file mode 100644
index 0000000..ad66030
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ImageBox.java
@@ -0,0 +1,96 @@
+package org.eclipse.wst.xml.vex.core.internal.layout;

+

+import java.net.URL;

+

+import org.eclipse.wst.xml.vex.core.internal.core.Image;

+import org.eclipse.wst.xml.vex.core.internal.core.Point;

+import org.eclipse.wst.xml.vex.core.internal.css.Styles;

+import org.eclipse.wst.xml.vex.core.internal.dom.Element;

+

+public class ImageBox extends AbstractInlineBox {

+

+	private final Image image;

+

+	public static ImageBox create(final Element element, final LayoutContext context, final int maxWidth) {

+		if (element == null)

+			return null;

+		final Styles styles = context.getStyleSheet().getStyles(element);

+		final URL imageUrl = context.resolveUrl(element.getBaseURI(), styles.getBackgroundImage());

+		if (imageUrl == null)

+			return null;

+

+		final Image image = context.getGraphics().getImage(imageUrl);

+		final Point imageDimensions = getImageDimensions(image, styles);

+		final int width = Math.min(imageDimensions.getX(), maxWidth);

+		final int height = scale(imageDimensions.getY(), imageDimensions.getX(), width);

+

+		final ImageBox result = new ImageBox(image);

+		result.setWidth(width);

+		result.setHeight(height);

+		return result;

+	}

+

+	private static Point getImageDimensions(final Image image, final Styles styles) {

+		int styleWidth = styles.getElementWidth().get(image.getWidth());

+		int styleHeight = styles.getElementHeight().get(image.getHeight());

+		if (styleWidth != 0 && styleHeight != 0)

+			return new Point(styleWidth, styleHeight);

+		if (styleWidth == 0 && styleHeight != 0)

+			return new Point(scale(image.getWidth(), image.getHeight(), styleHeight), styleHeight);

+		if (styleWidth != 0 && styleHeight == 0)

+			return new Point(styleWidth, scale(image.getHeight(), image.getWidth(), styleWidth));

+		return new Point(image.getWidth(), image.getHeight());

+	}

+	

+	private static int scale(int opposite, int current, int scaled) {

+		return Math.round(1f * scaled / current * opposite);

+	}

+

+	public static ImageBox createWithHeight(final Element element, final LayoutContext context, final int maxHeight) {

+		if (element == null)

+			return null;

+		final URL imageUrl = context.resolveUrl(element.getBaseURI(), context.getStyleSheet().getStyles(element).getBackgroundImage());

+		if (imageUrl == null)

+			return null;

+

+		final Image image = context.getGraphics().getImage(imageUrl);

+		final int height = Math.min(image.getHeight(), maxHeight);

+		final int width = scale(image.getWidth(), image.getHeight(), height);

+

+		final ImageBox result = new ImageBox(image);

+		result.setWidth(width);

+		result.setHeight(height);

+		return result;

+	}

+

+	public ImageBox(final Image image) {

+		this.image = image;

+		setWidth(image.getWidth());

+		setHeight(image.getHeight());

+	}

+

+	@Override

+	public void paint(final LayoutContext context, final int x, final int y) {

+		if (skipPaint(context, x, y))

+			return;

+		context.getGraphics().drawImage(image, x, y, getWidth(), getHeight());

+		super.paint(context, x, y);

+	}

+

+	public int getBaseline() {

+		return 0;

+	}

+

+	@Override

+	public void alignOnBaseline(final int baseline) {

+		setY(0);

+	}

+

+	public boolean isEOL() {

+		return false;

+	}

+

+	public Pair split(final LayoutContext context, final int maxWidth, final boolean force) {

+		return new Pair(null, this);

+	}

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineBox.java
new file mode 100644
index 0000000..2958a51
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineBox.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+/**
+ * Represents an inline box. Inline boxes are the children of line boxes.
+ */
+public interface InlineBox extends Box {
+
+	/**
+	 * Represents a pair of inline boxes as returned by the <code>split</code>
+	 * method.
+	 */
+	public class Pair {
+		private InlineBox left;
+		private InlineBox right;
+
+		/**
+		 * Class constructor.
+		 * 
+		 * @param left
+		 *            box to the left of the split
+		 * @param right
+		 *            box to the right of the split
+		 */
+		public Pair(InlineBox left, InlineBox right) {
+			this.left = left;
+			this.right = right;
+		}
+
+		/**
+		 * Returns the box to the left of the split.
+		 */
+		public InlineBox getLeft() {
+			return this.left;
+		}
+
+		/**
+		 * Returns the box to the right of the split.
+		 */
+		public InlineBox getRight() {
+			return this.right;
+		}
+	}
+
+	/**
+	 * Returns the distance from the top of the inline box to the baseline.
+	 */
+	public int getBaseline();
+
+	/**
+	 * Aligns this inline box on the given baseline.
+	 */
+	public void alignOnBaseline(int baseline);
+	
+	/**
+	 * Returns true if this inline box must be the last box on the current line.
+	 */
+	public boolean isEOL();
+
+	/**
+	 * Splits this inline box into two. If <code>force</code> is false, this
+	 * method should find a natural split point (e.g. after a space) and return
+	 * two boxes representing a split at that point. The width of the last box
+	 * must not exceed <code>maxWidth</code>. If no such natural split exists,
+	 * null should be returned as the left box and <code>this</code> returned as
+	 * the right box.
+	 * 
+	 * <p>
+	 * If <code>force</code> is true, it means we are adding the first inline
+	 * box to a line, therefore we must return something as the left box. In
+	 * some cases, we may find a suboptimal split (e.g. between characters) that
+	 * satisfies this. In other cases, <code>this</code> should be returned as
+	 * the left box even though it exceeds maxWidth.
+	 * </p>
+	 * 
+	 * <p>
+	 * If the entire box fits within <code>maxWidth</code>, it should only be
+	 * returned as the left box if it can end a line; otherwise, it should be
+	 * returned as the right box. Most implementations <i>cannot</i> end a line
+	 * (one notable exception being a text box ending in whitespace) and should
+	 * therefore return themselves as the right box.
+	 * </p>
+	 * 
+	 * @param context
+	 *            the layout context to be used.
+	 * @param maxWidth
+	 *            Maximum width of the left part of the box.
+	 * @param force
+	 *            if true, force a suboptimal split
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineElementBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineElementBox.java
new file mode 100644
index 0000000..15755d0
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/InlineElementBox.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Drawable;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.eclipse.wst.xml.vex.core.internal.dom.Node;
+import org.eclipse.wst.xml.vex.core.internal.dom.Text;
+
+/**
+ * An inline box that represents an inline element. This box is responsible for
+ * creating and laying out its child boxes.
+ */
+public class InlineElementBox extends CompositeInlineBox {
+
+	private Element element;
+	private InlineBox[] children;
+	private InlineBox firstContentChild = null;
+	private InlineBox lastContentChild = null;
+	private int baseline;
+	private int halfLeading;
+
+	/**
+	 * Class constructor, called by the createInlineBoxes static factory method.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param element
+	 *            Element that generated this box
+	 * @param startOffset
+	 *            Start offset of the range being rendered, which may be
+	 *            arbitrarily before or inside the element.
+	 * @param endOffset
+	 *            End offset of the range being rendered, which may be
+	 *            arbitrarily after or inside the element.
+	 */
+	private InlineElementBox(LayoutContext context, Element element,
+			int startOffset, int endOffset) {
+
+		this.element = element;
+
+		List<InlineBox> childList = new ArrayList<InlineBox>();
+
+		final Styles styles = context.getStyleSheet().getStyles(element);
+
+		if (startOffset <= element.getStartOffset()) {
+
+			// space for the left margin/border/padding
+			int space = styles.getMarginLeft().get(0)
+					+ styles.getBorderLeftWidth()
+					+ styles.getPaddingLeft().get(0);
+
+			if (space > 0) {
+				childList.add(new SpaceBox(space, 1));
+			}
+
+			// :before content
+			Element beforeElement = context.getStyleSheet().getBeforeElement(
+					element);
+			if (beforeElement != null) {
+				childList.addAll(LayoutUtils.createGeneratedInlines(context,
+						beforeElement));
+			}
+
+			// left marker
+			childList.add(createLeftMarker(element, styles));
+		}
+
+		// background image
+		if (styles.hasBackgroundImage() && !styles.getDisplay().equalsIgnoreCase(CSS.NONE)) {
+			final ImageBox imageBox = ImageBox.createWithHeight(getElement(), context, styles.getLineHeight());
+			if (imageBox != null)
+				childList.add(imageBox);
+		}
+
+		InlineBoxes inlines = createInlineBoxes(context, element, startOffset,
+				endOffset);
+		childList.addAll(inlines.boxes);
+		this.firstContentChild = inlines.firstContentBox;
+		this.lastContentChild = inlines.lastContentBox;
+
+		if (endOffset > element.getEndOffset()) {
+
+			childList.add(new PlaceholderBox(context, element, element
+					.getEndOffset()
+					- element.getStartOffset()));
+
+			// trailing marker
+			childList.add(createRightMarker(element, styles));
+
+			// :after content
+			Element afterElement = context.getStyleSheet().getAfterElement(
+					element);
+			if (afterElement != null) {
+				childList.addAll(LayoutUtils.createGeneratedInlines(context,
+						afterElement));
+			}
+
+			// space for the right margin/border/padding
+			int space = styles.getMarginRight().get(0)
+					+ styles.getBorderRightWidth()
+					+ styles.getPaddingRight().get(0);
+
+			if (space > 0) {
+				childList.add(new SpaceBox(space, 1));
+			}
+		}
+
+		this.children = childList.toArray(new InlineBox[childList.size()]);
+		layout(context);
+	}
+
+	/**
+	 * Class constructor. This constructor is called by the split method.
+	 * 
+	 * @param context
+	 *            LayoutContext used for the layout.
+	 * @param element
+	 *            Element to which this box applies.
+	 * @param children
+	 *            Child boxes.
+	 */
+	private InlineElementBox(LayoutContext context, Element element,
+			InlineBox[] children) {
+		this.element = element;
+		this.children = children;
+		layout(context);
+		for (int i = 0; i < children.length; i++) {
+			InlineBox child = children[i];
+			if (child.hasContent()) {
+				if (this.firstContentChild == null) {
+					this.firstContentChild = child;
+				}
+				this.lastContentChild = child;
+			}
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return this.baseline;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getChildren()
+	 */
+	public Box[] getChildren() {
+		return this.children;
+	}
+
+	/**
+	 * Returns the element associated with this box.
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		if (this.lastContentChild == null) {
+			return this.getElement().getEndOffset();
+		} else {
+			return this.lastContentChild.getEndOffset();
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		if (this.firstContentChild == null) {
+			return this.getElement().getStartOffset();
+		} else {
+			return this.firstContentChild.getStartOffset();
+		}
+	}
+
+	/**
+	 * Override to paint background and borders.
+	 * 
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.AbstractBox#paint(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+		this.drawBox(context, x, y, 0, true); // TODO CSS violation
+		super.paint(context, x, y);
+	}
+
+	public Pair split(LayoutContext context, InlineBox[] lefts,
+			InlineBox[] rights) {
+
+		InlineElementBox left = null;
+		InlineElementBox right = null;
+
+		if (lefts.length > 0 || rights.length == 0) {
+			left = new InlineElementBox(context, this.getElement(), lefts);
+		}
+
+		if (rights.length > 0) {
+			right = new InlineElementBox(context, this.getElement(), rights);
+		}
+
+		return new Pair(left, right);
+	}
+
+	public String toString() {
+		StringBuffer sb = new StringBuffer();
+		if (this.getStartOffset() == this.getElement().getStartOffset() + 1) {
+			sb.append("<");
+			sb.append(this.getElement().getPrefixedName());
+			sb.append(">");
+		}
+		Box[] children = this.getChildren();
+		for (int i = 0; i < children.length; i++) {
+			sb.append(children[i]);
+		}
+		if (this.getEndOffset() == this.getElement().getEndOffset()) {
+			sb.append("</");
+			sb.append(this.getElement().getPrefixedName());
+			sb.append(">");
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Holds the results of the createInlineBoxes method.
+	 */
+	static class InlineBoxes {
+
+		/** List of generated boxes */
+		public List<InlineBox> boxes = new ArrayList<InlineBox>();
+
+		/** First generated box that has content */
+		public InlineBox firstContentBox;
+
+		/** Last generated box that has content */
+		public InlineBox lastContentBox;
+	}
+
+	/**
+	 * Creates a list of inline boxes given a range of offsets. This method is
+	 * used when creating both ParagraphBoxes and InlineElementBoxes.
+	 * 
+	 * @param context
+	 *            LayoutContext to be used.
+	 * @param element2
+	 *            Element containing both offsets
+	 * @param startOffset
+	 *            The start of the range to convert to inline boxes.
+	 * @param endOffset
+	 *            The end of the range to convert to inline boxes.
+	 * @return
+	 */
+	static InlineBoxes createInlineBoxes(LayoutContext context,
+			Element element2, int startOffset, int endOffset) {
+
+		InlineBoxes result = new InlineBoxes();
+
+		List<Node> nodes = element2.getChildNodes();
+		for (int i = 0; i < nodes.size(); i++) {
+
+			Node node = nodes.get(i);
+			InlineBox child;
+ 
+			if (node.getStartOffset() >= endOffset) {
+				break;
+			} else if (node instanceof Text) {
+
+				// This check is different for Text and Element, so we have to
+				// do it here and below, too.
+				if (node.getEndOffset() <= startOffset) {
+					continue;
+				}
+
+				int start = Math.max(startOffset, node.getStartOffset());
+				int end = Math.min(endOffset, node.getEndOffset());
+				child = new DocumentTextBox(context, element2, start,
+						end);
+
+			} else {
+
+				if (node.getEndOffset() < startOffset) {
+					continue;
+				}
+
+				Element childElement = (Element) node;
+				InlineBox placeholder = new PlaceholderBox(context,
+						element2, childElement.getStartOffset()
+								- element2.getStartOffset());
+				result.boxes.add(placeholder);
+				if (result.firstContentBox == null) {
+					result.firstContentBox = placeholder;
+				}
+				child = new InlineElementBox(context, childElement,
+						startOffset, endOffset);
+			}
+
+			if (result.firstContentBox == null) {
+				result.firstContentBox = child;
+			}
+
+			result.lastContentBox = child;
+
+			result.boxes.add(child);
+		}
+
+		return result;
+	}
+
+	// ========================================================== PRIVATE
+
+	private static InlineBox createLeftMarker(Element element, Styles styles) {
+		final int size = Math.round(0.5f * styles.getFontSize());
+		final int lift = Math.round(0.1f * styles.getFontSize());
+		Drawable drawable = new Drawable() {
+			public void draw(Graphics g, int x, int y) {
+				g.setLineStyle(Graphics.LINE_SOLID);
+				g.setLineWidth(1);
+				y -= lift;
+				g.drawLine(x, y - size, x, y);
+				g.drawLine(x, y, x + size - 1, y - size / 2);
+				g.drawLine(x + size - 1, y - size / 2, x, y - size);
+			}
+
+			public Rectangle getBounds() {
+				return new Rectangle(0, -size, size, size);
+			}
+		};
+		return new DrawableBox(drawable, element, DrawableBox.START_MARKER);
+	}
+
+	private static InlineBox createRightMarker(Element element, Styles styles) {
+		final int size = Math.round(0.5f * styles.getFontSize());
+		final int lift = Math.round(0.1f * styles.getFontSize());
+		Drawable drawable = new Drawable() {
+			public void draw(Graphics g, int x, int y) {
+				g.setLineStyle(Graphics.LINE_SOLID);
+				g.setLineWidth(1);
+				y -= lift;
+				g.drawLine(x + size - 1, y - size, x + size - 1, y);
+				g.drawLine(x + size - 1, y, x, y - size / 2);
+				g.drawLine(x, y - size / 2, x + size - 1, y - size);
+			}
+
+			public Rectangle getBounds() {
+				return new Rectangle(0, -size, size, size);
+			}
+		};
+		return new DrawableBox(drawable, element, DrawableBox.END_MARKER);
+	}
+
+	private void layout(LayoutContext context) {
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(element);
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		FontMetrics fm = g.getFontMetrics();
+		this.setHeight(styles.getLineHeight());
+		this.halfLeading = (styles.getLineHeight() - fm.getAscent() - fm
+				.getDescent()) / 2;
+		this.baseline = this.halfLeading + fm.getAscent();
+		g.setFont(oldFont);
+		font.dispose();
+
+		int x = 0;
+		for (int i = 0; i < this.children.length; i++) {
+			InlineBox child = this.children[i];
+			// TODO: honour the child's vertical-align property
+			child.setX(x);
+			child.alignOnBaseline(baseline);
+			x += child.getWidth();
+		}
+
+		this.setWidth(x);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutContext.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutContext.java
new file mode 100644
index 0000000..84691fc
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutContext.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.StyleSheet;
+import org.eclipse.wst.xml.vex.core.internal.dom.Document;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Encapsulation of all the resources needed to create a box tree. Most
+ * operations on a box tree, such as creating the tree, painting the tree, and
+ * converting between spatial and model coordinates, require the context.
+ */
+public class LayoutContext {
+
+	private BoxFactory boxFactory;
+	private Document document;
+	private Graphics graphics;
+	private StyleSheet styleSheet;
+	private int selectionStart;
+	private int selectionEnd;
+	private long startTime = System.currentTimeMillis();
+
+	/**
+	 * Class constructor.
+	 */
+	public LayoutContext() {
+	}
+
+	/**
+	 * Returns the BoxFactory used to generate boxes for the layout.
+	 */
+	public BoxFactory getBoxFactory() {
+		return boxFactory;
+	}
+
+	/**
+	 * Returns the document being layed out.
+	 */
+	public Document getDocument() {
+		return document;
+	}
+
+	/**
+	 * Returns the <code>Graphics</code> object used for layout. Box paint
+	 * methods use this graphics for painting.
+	 */
+	public Graphics getGraphics() {
+		return this.graphics;
+	}
+
+	/**
+	 * Returns the time the layout was started. Actually, it's the time since
+	 * this context was created, as returned by System.currentTimeMills().
+	 */
+	public long getStartTime() {
+		return startTime;
+	}
+
+	/**
+	 * Returns the <code>StyleSheet</code> used for this layout.
+	 */
+	public StyleSheet getStyleSheet() {
+		return this.styleSheet;
+	}
+
+	/**
+	 * Helper method that returns true if the given element is in the selected
+	 * range.
+	 * 
+	 * @param element
+	 *            Element to test. May be null, in which case this method
+	 *            returns false.
+	 */
+	public boolean isElementSelected(Element element) {
+		return element != null
+				&& element.getStartOffset() >= this.getSelectionStart()
+				&& element.getEndOffset() + 1 <= this.getSelectionEnd();
+	}
+
+	/**
+	 * Resets the start time to currentTimeMillis.
+	 */
+	public void resetStartTime() {
+		this.startTime = System.currentTimeMillis();
+	}
+
+	/**
+	 * Sets the BoxFactory used to generate boxes for this layout.
+	 */
+	public void setBoxFactory(BoxFactory factory) {
+		boxFactory = factory;
+	}
+
+	/**
+	 * Sets the document being layed out.
+	 */
+	public void setDocument(Document document) {
+		this.document = document;
+	}
+
+	/**
+	 * Sets the Graphics object used for this layout.
+	 */
+	public void setGraphics(Graphics graphics) {
+		this.graphics = graphics;
+	}
+
+	/**
+	 * Sets the stylesheet used for this layout.
+	 */
+	public void setStyleSheet(StyleSheet sheet) {
+		styleSheet = sheet;
+	}
+
+	/**
+	 * Returns the offset where the current selection ends.
+	 */
+	public int getSelectionEnd() {
+		return selectionEnd;
+	}
+
+	/**
+	 * Returns the offset where the current selection starts.
+	 */
+	public int getSelectionStart() {
+		return selectionStart;
+	}
+
+	/**
+	 * Sets the offset where the current selection ends.
+	 * 
+	 * @param i
+	 *            the new value for selectionEnd
+	 */
+	public void setSelectionEnd(int i) {
+		selectionEnd = i;
+	}
+
+	/**
+	 * Sets the offset where the current selection starts.
+	 * 
+	 * @param i
+	 *            the new value for selectionStart
+	 */
+	public void setSelectionStart(int i) {
+		selectionStart = i;
+	}
+	
+	public URL resolveUrl(final String baseUri, final String urlSpecification) {
+		try {
+			if (baseUri == null)
+				return new URL(urlSpecification);
+			else
+				return new URL(new URL(baseUri), urlSpecification);
+		} catch (MalformedURLException e) {
+			VEXCorePlugin.getInstance().getLog().log(new Status(IStatus.ERROR, VEXCorePlugin.ID, MessageFormat.format("Cannot resolve image url: {0}", urlSpecification), e));
+			return null;
+		}
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutUtils.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutUtils.java
new file mode 100644
index 0000000..1dfff84
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LayoutUtils.java
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.StyleSheet;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+import org.eclipse.wst.xml.vex.core.internal.dom.Node;
+
+/**
+ * Tools for layout and rendering of CSS-styled boxes
+ */
+public class LayoutUtils {
+
+	/**
+	 * Creates a list of generated inline boxes for the given pseudo-element.
+	 * 
+	 * @param context
+	 *            LayoutContext in use
+	 * @param pseudoElement
+	 *            Element representing the generated content.
+	 */
+	public static List<InlineBox> createGeneratedInlines(LayoutContext context,
+			Element pseudoElement) {
+		String text = getGeneratedContent(context, pseudoElement);
+		List<InlineBox> list = new ArrayList<InlineBox>();
+		if (text.length() > 0) {
+			list.add(new StaticTextBox(context, pseudoElement, text));
+		}
+		return list;
+	}
+
+	/**
+	 * Returns <code>true</code> if the given offset falls within the given
+	 * element or range.
+	 * 
+	 * @param elementOrRange
+	 *            Element or IntRange object representing a range of offsets.
+	 * @param offset
+	 *            Offset to test.
+	 */
+	public static boolean elementOrRangeContains(Object elementOrRange,
+			int offset) {
+		if (elementOrRange instanceof Element) {
+			Element element = (Element) elementOrRange;
+			return offset > element.getStartOffset()
+					&& offset <= element.getEndOffset();
+		} else {
+			IntRange range = (IntRange) elementOrRange;
+			return offset >= range.getStart() && offset <= range.getEnd();
+		}
+	}
+
+	/**
+	 * Creates a string representing the generated content for the given
+	 * pseudo-element.
+	 * 
+	 * @param context
+	 *            LayoutContext in use
+	 * @param pseudoElement
+	 *            PseudoElement for which the generated content is to be
+	 *            returned.
+	 */
+	private static String getGeneratedContent(LayoutContext context,
+			Element pseudoElement) {
+		Styles styles = context.getStyleSheet().getStyles(pseudoElement);
+		List<String> content = styles.getContent();
+		StringBuffer sb = new StringBuffer();
+		for (String string : content) {
+			sb.append(string); // TODO: change to ContentPart
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Call the given callback for each child matching one of the given display
+	 * styles. Any nodes that do not match one of the given display types cause
+	 * the onRange callback to be called, with a range covering all such
+	 * contiguous nodes.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param displayStyles
+	 *            Display types to be explicitly recognized.
+	 * @param element
+	 *            Element containing the children over which to iterate.
+	 * @param startOffset
+	 *            Starting offset of the range containing nodes in which we're
+	 *            interested.
+	 * @param endOffset
+	 *            Ending offset of the range containing nodes in which we're
+	 *            interested.
+	 * @param callback
+	 *            DisplayStyleCallback through which the caller is notified of
+	 *            matching elements and non-matching ranges.
+	 */
+	public static void iterateChildrenByDisplayStyle(StyleSheet styleSheet,
+			                                         Set<String> displayStyles,
+			                                         Element element,
+			                                         int startOffset,
+			                                         int endOffset,
+			                                         ElementOrRangeCallback callback) {
+
+		List<Node> nonMatching = new ArrayList<Node>();
+
+		List<Node> nodes = element.getChildNodes();
+		for (int i = 0; i < nodes.size(); i++) {
+			if (nodes.get(i).getEndOffset() <= startOffset) {
+				continue;
+			} else if (nodes.get(i).getStartOffset() >= endOffset) {
+				break;
+			} else {
+				Node node = nodes.get(i);
+
+				if (node instanceof Element) {
+					Element childElement = (Element) node;
+					String display = styleSheet.getStyles(childElement)
+							.getDisplay();
+					if (displayStyles.contains(display)) {
+						if (!nonMatching.isEmpty()) {
+							Node firstNode = (Node) nonMatching.get(0);
+							Node lastNode = (Node) nonMatching.get(nonMatching
+									.size() - 1);
+							if (lastNode instanceof Element) {
+								callback.onRange(element, firstNode
+										.getStartOffset(), lastNode
+										.getEndOffset() + 1);
+							} else {
+								callback.onRange(element, firstNode
+										.getStartOffset(), lastNode
+										.getEndOffset());
+							}
+							nonMatching.clear();
+						}
+						callback.onElement(childElement, display);
+					} else {
+						nonMatching.add(node);
+					}
+				} else {
+					nonMatching.add(node);
+				}
+			}
+		}
+
+		if (!nonMatching.isEmpty()) {
+			Node firstNode = (Node) nonMatching.get(0);
+			Node lastNode = (Node) nonMatching.get(nonMatching.size() - 1);
+			if (lastNode instanceof Element) {
+				callback.onRange(element, firstNode.getStartOffset(), lastNode
+						.getEndOffset() + 1);
+			} else {
+				callback.onRange(element, firstNode.getStartOffset(), lastNode
+						.getEndOffset());
+			}
+		}
+	}
+
+	/**
+	 * Call the given callback for each child matching one of the given display
+	 * styles. Any nodes that do not match one of the given display types cause
+	 * the onRange callback to be called, with a range covering all such
+	 * contiguous nodes.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param displayStyles
+	 *            Display types to be explicitly recognized.
+	 * @param table
+	 *            Element containing the children over which to iterate.
+	 * @param callback
+	 *            DisplayStyleCallback through which the caller is notified of
+	 *            matching elements and non-matching ranges.
+	 */
+	public static void iterateChildrenByDisplayStyle(StyleSheet styleSheet,
+			                                         Set<String> displayStyles,
+			                                         Element table,
+			                                         ElementOrRangeCallback callback) {
+		iterateChildrenByDisplayStyle(styleSheet, displayStyles, table,
+				table.getStartOffset() + 1, table.getEndOffset(), callback);
+	}
+
+	/**
+	 * Returns true if the given styles represent an element that can be the
+	 * child of a table element.
+	 * 
+	 * @param styleSheet
+	 *            StyleSheet to use.
+	 * @param element
+	 *            Element to test.
+	 */
+	public static boolean isTableChild(StyleSheet styleSheet, Element element) {
+		String display = styleSheet.getStyles(element).getDisplay();
+		return TABLE_CHILD_STYLES.contains(display);
+	}
+
+	public static void iterateTableRows(final StyleSheet styleSheet,
+			final Element element, int startOffset, int endOffset,
+			final ElementOrRangeCallback callback) {
+
+		iterateChildrenByDisplayStyle(styleSheet, NON_ROW_STYLES, element,
+				startOffset, endOffset, new ElementOrRangeCallback() {
+					public void onElement(Element child, String displayStyle) {
+						if (displayStyle.equals(CSS.TABLE_ROW_GROUP)
+								|| displayStyle.equals(CSS.TABLE_HEADER_GROUP)
+								|| displayStyle.equals(CSS.TABLE_FOOTER_GROUP)) {
+
+							// iterate rows in group
+							iterateChildrenByDisplayStyle(styleSheet,
+									ROW_STYLES, child,
+									child.getStartOffset() + 1, child
+											.getEndOffset(), callback);
+						} else {
+							// other element's can't contain rows
+						}
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						// iterate over rows in range
+						iterateChildrenByDisplayStyle(styleSheet, ROW_STYLES,
+								element, startOffset, endOffset, callback);
+					}
+				});
+
+	}
+
+	public static void iterateTableCells(StyleSheet styleSheet,
+			Element element, int startOffset, int endOffset,
+			final ElementOrRangeCallback callback) {
+		iterateChildrenByDisplayStyle(styleSheet, CELL_STYLES, element,
+				startOffset, endOffset, callback);
+	}
+
+	public static void iterateTableCells(StyleSheet styleSheet,
+			Element row, final ElementOrRangeCallback callback) {
+		iterateChildrenByDisplayStyle(styleSheet, CELL_STYLES, row, row
+				.getStartOffset(), row.getEndOffset(), callback);
+	}
+
+	/**
+	 * Set of CSS display values that represent elements that can be children of
+	 * table elements.
+	 */
+	public static final Set<String> TABLE_CHILD_STYLES = new HashSet<String>();
+
+	private static final Set<String> NON_ROW_STYLES = new HashSet<String>();
+	private static final Set<String> ROW_STYLES = new HashSet<String>();
+	private static final Set<String> CELL_STYLES = new HashSet<String>();
+
+	static {
+		NON_ROW_STYLES.add(CSS.TABLE_CAPTION);
+		NON_ROW_STYLES.add(CSS.TABLE_COLUMN);
+		NON_ROW_STYLES.add(CSS.TABLE_COLUMN_GROUP);
+		NON_ROW_STYLES.add(CSS.TABLE_ROW_GROUP);
+		NON_ROW_STYLES.add(CSS.TABLE_HEADER_GROUP);
+		NON_ROW_STYLES.add(CSS.TABLE_FOOTER_GROUP);
+
+		ROW_STYLES.add(CSS.TABLE_ROW);
+
+		CELL_STYLES.add(CSS.TABLE_CELL);
+
+		TABLE_CHILD_STYLES.addAll(NON_ROW_STYLES);
+		TABLE_CHILD_STYLES.addAll(ROW_STYLES);
+		TABLE_CHILD_STYLES.addAll(CELL_STYLES);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LineBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LineBox.java
new file mode 100644
index 0000000..f1410f7
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/LineBox.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Represents a line of text and inline images.
+ */
+public class LineBox extends CompositeInlineBox {
+
+	private Element element;
+	private InlineBox[] children;
+	private InlineBox firstContentChild = null;
+	private InlineBox lastContentChild = null;
+	private int baseline;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param context
+	 *            LayoutContext for this layout.
+	 * @param children
+	 *            InlineBoxes that make up this line.
+	 */
+	public LineBox(LayoutContext context, Element element, InlineBox[] children) {
+
+		this.element = element;
+		this.children = children;
+
+		int height = 0;
+		int x = 0;
+		this.baseline = 0;
+		for (int i = 0; i < children.length; i++) {
+			InlineBox child = children[i];
+			child.setX(x);
+			child.setY(0); // TODO: do proper vertical alignment
+			this.baseline = Math.max(this.baseline, child.getBaseline());
+			x += child.getWidth();
+			height = Math.max(height, child.getHeight());
+			if (child.hasContent()) {
+				if (this.firstContentChild == null) {
+					this.firstContentChild = child;
+				}
+				this.lastContentChild = child;
+			}
+		}
+
+		this.setHeight(height);
+		this.setWidth(x);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return this.baseline;
+	}
+
+	public Box[] getChildren() {
+		return this.children;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getElement()
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		return this.lastContentChild.getEndOffset();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		return this.firstContentChild.getStartOffset();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#hasContent()
+	 */
+	public boolean hasContent() {
+		return this.firstContentChild != null;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.CompositeInlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      org.eclipse.wst.xml.vex.core.internal.layout.InlineBox[],
+	 *      org.eclipse.wst.xml.vex.core.internal.layout.InlineBox[])
+	 */
+	public Pair split(LayoutContext context, InlineBox[] lefts,
+			InlineBox[] rights) {
+
+		LineBox left = null;
+		LineBox right = null;
+
+		if (lefts.length > 0) {
+			left = new LineBox(context, this.getElement(), lefts);
+		}
+
+		if (rights.length > 0) {
+			right = new LineBox(context, this.getElement(), rights);
+		}
+
+		return new Pair(left, right);
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		Box[] children = this.getChildren();
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < children.length; i++) {
+			sb.append(children[i]);
+		}
+		return sb.toString();
+	}
+
+	// ========================================================== PRIVATE
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ParagraphBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ParagraphBox.java
new file mode 100644
index 0000000..82c1ddf
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/ParagraphBox.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * A box that wraps inline content into a paragraph.
+ */
+public class ParagraphBox extends AbstractBox implements BlockBox {
+
+	private LineBox[] children;
+	private LineBox firstContentLine;
+	private LineBox lastContentLine;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param children
+	 *            Line boxes that comprise the paragraph.
+	 */
+	private ParagraphBox(LineBox[] children) {
+		this.children = children;
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].hasContent()) {
+				if (this.firstContentLine == null) {
+					this.firstContentLine = children[i];
+				}
+				this.lastContentLine = children[i];
+			}
+		}
+	}
+
+	/**
+	 * Create a paragraph by word-wrapping a list of inline boxes.
+	 * 
+	 * @param context
+	 *            LayoutContext used for this layout.
+	 * @param element
+	 *            Element that controls the styling for this paragraph.
+	 * @param inlines
+	 *            List of InlineBox objects to be wrapped
+	 * @param width
+	 *            width to which the paragraph is to be wrapped
+	 */
+	public static ParagraphBox create(LayoutContext context, Element element,
+			List<InlineBox> inlines, int width) {
+		InlineBox[] array = inlines.toArray(new InlineBox[inlines.size()]);
+		return create(context, element, array, width);
+	}
+
+	/**
+	 * Create a paragraph by word-wrapping a list of inline boxes.
+	 * 
+	 * @param context
+	 *            LayoutContext used for this layout
+	 * @param element
+	 *            Element that controls the styling of this paragraph, in
+	 *            particular text alignment.
+	 * @param inlines
+	 *            Array of InlineBox objects to be wrapped.
+	 * @param width
+	 *            width to which the paragraph is to be wrapped.
+	 */
+	public static ParagraphBox create(LayoutContext context, Element element,
+			InlineBox[] inlines, int width) {
+
+		// lines is the list of LineBoxes we are creating
+		List<Box> lines = new ArrayList<Box>();
+
+		InlineBox right = new LineBox(context, element, inlines);
+
+		while (right != null) {
+			InlineBox.Pair pair = right.split(context, width, true);
+			//FIXME icampist this indicates some design problem, since later on, LineBoxes are expected
+			//either we cast here to LineBox or we make an if later on.
+			lines.add(pair.getLeft());
+			right = pair.getRight();
+		}
+
+		Styles styles = context.getStyleSheet().getStyles(element);
+		String textAlign = styles.getTextAlign();
+
+		// y-offset of the next line
+		int y = 0;
+
+		int actualWidth = 0;
+
+		//children for the ParagraphBox constructor that accepts only LineBoxes
+		List<LineBox> lineBoxesChildren = new ArrayList<LineBox>();
+
+		for (Box lineBox : lines) {
+			int x;
+			if (textAlign.equals(CSS.RIGHT)) {
+				x = width - lineBox.getWidth();
+			} else if (textAlign.equals(CSS.CENTER)) {
+				x = (width - lineBox.getWidth()) / 2;
+			} else {
+				x = 0;
+			}
+
+			lineBox.setX(x);
+			lineBox.setY(y);
+
+			y += lineBox.getHeight();
+			actualWidth = Math.max(actualWidth, lineBox.getWidth());
+			
+			//strange, but we need to check the case because it's not explicit anywhere
+			if (lineBox instanceof LineBox) {
+				lineBoxesChildren.add((LineBox) lineBox);
+			}
+		}
+		
+		ParagraphBox para = new ParagraphBox(lineBoxesChildren.toArray(new LineBox[lineBoxesChildren.size()]));
+		para.setWidth(actualWidth);
+		para.setHeight(y);
+
+		// BlockElementBox uses a scaling factor to estimate box height based
+		// on font size, layout width, and character count, as follows.
+		//
+		// estHeight = factor * fontSize * fontSize * charCount / width
+		//
+		// This bit reports the actual factor that would correctly estimate
+		// the height of a BlockElementBox containing only this paragraph.
+		//
+		// factor = estHeight * width / (fontSize * fontSize * charCount)
+		//
+		/*
+		 * Box firstContentBox = null; for (int i = 0; i < inlines.length; i++)
+		 * { Box box = inlines[i]; if (box.hasContent()) { firstContentBox =
+		 * box; break; } }
+		 * 
+		 * if (firstContentBox != null) { float fontSize = styles.getFontSize();
+		 * int charCount = lastContentBox.getEndOffset() -
+		 * firstContentBox.getStartOffset(); float factor = para.getHeight()
+		 * para.getWidth() / (fontSize fontSize charCount);
+		 * System.out.println("Actual factor is " + factor); }
+		 */
+
+		return para;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+
+		LineBox line = this.getLineAt(offset);
+		Caret caret = line.getCaret(context, offset);
+		caret.translate(line.getX(), line.getY());
+		return caret;
+
+	}
+
+	public Box[] getChildren() {
+		return this.children;
+	}
+
+	public int getEndOffset() {
+		return this.lastContentLine.getEndOffset();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getFirstLine()
+	 */
+	public LineBox getFirstLine() {
+		if (this.children.length == 0) {
+			return null;
+		} else {
+			return this.children[0];
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getLastLine()
+	 */
+	public LineBox getLastLine() {
+		if (this.children.length == 0) {
+			return null;
+		} else {
+			return this.children[this.children.length - 1];
+		}
+	}
+
+	/**
+	 * Returns the LineBox at the given offset.
+	 * 
+	 * @param offset
+	 *            the offset to check.
+	 */
+	public LineBox getLineAt(int offset) {
+		LineBox[] children = this.children;
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].hasContent()
+					&& offset <= children[i].getEndOffset()) {
+				return children[i];
+			}
+		}
+		return this.lastContentLine;
+	}
+
+	public int getLineEndOffset(int offset) {
+		return this.getLineAt(offset).getEndOffset();
+	}
+
+	public int getLineStartOffset(int offset) {
+		return this.getLineAt(offset).getStartOffset();
+	}
+
+	public int getMarginBottom() {
+		return 0;
+	}
+
+	public int getMarginTop() {
+		return 0;
+	}
+
+	public int getNextLineOffset(LayoutContext context, int offset, int x) {
+		LineBox nextLine = null;
+		LineBox[] children = this.children;
+		for (int i = 0; i < children.length; i++) {
+			if (children[i].hasContent()
+					&& children[i].getStartOffset() > offset) {
+				nextLine = children[i];
+				break;
+			}
+		}
+		if (nextLine == null) {
+			// return this.getEndOffset() + 1;
+			return -1;
+		} else {
+			return nextLine.viewToModel(context, x - nextLine.getX(), 0);
+		}
+	}
+
+	public BlockBox getParent() {
+		throw new IllegalStateException(
+				"ParagraphBox does not currently track parent");
+	}
+
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x) {
+		LineBox prevLine = null;
+		LineBox[] children = this.children;
+		for (int i = children.length - 1; i >= 0; i--) {
+			if (children[i].hasContent() && children[i].getEndOffset() < offset) {
+				prevLine = children[i];
+				break;
+			}
+		}
+		if (prevLine == null) {
+			// return this.getStartOffset() - 1;
+			return -1;
+		} else {
+			return prevLine.viewToModel(context, x - prevLine.getX(), 0);
+		}
+	}
+
+	public int getStartOffset() {
+		return this.firstContentLine.getStartOffset();
+	}
+
+	public boolean hasContent() {
+		return this.firstContentLine != null;
+	}
+
+	public IntRange layout(LayoutContext context, int top, int bottom) {
+		return null;
+	}
+
+	public void invalidate(boolean direct) {
+		throw new IllegalStateException(
+				"invalidate called on a non-element BlockBox");
+	}
+
+	public void setInitialSize(LayoutContext context) {
+		// NOP - size calculated in factory method
+	}
+
+	public String toString() {
+		return "ParagraphBox";
+	}
+
+	public int viewToModel(LayoutContext context, int x, int y) {
+
+		LineBox[] children = this.children;
+		for (int i = 0; i < children.length; i++) {
+			Box child = children[i];
+			if (child.hasContent() && y <= child.getY() + child.getHeight()) {
+				return child.viewToModel(context, x - child.getX(), y
+						- child.getY());
+			}
+		}
+		throw new RuntimeException("No line at (" + x + ", " + y + ")");
+	}
+
+	// ===================================================== PRIVATE
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/PlaceholderBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/PlaceholderBox.java
new file mode 100644
index 0000000..48dd34a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/PlaceholderBox.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * A zero-width box that represents a single offset in the document.
+ */
+public class PlaceholderBox extends AbstractInlineBox {
+
+	private Element element;
+	private int relOffset;
+	private int textTop;
+	private int baseline;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param context
+	 *            LayoutContext in effect.
+	 * @param element2
+	 *            Element containing this placeholder. the element is used both
+	 *            to determine the size of the box and its caret, but also as a
+	 *            base point for relOffset.
+	 * @param relOffset
+	 *            Offset of the placeholder, relative to the start of the
+	 *            element.
+	 */
+	public PlaceholderBox(LayoutContext context, Element element2, int relOffset) {
+
+		this.element = element2;
+		this.relOffset = relOffset;
+
+		this.setWidth(0);
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(element2);
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		FontMetrics fm = g.getFontMetrics();
+		int height = fm.getAscent() + fm.getDescent();
+
+		int lineHeight = styles.getLineHeight();
+		this.textTop = (lineHeight - height) / 2;
+
+		this.baseline = this.textTop + fm.getAscent();
+		this.setHeight(lineHeight);
+		g.setFont(oldFont);
+		font.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return this.baseline;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, boolean)
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force) {
+		return new Pair(null, this);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+		return new TextCaret(0, this.textTop, this.baseline - this.textTop);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getElement()
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		return this.element.getStartOffset() + this.relOffset;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		return this.element.getStartOffset() + this.relOffset;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#hasContent()
+	 */
+	public boolean hasContent() {
+		return true;
+	}
+
+	public boolean isEOL() {
+		return false;
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "[placeholder(" + this.getStartOffset() + ")]";
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#viewToModel(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public int viewToModel(LayoutContext context, int x, int y) {
+		return this.getStartOffset();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/RootBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/RootBox.java
new file mode 100644
index 0000000..1ea4c8d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/RootBox.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.VEXCorePlugin;
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * A wrapper for the top level <code>BlockElementBox</code> that applies its
+ * margins.
+ */
+public class RootBox extends AbstractBox implements BlockBox {
+
+	private Element element;
+	private BlockElementBox childBox;
+	private Box[] children = new Box[1];
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param context
+	 *            LayoutContext used to create children.
+	 * @param element
+	 *            Element associated with this box.
+	 * @param width
+	 *            width of this box
+	 */
+	public RootBox(LayoutContext context, Element element, int width) {
+		this.element = element;
+		this.setWidth(width);
+
+		this.childBox = new BlockElementBox(context, this, this.element);
+
+		Insets insets = this.getInsets(context, this.getWidth());
+		this.childBox.setX(insets.getLeft());
+		this.childBox.setY(insets.getTop());
+		this.childBox.setInitialSize(context);
+		this.children[0] = this.childBox;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+		Caret caret = this.childBox.getCaret(context, offset);
+		caret.translate(this.childBox.getX(), this.childBox.getY());
+		return caret;
+	}
+
+	public Box[] getChildren() {
+		return this.children;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getElement()
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	public int getEndOffset() {
+		return this.childBox.getEndOffset();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getFirstLine()
+	 */
+	public LineBox getFirstLine() {
+		return this.childBox.getFirstLine();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.BlockBox#getLastLine()
+	 */
+	public LineBox getLastLine() {
+		return this.childBox.getLastLine();
+	}
+
+	public int getLineEndOffset(int offset) {
+		return this.childBox.getLineEndOffset(offset);
+	}
+
+	public int getLineStartOffset(int offset) {
+		return this.childBox.getLineStartOffset(offset);
+	}
+
+	public int getMarginBottom() {
+		return 0;
+	}
+
+	public int getMarginTop() {
+		return 0;
+	}
+
+	public int getNextLineOffset(LayoutContext context, int offset, int x) {
+		return childBox.getNextLineOffset(context, offset, x - childBox.getX());
+	}
+
+	public BlockBox getParent() {
+		throw new IllegalStateException("RootBox does not have a parent");
+	}
+
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x) {
+		return childBox.getPreviousLineOffset(context, offset, x
+				- childBox.getX());
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	public int getStartOffset() {
+		return this.childBox.getStartOffset();
+	}
+
+	public void invalidate(boolean direct) {
+		// do nothing. layout is always propagated to our child box.
+	}
+
+	public IntRange layout(LayoutContext context, int top, int bottom) {
+
+		Insets insets = this.getInsets(context, this.getWidth());
+
+		long start = 0;
+		if (VEXCorePlugin.getInstance().isDebugging()) {
+			start = System.currentTimeMillis();
+		}
+
+		IntRange repaintRange = this.childBox.layout(context, top
+				- insets.getTop(), bottom - insets.getBottom());
+
+		if (VEXCorePlugin.getInstance().isDebugging()) {
+			long end = System.currentTimeMillis();
+			if (end - start > 50) {
+				System.out.println("RootBox.layout took " + (end - start)
+						+ "ms");
+			}
+		}
+
+		this.setHeight(this.childBox.getHeight() + insets.getTop()
+				+ insets.getBottom());
+
+		if (repaintRange != null) {
+			return new IntRange(repaintRange.getStart() + this.childBox.getY(),
+					repaintRange.getEnd() + this.childBox.getY());
+		} else {
+			return null;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.wst.xml.vex.core.internal.layout.AbstractBox#viewToModel(
+	 * org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext, int, int)
+	 */
+	public int viewToModel(LayoutContext context, int x, int y) {
+		return this.childBox.viewToModel(context, x - this.childBox.getX(), y
+				- this.childBox.getY());
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+		Rectangle r = context.getGraphics().getClipBounds();
+		long start = System.currentTimeMillis();
+		super.paint(context, x, y);
+		long end = System.currentTimeMillis();
+		if (end - start > 50) {
+			System.out.println("RootBox.paint " + r.getHeight()
+					+ " pixel rows in " + (end - start) + "ms");
+		}
+	}
+
+	public void setInitialSize(LayoutContext context) {
+		throw new IllegalStateException();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/SpaceBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/SpaceBox.java
new file mode 100644
index 0000000..90b710d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/SpaceBox.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+/**
+ * An empty inline box that simply takes up space.
+ */
+public class SpaceBox extends AbstractInlineBox {
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param width
+	 *            width of the box
+	 * @param height
+	 *            height of the box
+	 */
+	public SpaceBox(int width, int height) {
+		this.setWidth(width);
+		this.setHeight(height);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return this.getHeight();
+	}
+
+	public boolean isEOL() {
+		return false;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, boolean)
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force) {
+		return new Pair(null, this);
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return "[spacer]";
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/StaticTextBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/StaticTextBox.java
new file mode 100644
index 0000000..00a410c
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/StaticTextBox.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * A TextBox representing a static string. Represents text which is not editable
+ * within the VexWidget, such as enumerated list markers.
+ */
+public class StaticTextBox extends TextBox {
+
+	public static final byte NO_MARKER = 0;
+	public static final byte START_MARKER = 1;
+	public static final byte END_MARKER = 2;
+
+	private String text;
+	private byte marker;
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param context
+	 *            LayoutContext used to calculate the box's size.
+	 * @param element
+	 *            Element used to style the text.
+	 * @param text
+	 *            Static text to display
+	 */
+	public StaticTextBox(LayoutContext context, Element element, String text) {
+		this(context, element, text, NO_MARKER);
+		if (text.length() == 0) {
+			throw new IllegalArgumentException(
+					"StaticTextBox cannot have an empty text string.");
+		}
+	}
+
+	/**
+	 * Class constructor. This constructor is used when generating a static text
+	 * box representing a marker for the start or end of an inline element. If
+	 * the selection spans the related marker, the text is drawn in the
+	 * platform's text selection colours.
+	 * 
+	 * @param context
+	 *            LayoutContext used to calculate the box's size
+	 * @param element
+	 *            Element used to style the text
+	 * @param text
+	 *            Static text to display
+	 * @param marker
+	 *            START_MARKER or END_MARKER, depending on whether the text
+	 *            represents the start sentinel or the end sentinel of the
+	 *            element
+	 */
+	public StaticTextBox(LayoutContext context, Element element, String text,
+			byte marker) {
+		super(element);
+		this.text = text;
+		this.marker = marker;
+		this.calculateSize(context);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.TextBox#getText()
+	 */
+	public String getText() {
+		return this.text;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#hasContent()
+	 */
+	public boolean hasContent() {
+		return false;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#paint(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	public void paint(LayoutContext context, int x, int y) {
+
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		Graphics g = context.getGraphics();
+
+		boolean drawSelected = false;
+		if (this.marker == START_MARKER) {
+			drawSelected = this.getElement().getStartOffset() >= context
+					.getSelectionStart()
+					&& this.getElement().getStartOffset() + 1 <= context
+							.getSelectionEnd();
+		} else if (this.marker == END_MARKER) {
+			drawSelected = this.getElement().getEndOffset() >= context
+					.getSelectionStart()
+					&& this.getElement().getEndOffset() + 1 <= context
+							.getSelectionEnd();
+		}
+
+		FontResource font = g.createFont(styles.getFont());
+		ColorResource color = g.createColor(styles.getColor());
+
+		FontResource oldFont = g.setFont(font);
+		ColorResource oldColor = g.setColor(color);
+
+		if (drawSelected) {
+			this.paintSelectedText(context, this.getText(), x, y);
+		} else {
+			g.drawString(this.getText(), x, y);
+		}
+		paintTextDecoration(context, styles, this.getText(), x, y);
+
+		g.setFont(oldFont);
+		g.setColor(oldColor);
+		font.dispose();
+		color.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.TextBox#splitAt(int)
+	 */
+	public Pair splitAt(LayoutContext context, int offset) {
+
+		StaticTextBox left;
+		if (offset == 0) {
+			left = null;
+		} else {
+			left = new StaticTextBox(context, this.getElement(), this.getText()
+					.substring(0, offset), this.marker);
+		}
+
+		StaticTextBox right;
+		if (offset == this.getText().length()) {
+			right = null;
+		} else {
+			right = new StaticTextBox(context, this.getElement(), this
+					.getText().substring(offset), this.marker);
+		}
+		return new Pair(left, right);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBodyBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBodyBox.java
new file mode 100644
index 0000000..98ba230
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBodyBox.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * An anonymous box that contains the table row groups for a table. This box is
+ * generated by a TableBox and assumes the margins and borders of the table
+ * element.
+ */
+public class TableBodyBox extends AbstractBlockBox {
+
+	public TableBodyBox(LayoutContext context, TableBox parent,
+			int startOffset, int endOffset) {
+		super(context, parent, startOffset, endOffset);
+	}
+
+	protected List<Box> createChildren(final LayoutContext context) {
+		// TODO Auto-generated method stub
+
+		// Walk children:
+		// each table-*-group gets a non-anonymous TableRowGroupBox
+		// runs of others get anonymous TableRowGroupBox
+
+		final List<Box> children = new ArrayList<Box>();
+
+		this.iterateChildrenByDisplayStyle(context.getStyleSheet(),
+				childDisplayStyles, new ElementOrRangeCallback() {
+					public void onElement(Element child, String displayStyle) {
+						children.add(new TableRowGroupBox(context,
+								TableBodyBox.this, child));
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						children.add(new TableRowGroupBox(context,
+								TableBodyBox.this, startOffset, endOffset));
+					}
+				});
+
+		return children;
+	}
+
+	/**
+	 * Return the insets of the parent box.
+	 */
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+		if (this.getParent().getElement() != null) {
+			Styles styles = context.getStyleSheet().getStyles(
+					this.getParent().getElement());
+			return AbstractBox.getInsets(styles, containerWidth);
+		} else {
+			return Insets.ZERO_INSETS;
+		}
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+		this.drawBox(context, this.getParent().getElement(), x, y, this
+				.getParent().getWidth(), true);
+		this.paintChildren(context, x, y);
+	}
+
+	// ======================================================== PRIVATE
+
+	private static Set<String> childDisplayStyles = new HashSet<String>();
+
+	static {
+		childDisplayStyles.add(CSS.TABLE_ROW_GROUP);
+		childDisplayStyles.add(CSS.TABLE_HEADER_GROUP);
+		childDisplayStyles.add(CSS.TABLE_FOOTER_GROUP);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBox.java
new file mode 100644
index 0000000..8824053
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableBox.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.core.IntRange;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.StyleSheet;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Box that lays out a table.
+ */
+public class TableBox extends AbstractBlockBox {
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param element
+	 *            Element represented by this box.
+	 */
+	public TableBox(LayoutContext context, BlockBox parent, Element element) {
+		super(context, parent, element);
+	}
+
+	public TableBox(LayoutContext context, BlockBox parent, int startOffset,
+			int endOffset) {
+		super(context, parent, startOffset, endOffset);
+	}
+
+	protected List<Box> createChildren(final LayoutContext context) {
+
+		// Walk children:
+		// each table-caption gets a BEB
+		// each table-column gets a TableColumnBox
+		// each table-column-group gets a TableColumnGroupBox
+		// runs of others get TableBodyBox
+
+		final List<Box> children = new ArrayList<Box>();
+
+		this.iterateChildrenByDisplayStyle(context.getStyleSheet(),
+				captionOrColumnStyles, new ElementOrRangeCallback() {
+					public void onElement(Element child, String displayStyle) {
+						children.add(new BlockElementBox(context,
+								TableBox.this, child));
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						children.add(new TableBodyBox(context, TableBox.this,
+								startOffset, endOffset));
+					}
+				});
+
+		return children;
+	}
+
+	/**
+	 * Returns an array of widths of the table columns. These widths do not
+	 * include column spacing.
+	 */
+	public int[] getColumnWidths() {
+		return this.columnWidths;
+	}
+
+	public int getHorizonalSpacing() {
+		return this.horizonalSpacing;
+	}
+
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+		return new Insets(this.getMarginTop(), 0, this.getMarginBottom(), 0);
+	}
+
+	public int getVerticalSpacing() {
+		return this.verticalSpacing;
+	}
+
+	public IntRange layout(LayoutContext context, int top, int bottom) {
+
+		// TODO Only compute columns widths (a) if re-laying out the whole box
+		// or (b) if the invalid child row now has more columns than us
+		// or (c) if the invalid child row has < current column count and it
+		// used to be the only one with a valid child row.
+
+		int newColCount = this.computeColumnCount(context);
+		if (this.columnWidths == null
+				|| newColCount != this.columnWidths.length) {
+			this.setLayoutState(LAYOUT_REDO);
+		}
+
+		if (this.getLayoutState() == LAYOUT_REDO) {
+			this.computeColumnWidths(context, newColCount);
+		}
+
+		return super.layout(context, top, bottom);
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+
+		if (this.skipPaint(context, x, y)) {
+			return;
+		}
+
+		this.paintChildren(context, x, y);
+
+		this.paintSelectionFrame(context, x, y, true);
+	}
+
+	// ============================================================ PRIVATE
+
+	private static Set<String> captionOrColumnStyles = new HashSet<String>();
+
+	static {
+		captionOrColumnStyles.add(CSS.TABLE_CAPTION);
+		captionOrColumnStyles.add(CSS.TABLE_COLUMN);
+		captionOrColumnStyles.add(CSS.TABLE_COLUMN_GROUP);
+	}
+
+	private int[] columnWidths;
+	private int horizonalSpacing;
+	private int verticalSpacing;
+
+	private static class CountingCallback implements ElementOrRangeCallback {
+
+		public int getCount() {
+			return this.count;
+		}
+
+		public void reset() {
+			this.count = 0;
+		}
+
+		public void onElement(Element child, String displayStyle) {
+			this.count++;
+		}
+
+		public void onRange(Element parent, int startOffset, int endOffset) {
+			this.count++;
+		}
+
+		private int count;
+	}
+
+	/**
+	 * Performs a quick count of this table's columns. If the count has changed,
+	 * we must re-layout the entire table.
+	 */
+	private int computeColumnCount(LayoutContext context) {
+
+		Element tableElement = this.findContainingElement();
+		final int[] columnCounts = new int[1]; // work around Java's insistence
+												// on final
+		columnCounts[0] = 0;
+		final StyleSheet styleSheet = context.getStyleSheet();
+		final CountingCallback callback = new CountingCallback();
+		LayoutUtils.iterateTableRows(styleSheet, tableElement, this
+				.getStartOffset(), this.getEndOffset(),
+				new ElementOrRangeCallback() {
+					public void onElement(Element child, String displayStyle) {
+						LayoutUtils.iterateTableCells(styleSheet, child,
+								callback);
+						columnCounts[0] = Math.max(columnCounts[0], callback
+								.getCount());
+						callback.reset();
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						LayoutUtils.iterateTableCells(styleSheet, parent,
+								startOffset, endOffset, callback);
+						columnCounts[0] = Math.max(columnCounts[0], callback
+								.getCount());
+						callback.reset();
+					}
+
+				});
+
+		return columnCounts[0];
+	}
+
+	private void computeColumnWidths(final LayoutContext context,
+			int columnCount) {
+
+		this.columnWidths = new int[columnCount];
+
+		if (columnCount == 0) {
+			return;
+		}
+
+		this.horizonalSpacing = 0;
+		this.verticalSpacing = 0;
+		int myWidth = this.getWidth();
+		int availableWidth = myWidth;
+
+		if (!this.isAnonymous()) {
+			Styles styles = context.getStyleSheet()
+					.getStyles(this.getElement());
+			this.horizonalSpacing = styles.getBorderSpacing().getHorizontal();
+			this.verticalSpacing = styles.getBorderSpacing().getVertical();
+
+			// width available for columns
+			// Since we apply margins/borders/padding to the TableBodyBox,
+			// they're
+			// not reflected in the width of this box. Thus, we subtract them
+			// here
+			availableWidth -= +styles.getMarginLeft().get(myWidth)
+					+ styles.getBorderLeftWidth()
+					+ styles.getPaddingLeft().get(myWidth)
+					+ styles.getPaddingRight().get(myWidth)
+					+ styles.getBorderRightWidth()
+					+ styles.getMarginRight().get(myWidth);
+		}
+
+		int totalColumnWidth = this.horizonalSpacing;
+		int columnWidth = (availableWidth - this.horizonalSpacing
+				* (columnCount + 1))
+				/ columnCount;
+		for (int i = 0; i < this.columnWidths.length - 1; i++) {
+			System.err.print(" " + columnWidth);
+			this.columnWidths[i] = columnWidth;
+			totalColumnWidth += columnWidth + this.horizonalSpacing;
+		}
+
+		// Due to rounding errors in the expression above, we calculate the
+		// width of the last column separately, to make it exact.
+		this.columnWidths[this.columnWidths.length - 1] = availableWidth
+				- totalColumnWidth - this.horizonalSpacing;
+
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableCellBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableCellBox.java
new file mode 100644
index 0000000..ab56e46
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableCellBox.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Represents an element with display:table-cell, or a generated, anonymous
+ * table cell.
+ */
+public class TableCellBox extends AbstractBlockBox {
+
+	/**
+	 * Class constructor for non-anonymous table cells.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param parent
+	 *            Parent box.
+	 * @param element
+	 *            Element with which this box is associated.
+	 */
+	public TableCellBox(LayoutContext context, BlockBox parent,
+			Element element, int width) {
+		super(context, parent, element);
+		Styles styles = context.getStyleSheet().getStyles(element);
+		this.setWidth(width - styles.getBorderLeftWidth()
+				- styles.getPaddingLeft().get(parent.getWidth())
+				- styles.getPaddingRight().get(parent.getWidth())
+				- styles.getBorderRightWidth());
+	}
+
+	public TableCellBox(LayoutContext context, BlockBox parent,
+			int startOffset, int endOffset, int width) {
+		super(context, parent, startOffset, endOffset);
+		this.setWidth(width);
+	}
+
+	protected List<Box> createChildren(LayoutContext context) {
+		return this.createBlockBoxes(context, this.getStartOffset(), this
+				.getEndOffset(), this.getWidth(), null, null);
+	}
+
+	public void setInitialSize(LayoutContext context) {
+		// we've already set width in the ctor
+		// override to avoid setting width again
+		this.setHeight(this.getEstimatedHeight(context));
+	}
+
+	// ======================================================= PRIVATE
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowBox.java
new file mode 100644
index 0000000..88adfb8
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowBox.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Box representing a row in a table.
+ */
+public class TableRowBox extends AbstractBlockBox {
+
+	public TableRowBox(LayoutContext context, TableRowGroupBox parent,
+			Element element) {
+		super(context, parent, element);
+	}
+
+	public TableRowBox(LayoutContext context, BlockBox parent, int startOffset,
+			int endOffset) {
+		super(context, parent, startOffset, endOffset);
+	}
+
+	protected List<Box> createChildren(final LayoutContext context) {
+
+		final List<Box> children = new ArrayList<Box>();
+
+		Element element = this.findContainingElement();
+		final int[] widths = this.getTableBox().getColumnWidths();
+
+		LayoutUtils.iterateTableCells(context.getStyleSheet(), element, this
+				.getStartOffset(), this.getEndOffset(),
+				new ElementOrRangeCallback() {
+					private int column = 0;
+
+					public void onElement(Element child, String displayStyle) {
+						children.add(new TableCellBox(context,
+								TableRowBox.this, child, widths[column++]));
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						children.add(new TableCellBox(context,
+								TableRowBox.this, startOffset, endOffset,
+								widths[column++]));
+					}
+				});
+
+		return children;
+	}
+
+	/**
+	 * Override drawBox to do nothing. Table rows have no borders in
+	 * border-collapse:separate mode.
+	 */
+	public void drawBox(LayoutContext context, int x, int y,
+			int containerWidth, boolean drawBorders) {
+	}
+
+	public Caret getCaret(LayoutContext context, int offset) {
+
+		int hSpacing = this.getTableBox().getHorizonalSpacing();
+
+		Box[] children = this.getChildren();
+
+		// If we haven't yet laid out this block, estimate the caret.
+		if (children == null) {
+			int relative = offset - this.getStartOffset();
+			int size = this.getEndOffset() - this.getStartOffset();
+			int y = 0;
+			if (size > 0) {
+				y = this.getHeight() * relative / size;
+			}
+			return new HCaret(0, y, this.getWidth());
+		}
+
+		int x = hSpacing / 2;
+
+		int[] widths = this.getTableBox().getColumnWidths();
+
+		for (int i = 0; i < children.length; i++) {
+
+			Box child = children[i];
+
+			if (!child.hasContent()) {
+				continue; // TODO can we really have generated table cells?
+			}
+
+			if (offset < child.getStartOffset()) {
+				return new TextCaret(x, 0, this.getHeight());
+			}
+
+			if (offset >= child.getStartOffset()
+					&& offset <= child.getEndOffset()) {
+
+				Caret caret = child.getCaret(context, offset);
+				caret.translate(child.getX(), child.getY());
+				return caret;
+			}
+
+			x += widths[i] + hSpacing;
+		}
+
+		return new TextCaret(x, 0, this.getHeight());
+	}
+
+	/**
+	 * Override to return zero insets. Table rows have no insets in
+	 * border-collapse:separate mode.
+	 */
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+		return Insets.ZERO_INSETS;
+	}
+
+	public int getMarginBottom() {
+		return 0;
+	}
+
+	public int getMarginTop() {
+		return 0;
+	}
+
+	public int getNextLineOffset(LayoutContext context, int offset, int x) {
+
+		BlockBox[] children = this.getContentChildren();
+		int[] widths = this.getTableBox().getColumnWidths();
+		int leftEdge = 0;
+
+		for (int i = 0; i < children.length; i++) {
+			if (leftEdge + widths[i] > x) {
+				int newOffset = children[i].getNextLineOffset(context, offset,
+						x - leftEdge);
+				if (newOffset == children[i].getEndOffset() + 1) {
+					return -1;
+				} else {
+					return newOffset;
+				}
+			}
+			leftEdge += widths[i];
+		}
+
+		return -1;
+	}
+
+	public int getPreviousLineOffset(LayoutContext context, int offset, int x) {
+
+		BlockBox[] children = this.getContentChildren();
+		int[] widths = this.getTableBox().getColumnWidths();
+		int leftEdge = 0;
+
+		for (int i = 0; i < children.length; i++) {
+			if (leftEdge + widths[i] > x) {
+				int newOffset = children[i].getPreviousLineOffset(context,
+						offset, x - leftEdge);
+				if (newOffset == children[i].getStartOffset() - 1) {
+					return -1;
+				} else {
+					return newOffset;
+				}
+			}
+			leftEdge += widths[i];
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Returns the TableBox associated with this row.
+	 */
+	public TableBox getTableBox() {
+		return (TableBox) this.getParent().getParent().getParent();
+	}
+
+	protected int positionChildren(LayoutContext context) {
+
+		int hSpacing = this.getTableBox().getHorizonalSpacing();
+
+		int childX = hSpacing;
+		int topInset = 0;
+		int height = 0;
+		int bottomInset = 0;
+		for (int i = 0; i < this.getChildren().length; i++) {
+			Box child = this.getChildren()[i];
+			Insets insets = child.getInsets(context, this.getWidth());
+
+			childX += insets.getLeft();
+
+			child.setX(childX);
+
+			childX += child.getWidth() + insets.getRight() + hSpacing;
+
+			topInset = Math.max(topInset, insets.getTop());
+			height = Math.max(height, child.getHeight());
+			bottomInset = Math.max(bottomInset, insets.getBottom());
+		}
+
+		this.setHeight(topInset + height + bottomInset);
+
+		for (int i = 0; i < this.getChildren().length; i++) {
+			Box child = this.getChildren()[i];
+			child.setY(topInset);
+			child.setHeight(height);
+		}
+
+		return -1; // TODO revisit
+	}
+
+	public int viewToModel(LayoutContext context, int x, int y) {
+
+		Box[] children = getChildren();
+		if (children == null) {
+			return super.viewToModel(context, x, y);
+		}
+		
+		for (Box child : children) {
+			if (!child.hasContent()) continue;
+			
+			if (x < child.getX()) return child.getStartOffset() - 1;
+			
+			if (x < child.getX() + child.getWidth()) {
+				return child.viewToModel(context,
+						                 x - child.getX(),
+						                 y - child.getY());
+			}
+		}
+
+		return getEndOffset();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowGroupBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowGroupBox.java
new file mode 100644
index 0000000..a92b03d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TableRowGroupBox.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Insets;
+import org.eclipse.wst.xml.vex.core.internal.css.CSS;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * Container for TableRowBox objects. May correspond to an element with
+ * display:table-row-group, display:table-head-group, display:table-foot-group,
+ * or may be anonymous.
+ */
+public class TableRowGroupBox extends AbstractBlockBox {
+
+	/**
+	 * Class constructor for non-anonymous table row groups.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param parent
+	 *            Parent of this box.
+	 * @param element
+	 *            Element that generated this box.
+	 */
+	public TableRowGroupBox(LayoutContext context, BlockBox parent,
+			Element element) {
+		super(context, parent, element);
+	}
+
+	/**
+	 * Class constructor for anonymous table row groups.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param parent
+	 *            Parent of this box.
+	 * @param startOffset
+	 *            Start of the range encompassing the table.
+	 * @param endOffset
+	 *            End of the range encompassing the table.
+	 */
+	public TableRowGroupBox(LayoutContext context, BlockBox parent,
+			int startOffset, int endOffset) {
+		super(context, parent, startOffset, endOffset);
+
+	}
+
+	protected List<Box> createChildren(final LayoutContext context) {
+		// TODO Auto-generated method stub
+
+		// Walk children in range
+		// - table-row children get non-anonymous TableRowBox
+		// - runs of others get anonymous TableRowBox
+
+		final List<Box> children = new ArrayList<Box>();
+
+		this.iterateChildrenByDisplayStyle(context.getStyleSheet(),
+				childDisplayStyles, new ElementOrRangeCallback() {
+					public void onElement(Element child, String displayStyle) {
+						children.add(new TableRowBox(context,
+								TableRowGroupBox.this, child));
+					}
+
+					public void onRange(Element parent, int startOffset,
+							int endOffset) {
+						children.add(new TableRowBox(context,
+								TableRowGroupBox.this, startOffset, endOffset));
+					}
+				});
+
+		return children;
+	}
+
+	public Insets getInsets(LayoutContext context, int containerWidth) {
+		return Insets.ZERO_INSETS;
+	}
+
+	public int getMarginBottom() {
+		return 0;
+	}
+
+	public int getMarginTop() {
+		return 0;
+	}
+
+	public void paint(LayoutContext context, int x, int y) {
+
+		if (this.skipPaint(context, x, y)) {
+			return;
+		}
+
+		this.paintChildren(context, x, y);
+
+		this.paintSelectionFrame(context, x, y, true);
+	}
+
+	protected int positionChildren(LayoutContext context) {
+
+		Styles styles = context.getStyleSheet().getStyles(
+				this.findContainingElement());
+		int spacing = styles.getBorderSpacing().getVertical();
+
+		int childY = spacing;
+		for (int i = 0; i < this.getChildren().length; i++) {
+
+			TableRowBox child = (TableRowBox) this.getChildren()[i];
+			// TODO must force table row margins to be zero
+			Insets insets = child.getInsets(context, this.getWidth());
+
+			childY += insets.getTop();
+
+			child.setX(insets.getLeft());
+			child.setY(childY);
+
+			childY += child.getHeight() + insets.getBottom() + spacing;
+		}
+		this.setHeight(childY);
+
+		return -1; // TODO revisit
+	}
+
+	// ====================================================== PRIVATE
+
+	private static Set<String> childDisplayStyles = new HashSet<String>();
+
+	static {
+		childDisplayStyles.add(CSS.TABLE_ROW);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextBox.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextBox.java
new file mode 100644
index 0000000..6b21c3f
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextBox.java
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.FontMetrics;
+import org.eclipse.wst.xml.vex.core.internal.core.FontResource;
+import org.eclipse.wst.xml.vex.core.internal.core.FontSpec;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.css.Styles;
+import org.eclipse.wst.xml.vex.core.internal.dom.Element;
+
+/**
+ * An inline box containing text. The <code>getText</code> and
+ * <code>splitAt</code> methods are abstract and must be implemented by
+ * subclasses.
+ */
+public abstract class TextBox extends AbstractInlineBox implements InlineBox {
+
+	private Element element;
+	private int baseline;
+
+	public static final char NEWLINE_CHAR = 0xa;
+	public static final String NEWLINE_STRING = "\n";
+
+	/**
+	 * Class constructor.
+	 * 
+	 * @param element
+	 *            Element containing the text. This is used for styling
+	 *            information.
+	 */
+	public TextBox(Element element) {
+		this.element = element;
+	}
+
+
+	/**
+	 * Causes the box to recalculate it size. Subclasses should call this from
+	 * their constructors after they are initialized.
+	 * 
+	 * @param context
+	 *            LayoutContext used to calculate size.
+	 */
+	protected void calculateSize(LayoutContext context) {
+		String s = this.getText();
+		if (s.endsWith(NEWLINE_STRING)) {
+			s = s.substring(0, s.length() - 1);
+		}
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.getElement());
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+		FontMetrics fm = g.getFontMetrics();
+		this.setWidth(g.stringWidth(s));
+		this.setHeight(styles.getLineHeight());
+		int halfLeading = (this.getHeight() - (fm.getAscent() + fm.getDescent())) / 2;
+		this.baseline = halfLeading + fm.getAscent();
+		g.setFont(oldFont);
+		font.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#getBaseline()
+	 */
+	public int getBaseline() {
+		return this.baseline;
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.Box#getCaret(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int)
+	 */
+	public Caret getCaret(LayoutContext context, int offset) {
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.element);
+		FontResource oldFont = g.getFont();
+		FontResource font = g.createFont(styles.getFont());
+		g.setFont(font);
+		char[] chars = this.getText().toCharArray();
+		int x = g.charsWidth(chars, 0, offset - this.getStartOffset());
+		g.setFont(oldFont);
+		font.dispose();
+		return new TextCaret(x, 0, this.getHeight());
+	}
+
+	/**
+	 * Returns the element that controls the styling for this text element.
+	 */
+	public Element getElement() {
+		return this.element;
+	}
+
+	/**
+	 * Return the text that comprises this text box. The actual text can come
+	 * from the document content or from a static string.
+	 */
+	public abstract String getText();
+
+	/**
+	 * Returns true if the given character is one where a linebreak should
+	 * occur, e.g. a space.
+	 * 
+	 * @param c
+	 *            the character to test
+	 */
+	public static boolean isSplitChar(char c) {
+		return Character.isWhitespace(c);
+	}
+
+	public boolean isEOL() {
+		String s = this.getText();
+		return s.length() > 0 && s.charAt(s.length() - 1) == NEWLINE_CHAR;
+	}
+
+	/**
+	 * Paints a string as selected text.
+	 * 
+	 * @param context
+	 *            LayoutContext to be used. It is assumed that the contained
+	 *            Graphics object is set up with the proper font.
+	 * @param s
+	 *            String to draw
+	 * @param x
+	 *            x-coordinate at which to draw the text
+	 * @param y
+	 *            y-coordinate at which to draw the text
+	 */
+	protected void paintSelectedText(LayoutContext context, String s, int x,
+			int y) {
+		Graphics g = context.getGraphics();
+
+		boolean inSelectedBlock = false;
+		Element e = this.getElement();
+		while (e != null) {
+			Styles styles = context.getStyleSheet().getStyles(e);
+			if (styles.isBlock()) {
+				if (context.isElementSelected(e)) {
+					inSelectedBlock = true;
+				}
+				break;
+			}
+			e = e.getParent();
+		}
+
+		if (inSelectedBlock) {
+			g.setColor(g.getSystemColor(ColorResource.SELECTION_BACKGROUND));
+			g.drawString(s, x, y);
+		} else {
+			int width = g.stringWidth(s);
+			g.setColor(g.getSystemColor(ColorResource.SELECTION_BACKGROUND));
+			g.fillRect(x, y, width, this.getHeight());
+			g.setColor(g.getSystemColor(ColorResource.SELECTION_FOREGROUND));
+			g.drawString(s, x, y);
+		}
+	}
+
+	protected void paintTextDecoration(LayoutContext context, Styles styles,
+			String s, int x, int y) {
+		int fontStyle = styles.getFont().getStyle();
+		Graphics g = context.getGraphics();
+		FontMetrics fm = g.getFontMetrics();
+
+		if ((fontStyle & FontSpec.UNDERLINE) != 0) {
+			int lineWidth = fm.getAscent() / 12;
+			int ypos = y + fm.getAscent() + lineWidth;
+			paintBaseLine(g, s, x, ypos);
+		}
+		if ((fontStyle & FontSpec.OVERLINE) != 0) {
+			int lineWidth = fm.getAscent() / 12;
+			int ypos = y + lineWidth / 2;
+			paintBaseLine(g, s, x, ypos);
+		}
+		if ((fontStyle & FontSpec.LINE_THROUGH) != 0) {
+			int ypos = y + fm.getHeight() / 2;
+			paintBaseLine(g, s, x, ypos);
+		}
+	}
+
+	/**
+	 * Paint a line along the baseline of the text, for showing underline,
+	 * overline and strike-through formatting.
+	 * 
+	 * @param context
+	 *            LayoutContext to be used. It is assumed that the contained
+	 *            Graphics object is set up with the proper font.
+	 * @param x
+	 *            x-coordinate at which to start drawing baseline
+	 * @param y
+	 *            x-coordinate at which to start drawing baseline (adjusted to
+	 *            produce the desired under/over/though effect)
+	 */
+	protected void paintBaseLine(Graphics g, String s, int x, int y) {
+		FontMetrics fm = g.getFontMetrics();
+		int width = g.stringWidth(s);
+		int lineWidth = fm.getAscent() / 12;
+		g.setLineStyle(Graphics.LINE_SOLID);
+		g.setLineWidth(lineWidth);
+		g.drawLine(x, y, x + width, y);
+	}
+
+	/**
+	 * @see org.eclipse.wst.xml.vex.core.internal.layout.InlineBox#split(org.eclipse.wst.xml.vex.core.internal.layout.LayoutContext,
+	 *      int, boolean)
+	 */
+	public Pair split(LayoutContext context, int maxWidth, boolean force) {
+
+		char[] chars = this.getText().toCharArray();
+
+		if (chars.length == 0) {
+			throw new IllegalStateException();
+		}
+
+		Graphics g = context.getGraphics();
+		Styles styles = context.getStyleSheet().getStyles(this.element);
+		FontResource font = g.createFont(styles.getFont());
+		FontResource oldFont = g.setFont(font);
+
+		int split = 0;
+		int next = 1;
+		boolean eol = false; // end of line found
+		while (next < chars.length) {
+			if (isSplitChar(chars[next - 1])) {
+				if (g.charsWidth(chars, 0, next) <= maxWidth) {
+					split = next;
+					if (chars[next - 1] == NEWLINE_CHAR) {
+						eol = true;
+						break;
+					}
+				} else {
+					break;
+				}
+			}
+			next++;
+		}
+
+		if (force && split == 0) {
+			// find some kind of split
+			split = 1;
+			while (split < chars.length) {
+				if (g.charsWidth(chars, 0, split + 1) > maxWidth) {
+					break;
+				}
+				split++;
+			}
+
+		}
+
+		// include any trailing spaces in the split
+		// this also grabs any leading spaces when split==0
+		if (!eol) {
+			while (split < chars.length - 1 && chars[split] == ' ') {
+				split++;
+			}
+		}
+
+		g.setFont(oldFont);
+		font.dispose();
+
+		return this.splitAt(context, split);
+	}
+
+	/**
+	 * Return a pair of boxes representing a split at the given offset. If split
+	 * is zero, then the returned left box should be null. If the split is equal
+	 * to the length of the text, then the right box should be null.
+	 * 
+	 * @param context
+	 *            LayoutContext used to calculate the sizes of the resulting
+	 *            boxes.
+	 * @param offset
+	 *            location of the split, relative to the start of the text box.
+	 * @return
+	 */
+	public abstract Pair splitAt(LayoutContext context, int offset);
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		return this.getText();
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextCaret.java b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextCaret.java
new file mode 100644
index 0000000..b1dea06
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/TextCaret.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 John Krasnay 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:
+ *     John Krasnay - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.vex.core.internal.layout;
+
+import org.eclipse.wst.xml.vex.core.internal.core.Caret;
+import org.eclipse.wst.xml.vex.core.internal.core.Color;
+import org.eclipse.wst.xml.vex.core.internal.core.ColorResource;
+import org.eclipse.wst.xml.vex.core.internal.core.Graphics;
+import org.eclipse.wst.xml.vex.core.internal.core.Rectangle;
+
+/**
+ * A caret drawn as a vertical line between characters.
+ */
+public class TextCaret extends Caret {
+
+	private static final int LINE_WIDTH = 2;
+
+	private int height;
+
+	/**
+	 * Class constructor
+	 * 
+	 * @param x
+	 *            x-coordinate of the caret
+	 * @param y
+	 *            y-coordinate of the top of the caret
+	 * @param height
+	 *            height of the caret
+	 */
+	public TextCaret(int x, int y, int height) {
+		super(x, y);
+		this.height = height;
+	}
+
+	public void draw(Graphics g, Color color) {
+		ColorResource newColor = g.createColor(color);
+		ColorResource oldColor = g.setColor(newColor);
+		g.fillRect(this.getX(), this.getY(), LINE_WIDTH, height);
+		g.setColor(oldColor);
+		newColor.dispose();
+	}
+
+	public Rectangle getBounds() {
+		return new Rectangle(this.getX(), this.getY(), LINE_WIDTH, height);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/package.html b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/package.html
new file mode 100644
index 0000000..8042cab
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/package.html
@@ -0,0 +1,124 @@
+<?xml version='1.0'?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+
+  <head>
+
+    <title>The Vex Layout Engine</title>
+
+  </head>
+
+  <body>
+
+    <h1>The Vex Layout Engine</h1>
+
+    <p>The purpose of the Vex Layout Engine is to create a visual 
+    representation of a document given a CSS stylesheet. This visual 
+    representation is a nested hierarchy of rectangular boxes, 
+    implemented as a tree of objects each implementing the Box 
+    interface. Each box has the following properties.</p>
+
+    <ul>
+
+      <li>The x- and y-coordinates of the box. These coordinates are 
+      relative to the containing parent box for efficiency: if a 
+      box&apos;s position changes, it need not recalculate the 
+      positions of its children. For boxes corresponding to a document 
+      element, the coordinates match the coordinates of the CSS content 
+      area, which is inside any margins, borders, and padding.</li>
+
+      <li>The height and width of the box. For boxes corresponding to a 
+      document element, this is the height and width of the CSS content 
+      area, that is, the area inside any margins, borders, and 
+      padding.</li>
+
+      <li>The document element associated with the box, if any.</li>
+
+      <li>The child boxes of the box, if any.</li>
+
+      <li>The range of document offsets represented by the box, if 
+      any.</li>
+
+    </ul>
+
+    <p>Additionally, each box supports the following operations.</p>
+
+    <ul>
+
+      <li>Determine a <i>caret</i>, that is, a visual representation of 
+      the current insertion point, for a given document offset.</li>
+
+      <li>Return the document offset closest to a given (x, y) position 
+      relative to the top-left corner of the box.</li>
+
+    </ul>
+
+    <p>There are two main types of box. <i>Block boxes</i> normally 
+    contain other boxes and stack their children vertically (with 
+    TableRowBox being an exception whose children are stacked 
+    horizontally). <i>Inline boxes</i> may contain child boxes or other 
+    content such as text; their children are stacked horizontally and 
+    they may be split to wrap content into a series of lines.</p>
+
+    <p>A box may acquire its children in a number of ways. Boxes 
+    associated with document elements (e.g. BlockElementBox) create 
+    their own children by inspecting the child nodes of the associated 
+    element. This can happen immediately in the box&apos;s constructor, 
+    or may be deferred for performance. In other cases, a box&apos;s 
+    children are created by its parent and passed to its constructor. 
+    Finally, simple boxes such as DocumentTextBox and PlaceholderBox 
+    have no child boxes; they serve simply to display content or to aid 
+    in navigation.</p>
+
+    <h2>Layout Process</h2>
+
+    <p>The layout process begins with a VexWidgetImpl object, which 
+    creates a RootBox containing a BlockElementBox corresponding with 
+    the document&apos;s root element. Each BlockElementBox does not 
+    initially create its children. Instead, it estimates its height 
+    based on the current font size and the number of characters covered 
+    by the element.</p>
+
+    <p>At any one time,