bug 257806: read comments within the root element

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentReaderTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentReaderTest.java
index 4fc9658..ac2f657 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentReaderTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentReaderTest.java
@@ -10,6 +10,7 @@
  *******************************************************************************/

 package org.eclipse.vex.core.internal.dom;

 

+import static org.junit.Assert.*;

 import static org.junit.Assert.assertEquals;

 import static org.junit.Assert.assertFalse;

 import static org.junit.Assert.assertNotNull;

@@ -18,6 +19,7 @@
 

 import java.io.IOException;

 import java.net.URL;

+import java.util.List;

 

 import org.eclipse.vex.core.tests.TestResources;

 import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;

@@ -121,4 +123,22 @@
 		assertEquals(2, documentContentModelPosition[0]);

 		assertEquals(1, entityResolverPosition[0]);

 	}

+	

+	@Test

+	public void readDocumentWithComments() throws Exception {

+		final DocumentReader reader = new DocumentReader();

+		final Document document = reader.read(TestResources.get("documentWithComments.xml"));

+		final Element rootElement = document.getRootElement();

+		final List<Node> rootChildNodes = rootElement.getChildNodes();

+		assertEquals(4, rootChildNodes.size());

+		

+		final CommentElement comment1 = (CommentElement) rootChildNodes.get(0);

+		assertEquals("A comment within the root element.", comment1.getText());

+		

+		final CommentElement comment2 = (CommentElement) ((Element) rootChildNodes.get(1)).getChildNodes().get(1);

+		assertEquals("A comment within text.", comment2.getText());

+

+		final CommentElement comment3 = (CommentElement) rootChildNodes.get(2);

+		assertEquals("Another comment between two child elements.", comment3.getText());

+	}

 }

diff --git a/org.eclipse.vex.core.tests/testResources/documentWithComments.xml b/org.eclipse.vex.core.tests/testResources/documentWithComments.xml
new file mode 100644
index 0000000..13183f4
--- /dev/null
+++ b/org.eclipse.vex.core.tests/testResources/documentWithComments.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>

+<!DOCTYPE section PUBLIC "-//Eclipse Foundation//DTD Vex Test//EN" "test1.dtd">

+<!-- A comment before the root element. -->

+<section>

+	<!-- A comment within the root element. -->

+	<title>Testdocument with DTD <!--  A comment within text. -->(Public Identifier)</title>

+	<!-- Another comment between two child elements. -->

+	<para>This is a Testdocument using a DTD which is referenced by a public identifier.</para>

+</section>

+<!-- A final comment after the root element. -->
\ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/CommentElement.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/CommentElement.java
new file mode 100644
index 0000000..6da3fb6
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/CommentElement.java
@@ -0,0 +1,26 @@
+/*******************************************************************************

+ * 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.vex.core.internal.dom;

+

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

+

+/**

+ * Represents a comment

+ */

+public class CommentElement extends Element {

+

+	public static final QualifiedName COMMENT_ELEMENT_NAME = new QualifiedName(null, "!COMMENT");

+	

+	public CommentElement() {

+		super(COMMENT_ELEMENT_NAME);

+	}

+

+}

diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentBuilder.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentBuilder.java
index b368fa4..e48918f 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentBuilder.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/DocumentBuilder.java
@@ -11,6 +11,7 @@
  *******************************************************************************/
 package org.eclipse.vex.core.internal.dom;
 
+import java.util.Arrays;
 import java.util.LinkedList;
 
 import org.eclipse.core.runtime.Assert;
@@ -80,13 +81,21 @@
 	// ============================================= ContentHandler methods
 
 	public void characters(final char[] ch, final int start, final int length) throws SAXException {
+		appendPendingCharsFiltered(ch, start, length);
+	}
+
+	private void appendPendingCharsFiltered(final char[] ch, final int start, final int length) {
 		// Convert control characters 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);
+		for (int i = start; i < start + length; i++) {
+			if (isControlCharacter(ch[i]))
+				pendingChars.append(' ');
+			else
+				pendingChars.append(ch[i]);
+		}
+	}
+
+	private static boolean isControlCharacter(final char ch) {
+		return Character.isISOControl(ch) && ch != '\n' && ch != '\r' && ch != '\t';
 	}
 
 	public void endDocument() {
@@ -176,7 +185,7 @@
 			documentContentModel.initialize(baseUri, dtdPublicID, dtdSystemID, rootElement);
 			policy = documentContentModel.getWhitespacePolicy();
 		}
-		
+
 		appendChars(isBlock(element));
 
 		stack.add(new StackEntry(element, content.getLength(), isPre(element)));
@@ -206,6 +215,28 @@
 	// ============================================== LexicalHandler methods
 
 	public void comment(final char[] ch, final int start, final int length) {
+		System.out.println("Comment: " + Arrays.toString(ch) + " start: " + start + " length: " + length);
+		System.out.println("Element Stack: " + stack);
+
+		if (stack.isEmpty()) // TODO support comments outside of the root element
+			return;
+
+		final CommentElement element = new CommentElement();
+		final Element parent = stack.getLast().element;
+		parent.addChild(element);
+
+		appendChars(true); // comments are handled as block elements
+		final int startOffset = content.getLength();
+		content.insertElementMarker(content.getLength());
+
+		trimLeading = true; // comments are handled as block elements
+		appendPendingCharsFiltered(ch, start, length);
+		appendChars(true); // comments are handled as block elements
+
+		content.insertElementMarker(content.getLength());
+		element.setContent(content, startOffset, content.getLength() - 1);
+		
+		trimLeading = true; // comments are handled as block elements
 	}
 
 	public void endCDATA() {