forked from psiegman/epublib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue psiegman#127 The directory name for content should be configura…
…ble.
- Loading branch information
1 parent
5df3765
commit ef9d4fb
Showing
4 changed files
with
231 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriterConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package nl.siegmann.epublib.epub; | ||
|
||
/** | ||
* Allows for the configuration of an {@link EpubWriter}. | ||
*/ | ||
public class EpubWriterConfiguration { | ||
public static final String DEFAULT_CONTENT_DIRECTORY_NAME = "OEBPS"; | ||
|
||
private String contentDirectoryName = DEFAULT_CONTENT_DIRECTORY_NAME; | ||
|
||
/** | ||
* Creates a default configuration. | ||
*/ | ||
public EpubWriterConfiguration() { | ||
} | ||
|
||
/** | ||
* Builder-style method to change the directory name. | ||
* | ||
* @param contentDirectoryName New directory name. | ||
* @return EpubWriterConfiguration | ||
*/ | ||
public EpubWriterConfiguration withContentDirectoryName(String contentDirectoryName) { | ||
this.contentDirectoryName = contentDirectoryName; | ||
return this; | ||
} | ||
|
||
/** | ||
* Returns the directory name for the content directory. | ||
* | ||
* @return The directory name for the content directory. | ||
*/ | ||
public String getContentDirectoryName() { | ||
return contentDirectoryName; | ||
} | ||
|
||
/** | ||
* Sets the directory name for the content directory. | ||
* | ||
* @param contentDirectoryName The directory name for the content directory. | ||
*/ | ||
public void setContentDirectoryName(String contentDirectoryName) { | ||
this.contentDirectoryName = contentDirectoryName; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
epublib-core/src/test/java/nl/siegmann/epublib/epub/ContainerNamespaceContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package nl.siegmann.epublib.epub; | ||
|
||
import javax.xml.namespace.NamespaceContext; | ||
import java.util.Iterator; | ||
|
||
import static java.util.Collections.singleton; | ||
import static javax.xml.XMLConstants.*; | ||
|
||
class ContainerNamespaceContext implements NamespaceContext { | ||
public static final String XMLNS_CONTAINER = "urn:oasis:names:tc:opendocument:xmlns:container"; | ||
private static final String XMLNS_CONTAINER_PREFIX = "container"; | ||
|
||
@Override | ||
public String getNamespaceURI(String prefix) { | ||
if (prefix == null) throw new IllegalArgumentException(); | ||
switch (prefix) { | ||
case XMLNS_CONTAINER_PREFIX: return XMLNS_CONTAINER; | ||
case XML_NS_PREFIX: return XML_NS_URI; | ||
case XMLNS_ATTRIBUTE: return XMLNS_ATTRIBUTE_NS_URI; | ||
default: return NULL_NS_URI; | ||
} | ||
} | ||
|
||
@Override | ||
public String getPrefix(String namespaceURI) { | ||
if (namespaceURI == null) throw new IllegalArgumentException(); | ||
switch (namespaceURI) { | ||
case "urn:oasis:names:tc:opendocument:xmlns:container": return XMLNS_CONTAINER_PREFIX; | ||
case XML_NS_URI: return XML_NS_PREFIX; | ||
case XMLNS_ATTRIBUTE_NS_URI: return XMLNS_ATTRIBUTE; | ||
default: return null; | ||
} | ||
} | ||
|
||
@Override | ||
public Iterator<String> getPrefixes(String namespaceURI) { | ||
return singleton(getPrefix(namespaceURI)).iterator(); | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
epublib-core/src/test/java/nl/siegmann/epublib/epub/EpubWriterConfigurabilityTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package nl.siegmann.epublib.epub; | ||
|
||
import net.sf.jazzlib.ZipEntry; | ||
import net.sf.jazzlib.ZipInputStream; | ||
import nl.siegmann.epublib.domain.Book; | ||
import org.junit.Test; | ||
import org.w3c.dom.Document; | ||
import org.w3c.dom.NodeList; | ||
import org.w3c.dom.bootstrap.DOMImplementationRegistry; | ||
import org.w3c.dom.ls.DOMImplementationLS; | ||
import org.w3c.dom.ls.LSInput; | ||
import org.w3c.dom.ls.LSParser; | ||
|
||
import javax.xml.xpath.XPath; | ||
import javax.xml.xpath.XPathConstants; | ||
import javax.xml.xpath.XPathExpressionException; | ||
import javax.xml.xpath.XPathFactory; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import static java.util.Arrays.asList; | ||
import static java.util.Collections.unmodifiableSet; | ||
import static org.junit.Assert.fail; | ||
|
||
/** | ||
* Unit tests for the configurability of the {@link EpubWriter} using {@link EpubWriterConfiguration}. | ||
*/ | ||
public class EpubWriterConfigurabilityTest { | ||
|
||
/** | ||
* Tests that the behavior of {@link EpubWriter} without configuration uses a default configuration. | ||
* The default configuration must result in an unmodified behavior of the {@link EpubWriter}. | ||
*/ | ||
@Test | ||
public void regressionTestDirectoryName() throws IOException, XPathExpressionException { | ||
Book book = new Book(); | ||
EpubWriter epubWriter = new EpubWriter(); | ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
epubWriter.write(book, out); | ||
|
||
assertZipFileContainsEntries(out.toByteArray(), "mimetype", "META-INF/container.xml", "OEBPS/toc.ncx", "OEBPS/content.opf"); | ||
assertEpubIncludesContainerEntries(out.toByteArray(), "OEBPS/content.opf"); | ||
} | ||
|
||
/** | ||
* Tests that the behavior of {@link EpubWriter} with a configuration allows changing the content directory name. | ||
*/ | ||
@Test | ||
public void testConfigureContentDirectoryName() throws IOException, XPathExpressionException { | ||
Book book = new Book(); | ||
EpubWriter epubWriter = new EpubWriter(new EpubWriterConfiguration().withContentDirectoryName("OPS")); | ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
epubWriter.write(book, out); | ||
|
||
assertZipFileContainsEntries(out.toByteArray(), "mimetype", "META-INF/container.xml", "OPS/toc.ncx", "OPS/content.opf"); | ||
assertEpubIncludesContainerEntries(out.toByteArray(), "OPS/content.opf"); | ||
} | ||
|
||
private static void assertZipFileContainsEntries(byte[] zipFileData, String... expectedEntries) throws IOException { | ||
assertZipFileContainsEntries(new ByteArrayInputStream(zipFileData), expectedEntries); | ||
} | ||
|
||
private static void assertZipFileContainsEntries(InputStream in, String... expectedEntries) throws IOException { | ||
ZipInputStream zipInputStream = new ZipInputStream(in); | ||
Set<String> actualNames = new HashSet<>(); | ||
Set<String> expectedNames = setOf(expectedEntries); | ||
for (ZipEntry zipEntry; (zipEntry = zipInputStream.getNextEntry()) != null; ) | ||
actualNames.add(zipEntry.getName()); | ||
assertContainsAll(expectedNames, actualNames); | ||
} | ||
|
||
private static <T> void assertContainsAll(Set<T> expected, Set<T> actual) { | ||
Set<T> missing = new HashSet<>(expected); | ||
missing.removeAll(actual); | ||
if (!missing.isEmpty()) | ||
fail("Expected set " + actual + " to contain all elements from set " + actual + " but was missing he following elements: " + missing); | ||
} | ||
|
||
private static void assertEpubIncludesContainerEntries(byte[] zipFileData, String... expectedContainerEntries) throws IOException, XPathExpressionException { | ||
assertEpubIncludesContainerEntries(new ByteArrayInputStream(zipFileData), expectedContainerEntries); | ||
} | ||
|
||
private static void assertEpubIncludesContainerEntries(InputStream in, String... expectedContainerEntries) throws IOException, XPathExpressionException { | ||
ZipInputStream zipInputStream = new ZipInputStream(in); | ||
for (ZipEntry zipEntry; (zipEntry = zipInputStream.getNextEntry()) != null; ) | ||
if ("META-INF/container.xml".equals(zipEntry.getName())) { | ||
assertIncludesContainerEntries(zipInputStream, expectedContainerEntries); | ||
return; | ||
} | ||
fail("Could not find META-INF/container.xml"); | ||
} | ||
|
||
private static void assertIncludesContainerEntries(InputStream zipInputStream, String... expectedContainerEntries) throws XPathExpressionException { | ||
Document doc = readDocument(zipInputStream); | ||
XPath xPath = XPathFactory.newInstance().newXPath(); | ||
xPath.setNamespaceContext(new ContainerNamespaceContext()); | ||
|
||
NodeList nodeList = (NodeList) xPath.evaluate("/container:container/container:rootfiles/container:rootfile/@full-path", doc, XPathConstants.NODESET); | ||
Set<String> actualPaths = new HashSet<>(); | ||
for (int i = 0; i < nodeList.getLength(); i++) | ||
actualPaths.add(nodeList.item(i).getNodeValue()); | ||
assertContainsAll(setOf(expectedContainerEntries), actualPaths); | ||
} | ||
|
||
private static Document readDocument(InputStream in) { | ||
DOMImplementationLS domImplementationLS = (DOMImplementationLS) mustGetRegistry().getDOMImplementation("LS"); | ||
LSInput lsInput = domImplementationLS.createLSInput(); | ||
lsInput.setByteStream(in); | ||
LSParser lsParser = domImplementationLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); | ||
return lsParser.parse(lsInput); | ||
} | ||
|
||
private static DOMImplementationRegistry mustGetRegistry() { | ||
try { | ||
return DOMImplementationRegistry.newInstance(); | ||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { | ||
throw new AssertionError("Could not initialize DOMImplementationRegistry", e); | ||
} | ||
} | ||
|
||
@SafeVarargs | ||
private static <T> Set<T> setOf(T... elements) { | ||
return unmodifiableSet(new HashSet<>(asList(elements))); | ||
} | ||
} |
ef9d4fb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design questions to consider:
BookProcessor
become part of theEpubWriterConfiguration
? I suggest: yesEpubWriter.getBookProcessor()
andEpubWriter.setBookProcessor()
? I suggest: Delegate to EpubWriterConfiguration, deprecate, delete after 2 years.I can happily perform these changes if you agree.