summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarsten Hiesserich2013-06-20 15:12:27 (EDT)
committer Florian Thienel2013-06-20 15:12:27 (EDT)
commitc094e52672e4d2efc6ff28bf012baa51364c6aca (patch)
treee5d4be0893d28505d0164314fb95e230542a2052
parent950628a7a27c10d91b647bdf31266fd516982164 (diff)
downloadorg.eclipse.mylyn.docs.vex-c094e52672e4d2efc6ff28bf012baa51364c6aca.zip
org.eclipse.mylyn.docs.vex-c094e52672e4d2efc6ff28bf012baa51364c6aca.tar.gz
org.eclipse.mylyn.docs.vex-c094e52672e4d2efc6ff28bf012baa51364c6aca.tar.bz2
apply whitespace policy to inserted XML fragments
https://bugs.eclipse.org/bugs/show_bug.cgi?id=408501
-rw-r--r--org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/io/XMLFragmentTest.java82
-rw-r--r--org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2XmlInsertionTest.java183
-rw-r--r--org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java10
-rw-r--r--org.eclipse.vex.core.tests/testResources/test1.dtd2
-rw-r--r--org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/IWhitespacePolicy.java14
-rw-r--r--org.eclipse.vex.core/src/org/eclipse/vex/core/internal/io/XMLFragment.java147
-rw-r--r--org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/IVexWidget.java12
-rw-r--r--org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java77
-rw-r--r--org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/swt/tests/DocumentFragmentTransferTest.java10
-rw-r--r--org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/DocumentFragmentTransfer.java56
-rw-r--r--org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/VexWidget.java10
11 files changed, 550 insertions, 53 deletions
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/io/XMLFragmentTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/io/XMLFragmentTest.java
new file mode 100644
index 0000000..b921419
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/io/XMLFragmentTest.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.vex.core.internal.dom.Document;
+import org.eclipse.vex.core.internal.dom.DocumentFragment;
+import org.eclipse.vex.core.provisional.dom.IDocumentFragment;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.IText;
+import org.junit.Test;
+
+/**
+ * @author Florian Thienel
+ */
+public class XMLFragmentTest {
+
+ @Test
+ public void createFromXmlString() throws Exception {
+ final XMLFragment xmlFragment = new XMLFragment("prefix<child>inner text</child>suffix");
+ assertEquals("prefix<child>inner text</child>suffix", xmlFragment.getXML());
+ }
+
+ @Test
+ public void createFromDocumentFragment() throws Exception {
+ final Document doc = new Document(new QualifiedName(null, "root"));
+ doc.insertText(doc.getRootElement().getEndOffset(), "prefix");
+ final IElement child = doc.insertElement(doc.getRootElement().getEndOffset(), new QualifiedName(null, "child"));
+ doc.insertText(child.getEndOffset(), "inner text");
+ doc.insertText(doc.getRootElement().getEndOffset(), "suffix");
+ final DocumentFragment documentFragment = doc.getFragment(doc.getRootElement().getRange().resizeBy(1, -1));
+
+ final XMLFragment xmlFragment = new XMLFragment(documentFragment);
+ assertEquals("prefix<child>inner text</child>suffix", xmlFragment.getXML());
+ }
+
+ @Test
+ public void provideDocumentFragmentForXmlString() throws Exception {
+ final XMLFragment xmlFragment = new XMLFragment("prefix<child>inner text</child>suffix");
+ final IDocumentFragment documentFragment = xmlFragment.getDocumentFragment();
+
+ assertEquals(3, documentFragment.children().count());
+ assertTrue(documentFragment.children().get(0) instanceof IText);
+ assertTrue(documentFragment.children().get(1) instanceof IElement);
+ assertTrue(documentFragment.children().get(2) instanceof IText);
+
+ assertEquals(new QualifiedName(null, "child"), ((IElement) documentFragment.children().get(1)).getQualifiedName());
+
+ assertEquals("prefix", documentFragment.children().get(0).getText());
+ assertEquals("inner text", documentFragment.children().get(1).getText());
+ assertEquals("suffix", documentFragment.children().get(2).getText());
+ }
+
+ @Test
+ public void canCompressWhitespaceOfXmlString() throws Exception {
+ final XMLFragment xmlFragment = new XMLFragment("prefix\n<child>\n inner \t text</child>\r suffix");
+ final XMLFragment compressedXmlFragment = xmlFragment.compressWhitespace();
+
+ assertEquals("prefix <child> inner text</child> suffix", compressedXmlFragment.getXML());
+ }
+
+ @Test
+ public void shouldPreserveWhitespaceByDefault() throws Exception {
+ final XMLFragment xmlFragment = new XMLFragment("prefix\n<child>\n inner \t text</child>\n suffix");
+ final IDocumentFragment documentFragment = xmlFragment.getDocumentFragment();
+
+ assertEquals("prefix\n", documentFragment.children().get(0).getText());
+ assertEquals("\n inner \t text", documentFragment.children().get(1).getText());
+ assertEquals("\n suffix", documentFragment.children().get(2).getText());
+ }
+}
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2XmlInsertionTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2XmlInsertionTest.java
new file mode 100644
index 0000000..d2f0bc0
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2XmlInsertionTest.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * 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.widget;
+
+import static org.eclipse.vex.core.internal.widget.VexWidgetTest.PARA;
+import static org.eclipse.vex.core.internal.widget.VexWidgetTest.createDocumentWithDTD;
+import static org.eclipse.vex.core.tests.TestResources.TEST_DTD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.vex.core.internal.css.CssWhitespacePolicy;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.internal.css.StyleSheetReader;
+import org.eclipse.vex.core.internal.dom.DocumentFragment;
+import org.eclipse.vex.core.internal.dom.Element;
+import org.eclipse.vex.core.internal.dom.GapContent;
+import org.eclipse.vex.core.internal.dom.Node;
+import org.eclipse.vex.core.internal.io.XMLFragment;
+import org.eclipse.vex.core.provisional.dom.ContentRange;
+import org.eclipse.vex.core.provisional.dom.DocumentValidationException;
+import org.eclipse.vex.core.provisional.dom.IContent;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
+import org.eclipse.vex.core.provisional.dom.IParent;
+import org.eclipse.vex.core.provisional.dom.IText;
+import org.eclipse.vex.core.tests.TestResources;
+import org.junit.Before;
+import org.junit.Test;
+
+public class L2XmlInsertionTest {
+
+ private static final QualifiedName PRE = new QualifiedName(null, "pre");
+ private static final QualifiedName EMPHASIS = new QualifiedName(null, "emphasis");
+
+ private VexWidgetImpl widget;
+ private IElement para1;
+ private IElement pre;
+
+ @Before
+ public void setUp() throws Exception {
+ widget = new VexWidgetImpl(new MockHostComponent());
+ final StyleSheet styleSheet = new StyleSheetReader().read(TestResources.get("test.css"));
+ widget.setDocument(createDocumentWithDTD(TEST_DTD, "section"), styleSheet);
+ widget.setWhitespacePolicy(new CssWhitespacePolicy(styleSheet));
+ para1 = widget.insertElement(PARA);
+ widget.moveBy(1);
+ widget.insertElement(PARA);
+ widget.moveTo(para1.getEndOffset());
+ pre = widget.insertElement(PRE);
+ }
+
+ @Test
+ public void givenNonPreElement_whenInsertingNotAllowedFragment_shouldInsertTextOnly() throws Exception {
+ widget.moveTo(para1.getStartOffset() + 1);
+ widget.insertXML(createParaFragment());
+
+ assertEquals("beforeinnerafter", para1.getText());
+ }
+
+ @Test(expected = DocumentValidationException.class)
+ public void givenNonPreElement_whenInsertingInvalidFragment_shouldThrowDocumentValidationExeption() throws Exception {
+ widget.moveTo(para1.getStartOffset() + 1);
+
+ widget.insertXML("<emphasis>someText</para>");
+ }
+
+ @Test
+ public void givenNonPreElement_whenInsertingValidFragment_shouldInsertXml() throws Exception {
+ widget.moveTo(para1.getEndOffset());
+ widget.insertXML(createInlineFragment("before", "inner", "after"));
+
+ final List<? extends INode> children = para1.children().asList();
+ assertTrue("Expecting IParent", children.get(0) instanceof IParent); // the pre element
+ assertTrue("Expecting IText", children.get(1) instanceof IText);
+ assertEquals("before", children.get(1).getText());
+ assertTrue("Expecting IParent", children.get(2) instanceof IParent); // the inserted emphasis
+ assertEquals("inner", children.get(2).getText());
+ assertTrue("Expecting IText", children.get(3) instanceof IText);
+ assertEquals("after", children.get(3).getText());
+ }
+
+ @Test
+ public void givenPreElement_whenInsertingInvalidFragment_shouldInsertTextWithWhitespace() throws Exception {
+ widget.moveTo(pre.getStartOffset() + 1);
+ widget.insertXML(createParaFragment());
+
+ assertEquals("beforeinnerafter", pre.getText());
+ }
+
+ @Test
+ public void givenPreElement_whenInsertingValidFragment_shouldinsertXML() throws Exception {
+ widget.moveTo(pre.getEndOffset());
+ widget.insertXML(createInlineFragment("before", "inner", "after"));
+
+ final List<? extends INode> children = pre.children().asList();
+ assertTrue("Expecting IText", children.get(0) instanceof IText);
+ assertEquals("before", children.get(0).getText());
+ assertTrue("Expecting IParent", children.get(1) instanceof IParent); // the inserted emphasis
+ assertEquals("inner", children.get(1).getText());
+ assertTrue("Expecting IText", children.get(2) instanceof IText);
+ assertEquals("after", children.get(2).getText());
+ }
+
+ @Test
+ public void givenPreElement_whenInsertingValidFragmentWithWhitespace_shouldKeepWhitecpace() throws Exception {
+ widget.moveTo(pre.getEndOffset());
+ widget.insertXML(createInlineFragment("line1\nline2 end", "inner", "after"));
+
+ final List<? extends INode> children = pre.children().asList();
+ assertTrue("Expecting IText", children.get(0) instanceof IText);
+ assertEquals("line1\nline2 end", children.get(0).getText());
+ assertTrue("Expecting IParent", children.get(1) instanceof IParent); // the inserted emphasis
+ assertEquals("inner", children.get(1).getText());
+ assertTrue("Expecting IText", children.get(2) instanceof IText);
+ assertEquals("after", children.get(2).getText());
+ }
+
+ @Test
+ public void whenInsertingMixedFragmentWithWhitespace_shouldKeepWhitecpaceInPre() throws Exception {
+ widget.moveTo(para1.getEndOffset());
+ widget.insertXML("before<pre>pre1\npre2 end</pre>after \nend");
+
+ final List<? extends INode> children = para1.children().after(pre.getEndOffset()).asList();
+ assertEquals("New children count", 3, children.size());
+ assertTrue("Expecting IText", children.get(0) instanceof IText);
+ assertEquals("before", children.get(0).getText());
+ assertTrue("Expecting IParent", children.get(1) instanceof IParent); // the inserted pre element
+ assertEquals("pre1\npre2 end", children.get(1).getText());
+ assertTrue("Expecting IText", children.get(2) instanceof IText);
+ assertEquals("after end", children.get(2).getText());
+ }
+
+ private String createInlineFragment(final String before, final String inner, final String after) {
+ final IContent content = new GapContent(10);
+ final List<Node> nodes = new ArrayList<Node>();
+
+ content.insertText(0, before);
+ final int offset = content.length();
+ content.insertTagMarker(offset);
+ content.insertTagMarker(offset + 1);
+ final Element emp = new Element(EMPHASIS);
+ emp.associate(content, new ContentRange(offset, offset + 1));
+ nodes.add(emp);
+
+ content.insertText(emp.getEndOffset(), inner);
+
+ content.insertText(content.length(), after);
+
+ return new XMLFragment(new DocumentFragment(content, nodes)).getXML();
+ }
+
+ private String createParaFragment() {
+ final IContent content = new GapContent(10);
+ final List<Node> nodes = new ArrayList<Node>();
+
+ content.insertText(0, "before");
+ final int offset = content.length();
+ content.insertTagMarker(offset);
+ content.insertTagMarker(offset + 1);
+ final Element para = new Element(PARA);
+ para.associate(content, new ContentRange(offset, offset + 1));
+ nodes.add(para);
+
+ content.insertText(para.getEndOffset(), "inner");
+
+ content.insertText(content.length(), "after");
+
+ return new XMLFragment(new DocumentFragment(content, nodes)).getXML();
+ }
+
+}
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
index bb85ba2..924fa74 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2010 John Krasnay and others.
+ * Copyright (c) 2004, 2013 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
@@ -9,6 +9,8 @@
* John Krasnay - initial API and implementation
* Florian Thienel - bug 306639 - remove serializability from StyleSheet
* and dependend classes
+ * Carsten Hiesserich - bug 408501 - keep whitespace when copying fragments
+ * into pre elements
*******************************************************************************/
package org.eclipse.vex.core.tests;
@@ -22,6 +24,7 @@ import org.eclipse.vex.core.internal.css.RuleTest;
import org.eclipse.vex.core.internal.dom.AxisTest;
import org.eclipse.vex.core.internal.dom.BasicNodeTest;
import org.eclipse.vex.core.internal.dom.BlockElementBoxTest;
+import org.eclipse.vex.core.internal.dom.ContentRangeTest;
import org.eclipse.vex.core.internal.dom.CopyVisitorTest;
import org.eclipse.vex.core.internal.dom.DTDValidatorTest;
import org.eclipse.vex.core.internal.dom.DeepCopyTest;
@@ -34,13 +37,13 @@ import org.eclipse.vex.core.internal.dom.L1FragmentHandlingTest;
import org.eclipse.vex.core.internal.dom.L1TextHandlingTest;
import org.eclipse.vex.core.internal.dom.NamespaceTest;
import org.eclipse.vex.core.internal.dom.ParentTest;
-import org.eclipse.vex.core.internal.dom.ContentRangeTest;
import org.eclipse.vex.core.internal.io.DocumentContentModelTest;
import org.eclipse.vex.core.internal.io.DocumentReaderTest;
import org.eclipse.vex.core.internal.io.DocumentWriterTest;
import org.eclipse.vex.core.internal.io.NamespaceStackTest;
import org.eclipse.vex.core.internal.io.SpaceNormalizerTest;
import org.eclipse.vex.core.internal.io.TextWrapperTest;
+import org.eclipse.vex.core.internal.io.XMLFragmentTest;
import org.eclipse.vex.core.internal.layout.ImageBoxTest;
import org.eclipse.vex.core.internal.layout.LayoutTestSuite;
import org.eclipse.vex.core.internal.layout.TableLayoutTest;
@@ -52,6 +55,7 @@ import org.eclipse.vex.core.internal.validator.SchemaValidatorTest;
import org.eclipse.vex.core.internal.widget.L2CommentEditingTest;
import org.eclipse.vex.core.internal.widget.L2SelectionTest;
import org.eclipse.vex.core.internal.widget.L2SimpleEditingTest;
+import org.eclipse.vex.core.internal.widget.L2XmlInsertionTest;
import org.eclipse.vex.core.internal.widget.VexWidgetTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -62,7 +66,7 @@ import org.junit.runners.Suite;
L1TextHandlingTest.class, L1CommentHandlingTest.class, L1ElementHandlingTest.class, L1FragmentHandlingTest.class, DocumentFragmentTest.class, CopyVisitorTest.class, DeepCopyTest.class,
PropertyTest.class, RuleTest.class, BlockElementBoxTest.class, ImageBoxTest.class, DocumentWriterTest.class, DTDValidatorTest.class, GapContentTest.class, SpaceNormalizerTest.class,
TextWrapperTest.class, TestBlockElementBox.class, TestBlocksInInlines.class, TestDocumentTextBox.class, TestStaticTextBox.class, TableLayoutTest.class, LayoutTestSuite.class,
- ListenerListTest.class, VexWidgetTest.class, L2SimpleEditingTest.class, L2SelectionTest.class, L2CommentEditingTest.class
+ ListenerListTest.class, XMLFragmentTest.class, VexWidgetTest.class, L2SimpleEditingTest.class, L2SelectionTest.class, L2CommentEditingTest.class, L2XmlInsertionTest.class
})
public class VEXCoreTestSuite {
diff --git a/org.eclipse.vex.core.tests/testResources/test1.dtd b/org.eclipse.vex.core.tests/testResources/test1.dtd
index f1e5607..6f99380 100644
--- a/org.eclipse.vex.core.tests/testResources/test1.dtd
+++ b/org.eclipse.vex.core.tests/testResources/test1.dtd
@@ -9,7 +9,7 @@
<!ELEMENT document (title?, preface, section+, index)>
<!ELEMENT preface (para+)>
<!ELEMENT index (para+)>
-<!ELEMENT pre (#PCDATA)>
+<!ELEMENT pre (#PCDATA | emphasis)*>
<!-- a dummy attribute, just to make sure attribute def serialization is OK -->
<!ATTLIST section
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/IWhitespacePolicy.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/IWhitespacePolicy.java
index 5f4391f..9593426 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/IWhitespacePolicy.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/css/IWhitespacePolicy.java
@@ -13,7 +13,6 @@ package org.eclipse.vex.core.internal.css;
import org.eclipse.vex.core.provisional.dom.INode;
-
/**
* Determines whitespace policy for document elements. For example, a CSS stylesheet implements a whitespace policy via
* its display and white-space properties.
@@ -34,6 +33,19 @@ public interface IWhitespacePolicy {
};
/**
+ * This policy preserves whitespace but knows no blocks.
+ */
+ IWhitespacePolicy PRESERVE_WHITESPACE = new IWhitespacePolicy() {
+ public boolean isBlock(final INode node) {
+ return false;
+ }
+
+ public boolean isPre(final INode node) {
+ return true;
+ }
+ };
+
+ /**
* Returns true if the given element is normally block-formatted.
*
* @param element
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/io/XMLFragment.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/io/XMLFragment.java
new file mode 100644
index 0000000..79b77b9
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/io/XMLFragment.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.eclipse.vex.core.XML;
+import org.eclipse.vex.core.internal.css.IWhitespacePolicy;
+import org.eclipse.vex.core.internal.css.IWhitespacePolicyFactory;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.internal.dom.Namespace;
+import org.eclipse.vex.core.provisional.dom.DocumentContentModel;
+import org.eclipse.vex.core.provisional.dom.DocumentValidationException;
+import org.eclipse.vex.core.provisional.dom.IDocument;
+import org.eclipse.vex.core.provisional.dom.IDocumentFragment;
+import org.eclipse.vex.core.provisional.dom.IValidator;
+import org.xml.sax.SAXException;
+
+/**
+ * A helper class to transfer XML to an {@link IDocumentFragment} and vice versa.
+ */
+public class XMLFragment {
+
+ private static final String XML_PRE = "<?xml version='1.0' encoding='UTF-8'?>\n" + "<vex:vex-fragment xmlns:vex=\"" + Namespace.VEX_NAMESPACE_URI + "\">";
+ private static final String XML_POST = "</vex:vex-fragment>";
+
+ private final String fragmentXML;
+ private IDocumentFragment fragment = null;
+
+ /**
+ * @param xml
+ * The source XML of the wrapped fragment
+ */
+ public XMLFragment(final String xml) {
+ fragmentXML = xml;
+ fragment = null;
+ }
+
+ /**
+ * @param fragment
+ * The fragment to be wrapped
+ */
+ public XMLFragment(final IDocumentFragment fragment) {
+ fragmentXML = fragmentToString(fragment);
+ this.fragment = fragment;
+ }
+
+ private static String fragmentToString(final IDocumentFragment fragment) {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ new DocumentWriter().writeNoWrap(fragment, out);
+ return out.toString("UTF-8");
+ } catch (final IOException e) {
+ // should not happen with a ByteArrayOutputStream
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ /**
+ * @return true if this XML fragment contains only text
+ */
+ public boolean isTextOnly() {
+ return getDocumentFragment().getText().equals(fragmentXML);
+ }
+
+ /**
+ * @return a new instance of XMLFragment where all whitespace is compressed
+ */
+ public XMLFragment compressWhitespace() {
+ return new XMLFragment(XML.compressWhitespace(fragmentXML, true, true, false));
+ }
+
+ /**
+ * @return The raw XML of the wrapped fragment.
+ */
+ public String getXML() {
+ return fragmentXML;
+ }
+
+ /**
+ * @return An IDocumentFragment created from the parsed XML string, whitespace is preserved.
+ * @throws DocumentValidationException
+ * when the given String is no valid XML fragment
+ */
+ public IDocumentFragment getDocumentFragment() throws DocumentValidationException {
+ return getDocumentFragment(IWhitespacePolicy.PRESERVE_WHITESPACE);
+ }
+
+ /**
+ * @param whitespacePolicy
+ * the IWhitespacePolicy to apply when creating the IDocumentFragment
+ * @return An IDocumentFragment created from the parsed XML String.
+ * @throws DocumentValidationException
+ * when the given String is no valid XML fragment
+ */
+ public IDocumentFragment getDocumentFragment(final IWhitespacePolicy whitespacePolicy) throws DocumentValidationException {
+ if (fragment != null) {
+ return fragment;
+ }
+
+ try {
+ final DocumentReader reader = new DocumentReader();
+
+ reader.setWhitespacePolicyFactory(new IWhitespacePolicyFactory() {
+ public IWhitespacePolicy createPolicy(final IValidator validator, final DocumentContentModel documentContentModel, final StyleSheet styleSheet) {
+ return whitespacePolicy;
+ }
+ });
+
+ final String xml = wrap();
+ final IDocument document = reader.read(xml);
+ fragment = document.getFragment(document.getRootElement().getRange().resizeBy(1, -1));
+ return fragment;
+ } catch (final IOException e) {
+ e.printStackTrace();
+ } catch (final ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (final SAXException e) {
+ // Text is no valid XML;
+ fragment = null;
+ throw new DocumentValidationException(e.getLocalizedMessage());
+ }
+ fragment = null;
+ return null;
+ }
+
+ private String wrap() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(XML_PRE);
+ sb.append(fragmentXML);
+ sb.append(XML_POST);
+ return sb.toString();
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/IVexWidget.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/IVexWidget.java
index a0353cc..4958003 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/IVexWidget.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/IVexWidget.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2008 John Krasnay and others.
+ * Copyright (c) 2004, 2013 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
@@ -7,6 +7,7 @@
*
* Contributors:
* John Krasnay - initial API and implementation
+ * Carsten Hiesserich - insertXML (bug 408501)
*******************************************************************************/
package org.eclipse.vex.core.internal.widget;
@@ -271,6 +272,15 @@ public interface IVexWidget {
public void insertText(String text) throws DocumentValidationException;
/**
+ * Inserts the given XML fragment at the current caret position. Any selected content is first deleted.
+ *
+ * @param xml
+ * XML to insert
+ * @throws DocumentValidationException
+ */
+ public void insertXML(String xml) throws DocumentValidationException;
+
+ /**
* Inserts a comment a the current caret position. Any selected content is first deleted.
*
* @return the new comment
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
index 039b58b..c2e3649 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2008 John Krasnay and others.
+ * Copyright (c) 2004, 2013 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
@@ -11,7 +11,7 @@
* Holger Voormann - bug 315914: content assist should only show elements
* valid in the current context
* Carsten Hiesserich - handling of elements within comments (bug 407801)
- * Carsten Hiesserich - allow insertion of newline into pre elements (bug 407827)
+ * Carsten Hiesserich - handling of preformatted elements, XML insertion(bug 407827, bug 408501 )
*******************************************************************************/
package org.eclipse.vex.core.internal.widget;
@@ -40,6 +40,7 @@ import org.eclipse.vex.core.internal.css.StyleSheet;
import org.eclipse.vex.core.internal.css.StyleSheetReader;
import org.eclipse.vex.core.internal.dom.Document;
import org.eclipse.vex.core.internal.dom.Node;
+import org.eclipse.vex.core.internal.io.XMLFragment;
import org.eclipse.vex.core.internal.layout.BlockBox;
import org.eclipse.vex.core.internal.layout.Box;
import org.eclipse.vex.core.internal.layout.BoxFactory;
@@ -59,6 +60,7 @@ import org.eclipse.vex.core.internal.undo.InsertElementEdit;
import org.eclipse.vex.core.internal.undo.InsertFragmentEdit;
import org.eclipse.vex.core.internal.undo.InsertTextEdit;
import org.eclipse.vex.core.provisional.dom.AttributeChangeEvent;
+import org.eclipse.vex.core.provisional.dom.BaseNodeVisitor;
import org.eclipse.vex.core.provisional.dom.BaseNodeVisitorWithResult;
import org.eclipse.vex.core.provisional.dom.ContentChangeEvent;
import org.eclipse.vex.core.provisional.dom.ContentRange;
@@ -883,6 +885,77 @@ public class VexWidgetImpl implements IVexWidget {
}
}
+ public void insertXML(final String xml) throws DocumentValidationException, ReadOnlyException {
+ if (readOnly) {
+ throw new ReadOnlyException("Cannot insert text, because the editor is read-only.");
+ }
+
+ final XMLFragment wrappedFragment = new XMLFragment(xml);
+
+ // If fragment contains only simple Text, use insertText to ensure consistent behavior
+ if (wrappedFragment.isTextOnly()) {
+ insertText(wrappedFragment.getXML());
+ return;
+ }
+
+ final IElement element = getBlockForInsertionAt(getCaretOffset());
+ final boolean isPreformatted = whitespacePolicy.isPre(element);
+
+ try {
+ final IDocumentFragment fragment = wrappedFragment.getDocumentFragment();
+
+ if (document.canInsertFragment(getCaretOffset(), fragment)) {
+ insertFragment(fragment);
+ } else if (document.canInsertText(getCaretOffset())) {
+ insertText(fragment.getText());
+ }
+ } catch (final DocumentValidationException e) {
+ // given XML is not valid - Insert text instead if target is preformatted
+ if (isPreformatted) {
+ insertText(wrappedFragment.getXML());
+ } else {
+ throw e;
+ }
+ }
+
+ applyWhitespacePolicy(element, whitespacePolicy);
+ }
+
+ private static void applyWhitespacePolicy(final INode node, final IWhitespacePolicy whitespacePolicy) {
+ final IDocument document = node.getDocument();
+ if (document == null) {
+ throw new IllegalArgumentException("applyWhitespacePolicy works only for nodes in documents!");
+ }
+ node.accept(new BaseNodeVisitor() {
+ @Override
+ public void visit(final IDocument document) {
+ document.children().accept(this);
+ }
+
+ @Override
+ public void visit(final IDocumentFragment fragment) {
+ fragment.children().accept(this);
+ }
+
+ @Override
+ public void visit(final IElement element) {
+ element.children().accept(this);
+ }
+
+ @Override
+ public void visit(final IText text) {
+ final IParent parentElement = text.ancestors().matching(Filters.elements()).first();
+ if (!whitespacePolicy.isPre(parentElement)) {
+ final String compressedContent = XML.compressWhitespace(text.getText(), false, false, false);
+ final ContentRange originalTextRange = text.getRange();
+ document.delete(originalTextRange);
+ document.insertText(originalTextRange.getStartOffset(), compressedContent);
+ }
+
+ }
+ });
+ }
+
private IElement getBlockForInsertionAt(final int offset) {
final IElement element = getDocument().getElementForInsertionAt(offset);
diff --git a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/swt/tests/DocumentFragmentTransferTest.java b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/swt/tests/DocumentFragmentTransferTest.java
index 0a84e7d..c5c5757 100644
--- a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/swt/tests/DocumentFragmentTransferTest.java
+++ b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/swt/tests/DocumentFragmentTransferTest.java
@@ -12,9 +12,6 @@ package org.eclipse.vex.ui.internal.swt.tests;
import static org.eclipse.vex.core.internal.io.RoundTrip.assertContentEqual;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.vex.core.internal.dom.Document;
import org.eclipse.vex.core.provisional.dom.IComment;
@@ -87,10 +84,9 @@ public class DocumentFragmentTransferTest {
}
private static void assertRoundTripWorks(final IDocumentFragment expectedFragment) throws Exception {
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- final DocumentFragmentTransfer transfer = new DocumentFragmentTransfer();
- transfer.writeFragmentToStream(expectedFragment, buffer);
- final IDocumentFragment actualFragment = transfer.readFragmentFromStream(new ByteArrayInputStream(buffer.toByteArray()));
+ final DocumentFragmentTransfer transfer = DocumentFragmentTransfer.getInstance();
+ final byte[] buffer = transfer.writeFragmentToStream(expectedFragment);
+ final IDocumentFragment actualFragment = transfer.readFragmentFromStream(buffer);
assertContentEqual(expectedFragment, actualFragment);
}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/DocumentFragmentTransfer.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/DocumentFragmentTransfer.java
index 2d40d23..104f00e 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/DocumentFragmentTransfer.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/DocumentFragmentTransfer.java
@@ -8,25 +8,16 @@
* Contributors:
* John Krasnay - initial API and implementation
* Florian Thienel - use XML reader/writer for serialization
+ * Carsten Hiesserich - moved serialization to XMLFragment
*******************************************************************************/
package org.eclipse.vex.ui.internal.swt;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.xml.parsers.ParserConfigurationException;
+import java.io.UnsupportedEncodingException;
import org.eclipse.swt.dnd.ByteArrayTransfer;
import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.vex.core.internal.io.DocumentReader;
-import org.eclipse.vex.core.internal.io.DocumentWriter;
-import org.eclipse.vex.core.provisional.dom.IDocument;
+import org.eclipse.vex.core.internal.io.XMLFragment;
import org.eclipse.vex.core.provisional.dom.IDocumentFragment;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
/**
* Transfer object that handles Vex DocumentFragments.
@@ -73,16 +64,18 @@ public class DocumentFragmentTransfer extends ByteArrayTransfer {
}
final IDocumentFragment fragment = (IDocumentFragment) object;
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- try {
- writeFragmentToStream(fragment, out);
- } catch (final IOException e) {
- }
- super.javaToNative(out.toByteArray(), transferData);
+
+ super.javaToNative(writeFragmentToStream(fragment), transferData);
}
- public void writeFragmentToStream(final IDocumentFragment fragment, final OutputStream out) throws IOException {
- new DocumentWriter().write(fragment, out);
+ public byte[] writeFragmentToStream(final IDocumentFragment fragment) {
+ final XMLFragment wrapper = new XMLFragment(fragment);
+ try {
+ return wrapper.getXML().getBytes("UTF-8");
+ } catch (final UnsupportedEncodingException e) {
+ // This should not happen with UTF-8
+ return new byte[0];
+ }
}
// Reading
@@ -94,30 +87,21 @@ public class DocumentFragmentTransfer extends ByteArrayTransfer {
if (buffer == null) {
return null;
}
- final ByteArrayInputStream in = new ByteArrayInputStream(buffer);
- try {
- return readFragmentFromStream(in);
- } catch (final IOException ex) {
- return null;
- }
+ return readFragmentFromStream(buffer);
}
return null;
}
- public IDocumentFragment readFragmentFromStream(final InputStream in) throws IOException {
+ public IDocumentFragment readFragmentFromStream(final byte[] in) {
try {
- final IDocument document = new DocumentReader().read(new InputSource(in));
- return document.getFragment(document.getRootElement().getRange().resizeBy(1, -1));
- } catch (final ParserConfigurationException e) {
- // TODO shoult never happen - log this exception?
- e.printStackTrace();
- return null;
- } catch (final SAXException e) {
- // TODO shoult never happen - log this exception?
- e.printStackTrace();
+ final XMLFragment xmlFragment = new XMLFragment(new String(in, "UTF-8"));
+ return xmlFragment.getDocumentFragment();
+ } catch (final UnsupportedEncodingException e) {
+ // This should not happen with UTF-8
return null;
}
}
+
}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/VexWidget.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/VexWidget.java
index 40b1ca7..4974033 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/VexWidget.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/swt/VexWidget.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2008 John Krasnay and others.
+ * Copyright (c) 2004, 2013 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
@@ -8,6 +8,7 @@
* Contributors:
* John Krasnay - initial API and implementation
* Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ * Carsten Hiesserich - changed fragment pasting to allow XML content
*******************************************************************************/
package org.eclipse.vex.ui.internal.swt;
@@ -60,6 +61,7 @@ import org.eclipse.vex.core.internal.core.Graphics;
import org.eclipse.vex.core.internal.core.Rectangle;
import org.eclipse.vex.core.internal.css.IWhitespacePolicy;
import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.internal.dom.DocumentFragment;
import org.eclipse.vex.core.internal.layout.Box;
import org.eclipse.vex.core.internal.layout.BoxFactory;
import org.eclipse.vex.core.internal.widget.IBoxFilter;
@@ -292,6 +294,10 @@ public class VexWidget extends Canvas implements IVexWidget, ISelectionProvider
impl.insertText(text);
}
+ public void insertXML(final String xml) throws DocumentValidationException {
+ impl.insertXML(xml);
+ }
+
public IComment insertComment() throws DocumentValidationException {
return impl.insertComment();
}
@@ -358,7 +364,7 @@ public class VexWidget extends Canvas implements IVexWidget, ISelectionProvider
}
final Clipboard clipboard = new Clipboard(getDisplay());
- final IDocumentFragment fragment = (IDocumentFragment) clipboard.getContents(DocumentFragmentTransfer.getInstance());
+ final DocumentFragment fragment = (DocumentFragment) clipboard.getContents(DocumentFragmentTransfer.getInstance());
if (fragment != null) {
insertFragment(fragment);
} else {