优化epublib,清除警告,尝试兼容epub v3格式的导出。

pull/915/head
ag2s20150909 4 years ago
parent 1c205b56b1
commit dc24ae7244
  1. 5
      epublib/src/main/AndroidManifest.xml
  2. 2
      epublib/src/main/java/me/ag2s/epublib/Constants.java
  3. 12
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEvent.java
  4. 2
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEventListener.java
  5. 22
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationHistory.java
  6. 21
      epublib/src/main/java/me/ag2s/epublib/browsersupport/Navigator.java
  7. 112
      epublib/src/main/java/me/ag2s/epublib/domain/Author.java
  8. 323
      epublib/src/main/java/me/ag2s/epublib/domain/Book.java
  9. 134
      epublib/src/main/java/me/ag2s/epublib/domain/Date.java
  10. 314
      epublib/src/main/java/me/ag2s/epublib/domain/EpubBook.java
  11. 10
      epublib/src/main/java/me/ag2s/epublib/domain/Guide.java
  12. 2
      epublib/src/main/java/me/ag2s/epublib/domain/GuideReference.java
  13. 7
      epublib/src/main/java/me/ag2s/epublib/domain/Identifier.java
  14. 7
      epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java
  15. 4
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemProperties.java
  16. 4
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemRefProperties.java
  17. 12
      epublib/src/main/java/me/ag2s/epublib/domain/MediaType.java
  18. 11
      epublib/src/main/java/me/ag2s/epublib/domain/MediaTypes.java
  19. 429
      epublib/src/main/java/me/ag2s/epublib/domain/Metadata.java
  20. 18
      epublib/src/main/java/me/ag2s/epublib/domain/Resource.java
  21. 2
      epublib/src/main/java/me/ag2s/epublib/domain/ResourceReference.java
  22. 689
      epublib/src/main/java/me/ag2s/epublib/domain/Resources.java
  23. 21
      epublib/src/main/java/me/ag2s/epublib/domain/Spine.java
  24. 74
      epublib/src/main/java/me/ag2s/epublib/domain/SpineReference.java
  25. 9
      epublib/src/main/java/me/ag2s/epublib/domain/TOCReference.java
  26. 25
      epublib/src/main/java/me/ag2s/epublib/domain/TableOfContents.java
  27. 148
      epublib/src/main/java/me/ag2s/epublib/domain/TitledResourceReference.java
  28. 4
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessor.java
  29. 18
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessorPipeline.java
  30. 49
      epublib/src/main/java/me/ag2s/epublib/epub/DOMUtil.java
  31. 215
      epublib/src/main/java/me/ag2s/epublib/epub/EpubProcessorSupport.java
  32. 268
      epublib/src/main/java/me/ag2s/epublib/epub/EpubReader.java
  33. 60
      epublib/src/main/java/me/ag2s/epublib/epub/EpubWriter.java
  34. 2
      epublib/src/main/java/me/ag2s/epublib/epub/HtmlProcessor.java
  35. 5
      epublib/src/main/java/me/ag2s/epublib/epub/Main.java
  36. 296
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocument.java
  37. 574
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV2.java
  38. 376
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV3.java
  39. 9
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java
  40. 49
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataReader.java
  41. 44
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java
  42. 94
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentReader.java
  43. 407
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentWriter.java
  44. 276
      epublib/src/main/java/me/ag2s/epublib/epub/ResourcesLoader.java
  45. 15
      epublib/src/main/java/me/ag2s/epublib/util/CollectionUtil.java
  46. 34
      epublib/src/main/java/me/ag2s/epublib/util/IOUtil.java
  47. 3
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseOutputStream.java
  48. 5
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseWriter.java
  49. 293
      epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java
  50. 202
      epublib/src/main/java/me/ag2s/epublib/util/StreamWriterDelegate.txt
  51. 465
      epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java
  52. 6
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/BOMInputStream.java
  53. 1
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ByteOrderMark.java
  54. 8
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ProxyInputStream.java
  55. 21
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReader.java
  56. 1
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReaderException.java

@ -1,7 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.ag2s.epublib">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<manifest package="me.ag2s.epublib">
</manifest>

@ -6,7 +6,7 @@ public interface Constants {
String CHARACTER_ENCODING = "UTF-8";
String DOCTYPE_XHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">";
String NAMESPACE_XHTML = "http://www.w3.org/1999/xhtml";
String EPUB4J_GENERATOR_NAME = "EpubLib";
String EPUB_GENERATOR_NAME = "Ag2S EpubLib";
char FRAGMENT_SEPARATOR_CHAR = '#';
String DEFAULT_TOC_ID = "toc";
}

@ -2,7 +2,7 @@ package me.ag2s.epublib.browsersupport;
import java.util.EventObject;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.util.StringUtil;
@ -13,6 +13,7 @@ import me.ag2s.epublib.util.StringUtil;
* @author paul
*
*/
@SuppressWarnings("unused")
public class NavigationEvent extends EventObject {
private static final long serialVersionUID = -6346750144308952762L;
@ -20,7 +21,7 @@ public class NavigationEvent extends EventObject {
private Resource oldResource;
private int oldSpinePos;
private Navigator navigator;
private EpubBook oldBook;
private Book oldBook;
private int oldSectionPos;
private String oldFragmentId;
@ -60,7 +61,7 @@ public class NavigationEvent extends EventObject {
this.oldFragmentId = oldFragmentId;
}
public EpubBook getOldBook() {
public Book getOldBook() {
return oldBook;
}
@ -123,11 +124,11 @@ public class NavigationEvent extends EventObject {
}
public void setOldBook(EpubBook oldBook) {
public void setOldBook(Book oldBook) {
this.oldBook = oldBook;
}
public EpubBook getCurrentBook() {
public Book getCurrentBook() {
return getNavigator().getBook();
}
@ -135,6 +136,7 @@ public class NavigationEvent extends EventObject {
return oldResource != getCurrentResource();
}
@SuppressWarnings("NullableProblems")
public String toString() {
return StringUtil.toString(
"oldSectionPos", oldSectionPos,

@ -12,7 +12,7 @@ public interface NavigationEventListener {
/**
* Called whenever the user navigates to another position in the book.
*
* @param navigationEvent
* @param navigationEvent f
*/
void navigationPerformed(NavigationEvent navigationEvent);
}

@ -1,15 +1,15 @@
package me.ag2s.epublib.browsersupport;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import java.util.ArrayList;
import java.util.List;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Resource;
/**
* A history of the user's locations with the epub.
*
* @author paul.siegmann
*
*/
public class NavigationHistory implements NavigationEventListener {
@ -36,8 +36,8 @@ public class NavigationHistory implements NavigationEventListener {
}
private long lastUpdateTime = 0;
private List<Location> locations = new ArrayList<Location>();
private Navigator navigator;
private List<Location> locations = new ArrayList<>();
private final Navigator navigator;
private int currentPos = -1;
private int currentSize = 0;
private int maxHistorySize = DEFAULT_MAX_HISTORY_SIZE;
@ -58,11 +58,11 @@ public class NavigationHistory implements NavigationEventListener {
return currentSize;
}
public void initBook(EpubBook book) {
public void initBook(Book book) {
if (book == null) {
return;
}
locations = new ArrayList<Location>();
locations = new ArrayList<>();
currentPos = -1;
currentSize = 0;
if (navigator.getCurrentResource() != null) {
@ -103,8 +103,8 @@ public class NavigationHistory implements NavigationEventListener {
* <br/>
* If this nr of locations becomes larger then the historySize then the
* first item(s) will be removed.
*
* @param location
*v
* @param location d
*/
public void addLocation(Location location) {
// do nothing if the new location matches the current location
@ -149,9 +149,9 @@ public class NavigationHistory implements NavigationEventListener {
* Moves the current positions delta positions.
*
* move(-1) to go one position back in history.<br/>
* move(1) to go one position forward.<br/>
* move(1) to go one position forward.<br/>
*
* @param delta
* @param delta f
*
* @return Whether we actually moved. If the requested value is illegal
* it will return false, true otherwise.

@ -1,12 +1,11 @@
package me.ag2s.epublib.browsersupport;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Resource;
/**
* A helper class for epub browser applications.
*
@ -19,19 +18,19 @@ import me.ag2s.epublib.domain.Resource;
public class Navigator implements Serializable {
private static final long serialVersionUID = 1076126986424925474L;
private EpubBook book;
private Book book;
private int currentSpinePos;
private Resource currentResource;
private int currentPagePos;
private String currentFragmentId;
private List<NavigationEventListener> eventListeners = new ArrayList<>();
private final List<NavigationEventListener> eventListeners = new ArrayList<>();
public Navigator() {
this(null);
}
public Navigator(EpubBook book) {
public Navigator(Book book) {
this.book = book;
this.currentSpinePos = 0;
if (book != null) {
@ -136,8 +135,8 @@ public class Navigator implements Serializable {
* Go to a specific section.
* Illegal spine positions are silently ignored.
*
* @param newSpinePos
* @param source
* @param newSpinePos f
* @param source f
* @return The current position within the spine
*/
public int gotoSpineSection(int newSpinePos, int newPagePos, Object source) {
@ -159,7 +158,7 @@ public class Navigator implements Serializable {
return gotoSpineSection(book.getSpine().size() - 1, source);
}
public void gotoBook(EpubBook book, Object source) {
public void gotoBook(Book book, Object source) {
NavigationEvent navigationEvent = new NavigationEvent(source, this);
this.book = book;
this.currentFragmentId = null;
@ -187,14 +186,14 @@ public class Navigator implements Serializable {
*
* If you want the eventListeners called use gotoSection(index);
*
* @param currentIndex
* @param currentIndex f
*/
public void setCurrentSpinePos(int currentIndex) {
this.currentSpinePos = currentIndex;
this.currentResource = book.getSpine().getResource(currentIndex);
}
public EpubBook getBook() {
public Book getBook() {
return book;
}

@ -1,79 +1,89 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
/**
* Represents one of the authors of the book
*
* @author paul
*
*/
public class Author implements Serializable {
private static final long serialVersionUID = 6663408501416574200L;
private static final long serialVersionUID = 6663408501416574200L;
private String firstname;
private String lastname;
private Relator relator = Relator.AUTHOR;
private String firstname;
private String lastname;
private Relator relator = Relator.AUTHOR;
public Author(String singleName) {
this("", singleName);
}
public Author(String singleName) {
this("", singleName);
}
public Author(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public Author(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String toString() {
return lastname + ", " + firstname;
}
@Override
@SuppressWarnings("NullableProblems")
public String toString() {
return this.lastname + ", " + this.firstname;
}
public int hashCode() {
return StringUtil.hashCode(firstname, lastname);
}
public int hashCode() {
return StringUtil.hashCode(firstname, lastname);
}
public boolean equals(Object authorObject) {
if (!(authorObject instanceof Author)) {
return false;
public boolean equals(Object authorObject) {
if (!(authorObject instanceof Author)) {
return false;
}
Author other = (Author) authorObject;
return StringUtil.equals(firstname, other.firstname)
&& StringUtil.equals(lastname, other.lastname);
}
Author other = (Author) authorObject;
return StringUtil.equals(firstname, other.firstname)
&& StringUtil.equals(lastname, other.lastname);
}
public Relator setRole(String code) {
Relator result = Relator.byCode(code);
if (result == null) {
result = Relator.AUTHOR;
/**
* 设置贡献者的角色
*
* @param code 角色编号
*/
public void setRole(String code) {
Relator result = Relator.byCode(code);
if (result == null) {
result = Relator.AUTHOR;
}
this.relator = result;
}
this.relator = result;
return result;
}
public Relator getRelator() {
return relator;
}
public Relator getRelator() {
return relator;
}
public void setRelator(Relator relator) {
this.relator = relator;
}
public void setRelator(Relator relator) {
this.relator = relator;
}
}

@ -0,0 +1,323 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Representation of a Book.
* <p>
* All resources of a Book (html, css, xml, fonts, images) are represented
* as Resources. See getResources() for access to these.<br/>
* A Book as 3 indexes into these Resources, as per the epub specification.<br/>
* <dl>
* <dt>Spine</dt>
* <dd>these are the Resources to be shown when a user reads the book from
* start to finish.</dd>
* <dt>Table of Contents<dt>
* <dd>The table of contents. Table of Contents references may be in a
* different order and contain different Resources than the spine, and often do.
* <dt>Guide</dt>
* <dd>The Guide has references to a set of special Resources like the
* cover page, the Glossary, the copyright page, etc.
* </dl>
* <p/>
* The complication is that these 3 indexes may and usually do point to
* different pages.
* A chapter may be split up in 2 pieces to fit it in to memory. Then the
* spine will contain both pieces, but the Table of Contents only the first.
* <p>
* The Content page may be in the Table of Contents, the Guide, but not
* in the Spine.
* Etc.
* <p/>
* <p>
* Please see the illustration at: doc/schema.svg
*
* @author paul
* @author jake
*/
public class Book implements Serializable {
private static final long serialVersionUID = 2068355170895770100L;
private Resources resources = new Resources();
private Metadata metadata = new Metadata();
private Spine spine = new Spine();
private TableOfContents tableOfContents = new TableOfContents();
private final Guide guide = new Guide();
private Resource opfResource;
private Resource ncxResource;
private Resource coverImage;
private String version="2.0";
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public boolean isEpub3() {
return this.version.startsWith("3.");
}
@SuppressWarnings("UnusedReturnValue")
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource) {
return addSection(parentSection, sectionTitle, resource, null);
}
/**
* Adds the resource to the table of contents of the book as a child
* section of the given parentSection
*
* @param parentSection parentSection
* @param sectionTitle sectionTitle
* @param resource resource
* @param fragmentId fragmentId
* @return The table of contents
*/
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource,
String fragmentId) {
getResources().add(resource);
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return parentSection.addChildSection(
new TOCReference(sectionTitle, resource, fragmentId));
}
public TOCReference addSection(String title, Resource resource) {
return addSection(title, resource, null);
}
/**
* Adds a resource to the book's set of resources, table of contents and
* if there is no resource with the id in the spine also adds it to the spine.
*
* @param title title
* @param resource resource
* @param fragmentId fragmentId
* @return The table of contents
*/
public TOCReference addSection(
String title, Resource resource, String fragmentId) {
getResources().add(resource);
TOCReference tocReference = tableOfContents
.addTOCReference(new TOCReference(title, resource, fragmentId));
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return tocReference;
}
@SuppressWarnings("unused")
public void generateSpineFromTableOfContents() {
Spine spine = new Spine(tableOfContents);
// in case the tocResource was already found and assigned
spine.setTocResource(this.spine.getTocResource());
this.spine = spine;
}
/**
* The Book's metadata (titles, authors, etc)
*
* @return The Book's metadata (titles, authors, etc)
*/
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public void setResources(Resources resources) {
this.resources = resources;
}
@SuppressWarnings("unused")
public Resource addResource(Resource resource) {
return resources.add(resource);
}
/**
* The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*
* @return The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*/
public Resources getResources() {
return resources;
}
/**
* The sections of the book that should be shown if a user reads the book
* from start to finish.
*
* @return The Spine
*/
public Spine getSpine() {
return spine;
}
public void setSpine(Spine spine) {
this.spine = spine;
}
/**
* The Table of Contents of the book.
*
* @return The Table of Contents of the book.
*/
public TableOfContents getTableOfContents() {
return tableOfContents;
}
public void setTableOfContents(TableOfContents tableOfContents) {
this.tableOfContents = tableOfContents;
}
/**
* The book's cover page as a Resource.
* An XHTML document containing a link to the cover image.
*
* @return The book's cover page as a Resource
*/
public Resource getCoverPage() {
Resource coverPage = guide.getCoverPage();
if (coverPage == null) {
coverPage = spine.getResource(0);
}
return coverPage;
}
public void setCoverPage(Resource coverPage) {
if (coverPage == null) {
return;
}
if (resources.notContainsByHref(coverPage.getHref())) {
resources.add(coverPage);
}
guide.setCoverPage(coverPage);
}
/**
* Gets the first non-blank title from the book's metadata.
*
* @return the first non-blank title from the book's metadata.
*/
public String getTitle() {
return getMetadata().getFirstTitle();
}
/**
* The book's cover image.
*
* @return The book's cover image.
*/
public Resource getCoverImage() {
return coverImage;
}
public void setCoverImage(Resource coverImage) {
if (coverImage == null) {
return;
}
if (resources.notContainsByHref(coverImage.getHref())) {
resources.add(coverImage);
}
this.coverImage = coverImage;
}
/**
* The guide; contains references to special sections of the book like
* colophon, glossary, etc.
*
* @return The guide; contains references to special sections of the book
* like colophon, glossary, etc.
*/
public Guide getGuide() {
return guide;
}
/**
* All Resources of the Book that can be reached via the Spine, the
* TableOfContents or the Guide.
* <p/>
* Consists of a list of "reachable" resources:
* <ul>
* <li>The coverpage</li>
* <li>The resources of the Spine that are not already in the result</li>
* <li>The resources of the Table of Contents that are not already in the
* result</li>
* <li>The resources of the Guide that are not already in the result</li>
* </ul>
* To get all html files that make up the epub file use
* {@link #getResources()}
*
* @return All Resources of the Book that can be reached via the Spine,
* the TableOfContents or the Guide.
*/
public List<Resource> getContents() {
Map<String, Resource> result = new LinkedHashMap<>();
addToContentsResult(getCoverPage(), result);
for (SpineReference spineReference : getSpine().getSpineReferences()) {
addToContentsResult(spineReference.getResource(), result);
}
for (Resource resource : getTableOfContents().getAllUniqueResources()) {
addToContentsResult(resource, result);
}
for (GuideReference guideReference : getGuide().getReferences()) {
addToContentsResult(guideReference.getResource(), result);
}
return new ArrayList<>(result.values());
}
private static void addToContentsResult(Resource resource,
Map<String, Resource> allReachableResources) {
if (resource != null && (!allReachableResources
.containsKey(resource.getHref()))) {
allReachableResources.put(resource.getHref(), resource);
}
}
public Resource getOpfResource() {
return opfResource;
}
public void setOpfResource(Resource opfResource) {
this.opfResource = opfResource;
}
public void setNcxResource(Resource ncxResource) {
this.ncxResource = ncxResource;
}
public Resource getNcxResource() {
return ncxResource;
}
}

@ -1,104 +1,108 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.epub.PackageDocumentBase;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Locale;
/**
* A Date used by the book's metadata.
*
* <p>
* Examples: creation-date, modification-date, etc
*
* @author paul
*
*/
public class Date implements Serializable {
private static final long serialVersionUID = 7533866830395120136L;
private static final long serialVersionUID = 7533866830395120136L;
public enum Event {
PUBLICATION("publication"),
MODIFICATION("modification"),
CREATION("creation");
public enum Event {
PUBLICATION("publication"),
MODIFICATION("modification"),
CREATION("creation");
private final String value;
private final String value;
Event(String v) {
value = v;
}
Event(String v) {
value = v;
}
public static Event fromValue(String v) {
for (Event c : Event.values()) {
if (c.value.equals(v)) {
return c;
public static Event fromValue(String v) {
for (Event c : Event.values()) {
if (c.value.equals(v)) {
return c;
}
}
return null;
}
}
return null;
}
public String toString() {
return value;
@Override
@SuppressWarnings("NullableProblems")
public String toString() {
return value;
}
}
}
private Event event;
private String dateString;
private Event event;
private String dateString;
public Date(java.util.Date date) {
this(date, (Event) null);
}
public Date(java.util.Date date) {
this(date, (Event) null);
}
public Date(String dateString) {
this(dateString, (Event) null);
}
public Date(String dateString) {
this(dateString, (Event) null);
}
public Date(java.util.Date date, Event event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event);
}
public Date(java.util.Date date, Event event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event);
}
public Date(String dateString, Event event) {
this.dateString = dateString;
this.event = event;
}
public Date(String dateString, Event event) {
this.dateString = dateString;
this.event = event;
}
public Date(java.util.Date date, String event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat,Locale.US)).format(date),
event);
}
public Date(java.util.Date date, String event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event);
}
public Date(String dateString, String event) {
this(checkDate(dateString), Event.fromValue(event));
this.dateString = dateString;
}
public Date(String dateString, String event) {
this(checkDate(dateString), Event.fromValue(event));
this.dateString = dateString;
}
private static String checkDate(String dateString) {
if (dateString == null) {
throw new IllegalArgumentException(
"Cannot create a date from a blank string");
private static String checkDate(String dateString) {
if (dateString == null) {
throw new IllegalArgumentException(
"Cannot create a date from a blank string");
}
return dateString;
}
return dateString;
}
public String getValue() {
return dateString;
}
public String getValue() {
return dateString;
}
public Event getEvent() {
return event;
}
public Event getEvent() {
return event;
}
public void setEvent(Event event) {
this.event = event;
}
public void setEvent(Event event) {
this.event = event;
}
public String toString() {
if (event == null) {
return dateString;
@Override
@SuppressWarnings("NullableProblems")
public String toString() {
if (event == null) {
return dateString;
}
return "" + event + ":" + dateString;
}
return "" + event + ":" + dateString;
}
}

@ -1,316 +1,8 @@
package me.ag2s.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Representation of a Book.
*
* All resources of a Book (html, css, xml, fonts, images) are represented
* as Resources. See getResources() for access to these.<br/>
* A Book as 3 indexes into these Resources, as per the epub specification.<br/>
* <dl>
* <dt>Spine</dt>
* <dd>these are the Resources to be shown when a user reads the book from
* start to finish.</dd>
* <dt>Table of Contents<dt>
* <dd>The table of contents. Table of Contents references may be in a
* different order and contain different Resources than the spine, and often do.
* <dt>Guide</dt>
* <dd>The Guide has references to a set of special Resources like the
* cover page, the Glossary, the copyright page, etc.
* </dl>
* <p/>
* The complication is that these 3 indexes may and usually do point to
* different pages.
* A chapter may be split up in 2 pieces to fit it in to memory. Then the
* spine will contain both pieces, but the Table of Contents only the first.
*
* The Content page may be in the Table of Contents, the Guide, but not
* in the Spine.
* Etc.
* <p/>
*
* Please see the illustration at: doc/schema.svg
*
* @author paul
* @author jake
* 这个是用于其它与Book类同名时替换的
*/
public class EpubBook implements Serializable {
private static final long serialVersionUID = 2068355170895770100L;
private Resources resources = new Resources();
private Metadata metadata = new Metadata();
private Spine spine = new Spine();
private TableOfContents tableOfContents = new TableOfContents();
private Guide guide = new Guide();
private Resource opfResource;
private Resource ncxResource;
private Resource coverImage;
private String version;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource) {
return addSection(parentSection, sectionTitle, resource, null);
}
/**
* Adds the resource to the table of contents of the book as a child
* section of the given parentSection
*
* @param parentSection
* @param sectionTitle
* @param resource
* @param fragmentId
* @return The table of contents
*/
public TOCReference addSection(
TOCReference parentSection, String sectionTitle, Resource resource,
String fragmentId) {
getResources().add(resource);
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return parentSection.addChildSection(
new TOCReference(sectionTitle, resource, fragmentId));
}
public TOCReference addSection(String title, Resource resource) {
return addSection(title, resource, null);
}
/**
* Adds a resource to the book's set of resources, table of contents and
* if there is no resource with the id in the spine also adds it to the spine.
*
* @param title
* @param resource
* @param fragmentId
* @return The table of contents
*/
public TOCReference addSection(
String title, Resource resource, String fragmentId) {
getResources().add(resource);
TOCReference tocReference = tableOfContents
.addTOCReference(new TOCReference(title, resource, fragmentId));
if (spine.findFirstResourceById(resource.getId()) < 0) {
spine.addSpineReference(new SpineReference(resource));
}
return tocReference;
}
public void generateSpineFromTableOfContents() {
Spine spine = new Spine(tableOfContents);
// in case the tocResource was already found and assigned
spine.setTocResource(this.spine.getTocResource());
this.spine = spine;
}
/**
* The Book's metadata (titles, authors, etc)
*
* @return The Book's metadata (titles, authors, etc)
*/
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public void setResources(Resources resources) {
this.resources = resources;
}
public Resource addResource(Resource resource) {
return resources.add(resource);
}
/**
* The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*
* @return The collection of all images, chapters, sections, xhtml files,
* stylesheets, etc that make up the book.
*/
public Resources getResources() {
return resources;
}
/**
* The sections of the book that should be shown if a user reads the book
* from start to finish.
*
* @return The Spine
*/
public Spine getSpine() {
return spine;
}
public void setSpine(Spine spine) {
this.spine = spine;
}
/**
* The Table of Contents of the book.
*
* @return The Table of Contents of the book.
*/
public TableOfContents getTableOfContents() {
return tableOfContents;
}
public void setTableOfContents(TableOfContents tableOfContents) {
this.tableOfContents = tableOfContents;
}
/**
* The book's cover page as a Resource.
* An XHTML document containing a link to the cover image.
*
* @return The book's cover page as a Resource
*/
public Resource getCoverPage() {
Resource coverPage = guide.getCoverPage();
if (coverPage == null) {
coverPage = spine.getResource(0);
}
return coverPage;
}
public void setCoverPage(Resource coverPage) {
if (coverPage == null) {
return;
}
if (!resources.containsByHref(coverPage.getHref())) {
resources.add(coverPage);
}
guide.setCoverPage(coverPage);
}
/**
* Gets the first non-blank title from the book's metadata.
*
* @return the first non-blank title from the book's metadata.
*/
public String getTitle() {
return getMetadata().getFirstTitle();
}
/**
* The book's cover image.
*
* @return The book's cover image.
*/
public Resource getCoverImage() {
return coverImage;
}
public void setCoverImage(Resource coverImage) {
if (coverImage == null) {
return;
}
if (!resources.containsByHref(coverImage.getHref())) {
resources.add(coverImage);
}
this.coverImage = coverImage;
}
/**
* The guide; contains references to special sections of the book like
* colophon, glossary, etc.
*
* @return The guide; contains references to special sections of the book
* like colophon, glossary, etc.
*/
public Guide getGuide() {
return guide;
}
/**
* All Resources of the Book that can be reached via the Spine, the
* TableOfContents or the Guide.
* <p/>
* Consists of a list of "reachable" resources:
* <ul>
* <li>The coverpage</li>
* <li>The resources of the Spine that are not already in the result</li>
* <li>The resources of the Table of Contents that are not already in the
* result</li>
* <li>The resources of the Guide that are not already in the result</li>
* </ul>
* To get all html files that make up the epub file use
* {@link #getResources()}
* @return All Resources of the Book that can be reached via the Spine,
* the TableOfContents or the Guide.
*/
public List<Resource> getContents() {
Map<String, Resource> result = new LinkedHashMap<String, Resource>();
addToContentsResult(getCoverPage(), result);
for (SpineReference spineReference : getSpine().getSpineReferences()) {
addToContentsResult(spineReference.getResource(), result);
}
for (Resource resource : getTableOfContents().getAllUniqueResources()) {
addToContentsResult(resource, result);
}
for (GuideReference guideReference : getGuide().getReferences()) {
addToContentsResult(guideReference.getResource(), result);
}
return new ArrayList<Resource>(result.values());
}
private static void addToContentsResult(Resource resource,
Map<String, Resource> allReachableResources) {
if (resource != null && (!allReachableResources
.containsKey(resource.getHref()))) {
allReachableResources.put(resource.getHref(), resource);
}
}
public Resource getOpfResource() {
return opfResource;
}
public void setOpfResource(Resource opfResource) {
this.opfResource = opfResource;
}
public void setNcxResource(Resource ncxResource) {
this.ncxResource = ncxResource;
}
public Resource getNcxResource() {
return ncxResource;
}
@SuppressWarnings("unused declaration")
public class EpubBook extends Book {
}

@ -25,7 +25,7 @@ public class Guide implements Serializable {
public static final String DEFAULT_COVER_TITLE = GuideReference.COVER;
private List<GuideReference> references = new ArrayList<GuideReference>();
private List<GuideReference> references = new ArrayList<>();
private static final int COVERPAGE_NOT_FOUND = -1;
private static final int COVERPAGE_UNITIALIZED = -2;
@ -51,7 +51,7 @@ public class Guide implements Serializable {
}
return null;
}
@SuppressWarnings("UnusedReturnValue")
public int setCoverReference(GuideReference guideReference) {
if (coverPageIndex >= 0) {
references.set(coverPageIndex, guideReference);
@ -100,7 +100,7 @@ public class Guide implements Serializable {
setCoverReference(coverpageGuideReference);
}
@SuppressWarnings("UnusedReturnValue")
public ResourceReference addReference(GuideReference reference) {
this.references.add(reference);
uncheckCoverPage();
@ -111,13 +111,13 @@ public class Guide implements Serializable {
* A list of all GuideReferences that have the given
* referenceTypeName (ignoring case).
*
* @param referenceTypeName
* @param referenceTypeName referenceTypeName
* @return A list of all GuideReferences that have the given
* referenceTypeName (ignoring case).
*/
public List<GuideReference> getGuideReferencesByType(
String referenceTypeName) {
List<GuideReference> result = new ArrayList<GuideReference>();
List<GuideReference> result = new ArrayList<>();
for (GuideReference guideReference : references) {
if (referenceTypeName.equalsIgnoreCase(guideReference.getType())) {
result.add(guideReference);

@ -20,7 +20,7 @@ public class GuideReference extends TitledResourceReference
/**
* the book cover(s), jacket information, etc.
*/
public static String COVER = "cover";
public static final String COVER = "cover";
/**
* human-readable page with title, author, publisher, and other metadata

@ -15,7 +15,7 @@ import java.util.UUID;
public class Identifier implements Serializable {
private static final long serialVersionUID = 955949951416391810L;
@SuppressWarnings("unused")
public interface Scheme {
String UUID = "UUID";
@ -48,7 +48,7 @@ public class Identifier implements Serializable {
* If no identifier has bookId == true then the first bookId identifier
* is written as the primary.
*
* @param identifiers
* @param identifiers i
* @return The first identifier for which the bookId is true is made
* the bookId identifier.
*/
@ -119,7 +119,8 @@ public class Identifier implements Serializable {
return StringUtil.equals(scheme, ((Identifier) otherIdentifier).scheme)
&& StringUtil.equals(value, ((Identifier) otherIdentifier).value);
}
@SuppressWarnings("NullableProblems")
@Override
public String toString() {
if (StringUtil.isBlank(scheme)) {
return "" + value;

@ -3,7 +3,6 @@ package me.ag2s.epublib.domain;
import android.util.Log;
import me.ag2s.epublib.util.IOUtil;
//import io.documentnode.minilog.Logger;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -15,7 +14,7 @@ import java.io.InputStream;
public class LazyResource extends Resource {
private static final long serialVersionUID = 5089400472352002866L;
private static String TAG= LazyResource.class.getName();
private final String TAG= getClass().getName();
private final LazyResourceProvider resourceProvider;
private final long cachedSize;
@ -51,7 +50,7 @@ public class LazyResource extends Resource {
*
* @return The contents of the Resource.
*
* @throws IOException
* @throws IOException IOException
*/
public InputStream getInputStream() throws IOException {
if (isInitialized()) {
@ -64,7 +63,7 @@ public class LazyResource extends Resource {
/**
* Initializes the resource by loading its data into memory.
*
* @throws IOException
* @throws IOException IOException
*/
public void initialize() throws IOException {
getData();

@ -1,5 +1,5 @@
package me.ag2s.epublib.domain;
@SuppressWarnings("unused")
public enum ManifestItemProperties implements ManifestProperties {
COVER_IMAGE("cover-image"),
MATHML("mathml"),
@ -9,7 +9,7 @@ public enum ManifestItemProperties implements ManifestProperties {
SVG("svg"),
SWITCH("switch");
private String name;
private final String name;
ManifestItemProperties(String name) {
this.name = name;

@ -1,10 +1,10 @@
package me.ag2s.epublib.domain;
@SuppressWarnings("unused")
public enum ManifestItemRefProperties implements ManifestProperties {
PAGE_SPREAD_LEFT("page-spread-left"),
PAGE_SPREAD_RIGHT("page-spread-right");
private String name;
private final String name;
ManifestItemRefProperties(String name) {
this.name = name;

@ -18,9 +18,9 @@ import java.util.Collection;
public class MediaType implements Serializable {
private static final long serialVersionUID = -7256091153727506788L;
private String name;
private String defaultExtension;
private Collection<String> extensions;
private final String name;
private final String defaultExtension;
private final Collection<String> extensions;
public MediaType(String name, String defaultExtension) {
this(name, defaultExtension, new String[]{defaultExtension});
@ -39,11 +39,11 @@ public class MediaType implements Serializable {
}
public MediaType(String name, String defaultExtension,
Collection<String> extensions) {
Collection<String> mextensions) {
super();
this.name = name;
this.defaultExtension = defaultExtension;
this.extensions = extensions;
this.extensions = mextensions;
}
public String getName() {
@ -66,7 +66,7 @@ public class MediaType implements Serializable {
}
return name.equals(((MediaType) otherMediaType).getName());
}
@SuppressWarnings("NullableProblems")
public String toString() {
return name;
}

@ -1,6 +1,5 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.util.StringUtil;
import java.util.HashMap;
import java.util.Map;
@ -54,16 +53,16 @@ public class MediaTypes {
public static final MediaType PLS = new MediaType("application/pls+xml",
".pls");
public static MediaType[] mediaTypes = new MediaType[]{
public static final MediaType[] mediaTypes = new MediaType[]{
XHTML, EPUB, JPG, PNG, GIF, CSS, SVG, TTF, NCX, XPGT, OPENTYPE, WOFF,
SMIL, PLS, JAVASCRIPT, MP3, MP4, OGG
};
public static Map<String, MediaType> mediaTypesByName = new HashMap<String, MediaType>();
public static final Map<String, MediaType> mediaTypesByName = new HashMap<>();
static {
for (int i = 0; i < mediaTypes.length; i++) {
mediaTypesByName.put(mediaTypes[i].getName(), mediaTypes[i]);
for (MediaType mediaType : mediaTypes) {
mediaTypesByName.put(mediaType.getName(), mediaType);
}
}
@ -75,7 +74,7 @@ public class MediaTypes {
* Gets the MediaType based on the file extension.
* Null of no matching extension found.
*
* @param filename
* @param filename filename
* @return the MediaType based on the file extension.
*/
public static MediaType determineMediaType(String filename) {

@ -1,11 +1,13 @@
package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
/**
@ -17,214 +19,223 @@ import javax.xml.namespace.QName;
*/
public class Metadata implements Serializable {
private static final long serialVersionUID = -2437262888962149444L;
public static final String DEFAULT_LANGUAGE = "en";
private boolean autoGeneratedId = true;
private List<Author> authors = new ArrayList<Author>();
private List<Author> contributors = new ArrayList<Author>();
private List<Date> dates = new ArrayList<Date>();
private String language = DEFAULT_LANGUAGE;
private Map<QName, String> otherProperties = new HashMap<QName, String>();
private List<String> rights = new ArrayList<String>();
private List<String> titles = new ArrayList<String>();
private List<Identifier> identifiers = new ArrayList<Identifier>();
private List<String> subjects = new ArrayList<String>();
private String format = MediaTypes.EPUB.getName();
private List<String> types = new ArrayList<String>();
private List<String> descriptions = new ArrayList<String>();
private List<String> publishers = new ArrayList<String>();
private Map<String, String> metaAttributes = new HashMap<String, String>();
public Metadata() {
identifiers.add(new Identifier());
autoGeneratedId = true;
}
public boolean isAutoGeneratedId() {
return autoGeneratedId;
}
/**
* Metadata properties not hard-coded like the author, title, etc.
*
* @return Metadata properties not hard-coded like the author, title, etc.
*/
public Map<QName, String> getOtherProperties() {
return otherProperties;
}
public void setOtherProperties(Map<QName, String> otherProperties) {
this.otherProperties = otherProperties;
}
public Date addDate(Date date) {
this.dates.add(date);
return date;
}
public List<Date> getDates() {
return dates;
}
public void setDates(List<Date> dates) {
this.dates = dates;
}
public Author addAuthor(Author author) {
authors.add(author);
return author;
}
public List<Author> getAuthors() {
return authors;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
public Author addContributor(Author contributor) {
contributors.add(contributor);
return contributor;
}
public List<Author> getContributors() {
return contributors;
}
public void setContributors(List<Author> contributors) {
this.contributors = contributors;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public List<String> getSubjects() {
return subjects;
}
public void setSubjects(List<String> subjects) {
this.subjects = subjects;
}
public void setRights(List<String> rights) {
this.rights = rights;
}
public List<String> getRights() {
return rights;
}
/**
* Gets the first non-blank title of the book.
* Will return "" if no title found.
*
* @return the first non-blank title of the book.
*/
public String getFirstTitle() {
if (titles == null || titles.isEmpty()) {
return "";
}
for (String title : titles) {
if (StringUtil.isNotBlank(title)) {
private static final long serialVersionUID = -2437262888962149444L;
public static final String DEFAULT_LANGUAGE = "en";
private boolean autoGeneratedId;//true;
private List<Author> authors = new ArrayList<>();
private List<Author> contributors = new ArrayList<>();
private List<Date> dates = new ArrayList<>();
private String language = DEFAULT_LANGUAGE;
private Map<QName, String> otherProperties = new HashMap<>();
private List<String> rights = new ArrayList<>();
private List<String> titles = new ArrayList<>();
private List<Identifier> identifiers = new ArrayList<>();
private List<String> subjects = new ArrayList<>();
private String format = MediaTypes.EPUB.getName();
private List<String> types = new ArrayList<>();
private List<String> descriptions = new ArrayList<>();
private List<String> publishers = new ArrayList<>();
private Map<String, String> metaAttributes = new HashMap<>();
public Metadata() {
identifiers.add(new Identifier());
autoGeneratedId = true;
}
@SuppressWarnings("unused")
public boolean isAutoGeneratedId() {
return autoGeneratedId;
}
/**
* Metadata properties not hard-coded like the author, title, etc.
*
* @return Metadata properties not hard-coded like the author, title, etc.
*/
public Map<QName, String> getOtherProperties() {
return otherProperties;
}
public void setOtherProperties(Map<QName, String> otherProperties) {
this.otherProperties = otherProperties;
}
@SuppressWarnings("unused")
public Date addDate(Date date) {
this.dates.add(date);
return date;
}
public List<Date> getDates() {
return dates;
}
public void setDates(List<Date> dates) {
this.dates = dates;
}
@SuppressWarnings("UnusedReturnValue")
public Author addAuthor(Author author) {
authors.add(author);
return author;
}
public List<Author> getAuthors() {
return authors;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
@SuppressWarnings("UnusedReturnValue")
public Author addContributor(Author contributor) {
contributors.add(contributor);
return contributor;
}
public List<Author> getContributors() {
return contributors;
}
public void setContributors(List<Author> contributors) {
this.contributors = contributors;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public List<String> getSubjects() {
return subjects;
}
public void setSubjects(List<String> subjects) {
this.subjects = subjects;
}
public void setRights(List<String> rights) {
this.rights = rights;
}
public List<String> getRights() {
return rights;
}
/**
* Gets the first non-blank title of the book.
* Will return "" if no title found.
*
* @return the first non-blank title of the book.
*/
public String getFirstTitle() {
if (titles == null || titles.isEmpty()) {
return "";
}
for (String title : titles) {
if (StringUtil.isNotBlank(title)) {
return title;
}
}
return "";
}
public String addTitle(String title) {
this.titles.add(title);
return title;
}
}
return "";
}
public String addTitle(String title) {
this.titles.add(title);
return title;
}
public void setTitles(List<String> titles) {
this.titles = titles;
}
public List<String> getTitles() {
return titles;
}
public String addPublisher(String publisher) {
this.publishers.add(publisher);
return publisher;
}
public void setPublishers(List<String> publishers) {
this.publishers = publishers;
}
public List<String> getPublishers() {
return publishers;
}
public String addDescription(String description) {
this.descriptions.add(description);
return description;
}
public void setDescriptions(List<String> descriptions) {
this.descriptions = descriptions;
}
public List<String> getDescriptions() {
return descriptions;
}
public Identifier addIdentifier(Identifier identifier) {
if (autoGeneratedId && (!(identifiers.isEmpty()))) {
identifiers.set(0, identifier);
} else {
identifiers.add(identifier);
}
autoGeneratedId = false;
return identifier;
}
public void setIdentifiers(List<Identifier> identifiers) {
this.identifiers = identifiers;
autoGeneratedId = false;
}
public List<Identifier> getIdentifiers() {
return identifiers;
}
public void setFormat(String format) {
this.format = format;
}
public String getFormat() {
return format;
}
public String addType(String type) {
this.types.add(type);
return type;
}
public List<String> getTypes() {
return types;
}
public void setTypes(List<String> types) {
this.types = types;
}
public String getMetaAttribute(String name) {
return metaAttributes.get(name);
}
public void setMetaAttributes(Map<String, String> metaAttributes) {
this.metaAttributes = metaAttributes;
}
}
public void setTitles(List<String> titles) {
this.titles = titles;
}
public List<String> getTitles() {
return titles;
}
@SuppressWarnings("UnusedReturnValue")
public String addPublisher(String publisher) {
this.publishers.add(publisher);
return publisher;
}
public void setPublishers(List<String> publishers) {
this.publishers = publishers;
}
public List<String> getPublishers() {
return publishers;
}
@SuppressWarnings("UnusedReturnValue")
public String addDescription(String description) {
this.descriptions.add(description);
return description;
}
public void setDescriptions(List<String> descriptions) {
this.descriptions = descriptions;
}
public List<String> getDescriptions() {
return descriptions;
}
@SuppressWarnings("unused")
public Identifier addIdentifier(Identifier identifier) {
if (autoGeneratedId && (!(identifiers.isEmpty()))) {
identifiers.set(0, identifier);
} else {
identifiers.add(identifier);
}
autoGeneratedId = false;
return identifier;
}
public void setIdentifiers(List<Identifier> identifiers) {
this.identifiers = identifiers;
autoGeneratedId = false;
}
public List<Identifier> getIdentifiers() {
return identifiers;
}
public void setFormat(String format) {
this.format = format;
}
public String getFormat() {
return format;
}
@SuppressWarnings("UnusedReturnValue")
public String addType(String type) {
this.types.add(type);
return type;
}
public List<String> getTypes() {
return types;
}
public void setTypes(List<String> types) {
this.types = types;
}
@SuppressWarnings("unused")
public String getMetaAttribute(String name) {
return metaAttributes.get(name);
}
public void setMetaAttributes(Map<String, String> metaAttributes) {
this.metaAttributes = metaAttributes;
}
}

@ -27,9 +27,9 @@ public class Resource implements Serializable {
private String properties;
protected String originalHref;
protected final String originalHref;
private MediaType mediaType;
private String inputEncoding = Constants.CHARACTER_ENCODING;
private String inputEncoding;
protected byte[] data;
/**
@ -147,7 +147,7 @@ public class Resource implements Serializable {
*
* @return The contents of the Resource.
*
* @throws IOException
* @throws IOException IOException
*/
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(getData());
@ -174,7 +174,7 @@ public class Resource implements Serializable {
* Sets the data of the Resource.
* If the data is a of a different type then the original data then make sure to change the MediaType.
*
* @param data
* @param data the data of the Resource
*/
public void setData(byte[] data) {
this.data = data;
@ -201,7 +201,7 @@ public class Resource implements Serializable {
/**
* Sets the Resource's id: Make sure it is unique and a valid identifier.
*
* @param id
* @param id Resource's id
*/
public void setId(String id) {
this.id = id;
@ -233,7 +233,7 @@ public class Resource implements Serializable {
/**
* Sets the Resource's href.
*
* @param href
* @param href Resource's href.
*/
public void setHref(String href) {
this.href = href;
@ -252,7 +252,7 @@ public class Resource implements Serializable {
/**
* Sets the Resource's input character encoding.
*
* @param encoding
* @param encoding Resource's input character encoding.
*/
public void setInputEncoding(String encoding) {
this.inputEncoding = encoding;
@ -264,7 +264,7 @@ public class Resource implements Serializable {
* Does all sorts of smart things (courtesy of apache commons io XMLStreamREader) to handle encodings, byte order markers, etc.
*
* @return the contents of the Resource as Reader.
* @throws IOException
* @throws IOException IOException
*/
public Reader getReader() throws IOException {
return new XmlStreamReader(new ByteArrayInputStream(getData()),
@ -315,7 +315,7 @@ public class Resource implements Serializable {
public void setProperties(String properties) {
this.properties = properties;
}
@SuppressWarnings("NullableProblems")
public String toString() {
return StringUtil.toString("id", id,
"title", title,

@ -20,7 +20,7 @@ public class ResourceReference implements Serializable {
/**
* Besides setting the resource it also sets the fragmentId to null.
*
* @param resource
* @param resource resource
*/
public void setResource(Resource resource) {
this.resource = resource;

@ -2,6 +2,7 @@ package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
@ -15,373 +16,387 @@ import java.util.Map;
* XHTML files, images and epub xml documents must be here.
*
* @author paul
*
*/
public class Resources implements Serializable {
private static final long serialVersionUID = 2450876953383871451L;
private static final String IMAGE_PREFIX = "image_";
private static final String ITEM_PREFIX = "item_";
private int lastId = 1;
private Map<String, Resource> resources = new HashMap<>();
/**
* Adds a resource to the resources.
*
* Fixes the resources id and href if necessary.
*
* @param resource
* @return the newly added resource
*/
public Resource add(Resource resource) {
fixResourceHref(resource);
fixResourceId(resource);
this.resources.put(resource.getHref(), resource);
return resource;
}
/**
* Checks the id of the given resource and changes to a unique identifier if it isn't one already.
*
* @param resource
*/
public void fixResourceId(Resource resource) {
String resourceId = resource.getId();
// first try and create a unique id based on the resource's href
if (StringUtil.isBlank(resource.getId())) {
resourceId = StringUtil.substringBeforeLast(resource.getHref(), '.');
resourceId = StringUtil.substringAfterLast(resourceId, '/');
private static final long serialVersionUID = 2450876953383871451L;
private static final String IMAGE_PREFIX = "image_";
private static final String ITEM_PREFIX = "item_";
private int lastId = 1;
private Map<String, Resource> resources = new HashMap<>();
/**
* Adds a resource to the resources.
* <p>
* Fixes the resources id and href if necessary.
*
* @param resource resource
* @return the newly added resource
*/
public Resource add(Resource resource) {
fixResourceHref(resource);
fixResourceId(resource);
this.resources.put(resource.getHref(), resource);
return resource;
}
resourceId = makeValidId(resourceId, resource);
/**
* Checks the id of the given resource and changes to a unique identifier if it isn't one already.
*
* @param resource resource
*/
public void fixResourceId(Resource resource) {
String resourceId = resource.getId();
// first try and create a unique id based on the resource's href
if (StringUtil.isBlank(resource.getId())) {
resourceId = StringUtil.substringBeforeLast(resource.getHref(), '.');
resourceId = StringUtil.substringAfterLast(resourceId, '/');
}
resourceId = makeValidId(resourceId, resource);
// check if the id is unique. if not: create one from scratch
if (StringUtil.isBlank(resourceId) || containsId(resourceId)) {
resourceId = createUniqueResourceId(resource);
}
resource.setId(resourceId);
}
// check if the id is unique. if not: create one from scratch
if (StringUtil.isBlank(resourceId) || containsId(resourceId)) {
resourceId = createUniqueResourceId(resource);
/**
* Check if the id is a valid identifier. if not: prepend with valid identifier
*
* @param resource resource
* @return a valid id
*/
private String makeValidId(String resourceId, Resource resource) {
if (StringUtil.isNotBlank(resourceId) && !Character
.isJavaIdentifierStart(resourceId.charAt(0))) {
resourceId = getResourceItemPrefix(resource) + resourceId;
}
return resourceId;
}
resource.setId(resourceId);
}
/**
* Check if the id is a valid identifier. if not: prepend with valid identifier
*
* @param resource
* @return a valid id
*/
private String makeValidId(String resourceId, Resource resource) {
if (StringUtil.isNotBlank(resourceId) && !Character
.isJavaIdentifierStart(resourceId.charAt(0))) {
resourceId = getResourceItemPrefix(resource) + resourceId;
private String getResourceItemPrefix(Resource resource) {
String result;
if (MediaTypes.isBitmapImage(resource.getMediaType())) {
result = IMAGE_PREFIX;
} else {
result = ITEM_PREFIX;
}
return result;
}
return resourceId;
}
private String getResourceItemPrefix(Resource resource) {
String result;
if (MediaTypes.isBitmapImage(resource.getMediaType())) {
result = IMAGE_PREFIX;
} else {
result = ITEM_PREFIX;
/**
* Creates a new resource id that is guaranteed to be unique for this set of Resources
*
* @param resource resource
* @return a new resource id that is guaranteed to be unique for this set of Resources
*/
private String createUniqueResourceId(Resource resource) {
int counter = lastId;
if (counter == Integer.MAX_VALUE) {
if (resources.size() == Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Resources contains " + Integer.MAX_VALUE
+ " elements: no new elements can be added");
} else {
counter = 1;
}
}
String prefix = getResourceItemPrefix(resource);
String result = prefix + counter;
while (containsId(result)) {
result = prefix + (++counter);
}
lastId = counter;
return result;
}
return result;
}
/**
* Creates a new resource id that is guaranteed to be unique for this set of Resources
*
* @param resource
* @return a new resource id that is guaranteed to be unique for this set of Resources
*/
private String createUniqueResourceId(Resource resource) {
int counter = lastId;
if (counter == Integer.MAX_VALUE) {
if (resources.size() == Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Resources contains " + Integer.MAX_VALUE
+ " elements: no new elements can be added");
} else {
counter = 1;
}
/**
* Whether the map of resources already contains a resource with the given id.
*
* @param id id
* @return Whether the map of resources already contains a resource with the given id.
*/
public boolean containsId(String id) {
if (StringUtil.isBlank(id)) {
return false;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return true;
}
}
return false;
}
String prefix = getResourceItemPrefix(resource);
String result = prefix + counter;
while (containsId(result)) {
result = prefix + (++counter);
/**
* Gets the resource with the given id.
*
* @param id id
* @return null if not found
*/
public Resource getById(String id) {
if (StringUtil.isBlank(id)) {
return null;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return resource;
}
}
return null;
}
lastId = counter;
return result;
}
/**
* Whether the map of resources already contains a resource with the given id.
*
* @param id
* @return Whether the map of resources already contains a resource with the given id.
*/
public boolean containsId(String id) {
if (StringUtil.isBlank(id)) {
return false;
public Resource getByProperties(String properties) {
if (StringUtil.isBlank(properties)) {
return null;
}
for (Resource resource : resources.values()) {
if (properties.equals(resource.getProperties())) {
return resource;
}
}
return null;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return true;
}
/**
* Remove the resource with the given href.
*
* @param href href
* @return the removed resource, null if not found
*/
public Resource remove(String href) {
return resources.remove(href);
}
return false;
}
/**
* Gets the resource with the given id.
*
* @param id
* @return null if not found
*/
public Resource getById(String id) {
if (StringUtil.isBlank(id)) {
return null;
private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
&& !resources.containsKey(resource.getHref())) {
return;
}
if (StringUtil.isBlank(resource.getHref())) {
if (resource.getMediaType() == null) {
throw new IllegalArgumentException(
"Resource must have either a MediaType or a href");
}
int i = 1;
String href = createHref(resource.getMediaType(), i);
while (resources.containsKey(href)) {
href = createHref(resource.getMediaType(), (++i));
}
resource.setHref(href);
}
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return resource;
}
private String createHref(MediaType mediaType, int counter) {
if (MediaTypes.isBitmapImage(mediaType)) {
return "image_" + counter + mediaType.getDefaultExtension();
} else {
return "item_" + counter + mediaType.getDefaultExtension();
}
}
return null;
}
public Resource getByProperties(String properties) {
if (StringUtil.isBlank(properties)) {
return null;
public boolean isEmpty() {
return resources.isEmpty();
}
for (Resource resource : resources.values()) {
if (properties.equals(resource.getProperties())) {
return resource;
}
/**
* The number of resources
*
* @return The number of resources
*/
public int size() {
return resources.size();
}
return null;
}
/**
* Remove the resource with the given href.
*
* @param href
* @return the removed resource, null if not found
*/
public Resource remove(String href) {
return resources.remove(href);
}
private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
&& !resources.containsKey(resource.getHref())) {
return;
/**
* The resources that make up this book.
* Resources can be xhtml pages, images, xml documents, etc.
*
* @return The resources that make up this book.
*/
@SuppressWarnings("unused")
public Map<String, Resource> getResourceMap() {
return resources;
}
if (StringUtil.isBlank(resource.getHref())) {
if (resource.getMediaType() == null) {
throw new IllegalArgumentException(
"Resource must have either a MediaType or a href");
}
int i = 1;
String href = createHref(resource.getMediaType(), i);
while (resources.containsKey(href)) {
href = createHref(resource.getMediaType(), (++i));
}
resource.setHref(href);
public Collection<Resource> getAll() {
return resources.values();
}
}
private String createHref(MediaType mediaType, int counter) {
if (MediaTypes.isBitmapImage(mediaType)) {
return "image_" + counter + mediaType.getDefaultExtension();
} else {
return "item_" + counter + mediaType.getDefaultExtension();
/**
* Whether there exists a resource with the given href
*
* @param href href
* @return Whether there exists a resource with the given href
*/
public boolean notContainsByHref(String href) {
if (StringUtil.isBlank(href)) {
return true;
} else {
return !resources.containsKey(
StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR));
}
}
}
public boolean isEmpty() {
return resources.isEmpty();
}
/**
* The number of resources
* @return The number of resources
*/
public int size() {
return resources.size();
}
/**
* The resources that make up this book.
* Resources can be xhtml pages, images, xml documents, etc.
*
* @return The resources that make up this book.
*/
public Map<String, Resource> getResourceMap() {
return resources;
}
public Collection<Resource> getAll() {
return resources.values();
}
/**
* Whether there exists a resource with the given href
* @param href
* @return Whether there exists a resource with the given href
*/
public boolean containsByHref(String href) {
if (StringUtil.isBlank(href)) {
return false;
/**
* Whether there exists a resource with the given href
*
* @param href href
* @return Whether there exists a resource with the given href
*/
@SuppressWarnings("unused")
public boolean containsByHref(String href) {
return !notContainsByHref(href);
}
return resources.containsKey(
StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR));
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources
*/
public void set(Collection<Resource> resources) {
this.resources.clear();
addAll(resources);
}
/**
* Adds all resources from the given Collection of resources to the existing collection.
*
* @param resources
*/
public void addAll(Collection<Resource> resources) {
for (Resource resource : resources) {
fixResourceHref(resource);
this.resources.put(resource.getHref(), resource);
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources resources
*/
public void set(Collection<Resource> resources) {
this.resources.clear();
addAll(resources);
}
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources A map with as keys the resources href and as values the Resources
*/
public void set(Map<String, Resource> resources) {
this.resources = new HashMap<String, Resource>(resources);
}
/**
* First tries to find a resource with as id the given idOrHref, if that
* fails it tries to find one with the idOrHref as href.
*
* @param idOrHref
* @return the found Resource
*/
public Resource getByIdOrHref(String idOrHref) {
Resource resource = getById(idOrHref);
if (resource == null) {
resource = getByHref(idOrHref);
/**
* Adds all resources from the given Collection of resources to the existing collection.
*
* @param resources resources
*/
public void addAll(Collection<Resource> resources) {
for (Resource resource : resources) {
fixResourceHref(resource);
this.resources.put(resource.getHref(), resource);
}
}
return resource;
}
/**
* Gets the resource with the given href.
* If the given href contains a fragmentId then that fragment id will be ignored.
*
* @param href
* @return null if not found.
*/
public Resource getByHref(String href) {
if (StringUtil.isBlank(href)) {
return null;
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources A map with as keys the resources href and as values the Resources
*/
public void set(Map<String, Resource> resources) {
this.resources = new HashMap<>(resources);
}
href = StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource result = resources.get(href);
return result;
}
/**
* Gets the first resource (random order) with the give mediatype.
*
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType
* @return the first resource (random order) with the give mediatype.
*/
public Resource findFirstResourceByMediaType(MediaType mediaType) {
return findFirstResourceByMediaType(resources.values(), mediaType);
}
/**
* Gets the first resource (random order) with the give mediatype.
*
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType
* @return the first resource (random order) with the give mediatype.
*/
public static Resource findFirstResourceByMediaType(
Collection<Resource> resources, MediaType mediaType) {
for (Resource resource : resources) {
if (resource.getMediaType() == mediaType) {
/**
* First tries to find a resource with as id the given idOrHref, if that
* fails it tries to find one with the idOrHref as href.
*
* @param idOrHref idOrHref
* @return the found Resource
*/
public Resource getByIdOrHref(String idOrHref) {
Resource resource = getById(idOrHref);
if (resource == null) {
resource = getByHref(idOrHref);
}
return resource;
}
}
return null;
}
/**
* All resources that have the given MediaType.
*
* @param mediaType
* @return All resources that have the given MediaType.
*/
public List<Resource> getResourcesByMediaType(MediaType mediaType) {
List<Resource> result = new ArrayList<Resource>();
if (mediaType == null) {
return result;
/**
* Gets the resource with the given href.
* If the given href contains a fragmentId then that fragment id will be ignored.
*
* @param href href
* @return null if not found.
*/
public Resource getByHref(String href) {
if (StringUtil.isBlank(href)) {
return null;
}
href = StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR);
return resources.get(href);
}
/**
* Gets the first resource (random order) with the give mediatype.
* <p>
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType mediaType
* @return the first resource (random order) with the give mediatype.
*/
public Resource findFirstResourceByMediaType(MediaType mediaType) {
return findFirstResourceByMediaType(resources.values(), mediaType);
}
/**
* Gets the first resource (random order) with the give mediatype.
* <p>
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
*
* @param mediaType mediaType
* @return the first resource (random order) with the give mediatype.
*/
public static Resource findFirstResourceByMediaType(
Collection<Resource> resources, MediaType mediaType) {
for (Resource resource : resources) {
if (resource.getMediaType() == mediaType) {
return resource;
}
}
return null;
}
for (Resource resource : getAll()) {
if (resource.getMediaType() == mediaType) {
result.add(resource);
}
/**
* All resources that have the given MediaType.
*
* @param mediaType mediaType
* @return All resources that have the given MediaType.
*/
public List<Resource> getResourcesByMediaType(MediaType mediaType) {
List<Resource> result = new ArrayList<>();
if (mediaType == null) {
return result;
}
for (Resource resource : getAll()) {
if (resource.getMediaType() == mediaType) {
result.add(resource);
}
}
return result;
}
return result;
}
/**
* All Resources that match any of the given list of MediaTypes
*
* @param mediaTypes
* @return All Resources that match any of the given list of MediaTypes
*/
public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) {
List<Resource> result = new ArrayList<Resource>();
if (mediaTypes == null) {
return result;
/**
* All Resources that match any of the given list of MediaTypes
*
* @param mediaTypes mediaType
* @return All Resources that match any of the given list of MediaTypes
*/
@SuppressWarnings("unused")
public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) {
List<Resource> result = new ArrayList<>();
if (mediaTypes == null) {
return result;
}
// this is the fastest way of doing this according to
// http://stackoverflow.com/questions/1128723/in-java-how-can-i-test-if-an-array-contains-a-certain-value
List<MediaType> mediaTypesList = Arrays.asList(mediaTypes);
for (Resource resource : getAll()) {
if (mediaTypesList.contains(resource.getMediaType())) {
result.add(resource);
}
}
return result;
}
// this is the fastest way of doing this according to
// http://stackoverflow.com/questions/1128723/in-java-how-can-i-test-if-an-array-contains-a-certain-value
List<MediaType> mediaTypesList = Arrays.asList(mediaTypes);
for (Resource resource : getAll()) {
if (mediaTypesList.contains(resource.getMediaType())) {
result.add(resource);
}
/**
* All resource hrefs
*
* @return all resource hrefs
*/
public Collection<String> getAllHrefs() {
return resources.keySet();
}
return result;
}
/**
* All resource hrefs
*
* @return all resource hrefs
*/
public Collection<String> getAllHrefs() {
return resources.keySet();
}
}

@ -22,13 +22,13 @@ public class Spine implements Serializable {
private List<SpineReference> spineReferences;
public Spine() {
this(new ArrayList<SpineReference>());
this(new ArrayList<>());
}
/**
* Creates a spine out of all the resources in the table of contents.
*
* @param tableOfContents
* @param tableOfContents tableOfContents
*/
public Spine(TableOfContents tableOfContents) {
this.spineReferences = createSpineReferences(
@ -41,8 +41,8 @@ public class Spine implements Serializable {
public static List<SpineReference> createSpineReferences(
Collection<Resource> resources) {
List<SpineReference> result = new ArrayList<SpineReference>(
resources.size());
List<SpineReference> result = new ArrayList<>(
resources.size());
for (Resource resource : resources) {
result.add(new SpineReference(resource));
}
@ -61,7 +61,7 @@ public class Spine implements Serializable {
* Gets the resource at the given index.
* Null if not found.
*
* @param index
* @param index index
* @return the resource at the given index.
*/
public Resource getResource(int index) {
@ -76,7 +76,7 @@ public class Spine implements Serializable {
*
* Null if not found.
*
* @param resourceId
* @param resourceId resourceId
* @return the first resource that has the given resourceId.
*/
public int findFirstResourceById(String resourceId) {
@ -96,12 +96,12 @@ public class Spine implements Serializable {
/**
* Adds the given spineReference to the spine references and returns it.
*
* @param spineReference
* @param spineReference spineReference
* @return the given spineReference
*/
public SpineReference addSpineReference(SpineReference spineReference) {
if (spineReferences == null) {
this.spineReferences = new ArrayList<SpineReference>();
this.spineReferences = new ArrayList<>();
}
spineReferences.add(spineReference);
return spineReference;
@ -112,6 +112,7 @@ public class Spine implements Serializable {
*
* @return the given spineReference
*/
@SuppressWarnings("unused")
public SpineReference addResource(Resource resource) {
return addSpineReference(new SpineReference(resource));
}
@ -130,7 +131,7 @@ public class Spine implements Serializable {
* The epubwriter will look for it here first, followed by some clever tricks to find it elsewhere if not found.
* Put it here to be sure of the expected behaviours.
*
* @param tocResource
* @param tocResource tocResource
*/
public void setTocResource(Resource tocResource) {
this.tocResource = tocResource;
@ -149,7 +150,7 @@ public class Spine implements Serializable {
/**
* The position within the spine of the given resource.
*
* @param currentResource
* @param currentResource currentResource
* @return something &lt; 0 if not found.
*
*/

@ -11,42 +11,42 @@ import java.io.Serializable;
*/
public class SpineReference extends ResourceReference implements Serializable {
private static final long serialVersionUID = -7921609197351510248L;
private boolean linear = true;
public SpineReference(Resource resource) {
this(resource, true);
}
public SpineReference(Resource resource, boolean linear) {
super(resource);
this.linear = linear;
}
/**
* Linear denotes whether the section is Primary or Auxiliary.
* Usually the cover page has linear set to false and all the other sections
* have it set to true.
*
* It's an optional property that readers may also ignore.
*
* <blockquote>primary or auxiliary is useful for Reading Systems which
* opt to present auxiliary content differently than primary content.
* For example, a Reading System might opt to render auxiliary content in
* a popup window apart from the main window which presents the primary
* content. (For an example of the types of content that may be considered
* auxiliary, refer to the example below and the subsequent discussion.)</blockquote>
* @see <a href="http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4">OPF Spine specification</a>
*
* @return whether the section is Primary or Auxiliary.
*/
public boolean isLinear() {
return linear;
}
public void setLinear(boolean linear) {
this.linear = linear;
}
private static final long serialVersionUID = -7921609197351510248L;
private boolean linear;//default = true;
public SpineReference(Resource resource) {
this(resource, true);
}
public SpineReference(Resource resource, boolean linear) {
super(resource);
this.linear = linear;
}
/**
* Linear denotes whether the section is Primary or Auxiliary.
* Usually the cover page has linear set to false and all the other sections
* have it set to true.
* <p>
* It's an optional property that readers may also ignore.
*
* <blockquote>primary or auxiliary is useful for Reading Systems which
* opt to present auxiliary content differently than primary content.
* For example, a Reading System might opt to render auxiliary content in
* a popup window apart from the main window which presents the primary
* content. (For an example of the types of content that may be considered
* auxiliary, refer to the example below and the subsequent discussion.)</blockquote>
*
* @return whether the section is Primary or Auxiliary.
* @see <a href="http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4">OPF Spine specification</a>
*/
public boolean isLinear() {
return linear;
}
public void setLinear(boolean linear) {
this.linear = linear;
}
}

@ -17,11 +17,8 @@ public class TOCReference extends TitledResourceReference
private static final long serialVersionUID = 5787958246077042456L;
private List<TOCReference> children;
private static final Comparator<TOCReference> COMPARATOR_BY_TITLE_IGNORE_CASE =
(tocReference1, tocReference2) ->
String.CASE_INSENSITIVE_ORDER
.compare(tocReference1.getTitle(), tocReference2.getTitle());
private static final Comparator<TOCReference> COMPARATOR_BY_TITLE_IGNORE_CASE = (tocReference1, tocReference2) -> String.CASE_INSENSITIVE_ORDER.compare(tocReference1.getTitle(), tocReference2.getTitle());
@Deprecated
public TOCReference() {
this(null, null, null);
}
@ -39,7 +36,7 @@ public class TOCReference extends TitledResourceReference
super(resource, title, fragmentId);
this.children = children;
}
@SuppressWarnings("unused")
public static Comparator<TOCReference> getComparatorByTitleIgnoreCase() {
return COMPARATOR_BY_TITLE_IGNORE_CASE;
}

@ -29,7 +29,7 @@ public class TableOfContents implements Serializable {
private List<TOCReference> tocReferences;
public TableOfContents() {
this(new ArrayList<TOCReference>());
this(new ArrayList<>());
}
public TableOfContents(List<TOCReference> tocReferences) {
@ -48,6 +48,7 @@ public class TableOfContents implements Serializable {
* Calls addTOCReferenceAtLocation after splitting the path using the DEFAULT_PATH_SEPARATOR.
* @return the new TOCReference
*/
@SuppressWarnings("unused")
public TOCReference addSection(Resource resource, String path) {
return addSection(resource, path, DEFAULT_PATH_SEPARATOR);
}
@ -55,9 +56,9 @@ public class TableOfContents implements Serializable {
/**
* Calls addTOCReferenceAtLocation after splitting the path using the given pathSeparator.
*
* @param resource
* @param path
* @param pathSeparator
* @param resource resource
* @param path path
* @param pathSeparator pathSeparator
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path,
@ -69,8 +70,8 @@ public class TableOfContents implements Serializable {
/**
* Finds the first TOCReference in the given list that has the same title as the given Title.
*
* @param title
* @param tocReferences
* @param title title
* @param tocReferences tocReferences
* @return null if not found.
*/
private static TOCReference findTocReferenceByTitle(String title,
@ -95,8 +96,8 @@ public class TableOfContents implements Serializable {
* will point to the given Resource</li>
* </ul>
*
* @param resource
* @param pathElements
* @param resource resource
* @param pathElements pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String[] pathElements) {
@ -105,8 +106,7 @@ public class TableOfContents implements Serializable {
}
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
String currentTitle = pathElements[i];
for (String currentTitle : pathElements) {
result = findTocReferenceByTitle(currentTitle, currentTocReferences);
if (result == null) {
result = new TOCReference(currentTitle, null);
@ -130,10 +130,11 @@ public class TableOfContents implements Serializable {
* If this TOCReference didn't exist yet it will be created and have a title of ""</li>
* </ul>
*
* @param resource
* @param pathElements
* @param resource resource
* @param pathElements pathElements
* @return the new TOCReference
*/
@SuppressWarnings("unused")
public TOCReference addSection(Resource resource, int[] pathElements,
String sectionTitlePrefix, String sectionNumberSeparator) {
if (pathElements == null || pathElements.length == 0) {

@ -2,77 +2,87 @@ package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
public class TitledResourceReference extends ResourceReference
implements Serializable {
private static final long serialVersionUID = 3918155020095190080L;
private String fragmentId;
private String title;
public TitledResourceReference(Resource resource) {
this(resource, null);
}
public TitledResourceReference(Resource resource, String title) {
this(resource, title, null);
}
public TitledResourceReference(Resource resource, String title,
String fragmentId) {
super(resource);
this.title = title;
this.fragmentId = fragmentId;
}
public String getFragmentId() {
return fragmentId;
}
public void setFragmentId(String fragmentId) {
this.fragmentId = fragmentId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/**
* If the fragmentId is blank it returns the resource href, otherwise
* it returns the resource href + '#' + the fragmentId.
*
* @return If the fragmentId is blank it returns the resource href,
* otherwise it returns the resource href + '#' + the fragmentId.
*/
public String getCompleteHref() {
if (StringUtil.isBlank(fragmentId)) {
return resource.getHref();
} else {
return resource.getHref() + Constants.FRAGMENT_SEPARATOR_CHAR
+ fragmentId;
implements Serializable {
private static final long serialVersionUID = 3918155020095190080L;
private String fragmentId;
private String title;
/**
* 这会使title为null
*
* @param resource resource
*/
@Deprecated
@SuppressWarnings("unused")
public TitledResourceReference(Resource resource) {
this(resource, null);
}
public TitledResourceReference(Resource resource, String title) {
this(resource, title, null);
}
public TitledResourceReference(Resource resource, String title,
String fragmentId) {
super(resource);
this.title = title;
this.fragmentId = fragmentId;
}
public String getFragmentId() {
return fragmentId;
}
public void setFragmentId(String fragmentId) {
this.fragmentId = fragmentId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/**
* If the fragmentId is blank it returns the resource href, otherwise
* it returns the resource href + '#' + the fragmentId.
*
* @return If the fragmentId is blank it returns the resource href,
* otherwise it returns the resource href + '#' + the fragmentId.
*/
public String getCompleteHref() {
if (StringUtil.isBlank(fragmentId)) {
return resource.getHref();
} else {
return resource.getHref() + Constants.FRAGMENT_SEPARATOR_CHAR
+ fragmentId;
}
}
@Override
public Resource getResource() {
//修复getTitle为null的bug
resource.setTitle(title);
return resource;
}
public void setResource(Resource resource, String fragmentId) {
super.setResource(resource);
this.fragmentId = fragmentId;
}
/**
* Sets the resource to the given resource and sets the fragmentId to null.
*/
public void setResource(Resource resource) {
setResource(resource, null);
}
}
@Override
public Resource getResource() {
resource.setTitle(title);
return resource;
}
public void setResource(Resource resource, String fragmentId) {
super.setResource(resource);
this.fragmentId = fragmentId;
}
/**
* Sets the resource to the given resource and sets the fragmentId to null.
*/
public void setResource(Resource resource) {
setResource(resource, null);
}
}

@ -1,6 +1,6 @@
package me.ag2s.epublib.epub;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
/**
* Post-processes a book.
@ -16,5 +16,5 @@ public interface BookProcessor {
*/
BookProcessor IDENTITY_BOOKPROCESSOR = book -> book;
EpubBook processBook(EpubBook book);
Book processBook(Book book);
}

@ -2,25 +2,23 @@ package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.Book;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import me.ag2s.epublib.domain.EpubBook;
//import io.documentnode.minilog.Logger;
/**
* A book processor that combines several other bookprocessors
* <p>
*
* Fixes coverpage/coverimage.
* Cleans up the XHTML.
*
* @author paul.siegmann
*/
@SuppressWarnings("unused declaration")
public class BookProcessorPipeline implements BookProcessor {
private static String TAG= BookProcessorPipeline.class.getName();
private static final String TAG= BookProcessorPipeline.class.getName();
private List<BookProcessor> bookProcessors;
public BookProcessorPipeline() {
@ -32,7 +30,7 @@ public class BookProcessorPipeline implements BookProcessor {
}
@Override
public EpubBook processBook(EpubBook book) {
public Book processBook(Book book) {
if (bookProcessors == null) {
return book;
}
@ -40,7 +38,7 @@ public class BookProcessorPipeline implements BookProcessor {
try {
book = bookProcessor.processBook(book);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Log.e(TAG,e.getMessage(), e);
}
}
return book;
@ -48,14 +46,14 @@ public class BookProcessorPipeline implements BookProcessor {
public void addBookProcessor(BookProcessor bookProcessor) {
if (this.bookProcessors == null) {
bookProcessors = new ArrayList<BookProcessor>();
bookProcessors = new ArrayList<>();
}
this.bookProcessors.add(bookProcessor);
}
public void addBookProcessors(Collection<BookProcessor> bookProcessors) {
if (this.bookProcessors == null) {
this.bookProcessors = new ArrayList<BookProcessor>();
this.bookProcessors = new ArrayList<>();
}
this.bookProcessors.addAll(bookProcessors);
}

@ -21,10 +21,10 @@ class DOMUtil {
/**
* First tries to get the attribute value by doing an getAttributeNS on the element, if that gets an empty element it does a getAttribute without namespace.
*
* @param element
* @param namespace
* @param attribute
* @return
* @param element element
* @param namespace namespace
* @param attribute attribute
* @return String Attribute
*/
public static String getAttribute(Element element, String namespace,
String attribute) {
@ -38,16 +38,17 @@ class DOMUtil {
/**
* Gets all descendant elements of the given parentElement with the given namespace and tagname and returns their text child as a list of String.
*
* @param parentElement
* @param namespace
* @param tagname
* @return
* @param parentElement parentElement
* @param namespace namespace
* @param tagName tagName
* @return List<String>
*/
public static List<String> getElementsTextChild(Element parentElement,
String namespace, String tagname) {
String namespace, String tagName) {
NodeList elements = parentElement
.getElementsByTagNameNS(namespace, tagname);
List<String> result = new ArrayList<String>(elements.getLength());
.getElementsByTagNameNS(namespace, tagName);
//ArrayList 初始化时指定长度提高性能
List<String> result = new ArrayList<>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
result.add(getTextChildrenContent((Element) elements.item(i)));
}
@ -58,13 +59,13 @@ class DOMUtil {
* Finds in the current document the first element with the given namespace and elementName and with the given findAttributeName and findAttributeValue.
* It then returns the value of the given resultAttributeName.
*
* @param document
* @param namespace
* @param elementName
* @param findAttributeName
* @param findAttributeValue
* @param resultAttributeName
* @return
* @param document document
* @param namespace namespace
* @param elementName elementName
* @param findAttributeName findAttributeName
* @param findAttributeValue findAttributeValue
* @param resultAttributeName resultAttributeName
* @return String value
*/
public static String getFindAttributeValue(Document document,
String namespace, String elementName, String findAttributeName,
@ -85,10 +86,10 @@ class DOMUtil {
/**
* Gets the first element that is a child of the parentElement and has the given namespace and tagName
*
* @param parentElement
* @param namespace
* @param tagName
* @return
* @param parentElement parentElement
* @param namespace namespace
* @param tagName tagName
* @return Element
*/
public static Element getFirstElementByTagNameNS(Element parentElement,
String namespace, String tagName) {
@ -111,8 +112,8 @@ class DOMUtil {
* when the text is Chinese characters then on Android each Characater is represented in the DOM as
* an individual Text node.
*
* @param parentElement
* @return
* @param parentElement parentElement
* @return String value
*/
public static String getTextChildrenContent(Element parentElement) {
if (parentElement == null) {

@ -3,7 +3,7 @@ package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.Constants;
//import io.documentnode.minilog.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -11,13 +11,14 @@ import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
//import org.kxml2.io.KXmlSerializer;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
@ -25,114 +26,114 @@ import org.xmlpull.v1.XmlSerializer;
* Various low-level support methods for reading/writing epubs.
*
* @author paul.siegmann
*
*/
public class EpubProcessorSupport {
private static String TAG= EpubProcessorSupport.class.getName();
protected static DocumentBuilderFactory documentBuilderFactory;
static {
init();
}
static class EntityResolverImpl implements EntityResolver {
private String previousLocation;
@Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
String resourcePath;
if (systemId.startsWith("http:")) {
URL url = new URL(systemId);
resourcePath = "dtd/" + url.getHost() + url.getPath();
previousLocation = resourcePath
.substring(0, resourcePath.lastIndexOf('/'));
} else {
resourcePath =
previousLocation + systemId.substring(systemId.lastIndexOf('/'));
}
if (this.getClass().getClassLoader().getResource(resourcePath) == null) {
throw new RuntimeException(
"remote resource is not cached : [" + systemId
+ "] cannot continue");
}
InputStream in = EpubProcessorSupport.class.getClassLoader()
.getResourceAsStream(resourcePath);
return new InputSource(in);
private static final String TAG = EpubProcessorSupport.class.getName();
protected static DocumentBuilderFactory documentBuilderFactory;
static {
init();
}
static class EntityResolverImpl implements EntityResolver {
private String previousLocation;
@Override
public InputSource resolveEntity(String publicId, String systemId)
throws IOException {
String resourcePath;
if (systemId.startsWith("http:")) {
URL url = new URL(systemId);
resourcePath = "dtd/" + url.getHost() + url.getPath();
previousLocation = resourcePath
.substring(0, resourcePath.lastIndexOf('/'));
} else {
resourcePath =
previousLocation + systemId.substring(systemId.lastIndexOf('/'));
}
if (Objects.requireNonNull(this.getClass().getClassLoader()).getResource(resourcePath) == null) {
throw new RuntimeException(
"remote resource is not cached : [" + systemId
+ "] cannot continue");
}
InputStream in = Objects.requireNonNull(EpubProcessorSupport.class.getClassLoader())
.getResourceAsStream(resourcePath);
return new InputSource(in);
}
}
}
private static void init() {
EpubProcessorSupport.documentBuilderFactory = DocumentBuilderFactory
.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setValidating(false);
}
public static XmlSerializer createXmlSerializer(OutputStream out)
throws UnsupportedEncodingException {
return createXmlSerializer(
new OutputStreamWriter(out, Constants.CHARACTER_ENCODING));
}
public static XmlSerializer createXmlSerializer(Writer out) {
XmlSerializer result = null;
try {
/*
* Disable XmlPullParserFactory here before it doesn't work when
* building native image using GraalVM
*/
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setValidating(true);
result = factory.newSerializer();
//result = new KXmlSerializer();
result.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
result.setOutput(out);
} catch (Exception e) {
Log.e(TAG,
"When creating XmlSerializer: " + e.getClass().getName() + ": " + e
.getMessage());
private static void init() {
EpubProcessorSupport.documentBuilderFactory = DocumentBuilderFactory
.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setValidating(false);
}
return result;
}
/**
* Gets an EntityResolver that loads dtd's and such from the epub4j classpath.
* In order to enable the loading of relative urls the given EntityResolver contains the previousLocation.
* Because of a new EntityResolver is created every time this method is called.
* Fortunately the EntityResolver created uses up very little memory per instance.
*
* @return an EntityResolver that loads dtd's and such from the epub4j classpath.
*/
public static EntityResolver getEntityResolver() {
return new EntityResolverImpl();
}
public DocumentBuilderFactory getDocumentBuilderFactory() {
return documentBuilderFactory;
}
/**
* Creates a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*
* @return a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*/
public static DocumentBuilder createDocumentBuilder() {
DocumentBuilder result = null;
try {
result = documentBuilderFactory.newDocumentBuilder();
result.setEntityResolver(getEntityResolver());
} catch (ParserConfigurationException e) {
Log.e(TAG,e.getMessage());
public static XmlSerializer createXmlSerializer(OutputStream out)
throws UnsupportedEncodingException {
return createXmlSerializer(
new OutputStreamWriter(out, Constants.CHARACTER_ENCODING));
}
public static XmlSerializer createXmlSerializer(Writer out) {
XmlSerializer result = null;
try {
/*
* Disable XmlPullParserFactory here before it doesn't work when
* building native image using GraalVM
*/
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setValidating(true);
result = factory.newSerializer();
//result = new KXmlSerializer();
result.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
result.setOutput(out);
} catch (Exception e) {
Log.e(TAG,
"When creating XmlSerializer: " + e.getClass().getName() + ": " + e
.getMessage());
}
return result;
}
/**
* Gets an EntityResolver that loads dtd's and such from the epub4j classpath.
* In order to enable the loading of relative urls the given EntityResolver contains the previousLocation.
* Because of a new EntityResolver is created every time this method is called.
* Fortunately the EntityResolver created uses up very little memory per instance.
*
* @return an EntityResolver that loads dtd's and such from the epub4j classpath.
*/
public static EntityResolver getEntityResolver() {
return new EntityResolverImpl();
}
@SuppressWarnings("unused")
public DocumentBuilderFactory getDocumentBuilderFactory() {
return documentBuilderFactory;
}
/**
* Creates a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*
* @return a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath.
*/
public static DocumentBuilder createDocumentBuilder() {
DocumentBuilder result = null;
try {
result = documentBuilderFactory.newDocumentBuilder();
result.setEntityResolver(getEntityResolver());
} catch (ParserConfigurationException e) {
Log.e(TAG, e.getMessage());
}
return result;
}
return result;
}
}

@ -13,7 +13,7 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
@ -21,150 +21,156 @@ import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
/**
* Reads an epub file.
*
* @author paul
*
*/
public class EpubReader {
private static String TAG= EpubReader.class.getName();
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
public EpubBook readEpub(InputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
public EpubBook readEpub(ZipInputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
public EpubBook readEpub(ZipFile zipfile) throws IOException {
return readEpub(zipfile, Constants.CHARACTER_ENCODING);
}
/**
* Read epub from inputstream
*
* @param in the inputstream from which to read the epub
* @param encoding the encoding to use for the html files within the epub
* @return the Book as read from the inputstream
* @throws IOException
*/
public EpubBook readEpub(InputStream in, String encoding) throws IOException {
return readEpub(new ZipInputStream(in), encoding);
}
/**
* Reads this EPUB without loading any resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
* @return this Book without loading all resources into memory.
* @throws IOException
*/
public EpubBook readEpubLazy(ZipFile zipFile, String encoding)
throws IOException {
return readEpubLazy(zipFile, encoding,
Arrays.asList(MediaTypes.mediaTypes));
}
public EpubBook readEpub(ZipInputStream in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
public EpubBook readEpub(ZipFile in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
/**
* Reads this EPUB without loading all resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
* @param lazyLoadedTypes a list of the MediaType to load lazily
* @return this Book without loading all resources into memory.
* @throws IOException
*/
public EpubBook readEpubLazy(ZipFile zipFile, String encoding,
List<MediaType> lazyLoadedTypes) throws IOException {
Resources resources = ResourcesLoader
.loadResources(zipFile, encoding, lazyLoadedTypes);
return readEpub(resources);
}
public EpubBook readEpub(Resources resources) throws IOException {
return readEpub(resources, new EpubBook());
}
public EpubBook readEpub(Resources resources, EpubBook result) throws IOException {
if (result == null) {
result = new EpubBook();
private static final String TAG = EpubReader.class.getName();
private final BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
public Book readEpub(InputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
@SuppressWarnings("unused")
public Book readEpub(ZipInputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING);
}
@SuppressWarnings("unused")
public Book readEpub(ZipFile zipfile) throws IOException {
return readEpub(zipfile, Constants.CHARACTER_ENCODING);
}
/**
* Read epub from inputstream
*
* @param in the inputstream from which to read the epub
* @param encoding the encoding to use for the html files within the epub
* @return the Book as read from the inputstream
* @throws IOException IOException
*/
public Book readEpub(InputStream in, String encoding) throws IOException {
return readEpub(new ZipInputStream(in), encoding);
}
/**
* Reads this EPUB without loading any resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
* @return this Book without loading all resources into memory.
* @throws IOException IOException
*/
@SuppressWarnings("unused")
public Book readEpubLazy(ZipFile zipFile, String encoding)
throws IOException {
return readEpubLazy(zipFile, encoding,
Arrays.asList(MediaTypes.mediaTypes));
}
public Book readEpub(ZipInputStream in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
handleMimeType(result, resources);
String packageResourceHref = getPackageResourceHref(resources);
Resource packageResource = processPackageResource(packageResourceHref,
result, resources);
result.setOpfResource(packageResource);
Resource ncxResource = processNcxResource(packageResource, result);
result.setNcxResource(ncxResource);
result = postProcessBook(result);
return result;
}
private EpubBook postProcessBook(EpubBook book) {
if (bookProcessor != null) {
book = bookProcessor.processBook(book);
public Book readEpub(ZipFile in, String encoding) throws IOException {
return readEpub(ResourcesLoader.loadResources(in, encoding));
}
return book;
}
private Resource processNcxResource(Resource packageResource, EpubBook book) {
Log.d(TAG, "OPF:getHref()" + packageResource.getHref());
return NCXDocument.read(book, this);
}
private Resource processPackageResource(String packageResourceHref, EpubBook book,
Resources resources) {
Resource packageResource = resources.remove(packageResourceHref);
try {
PackageDocumentReader.read(packageResource, this, book, resources);
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
/**
* Reads this EPUB without loading all resources into memory.
*
* @param zipFile the file to load
* @param encoding the encoding for XHTML files
* @param lazyLoadedTypes a list of the MediaType to load lazily
* @return this Book without loading all resources into memory.
* @throws IOException IOException
*/
public Book readEpubLazy(ZipFile zipFile, String encoding,
List<MediaType> lazyLoadedTypes) throws IOException {
Resources resources = ResourcesLoader
.loadResources(zipFile, encoding, lazyLoadedTypes);
return readEpub(resources);
}
return packageResource;
}
private String getPackageResourceHref(Resources resources) {
String defaultResult = "OEBPS/content.opf";
String result = defaultResult;
public Book readEpub(Resources resources) {
return readEpub(resources, new Book());
}
Resource containerResource = resources.remove("META-INF/container.xml");
if (containerResource == null) {
return result;
public Book readEpub(Resources resources, Book result) {
if (result == null) {
result = new Book();
}
handleMimeType(result, resources);
String packageResourceHref = getPackageResourceHref(resources);
Resource packageResource = processPackageResource(packageResourceHref,
result, resources);
result.setOpfResource(packageResource);
Resource ncxResource = processNcxResource(packageResource, result);
result.setNcxResource(ncxResource);
result = postProcessBook(result);
return result;
}
try {
Document document = ResourceUtil.getAsDocument(containerResource);
Element rootFileElement = (Element) ((Element) document
.getDocumentElement().getElementsByTagName("rootfiles").item(0))
.getElementsByTagName("rootfile").item(0);
result = rootFileElement.getAttribute("full-path");
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
private Book postProcessBook(Book book) {
if (bookProcessor != null) {
book = bookProcessor.processBook(book);
}
return book;
}
if (StringUtil.isBlank(result)) {
result = defaultResult;
private Resource processNcxResource(Resource packageResource, Book book) {
Log.d(TAG, "OPF:getHref()" + packageResource.getHref());
if (book.isEpub3()) {
return NCXDocumentV3.read(book, this);
} else {
return NCXDocumentV2.read(book, this);
}
}
private Resource processPackageResource(String packageResourceHref, Book book,
Resources resources) {
Resource packageResource = resources.remove(packageResourceHref);
try {
PackageDocumentReader.read(packageResource, this, book, resources);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
return packageResource;
}
return result;
}
private void handleMimeType(EpubBook result, Resources resources) {
resources.remove("mimetype");
}
private String getPackageResourceHref(Resources resources) {
String defaultResult = "OEBPS/content.opf";
String result = defaultResult;
Resource containerResource = resources.remove("META-INF/container.xml");
if (containerResource == null) {
return result;
}
try {
Document document = ResourceUtil.getAsDocument(containerResource);
Element rootFileElement = (Element) ((Element) document
.getDocumentElement().getElementsByTagName("rootfiles").item(0))
.getElementsByTagName("rootfile").item(0);
result = rootFileElement.getAttribute("full-path");
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
if (StringUtil.isBlank(result)) {
result = defaultResult;
}
return result;
}
@SuppressWarnings("unused")
private void handleMimeType(Book result, Resources resources) {
resources.remove("mimetype");
//result.setResources(resources);
}
}

@ -2,8 +2,10 @@ package me.ag2s.epublib.epub;
import android.util.Log;
import org.xmlpull.v1.XmlSerializer;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.util.IOUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -12,13 +14,7 @@ import java.io.Writer;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.util.IOUtil;
//import io.documentnode.minilog.Logger;
import org.xmlpull.v1.XmlSerializer;
/**
* Generates an epub file. Not thread-safe, single use object.
@ -27,12 +23,12 @@ import me.ag2s.epublib.util.IOUtil;
*/
public class EpubWriter {
private static String TAG= EpubWriter.class.getName();
private static final String TAG= EpubWriter.class.getName();
// package
static final String EMPTY_NAMESPACE_PREFIX = "";
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
private BookProcessor bookProcessor;
public EpubWriter() {
this(BookProcessor.IDENTITY_BOOKPROCESSOR);
@ -44,7 +40,7 @@ public class EpubWriter {
}
public void write(EpubBook book, OutputStream out) throws IOException {
public void write(Book book, OutputStream out) throws IOException {
book = processBook(book);
ZipOutputStream resultStream = new ZipOutputStream(out);
writeMimeType(resultStream);
@ -55,17 +51,22 @@ public class EpubWriter {
resultStream.close();
}
private EpubBook processBook(EpubBook book) {
private Book processBook(Book book) {
if (bookProcessor != null) {
book = bookProcessor.processBook(book);
}
return book;
}
private void initTOCResource(EpubBook book) {
private void initTOCResource(Book book) {
Resource tocResource;
try {
tocResource = NCXDocument.createNCXResource(book);
if(book.isEpub3()){
tocResource = NCXDocumentV3.createNCXResource(book);
}else {
tocResource = NCXDocumentV2.createNCXResource(book);
}
Resource currentTocResource = book.getSpine().getTocResource();
if (currentTocResource != null) {
book.getResources().remove(currentTocResource.getHref());
@ -80,8 +81,7 @@ public class EpubWriter {
}
private void writeResources(EpubBook book, ZipOutputStream resultStream)
throws IOException {
private void writeResources(Book book, ZipOutputStream resultStream) {
for (Resource resource : book.getResources().getAll()) {
writeResource(resource, resultStream);
}
@ -90,12 +90,10 @@ public class EpubWriter {
/**
* Writes the resource to the resultStream.
*
* @param resource
* @param resultStream
* @throws IOException
* @param resource resource
* @param resultStream resultStream
*/
private void writeResource(Resource resource, ZipOutputStream resultStream)
throws IOException {
private void writeResource(Resource resource, ZipOutputStream resultStream) {
if (resource == null) {
return;
}
@ -110,11 +108,11 @@ public class EpubWriter {
}
private void writePackageDocument(EpubBook book, ZipOutputStream resultStream)
throws IOException {
private void writePackageDocument(Book book, ZipOutputStream resultStream)
throws IOException {
resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf"));
XmlSerializer xmlSerializer = EpubProcessorSupport
.createXmlSerializer(resultStream);
.createXmlSerializer(resultStream);
PackageDocumentWriter.write(this, xmlSerializer, book);
xmlSerializer.flush();
// String resultAsString = result.toString();
@ -124,8 +122,8 @@ public class EpubWriter {
/**
* Writes the META-INF/container.xml file.
*
* @param resultStream
* @throws IOException
* @param resultStream resultStream
* @throws IOException IOException
*/
private void writeContainer(ZipOutputStream resultStream) throws IOException {
resultStream.putNextEntry(new ZipEntry("META-INF/container.xml"));
@ -144,8 +142,8 @@ public class EpubWriter {
/**
* Stores the mimetype as an uncompressed file in the ZipOutputStream.
*
* @param resultStream
* @throws IOException
* @param resultStream resultStream
* @throws IOException IOException
*/
private void writeMimeType(ZipOutputStream resultStream) throws IOException {
ZipEntry mimetypeZipEntry = new ZipEntry("mimetype");
@ -175,11 +173,13 @@ public class EpubWriter {
return MediaTypes.NCX.getName();
}
@SuppressWarnings("unused")
public BookProcessor getBookProcessor() {
return bookProcessor;
}
@SuppressWarnings("unused")
public void setBookProcessor(BookProcessor bookProcessor) {
this.bookProcessor = bookProcessor;
}

@ -2,7 +2,7 @@ package me.ag2s.epublib.epub;
import me.ag2s.epublib.domain.Resource;
import java.io.OutputStream;
@SuppressWarnings("unused")
public interface HtmlProcessor {
void processHtmlResource(Resource resource, OutputStream out);

@ -1,5 +0,0 @@
package me.ag2s.epublib.epub;
public class Main {
}

@ -1,296 +0,0 @@
package me.ag2s.epublib.epub;
import android.util.Log;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
//import javax.xml.stream.FactoryConfigurationError;
/**
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
*
* @author paul
*/
public class NCXDocument {
public static final String NAMESPACE_NCX = "http://www.daisy.org/z3986/2005/ncx/";
public static final String PREFIX_NCX = "ncx";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.ncx";
public static final String PREFIX_DTB = "dtb";
private static String TAG = NCXDocument.class.getName();
private interface NCXTags {
String ncx = "ncx";
String meta = "meta";
String navPoint = "navPoint";
String navMap = "navMap";
String navLabel = "navLabel";
String content = "content";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
private interface NCXAttributes {
String src = "src";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2005-1";
}
public static Resource read(EpubBook book, EpubReader epubReader) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.read(book, epubReader);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.read(book, epubReader);
} else {
return NCXDocumentV2.read(book, epubReader);
}
}
private static List<TOCReference> readTOCReferences(NodeList navpoints,
EpubBook book) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.readTOCReferences(navpoints, book);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.readTOCReferences(navpoints, book);
} else {
return NCXDocumentV2.readTOCReferences(navpoints, book);
}
}
static TOCReference readTOCReference(Element navpointElement, EpubBook book) {
Log.d(TAG, book.getVersion());
String version = book.getVersion();
if (version.startsWith("2.")) {
return NCXDocumentV2.readTOCReference(navpointElement, book);
} else if (version.startsWith("3.")) {
return NCXDocumentV3.readTOCReference(navpointElement, book);
} else {
return NCXDocumentV2.readTOCReference(navpointElement, book);
}
}
private static String readNavReference(Element navpointElement) {
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.content);
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.getMessage());
}
return result;
}
private static String readNavLabel(Element navpointElement) {
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.navLabel);
return DOMUtil.getTextChildrenContent(DOMUtil
.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXTags.text));
}
public static void write(EpubWriter epubWriter, EpubBook book,
ZipOutputStream resultStream) throws IOException {
resultStream
.putNextEntry(new ZipEntry(book.getSpine().getTocResource().getHref()));
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(resultStream);
write(out, book);
out.flush();
}
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* @1throws FactoryConfigurationError
*/
public static void write(XmlSerializer xmlSerializer, EpubBook book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
public static Resource createNCXResource(EpubBook book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
Resource resource = new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, MediaTypes.NCX);
return resource;
}
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_NCX);
serializer.startTag(NAMESPACE_NCX, NCXTags.ncx);
// serializer.writeNamespace("ncx", NAMESPACE_NCX);
// serializer.attribute("xmlns", NAMESPACE_NCX);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.version,
NCXAttributeValues.version);
serializer.startTag(NAMESPACE_NCX, NCXTags.head);
for (Identifier identifier : identifiers) {
writeMetaElement(identifier.getScheme(), identifier.getValue(),
serializer);
}
writeMetaElement("generator", Constants.EPUB4J_GENERATOR_NAME, serializer);
writeMetaElement("depth", String.valueOf(tableOfContents.calculateDepth()),
serializer);
writeMetaElement("totalPageCount", "0", serializer);
writeMetaElement("maxPageNumber", "0", serializer);
serializer.endTag(NAMESPACE_NCX, "head");
serializer.startTag(NAMESPACE_NCX, NCXTags.docTitle);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
// write the first title
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docTitle);
for (Author author : authors) {
serializer.startTag(NAMESPACE_NCX, NCXTags.docAuthor);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(author.getLastname() + ", " + author.getFirstname());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docAuthor);
}
serializer.startTag(NAMESPACE_NCX, NCXTags.navMap);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_NCX, NCXTags.navMap);
serializer.endTag(NAMESPACE_NCX, "ncx");
serializer.endDocument();
}
private static void writeMetaElement(String dtbName, String content,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.name,
PREFIX_DTB + ":" + dtbName);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.content,
content);
serializer.endTag(NAMESPACE_NCX, NCXTags.meta);
}
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, playOrder, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
}
return playOrder;
}
private static void writeNavPointStart(TOCReference tocReference,
int playOrder, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.navPoint);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.id,
"navPoint-" + playOrder);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.playOrder,
String.valueOf(playOrder));
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.clazz,
NCXAttributeValues.chapter);
serializer.startTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(tocReference.getTitle());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.content);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.src,
tocReference.getCompleteHref());
serializer.endTag(NAMESPACE_NCX, NCXTags.content);
}
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.endTag(NAMESPACE_NCX, NCXTags.navPoint);
}
}

@ -19,7 +19,7 @@ import java.util.zip.ZipOutputStream;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
@ -33,302 +33,310 @@ import me.ag2s.epublib.util.StringUtil;
*
* @author paul
*/
public class NCXDocumentV2 extends NCXDocument{
public static final String NAMESPACE_NCX = "http://www.daisy.org/z3986/2005/ncx/";
public static final String PREFIX_NCX = "ncx";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.ncx";
public static final String PREFIX_DTB = "dtb";
private static String TAG= NCXDocumentV2.class.getName();
private interface NCXTags {
String ncx = "ncx";
String meta = "meta";
String navPoint = "navPoint";
String navMap = "navMap";
String navLabel = "navLabel";
String content = "content";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
private interface NCXAttributes {
String src = "src";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2005-1";
}
public static Resource read(EpubBook book, EpubReader epubReader) {
Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) {
Log.e(TAG, "Book does not contain a table of contents file");
return ncxResource;
public class NCXDocumentV2 {
public static final String NAMESPACE_NCX = "http://www.daisy.org/z3986/2005/ncx/";
@SuppressWarnings("unused")
public static final String PREFIX_NCX = "ncx";
public static final String NCX_ITEM_ID = "ncx";
public static final String DEFAULT_NCX_HREF = "toc.ncx";
public static final String PREFIX_DTB = "dtb";
private static final String TAG = NCXDocumentV2.class.getName();
private interface NCXTags {
String ncx = "ncx";
String meta = "meta";
String navPoint = "navPoint";
String navMap = "navMap";
String navLabel = "navLabel";
String content = "content";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head";
}
try {
ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) {
private interface NCXAttributes {
String src = "src";
String name = "name";
String content = "content";
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
private interface NCXAttributeValues {
String chapter = "chapter";
String version = "2005-1";
}
@SuppressWarnings("unused")
public static Resource read(Book book, EpubReader epubReader) {
Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) {
Log.e(TAG, "Book does not contain a table of contents file");
return null;
}
try {
ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) {
return null;
}
Log.d(TAG, ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
Element navMapElement = DOMUtil
.getFirstElementByTagNameNS(ncxDocument.getDocumentElement(),
NAMESPACE_NCX, NCXTags.navMap);
if (navMapElement == null) {
return null;
}
TableOfContents tableOfContents = new TableOfContents(
readTOCReferences(navMapElement.getChildNodes(), book));
book.setTableOfContents(tableOfContents);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
return ncxResource;
}
Log.d(TAG,ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
Element navMapElement = DOMUtil
.getFirstElementByTagNameNS(ncxDocument.getDocumentElement(),
NAMESPACE_NCX, NCXTags.navMap);
TableOfContents tableOfContents = new TableOfContents(
readTOCReferences(navMapElement.getChildNodes(), book));
book.setTableOfContents(tableOfContents);
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e);
}
return ncxResource;
}
static List<TOCReference> readTOCReferences(NodeList navpoints,
EpubBook book) {
if (navpoints == null) {
return new ArrayList<>();
static List<TOCReference> readTOCReferences(NodeList navpoints,
Book book) {
if (navpoints == null) {
return new ArrayList<>();
}
List<TOCReference> result = new ArrayList<>(
navpoints.getLength());
for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i);
if (node.getNodeType() != Document.ELEMENT_NODE) {
continue;
}
if (!(node.getLocalName().equals(NCXTags.navPoint))) {
continue;
}
TOCReference tocReference = readTOCReference((Element) node, book);
result.add(tocReference);
}
return result;
}
static TOCReference readTOCReference(Element navpointElement, Book book) {
String label = readNavLabel(navpointElement);
//Log.d(TAG,"label:"+label);
String tocResourceRoot = StringUtil
.substringBeforeLast(book.getSpine().getTocResource().getHref(), '/');
if (tocResourceRoot.length() == book.getSpine().getTocResource().getHref()
.length()) {
tocResourceRoot = "";
} else {
tocResourceRoot = tocResourceRoot + "/";
}
String reference = StringUtil
.collapsePathDots(tocResourceRoot + readNavReference(navpointElement));
String href = StringUtil
.substringBefore(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
String fragmentId = StringUtil
.substringAfter(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource resource = book.getResources().getByHref(href);
if (resource == null) {
Log.e(TAG, "Resource with href " + href + " in NCX document not found");
}
Log.d(TAG, "label:" + label);
Log.d(TAG, "href:" + href);
Log.d(TAG, "fragmentId:" + fragmentId);
TOCReference result = new TOCReference(label, resource, fragmentId);
List<TOCReference> childTOCReferences = readTOCReferences(
navpointElement.getChildNodes(), book);
result.setChildren(childTOCReferences);
return result;
}
private static String readNavReference(Element navpointElement) {
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.content);
if (contentElement == null) {
return null;
}
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.getMessage());
}
return result;
}
List<TOCReference> result = new ArrayList<>(
navpoints.getLength());
for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i);
if (node.getNodeType() != Document.ELEMENT_NODE) {
continue;
}
if (!(node.getLocalName().equals(NCXTags.navPoint))) {
continue;
}
TOCReference tocReference = readTOCReference((Element) node, book);
result.add(tocReference);
private static String readNavLabel(Element navpointElement) {
//Log.d(TAG,navpointElement.getTagName());
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.navLabel);
assert navLabel != null;
return DOMUtil.getTextChildrenContent(DOMUtil
.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXTags.text));
}
@SuppressWarnings("unused")
public static void write(EpubWriter epubWriter, Book book,
ZipOutputStream resultStream) throws IOException {
resultStream
.putNextEntry(new ZipEntry(book.getSpine().getTocResource().getHref()));
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(resultStream);
write(out, book);
out.flush();
}
return result;
}
static TOCReference readTOCReference(Element navpointElement, EpubBook book) {
String label = readNavLabel(navpointElement);
//Log.d(TAG,"label:"+label);
String tocResourceRoot = StringUtil
.substringBeforeLast(book.getSpine().getTocResource().getHref(), '/');
if (tocResourceRoot.length() == book.getSpine().getTocResource().getHref()
.length()) {
tocResourceRoot = "";
} else {
tocResourceRoot = tocResourceRoot + "/";
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
* @throws IOException IOException
* @throws IllegalStateException IllegalStateException
* @throws IllegalArgumentException IllegalArgumentException
*/
public static void write(XmlSerializer xmlSerializer, Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
String reference = StringUtil
.collapsePathDots(tocResourceRoot + readNavReference(navpointElement));
String href = StringUtil
.substringBefore(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
String fragmentId = StringUtil
.substringAfter(reference, Constants.FRAGMENT_SEPARATOR_CHAR);
Resource resource = book.getResources().getByHref(href);
if (resource == null) {
Log.e(TAG,"Resource with href " + href + " in NCX document not found");
public static Resource createNCXResource(Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
TOCReference result = new TOCReference(label, resource, fragmentId);
List<TOCReference> childTOCReferences = readTOCReferences(
navpointElement.getChildNodes(), book);
result.setChildren(childTOCReferences);
return result;
}
private static String readNavReference(Element navpointElement) {
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.content);
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXAttributes.src);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
Log.e(TAG,e.getMessage());
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
return new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, MediaTypes.NCX);
}
return result;
}
private static String readNavLabel(Element navpointElement) {
//Log.d(TAG,navpointElement.getTagName());
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXTags.navLabel);
return DOMUtil.getTextChildrenContent(DOMUtil
.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXTags.text));
}
public static void write(EpubWriter epubWriter, EpubBook book,
ZipOutputStream resultStream) throws IOException {
resultStream
.putNextEntry(new ZipEntry(book.getSpine().getTocResource().getHref()));
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(resultStream);
write(out, book);
out.flush();
}
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
*
* @1throws FactoryConfigurationError
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public static void write(XmlSerializer xmlSerializer, EpubBook book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
public static Resource createNCXResource(EpubBook book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
Resource resource = new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, MediaTypes.NCX);
return resource;
}
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_NCX);
serializer.startTag(NAMESPACE_NCX, NCXTags.ncx);
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_NCX);
serializer.startTag(NAMESPACE_NCX, NCXTags.ncx);
// serializer.writeNamespace("ncx", NAMESPACE_NCX);
// serializer.attribute("xmlns", NAMESPACE_NCX);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.version,
NCXAttributeValues.version);
serializer.startTag(NAMESPACE_NCX, NCXTags.head);
for (Identifier identifier : identifiers) {
writeMetaElement(identifier.getScheme(), identifier.getValue(),
serializer);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.version,
NCXAttributeValues.version);
serializer.startTag(NAMESPACE_NCX, NCXTags.head);
for (Identifier identifier : identifiers) {
writeMetaElement(identifier.getScheme(), identifier.getValue(),
serializer);
}
writeMetaElement("generator", Constants.EPUB_GENERATOR_NAME, serializer);
writeMetaElement("depth", String.valueOf(tableOfContents.calculateDepth()),
serializer);
writeMetaElement("totalPageCount", "0", serializer);
writeMetaElement("maxPageNumber", "0", serializer);
serializer.endTag(NAMESPACE_NCX, "head");
serializer.startTag(NAMESPACE_NCX, NCXTags.docTitle);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
// write the first title
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docTitle);
for (Author author : authors) {
serializer.startTag(NAMESPACE_NCX, NCXTags.docAuthor);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(author.getLastname() + ", " + author.getFirstname());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docAuthor);
}
serializer.startTag(NAMESPACE_NCX, NCXTags.navMap);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_NCX, NCXTags.navMap);
serializer.endTag(NAMESPACE_NCX, "ncx");
serializer.endDocument();
}
private static void writeMetaElement(String dtbName, String content,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.name,
PREFIX_DTB + ":" + dtbName);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.content,
content);
serializer.endTag(NAMESPACE_NCX, NCXTags.meta);
}
writeMetaElement("generator", Constants.EPUB4J_GENERATOR_NAME, serializer);
writeMetaElement("depth", String.valueOf(tableOfContents.calculateDepth()),
serializer);
writeMetaElement("totalPageCount", "0", serializer);
writeMetaElement("maxPageNumber", "0", serializer);
serializer.endTag(NAMESPACE_NCX, "head");
serializer.startTag(NAMESPACE_NCX, NCXTags.docTitle);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
// write the first title
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docTitle);
for (Author author : authors) {
serializer.startTag(NAMESPACE_NCX, NCXTags.docAuthor);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(author.getLastname() + ", " + author.getFirstname());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.docAuthor);
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, playOrder, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
}
return playOrder;
}
serializer.startTag(NAMESPACE_NCX, NCXTags.navMap);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_NCX, NCXTags.navMap);
serializer.endTag(NAMESPACE_NCX, "ncx");
serializer.endDocument();
}
private static void writeMetaElement(String dtbName, String content,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.meta);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.name,
PREFIX_DTB + ":" + dtbName);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.content,
content);
serializer.endTag(NAMESPACE_NCX, NCXTags.meta);
}
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, playOrder, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
private static void writeNavPointStart(TOCReference tocReference,
int playOrder, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.navPoint);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.id,
"navPoint-" + playOrder);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.playOrder,
String.valueOf(playOrder));
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.clazz,
NCXAttributeValues.chapter);
serializer.startTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(tocReference.getTitle());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.content);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.src,
tocReference.getCompleteHref());
serializer.endTag(NAMESPACE_NCX, NCXTags.content);
}
@SuppressWarnings("unused")
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.endTag(NAMESPACE_NCX, NCXTags.navPoint);
}
return playOrder;
}
private static void writeNavPointStart(TOCReference tocReference,
int playOrder, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_NCX, NCXTags.navPoint);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.id,
"navPoint-" + playOrder);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.playOrder,
String.valueOf(playOrder));
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.clazz,
NCXAttributeValues.chapter);
serializer.startTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.text);
serializer.text(tocReference.getTitle());
serializer.endTag(NAMESPACE_NCX, NCXTags.text);
serializer.endTag(NAMESPACE_NCX, NCXTags.navLabel);
serializer.startTag(NAMESPACE_NCX, NCXTags.content);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, NCXAttributes.src,
tocReference.getCompleteHref());
serializer.endTag(NAMESPACE_NCX, NCXTags.content);
}
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.endTag(NAMESPACE_NCX, NCXTags.navPoint);
}
}

@ -6,79 +6,114 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
public class NCXDocumentV3 extends NCXDocument {
public static final String NAMESPACE_NCX = "";
public static final String PREFIX_NCX = "html";
public static final String NCX_ITEM_ID = "ncx";
/**
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
*
* @author Ag2S20150909
*/
public class NCXDocumentV3 {
public static final String NAMESPACE_XHTML = "http://www.w3.org/1999/xhtml";
public static final String NAMESPACE_EPUB = "http://www.idpf.org/2007/ops";
public static final String LANGUAGE = "en";
@SuppressWarnings("unused")
public static final String PREFIX_XHTML = "html";
public static final String NCX_ITEM_ID = "htmltoc";
public static final String DEFAULT_NCX_HREF = "toc.xhtml";
public static final String PREFIX_DTB = "dtb";
private static String TAG = NCXDocumentV3.class.getName();
public static final String V3_NCX_PROPERTIES = "nav";
public static final MediaType V3_NCX_MEDIATYPE = MediaTypes.XHTML;
private interface NCXTags {
//String nav="nav";
//String li="li";
//String ncx = "ncx";
String meta = "meta";
String navPoint = "li";
String navMap = "nav";
String navLabel = "a";
String content = "a";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
private static final String TAG = NCXDocumentV3.class.getName();
private interface XHTMLTgs {
String html = "html";
String head = "head";
String title = "title";
String meta = "meta";
String link = "link";
String body = "body";
String h1 = "h1";
String h2 = "h2";
String nav = "nav";
String ol = "ol";
String li = "li";
String a = "a";
String span = "span";
}
private interface NCXAttributes {
String src = "href";
String name = "name";
String content = "content";
private interface XHTMLAttributes {
String xmlns = "xmlns";
String xmlns_epub = "xmlns:epub";
String lang = "lang";
String xml_lang = "xml:lang";
String rel = "rel";
String type = "type";
String epub_type = "epub:type";//nav的必须属性
String id = "id";
String playOrder = "playOrder";
String clazz = "class";
String version = "version";
}
String role = "role";
String href = "href";
String http_equiv = "http-equiv";
String content = "content";
private interface NCXAttributeValues {
}
String chapter = "chapter";
String version = "2007";
private interface XHTMLAttributeValues {
String Content_Type = "Content-Type";
String HTML_UTF8 = "text/html; charset=utf-8";
String lang = "en";
String epub_type = "toc";
String role_toc = "doc-toc";
}
public static Resource read(EpubBook book, EpubReader epubReader) {
/**
* 解析epub的目录文件
*
* @param book Book
* @param epubReader epubreader
* @return Resource
*/
@SuppressWarnings("unused")
public static Resource read(Book book, EpubReader epubReader) {
Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) {
Log.e(TAG, "Book does not contain a table of contents file");
return ncxResource;
return null;
}
try {
ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) {
return ncxResource;
return null;
}
//Log.d(TAG, ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
//Log.d(TAG, ncxDocument.getNodeName());
Element navMapElement = (Element) ncxDocument.getElementsByTagName("nav").item(0);
navMapElement = (Element) navMapElement.getElementsByTagName("ol").item(0);
Element navMapElement = (Element) ncxDocument.getElementsByTagName(XHTMLTgs.nav).item(0);
navMapElement = (Element) navMapElement.getElementsByTagName(XHTMLTgs.ol).item(0);
Log.d(TAG, navMapElement.getTagName());
TableOfContents tableOfContents = new TableOfContents(
@ -91,46 +126,42 @@ public class NCXDocumentV3 extends NCXDocument {
return ncxResource;
}
public static List<TOCReference> doToc(Node n, EpubBook book) {
private static List<TOCReference> doToc(Node n, Book book) {
List<TOCReference> result = new ArrayList<>();
if (n == null || n.getNodeType() != Document.ELEMENT_NODE) {
return result;
} else {
Element el = (Element) n;
NodeList nodeList = el.getElementsByTagName("li");
NodeList nodeList = el.getElementsByTagName(XHTMLTgs.li);
for (int i = 0; i < nodeList.getLength(); i++) {
result.add(readTOCReference((Element) nodeList.item(i), book));
}
}
return result;
}
static List<TOCReference> readTOCReferences(NodeList navpoints,
EpubBook book) {
Book book) {
if (navpoints == null) {
return new ArrayList<>();
}
//Log.d(TAG, "readTOCReferences:navpoints.getLength()" + navpoints.getLength());
List<TOCReference> result = new ArrayList<>();
List<TOCReference> result = new ArrayList<>(navpoints.getLength());
for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i);
//如果该node是null,或者不是Element,跳出本次循环
if (node == null || node.getNodeType() != Document.ELEMENT_NODE) {
continue;
} else {
Element el = (Element) node;
if (el.getTagName().equals("li")) {
result.add(readTOCReference(el, book));
}
//NodeList nodeList=el.getElementsByTagName("li");
//for (int i=0;i<nodeList.getLength();i++){
//result.add(readTOCReference((Element) nodeList.item(i),book));
// }
}
//result.addAll(doToc(node, book));
Element el = (Element) node;
//如果该Element的name为”li“,将其添加到目录结果
if (el.getTagName().equals(XHTMLTgs.li)) {
result.add(readTOCReference(el, book));
}
}
@ -138,7 +169,8 @@ public class NCXDocumentV3 extends NCXDocument {
}
static TOCReference readTOCReference(Element navpointElement, EpubBook book) {
static TOCReference readTOCReference(Element navpointElement, Book book) {
//章节的名称
String label = readNavLabel(navpointElement);
//Log.d(TAG, "label:" + label);
String tocResourceRoot = StringUtil
@ -160,7 +192,13 @@ public class NCXDocumentV3 extends NCXDocument {
if (resource == null) {
Log.e(TAG, "Resource with href " + href + " in NCX document not found");
}
Log.v(TAG, "label:" + label);
Log.v(TAG, "href:" + href);
Log.v(TAG, "fragmentId:" + fragmentId);
//父级目录
TOCReference result = new TOCReference(label, resource, fragmentId);
//解析子级目录
List<TOCReference> childTOCReferences = doToc(navpointElement, book);
//readTOCReferences(
//navpointElement.getChildNodes(), book);
@ -168,13 +206,24 @@ public class NCXDocumentV3 extends NCXDocument {
return result;
}
/**
* 获取目录节点的href
*
* @param navpointElement navpointElement
* @return String
*/
private static String readNavReference(Element navpointElement) {
//https://www.w3.org/publishing/epub/epub-packages.html#sec-package-nav
//父级节点必须是 "li"
//Log.d(TAG, "readNavReference:" + navpointElement.getTagName());
Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXDocumentV3.NCXTags.content);
.getFirstElementByTagNameNS(navpointElement, "", XHTMLTgs.a);
if (contentElement == null) {
return null;
}
String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXDocumentV3.NCXAttributes.src);
.getAttribute(contentElement, "", XHTMLAttributes.href);
try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
@ -185,13 +234,218 @@ public class NCXDocumentV3 extends NCXDocument {
}
/**
* 获取目录节点里面的章节名
*
* @param navpointElement navpointElement
* @return String
*/
private static String readNavLabel(Element navpointElement) {
//https://www.w3.org/publishing/epub/epub-packages.html#sec-package-nav
//父级节点必须是 "li"
//Log.d(TAG, "readNavLabel:" + navpointElement.getTagName());
Element navLabel = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX,
NCXDocumentV3.NCXTags.navLabel);
return navLabel.getTextContent();//DOMUtil.getTextChildrenContent(DOMUtil
//.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXDocumentV3.NCXTags.text));
String label;
Element labelElement = DOMUtil.getFirstElementByTagNameNS(navpointElement, "", "a");
assert labelElement != null;
label = labelElement.getTextContent();
if (StringUtil.isNotBlank(label)) {
return label;
} else {
labelElement = DOMUtil.getFirstElementByTagNameNS(navpointElement, "", "span");
}
assert labelElement != null;
label = labelElement.getTextContent();
//如果通过 a 标签无法获取章节列表,则是无href章节名
return label;
}
public static Resource createNCXResource(Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
return createNCXResource(book.getMetadata().getIdentifiers(),
book.getTitle(), book.getMetadata().getAuthors(),
book.getTableOfContents());
}
public static Resource createNCXResource(List<Identifier> identifiers,
String title, List<Author> authors, TableOfContents tableOfContents)
throws IllegalArgumentException, IllegalStateException, IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
XmlSerializer out = EpubProcessorSupport.createXmlSerializer(data);
write(out, identifiers, title, authors, tableOfContents);
Resource resource = new Resource(NCX_ITEM_ID, data.toByteArray(),
DEFAULT_NCX_HREF, V3_NCX_MEDIATYPE);
resource.setProperties(V3_NCX_PROPERTIES);
return resource;
}
/**
* Generates a resource containing an xml document containing the table of contents of the book in ncx format.
*
* @param xmlSerializer the serializer used
* @param book the book to serialize
* @throws IOException IOException
* @throws IllegalStateException IllegalStateException
* @throws IllegalArgumentException IllegalArgumentException
*/
public static void write(XmlSerializer xmlSerializer, Book book)
throws IllegalArgumentException, IllegalStateException, IOException {
write(xmlSerializer, book.getMetadata().getIdentifiers(), book.getTitle(),
book.getMetadata().getAuthors(), book.getTableOfContents());
}
/**
* 写入
*
* @param serializer serializer
* @param identifiers identifiers
* @param title title
* @param authors authors
* @param tableOfContents tableOfContents
*/
@SuppressWarnings("unused")
public static void write(XmlSerializer serializer,
List<Identifier> identifiers, String title, List<Author> authors,
TableOfContents tableOfContents) throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(EpubWriter.EMPTY_NAMESPACE_PREFIX, NAMESPACE_XHTML);
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.html);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, XHTMLAttributes.xmlns_epub, NAMESPACE_EPUB);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, XHTMLAttributes.xml_lang, XHTMLAttributeValues.lang);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, XHTMLAttributes.lang, LANGUAGE);
//写入头部head标签
writeHead(title, serializer);
//body开始
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.body);
//h1开始
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.h1);
serializer.text(title);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.h1);
//h1关闭
//nav开始
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.nav);
serializer.attribute("", XHTMLAttributes.epub_type, XHTMLAttributeValues.epub_type);
serializer.attribute("", XHTMLAttributes.id, XHTMLAttributeValues.epub_type);
serializer.attribute("", XHTMLAttributes.role, XHTMLAttributeValues.role_toc);
//h2开始
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.h2);
serializer.text("目录");
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.h2);
writeNavPoints(tableOfContents.getTocReferences(), 1, serializer);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.nav);
//body关闭
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.body);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.html);
serializer.endDocument();
}
private static int writeNavPoints(List<TOCReference> tocReferences,
int playOrder,
XmlSerializer serializer) throws IOException {
writeOlStart(serializer);
for (TOCReference tocReference : tocReferences) {
if (tocReference.getResource() == null) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
continue;
}
writeNavPointStart(tocReference, serializer);
playOrder++;
if (!tocReference.getChildren().isEmpty()) {
playOrder = writeNavPoints(tocReference.getChildren(), playOrder,
serializer);
}
writeNavPointEnd(tocReference, serializer);
}
writeOlSEnd(serializer);
return playOrder;
}
private static void writeNavPointStart(TOCReference tocReference, XmlSerializer serializer) throws IOException {
writeLiStart(serializer);
String title = tocReference.getTitle();
String href = tocReference.getCompleteHref();
if (StringUtil.isNotBlank(href)) {
writeLabel(title, href, serializer);
} else {
writeLabel(title, serializer);
}
}
@SuppressWarnings("unused")
private static void writeNavPointEnd(TOCReference tocReference,
XmlSerializer serializer) throws IOException {
writeLiEnd(serializer);
}
protected static void writeLabel(String title, String href, XmlSerializer serializer) throws IOException {
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.a);
serializer.attribute("", XHTMLAttributes.href, href);
//attribute必须在Text之前设置。
serializer.text(title);
//serializer.attribute(NAMESPACE_XHTML, XHTMLAttributes.href, href);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.a);
}
protected static void writeLabel(String title, XmlSerializer serializer) throws IOException {
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.span);
serializer.text(title);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.span);
}
private static void writeLiStart(XmlSerializer serializer) throws IOException {
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.li);
Log.d(TAG, "writeLiStart");
}
private static void writeLiEnd(XmlSerializer serializer) throws IOException {
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.li);
Log.d(TAG, "writeLiEND");
}
private static void writeOlStart(XmlSerializer serializer) throws IOException {
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.ol);
Log.d(TAG, "writeOlStart");
}
private static void writeOlSEnd(XmlSerializer serializer) throws IOException {
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.ol);
Log.d(TAG, "writeOlEnd");
}
private static void writeHead(String title, XmlSerializer serializer) throws IOException {
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.head);
//title
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.title);
serializer.text(StringUtil.defaultIfNull(title));
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.title);
//link
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.link);
serializer.attribute("", XHTMLAttributes.rel, "stylesheet");
serializer.attribute("", XHTMLAttributes.type, "text/css");
serializer.attribute("", XHTMLAttributes.href, "css/style.css");
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.link);
//meta
serializer.startTag(NAMESPACE_XHTML, XHTMLTgs.meta);
serializer.attribute("", XHTMLAttributes.http_equiv, XHTMLAttributeValues.Content_Type);
serializer.attribute("", XHTMLAttributes.content, XHTMLAttributeValues.HTML_UTF8);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.meta);
serializer.endTag(NAMESPACE_XHTML, XHTMLTgs.head);
}

@ -14,8 +14,11 @@ public class PackageDocumentBase {
public static final String NAMESPACE_DUBLIN_CORE = "http://purl.org/dc/elements/1.1/";
public static final String PREFIX_DUBLIN_CORE = "dc";
//public static final String PREFIX_OPF = "opf";
//Some Epub Reader not reconize opt:packge,So just let it empty;
//在EPUB3标准中,packge前面没有opf头,一些epub阅读器也不支持opf头。
//Some Epub Reader not reconize op:packge,So just let it empty;
public static final String PREFIX_OPF = "";
//添加 version 变量来区分Epub文件的版本
//Add the version field to distinguish the version of EPUB file
public static final String version="version";
public static final String dateFormat = "yyyy-MM-dd";
@ -76,6 +79,10 @@ public class PackageDocumentBase {
String version = "version";
String scheme = "scheme";
String property = "property";
//add for epub3
/**
* add for epub3
*/
String properties="properties";
}

@ -2,21 +2,23 @@ package me.ag2s.epublib.epub;
import android.util.Log;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Date;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.Metadata;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Date;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.domain.Metadata;
import me.ag2s.epublib.util.StringUtil;
/**
* Reads the package document metadata.
@ -28,7 +30,7 @@ import org.w3c.dom.NodeList;
// package
class PackageDocumentMetadataReader extends PackageDocumentBase {
private static String TAG= PackageDocumentMetadataReader.class.getName();
private static final String TAG= PackageDocumentMetadataReader.class.getName();
public static Metadata readMetadata(Document packageDocument) {
Metadata result = new Metadata();
@ -76,12 +78,12 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
/**
* consumes meta tags that have a property attribute as defined in the standard. For example:
* &lt;meta property="rendition:layout"&gt;pre-paginated&lt;/meta&gt;
* @param metadataElement
* @return
* @param metadataElement metadataElement
* @return Map<QName, String>
*/
private static Map<QName, String> readOtherProperties(
Element metadataElement) {
Map<QName, String> result = new HashMap<QName, String>();
Map<QName, String> result = new HashMap<>();
NodeList metaTags = metadataElement.getElementsByTagName(OPFTags.meta);
for (int i = 0; i < metaTags.getLength(); i++) {
@ -101,12 +103,12 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
/**
* consumes meta tags that have a property attribute as defined in the standard. For example:
* &lt;meta property="rendition:layout"&gt;pre-paginated&lt;/meta&gt;
* @param metadataElement
* @return
* @param metadataElement metadataElement
* @return Map<String, String>
*/
private static Map<String, String> readMetaProperties(
Element metadataElement) {
Map<String, String> result = new HashMap<String, String>();
Map<String, String> result = new HashMap<>();
NodeList metaTags = metadataElement.getElementsByTagName(OPFTags.meta);
for (int i = 0; i < metaTags.getLength(); i++) {
@ -126,9 +128,8 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
if (packageElement == null) {
return null;
}
String result = packageElement
return packageElement
.getAttributeNS(NAMESPACE_OPF, OPFAttributes.uniqueIdentifier);
return result;
}
private static List<Author> readCreators(Element metadataElement) {
@ -143,7 +144,7 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
Element metadataElement) {
NodeList elements = metadataElement
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, authorTag);
List<Author> result = new ArrayList<Author>(elements.getLength());
List<Author> result = new ArrayList<>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
Element authorElement = (Element) elements.item(i);
Author author = createAuthor(authorElement);
@ -158,7 +159,7 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
private static List<Date> readDates(Element metadataElement) {
NodeList elements = metadataElement
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, DCTags.date);
List<Date> result = new ArrayList<Date>(elements.getLength());
List<Date> result = new ArrayList<>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) {
Element dateElement = (Element) elements.item(i);
Date date;
@ -198,11 +199,11 @@ class PackageDocumentMetadataReader extends PackageDocumentBase {
.getElementsByTagNameNS(NAMESPACE_DUBLIN_CORE, DCTags.identifier);
if (identifierElements.getLength() == 0) {
Log.e(TAG,"Package does not contain element " + DCTags.identifier);
return new ArrayList<Identifier>();
return new ArrayList<>();
}
String bookIdId = getBookIdId(metadataElement.getOwnerDocument());
List<Identifier> result = new ArrayList<Identifier>(
identifierElements.getLength());
List<Identifier> result = new ArrayList<>(
identifierElements.getLength());
for (int i = 0; i < identifierElements.getLength(); i++) {
Element identifierElement = (Element) identifierElements.item(i);
String schemeName = identifierElement

@ -1,42 +1,39 @@
package me.ag2s.epublib.epub;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Author;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Date;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Identifier;
import me.ag2s.epublib.util.StringUtil;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.xmlpull.v1.XmlSerializer;
public class PackageDocumentMetadataWriter extends PackageDocumentBase {
/**
* Writes the book's metadata.
*
* @param book
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* @param book book
* @param serializer serializer
* @throws IOException IOException
* @throws IllegalStateException IllegalStateException
* @throws IllegalArgumentException IllegalArgumentException
*/
public static void writeMetaData(EpubBook book, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
public static void writeMetaData(Book book, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.metadata);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
writeIdentifiers(book.getMetadata().getIdentifiers(), serializer);
writeSimpleMetdataElements(DCTags.title, book.getMetadata().getTitles(),
serializer);
serializer);
writeSimpleMetdataElements(DCTags.subject, book.getMetadata().getSubjects(),
serializer);
serializer);
writeSimpleMetdataElements(DCTags.description,
book.getMetadata().getDescriptions(), serializer);
writeSimpleMetdataElements(DCTags.publisher,
@ -117,7 +114,7 @@ public class PackageDocumentMetadataWriter extends PackageDocumentBase {
OPFValues.generator);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content,
Constants.EPUB4J_GENERATOR_NAME);
Constants.EPUB_GENERATOR_NAME);
serializer.endTag(NAMESPACE_OPF, OPFTags.meta);
serializer.endTag(NAMESPACE_OPF, OPFTags.metadata);
@ -142,11 +139,10 @@ public class PackageDocumentMetadataWriter extends PackageDocumentBase {
* The first identifier for which the bookId is true is made the bookId identifier.
* If no identifier has bookId == true then the first bookId identifier is written as the primary.
*
* @param identifiers
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* @param identifiers identifiers
* @param serializer serializer
* @throws IllegalStateException e
* @throws IllegalArgumentException e
* @
*/
private static void writeIdentifiers(List<Identifier> identifiers,

@ -19,10 +19,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Guide;
import me.ag2s.epublib.domain.GuideReference;
import me.ag2s.epublib.domain.MediaType;
@ -34,8 +32,6 @@ import me.ag2s.epublib.domain.SpineReference;
import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
/**
* Reads the opf package document as defined by namespace http://www.idpf.org/2007/opf
*
@ -44,22 +40,22 @@ import me.ag2s.epublib.util.StringUtil;
*/
public class PackageDocumentReader extends PackageDocumentBase {
private static String TAG= PackageDocumentReader.class.getName();
private static final String TAG= PackageDocumentReader.class.getName();
private static final String[] POSSIBLE_NCX_ITEM_IDS = new String[]{"toc",
"ncx", "ncxtoc","htmltoc"};
public static void read(
Resource packageResource, EpubReader epubReader, EpubBook book,
Resources resources)
throws UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException {
Resource packageResource, EpubReader epubReader, Book book,
Resources resources)
throws SAXException, IOException {
Document packageDocument = ResourceUtil.getAsDocument(packageResource);
String packageHref = packageResource.getHref();
resources = fixHrefs(packageHref, resources);
readGuide(packageDocument, epubReader, book, resources);
// Books sometimes use non-identifier ids. We map these here to legal ones
Map<String, String> idMapping = new HashMap<String, String>();
Map<String, String> idMapping = new HashMap<>();
String version=DOMUtil.getAttribute(packageDocument.getDocumentElement(),PREFIX_OPF,PackageDocumentBase.version);
resources = readManifest(packageDocument, packageHref, epubReader,
@ -90,13 +86,14 @@ public class PackageDocumentReader extends PackageDocumentBase {
/**
* Reads the manifest containing the resource ids, hrefs and mediatypes.
*
* @param packageDocument
* @param packageHref
* @param epubReader
* @param resources
* @param idMapping
* @param packageDocument e
* @param packageHref e
* @param epubReader e
* @param resources e
* @param idMapping e
* @return a Map with resources, with their id's as key.
*/
@SuppressWarnings("unused")
private static Resources readManifest(Document packageDocument,
String packageHref,
EpubReader epubReader, Resources resources,
@ -118,7 +115,7 @@ public class PackageDocumentReader extends PackageDocumentBase {
.getAttribute(itemElement, NAMESPACE_OPF, OPFAttributes.id);
String href = DOMUtil
.getAttribute(itemElement, NAMESPACE_OPF, OPFAttributes.href);
String properties=DOMUtil.getAttribute(itemElement,NAMESPACE_OPF,OPFAttributes.properties);
try {
href = URLDecoder.decode(href, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) {
@ -132,7 +129,10 @@ public class PackageDocumentReader extends PackageDocumentBase {
continue;
}
resource.setId(id);
//for epub3
String properties=DOMUtil.getAttribute(itemElement,NAMESPACE_OPF,OPFAttributes.properties);
resource.setProperties(properties);
MediaType mediaType = MediaTypes.getMediaTypeByName(mediaTypeName);
if (mediaType != null) {
resource.setMediaType(mediaType);
@ -148,22 +148,23 @@ public class PackageDocumentReader extends PackageDocumentBase {
* Reads the book's guide.
* Here some more attempts are made at finding the cover page.
*
* @param packageDocument
* @param epubReader
* @param book
* @param resources
* @param packageDocument r
* @param epubReader r
* @param book r
* @param resources g
*/
@SuppressWarnings("unused")
private static void readGuide(Document packageDocument,
EpubReader epubReader, EpubBook book, Resources resources) {
EpubReader epubReader, Book book, Resources resources) {
Element guideElement = DOMUtil
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.guide);
.getFirstElementByTagNameNS(packageDocument.getDocumentElement(),
NAMESPACE_OPF, OPFTags.guide);
if (guideElement == null) {
return;
}
Guide guide = book.getGuide();
NodeList guideReferences = guideElement
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.reference);
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.reference);
for (int i = 0; i < guideReferences.getLength(); i++) {
Element referenceElement = (Element) guideReferences.item(i);
String resourceHref = DOMUtil
@ -204,8 +205,8 @@ public class PackageDocumentReader extends PackageDocumentBase {
* Example:
* If the packageHref is "OEBPS/content.opf" then a resource href like "OEBPS/foo/bar.html" will be turned into "foo/bar.html"
*
* @param packageHref
* @param resourcesByHref
* @param packageHref f
* @param resourcesByHref g
* @return The stripped package href
*/
static Resources fixHrefs(String packageHref,
@ -228,9 +229,9 @@ public class PackageDocumentReader extends PackageDocumentBase {
/**
* Reads the document's spine, containing all sections in reading order.
*
* @param packageDocument
* @param resources
* @param idMapping
* @param packageDocument b
* @param resources b
* @param idMapping b
* @return the document's spine, containing all sections in reading order.
*/
private static Spine readSpine(Document packageDocument, Resources resources,
@ -251,8 +252,8 @@ public class PackageDocumentReader extends PackageDocumentBase {
.setTocResource(findTableOfContentsResource(tocResourceId, resources));
NodeList spineNodes = packageDocument
.getElementsByTagNameNS(NAMESPACE_OPF, OPFTags.itemref);
List<SpineReference> spineReferences = new ArrayList<SpineReference>(
spineNodes.getLength());
List<SpineReference> spineReferences = new ArrayList<>(
spineNodes.getLength());
for (int i = 0; i < spineNodes.getLength(); i++) {
Element spineItem = (Element) spineNodes.item(i);
String itemref = DOMUtil
@ -267,7 +268,7 @@ public class PackageDocumentReader extends PackageDocumentBase {
}
Resource resource = resources.getByIdOrHref(id);
if (resource == null) {
Log.e(TAG,"resource with id \'" + id + "\' not found");
Log.e(TAG, "resource with id '" + id + "' not found");
continue;
}
@ -286,13 +287,12 @@ public class PackageDocumentReader extends PackageDocumentBase {
* Creates a spine out of all resources in the resources.
* The generated spine consists of all XHTML pages in order of their href.
*
* @param resources
* @param resources f
* @return a spine created out of all resources in the resources.
*/
private static Spine generateSpineFromResources(Resources resources) {
Spine result = new Spine();
List<String> resourceHrefs = new ArrayList<String>();
resourceHrefs.addAll(resources.getAllHrefs());
List<String> resourceHrefs = new ArrayList<>(resources.getAllHrefs());
Collections.sort(resourceHrefs, String.CASE_INSENSITIVE_ORDER);
for (String resourceHref : resourceHrefs) {
Resource resource = resources.getByHref(resourceHref);
@ -312,8 +312,8 @@ public class PackageDocumentReader extends PackageDocumentBase {
* Here we try several ways of finding this table of contents resource.
* We try the given attribute value, some often-used ones and finally look through all resources for the first resource with the table of contents mimetype.
*
* @param tocResourceId
* @param resources
* @param tocResourceId g
* @param resources g
* @return the Resource containing the table of contents
*/
static Resource findTableOfContentsResource(String tocResourceId,
@ -331,13 +331,13 @@ public class PackageDocumentReader extends PackageDocumentBase {
tocResource = resources.findFirstResourceByMediaType(MediaTypes.NCX);
if (tocResource == null) {
for (int i = 0; i < POSSIBLE_NCX_ITEM_IDS.length; i++) {
tocResource = resources.getByIdOrHref(POSSIBLE_NCX_ITEM_IDS[i]);
for (String possibleNcxItemId : POSSIBLE_NCX_ITEM_IDS) {
tocResource = resources.getByIdOrHref(possibleNcxItemId);
if (tocResource != null) {
break;
}
tocResource = resources
.getByIdOrHref(POSSIBLE_NCX_ITEM_IDS[i].toUpperCase());
.getByIdOrHref(possibleNcxItemId.toUpperCase());
if (tocResource != null) {
break;
}
@ -363,13 +363,13 @@ public class PackageDocumentReader extends PackageDocumentBase {
* Find all resources that have something to do with the coverpage and the cover image.
* Search the meta tags and the guide references
*
* @param packageDocument
* @param packageDocument s
* @return all resources that have something to do with the coverpage and the cover image.
*/
// package
static Set<String> findCoverHrefs(Document packageDocument) {
Set<String> result = new HashSet<String>();
Set<String> result = new HashSet<>();
// try and find a meta tag with name = 'cover' and a non-blank id
String coverResourceId = DOMUtil
@ -403,18 +403,16 @@ public class PackageDocumentReader extends PackageDocumentBase {
/**
* Finds the cover resource in the packageDocument and adds it to the book if found.
* Keeps the cover resource in the resources map
*
* @param packageDocument
* @param book
* @1param resources
* @param packageDocument s
* @param book x
*/
private static void readCover(Document packageDocument, EpubBook book) {
private static void readCover(Document packageDocument, Book book) {
Collection<String> coverHrefs = findCoverHrefs(packageDocument);
for (String coverHref : coverHrefs) {
Resource resource = book.getResources().getByHref(coverHref);
if (resource == null) {
Log.e(TAG, "Cover resource " + coverHref + " not found");
Log.e(TAG,"Cover resource " + coverHref + " not found");
continue;
}
if (resource.getMediaType() == MediaTypes.XHTML) {

@ -2,16 +2,8 @@ package me.ag2s.epublib.epub;
import android.util.Log;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Guide;
import me.ag2s.epublib.domain.GuideReference;
import me.ag2s.epublib.domain.MediaTypes;
@ -20,237 +12,242 @@ import me.ag2s.epublib.domain.Spine;
import me.ag2s.epublib.domain.SpineReference;
import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
//import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.xmlpull.v1.XmlSerializer;
/**
* Writes the opf package document as defined by namespace http://www.idpf.org/2007/opf
*
* @author paul
*
*/
public class PackageDocumentWriter extends PackageDocumentBase {
//private static final Logger log = Logger.create(PackageDocumentWriter.class);
private static String TAG= PackageDocumentWriter.class.getName();
public static void write(EpubWriter epubWriter, XmlSerializer serializer,
EpubBook book) throws IOException {
try {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version,
"2.0");
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX,
OPFAttributes.uniqueIdentifier, BOOK_ID_ID);
PackageDocumentMetadataWriter.writeMetaData(book, serializer);
writeManifest(book, epubWriter, serializer);
writeSpine(book, epubWriter, serializer);
writeGuide(book, epubWriter, serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer.endDocument();
serializer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
private static final String TAG = PackageDocumentWriter.class.getName();
public static void write(EpubWriter epubWriter, XmlSerializer serializer,
Book book) {
try {
serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version,
book.getVersion());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX,
OPFAttributes.uniqueIdentifier, BOOK_ID_ID);
PackageDocumentMetadataWriter.writeMetaData(book, serializer);
writeManifest(book, epubWriter, serializer);
writeSpine(book, epubWriter, serializer);
writeGuide(book, epubWriter, serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer.endDocument();
serializer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Writes the package's spine.
*
* @param book
* @param epubWriter
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
* 1@throws XMLStreamException
*/
private static void writeSpine(EpubBook book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.spine);
Resource tocResource = book.getSpine().getTocResource();
String tocResourceId = tocResource.getId();
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.toc,
tocResourceId);
if (book.getCoverPage() != null // there is a cover page
&& book.getSpine().findFirstResourceById(book.getCoverPage().getId())
< 0) { // cover page is not already in the spine
// write the cover html file
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
book.getCoverPage().getId());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
"no");
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
/**
* Writes the package's spine.
*
* @param book e
* @param epubWriter g
* @param serializer g
* @throws IOException g
* @throws IllegalStateException g
* @throws IllegalArgumentException 1@throws XMLStreamException
*/
@SuppressWarnings("unused")
private static void writeSpine(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.spine);
Resource tocResource = book.getSpine().getTocResource();
String tocResourceId = tocResource.getId();
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.toc,
tocResourceId);
if (book.getCoverPage() != null // there is a cover page
&& book.getSpine().findFirstResourceById(book.getCoverPage().getId())
< 0) { // cover page is not already in the spine
// write the cover html file
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
book.getCoverPage().getId());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
"no");
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
}
writeSpineItems(book.getSpine(), serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.spine);
}
writeSpineItems(book.getSpine(), serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.spine);
}
private static void writeManifest(EpubBook book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.manifest);
private static void writeManifest(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.manifest);
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
epubWriter.getNcxId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
epubWriter.getNcxHref());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
epubWriter.getNcxMediaType());
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
// writeCoverResources(book, serializer);
//For EPUB3
if (book.isEpub3()) {
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.properties,NCXDocumentV3.V3_NCX_PROPERTIES);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id, NCXDocumentV3.NCX_ITEM_ID);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, NCXDocumentV3.DEFAULT_NCX_HREF);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type, NCXDocumentV3.V3_NCX_MEDIATYPE.getName());
} else {
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
epubWriter.getNcxId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, epubWriter.getNcxHref());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type, epubWriter.getNcxMediaType());
}
for (Resource resource : getAllResourcesSortById(book)) {
writeItem(book, resource, serializer);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
serializer.endTag(NAMESPACE_OPF, OPFTags.manifest);
}
// writeCoverResources(book, serializer);
private static List<Resource> getAllResourcesSortById(EpubBook book) {
List<Resource> allResources = new ArrayList<Resource>(
book.getResources().getAll());
Collections.sort(allResources, new Comparator<Resource>() {
for (Resource resource : getAllResourcesSortById(book)) {
writeItem(book, resource, serializer);
}
@Override
public int compare(Resource resource1, Resource resource2) {
return resource1.getId().compareToIgnoreCase(resource2.getId());
}
});
return allResources;
}
serializer.endTag(NAMESPACE_OPF, OPFTags.manifest);
}
/**
* Writes a resources as an item element
*
* @param resource
* @param serializer
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException 1@throws XMLStreamException
*/
private static void writeItem(EpubBook book, Resource resource,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (resource == null ||
(resource.getMediaType() == MediaTypes.NCX
&& book.getSpine().getTocResource() != null)) {
return;
private static List<Resource> getAllResourcesSortById(Book book) {
List<Resource> allResources = new ArrayList<>(
book.getResources().getAll());
Collections.sort(allResources, (resource1, resource2) -> resource1.getId().compareToIgnoreCase(resource2.getId()));
return allResources;
}
if (StringUtil.isBlank(resource.getId())) {
/**
* Writes a resources as an item element
*
* @param resource g
* @param serializer g
* @throws IOException g
* @throws IllegalStateException g
* @throws IllegalArgumentException 1@throws XMLStreamException
*/
private static void writeItem(Book book, Resource resource,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (resource == null ||
(resource.getMediaType() == MediaTypes.NCX
&& book.getSpine().getTocResource() != null)) {
return;
}
if (StringUtil.isBlank(resource.getId())) {
// log.error("resource id must not be empty (href: " + resource.getHref()
// + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG, "resource id must not be empty (href: " + resource.getHref()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (StringUtil.isBlank(resource.getHref())) {
Log.e(TAG, "resource id must not be empty (href: " + resource.getHref()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (StringUtil.isBlank(resource.getHref())) {
// log.error("resource href must not be empty (id: " + resource.getId()
// + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG,"resource href must not be empty (id: " + resource.getId()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (resource.getMediaType() == null) {
Log.e(TAG, "resource href must not be empty (id: " + resource.getId()
+ ", mediatype:" + resource.getMediaType() + ")");
return;
}
if (resource.getMediaType() == null) {
// log.error("resource mediatype must not be empty (id: " + resource.getId()
// + ", href:" + resource.getHref() + ")");
Log.e(TAG,"resource mediatype must not be empty (id: " + resource.getId()
+ ", href:" + resource.getHref() + ")");
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
resource.getId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
resource.getHref());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
resource.getMediaType().getName());
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
}
/**
* List all spine references
* @throws IOException
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
private static void writeSpineItems(Spine spine, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (SpineReference spineReference : spine.getSpineReferences()) {
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
spineReference.getResourceId());
if (!spineReference.isLinear()) {
Log.e(TAG, "resource mediatype must not be empty (id: " + resource.getId()
+ ", href:" + resource.getHref() + ")");
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
resource.getId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
resource.getHref());
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
OPFValues.no);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
resource.getMediaType().getName());
serializer.endTag(NAMESPACE_OPF, OPFTags.item);
}
}
private static void writeGuide(EpubBook book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.guide);
ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter,
serializer);
for (GuideReference reference : book.getGuide().getReferences()) {
writeGuideReference(reference, serializer);
/**
* List all spine references
*
* @throws IOException f
* @throws IllegalStateException f
* @throws IllegalArgumentException f
*/
@SuppressWarnings("unused")
private static void writeSpineItems(Spine spine, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
for (SpineReference spineReference : spine.getSpineReferences()) {
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref);
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref,
spineReference.getResourceId());
if (!spineReference.isLinear()) {
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear,
OPFValues.no);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
}
}
serializer.endTag(NAMESPACE_OPF, OPFTags.guide);
}
private static void ensureCoverPageGuideReferenceWritten(Guide guide,
EpubWriter epubWriter, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (!(guide.getGuideReferencesByType(GuideReference.COVER).isEmpty())) {
return;
private static void writeGuide(Book book, EpubWriter epubWriter,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.guide);
ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter,
serializer);
for (GuideReference reference : book.getGuide().getReferences()) {
writeGuideReference(reference, serializer);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.guide);
}
Resource coverPage = guide.getCoverPage();
if (coverPage != null) {
writeGuideReference(
new GuideReference(guide.getCoverPage(), GuideReference.COVER,
GuideReference.COVER), serializer);
@SuppressWarnings("unused")
private static void ensureCoverPageGuideReferenceWritten(Guide guide,
EpubWriter epubWriter, XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (!(guide.getGuideReferencesByType(GuideReference.COVER).isEmpty())) {
return;
}
Resource coverPage = guide.getCoverPage();
if (coverPage != null) {
writeGuideReference(
new GuideReference(guide.getCoverPage(), GuideReference.COVER,
GuideReference.COVER), serializer);
}
}
}
private static void writeGuideReference(GuideReference reference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (reference == null) {
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.reference);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type,
reference.getType());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
reference.getCompleteHref());
if (StringUtil.isNotBlank(reference.getTitle())) {
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title,
reference.getTitle());
private static void writeGuideReference(GuideReference reference,
XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException {
if (reference == null) {
return;
}
serializer.startTag(NAMESPACE_OPF, OPFTags.reference);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type,
reference.getType());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
reference.getCompleteHref());
if (StringUtil.isNotBlank(reference.getTitle())) {
serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title,
reference.getTitle());
}
serializer.endTag(NAMESPACE_OPF, OPFTags.reference);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.reference);
}
}

@ -11,9 +11,10 @@ import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.util.CollectionUtil;
import me.ag2s.epublib.util.ResourceUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
@ -26,148 +27,147 @@ import java.util.zip.ZipInputStream;
* Loads Resources from inputStreams, ZipFiles, etc
*
* @author paul
*
*/
public class ResourcesLoader {
private static String TAG=ResourceUtil.class.getName();
/**
* Loads the entries of the zipFile as resources.
*
* The MediaTypes that are in the lazyLoadedTypes will not get their
* contents loaded, but are stored as references to entries into the
* ZipFile and are loaded on demand by the Resource system.
*
* @param zipFile
* @param defaultHtmlEncoding
* @param lazyLoadedTypes
* @return
* @throws IOException
*/
public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding,
List<MediaType> lazyLoadedTypes) throws IOException {
LazyResourceProvider resourceProvider =
new EpubResourceProvider(zipFile.getName());
Resources result = new Resources();
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
if (zipEntry == null || zipEntry.isDirectory()) {
continue;
}
String href = zipEntry.getName();
Resource resource;
if (shouldLoadLazy(href, lazyLoadedTypes)) {
resource = new LazyResource(resourceProvider, zipEntry.getSize(), href);
} else {
resource = ResourceUtil
.createResource(zipEntry, zipFile.getInputStream(zipEntry));
}
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
private static final String TAG = ResourcesLoader.class.getName();
/**
* Loads the entries of the zipFile as resources.
* <p>
* The MediaTypes that are in the lazyLoadedTypes will not get their
* contents loaded, but are stored as references to entries into the
* ZipFile and are loaded on demand by the Resource system.
*
* @param zipFile import epub zipfile
* @param defaultHtmlEncoding epub xhtml default encoding
* @param lazyLoadedTypes lazyLoadedTypes
* @return Resources
* @throws IOException IOException
*/
public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding,
List<MediaType> lazyLoadedTypes) throws IOException {
LazyResourceProvider resourceProvider =
new EpubResourceProvider(zipFile.getName());
Resources result = new Resources();
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
if (zipEntry == null || zipEntry.isDirectory()) {
continue;
}
String href = zipEntry.getName();
Resource resource;
if (shouldLoadLazy(href, lazyLoadedTypes)) {
resource = new LazyResource(resourceProvider, zipEntry.getSize(), href);
} else {
resource = ResourceUtil
.createResource(zipEntry, zipFile.getInputStream(zipEntry));
}
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
}
return result;
}
/**
* Whether the given href will load a mediaType that is in the
* collection of lazilyLoadedMediaTypes.
*
* @param href href
* @param lazilyLoadedMediaTypes lazilyLoadedMediaTypes
* @return Whether the given href will load a mediaType that is
* in the collection of lazilyLoadedMediaTypes.
*/
private static boolean shouldLoadLazy(String href,
Collection<MediaType> lazilyLoadedMediaTypes) {
if (CollectionUtil.isEmpty(lazilyLoadedMediaTypes)) {
return false;
}
MediaType mediaType = MediaTypes.determineMediaType(href);
return lazilyLoadedMediaTypes.contains(mediaType);
}
return result;
}
/**
* Whether the given href will load a mediaType that is in the
* collection of lazilyLoadedMediaTypes.
*
* @param href
* @param lazilyLoadedMediaTypes
* @return Whether the given href will load a mediaType that is
* in the collection of lazilyLoadedMediaTypes.
*/
private static boolean shouldLoadLazy(String href,
Collection<MediaType> lazilyLoadedMediaTypes) {
if (CollectionUtil.isEmpty(lazilyLoadedMediaTypes)) {
return false;
/**
* Loads all entries from the ZipInputStream as Resources.
* <p>
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipInputStream zipInputStream
* @param defaultHtmlEncoding defaultHtmlEncoding
* @return Resources
* @throws IOException IOException
*/
public static Resources loadResources(ZipInputStream zipInputStream,
String defaultHtmlEncoding) throws IOException {
Resources result = new Resources();
ZipEntry zipEntry;
do {
// get next valid zipEntry
zipEntry = getNextZipEntry(zipInputStream);
if ((zipEntry == null) || zipEntry.isDirectory()) {
continue;
}
// store resource
Resource resource = ResourceUtil.createResource(zipEntry, zipInputStream);
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
} while (zipEntry != null);
return result;
}
MediaType mediaType = MediaTypes.determineMediaType(href);
return lazilyLoadedMediaTypes.contains(mediaType);
}
/**
* Loads all entries from the ZipInputStream as Resources.
*
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipInputStream
* @param defaultHtmlEncoding
* @return
* @throws IOException
*/
public static Resources loadResources(ZipInputStream zipInputStream,
String defaultHtmlEncoding) throws IOException {
Resources result = new Resources();
ZipEntry zipEntry;
do {
// get next valid zipEntry
zipEntry = getNextZipEntry(zipInputStream);
if ((zipEntry == null) || zipEntry.isDirectory()) {
continue;
}
// store resource
Resource resource = ResourceUtil.createResource(zipEntry, zipInputStream);
if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding);
}
result.add(resource);
} while (zipEntry != null);
return result;
}
private static ZipEntry getNextZipEntry(ZipInputStream zipInputStream)
throws IOException {
try {
return zipInputStream.getNextEntry();
} catch (ZipException e) {
//see <a href="https://github.com/psiegman/epublib/issues/122">Issue #122 Infinite loop</a>.
//when reading a file that is not a real zip archive or a zero length file, zipInputStream.getNextEntry()
//throws an exception and does not advance, so loadResources enters an infinite loop
//log.error("Invalid or damaged zip file.", e);
Log.e(TAG,"",e);
try {
zipInputStream.closeEntry();
} catch (Exception ignored) {
}
throw e;
private static ZipEntry getNextZipEntry(ZipInputStream zipInputStream)
throws IOException {
try {
return zipInputStream.getNextEntry();
} catch (ZipException e) {
//see <a href="https://github.com/psiegman/epublib/issues/122">Issue #122 Infinite loop</a>.
//when reading a file that is not a real zip archive or a zero length file, zipInputStream.getNextEntry()
//throws an exception and does not advance, so loadResources enters an infinite loop
//log.error("Invalid or damaged zip file.", e);
Log.e(TAG, e.getLocalizedMessage());
try {
zipInputStream.closeEntry();
} catch (Exception ignored) {
}
throw e;
}
}
/**
* Loads all entries from the ZipInputStream as Resources.
* <p>
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipFile zipFile
* @param defaultHtmlEncoding defaultHtmlEncoding
* @return Resources
* @throws IOException IOException
*/
public static Resources loadResources(ZipFile zipFile, String defaultHtmlEncoding) throws IOException {
List<MediaType> ls = new ArrayList<>();
return loadResources(zipFile, defaultHtmlEncoding, ls);
}
}
/**
* Loads all entries from the ZipInputStream as Resources.
*
* Loads the contents of all ZipEntries into memory.
* Is fast, but may lead to memory problems when reading large books
* on devices with small amounts of memory.
*
* @param zipFile
* @param defaultHtmlEncoding
* @return
* @throws IOException
*/
public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding) throws IOException {
return loadResources(zipFile, defaultHtmlEncoding, Collections.emptyList());
}
}

@ -15,7 +15,7 @@ public class CollectionUtil {
*/
private static class IteratorEnumerationAdapter<T> implements Enumeration<T> {
private Iterator<T> iterator;
private final Iterator<T> iterator;
public IteratorEnumerationAdapter(Iterator<T> iter) {
this.iterator = iter;
@ -34,21 +34,22 @@ public class CollectionUtil {
/**
* Creates an Enumeration out of the given Iterator.
* @param <T>
* @param it
* @param <T> g
* @param it g
* @return an Enumeration created out of the given Iterator.
*/
@SuppressWarnings("unused")
public static <T> Enumeration<T> createEnumerationFromIterator(
Iterator<T> it) {
return new IteratorEnumerationAdapter<T>(it);
return new IteratorEnumerationAdapter<>(it);
}
/**
* Returns the first element of the list, null if the list is null or empty.
*
* @param <T>
* @param list
* @param <T> f
* @param list f
* @return the first element of the list, null if the list is null or empty.
*/
public static <T> T first(List<T> list) {
@ -61,7 +62,7 @@ public class CollectionUtil {
/**
* Whether the given collection is null or has no elements.
*
* @param collection
* @param collection g
* @return Whether the given collection is null or has no elements.
*/
public static boolean isEmpty(Collection<?> collection) {

@ -23,10 +23,10 @@ public class IOUtil {
/**
* Gets the contents of the Reader as a byte[], with the given character encoding.
*
* @param in
* @param encoding
* @param in g
* @param encoding g
* @return the contents of the Reader as a byte[], with the given character encoding.
* @throws IOException
* @throws IOException g
*/
public static byte[] toByteArray(Reader in, String encoding)
throws IOException {
@ -39,9 +39,9 @@ public class IOUtil {
/**
* Returns the contents of the InputStream as a byte[]
*
* @param in
* @param in f
* @return the contents of the InputStream as a byte[]
* @throws IOException
* @throws IOException f
*/
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
@ -59,7 +59,7 @@ public class IOUtil {
* @param in the stream to read data from
* @param size the size of the array to create
* @return the array, or null
* @throws IOException
* @throws IOException f
*/
public static byte[] toByteArray(InputStream in, int size)
throws IOException {
@ -89,8 +89,8 @@ public class IOUtil {
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise.
*
* @param nrRead
* @param totalNrNread
* @param nrRead f
* @param totalNrNread f
* @return if totalNrRead &lt; 0 then totalNrRead is returned, if
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise.
@ -109,15 +109,15 @@ public class IOUtil {
/**
* Copies the contents of the InputStream to the OutputStream.
*
* @param in
* @param out
* @param in f
* @param out f
* @return the nr of bytes read, or -1 if the amount &gt; Integer.MAX_VALUE
* @throws IOException
* @throws IOException f
*/
public static int copy(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[IO_COPY_BUFFER_SIZE];
int readSize = -1;
int readSize ;
int result = 0;
while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize);
@ -130,14 +130,14 @@ public class IOUtil {
/**
* Copies the contents of the Reader to the Writer.
*
* @param in
* @param out
* @param in f
* @param out f
* @return the nr of characters read, or -1 if the amount &gt; Integer.MAX_VALUE
* @throws IOException
* @throws IOException f
*/
public static int copy(Reader in, Writer out) throws IOException {
char[] buffer = new char[IO_COPY_BUFFER_SIZE];
int readSize = -1;
int readSize;
int result = 0;
while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize);
@ -148,7 +148,7 @@ public class IOUtil {
}
public static String Stream2String(InputStream inputStream) {
String str="";
String str;
try {
BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream();

@ -11,9 +11,10 @@ import java.io.OutputStream;
*
* @author paul
*/
@SuppressWarnings("unused")
public class NoCloseOutputStream extends OutputStream {
private OutputStream outputStream;
private final OutputStream outputStream;
public NoCloseOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;

@ -11,16 +11,17 @@ import java.io.Writer;
*
* @author paul
*/
@SuppressWarnings("unused")
public class NoCloseWriter extends Writer {
private Writer writer;
private final Writer writer;
public NoCloseWriter(Writer writer) {
this.writer = writer;
}
@Override
public void close() throws IOException {
public void close() {
}
@Override

@ -1,10 +1,9 @@
package me.ag2s.epublib.util;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.epub.EpubProcessorSupport;
import me.ag2s.epublib.domain.MediaTypes;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@ -15,11 +14,12 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.epub.EpubProcessorSupport;
/**
* Various resource utility methods
@ -28,147 +28,148 @@ import org.xml.sax.SAXException;
*/
public class ResourceUtil {
public static Resource createHTMLResource(String title, String string) {
String html=createHtml(title,string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
Resource result = new Resource(data, mediaTypeProperty);
return result;
}
public static Resource createHTMLResource(String title, String string,String href) {
String html=createHtml(title,string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
Resource result = new Resource(null,data,href ,mediaTypeProperty);
return result;
}
private static String createHtml(String title,String txt){
StringBuilder body= new StringBuilder();
for(String s:txt.split("\\r?\\n")){
s=s.trim();
if(s!=null&&s.length()!=0) {
body.append("<p>").append(s).append("</p>");
}
public static Resource createHTMLResource(String title, String string) {
String html = createHtml(title, string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
return new Resource(data, mediaTypeProperty);
}
@SuppressWarnings("unused")
public static Resource createHTMLResource(String title, String string, String href) {
String html = createHtml(title, string);
MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes();
return new Resource(null, data, href, mediaTypeProperty);
}
@SuppressWarnings("unused")
private static String createHtml(String title, String txt) {
StringBuilder body = new StringBuilder();
for (String s : txt.split("\\r?\\n")) {
s = s.trim();
if (s.length() != 0) {
body.append("<p>").append(s).append("</p>");
}
}
String html = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
html += "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
html += "<head><title>" + title + "</title>" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"css/style.css\"/>" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/></head>";
html += "<body><h1>" + title + "</h1>" +
body +
"</body></html>";
return html;
}
@SuppressWarnings("unused")
public static Resource createResource(File file) throws IOException {
if (file == null) {
return null;
}
MediaType mediaType = MediaTypes.determineMediaType(file.getName());
byte[] data = IOUtil.toByteArray(new FileInputStream(file));
return new Resource(data, mediaType);
}
/**
* Creates a resource with as contents a html page with the given title.
*
* @param title v
* @param href v
* @return a resource with as contents a html page with the given title.
*/
@SuppressWarnings("unused")
public static Resource createResource(String title, String href) {
String content =
"<html><head><title>" + title + "</title></head><body><h1>" + title
+ "</h1></body></html>";
return new Resource(null, content.getBytes(), href, MediaTypes.XHTML,
Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource out of the given zipEntry and zipInputStream.
*
* @param zipEntry v
* @param zipInputStream v
* @return a resource created out of the given zipEntry and zipInputStream.
* @throws IOException v
*/
public static Resource createResource(ZipEntry zipEntry,
ZipInputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
String html="<?xml version=\"1.0\" encoding=\"utf-8\"?>";
html+="<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
html+="<head><title>"+title+"</title>" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"css/style.css\"/>" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/></head>";
html+="<body><h1>"+title+"</h1>" +
body+
"</body></html>";
return html;
}
public static Resource createResource(File file) throws IOException {
if (file == null) {
return null;
public static Resource createResource(ZipEntry zipEntry,
InputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
MediaType mediaType = MediaTypes.determineMediaType(file.getName());
byte[] data = IOUtil.toByteArray(new FileInputStream(file));
Resource result = new Resource(data, mediaType);
return result;
}
/**
* Creates a resource with as contents a html page with the given title.
*
* @param title
* @param href
* @return a resource with as contents a html page with the given title.
*/
public static Resource createResource(String title, String href) {
String content =
"<html><head><title>" + title + "</title></head><body><h1>" + title
+ "</h1></body></html>";
return new Resource(null, content.getBytes(), href, MediaTypes.XHTML,
Constants.CHARACTER_ENCODING);
}
/**
* Creates a resource out of the given zipEntry and zipInputStream.
*
* @param zipEntry
* @param zipInputStream
* @return a resource created out of the given zipEntry and zipInputStream.
* @throws IOException
*/
public static Resource createResource(ZipEntry zipEntry,
ZipInputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
public static Resource createResource(ZipEntry zipEntry,
InputStream zipInputStream) throws IOException {
return new Resource(zipInputStream, zipEntry.getName());
}
/**
* Converts a given string from given input character encoding to the requested output character encoding.
*
* @param inputEncoding
* @param outputEncoding
* @param input
* @return the string from given input character encoding converted to the requested output character encoding.
* @throws UnsupportedEncodingException
*/
public static byte[] recode(String inputEncoding, String outputEncoding,
byte[] input) throws UnsupportedEncodingException {
return new String(input, inputEncoding).getBytes(outputEncoding);
}
/**
* Gets the contents of the Resource as an InputSource in a null-safe manner.
*
*/
public static InputSource getInputSource(Resource resource)
throws IOException {
if (resource == null) {
return null;
/**
* Converts a given string from given input character encoding to the requested output character encoding.
*
* @param inputEncoding v
* @param outputEncoding v
* @param input v
* @return the string from given input character encoding converted to the requested output character encoding.
* @throws UnsupportedEncodingException v
*/
@SuppressWarnings("unused")
public static byte[] recode(String inputEncoding, String outputEncoding,
byte[] input) throws UnsupportedEncodingException {
return new String(input, inputEncoding).getBytes(outputEncoding);
}
Reader reader = resource.getReader();
if (reader == null) {
return null;
/**
* Gets the contents of the Resource as an InputSource in a null-safe manner.
*/
@SuppressWarnings("unused")
public static InputSource getInputSource(Resource resource)
throws IOException {
if (resource == null) {
return null;
}
Reader reader = resource.getReader();
if (reader == null) {
return null;
}
return new InputSource(reader);
}
InputSource inputSource = new InputSource(reader);
return inputSource;
}
/**
* Reads parses the xml therein and returns the result as a Document
*/
public static Document getAsDocument(Resource resource)
throws SAXException, IOException, ParserConfigurationException {
return getAsDocument(resource,
EpubProcessorSupport.createDocumentBuilder());
}
/**
* Reads the given resources inputstream, parses the xml therein and returns the result as a Document
*
* @param resource
* @param documentBuilder
* @return the document created from the given resource
* @throws UnsupportedEncodingException
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public static Document getAsDocument(Resource resource,
DocumentBuilder documentBuilder)
throws UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException {
InputSource inputSource = getInputSource(resource);
if (inputSource == null) {
return null;
/**
* Reads parses the xml therein and returns the result as a Document
*/
public static Document getAsDocument(Resource resource)
throws SAXException, IOException {
return getAsDocument(resource,
EpubProcessorSupport.createDocumentBuilder());
}
/**
* Reads the given resources inputstream, parses the xml therein and returns the result as a Document
*
* @param resource v
* @param documentBuilder v
* @return the document created from the given resource
* @throws UnsupportedEncodingException v
* @throws SAXException v
* @throws IOException v
*/
public static Document getAsDocument(Resource resource,
DocumentBuilder documentBuilder)
throws UnsupportedEncodingException, SAXException, IOException {
InputSource inputSource = getInputSource(resource);
if (inputSource == null) {
return null;
}
return documentBuilder.parse(inputSource);
}
Document result = documentBuilder.parse(inputSource);
return result;
}
}

@ -1,202 +0,0 @@
package me.ag2s.epublib.util;
/*
* Copyright (c) 2006, John Kristian
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of StAX-Utils nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* Abstract class for writing filtered XML streams. This class provides methods
* that merely delegate to the contained stream. Subclasses should override some
* of these methods, and may also provide additional methods and fields.
*
* @author <a href="mailto:jk2006@engineer.com">John Kristian</a>
*/
public abstract class StreamWriterDelegate implements XMLStreamWriter {
protected StreamWriterDelegate(XMLStreamWriter out) {
this.out = out;
}
protected XMLStreamWriter out;
public Object getProperty(String name)
throws IllegalArgumentException {
return out.getProperty(name);
}
public NamespaceContext getNamespaceContext() {
return out.getNamespaceContext();
}
public void setNamespaceContext(NamespaceContext context)
throws XMLStreamException {
out.setNamespaceContext(context);
}
public void setDefaultNamespace(String uri)
throws XMLStreamException {
out.setDefaultNamespace(uri);
}
public void writeStartDocument() throws XMLStreamException {
out.writeStartDocument();
}
public void writeStartDocument(String version)
throws XMLStreamException {
out.writeStartDocument(version);
}
public void writeStartDocument(String encoding, String version)
throws XMLStreamException {
out.writeStartDocument(encoding, version);
}
public void writeDTD(String dtd) throws XMLStreamException {
out.writeDTD(dtd);
}
public void writeProcessingInstruction(String target)
throws XMLStreamException {
out.writeProcessingInstruction(target);
}
public void writeProcessingInstruction(String target, String data)
throws XMLStreamException {
out.writeProcessingInstruction(target, data);
}
public void writeComment(String data) throws XMLStreamException {
out.writeComment(data);
}
public void writeEmptyElement(String localName)
throws XMLStreamException {
out.writeEmptyElement(localName);
}
public void writeEmptyElement(String namespaceURI, String localName)
throws XMLStreamException {
out.writeEmptyElement(namespaceURI, localName);
}
public void writeEmptyElement(String prefix, String localName,
String namespaceURI) throws XMLStreamException {
out.writeEmptyElement(prefix, localName, namespaceURI);
}
public void writeStartElement(String localName)
throws XMLStreamException {
out.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName)
throws XMLStreamException {
out.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName,
String namespaceURI) throws XMLStreamException {
out.writeStartElement(prefix, localName, namespaceURI);
}
public void writeDefaultNamespace(String namespaceURI)
throws XMLStreamException {
out.writeDefaultNamespace(namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI)
throws XMLStreamException {
out.writeNamespace(prefix, namespaceURI);
}
public String getPrefix(String uri) throws XMLStreamException {
return out.getPrefix(uri);
}
public void setPrefix(String prefix, String uri)
throws XMLStreamException {
out.setPrefix(prefix, uri);
}
public void writeAttribute(String localName, String value)
throws XMLStreamException {
out.writeAttribute(localName, value);
}
public void writeAttribute(String namespaceURI, String localName,
String value) throws XMLStreamException {
out.writeAttribute(namespaceURI, localName, value);
}
public void writeAttribute(String prefix, String namespaceURI,
String localName, String value) throws XMLStreamException {
out.writeAttribute(prefix, namespaceURI, localName, value);
}
public void writeCharacters(String text) throws XMLStreamException {
out.writeCharacters(text);
}
public void writeCharacters(char[] text, int start, int len)
throws XMLStreamException {
out.writeCharacters(text, start, len);
}
public void writeCData(String data) throws XMLStreamException {
out.writeCData(data);
}
public void writeEntityRef(String name) throws XMLStreamException {
out.writeEntityRef(name);
}
public void writeEndElement() throws XMLStreamException {
out.writeEndElement();
}
public void writeEndDocument() throws XMLStreamException {
out.writeEndDocument();
}
public void flush() throws XMLStreamException {
out.flush();
}
public void close() throws XMLStreamException {
out.close();
}
}

@ -6,270 +6,269 @@ import java.util.List;
/**
* Various String utility functions.
*
* <p>
* Most of the functions herein are re-implementations of the ones in apache
* commons StringUtils. The reason for re-implementing this is that the
* functions are fairly simple and using my own implementation saves the
* inclusion of a 200Kb jar file.
*
* @author paul.siegmann
*
*/
public class StringUtil {
/**
* Changes a path containing '..', '.' and empty dirs into a path that
* doesn't. X/foo/../Y is changed into 'X/Y', etc. Does not handle invalid
* paths like "../".
*
* @param path
* @return the normalized path
*/
public static String collapsePathDots(String path) {
String[] stringParts = path.split("/");
List<String> parts = new ArrayList<String>(Arrays.asList(stringParts));
for (int i = 0; i < parts.size() - 1; i++) {
String currentDir = parts.get(i);
if (currentDir.length() == 0 || currentDir.equals(".")) {
parts.remove(i);
i--;
} else if (currentDir.equals("..")) {
parts.remove(i - 1);
parts.remove(i - 1);
i -= 2;
}
}
StringBuilder result = new StringBuilder();
if (path.startsWith("/")) {
result.append('/');
}
for (int i = 0; i < parts.size(); i++) {
result.append(parts.get(i));
if (i < (parts.size() - 1)) {
result.append('/');
}
/**
* Changes a path containing '..', '.' and empty dirs into a path that
* doesn't. X/foo/../Y is changed into 'X/Y', etc. Does not handle invalid
* paths like "../".
*
* @param path path
* @return the normalized path
*/
public static String collapsePathDots(String path) {
String[] stringParts = path.split("/");
List<String> parts = new ArrayList<>(Arrays.asList(stringParts));
for (int i = 0; i < parts.size() - 1; i++) {
String currentDir = parts.get(i);
if (currentDir.length() == 0 || currentDir.equals(".")) {
parts.remove(i);
i--;
} else if (currentDir.equals("..")) {
parts.remove(i - 1);
parts.remove(i - 1);
i -= 2;
}
}
StringBuilder result = new StringBuilder();
if (path.startsWith("/")) {
result.append('/');
}
for (int i = 0; i < parts.size(); i++) {
result.append(parts.get(i));
if (i < (parts.size() - 1)) {
result.append('/');
}
}
return result.toString();
}
return result.toString();
}
/**
* Whether the String is not null, not zero-length and does not contain of
* only whitespace.
*
* @param text
* @return Whether the String is not null, not zero-length and does not contain of
*/
public static boolean isNotBlank(String text) {
return !isBlank(text);
}
/**
* Whether the String is null, zero-length and does contain only whitespace.
*
* @return Whether the String is null, zero-length and does contain only whitespace.
*/
public static boolean isBlank(String text) {
if (isEmpty(text)) {
return true;
}
for (int i = 0; i < text.length(); i++) {
if (!Character.isWhitespace(text.charAt(i))) {
return false;
}
/**
* Whether the String is not null, not zero-length and does not contain of
* only whitespace.
*
* @param text text
* @return Whether the String is not null, not zero-length and does not contain of
*/
public static boolean isNotBlank(String text) {
return !isBlank(text);
}
return true;
}
/**
* Whether the given string is null or zero-length.
*
* @param text the input for this method
* @return Whether the given string is null or zero-length.
*/
public static boolean isEmpty(String text) {
return (text == null) || (text.length() == 0);
}
/**
* Whether the given source string ends with the given suffix, ignoring
* case.
*
* @param source
* @param suffix
* @return Whether the given source string ends with the given suffix, ignoring case.
*/
public static boolean endsWithIgnoreCase(String source, String suffix) {
if (isEmpty(suffix)) {
return true;
/**
* Whether the String is null, zero-length and does contain only whitespace.
*
* @return Whether the String is null, zero-length and does contain only whitespace.
*/
public static boolean isBlank(String text) {
if (isEmpty(text)) {
return true;
}
for (int i = 0; i < text.length(); i++) {
if (!Character.isWhitespace(text.charAt(i))) {
return false;
}
}
return true;
}
if (isEmpty(source)) {
return false;
}
if (suffix.length() > source.length()) {
return false;
}
return source.substring(source.length() - suffix.length())
.toLowerCase().endsWith(suffix.toLowerCase());
}
/**
* If the given text is null return "", the original text otherwise.
*
* @param text
* @return If the given text is null "", the original text otherwise.
*/
public static String defaultIfNull(String text) {
return defaultIfNull(text, "");
}
/**
* If the given text is null return "", the given defaultValue otherwise.
*
* @param text
* @param defaultValue
* @return If the given text is null "", the given defaultValue otherwise.
*/
public static String defaultIfNull(String text, String defaultValue) {
if (text == null) {
return defaultValue;
/**
* Whether the given string is null or zero-length.
*
* @param text the input for this method
* @return Whether the given string is null or zero-length.
*/
public static boolean isEmpty(String text) {
return (text == null) || (text.length() == 0);
}
return text;
}
/**
* Null-safe string comparator
*
* @param text1
* @param text2
* @return whether the two strings are equal
*/
public static boolean equals(String text1, String text2) {
if (text1 == null) {
return (text2 == null);
/**
* Whether the given source string ends with the given suffix, ignoring
* case.
*
* @param source source
* @param suffix suffix
* @return Whether the given source string ends with the given suffix, ignoring case.
*/
public static boolean endsWithIgnoreCase(String source, String suffix) {
if (isEmpty(suffix)) {
return true;
}
if (isEmpty(source)) {
return false;
}
if (suffix.length() > source.length()) {
return false;
}
return source.substring(source.length() - suffix.length())
.toLowerCase().endsWith(suffix.toLowerCase());
}
return text1.equals(text2);
}
/**
* Pretty toString printer.
*
* @param keyValues
* @return a string representation of the input values
*/
public static String toString(Object... keyValues) {
StringBuilder result = new StringBuilder();
result.append('[');
for (int i = 0; i < keyValues.length; i += 2) {
if (i > 0) {
result.append(", ");
}
result.append(keyValues[i]);
result.append(": ");
Object value = null;
if ((i + 1) < keyValues.length) {
value = keyValues[i + 1];
}
if (value == null) {
result.append("<null>");
} else {
result.append('\'');
result.append(value);
result.append('\'');
}
/**
* If the given text is null return "", the original text otherwise.
*
* @param text text
* @return If the given text is null "", the original text otherwise.
*/
public static String defaultIfNull(String text) {
return defaultIfNull(text, "");
}
result.append(']');
return result.toString();
}
public static int hashCode(String... values) {
int result = 31;
for (int i = 0; i < values.length; i++) {
result ^= String.valueOf(values[i]).hashCode();
/**
* If the given text is null return "", the given defaultValue otherwise.
*
* @param text d
* @param defaultValue d
* @return If the given text is null "", the given defaultValue otherwise.
*/
public static String defaultIfNull(String text, String defaultValue) {
if (text == null) {
return defaultValue;
}
return text;
}
return result;
}
/**
* Gives the substring of the given text before the given separator.
*
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text
* @param separator
* @return the substring of the given text before the given separator.
*/
public static String substringBefore(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int sepPos = text.indexOf(separator);
if (sepPos < 0) {
return text;
/**
* Null-safe string comparator
*
* @param text1 d
* @param text2 d
* @return whether the two strings are equal
*/
public static boolean equals(String text1, String text2) {
if (text1 == null) {
return (text2 == null);
}
return text1.equals(text2);
}
return text.substring(0, sepPos);
}
/**
* Gives the substring of the given text before the last occurrence of the
* given separator.
*
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text
* @param separator
* @return the substring of the given text before the last occurrence of the given separator.
*/
public static String substringBeforeLast(String text, char separator) {
if (isEmpty(text)) {
return text;
/**
* Pretty toString printer.
*
* @param keyValues d
* @return a string representation of the input values
*/
public static String toString(Object... keyValues) {
StringBuilder result = new StringBuilder();
result.append('[');
for (int i = 0; i < keyValues.length; i += 2) {
if (i > 0) {
result.append(", ");
}
result.append(keyValues[i]);
result.append(": ");
Object value = null;
if ((i + 1) < keyValues.length) {
value = keyValues[i + 1];
}
if (value == null) {
result.append("<null>");
} else {
result.append('\'');
result.append(value);
result.append('\'');
}
}
result.append(']');
return result.toString();
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return text;
public static int hashCode(String... values) {
int result = 31;
for (String value : values) {
result ^= String.valueOf(value).hashCode();
}
return result;
}
return text.substring(0, cPos);
}
/**
* Gives the substring of the given text after the last occurrence of the
* given separator.
*
* If the text does not contain the given separator then "" is returned.
*
* @param text
* @param separator
* @return the substring of the given text after the last occurrence of the given separator.
*/
public static String substringAfterLast(String text, char separator) {
if (isEmpty(text)) {
return text;
/**
* Gives the substring of the given text before the given separator.
* <p>
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text d
* @param separator d
* @return the substring of the given text before the given separator.
*/
public static String substringBefore(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int sepPos = text.indexOf(separator);
if (sepPos < 0) {
return text;
}
return text.substring(0, sepPos);
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return "";
/**
* Gives the substring of the given text before the last occurrence of the
* given separator.
* <p>
* If the text does not contain the given separator then the given text is
* returned.
*
* @param text d
* @param separator d
* @return the substring of the given text before the last occurrence of the given separator.
*/
public static String substringBeforeLast(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return text;
}
return text.substring(0, cPos);
}
return text.substring(cPos + 1);
}
/**
* Gives the substring of the given text after the given separator.
*
* If the text does not contain the given separator then "" is returned.
*
* @param text the input text
* @param c the separator char
* @return the substring of the given text after the given separator.
*/
public static String substringAfter(String text, char c) {
if (isEmpty(text)) {
return text;
/**
* Gives the substring of the given text after the last occurrence of the
* given separator.
* <p>
* If the text does not contain the given separator then "" is returned.
*
* @param text d
* @param separator d
* @return the substring of the given text after the last occurrence of the given separator.
*/
public static String substringAfterLast(String text, char separator) {
if (isEmpty(text)) {
return text;
}
int cPos = text.lastIndexOf(separator);
if (cPos < 0) {
return "";
}
return text.substring(cPos + 1);
}
int cPos = text.indexOf(c);
if (cPos < 0) {
return "";
/**
* Gives the substring of the given text after the given separator.
* <p>
* If the text does not contain the given separator then "" is returned.
*
* @param text the input text
* @param c the separator char
* @return the substring of the given text after the given separator.
*/
public static String substringAfter(String text, char c) {
if (isEmpty(text)) {
return text;
}
int cPos = text.indexOf(c);
if (cPos < 0) {
return "";
}
return text.substring(cPos + 1);
}
return text.substring(cPos + 1);
}
}

@ -85,6 +85,7 @@ public class BOMInputStream extends ProxyInputStream {
* a {@link ByteOrderMark#UTF_8} BOM.
* @param delegate the InputStream to delegate to
*/
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate) {
this(delegate, false, ByteOrderMark.UTF_8);
}
@ -96,6 +97,7 @@ public class BOMInputStream extends ProxyInputStream {
* @param include true to include the UTF-8 BOM or
* false to exclude it
*/
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, boolean include) {
this(delegate, include, ByteOrderMark.UTF_8);
}
@ -106,6 +108,7 @@ public class BOMInputStream extends ProxyInputStream {
* @param delegate the InputStream to delegate to
* @param boms The BOMs to detect and exclude
*/
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, ByteOrderMark... boms) {
this(delegate, false, boms);
}
@ -118,6 +121,7 @@ public class BOMInputStream extends ProxyInputStream {
* false to exclude them
* @param boms The BOMs to detect and optionally exclude
*/
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, boolean include, ByteOrderMark... boms) {
super(delegate);
if (boms == null || boms.length == 0) {
@ -134,6 +138,7 @@ public class BOMInputStream extends ProxyInputStream {
* if it does not
* @throws IOException if an error reading the first bytes of the stream occurs
*/
@SuppressWarnings("unused")
public boolean hasBOM() throws IOException {
return (getBOM() != null);
}
@ -148,6 +153,7 @@ public class BOMInputStream extends ProxyInputStream {
* is configured to detect
* @throws IOException if an error reading the first bytes of the stream occurs
*/
@SuppressWarnings("unused")
public boolean hasBOM(ByteOrderMark bom) throws IOException {
if (!boms.contains(bom)) {
throw new IllegalArgumentException("Stream not configure to detect " + bom);

@ -150,6 +150,7 @@ public class ByteOrderMark implements Serializable {
* @return the length of the BOM's bytes
*/
@Override
@SuppressWarnings("NullableProblems")
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());

@ -197,9 +197,9 @@ public abstract class ProxyInputStream extends FilterInputStream {
*
* @since Commons IO 2.0
* @param n number of bytes that the caller asked to be read
* @throws IOException if the pre-processing fails
*/
protected void beforeRead(int n) throws IOException {
@SuppressWarnings("unused")
protected void beforeRead(int n) {
}
/**
@ -217,9 +217,9 @@ public abstract class ProxyInputStream extends FilterInputStream {
*
* @since Commons IO 2.0
* @param n number of bytes read, or -1 if the end of stream was reached
* @throws IOException if the post-processing fails
*/
protected void afterRead(int n) throws IOException {
@SuppressWarnings("unused")
protected void afterRead(int n) {
}
/**

@ -30,6 +30,7 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -105,6 +106,7 @@ public class XmlStreamReader extends Reader {
*
* @return the default encoding to use.
*/
@SuppressWarnings("unused")
public String getDefaultEncoding() {
return defaultEncoding;
}
@ -121,6 +123,7 @@ public class XmlStreamReader extends Reader {
* @param file File to create a Reader from.
* @throws IOException thrown if there is a problem reading the file.
*/
@SuppressWarnings("unused")
public XmlStreamReader(File file) throws IOException {
this(new FileInputStream(file));
}
@ -224,6 +227,7 @@ public class XmlStreamReader extends Reader {
* @throws IOException thrown if there is a problem reading the stream of
* the URL.
*/
@SuppressWarnings("unused")
public XmlStreamReader(URL url) throws IOException {
this(url.openConnection(), null);
}
@ -249,15 +253,16 @@ public class XmlStreamReader extends Reader {
*/
public XmlStreamReader(URLConnection conn, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding;
@SuppressWarnings("unused")
boolean lenient = true;
String contentType = conn.getContentType();
InputStream is = conn.getInputStream();
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
if (conn instanceof HttpURLConnection || contentType != null) {
this.encoding = doHttpStream(bom, pis, contentType, lenient);
this.encoding = doHttpStream(bom, pis, contentType, true);
} else {
this.encoding = doRawStream(bom, pis, lenient);
this.encoding = doRawStream(bom, pis, true);
}
this.reader = new InputStreamReader(pis, encoding);
}
@ -509,11 +514,11 @@ public class XmlStreamReader extends Reader {
// BOM is UTF-8
if (bomEnc.equals(UTF_8)) {
if (xmlGuessEnc != null && !xmlGuessEnc.equals(UTF_8)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
String msg = MessageFormat.format(RAW_EX_1, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
if (xmlEnc != null && !xmlEnc.equals(UTF_8)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
String msg = MessageFormat.format(RAW_EX_1, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
return bomEnc;
@ -522,18 +527,18 @@ public class XmlStreamReader extends Reader {
// BOM is UTF-16BE or UTF-16LE
if (bomEnc.equals(UTF_16BE) || bomEnc.equals(UTF_16LE)) {
if (xmlGuessEnc != null && !xmlGuessEnc.equals(bomEnc)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
String msg = MessageFormat.format(RAW_EX_1, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
if (xmlEnc != null && !xmlEnc.equals(UTF_16) && !xmlEnc.equals(bomEnc)) {
String msg = MessageFormat.format(RAW_EX_1, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
String msg = MessageFormat.format(RAW_EX_1, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
return bomEnc;
}
// BOM is something else
String msg = MessageFormat.format(RAW_EX_2, new Object[] { bomEnc, xmlGuessEnc, xmlEnc });
String msg = MessageFormat.format(RAW_EX_2, bomEnc, xmlGuessEnc, xmlEnc);
throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
}
@ -697,7 +702,7 @@ public class XmlStreamReader extends Reader {
}
Matcher m = ENCODING_PATTERN.matcher(prolog);
if (m.find()) {
encoding = m.group(1).toUpperCase();
encoding = Objects.requireNonNull(m.group(1)).toUpperCase();
encoding = encoding.substring(1, encoding.length() - 1);
}
}

@ -120,6 +120,7 @@ public class XmlStreamReaderException extends IOException {
* @return the MIME type in the content-type, null if there was not
* content-type or the encoding detection did not involve HTTP.
*/
@SuppressWarnings("unused")
public String getContentTypeMime() {
return contentTypeMime;
}

Loading…
Cancel
Save