summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/archives/Archive.java1
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java5
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java5
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java36
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/util/Lists.java8
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java5
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/docx/DocumentReaderTests.java60
7 files changed, 116 insertions, 4 deletions
diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java b/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java
index 4440234..2f5a4ae 100644
--- a/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java
+++ b/src/main/java/org/zwobble/mammoth/internal/archives/Archive.java
@@ -7,4 +7,5 @@ import java.util.Optional;
public interface Archive extends Closeable {
Optional<InputStream> tryGetInputStream(String name) throws IOException;
+ boolean exists(String name);
}
diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java b/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java
index 9ad7134..0844e8d 100644
--- a/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java
+++ b/src/main/java/org/zwobble/mammoth/internal/archives/InMemoryArchive.java
@@ -46,6 +46,11 @@ public class InMemoryArchive implements MutableArchive {
}
@Override
+ public boolean exists(String name) {
+ return entries.containsKey(name);
+ }
+
+ @Override
public void writeEntry(String path, String content) {
entries.put(path, content.getBytes(StandardCharsets.UTF_8));
}
diff --git a/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java b/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java
index 853ced9..47be074 100644
--- a/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java
+++ b/src/main/java/org/zwobble/mammoth/internal/archives/ZippedArchive.java
@@ -29,6 +29,11 @@ public class ZippedArchive implements Archive {
}
@Override
+ public boolean exists(String name) {
+ return zipFile.getEntry(name) != null;
+ }
+
+ @Override
public void close() throws IOException {
zipFile.close();
}
diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
index 4ef9c37..9d1b270 100644
--- a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
+++ b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
@@ -14,7 +14,8 @@ import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
-import static org.zwobble.mammoth.internal.util.Lists.list;
+import static org.zwobble.mammoth.internal.util.Lists.*;
+import static org.zwobble.mammoth.internal.util.Strings.trimLeft;
public class DocumentReader {
@FunctionalInterface
@@ -23,12 +24,15 @@ public class DocumentReader {
}
public static InternalResult<Document> readDocument(Optional<Path> path, Archive zipFile) {
+ Relationships packageRelationships = readPackageRelationships(zipFile);
+ String documentFilename = findDocumentFilename(zipFile, packageRelationships);
+
Styles styles = readStyles(zipFile);
Numbering numbering = readNumbering(zipFile);
ContentTypes contentTypes = readContentTypes(zipFile);
FileReader fileReader = new PathRelativeFileReader(path);
BodyReaders bodyReaders = name -> {
- Relationships relationships = readRelationships(zipFile, name);
+ Relationships relationships = readRelationshipsFor(zipFile, name);
return new BodyXmlReader(styles, numbering, relationships, contentTypes, zipFile, fileReader);
};
return InternalResult.flatMap(
@@ -36,10 +40,30 @@ public class DocumentReader {
readComments(zipFile, bodyReaders),
(notes, comments) -> {
DocumentXmlReader reader = new DocumentXmlReader(bodyReaders.forName("document"), notes, comments);
- return reader.readElement(parseOfficeXml(zipFile, "word/document.xml"));
+ return reader.readElement(parseOfficeXml(zipFile, documentFilename));
});
}
+ private static Relationships readPackageRelationships(Archive archive) {
+ return readRelationships(archive, "_rels/.rels");
+ }
+
+ private static String findDocumentFilename(Archive archive, Relationships packageRelationships) {
+ String officeDocumentType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
+ List<String> targets = eagerConcat(
+ eagerMap(
+ packageRelationships.findTargetsByType(officeDocumentType),
+ target -> trimLeft(target, '/')
+ ),
+ list("word/document.xml")
+ );
+ List<String> validTargets = eagerFilter(targets, archive::exists);
+ return tryGetFirst(validTargets)
+ .orElseThrow(() -> new PassThroughException(
+ new IOException("Could not find word/document.xml in ZIP file. Are you sure this is a valid .docx file?")
+ ));
+ }
+
private static InternalResult<List<Comment>> readComments(Archive file, BodyReaders bodyReaders) {
return tryParseOfficeXml(file, "word/comments.xml")
.map(new CommentXmlReader(bodyReaders.forName("comments"))::readElement)
@@ -75,8 +99,12 @@ public class DocumentReader {
.orElse(ContentTypes.DEFAULT);
}
+ private static Relationships readRelationshipsFor(Archive zipFile, String name) {
+ return readRelationships(zipFile, "word/_rels/" + name + ".xml.rels");
+ }
+
private static Relationships readRelationships(Archive zipFile, String name) {
- return tryParseOfficeXml(zipFile, "word/_rels/" + name + ".xml.rels")
+ return tryParseOfficeXml(zipFile, name)
.map(RelationshipsXml::readRelationshipsXmlElement)
.orElse(Relationships.EMPTY);
}
diff --git a/src/main/java/org/zwobble/mammoth/internal/util/Lists.java b/src/main/java/org/zwobble/mammoth/internal/util/Lists.java
index 3fbb5dd..b6d6570 100644
--- a/src/main/java/org/zwobble/mammoth/internal/util/Lists.java
+++ b/src/main/java/org/zwobble/mammoth/internal/util/Lists.java
@@ -80,6 +80,14 @@ public class Lists {
return stream(iterable).collect(Collectors.toList());
}
+ public static <T> Optional<T> tryGetFirst(List<T> list) {
+ if (list.isEmpty()) {
+ return Optional.empty();
+ } else {
+ return Optional.of(list.get(0));
+ }
+ }
+
public static <T> Optional<T> tryGetLast(List<T> list) {
if (list.isEmpty()) {
return Optional.empty();
diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
index acbf95f..1a172ce 100644
--- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlReaderMakers.java
@@ -23,6 +23,11 @@ public class BodyXmlReaderMakers {
}
@Override
+ public boolean exists(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void close() throws IOException {
}
}),
diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/DocumentReaderTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/DocumentReaderTests.java
new file mode 100644
index 0000000..d7f1a11
--- /dev/null
+++ b/src/test/java/org/zwobble/mammoth/tests/docx/DocumentReaderTests.java
@@ -0,0 +1,60 @@
+package org.zwobble.mammoth.tests.docx;
+
+import org.junit.jupiter.api.Test;
+import org.zwobble.mammoth.internal.archives.InMemoryArchive;
+import org.zwobble.mammoth.internal.documents.Document;
+import org.zwobble.mammoth.internal.docx.DocumentReader;
+import org.zwobble.mammoth.internal.results.InternalResult;
+import org.zwobble.mammoth.internal.xml.NamespacePrefixes;
+import org.zwobble.mammoth.internal.xml.XmlWriter;
+
+import java.util.Optional;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.zwobble.mammoth.internal.util.Lists.list;
+import static org.zwobble.mammoth.internal.util.Maps.map;
+import static org.zwobble.mammoth.internal.xml.XmlNodes.element;
+import static org.zwobble.mammoth.internal.xml.XmlNodes.text;
+import static org.zwobble.mammoth.tests.ResultMatchers.isInternalSuccess;
+import static org.zwobble.mammoth.tests.documents.DocumentElementMakers.*;
+
+public class DocumentReaderTests {
+ private final NamespacePrefixes mainDocumentNamespaces = NamespacePrefixes.builder()
+ .put("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")
+ .build();
+ private final NamespacePrefixes relationshipsNamespaces = NamespacePrefixes.builder()
+ .defaultPrefix("http://schemas.openxmlformats.org/package/2006/relationships")
+ .build();
+
+ @Test
+ public void mainDocumentIsFoundUsingPackageRelationships() {
+ InternalResult<Document> result = DocumentReader.readDocument(Optional.empty(), InMemoryArchive.fromStrings(map(
+ "word/document2.xml", XmlWriter.toString(
+ element("w:document", list(
+ element("w:body", list(
+ element("w:p", list(
+ element("w:r", list(
+ element("w:t", list(text("Hello.")))
+ ))
+ ))
+ ))
+ )),
+ mainDocumentNamespaces
+ ),
+ "_rels/.rels", XmlWriter.toString(
+ element("Relationships", list(
+ element("Relationship", map(
+ "Id", "rId1",
+ "Target", "/word/document2.xml",
+ "Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
+ ))
+ )),
+ relationshipsNamespaces
+ )
+ )));
+
+ assertThat(result, isInternalSuccess(document(
+ withChildren(paragraphWithText("Hello."))
+ )));
+ }
+}