add basic CSS styles and layout elements for XInclude's. 

Change-Id: Ifffed7e87d828137148d1bd46344cd8c9f0621cd
Signed-off-by: Carsten Hiesserich <carsten.hie@gmail.com>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DummyValidator.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DummyValidator.java
index e33020f..4102813 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DummyValidator.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DummyValidator.java
@@ -64,6 +64,11 @@
 		return false;

 	}

 

+	@Override

+	public boolean isValidSequenceXInclude(final List<QualifiedName> nodes, final boolean partial) {

+		return false;

+	}

+

 	public Set<String> getRequiredNamespaces() {

 		return Collections.emptySet();

 	}

diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/LayoutTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/LayoutTest.java
index 25ea549..e3b3181 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/LayoutTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/LayoutTest.java
@@ -81,6 +81,7 @@
 		suite.addTest(loadSuite("tables.xml"));
 		suite.addTest(loadSuite("simple-edit.xml"));
 		suite.addTest(loadSuite("comment-processing-instr.xml"));
+		suite.addTest(loadSuite("include.xml"));
 		return suite;
 	}
 
@@ -189,7 +190,7 @@
 
 		boxSpec.children.removeAll(toRemove);
 
-		if (invalidateParentBlock && box instanceof BlockBox && box.getNode() != null) {
+		if (invalidateParentBlock && box instanceof BlockBox && !box.isAnonymous()) {
 			((BlockBox) box).invalidate(true);
 			invalidateParentBlock = false;
 		}
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/include.xml b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/include.xml
new file mode 100644
index 0000000..0e2b733
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/include.xml
@@ -0,0 +1,117 @@
+<?xml version='1.0'?>

+<testcases css="test.css">

+

+	<test id="Include BlockBox" layoutWidth="100">

+		<doc><![CDATA[ <root><blockNoInsert><include/></blockNoInsert></root> ]]></doc>

+		<result>

+			<box class="RootBox">

+				<box class="BlockElementBox">

+					<box class="BlockElementBox" element="root">

+						<box class="BlockElementBox" element="blockNoInsert">

+							<box class="IncludeBlockBox" element="include">

+								<box class="ParagraphBox">

+									<box class="LineBox">

+										<box class="StaticTextBox" />

+									</box>

+								</box>

+							</box>

+						</box>

+					</box>

+				</box>

+			</box>

+		</result>

+	</test>

+	

+	<test id="Include BlockBox With Before" layoutWidth="100">

+		<doc><![CDATA[ <root><blockNoInsert><includeb/></blockNoInsert></root> ]]></doc>

+		<result>

+			<box class="RootBox">

+				<box class="BlockElementBox">

+					<box class="BlockElementBox" element="root">

+						<box class="BlockElementBox" element="blockNoInsert">

+							<box class="IncludeBlockBox" element="includeb">

+								<box class="ParagraphBox">

+									<box class="LineBox">

+										<box class="StaticTextBox" text="before" />

+										<box class="StaticTextBox" text="after" />

+									</box>

+								</box>

+							</box>

+						</box>

+					</box>

+				</box>

+			</box>

+		</result>

+	</test>

+	

+	<test id="Include InlineBox" layoutWidth="100">

+		<doc><![CDATA[ <root><p><include/></p></root> ]]></doc>

+		<result>

+			<box class="RootBox">

+				<box class="BlockElementBox">

+					<box class="BlockElementBox" element="root">

+						<box class="BlockElementBox" element="p">

+							<box class="ParagraphBox">

+								<box class="LineBox">

+									<box class="PlaceholderBox" />

+										<box class="IncludeInlineBox" element="include">

+											<box class="StaticTextBox" />

+										</box>

+									<box class="PlaceholderBox" />

+								</box>

+							</box>

+						</box>

+					</box>

+				</box>

+			</box>

+		</result>

+	</test>

+	

+	<test id="Include InlineBox With Before And After" layoutWidth="100">

+		<doc><![CDATA[ <root><p><includeb/></p></root> ]]></doc>

+		<result>

+			<box class="RootBox">

+				<box class="BlockElementBox">

+					<box class="BlockElementBox" element="root">

+						<box class="BlockElementBox" element="p">

+							<box class="ParagraphBox">

+								<box class="LineBox">

+									<box class="PlaceholderBox" />

+										<box class="IncludeInlineBox" element="includeb">

+											<box class="StaticTextBox" text="before"/>

+											<box class="StaticTextBox" text="after" />

+										</box>

+									<box class="PlaceholderBox" />

+								</box>

+							</box>

+						</box>

+					</box>

+				</box>

+			</box>

+		</result>

+	</test>

+	

+	<test id="Remove InlineBox" layoutWidth="100" performActions="true">

+		<doc><![CDATA[ <root><p><includeb/></p></root> ]]></doc>

+		<result>

+			<box class="RootBox">

+				<box class="BlockElementBox">

+					<box class="BlockElementBox" element="root">

+						<box class="BlockElementBox" element="p">

+							<box class="ParagraphBox">

+								<box class="LineBox">

+									<box class="PlaceholderBox" shouldBeRemoved="1"/>

+										<box class="IncludeInlineBox" element="includeb" removeElementAction="1">

+											<box class="StaticTextBox" text="before"/>

+											<box class="StaticTextBox" text="after" />

+										</box>

+									<box class="PlaceholderBox" />

+								</box>

+							</box>

+						</box>

+					</box>

+				</box>

+			</box>

+		</result>

+	</test>

+</testcases>

diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/test.css b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/test.css
index 42ccd67..6cab2ff 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/test.css
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/layout/test.css
@@ -24,6 +24,29 @@
   display: block;
 }
 
+blockNoInsert {
+	display: block;
+}
+
+include {
+	display: include;
+}
+
+includeb {
+	display: include;
+}
+
+includeb:before {
+	display: inline;
+	content: "before";
+}
+
+includeb:after {
+	display: inline;
+	content: "after";
+}
+
+
 .blockAfter:after {
   display: block;
   content: 'AFTER';
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/DTDValidatorTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/DTDValidatorTest.java
index df646ed..50253f3 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/DTDValidatorTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/DTDValidatorTest.java
@@ -28,6 +28,7 @@
 
 import org.eclipse.core.runtime.QualifiedName;
 import org.eclipse.vex.core.internal.dom.Document;
+import org.eclipse.vex.core.internal.dom.Namespace;
 import org.eclipse.vex.core.provisional.dom.AttributeDefinition;
 import org.eclipse.vex.core.provisional.dom.IDocument;
 import org.eclipse.vex.core.provisional.dom.IElement;
@@ -39,6 +40,7 @@
 public class DTDValidatorTest {
 
 	private IValidator validator = null;
+	private static final QualifiedName XI = new QualifiedName(Namespace.XINCLUDE_NAMESPACE_URI, "include");
 
 	@Before
 	public void setUp() {
@@ -200,6 +202,16 @@
 		}
 	}
 
+	private void assertIsValidSequence(final QualifiedName parentElement, final QualifiedName... sequence) {
+		for (int i = 0; i < sequence.length; i++) {
+			final List<QualifiedName> prefix = createPrefix(i, sequence);
+			final List<QualifiedName> toInsert = Collections.singletonList(sequence[i]);
+			final List<QualifiedName> suffix = createSuffix(i, sequence);
+
+			assertTrue(validator.isValidSequence(parentElement, prefix, toInsert, suffix, false));
+		}
+	}
+
 	private static List<QualifiedName> createPrefix(final int index, final String... sequence) {
 		final List<QualifiedName> prefix = new ArrayList<QualifiedName>();
 		for (int i = 0; i < index; i++) {
@@ -216,6 +228,22 @@
 		return suffix;
 	}
 
+	private static List<QualifiedName> createPrefix(final int index, final QualifiedName... sequence) {
+		final List<QualifiedName> prefix = new ArrayList<QualifiedName>();
+		for (int i = 0; i < index; i++) {
+			prefix.add(sequence[i]);
+		}
+		return prefix;
+	}
+
+	private static List<QualifiedName> createSuffix(final int index, final QualifiedName... sequence) {
+		final List<QualifiedName> suffix = new ArrayList<QualifiedName>();
+		for (int i = index + 1; i < sequence.length; i++) {
+			suffix.add(sequence[i]);
+		}
+		return suffix;
+	}
+
 	@Test
 	public void testGetAttributes_shouldReturnAllAttributesButNamespaceDefs() throws Exception {
 		// xmlns:xxx attributes should not be returned
@@ -273,4 +301,14 @@
 		}
 		return adMap;
 	}
+
+	@Test
+	public void testValidationShouldIgnoreXInclude() throws Exception {
+		final QualifiedName section = new QualifiedName(null, "section");
+		final QualifiedName title = new QualifiedName(null, "title");
+		final QualifiedName para = new QualifiedName(null, "para");
+
+		assertIsValidSequence(section, XI, title, para);
+		assertIsValidSequence(section, para, XI, para);
+	}
 }
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/SchemaValidatorTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/SchemaValidatorTest.java
index 6b18519..c86efe8 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/SchemaValidatorTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/validator/SchemaValidatorTest.java
@@ -32,6 +32,7 @@
 import org.eclipse.core.runtime.QualifiedName;

 import org.eclipse.vex.core.internal.dom.Document;

 import org.eclipse.vex.core.internal.dom.Element;

+import org.eclipse.vex.core.internal.dom.Namespace;

 import org.eclipse.vex.core.internal.io.DocumentReader;

 import org.eclipse.vex.core.provisional.dom.AttributeDefinition;

 import org.eclipse.vex.core.provisional.dom.DocumentContentModel;

@@ -57,6 +58,7 @@
 	private static final QualifiedName P = new QualifiedName(CONTENT_NS, "p");

 	private static final QualifiedName B = new QualifiedName(CONTENT_NS, "b");

 	private static final QualifiedName I = new QualifiedName(CONTENT_NS, "i");

+	private static final QualifiedName XI = new QualifiedName(Namespace.XINCLUDE_NAMESPACE_URI, "include");

 

 	@Test

 	public void readDocumentWithTwoSchemas() throws Exception {

@@ -282,6 +284,13 @@
 		assertTrue("isRequired should be true", ad.isRequired());

 	}

 

+	@Test

+	public void testValidationShouldIgnoreXInclude() throws Exception {

+		final IValidator validator = new WTPVEXValidator(CONTENT_NS);

+		assertIsValidSequence(validator, P, XI);

+		assertIsValidSequence(validator, P, XI, B, I);

+	}

+

 	private Map<QualifiedName, AttributeDefinition> getAttributeMap() {

 		final IDocument doc = new Document(new QualifiedName(null, "chapter"));

 		final IValidator validator = new WTPVEXValidator(STRUCTURE_NS);

diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CSS.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CSS.java
index 83f064c..5245f5c 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CSS.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CSS.java
@@ -191,6 +191,7 @@
 	public static final String DOTTED = "dotted";
 	public static final String DOUBLE = "double";
 	public static final String GROOVE = "groove";
+	public static final String INCLUDE = "include";
 	public static final String INLINE = "inline";
 	public static final String INLINE_BLOCK = "inline-block";
 	public static final String INLINE_TABLE = "inline-table";
@@ -238,6 +239,7 @@
 	// Common element names
 	public static final String XML_PROCESSING_INSTRUCTION = "processing-instruction";
 	public static final String XML_COMMENT = "comment";
+	public static final String XINCLUDE_NAMESPACE_PREFIX = "xi";
 	public static final String VEX_NAMESPACE_PREFIX = "vex";
 
 	// Pseudo elements
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CssWhitespacePolicy.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CssWhitespacePolicy.java
index 8ae7730..4635e76 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CssWhitespacePolicy.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/CssWhitespacePolicy.java
@@ -56,6 +56,19 @@
 					return true;
 				}
 
+				if (isDisplay(node, CSS.INCLUDE)) {
+					// When this method is called by the DocumentBuilder, the note is not yet associated
+					if (!node.isAssociated() || node.getDocument() == null) {
+						return false;
+					}
+
+					if (node.getDocument().canInsertText(node.getStartOffset())) {
+						return false;
+					}
+
+					return isBlock(element.getParent());
+				}
+
 				if (isDisplay(node, CSS.TABLE)) {
 					return true;
 				}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/DisplayProperty.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/DisplayProperty.java
index 1cacb75..bee9796 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/DisplayProperty.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/DisplayProperty.java
@@ -52,7 +52,7 @@
 			final 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.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);
+					|| s.equals(CSS.TABLE_HEADER_GROUP) || s.equals(CSS.TABLE_ROW) || s.equals(CSS.TABLE_ROW_GROUP) || s.equals(CSS.INCLUDE);
 		} else {
 			return false;
 		}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/VexSelectorFactory.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/VexSelectorFactory.java
index e092e7e..d19fe82 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/VexSelectorFactory.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/VexSelectorFactory.java
@@ -30,7 +30,10 @@
 		final int seperatorIndex = tagName != null ? tagName.indexOf("|") : -1;
 		if (seperatorIndex > -1) {
 			final String namespacePrefix = tagName.substring(0, seperatorIndex);
-			if (namespacePrefix.equals(CSS.VEX_NAMESPACE_PREFIX)) {
+			if (namespacePrefix.equals(CSS.XINCLUDE_NAMESPACE_PREFIX)) {
+				namespaceURI = Namespace.XINCLUDE_NAMESPACE_URI;
+				tagName = tagName.substring(seperatorIndex + 1);
+			} else if (namespacePrefix.equals(CSS.VEX_NAMESPACE_PREFIX)) {
 				namespaceURI = Namespace.VEX_NAMESPACE_URI;
 				tagName = tagName.substring(seperatorIndex + 1);
 			}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css
index f3c7ca2..18f2433 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/vex-core-styles.css
@@ -45,4 +45,18 @@
 vex|comment:after {
 	display: inline;
 	content: '-->';
-}
\ No newline at end of file
+}
+
+xi|include {
+	background-color: #cccccc;
+	margin-top: 5px;
+	mrgin-bottom: 5px;
+	display: include;
+	_vex-outline-content: attr(href);
+}
+
+xi|include:before {
+	display: inline;
+	color: blue;
+	content: 'XInclude href: ',attr(href);
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Namespace.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Namespace.java
index 7b56681..c87fcd0 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Namespace.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Namespace.java
@@ -26,4 +26,6 @@
 

 	public static final String VEX_NAMESPACE_URI = "http://www.eclipse.org/vex";

 

+	public static final String XINCLUDE_NAMESPACE_URI = "http://www.w3.org/2001/XInclude";

+

 }

diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CssBoxFactory.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CssBoxFactory.java
index 44de5e3..5afde07 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CssBoxFactory.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CssBoxFactory.java
@@ -43,8 +43,11 @@
 
 			@Override
 			public Box visit(final IElement element) {
-				if (styles.getDisplay().equals(CSS.TABLE)) {
+				final String displayStyle = styles.getDisplay();
+				if (displayStyle.equals(CSS.TABLE)) {
 					return new TableBox(context, parentBox, element);
+				} else if (displayStyle.equals(CSS.INCLUDE)) {
+					return new IncludeBlockBox(context, parentBox, element);
 				} else if (context.getWhitespacePolicy().isBlock(element)) {
 					return new BlockElementBox(context, parentBox, node);
 				} else {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeBlockBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeBlockBox.java
new file mode 100644
index 0000000..edf7e1a
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeBlockBox.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ * 		Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.vex.core.internal.core.Caret;
+import org.eclipse.vex.core.internal.css.CSS;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.provisional.dom.ContentPosition;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
+
+/**
+ * This class displays include elements like XInclude.
+ */
+public class IncludeBlockBox extends BlockElementBox {
+	private static final int H_CARET_LENGTH = 20;
+
+	public IncludeBlockBox(final LayoutContext context, final BlockBox parent, final INode node) {
+		super(context, parent, node);
+	}
+
+	@Override
+	public Caret getCaret(final LayoutContext context, final ContentPosition position) {
+		return new HCaret(0, getHeight() - 2, H_CARET_LENGTH);
+	}
+
+	@Override
+	public List<Box> createChildren(final LayoutContext context) {
+		final INode node = getNode();
+		final int width = getWidth();
+
+		final StyleSheet styleSheet = context.getStyleSheet();
+		final List<Box> result = new ArrayList<Box>();
+		final List<InlineBox> pendingInlines = new ArrayList<InlineBox>();
+
+		// :before content - includes the target
+		final IElement before = styleSheet.getPseudoElement(node, CSS.PSEUDO_BEFORE, true);
+		if (before != null) {
+			pendingInlines.addAll(LayoutUtils.createGeneratedInlines(context, before, StaticTextBox.START_MARKER));
+		}
+
+		// content
+		final String text = LayoutUtils.getGeneratedContent(context, styleSheet.getStyles(node), node);
+		if (text.length() > 0) {
+			pendingInlines.add(new StaticTextBox(context, node, text));
+		}
+
+		// :after content
+		final IElement after = styleSheet.getPseudoElement(node, CSS.PSEUDO_AFTER, true);
+		if (after != null) {
+			pendingInlines.addAll(LayoutUtils.createGeneratedInlines(context, after, StaticTextBox.END_MARKER));
+		}
+
+		if (pendingInlines.isEmpty()) {
+			// No pseudo element and no content - return something
+			pendingInlines.add(new StaticTextBox(context, node, CSS.INCLUDE, StaticTextBox.START_MARKER));
+		}
+		result.add(ParagraphBox.create(context, node, pendingInlines, width));
+
+		return result;
+	}
+
+	@Override
+	public ContentPosition viewToModel(final LayoutContext context, final int x, final int y) {
+		return getNode().getStartPosition().moveBy(1);
+	}
+
+	@Override
+	public String toString() {
+		return "IncludeBlockBox: <" + getNode() + ">" + "[x=" + getX() + ",y=" + getY() + ",width=" + getWidth() + ",height=" + getHeight() + "]";
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeInlineBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeInlineBox.java
new file mode 100644
index 0000000..2fc7832
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/IncludeInlineBox.java
@@ -0,0 +1,231 @@
+package org.eclipse.vex.core.internal.layout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.vex.core.internal.core.Caret;
+import org.eclipse.vex.core.internal.core.FontMetrics;
+import org.eclipse.vex.core.internal.core.FontResource;
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.css.CSS;
+import org.eclipse.vex.core.internal.css.Styles;
+import org.eclipse.vex.core.provisional.dom.ContentPosition;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
+
+public class IncludeInlineBox extends CompositeInlineBox {
+	private static final int H_CARET_LENGTH = 20;
+
+	private final INode node;
+	private InlineBox[] children;
+	private InlineBox firstContentChild = null;
+	private InlineBox lastContentChild = null;
+	private int baseline;
+
+	/**
+	 * Class constructor, called by the {@link InlineElementBox#createInlineBoxes} static factory method. The Box
+	 * created here is only temporary, it will be replaced by the split method.
+	 * 
+	 * @param context
+	 *            LayoutContext to use.
+	 * @param node
+	 *            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.
+	 */
+	public IncludeInlineBox(final LayoutContext context, final INode node, final int startOffset, final int endOffset) {
+		this.node = node;
+		final Styles styles = context.getStyleSheet().getStyles(node);
+		final List<InlineBox> childList = new ArrayList<InlineBox>();
+
+		if (startOffset <= node.getStartOffset()) {
+			// space for the left margin/border/padding
+			final int space = styles.getMarginLeft().get(0) + styles.getBorderLeftWidth() + styles.getPaddingLeft().get(0);
+
+			if (space > 0) {
+				childList.add(new SpaceBox(space, 1));
+			}
+
+			// :before content
+			final IElement beforeElement = context.getStyleSheet().getPseudoElement(node, CSS.PSEUDO_BEFORE, true);
+			if (beforeElement != null) {
+				childList.addAll(LayoutUtils.createGeneratedInlines(context, beforeElement, StaticTextBox.START_MARKER));
+			}
+
+		}
+
+		if (endOffset > node.getEndOffset()) {
+			// :after content
+			final IElement afterElement = context.getStyleSheet().getPseudoElement(node, CSS.PSEUDO_AFTER, true);
+			if (afterElement != null) {
+				childList.addAll(LayoutUtils.createGeneratedInlines(context, afterElement));
+			}
+
+			// space for the right margin/border/padding
+			final int space = styles.getMarginRight().get(0) + styles.getBorderRightWidth() + styles.getPaddingRight().get(0);
+
+			if (space > 0) {
+				childList.add(new SpaceBox(space, 1));
+			}
+		}
+
+		if (childList.isEmpty()) {
+			// No pseudo element and no content - return something
+			childList.add(new StaticTextBox(context, node, CSS.INCLUDE, StaticTextBox.START_MARKER));
+		}
+
+		children = childList.toArray(new InlineBox[childList.size()]);
+	}
+
+	/**
+	 * Class constructor. This constructor is called by the split method.
+	 * 
+	 * @param context
+	 *            LayoutContext used for the layout.
+	 * @param node
+	 *            Node to which this box applies.
+	 * @param children
+	 *            Child boxes.
+	 */
+	private IncludeInlineBox(final LayoutContext context, final INode node, final InlineBox[] children) {
+		this.node = node;
+		this.children = children;
+		layout(context);
+		for (final InlineBox child : children) {
+			if (child.hasContent()) {
+				if (firstContentChild == null) {
+					firstContentChild = child;
+				}
+				lastContentChild = child;
+			}
+		}
+	}
+
+	private void layout(final LayoutContext context) {
+		final Graphics g = context.getGraphics();
+		final Styles styles = context.getStyleSheet().getStyles(node);
+		final FontResource font = g.createFont(styles.getFont());
+		final FontResource oldFont = g.setFont(font);
+		final FontMetrics fm = g.getFontMetrics();
+		setHeight(styles.getLineHeight());
+		final int halfLeading = (styles.getLineHeight() - fm.getAscent() - fm.getDescent()) / 2;
+		baseline = halfLeading + fm.getAscent();
+		g.setFont(oldFont);
+		font.dispose();
+
+		int x = 0;
+		for (final InlineBox child : children) {
+			// TODO: honour the child's vertical-align property
+			child.setX(x);
+			child.alignOnBaseline(baseline);
+			x += child.getWidth();
+		}
+
+		setWidth(x);
+	}
+
+	/**
+	 * Override to paint background and borders.
+	 * 
+	 * @see org.eclipse.vex.core.internal.layout.AbstractBox#paint(org.eclipse.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	@Override
+	public void paint(final LayoutContext context, final int x, final int y) {
+		this.drawBox(context, x, y, 0, true);
+		super.paint(context, x, y);
+	}
+
+	/**
+	 * @see org.eclipse.vex.core.internal.layout.Box#getChildren()
+	 */
+	@Override
+	public Box[] getChildren() {
+		return children;
+	}
+
+	/**
+	 * Returns the element associated with this box.
+	 */
+	@Override
+	public INode getNode() {
+		return node;
+	}
+
+	/**
+	 * @see org.eclipse.vex.core.internal.layout.Box#getStartOffset()
+	 */
+	@Override
+	public int getStartOffset() {
+		return getNode().getStartOffset();
+	}
+
+	/**
+	 * @see org.eclipse.vex.core.internal.layout.Box#getEndOffset()
+	 */
+	@Override
+	public int getEndOffset() {
+		return getNode().getEndOffset();
+	}
+
+	@Override
+	public boolean hasContent() {
+		return node.isAssociated();
+	}
+
+	@Override
+	public boolean isAnonymous() {
+		return false;
+	}
+
+	@Override
+	public Caret getCaret(final LayoutContext context, final ContentPosition position) {
+		return new HCaret(0, getBaseline() + 1, H_CARET_LENGTH);
+	}
+
+	@Override
+	public int getBaseline() {
+		return baseline;
+	}
+
+	/**
+	 * @see org.eclipse.vex.core.internal.layout.Box#viewToModel(org.eclipse.vex.core.internal.layout.LayoutContext,
+	 *      int, int)
+	 */
+	@Override
+	public ContentPosition viewToModel(final LayoutContext context, final int x, final int y) {
+		return getNode().getStartPosition().moveBy(1);
+	}
+
+	@Override
+	public Pair split(final LayoutContext context, final InlineBox[] lefts, final InlineBox[] rights, final int remaining) {
+
+		IncludeInlineBox left = null;
+		IncludeInlineBox right = null;
+
+		if (lefts.length > 0 || rights.length == 0) {
+			left = new IncludeInlineBox(context, getNode(), lefts);
+		}
+
+		if (rights.length > 0) {
+			// We reuse this element instead of creating a new one
+			children = rights;
+			for (final InlineBox child : children) {
+				if (child.hasContent()) {
+					// The lastContentChild is already set in this instance an did not change
+					firstContentChild = child;
+					break;
+				}
+			}
+			if (left == null && remaining >= 0) {
+				// There is no left box, and the right box fits without further splitting, so we have to calculate the size here
+				layout(context);
+			}
+			right = this;
+		}
+
+		return new Pair(left, right, remaining);
+	}
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/InlineElementBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/InlineElementBox.java
index 1253f70..5560da7 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/InlineElementBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/InlineElementBox.java
@@ -305,7 +305,12 @@
 						@Override
 						public void visit(final IElement element) {
 							addPlaceholderBox(result, new PlaceholderBox(context, node, element.getStartOffset() - node.getStartOffset()));
-							final InlineBox child = new InlineElementBox(context, element, range.getStartOffset(), range.getEndOffset());
+							InlineBox child;
+							if (CSS.INCLUDE.equals(context.getStyleSheet().getStyles(element).getDisplay())) {
+								child = new IncludeInlineBox(context, element, range.getStartOffset(), range.getEndOffset());
+							} else {
+								child = new InlineElementBox(context, element, range.getStartOffset(), range.getEndOffset());
+							}
 							addChildInlineBox(result, child);
 						}
 
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/validator/WTPVEXValidator.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/validator/WTPVEXValidator.java
index 7e374dc..35ba5a5 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/validator/WTPVEXValidator.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/validator/WTPVEXValidator.java
@@ -346,6 +346,10 @@
 			return true;
 		}
 
+		if (Namespace.XINCLUDE_NAMESPACE_URI.equals(element.getQualifier())) {
+			return isValidSequenceXInclude(nodes, partial);
+		}
+
 		final CMDocument document = getContentModelDoc(element.getQualifier());
 		if (document == null) {
 			System.out.println("Unable to resolve validation schema for " + element.getQualifier());
@@ -360,12 +364,19 @@
 		final CMElementDeclaration elementDeclaration = (CMElementDeclaration) parent;
 		final ElementPathRecordingResult validationResult = new ElementPathRecordingResult();
 		final List<String> nodeNames = new ArrayList<String>();
+		int elementCount = 0;
 		for (final QualifiedName node : nodes) {
-			nodeNames.add(node.getLocalName()); // TODO learn how the WTP content model handles namespaces
+			// XInlude is normally not a an allowed elements (parser are expected to process
+			// XIncludes before validation), so we ignore them here.
+			if (!Namespace.XINCLUDE_NAMESPACE_URI.equals(node.getQualifier())) {
+				nodeNames.add(node.getLocalName()); // TODO learn how the WTP content model handles namespaces
+				if (ELEMENT_CONTENT_COMPARATOR.isElement(node.getLocalName())) {
+					elementCount++;
+				}
+			}
 		}
 		validator.validate(elementDeclaration, nodeNames, ELEMENT_CONTENT_COMPARATOR, validationResult);
 
-		final int elementCount = getElementCount(nodes);
 		if (partial && elementCount > 0) {
 			return validationResult.getPartialValidationCount() >= elementCount;
 		}
@@ -373,16 +384,6 @@
 		return validationResult.isValid;
 	}
 
-	private static int getElementCount(final List<QualifiedName> nodes) {
-		int count = 0;
-		for (final QualifiedName node : nodes) {
-			if (ELEMENT_CONTENT_COMPARATOR.isElement(node.getLocalName())) {
-				count++;
-			}
-		}
-		return count;
-	}
-
 	public boolean isValidSequence(final QualifiedName element, final List<QualifiedName> seq1, final List<QualifiedName> seq2, final List<QualifiedName> seq3, final boolean partial) {
 		final List<QualifiedName> joinedSequence = new ArrayList<QualifiedName>();
 		if (seq1 != null) {
@@ -397,6 +398,10 @@
 		return isValidSequence(element, joinedSequence, partial);
 	}
 
+	public boolean isValidSequenceXInclude(final List<QualifiedName> nodes, final boolean partial) {
+		return false;
+	}
+
 	public Set<String> getRequiredNamespaces() {
 		if (documentContentModel.isDtdAssigned()) {
 			return Collections.emptySet();
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/VexWidget.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/VexWidget.java
index adedf48..b25317e 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/VexWidget.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/VexWidget.java
@@ -188,9 +188,18 @@
 	@Override
 	public void copySelection() {
 		final Clipboard clipboard = new Clipboard(getDisplay());
-		final Object[] data = { getSelectedFragment(), getSelectedText() };
-		final Transfer[] transfers = { DocumentFragmentTransfer.getInstance(), TextTransfer.getInstance() };
-		clipboard.setContents(data, transfers);
+		final String text = getSelectedText();
+		if (text.isEmpty()) {
+			// Some elements (like XInclude) may not contain textual content. 
+			final Object[] data = { getSelectedFragment() };
+			final Transfer[] transfers = { DocumentFragmentTransfer.getInstance() };
+			clipboard.setContents(data, transfers);
+		} else {
+			final Object[] data = { getSelectedFragment(), getSelectedText() };
+			final Transfer[] transfers = { DocumentFragmentTransfer.getInstance(), TextTransfer.getInstance() };
+			clipboard.setContents(data, transfers);
+		}
+
 	}
 
 	@Override
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IValidator.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IValidator.java
index 5da5957..e91fc37 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IValidator.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IValidator.java
@@ -58,6 +58,11 @@
 			return false;
 		}
 
+		@Override
+		public boolean isValidSequenceXInclude(final List<QualifiedName> nodes, final boolean partial) {
+			return false;
+		}
+
 		public Set<String> getRequiredNamespaces() {
 			return Collections.emptySet();
 		}
@@ -130,6 +135,17 @@
 	boolean isValidSequence(QualifiedName element, List<QualifiedName> seq1, List<QualifiedName> seq2, List<QualifiedName> seq3, boolean partial);
 
 	/**
+	 * Returns true if the given sequence is valid for an XInclude element. We totally ignore the XInclude Schema here
+	 * and allow nothing.
+	 * 
+	 * @param nodes
+	 *            Array of element names and Validator.PCDATA.
+	 * @param partial
+	 *            If true, an valid but incomplete sequence is acceptable.
+	 */
+	boolean isValidSequenceXInclude(List<QualifiedName> nodes, boolean partial);
+
+	/**
 	 * Returns a set of QualifiedNames representing valid root elements for the given document type.
 	 */
 	Set<QualifiedName> getValidRootElements();