[283980]: VEX stack overflow with thead insertion.
diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/META-INF/MANIFEST.MF b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/META-INF/MANIFEST.MF
index a23dd7d..d3e0998 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/META-INF/MANIFEST.MF
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/META-INF/MANIFEST.MF
@@ -16,10 +16,10 @@
  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-internal:=true,
- org.eclipse.wst.xml.vex.core.internal.core;x-internal:=true,
- org.eclipse.wst.xml.vex.core.internal.css;x-internal:=true,
- org.eclipse.wst.xml.vex.core.internal.dom;x-internal:=true,
- org.eclipse.wst.xml.vex.core.internal.layout;x-internal:=true,
+ org.eclipse.wst.xml.vex.core.internal.core;x-friends:="org.eclipse.wst.xml.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.css;x-friends:="org.eclipse.wst.xml.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.dom;x-friends:="org.eclipse.wst.xml.vex.core.tests",
+ org.eclipse.wst.xml.vex.core.internal.layout;x-friends:="org.eclipse.wst.xml.vex.core.tests",
  org.eclipse.wst.xml.vex.core.internal.provisional.dom;x-internal:=true,
  org.eclipse.wst.xml.vex.core.internal.provisional.dom.I,
  org.eclipse.wst.xml.vex.core.internal.provisional.dom.impl,
diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java
index 23ecf22..2417fb4 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/dom/Document.java
@@ -10,8 +10,6 @@
  *******************************************************************************/
 package org.eclipse.wst.xml.vex.core.internal.dom;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
@@ -340,7 +338,7 @@
 		for (int i = 0; i < nodes.size(); i++) {
 			VEXNode node = nodes.get(i);
 			if (node instanceof Element) {
-				names.add(((VEXElement) node).getName());
+				names.add(((Element) node).getName());
 			} else {
 				names.add("#PCDATA");
 			}
diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java
index db4ec79..aa4e5df 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.core/src/org/eclipse/wst/xml/vex/core/internal/layout/AbstractBlockBox.java
@@ -920,28 +920,87 @@
 		List<VEXElement> children = element.getChildElements();
 		for (int i = 0; i < children.size(); i++) {
 			VEXElement child = children.get(i);
-			if (child.getEndOffset() < startOffset) {
-				continue;
-			} else if (child.getStartOffset() >= endOffset) {
-				break;
-			} else {
-				Styles styles = context.getStyleSheet().getStyles(child);
-				if (!styles.getDisplay().equals(CSS.INLINE)) { // TODO do proper
-																// block display
-																// determination
-					return child;
-				} else {
-					VEXElement fromChild = findNextBlockElement(context, child,
-							startOffset, endOffset);
-					if (fromChild != null) {
-						return fromChild;
-					}
-				}
+
+            // inside range?
+			if (child.getEndOffset() < startOffset) continue;
+			if (child.getStartOffset() >= endOffset) break;
+
+            // found?
+			if (!isInline(context, child, element)) return child;
+
+			// recursion
+			VEXElement fromChild =
+				findNextBlockElement(context, child, startOffset, endOffset);
+			if (fromChild != null) {
+				return fromChild;
 			}
 		}
 
 		return null;
 	}
+	
+	private static boolean isInline(LayoutContext context,
+			                        VEXElement child,
+			                        VEXElement 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(VEXElement element,
+			                             LayoutContext context) {
+		return context.getStyleSheet().getStyles(element).getDisplay();
+	}
 
 	/**
 	 * Return the end position of an anonymous box. The default implementation
diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/dita_classed_shell_vex.css b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/dita_classed_shell_vex.css
index 7a660a9..476ddb5 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/dita_classed_shell_vex.css
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/dita_classed_shell_vex.css
@@ -21,3 +21,6 @@
   margin-left: 40px;
 }
 
+entry {
+  display: table-cell;
+}
diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic.css b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic.css
index 443a415..ce736a7 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic.css
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic.css
@@ -408,6 +408,7 @@
 }

 

 thead > row > entry    {

+	display: table-cell;

 	font-weight: bold;

 }

 

@@ -432,7 +433,7 @@
 }

 

 entry    {

-	display: table-entry;

+	display: table-cell;

 	border: thin solid black;

 }

 

diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic_classed.css b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic_classed.css
index 7385e56..ff803a1 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic_classed.css
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.dita/css/topic_classed.css
@@ -434,7 +434,7 @@
 }

 

 *[class~="topic\/entry"]    {

-  display: table-entry;

+  display: table-cell;

   border: thin solid black;

 }

 

diff --git a/sourceediting/plugins/org.eclipse.wst.xml.vex.docbook/styles/docbook-plain.css b/sourceediting/plugins/org.eclipse.wst.xml.vex.docbook/styles/docbook-plain.css
index 55fc767..e390810 100644
--- a/sourceediting/plugins/org.eclipse.wst.xml.vex.docbook/styles/docbook-plain.css
+++ b/sourceediting/plugins/org.eclipse.wst.xml.vex.docbook/styles/docbook-plain.css
@@ -260,6 +260,7 @@
 
 entry {
 	display: table-cell;
+	border: thin solid black;
 }
 
 entrytbl {
@@ -744,10 +745,19 @@
 	display: inline;
 }
 
+table {
+	display: table;
+}
+
 tbody {
 	display: table-row-group;
 }
 
+td {
+	display: table-cell;
+	border: thin solid black;
+}
+
 term {
 	display: block;
 }
@@ -756,12 +766,22 @@
 	display: inline;
 }
 
-tgroup { /* display: table; */
-	display: block;
+tfoot {
+	display: table-footer-group;
+}
+
+tgroup {
+	display: table-row-group;
 }
 
 thead {
-	display: table-row-group;
+	display: table-header-group;
+}
+
+th {
+	display: table-cell;
+	border: thin solid black;
+	font-weight: bold;
 }
 
 tip {
@@ -778,6 +798,10 @@
 	display: block;
 }
 
+tr {
+	display: table-row;
+}
+
 article>title {
 	font: bold 20pt Arial, sans-serif;
 	display: block;
diff --git a/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/internal/layout/TableLayoutTest.java b/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/internal/layout/TableLayoutTest.java
new file mode 100644
index 0000000..e3340e1
--- /dev/null
+++ b/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/internal/layout/TableLayoutTest.java
@@ -0,0 +1,244 @@
+/*******************************************************************************

+ * Copyright (c) 2009 Holger Voormann 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:

+ *     Holger Voormann - initial API and implementation

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

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

+

+import java.util.Arrays;

+import java.util.Stack;

+

+import junit.framework.TestCase;

+

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

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

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

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

+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.RootElement;

+import org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXDocument;

+

+public class TableLayoutTest extends TestCase {

+

+    private static interface StackVisitor {

+        void visit(StackElement element);

+    }

+

+    private static class StackElement {

+        public final int indent;

+        public final Box box;

+        public StackElement(int indent, Box box) {

+            this.indent = indent;

+            this.box = box;

+        }

+    }

+

+    private LayoutContext context;

+    private VEXDocument document;

+    private RootBox rootBox;

+    private int caretPosition;

+

+    @Override

+    protected void setUp() throws Exception {

+

+        // display dummy

+        DisplayDevice.setCurrent(new MockDisplayDevice(90, 90));

+

+        // context dummy

+        context = new LayoutContext();

+        context.setBoxFactory(new MockBoxFactory());

+        context.setGraphics(new FakeGraphics());

+

+        // set CSS

+        String css =   "root   {display:block}"

+                     + "inline {display:inline}"

+                     + "table  {display:table}"

+                     + "tcap   {display:table-caption}"

+                     + "td     {display:table-cell}"

+                     + "tc     {display:table-column}"

+                     + "tcg    {display:table-column-group}"

+                     + "tfg    {display:table-footer-group}"

+                     + "thg    {display:table-header-group}"

+                     + "tr     {display:table-row}"

+                     + "trg    {display:table-row-group}";

+        StyleSheet styleSheet = new StyleSheetReader().read(css);

+        context.setStyleSheet(styleSheet);

+

+        resetDocument();

+    }

+

+    @Override

+    protected void tearDown() throws Exception {

+        rootBox = null;

+        document = null;

+        context = null;

+    }

+

+    private void resetDocument() {

+        document = new Document(new RootElement("root"));

+        context.setDocument(document);

+        caretPosition = 1;

+        rootBox = new RootBox(this.context, document.getRootElement(), 500);

+    }

+

+    private void insertElement(String elementName) {

+        document.insertElement(caretPosition, new Element(elementName));

+        caretPosition++;

+    }

+

+    private void insertText(String text) {

+        document.insertText(caretPosition, text);;

+        caretPosition += text.length();

+    }

+

+    public void testValidTable() throws Exception {

+

+        // single cell Table

+        insertElement("table");

+        insertElement("tr");

+        insertElement("td");

+        insertText("a");

+        assertCount(1, TableBox.class);

+        assertCount(1, TableRowBox.class);

+        assertCount(1, TableCellBox.class);

+        assertCount(1, DocumentTextBox.class);

+        assertEquals("a", contentAsText());

+

+        // 2x2 table plus text

+        resetDocument();

+        insertText("_");

+        insertElement("table");

+        insertElement("tr");

+        insertElement("td");

+        insertText("a");

+        caretPosition++;

+        insertElement("td");

+        insertText("b");

+        caretPosition+=2;

+        insertElement("tr");

+        insertElement("td");

+        insertText("c");

+        caretPosition++;

+        insertElement("td");

+        insertText("d");

+        assertCount(1, TableBox.class);

+        assertCount(2, TableRowBox.class);

+        assertCount(4, TableCellBox.class);

+        assertCount(5, DocumentTextBox.class);

+        assertEquals("_abcd", contentAsText());

+    }

+

+    // table elements outside table (separately tested to improve tracing if

+    // StackOverflowError will be thrown)

+    public void testCaptionOutsideTable()     { test("tcap"); }

+    public void testCellOutsideTable()        { test("td"); }

+    public void testColumnOutsideTable()      { test("tc"); }

+    public void testColumnGroupOutsideTable() { test("tcg"); }

+    public void testFooterGroupOutsideTable() { test("tfg"); }

+    public void testHeaderGroupOutsideTable() { test("thg"); }

+    public void testRowOutsideTable()         { test("tr"); }

+    public void testRowGroupOutsideTable()    { test("trg"); }

+    

+    // invalid nested table elements (separately tested to improve tracing if

+    // StackOverflowError will be thrown)

+    public void testInvalidNesting1() { test("inline", "tcap"); }

+    public void testInvalidNesting2() { test("table", "td"); }

+    public void testInvalidNesting3() { test("td", "tr"); }

+    public void testInvalidNesting4() { test("trg", "trg"); }

+    public void testInvalidNesting5() { test("tr", "tfg"); }

+    public void testInvalidNesting6() { test("td", "thg"); }

+    public void testInvalidNesting7() { test("table", "tc"); }

+    public void testInvalidNesting8() { test("thg", "tcg"); }

+    

+    public void test(String ... elements) {

+        resetDocument();

+        insertElement("inline");

+        for (int i = 0; i < elements.length; i++) {

+            insertElement(elements[i]);

+        }

+        insertText("x");

+        assertCount(1, DocumentTextBox.class);

+        assertEquals("x", contentAsText());

+    }

+    

+    private String contentAsText() {

+        return document.getText(0, document.getLength());

+    }

+

+    private void assertCount(int expected, Class<? extends Box> blockClass) {

+        int count = count(blockClass);

+        String message =   "expected count of <"

+                         + blockClass.getSimpleName()

+                         + ">: <"

+                         + expected

+                         + "> but was: <"

+                         + count

+                         + ">\n"

+                         + "Actual layout stack trace:\n"

+                         + layoutStackToString();

+        assertEquals(message, expected, count);

+    }

+

+    private int count(final Class<? extends Box> blockClass) {

+        final int[] mutableInteger = new int[1];

+        mutableInteger[0] = 0;

+        travelLayoutStack(new StackVisitor() {

+

+            public void visit(StackElement element) {

+                if (element.box.getClass().equals(blockClass)) {

+                    mutableInteger[0]++;

+                }

+            }

+

+        });

+        return mutableInteger[0];

+    }

+

+    private String layoutStackToString() {

+        final StringBuilder result = new StringBuilder();

+        travelLayoutStack(new StackVisitor() {

+

+            public void visit(StackElement element) {

+                if (element.indent > 0) {

+                    char[] indentChars = new char[element.indent * 2];

+                    Arrays.fill(indentChars, ' ');

+                    result.append(indentChars);

+                }

+                result.append(element.box.getClass().getSimpleName());

+                result.append('\n');

+

+            }

+

+        });

+        return result.toString();

+    }

+

+    private void travelLayoutStack(StackVisitor visitor) {

+

+        // already layouted?

+        Box[] rootElementChildren = rootBox.getChildren()[0].getChildren();

+        if (rootElementChildren == null || rootElementChildren.length == 0) {

+            rootBox.layout(this.context, 0, Integer.MAX_VALUE);

+        }

+

+        Stack<StackElement> stack = new Stack<StackElement>();

+        stack.push(new StackElement(0, rootBox));

+        while (!stack.isEmpty()) {

+            StackElement current = stack.pop();

+            visitor.visit(current);

+

+            // iterate deep-first

+            Box[] children = current.box.getChildren();

+            for (int i = 0; i < children.length; i++) {

+                stack.push(new StackElement(current.indent + 1, children[i]));

+            }

+        }

+    }

+

+}

diff --git a/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/tests/VEXCoreTestSuite.java b/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/tests/VEXCoreTestSuite.java
index 4631bfe..2e1404a 100755
--- a/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/tests/VEXCoreTestSuite.java
+++ b/sourceediting/tests/org.eclipse.wst.xml.vex.core.tests/src/org/eclipse/wst/xml/vex/core/tests/VEXCoreTestSuite.java
@@ -41,5 +41,6 @@
 		addTestSuite(TestDocumentTextBox.class);
 		addTestSuite(TestStaticTextBox.class);
 		addTestSuite(DOMSynchronizationTest.class);
+		addTestSuite(TableLayoutTest.class);
 	}
 }