[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);
}
}