From b31c5a4b4ccb835a583efa33cee178e122d564de Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 19 Feb 2017 18:16:52 +0000 Subject: Handle hyperlinks created with complex fields --- .../zwobble/mammoth/tests/docx/BodyXmlTests.java | 188 +++++++++++++++++++-- .../mammoth/tests/docx/DocumentMatchers.java | 79 +++++++++ 2 files changed, 250 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/zwobble/mammoth/tests/docx/DocumentMatchers.java (limited to 'src/test') diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java index 3f3dca8..0bbd672 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java @@ -13,7 +13,6 @@ import org.zwobble.mammoth.internal.results.InternalResult; import org.zwobble.mammoth.internal.xml.XmlElement; import org.zwobble.mammoth.internal.xml.XmlNode; import org.zwobble.mammoth.internal.xml.XmlNodes; -import org.zwobble.mammoth.tests.DeepReflectionMatcher; import java.io.IOException; import java.io.InputStream; @@ -34,6 +33,7 @@ import static org.zwobble.mammoth.tests.DeepReflectionMatcher.deepEquals; import static org.zwobble.mammoth.tests.ResultMatchers.*; import static org.zwobble.mammoth.tests.documents.DocumentElementMakers.*; import static org.zwobble.mammoth.tests.docx.BodyXmlReaderMakers.bodyReader; +import static org.zwobble.mammoth.tests.docx.DocumentMatchers.*; import static org.zwobble.mammoth.tests.docx.OfficeXmlBuilders.*; public class BodyXmlTests { @@ -147,6 +147,168 @@ public class BodyXmlTests { hasNumbering(Optional.empty())); } + public static class ComplexFieldsTests { + private static final String URI ="http://example.com"; + private static final XmlElement BEGIN_COMPLEX_FIELD = element("w:r", list( + element("w:fldChar", map("w:fldCharType", "begin")) + )); + private static final XmlElement SEPARATE_COMPLEX_FIELD = element("w:r", list( + element("w:fldChar", map("w:fldCharType", "separate")) + )); + private static final XmlElement END_COMPLEX_FIELD = element("w:r", list( + element("w:fldChar", map("w:fldCharType", "end")) + )); + private static final XmlElement HYPERLINK_INSTRTEXT = element("w:instrText", list( + XmlNodes.text(" HYPERLINK \"" + URI + '"') + )); + private static Matcher isEmptyHyperlinkedRun() { + return isHyperlinkedRun(hasChildren()); + } + @SafeVarargs + private static Matcher isHyperlinkedRun(Matcher... matchers) { + return isRun(hasChildren( + allOf( + isHyperlink(hasHref(URI)), + isHyperlink(matchers) + ) + )); + } + + @Test + public void runsInAComplexFieldForHyperlinksAreReadAsHyperlinks() { + XmlElement hyperlinkRunXml = runXml("this is a hyperlink"); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + HYPERLINK_INSTRTEXT, + SEPARATE_COMPLEX_FIELD, + hyperlinkRunXml, + END_COMPLEX_FIELD + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isHyperlinkedRun(hasChildren( + isTextElement("this is a hyperlink") + )), + isEmptyRun() + ))); + } + + @Test + public void runsAfterAComplexFieldForHyperlinksAreNotReadAsHyperlinks() { + XmlElement afterEndXml = runXml("this will not be a hyperlink"); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + HYPERLINK_INSTRTEXT, + SEPARATE_COMPLEX_FIELD, + END_COMPLEX_FIELD, + afterEndXml + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isEmptyRun(), + isRun(hasChildren( + isTextElement("this will not be a hyperlink") + )) + ))); + } + + @Test + public void canHandleSplitInstrTextElements() { + XmlElement hyperlinkRunXml = runXml("this is a hyperlink"); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + element("w:instrText", list( + XmlNodes.text(" HYPE") + )), + element("w:instrText", list( + XmlNodes.text("RLINK \"" + URI + '"') + )), + SEPARATE_COMPLEX_FIELD, + hyperlinkRunXml, + END_COMPLEX_FIELD + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isHyperlinkedRun(hasChildren( + isTextElement("this is a hyperlink") + )), + isEmptyRun() + ))); + } + + @Test + public void hyperlinkIsNotEndedByEndOfNestedComplexField() { + XmlElement authorInstrText = element("w:instrText", list( + XmlNodes.text(" AUTHOR \"John Doe\"") + )); + XmlElement hyperlinkRunXml = runXml("this is a hyperlink"); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + HYPERLINK_INSTRTEXT, + SEPARATE_COMPLEX_FIELD, + BEGIN_COMPLEX_FIELD, + authorInstrText, + SEPARATE_COMPLEX_FIELD, + END_COMPLEX_FIELD, + hyperlinkRunXml, + END_COMPLEX_FIELD + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isEmptyHyperlinkedRun(), + isEmptyHyperlinkedRun(), + isEmptyHyperlinkedRun(), + isHyperlinkedRun(hasChildren( + isTextElement("this is a hyperlink") + )), + isEmptyRun() + ))); + } + + @Test + public void complexFieldNestedWithinAHyperlinkComplexFieldIsWrappedWithTheHyperlink() { + XmlElement authorInstrText = element("w:instrText", list( + XmlNodes.text(" AUTHOR \"John Doe\"") + )); + XmlElement element = paragraphXml(list( + BEGIN_COMPLEX_FIELD, + HYPERLINK_INSTRTEXT, + SEPARATE_COMPLEX_FIELD, + BEGIN_COMPLEX_FIELD, + authorInstrText, + SEPARATE_COMPLEX_FIELD, + runXml("John Doe"), + END_COMPLEX_FIELD, + END_COMPLEX_FIELD + )); + DocumentElement paragraph = readSuccess(bodyReader(), element); + + assertThat(paragraph, isParagraph(hasChildren( + isEmptyRun(), + isEmptyHyperlinkedRun(), + isEmptyHyperlinkedRun(), + isEmptyHyperlinkedRun(), + isHyperlinkedRun(hasChildren( + isTextElement("John Doe") + )), + isEmptyHyperlinkedRun(), + isEmptyRun() + ))); + } + } + @Test public void runHasNoStyleIfItHasNoProperties() { XmlElement element = runXml(list()); @@ -904,11 +1066,15 @@ public class BodyXmlTests { return paragraphXml(list()); } - private XmlElement paragraphXml(List children) { + private static XmlElement paragraphXml(List children) { return element("w:p", children); } - private XmlElement runXml(List children) { + private static XmlElement runXml(String text) { + return runXml(list(textXml(text))); + } + + private static XmlElement runXml(List children) { return element("w:r", children); } @@ -916,22 +1082,10 @@ public class BodyXmlTests { return element("w:r", list(element("w:rPr", asList(children)))); } - private XmlElement textXml(String value) { + private static XmlElement textXml(String value) { return element("w:t", list(XmlNodes.text(value))); } - private Matcher isParagraph(Paragraph expected) { - return new DeepReflectionMatcher<>(expected); - } - - private Matcher isRun(Run expected) { - return new DeepReflectionMatcher<>(expected); - } - - private Matcher isTextElement(String value) { - return new DeepReflectionMatcher<>(new Text(value)); - } - private Matcher hasStyle(Optional