diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-01-03 11:36:08 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-01-03 11:36:08 +0000 |
| commit | df0e65ca725de5fb471abdd7ff8621904e41a209 (patch) | |
| tree | 627dfab26fb684b74a65b9b6247912abfc1688c3 | |
| parent | 1b5c6e99770d3b555c017751f29261037d2e540d (diff) | |
Collapse alternate content elements using fallback
3 files changed, 117 insertions, 9 deletions
diff --git a/src/main/java/org/zwobble/mammoth/docx/OfficeXml.java b/src/main/java/org/zwobble/mammoth/docx/OfficeXml.java index 98ec1bf..a1d156f 100644 --- a/src/main/java/org/zwobble/mammoth/docx/OfficeXml.java +++ b/src/main/java/org/zwobble/mammoth/docx/OfficeXml.java @@ -1,10 +1,18 @@ package org.zwobble.mammoth.docx; import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableList; import org.zwobble.mammoth.xml.XmlElement; +import org.zwobble.mammoth.xml.XmlNode; +import org.zwobble.mammoth.xml.XmlNodeVisitor; +import org.zwobble.mammoth.xml.XmlTextNode; import org.zwobble.mammoth.xml.parsing.XmlParser; import java.io.InputStream; +import java.util.List; + +import static com.google.common.collect.Iterables.concat; +import static com.google.common.collect.Iterables.transform; public class OfficeXml { private static ImmutableBiMap<String, String> XML_NAMESPACES = ImmutableBiMap.<String, String>builder() @@ -21,6 +29,29 @@ public class OfficeXml { .build(); public static XmlElement parseXml(InputStream inputStream) { - return new XmlParser(XML_NAMESPACES).parseStream(inputStream); + XmlParser parser = new XmlParser(XML_NAMESPACES); + return (XmlElement)collapseAlternateContent(parser.parseStream(inputStream)).get(0); + } + + private static List<XmlNode> collapseAlternateContent(XmlNode node) { + return node.accept(new XmlNodeVisitor<List<XmlNode>>() { + @Override + public List<XmlNode> visit(XmlElement element) { + if (element.getName().equals("mc:AlternateContent")) { + return element.findChild("mc:Fallback").getChildren(); + } else { + XmlElement collapsedElement = new XmlElement( + element.getName(), + element.getAttributes(), + ImmutableList.copyOf(concat(transform(element.children(), OfficeXml::collapseAlternateContent)))); + return ImmutableList.of(collapsedElement); + } + } + + @Override + public List<XmlNode> visit(XmlTextNode textNode) { + return ImmutableList.of(textNode); + } + }); } } diff --git a/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java b/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java index ef1fa6e..09258af 100644 --- a/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java +++ b/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java @@ -3,6 +3,7 @@ package org.zwobble.mammoth.tests; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -11,6 +12,7 @@ import org.hamcrest.TypeSafeDiagnosingMatcher; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; +import java.util.Map; import java.util.Optional; import static com.google.common.collect.Iterables.filter; @@ -38,6 +40,10 @@ public class DeepReflectionMatcher<T> extends TypeSafeDiagnosingMatcher<T> { return matchesList(path, (List)expected, (List)actual, mismatchDescription); } + if (expected instanceof Map && actual instanceof Map) { + return matchesMap(path, (Map)expected, (Map)actual, mismatchDescription); + } + if (!expected.getClass().equals(actual.getClass())) { mismatchDescription.appendText("was " + actual.getClass().getName()); return false; @@ -63,14 +69,14 @@ public class DeepReflectionMatcher<T> extends TypeSafeDiagnosingMatcher<T> { if (actual.size() > expected.size()) { appendPath(mismatchDescription, path); mismatchDescription.appendText("extra elements:" + - indentedList(transform(Iterables.skip(actual, expected.size()), DeepReflectionMatcher::generateDescriptionOfValue))); + indentedList(transform(Iterables.skip(actual, expected.size()), DeepReflectionMatcher::describeValue))); return false; } if (actual.size() < expected.size()) { appendPath(mismatchDescription, path); mismatchDescription.appendText("missing elements:" + - indentedList(transform(Iterables.skip(expected, actual.size()), DeepReflectionMatcher::generateDescriptionOfValue))); + indentedList(transform(Iterables.skip(expected, actual.size()), DeepReflectionMatcher::describeValue))); return false; } @@ -83,10 +89,40 @@ public class DeepReflectionMatcher<T> extends TypeSafeDiagnosingMatcher<T> { return true; } + private static <T> boolean matchesMap(String path, Map<?, ?> expected, Map<?, ?> actual, Description mismatchDescription) { + if (!handleExtraElements(path, expected, actual, mismatchDescription, "extra elements:")) { + return false; + } + if (!handleExtraElements(path, actual, expected, mismatchDescription, "missing elements:")) { + return false; + } + + for (Object key : expected.keySet()) { + if (!matchesSafely(path + "[" + key + "]", expected.get(key), actual.get(key), mismatchDescription)) { + return false; + } + } + + return true; + } + + private static boolean handleExtraElements(String path, Map<?, ?> expected, Map<?, ?> actual, Description mismatchDescription, String prefix) { + Sets.SetView<?> extraElements = Sets.difference(actual.keySet(), expected.keySet()); + if (!extraElements.isEmpty()) { + appendPath(mismatchDescription, path); + mismatchDescription.appendText(prefix + + indentedList(transform( + extraElements, + key -> describeValue(key) + "=" + describeValue(actual.get(key))))); + return false; + } + return true; + } + private static boolean matchesOptional(String path, Optional expected, Optional actual, Description mismatchDescription) { if (actual.isPresent() && !expected.isPresent()) { appendPath(mismatchDescription, path); - mismatchDescription.appendText("had value " + generateDescriptionOfValue(actual.get())); + mismatchDescription.appendText("had value " + describeValue(actual.get())); return false; } @@ -120,28 +156,35 @@ public class DeepReflectionMatcher<T> extends TypeSafeDiagnosingMatcher<T> { @Override public void describeTo(Description description) { - description.appendText(generateDescriptionOfValue(expected)); + description.appendText(describeValue(expected)); } - private static String generateDescriptionOfValue(Object value) { + private static String describeValue(Object value) { if (value instanceof String) { return value.toString(); } else if (value instanceof Optional) { Optional<?> optional = (Optional)value; if (optional.isPresent()) { - return generateDescriptionOfValue(optional.get()); + return describeValue(optional.get()); } else { return "(empty)"; } } else if (value instanceof List) { List<?> list = (List)value; - return "[" + indentedList(transform(list, DeepReflectionMatcher::generateDescriptionOfValue)) + "]"; + return "[" + indentedList(transform(list, DeepReflectionMatcher::describeValue)) + "]"; + } else if (value instanceof Map) { + Map<?, ?> map = (Map)value; + String entries = indentedList( + transform( + map.entrySet(), + entry -> describeValue(entry.getKey()) + "=" + describeValue(entry.getValue()))); + return "{" + entries + "}"; } else { Class<?> clazz = value.getClass(); List<Field> fields = fields(clazz); Iterable<String> fieldStrings = transform( fields, - field -> field.getName() + "=" + generateDescriptionOfValue(readField(value, field))); + field -> field.getName() + "=" + describeValue(readField(value, field))); return String.format("%s(%s)", clazz.getSimpleName(), indentedList(fieldStrings)); } } diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/OfficeXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/OfficeXmlTests.java new file mode 100644 index 0000000..6b2fb77 --- /dev/null +++ b/src/test/java/org/zwobble/mammoth/tests/docx/OfficeXmlTests.java @@ -0,0 +1,34 @@ +package org.zwobble.mammoth.tests.docx; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.zwobble.mammoth.docx.OfficeXml; +import org.zwobble.mammoth.xml.XmlElement; + +import java.io.ByteArrayInputStream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.zwobble.mammoth.tests.DeepReflectionMatcher.deepEquals; +import static org.zwobble.mammoth.xml.XmlNodes.element; + +public class OfficeXmlTests { + @Test + public void alternateContentIsReplacedByContentsOfFallback() { + String xmlString = + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + + "<numbering xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\">" + + "<mc:AlternateContent>" + + "<mc:Choice Requires=\"w14\">" + + "<choice/>" + + "</mc:Choice>" + + "<mc:Fallback>" + + "<fallback/>" + + "</mc:Fallback>" + + "</mc:AlternateContent>" + + "</numbering>"; + + XmlElement result = OfficeXml.parseXml(new ByteArrayInputStream(xmlString.getBytes(Charsets.UTF_8))); + assertThat(result.children(), deepEquals(ImmutableList.of(element("fallback")))); + } +} |
