Merge pull request #915 from ag2s20150909/master

Epublib优化
pull/921/head
kunfei 4 years ago committed by GitHub
commit fb2d43837b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/src/main/java/io/legado/app/model/localBook/EpubFile.kt
  2. 5
      epublib/src/main/AndroidManifest.xml
  3. 2
      epublib/src/main/java/me/ag2s/epublib/Constants.java
  4. 12
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEvent.java
  5. 2
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationEventListener.java
  6. 22
      epublib/src/main/java/me/ag2s/epublib/browsersupport/NavigationHistory.java
  7. 21
      epublib/src/main/java/me/ag2s/epublib/browsersupport/Navigator.java
  8. 112
      epublib/src/main/java/me/ag2s/epublib/domain/Author.java
  9. 323
      epublib/src/main/java/me/ag2s/epublib/domain/Book.java
  10. 134
      epublib/src/main/java/me/ag2s/epublib/domain/Date.java
  11. 313
      epublib/src/main/java/me/ag2s/epublib/domain/EpubBook.java
  12. 10
      epublib/src/main/java/me/ag2s/epublib/domain/Guide.java
  13. 2
      epublib/src/main/java/me/ag2s/epublib/domain/GuideReference.java
  14. 7
      epublib/src/main/java/me/ag2s/epublib/domain/Identifier.java
  15. 7
      epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java
  16. 4
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemProperties.java
  17. 4
      epublib/src/main/java/me/ag2s/epublib/domain/ManifestItemRefProperties.java
  18. 12
      epublib/src/main/java/me/ag2s/epublib/domain/MediaType.java
  19. 11
      epublib/src/main/java/me/ag2s/epublib/domain/MediaTypes.java
  20. 429
      epublib/src/main/java/me/ag2s/epublib/domain/Metadata.java
  21. 18
      epublib/src/main/java/me/ag2s/epublib/domain/Resource.java
  22. 2
      epublib/src/main/java/me/ag2s/epublib/domain/ResourceReference.java
  23. 689
      epublib/src/main/java/me/ag2s/epublib/domain/Resources.java
  24. 21
      epublib/src/main/java/me/ag2s/epublib/domain/Spine.java
  25. 74
      epublib/src/main/java/me/ag2s/epublib/domain/SpineReference.java
  26. 9
      epublib/src/main/java/me/ag2s/epublib/domain/TOCReference.java
  27. 25
      epublib/src/main/java/me/ag2s/epublib/domain/TableOfContents.java
  28. 148
      epublib/src/main/java/me/ag2s/epublib/domain/TitledResourceReference.java
  29. 4
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessor.java
  30. 18
      epublib/src/main/java/me/ag2s/epublib/epub/BookProcessorPipeline.java
  31. 49
      epublib/src/main/java/me/ag2s/epublib/epub/DOMUtil.java
  32. 215
      epublib/src/main/java/me/ag2s/epublib/epub/EpubProcessorSupport.java
  33. 289
      epublib/src/main/java/me/ag2s/epublib/epub/EpubReader.java
  34. 60
      epublib/src/main/java/me/ag2s/epublib/epub/EpubWriter.java
  35. 2
      epublib/src/main/java/me/ag2s/epublib/epub/HtmlProcessor.java
  36. 5
      epublib/src/main/java/me/ag2s/epublib/epub/Main.java
  37. 296
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocument.java
  38. 574
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV2.java
  39. 376
      epublib/src/main/java/me/ag2s/epublib/epub/NCXDocumentV3.java
  40. 9
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java
  41. 49
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataReader.java
  42. 44
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java
  43. 94
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentReader.java
  44. 407
      epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentWriter.java
  45. 276
      epublib/src/main/java/me/ag2s/epublib/epub/ResourcesLoader.java
  46. 15
      epublib/src/main/java/me/ag2s/epublib/util/CollectionUtil.java
  47. 34
      epublib/src/main/java/me/ag2s/epublib/util/IOUtil.java
  48. 3
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseOutputStream.java
  49. 5
      epublib/src/main/java/me/ag2s/epublib/util/NoCloseWriter.java
  50. 293
      epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java
  51. 202
      epublib/src/main/java/me/ag2s/epublib/util/StreamWriterDelegate.txt
  52. 465
      epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java
  53. 6
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/BOMInputStream.java
  54. 1
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ByteOrderMark.java
  55. 8
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/ProxyInputStream.java
  56. 21
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReader.java
  57. 1
      epublib/src/main/java/me/ag2s/epublib/util/commons/io/XmlStreamReaderException.java

@ -120,7 +120,7 @@ class EpubFile(var book: Book) {
} }
resources.add(resource) resources.add(resource)
} while (zipEntry != null) } while (zipEntry != null)
if (resources.size() > 0) return EpubReader().readEpub(resources) if (resources.size() > 0) return EpubReader().readEpubBook(resources)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }

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

@ -6,7 +6,7 @@ public interface Constants {
String CHARACTER_ENCODING = "UTF-8"; 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 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 NAMESPACE_XHTML = "http://www.w3.org/1999/xhtml";
String EPUB4J_GENERATOR_NAME = "EpubLib"; String EPUB_GENERATOR_NAME = "Ag2S EpubLib";
char FRAGMENT_SEPARATOR_CHAR = '#'; char FRAGMENT_SEPARATOR_CHAR = '#';
String DEFAULT_TOC_ID = "toc"; String DEFAULT_TOC_ID = "toc";
} }

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

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

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

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

@ -1,79 +1,89 @@
package me.ag2s.epublib.domain; package me.ag2s.epublib.domain;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Represents one of the authors of the book * Represents one of the authors of the book
* *
* @author paul * @author paul
*
*/ */
public class Author implements Serializable { public class Author implements Serializable {
private static final long serialVersionUID = 6663408501416574200L; private static final long serialVersionUID = 6663408501416574200L;
private String firstname; private String firstname;
private String lastname; private String lastname;
private Relator relator = Relator.AUTHOR; private Relator relator = Relator.AUTHOR;
public Author(String singleName) { public Author(String singleName) {
this("", singleName); this("", singleName);
} }
public Author(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public Author(String firstname, String lastname) { public String getFirstname() {
this.firstname = firstname; return firstname;
this.lastname = lastname; }
}
public String getFirstname() { public void setFirstname(String firstname) {
return firstname; this.firstname = firstname;
} }
public void setFirstname(String firstname) { public String getLastname() {
this.firstname = firstname; return lastname;
} }
public String getLastname() { public void setLastname(String lastname) {
return lastname; this.lastname = lastname;
} }
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String toString() { @Override
return lastname + ", " + firstname; @SuppressWarnings("NullableProblems")
} public String toString() {
return this.lastname + ", " + this.firstname;
}
public int hashCode() { public int hashCode() {
return StringUtil.hashCode(firstname, lastname); return StringUtil.hashCode(firstname, lastname);
} }
public boolean equals(Object authorObject) { public boolean equals(Object authorObject) {
if (!(authorObject instanceof Author)) { if (!(authorObject instanceof Author)) {
return false; 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); * 设置贡献者的角色
} *
* @param code 角色编号
public Relator setRole(String code) { */
Relator result = Relator.byCode(code);
if (result == null) { public void setRole(String code) {
result = Relator.AUTHOR; Relator result = Relator.byCode(code);
if (result == null) {
result = Relator.AUTHOR;
}
this.relator = result;
} }
this.relator = result;
return result;
}
public Relator getRelator() { public Relator getRelator() {
return relator; return relator;
} }
public void setRelator(Relator relator) { public void setRelator(Relator relator) {
this.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; package me.ag2s.epublib.domain;
import me.ag2s.epublib.epub.PackageDocumentBase; import me.ag2s.epublib.epub.PackageDocumentBase;
import java.io.Serializable; import java.io.Serializable;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale; import java.util.Locale;
/** /**
* A Date used by the book's metadata. * A Date used by the book's metadata.
* * <p>
* Examples: creation-date, modification-date, etc * Examples: creation-date, modification-date, etc
* *
* @author paul * @author paul
*
*/ */
public class Date implements Serializable { public class Date implements Serializable {
private static final long serialVersionUID = 7533866830395120136L; private static final long serialVersionUID = 7533866830395120136L;
public enum Event { public enum Event {
PUBLICATION("publication"), PUBLICATION("publication"),
MODIFICATION("modification"), MODIFICATION("modification"),
CREATION("creation"); CREATION("creation");
private final String value; private final String value;
Event(String v) { Event(String v) {
value = v; value = v;
} }
public static Event fromValue(String v) { public static Event fromValue(String v) {
for (Event c : Event.values()) { for (Event c : Event.values()) {
if (c.value.equals(v)) { if (c.value.equals(v)) {
return c; return c;
}
}
return null;
} }
}
return null;
}
public String toString() { @Override
return value; @SuppressWarnings("NullableProblems")
public String toString() {
return value;
}
} }
}
private Event event; private Event event;
private String dateString; private String dateString;
public Date(java.util.Date date) { public Date(java.util.Date date) {
this(date, (Event) null); this(date, (Event) null);
} }
public Date(String dateString) { public Date(String dateString) {
this(dateString, (Event) null); this(dateString, (Event) null);
} }
public Date(java.util.Date date, Event event) { public Date(java.util.Date date, Event event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date), this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event); event);
} }
public Date(String dateString, Event event) { public Date(String dateString, Event event) {
this.dateString = dateString; this.dateString = dateString;
this.event = event; this.event = event;
} }
public Date(java.util.Date date, String event) { public Date(java.util.Date date, String event) {
this((new SimpleDateFormat(PackageDocumentBase.dateFormat,Locale.US)).format(date), this((new SimpleDateFormat(PackageDocumentBase.dateFormat, Locale.US)).format(date),
event); event);
} }
public Date(String dateString, String event) { public Date(String dateString, String event) {
this(checkDate(dateString), Event.fromValue(event)); this(checkDate(dateString), Event.fromValue(event));
this.dateString = dateString; this.dateString = dateString;
} }
private static String checkDate(String dateString) { private static String checkDate(String dateString) {
if (dateString == null) { if (dateString == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot create a date from a blank string"); "Cannot create a date from a blank string");
}
return dateString;
} }
return dateString;
}
public String getValue() { public String getValue() {
return dateString; return dateString;
} }
public Event getEvent() { public Event getEvent() {
return event; return event;
} }
public void setEvent(Event event) { public void setEvent(Event event) {
this.event = event; this.event = event;
} }
public String toString() { @Override
if (event == null) { @SuppressWarnings("NullableProblems")
return dateString; public String toString() {
if (event == null) {
return dateString;
}
return "" + event + ":" + dateString;
} }
return "" + event + ":" + dateString;
}
} }

@ -1,316 +1,9 @@
package me.ag2s.epublib.domain; 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. * 这个是用于其它与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
*/ */
public class EpubBook implements Serializable { @SuppressWarnings("unused declaration")
public class EpubBook extends Book {
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;
}
} }

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

@ -20,7 +20,7 @@ public class GuideReference extends TitledResourceReference
/** /**
* the book cover(s), jacket information, etc. * 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 * human-readable page with title, author, publisher, and other metadata

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

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

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

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

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

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

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

@ -27,9 +27,9 @@ public class Resource implements Serializable {
private String properties; private String properties;
protected String originalHref; protected final String originalHref;
private MediaType mediaType; private MediaType mediaType;
private String inputEncoding = Constants.CHARACTER_ENCODING; private String inputEncoding;
protected byte[] data; protected byte[] data;
/** /**
@ -147,7 +147,7 @@ public class Resource implements Serializable {
* *
* @return The contents of the Resource. * @return The contents of the Resource.
* *
* @throws IOException * @throws IOException IOException
*/ */
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(getData()); return new ByteArrayInputStream(getData());
@ -174,7 +174,7 @@ public class Resource implements Serializable {
* Sets the data of the Resource. * 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. * 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) { public void setData(byte[] data) {
this.data = 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. * 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) { public void setId(String id) {
this.id = id; this.id = id;
@ -233,7 +233,7 @@ public class Resource implements Serializable {
/** /**
* Sets the Resource's href. * Sets the Resource's href.
* *
* @param href * @param href Resource's href.
*/ */
public void setHref(String href) { public void setHref(String href) {
this.href = href; this.href = href;
@ -252,7 +252,7 @@ public class Resource implements Serializable {
/** /**
* Sets the Resource's input character encoding. * Sets the Resource's input character encoding.
* *
* @param encoding * @param encoding Resource's input character encoding.
*/ */
public void setInputEncoding(String encoding) { public void setInputEncoding(String encoding) {
this.inputEncoding = 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. * 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. * @return the contents of the Resource as Reader.
* @throws IOException * @throws IOException IOException
*/ */
public Reader getReader() throws IOException { public Reader getReader() throws IOException {
return new XmlStreamReader(new ByteArrayInputStream(getData()), return new XmlStreamReader(new ByteArrayInputStream(getData()),
@ -315,7 +315,7 @@ public class Resource implements Serializable {
public void setProperties(String properties) { public void setProperties(String properties) {
this.properties = properties; this.properties = properties;
} }
@SuppressWarnings("NullableProblems")
public String toString() { public String toString() {
return StringUtil.toString("id", id, return StringUtil.toString("id", id,
"title", title, "title", title,

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

@ -2,6 +2,7 @@ package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants; import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -15,373 +16,387 @@ import java.util.Map;
* XHTML files, images and epub xml documents must be here. * XHTML files, images and epub xml documents must be here.
* *
* @author paul * @author paul
*
*/ */
public class Resources implements Serializable { public class Resources implements Serializable {
private static final long serialVersionUID = 2450876953383871451L; private static final long serialVersionUID = 2450876953383871451L;
private static final String IMAGE_PREFIX = "image_"; private static final String IMAGE_PREFIX = "image_";
private static final String ITEM_PREFIX = "item_"; private static final String ITEM_PREFIX = "item_";
private int lastId = 1; private int lastId = 1;
private Map<String, Resource> resources = new HashMap<>(); private Map<String, Resource> resources = new HashMap<>();
/** /**
* Adds a resource to the resources. * Adds a resource to the resources.
* * <p>
* Fixes the resources id and href if necessary. * Fixes the resources id and href if necessary.
* *
* @param resource * @param resource resource
* @return the newly added resource * @return the newly added resource
*/ */
public Resource add(Resource resource) { public Resource add(Resource resource) {
fixResourceHref(resource); fixResourceHref(resource);
fixResourceId(resource); fixResourceId(resource);
this.resources.put(resource.getHref(), resource); this.resources.put(resource.getHref(), resource);
return 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, '/');
} }
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)) { * Check if the id is a valid identifier. if not: prepend with valid identifier
resourceId = createUniqueResourceId(resource); *
* @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);
} private String getResourceItemPrefix(Resource resource) {
String result;
/** if (MediaTypes.isBitmapImage(resource.getMediaType())) {
* Check if the id is a valid identifier. if not: prepend with valid identifier result = IMAGE_PREFIX;
* } else {
* @param resource result = ITEM_PREFIX;
* @return a valid id }
*/ return result;
private String makeValidId(String resourceId, Resource resource) {
if (StringUtil.isNotBlank(resourceId) && !Character
.isJavaIdentifierStart(resourceId.charAt(0))) {
resourceId = getResourceItemPrefix(resource) + resourceId;
} }
return resourceId;
} /**
* Creates a new resource id that is guaranteed to be unique for this set of Resources
private String getResourceItemPrefix(Resource resource) { *
String result; * @param resource resource
if (MediaTypes.isBitmapImage(resource.getMediaType())) { * @return a new resource id that is guaranteed to be unique for this set of Resources
result = IMAGE_PREFIX; */
} else { private String createUniqueResourceId(Resource resource) {
result = ITEM_PREFIX; 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;
} /**
* Whether the map of resources already contains a resource with the given id.
/** *
* Creates a new resource id that is guaranteed to be unique for this set of Resources * @param id id
* * @return Whether the map of resources already contains a resource with the given id.
* @param resource */
* @return a new resource id that is guaranteed to be unique for this set of Resources public boolean containsId(String id) {
*/ if (StringUtil.isBlank(id)) {
private String createUniqueResourceId(Resource resource) { return false;
int counter = lastId; }
if (counter == Integer.MAX_VALUE) { for (Resource resource : resources.values()) {
if (resources.size() == Integer.MAX_VALUE) { if (id.equals(resource.getId())) {
throw new IllegalArgumentException( return true;
"Resources contains " + Integer.MAX_VALUE }
+ " elements: no new elements can be added"); }
} else { return false;
counter = 1;
}
} }
String prefix = getResourceItemPrefix(resource);
String result = prefix + counter; /**
while (containsId(result)) { * Gets the resource with the given id.
result = prefix + (++counter); *
* @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; public Resource getByProperties(String properties) {
} if (StringUtil.isBlank(properties)) {
return null;
/** }
* Whether the map of resources already contains a resource with the given id. for (Resource resource : resources.values()) {
* if (properties.equals(resource.getProperties())) {
* @param id return resource;
* @return Whether the map of resources already contains a resource with the given id. }
*/ }
public boolean containsId(String id) { return null;
if (StringUtil.isBlank(id)) {
return false;
} }
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;
} private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
/** && !resources.containsKey(resource.getHref())) {
* Gets the resource with the given id. return;
* }
* @param id if (StringUtil.isBlank(resource.getHref())) {
* @return null if not found if (resource.getMediaType() == null) {
*/ throw new IllegalArgumentException(
public Resource getById(String id) { "Resource must have either a MediaType or a href");
if (StringUtil.isBlank(id)) { }
return null; 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())) { private String createHref(MediaType mediaType, int counter) {
return resource; if (MediaTypes.isBitmapImage(mediaType)) {
} return "image_" + counter + mediaType.getDefaultExtension();
} else {
return "item_" + counter + mediaType.getDefaultExtension();
}
} }
return null;
}
public Resource getByProperties(String properties) { public boolean isEmpty() {
if (StringUtil.isBlank(properties)) { return resources.isEmpty();
return null;
} }
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;
} /**
* The resources that make up this book.
/** * Resources can be xhtml pages, images, xml documents, etc.
* Remove the resource with the given href. *
* * @return The resources that make up this book.
* @param href */
* @return the removed resource, null if not found @SuppressWarnings("unused")
*/ public Map<String, Resource> getResourceMap() {
public Resource remove(String href) { return resources;
return resources.remove(href);
}
private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
&& !resources.containsKey(resource.getHref())) {
return;
} }
if (StringUtil.isBlank(resource.getHref())) {
if (resource.getMediaType() == null) { public Collection<Resource> getAll() {
throw new IllegalArgumentException( return resources.values();
"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);
} }
}
private String createHref(MediaType mediaType, int counter) {
if (MediaTypes.isBitmapImage(mediaType)) { /**
return "image_" + counter + mediaType.getDefaultExtension(); * Whether there exists a resource with the given href
} else { *
return "item_" + counter + mediaType.getDefaultExtension(); * @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));
}
} }
} /**
* Whether there exists a resource with the given href
*
public boolean isEmpty() { * @param href href
return resources.isEmpty(); * @return Whether there exists a resource with the given href
} */
@SuppressWarnings("unused")
/** public boolean containsByHref(String href) {
* The number of resources return !notContainsByHref(href);
* @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;
} }
return resources.containsKey(
StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR)); /**
} * Sets the collection of Resources to the given collection of resources
*
/** * @param resources resources
* Sets the collection of Resources to the given collection of resources */
* public void set(Collection<Resource> resources) {
* @param resources this.resources.clear();
*/ addAll(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);
} }
}
/**
/** * Adds all resources from the given Collection of resources to the existing collection.
* Sets the collection of Resources to the given collection of resources *
* * @param resources resources
* @param resources A map with as keys the resources href and as values the Resources */
*/ public void addAll(Collection<Resource> resources) {
public void set(Map<String, Resource> resources) { for (Resource resource : resources) {
this.resources = new HashMap<String, Resource>(resources); fixResourceHref(resource);
} this.resources.put(resource.getHref(), resource);
}
/**
* 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);
} }
return resource;
} /**
* 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
* Gets the resource with the given href. */
* If the given href contains a fragmentId then that fragment id will be ignored. public void set(Map<String, Resource> resources) {
* this.resources = new HashMap<>(resources);
* @param 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);
Resource result = resources.get(href);
return result; /**
} * 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.
/** *
* Gets the first resource (random order) with the give mediatype. * @param idOrHref idOrHref
* * @return the found Resource
* Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype. */
* public Resource getByIdOrHref(String idOrHref) {
* @param mediaType Resource resource = getById(idOrHref);
* @return the first resource (random order) with the give mediatype. if (resource == null) {
*/ resource = getByHref(idOrHref);
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) {
return resource; return resource;
}
} }
return null;
}
/**
/** * Gets the resource with the given href.
* All resources that have the given MediaType. * If the given href contains a fragmentId then that fragment id will be ignored.
* *
* @param mediaType * @param href href
* @return All resources that have the given MediaType. * @return null if not found.
*/ */
public List<Resource> getResourcesByMediaType(MediaType mediaType) { public Resource getByHref(String href) {
List<Resource> result = new ArrayList<Resource>(); if (StringUtil.isBlank(href)) {
if (mediaType == null) { return null;
return result; }
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
/** *
* 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
* @param mediaTypes */
* @return All Resources that match any of the given list of MediaTypes @SuppressWarnings("unused")
*/ public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) {
public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) { List<Resource> result = new ArrayList<>();
List<Resource> result = new ArrayList<Resource>(); if (mediaTypes == null) {
if (mediaTypes == null) { return result;
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); * All resource hrefs
for (Resource resource : getAll()) { *
if (mediaTypesList.contains(resource.getMediaType())) { * @return all resource hrefs
result.add(resource); */
} 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; private List<SpineReference> spineReferences;
public Spine() { public Spine() {
this(new ArrayList<SpineReference>()); this(new ArrayList<>());
} }
/** /**
* Creates a spine out of all the resources in the table of contents. * Creates a spine out of all the resources in the table of contents.
* *
* @param tableOfContents * @param tableOfContents tableOfContents
*/ */
public Spine(TableOfContents tableOfContents) { public Spine(TableOfContents tableOfContents) {
this.spineReferences = createSpineReferences( this.spineReferences = createSpineReferences(
@ -41,8 +41,8 @@ public class Spine implements Serializable {
public static List<SpineReference> createSpineReferences( public static List<SpineReference> createSpineReferences(
Collection<Resource> resources) { Collection<Resource> resources) {
List<SpineReference> result = new ArrayList<SpineReference>( List<SpineReference> result = new ArrayList<>(
resources.size()); resources.size());
for (Resource resource : resources) { for (Resource resource : resources) {
result.add(new SpineReference(resource)); result.add(new SpineReference(resource));
} }
@ -61,7 +61,7 @@ public class Spine implements Serializable {
* Gets the resource at the given index. * Gets the resource at the given index.
* Null if not found. * Null if not found.
* *
* @param index * @param index index
* @return the resource at the given index. * @return the resource at the given index.
*/ */
public Resource getResource(int index) { public Resource getResource(int index) {
@ -76,7 +76,7 @@ public class Spine implements Serializable {
* *
* Null if not found. * Null if not found.
* *
* @param resourceId * @param resourceId resourceId
* @return the first resource that has the given resourceId. * @return the first resource that has the given resourceId.
*/ */
public int findFirstResourceById(String 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. * Adds the given spineReference to the spine references and returns it.
* *
* @param spineReference * @param spineReference spineReference
* @return the given spineReference * @return the given spineReference
*/ */
public SpineReference addSpineReference(SpineReference spineReference) { public SpineReference addSpineReference(SpineReference spineReference) {
if (spineReferences == null) { if (spineReferences == null) {
this.spineReferences = new ArrayList<SpineReference>(); this.spineReferences = new ArrayList<>();
} }
spineReferences.add(spineReference); spineReferences.add(spineReference);
return spineReference; return spineReference;
@ -112,6 +112,7 @@ public class Spine implements Serializable {
* *
* @return the given spineReference * @return the given spineReference
*/ */
@SuppressWarnings("unused")
public SpineReference addResource(Resource resource) { public SpineReference addResource(Resource resource) {
return addSpineReference(new SpineReference(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. * 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. * Put it here to be sure of the expected behaviours.
* *
* @param tocResource * @param tocResource tocResource
*/ */
public void setTocResource(Resource tocResource) { public void setTocResource(Resource tocResource) {
this.tocResource = tocResource; this.tocResource = tocResource;
@ -149,7 +150,7 @@ public class Spine implements Serializable {
/** /**
* The position within the spine of the given resource. * The position within the spine of the given resource.
* *
* @param currentResource * @param currentResource currentResource
* @return something &lt; 0 if not found. * @return something &lt; 0 if not found.
* *
*/ */

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

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

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

@ -2,77 +2,87 @@ package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants; import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable; import java.io.Serializable;
public class TitledResourceReference extends ResourceReference public class TitledResourceReference extends ResourceReference
implements Serializable { implements Serializable {
private static final long serialVersionUID = 3918155020095190080L; private static final long serialVersionUID = 3918155020095190080L;
private String fragmentId; private String fragmentId;
private String title; private String title;
public TitledResourceReference(Resource resource) { /**
this(resource, null); * 这会使title为null
} *
* @param resource resource
public TitledResourceReference(Resource resource, String title) { */
this(resource, title, null); @Deprecated
} @SuppressWarnings("unused")
public TitledResourceReference(Resource resource) {
public TitledResourceReference(Resource resource, String title, this(resource, null);
String fragmentId) { }
super(resource);
this.title = title; public TitledResourceReference(Resource resource, String title) {
this.fragmentId = fragmentId; this(resource, title, null);
} }
public String getFragmentId() { public TitledResourceReference(Resource resource, String title,
return fragmentId; String fragmentId) {
} super(resource);
this.title = title;
public void setFragmentId(String fragmentId) { this.fragmentId = fragmentId;
this.fragmentId = fragmentId; }
}
public String getFragmentId() {
public String getTitle() { return fragmentId;
return title; }
}
public void setFragmentId(String fragmentId) {
public void setTitle(String title) { this.fragmentId = fragmentId;
this.title = title; }
}
public String getTitle() {
return title;
/** }
* If the fragmentId is blank it returns the resource href, otherwise
* it returns the resource href + '#' + the fragmentId. public void setTitle(String title) {
* this.title = title;
* @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)) { * If the fragmentId is blank it returns the resource href, otherwise
return resource.getHref(); * it returns the resource href + '#' + the fragmentId.
} else { *
return resource.getHref() + Constants.FRAGMENT_SEPARATOR_CHAR * @return If the fragmentId is blank it returns the resource href,
+ fragmentId; * 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; package me.ag2s.epublib.epub;
import me.ag2s.epublib.domain.EpubBook; import me.ag2s.epublib.domain.Book;
/** /**
* Post-processes a book. * Post-processes a book.
@ -16,5 +16,5 @@ public interface BookProcessor {
*/ */
BookProcessor IDENTITY_BOOKPROCESSOR = book -> book; 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 android.util.Log;
import me.ag2s.epublib.domain.Book;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import me.ag2s.epublib.domain.EpubBook;
//import io.documentnode.minilog.Logger;
/** /**
* A book processor that combines several other bookprocessors * A book processor that combines several other bookprocessors
* <p> *
* Fixes coverpage/coverimage. * Fixes coverpage/coverimage.
* Cleans up the XHTML. * Cleans up the XHTML.
* *
* @author paul.siegmann * @author paul.siegmann
*/ */
@SuppressWarnings("unused declaration")
public class BookProcessorPipeline implements BookProcessor { public class BookProcessorPipeline implements BookProcessor {
private static String TAG= BookProcessorPipeline.class.getName(); private static final String TAG= BookProcessorPipeline.class.getName();
private List<BookProcessor> bookProcessors; private List<BookProcessor> bookProcessors;
public BookProcessorPipeline() { public BookProcessorPipeline() {
@ -32,7 +30,7 @@ public class BookProcessorPipeline implements BookProcessor {
} }
@Override @Override
public EpubBook processBook(EpubBook book) { public Book processBook(Book book) {
if (bookProcessors == null) { if (bookProcessors == null) {
return book; return book;
} }
@ -40,7 +38,7 @@ public class BookProcessorPipeline implements BookProcessor {
try { try {
book = bookProcessor.processBook(book); book = bookProcessor.processBook(book);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, e.getMessage(), e); Log.e(TAG,e.getMessage(), e);
} }
} }
return book; return book;
@ -48,14 +46,14 @@ public class BookProcessorPipeline implements BookProcessor {
public void addBookProcessor(BookProcessor bookProcessor) { public void addBookProcessor(BookProcessor bookProcessor) {
if (this.bookProcessors == null) { if (this.bookProcessors == null) {
bookProcessors = new ArrayList<BookProcessor>(); bookProcessors = new ArrayList<>();
} }
this.bookProcessors.add(bookProcessor); this.bookProcessors.add(bookProcessor);
} }
public void addBookProcessors(Collection<BookProcessor> bookProcessors) { public void addBookProcessors(Collection<BookProcessor> bookProcessors) {
if (this.bookProcessors == null) { if (this.bookProcessors == null) {
this.bookProcessors = new ArrayList<BookProcessor>(); this.bookProcessors = new ArrayList<>();
} }
this.bookProcessors.addAll(bookProcessors); 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. * 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 element element
* @param namespace * @param namespace namespace
* @param attribute * @param attribute attribute
* @return * @return String Attribute
*/ */
public static String getAttribute(Element element, String namespace, public static String getAttribute(Element element, String namespace,
String attribute) { 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. * 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 parentElement parentElement
* @param namespace * @param namespace namespace
* @param tagname * @param tagName tagName
* @return * @return List<String>
*/ */
public static List<String> getElementsTextChild(Element parentElement, public static List<String> getElementsTextChild(Element parentElement,
String namespace, String tagname) { String namespace, String tagName) {
NodeList elements = parentElement NodeList elements = parentElement
.getElementsByTagNameNS(namespace, tagname); .getElementsByTagNameNS(namespace, tagName);
List<String> result = new ArrayList<String>(elements.getLength()); //ArrayList 初始化时指定长度提高性能
List<String> result = new ArrayList<>(elements.getLength());
for (int i = 0; i < elements.getLength(); i++) { for (int i = 0; i < elements.getLength(); i++) {
result.add(getTextChildrenContent((Element) elements.item(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. * 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. * It then returns the value of the given resultAttributeName.
* *
* @param document * @param document document
* @param namespace * @param namespace namespace
* @param elementName * @param elementName elementName
* @param findAttributeName * @param findAttributeName findAttributeName
* @param findAttributeValue * @param findAttributeValue findAttributeValue
* @param resultAttributeName * @param resultAttributeName resultAttributeName
* @return * @return String value
*/ */
public static String getFindAttributeValue(Document document, public static String getFindAttributeValue(Document document,
String namespace, String elementName, String findAttributeName, 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 * Gets the first element that is a child of the parentElement and has the given namespace and tagName
* *
* @param parentElement * @param parentElement parentElement
* @param namespace * @param namespace namespace
* @param tagName * @param tagName tagName
* @return * @return Element
*/ */
public static Element getFirstElementByTagNameNS(Element parentElement, public static Element getFirstElementByTagNameNS(Element parentElement,
String namespace, String tagName) { 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 * when the text is Chinese characters then on Android each Characater is represented in the DOM as
* an individual Text node. * an individual Text node.
* *
* @param parentElement * @param parentElement parentElement
* @return * @return String value
*/ */
public static String getTextChildrenContent(Element parentElement) { public static String getTextChildrenContent(Element parentElement) {
if (parentElement == null) { if (parentElement == null) {

@ -3,7 +3,7 @@ package me.ag2s.epublib.epub;
import android.util.Log; import android.util.Log;
import me.ag2s.epublib.Constants; import me.ag2s.epublib.Constants;
//import io.documentnode.minilog.Logger;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -11,13 +11,14 @@ import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.net.URL; import java.net.URL;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
//import org.kxml2.io.KXmlSerializer;
import org.xml.sax.EntityResolver; import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
@ -25,114 +26,114 @@ import org.xmlpull.v1.XmlSerializer;
* Various low-level support methods for reading/writing epubs. * Various low-level support methods for reading/writing epubs.
* *
* @author paul.siegmann * @author paul.siegmann
*
*/ */
public class EpubProcessorSupport { public class EpubProcessorSupport {
private static String TAG= EpubProcessorSupport.class.getName(); private static final String TAG = EpubProcessorSupport.class.getName();
protected static DocumentBuilderFactory documentBuilderFactory; protected static DocumentBuilderFactory documentBuilderFactory;
static { static {
init(); init();
} }
static class EntityResolverImpl implements EntityResolver { static class EntityResolverImpl implements EntityResolver {
private String previousLocation; private String previousLocation;
@Override @Override
public InputSource resolveEntity(String publicId, String systemId) public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException { throws IOException {
String resourcePath; String resourcePath;
if (systemId.startsWith("http:")) { if (systemId.startsWith("http:")) {
URL url = new URL(systemId); URL url = new URL(systemId);
resourcePath = "dtd/" + url.getHost() + url.getPath(); resourcePath = "dtd/" + url.getHost() + url.getPath();
previousLocation = resourcePath previousLocation = resourcePath
.substring(0, resourcePath.lastIndexOf('/')); .substring(0, resourcePath.lastIndexOf('/'));
} else { } else {
resourcePath = resourcePath =
previousLocation + systemId.substring(systemId.lastIndexOf('/')); previousLocation + systemId.substring(systemId.lastIndexOf('/'));
} }
if (this.getClass().getClassLoader().getResource(resourcePath) == null) { if (Objects.requireNonNull(this.getClass().getClassLoader()).getResource(resourcePath) == null) {
throw new RuntimeException( throw new RuntimeException(
"remote resource is not cached : [" + systemId "remote resource is not cached : [" + systemId
+ "] cannot continue"); + "] cannot continue");
} }
InputStream in = EpubProcessorSupport.class.getClassLoader() InputStream in = Objects.requireNonNull(EpubProcessorSupport.class.getClassLoader())
.getResourceAsStream(resourcePath); .getResourceAsStream(resourcePath);
return new InputSource(in); return new InputSource(in);
}
} }
}
private static void init() {
private static void init() { EpubProcessorSupport.documentBuilderFactory = DocumentBuilderFactory
EpubProcessorSupport.documentBuilderFactory = DocumentBuilderFactory .newInstance();
.newInstance(); documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setValidating(false);
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());
} }
return result;
} public static XmlSerializer createXmlSerializer(OutputStream out)
throws UnsupportedEncodingException {
/** return createXmlSerializer(
* Gets an EntityResolver that loads dtd's and such from the epub4j classpath. new OutputStreamWriter(out, Constants.CHARACTER_ENCODING));
* 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. public static XmlSerializer createXmlSerializer(Writer out) {
* XmlSerializer result = null;
* @return an EntityResolver that loads dtd's and such from the epub4j classpath. try {
*/ /*
public static EntityResolver getEntityResolver() { * Disable XmlPullParserFactory here before it doesn't work when
return new EntityResolverImpl(); * building native image using GraalVM
} */
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
public DocumentBuilderFactory getDocumentBuilderFactory() { factory.setValidating(true);
return documentBuilderFactory; result = factory.newSerializer();
}
//result = new KXmlSerializer();
/** result.setFeature(
* Creates a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath. "http://xmlpull.org/v1/doc/features.html#indent-output", true);
* result.setOutput(out);
* @return a DocumentBuilder that looks up dtd's and schema's from epub4j's classpath. } catch (Exception e) {
*/ Log.e(TAG,
public static DocumentBuilder createDocumentBuilder() { "When creating XmlSerializer: " + e.getClass().getName() + ": " + e
DocumentBuilder result = null; .getMessage());
try { }
result = documentBuilderFactory.newDocumentBuilder(); return result;
result.setEntityResolver(getEntityResolver()); }
} catch (ParserConfigurationException e) {
Log.e(TAG,e.getMessage()); /**
* 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,6 +13,7 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import me.ag2s.epublib.Constants; import me.ag2s.epublib.Constants;
import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.EpubBook; import me.ag2s.epublib.domain.EpubBook;
import me.ag2s.epublib.domain.MediaType; import me.ag2s.epublib.domain.MediaType;
import me.ag2s.epublib.domain.MediaTypes; import me.ag2s.epublib.domain.MediaTypes;
@ -21,150 +22,160 @@ import me.ag2s.epublib.domain.Resources;
import me.ag2s.epublib.util.ResourceUtil; import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger;
/** /**
* Reads an epub file. * Reads an epub file.
* *
* @author paul * @author paul
*
*/ */
public class EpubReader { public class EpubReader {
private static String TAG= EpubReader.class.getName(); private static final String TAG = EpubReader.class.getName();
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR; private final BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR;
@SuppressWarnings("unused")
public EpubBook readEpub(InputStream in) throws IOException { public Book readEpub(InputStream in) throws IOException {
return readEpub(in, Constants.CHARACTER_ENCODING); return readEpub(in, Constants.CHARACTER_ENCODING);
} }
public EpubBook readEpub(ZipInputStream in) throws IOException { @SuppressWarnings("unused")
return readEpub(in, Constants.CHARACTER_ENCODING); public Book readEpub(ZipInputStream in) throws IOException {
} return readEpub(in, Constants.CHARACTER_ENCODING);
}
public EpubBook readEpub(ZipFile zipfile) throws IOException {
return readEpub(zipfile, 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 * Read epub from inputstream
* @param encoding the encoding to use for the html files within the epub *
* @return the Book as read from the inputstream * @param in the inputstream from which to read the epub
* @throws IOException * @param encoding the encoding to use for the html files within the epub
*/ * @return the Book as read from the inputstream
public EpubBook readEpub(InputStream in, String encoding) throws IOException { * @throws IOException IOException
return readEpub(new ZipInputStream(in), encoding); */
} 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 * Reads this EPUB without loading any resources into memory.
* @param encoding the encoding for XHTML files *
* @return this Book without loading all resources into memory. * @param zipFile the file to load
* @throws IOException * @param encoding the encoding for XHTML files
*/ * @return this Book without loading all resources into memory.
public EpubBook readEpubLazy(ZipFile zipFile, String encoding) * @throws IOException IOException
throws IOException { */
return readEpubLazy(zipFile, encoding, @SuppressWarnings("unused")
Arrays.asList(MediaTypes.mediaTypes)); public Book readEpubLazy(ZipFile zipFile, String encoding)
} throws IOException {
return readEpubLazy(zipFile, encoding,
public EpubBook readEpub(ZipInputStream in, String encoding) throws IOException { Arrays.asList(MediaTypes.mediaTypes));
return readEpub(ResourcesLoader.loadResources(in, encoding)); }
}
public Book readEpub(ZipInputStream in, String encoding) throws IOException {
public EpubBook readEpub(ZipFile in, String encoding) throws IOException { return readEpub(ResourcesLoader.loadResources(in, encoding));
return readEpub(ResourcesLoader.loadResources(in, encoding)); }
}
public Book 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 * Reads this EPUB without loading all resources into memory.
* @param lazyLoadedTypes a list of the MediaType to load lazily *
* @return this Book without loading all resources into memory. * @param zipFile the file to load
* @throws IOException * @param encoding the encoding for XHTML files
*/ * @param lazyLoadedTypes a list of the MediaType to load lazily
public EpubBook readEpubLazy(ZipFile zipFile, String encoding, * @return this Book without loading all resources into memory.
List<MediaType> lazyLoadedTypes) throws IOException { * @throws IOException IOException
Resources resources = ResourcesLoader */
.loadResources(zipFile, encoding, lazyLoadedTypes); public Book readEpubLazy(ZipFile zipFile, String encoding,
return readEpub(resources); List<MediaType> lazyLoadedTypes) throws IOException {
} Resources resources = ResourcesLoader
.loadResources(zipFile, encoding, lazyLoadedTypes);
public EpubBook readEpub(Resources resources) throws IOException { return readEpub(resources);
return readEpub(resources, new EpubBook()); }
}
@SuppressWarnings("unused")
public EpubBook readEpub(Resources resources, EpubBook result) throws IOException { public Book readEpub(Resources resources) {
if (result == null) { return readEpub(resources, new Book());
result = new EpubBook(); }
} public EpubBook readEpubBook(Resources resources) {
handleMimeType(result, resources); return (EpubBook) readEpub(resources, new Book());
String packageResourceHref = getPackageResourceHref(resources); }
Resource packageResource = processPackageResource(packageResourceHref,
result, resources); public Book readEpub(Resources resources, Book result) {
result.setOpfResource(packageResource); if (result == null) {
Resource ncxResource = processNcxResource(packageResource, result); result = new Book();
result.setNcxResource(ncxResource); }
result = postProcessBook(result); handleMimeType(result, resources);
return result; String packageResourceHref = getPackageResourceHref(resources);
} Resource packageResource = processPackageResource(packageResourceHref,
result, resources);
result.setOpfResource(packageResource);
private EpubBook postProcessBook(EpubBook book) { Resource ncxResource = processNcxResource(packageResource, result);
if (bookProcessor != null) { result.setNcxResource(ncxResource);
book = bookProcessor.processBook(book); result = postProcessBook(result);
} return result;
return book; }
}
private Resource processNcxResource(Resource packageResource, EpubBook book) { private Book postProcessBook(Book book) {
Log.d(TAG, "OPF:getHref()" + packageResource.getHref()); if (bookProcessor != null) {
book = bookProcessor.processBook(book);
return NCXDocument.read(book, this); }
} return book;
}
private Resource processPackageResource(String packageResourceHref, EpubBook book,
Resources resources) { private Resource processNcxResource(Resource packageResource, Book book) {
Resource packageResource = resources.remove(packageResourceHref); Log.d(TAG, "OPF:getHref()" + packageResource.getHref());
try { if (book.isEpub3()) {
PackageDocumentReader.read(packageResource, this, book, resources); return NCXDocumentV3.read(book, this);
} catch (Exception e) { } else {
Log.e(TAG,e.getMessage(), e); return NCXDocumentV2.read(book, this);
} }
return packageResource;
} }
private String getPackageResourceHref(Resources resources) { private Resource processPackageResource(String packageResourceHref, Book book,
String defaultResult = "OEBPS/content.opf"; Resources resources) {
String result = defaultResult; Resource packageResource = resources.remove(packageResourceHref);
try {
Resource containerResource = resources.remove("META-INF/container.xml"); PackageDocumentReader.read(packageResource, this, book, resources);
if (containerResource == null) { } catch (Exception e) {
return result; Log.e(TAG, e.getMessage(), e);
} }
try { return packageResource;
Document document = ResourceUtil.getAsDocument(containerResource); }
Element rootFileElement = (Element) ((Element) document
.getDocumentElement().getElementsByTagName("rootfiles").item(0)) private String getPackageResourceHref(Resources resources) {
.getElementsByTagName("rootfile").item(0); String defaultResult = "OEBPS/content.opf";
result = rootFileElement.getAttribute("full-path"); String result = defaultResult;
} catch (Exception e) {
Log.e(TAG,e.getMessage(), e); Resource containerResource = resources.remove("META-INF/container.xml");
} if (containerResource == null) {
if (StringUtil.isBlank(result)) { return result;
result = defaultResult; }
} try {
return result; Document document = ResourceUtil.getAsDocument(containerResource);
} Element rootFileElement = (Element) ((Element) document
.getDocumentElement().getElementsByTagName("rootfiles").item(0))
private void handleMimeType(EpubBook result, Resources resources) { .getElementsByTagName("rootfile").item(0);
resources.remove("mimetype"); 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 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -12,13 +14,7 @@ import java.io.Writer;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import org.xmlpull.v1.XmlSerializer;
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;
/** /**
* Generates an epub file. Not thread-safe, single use object. * Generates an epub file. Not thread-safe, single use object.
@ -27,12 +23,12 @@ import me.ag2s.epublib.util.IOUtil;
*/ */
public class EpubWriter { public class EpubWriter {
private static String TAG= EpubWriter.class.getName(); private static final String TAG= EpubWriter.class.getName();
// package // package
static final String EMPTY_NAMESPACE_PREFIX = ""; static final String EMPTY_NAMESPACE_PREFIX = "";
private BookProcessor bookProcessor = BookProcessor.IDENTITY_BOOKPROCESSOR; private BookProcessor bookProcessor;
public EpubWriter() { public EpubWriter() {
this(BookProcessor.IDENTITY_BOOKPROCESSOR); 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); book = processBook(book);
ZipOutputStream resultStream = new ZipOutputStream(out); ZipOutputStream resultStream = new ZipOutputStream(out);
writeMimeType(resultStream); writeMimeType(resultStream);
@ -55,17 +51,22 @@ public class EpubWriter {
resultStream.close(); resultStream.close();
} }
private EpubBook processBook(EpubBook book) { private Book processBook(Book book) {
if (bookProcessor != null) { if (bookProcessor != null) {
book = bookProcessor.processBook(book); book = bookProcessor.processBook(book);
} }
return book; return book;
} }
private void initTOCResource(EpubBook book) { private void initTOCResource(Book book) {
Resource tocResource; Resource tocResource;
try { try {
tocResource = NCXDocument.createNCXResource(book); if(book.isEpub3()){
tocResource = NCXDocumentV3.createNCXResource(book);
}else {
tocResource = NCXDocumentV2.createNCXResource(book);
}
Resource currentTocResource = book.getSpine().getTocResource(); Resource currentTocResource = book.getSpine().getTocResource();
if (currentTocResource != null) { if (currentTocResource != null) {
book.getResources().remove(currentTocResource.getHref()); book.getResources().remove(currentTocResource.getHref());
@ -80,8 +81,7 @@ public class EpubWriter {
} }
private void writeResources(EpubBook book, ZipOutputStream resultStream) private void writeResources(Book book, ZipOutputStream resultStream) {
throws IOException {
for (Resource resource : book.getResources().getAll()) { for (Resource resource : book.getResources().getAll()) {
writeResource(resource, resultStream); writeResource(resource, resultStream);
} }
@ -90,12 +90,10 @@ public class EpubWriter {
/** /**
* Writes the resource to the resultStream. * Writes the resource to the resultStream.
* *
* @param resource * @param resource resource
* @param resultStream * @param resultStream resultStream
* @throws IOException
*/ */
private void writeResource(Resource resource, ZipOutputStream resultStream) private void writeResource(Resource resource, ZipOutputStream resultStream) {
throws IOException {
if (resource == null) { if (resource == null) {
return; return;
} }
@ -110,11 +108,11 @@ public class EpubWriter {
} }
private void writePackageDocument(EpubBook book, ZipOutputStream resultStream) private void writePackageDocument(Book book, ZipOutputStream resultStream)
throws IOException { throws IOException {
resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf")); resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf"));
XmlSerializer xmlSerializer = EpubProcessorSupport XmlSerializer xmlSerializer = EpubProcessorSupport
.createXmlSerializer(resultStream); .createXmlSerializer(resultStream);
PackageDocumentWriter.write(this, xmlSerializer, book); PackageDocumentWriter.write(this, xmlSerializer, book);
xmlSerializer.flush(); xmlSerializer.flush();
// String resultAsString = result.toString(); // String resultAsString = result.toString();
@ -124,8 +122,8 @@ public class EpubWriter {
/** /**
* Writes the META-INF/container.xml file. * Writes the META-INF/container.xml file.
* *
* @param resultStream * @param resultStream resultStream
* @throws IOException * @throws IOException IOException
*/ */
private void writeContainer(ZipOutputStream resultStream) throws IOException { private void writeContainer(ZipOutputStream resultStream) throws IOException {
resultStream.putNextEntry(new ZipEntry("META-INF/container.xml")); 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. * Stores the mimetype as an uncompressed file in the ZipOutputStream.
* *
* @param resultStream * @param resultStream resultStream
* @throws IOException * @throws IOException IOException
*/ */
private void writeMimeType(ZipOutputStream resultStream) throws IOException { private void writeMimeType(ZipOutputStream resultStream) throws IOException {
ZipEntry mimetypeZipEntry = new ZipEntry("mimetype"); ZipEntry mimetypeZipEntry = new ZipEntry("mimetype");
@ -175,11 +173,13 @@ public class EpubWriter {
return MediaTypes.NCX.getName(); return MediaTypes.NCX.getName();
} }
@SuppressWarnings("unused")
public BookProcessor getBookProcessor() { public BookProcessor getBookProcessor() {
return bookProcessor; return bookProcessor;
} }
@SuppressWarnings("unused")
public void setBookProcessor(BookProcessor bookProcessor) { public void setBookProcessor(BookProcessor bookProcessor) {
this.bookProcessor = bookProcessor; this.bookProcessor = bookProcessor;
} }

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

@ -6,79 +6,114 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import me.ag2s.epublib.Constants; 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.Resource;
import me.ag2s.epublib.domain.TOCReference; import me.ag2s.epublib.domain.TOCReference;
import me.ag2s.epublib.domain.TableOfContents; import me.ag2s.epublib.domain.TableOfContents;
import me.ag2s.epublib.util.ResourceUtil; import me.ag2s.epublib.util.ResourceUtil;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
public class NCXDocumentV3 extends NCXDocument { /**
public static final String NAMESPACE_NCX = ""; * Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
public static final String PREFIX_NCX = "html"; *
public static final String NCX_ITEM_ID = "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 DEFAULT_NCX_HREF = "toc.xhtml";
public static final String PREFIX_DTB = "dtb"; public static final String V3_NCX_PROPERTIES = "nav";
private static String TAG = NCXDocumentV3.class.getName(); public static final MediaType V3_NCX_MEDIATYPE = MediaTypes.XHTML;
private interface NCXTags { private static final String TAG = NCXDocumentV3.class.getName();
//String nav="nav";
//String li="li"; private interface XHTMLTgs {
//String ncx = "ncx"; String html = "html";
String meta = "meta";
String navPoint = "li";
String navMap = "nav";
String navLabel = "a";
String content = "a";
String text = "text";
String docTitle = "docTitle";
String docAuthor = "docAuthor";
String head = "head"; 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 { private interface XHTMLAttributes {
String xmlns = "xmlns";
String src = "href"; String xmlns_epub = "xmlns:epub";
String name = "name"; String lang = "lang";
String content = "content"; String xml_lang = "xml:lang";
String rel = "rel";
String type = "type";
String epub_type = "epub:type";//nav的必须属性
String id = "id"; String id = "id";
String playOrder = "playOrder"; String role = "role";
String clazz = "class"; String href = "href";
String version = "version"; String http_equiv = "http-equiv";
} String content = "content";
private interface NCXAttributeValues { }
String chapter = "chapter"; private interface XHTMLAttributeValues {
String version = "2007"; 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; Resource ncxResource = null;
if (book.getSpine().getTocResource() == null) { if (book.getSpine().getTocResource() == null) {
Log.e(TAG, "Book does not contain a table of contents file"); Log.e(TAG, "Book does not contain a table of contents file");
return ncxResource; return null;
} }
try { try {
ncxResource = book.getSpine().getTocResource(); ncxResource = book.getSpine().getTocResource();
if (ncxResource == null) { if (ncxResource == null) {
return ncxResource; return null;
} }
//Log.d(TAG, ncxResource.getHref()); //Log.d(TAG, ncxResource.getHref());
Document ncxDocument = ResourceUtil.getAsDocument(ncxResource); Document ncxDocument = ResourceUtil.getAsDocument(ncxResource);
//Log.d(TAG, ncxDocument.getNodeName()); //Log.d(TAG, ncxDocument.getNodeName());
Element navMapElement = (Element) ncxDocument.getElementsByTagName("nav").item(0); Element navMapElement = (Element) ncxDocument.getElementsByTagName(XHTMLTgs.nav).item(0);
navMapElement = (Element) navMapElement.getElementsByTagName("ol").item(0); navMapElement = (Element) navMapElement.getElementsByTagName(XHTMLTgs.ol).item(0);
Log.d(TAG, navMapElement.getTagName()); Log.d(TAG, navMapElement.getTagName());
TableOfContents tableOfContents = new TableOfContents( TableOfContents tableOfContents = new TableOfContents(
@ -91,46 +126,42 @@ public class NCXDocumentV3 extends NCXDocument {
return ncxResource; 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<>(); List<TOCReference> result = new ArrayList<>();
if (n == null || n.getNodeType() != Document.ELEMENT_NODE) { if (n == null || n.getNodeType() != Document.ELEMENT_NODE) {
return result; return result;
} else { } else {
Element el = (Element) n; Element el = (Element) n;
NodeList nodeList = el.getElementsByTagName("li"); NodeList nodeList = el.getElementsByTagName(XHTMLTgs.li);
for (int i = 0; i < nodeList.getLength(); i++) { for (int i = 0; i < nodeList.getLength(); i++) {
result.add(readTOCReference((Element) nodeList.item(i), book)); result.add(readTOCReference((Element) nodeList.item(i), book));
} }
} }
return result; return result;
} }
static List<TOCReference> readTOCReferences(NodeList navpoints, static List<TOCReference> readTOCReferences(NodeList navpoints,
EpubBook book) { Book book) {
if (navpoints == null) { if (navpoints == null) {
return new ArrayList<>(); return new ArrayList<>();
} }
//Log.d(TAG, "readTOCReferences:navpoints.getLength()" + navpoints.getLength()); //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++) { for (int i = 0; i < navpoints.getLength(); i++) {
Node node = navpoints.item(i); Node node = navpoints.item(i);
//如果该node是null,或者不是Element,跳出本次循环
if (node == null || node.getNodeType() != Document.ELEMENT_NODE) { if (node == null || node.getNodeType() != Document.ELEMENT_NODE) {
continue; 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); String label = readNavLabel(navpointElement);
//Log.d(TAG, "label:" + label); //Log.d(TAG, "label:" + label);
String tocResourceRoot = StringUtil String tocResourceRoot = StringUtil
@ -160,7 +192,13 @@ public class NCXDocumentV3 extends NCXDocument {
if (resource == null) { if (resource == null) {
Log.e(TAG, "Resource with href " + href + " in NCX document not found"); 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); TOCReference result = new TOCReference(label, resource, fragmentId);
//解析子级目录
List<TOCReference> childTOCReferences = doToc(navpointElement, book); List<TOCReference> childTOCReferences = doToc(navpointElement, book);
//readTOCReferences( //readTOCReferences(
//navpointElement.getChildNodes(), book); //navpointElement.getChildNodes(), book);
@ -168,13 +206,24 @@ public class NCXDocumentV3 extends NCXDocument {
return result; return result;
} }
/**
* 获取目录节点的href
*
* @param navpointElement navpointElement
* @return String
*/
private static String readNavReference(Element navpointElement) { 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()); //Log.d(TAG, "readNavReference:" + navpointElement.getTagName());
Element contentElement = DOMUtil Element contentElement = DOMUtil
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX, .getFirstElementByTagNameNS(navpointElement, "", XHTMLTgs.a);
NCXDocumentV3.NCXTags.content); if (contentElement == null) {
return null;
}
String result = DOMUtil String result = DOMUtil
.getAttribute(contentElement, NAMESPACE_NCX, NCXDocumentV3.NCXAttributes.src); .getAttribute(contentElement, "", XHTMLAttributes.href);
try { try {
result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING); result = URLDecoder.decode(result, Constants.CHARACTER_ENCODING);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
@ -185,13 +234,218 @@ public class NCXDocumentV3 extends NCXDocument {
} }
/**
* 获取目录节点里面的章节名
*
* @param navpointElement navpointElement
* @return String
*/
private static String readNavLabel(Element navpointElement) { 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()); //Log.d(TAG, "readNavLabel:" + navpointElement.getTagName());
Element navLabel = DOMUtil String label;
.getFirstElementByTagNameNS(navpointElement, NAMESPACE_NCX, Element labelElement = DOMUtil.getFirstElementByTagNameNS(navpointElement, "", "a");
NCXDocumentV3.NCXTags.navLabel); assert labelElement != null;
return navLabel.getTextContent();//DOMUtil.getTextChildrenContent(DOMUtil label = labelElement.getTextContent();
//.getFirstElementByTagNameNS(navLabel, NAMESPACE_NCX, NCXDocumentV3.NCXTags.text)); 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 NAMESPACE_DUBLIN_CORE = "http://purl.org/dc/elements/1.1/";
public static final String PREFIX_DUBLIN_CORE = "dc"; public static final String PREFIX_DUBLIN_CORE = "dc";
//public static final String PREFIX_OPF = "opf"; //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 = ""; 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 version="version";
public static final String dateFormat = "yyyy-MM-dd"; public static final String dateFormat = "yyyy-MM-dd";
@ -76,6 +79,10 @@ public class PackageDocumentBase {
String version = "version"; String version = "version";
String scheme = "scheme"; String scheme = "scheme";
String property = "property"; String property = "property";
//add for epub3
/**
* add for epub3
*/
String properties="properties"; String properties="properties";
} }

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

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

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

@ -2,16 +2,8 @@ package me.ag2s.epublib.epub;
import android.util.Log; 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.Constants;
import me.ag2s.epublib.domain.EpubBook; import me.ag2s.epublib.domain.Book;
import me.ag2s.epublib.domain.Guide; import me.ag2s.epublib.domain.Guide;
import me.ag2s.epublib.domain.GuideReference; import me.ag2s.epublib.domain.GuideReference;
import me.ag2s.epublib.domain.MediaTypes; 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.domain.SpineReference;
import me.ag2s.epublib.util.StringUtil; import me.ag2s.epublib.util.StringUtil;
//import io.documentnode.minilog.Logger; import java.io.IOException;
//import javax.xml.stream.XMLStreamException; 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 * Writes the opf package document as defined by namespace http://www.idpf.org/2007/opf
* *
* @author paul * @author paul
*
*/ */
public class PackageDocumentWriter extends PackageDocumentBase { public class PackageDocumentWriter extends PackageDocumentBase {
//private static final Logger log = Logger.create(PackageDocumentWriter.class); private static final String TAG = PackageDocumentWriter.class.getName();
private static String TAG= PackageDocumentWriter.class.getName();
public static void write(EpubWriter epubWriter, XmlSerializer serializer,
public static void write(EpubWriter epubWriter, XmlSerializer serializer, Book book) {
EpubBook book) throws IOException { try {
try { serializer.startDocument(Constants.CHARACTER_ENCODING, false);
serializer.startDocument(Constants.CHARACTER_ENCODING, false); serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF);
serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF); serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE);
serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE); serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag); serializer
serializer .attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version,
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version, book.getVersion());
"2.0"); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX,
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.uniqueIdentifier, BOOK_ID_ID);
OPFAttributes.uniqueIdentifier, BOOK_ID_ID);
PackageDocumentMetadataWriter.writeMetaData(book, serializer);
PackageDocumentMetadataWriter.writeMetaData(book, serializer);
writeManifest(book, epubWriter, serializer);
writeManifest(book, epubWriter, serializer); writeSpine(book, epubWriter, serializer);
writeSpine(book, epubWriter, serializer); writeGuide(book, epubWriter, serializer);
writeGuide(book, epubWriter, serializer);
serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag);
serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag); serializer.endDocument();
serializer.endDocument(); serializer.flush();
serializer.flush(); } catch (IOException e) {
} catch (IOException e) { e.printStackTrace();
// TODO Auto-generated catch block }
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()) * Writes the package's spine.
< 0) { // cover page is not already in the spine *
// write the cover html file * @param book e
serializer.startTag(NAMESPACE_OPF, OPFTags.itemref); * @param epubWriter g
serializer * @param serializer g
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref, * @throws IOException g
book.getCoverPage().getId()); * @throws IllegalStateException g
serializer * @throws IllegalArgumentException 1@throws XMLStreamException
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear, */
"no"); @SuppressWarnings("unused")
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref); 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, private static void writeManifest(Book book, EpubWriter epubWriter,
XmlSerializer serializer) XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException { throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(NAMESPACE_OPF, OPFTags.manifest); serializer.startTag(NAMESPACE_OPF, OPFTags.manifest);
serializer.startTag(NAMESPACE_OPF, OPFTags.item); 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);
// 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)) { serializer.endTag(NAMESPACE_OPF, OPFTags.item);
writeItem(book, resource, serializer);
}
serializer.endTag(NAMESPACE_OPF, OPFTags.manifest); // writeCoverResources(book, serializer);
}
private static List<Resource> getAllResourcesSortById(EpubBook book) { for (Resource resource : getAllResourcesSortById(book)) {
List<Resource> allResources = new ArrayList<Resource>( writeItem(book, resource, serializer);
book.getResources().getAll()); }
Collections.sort(allResources, new Comparator<Resource>() {
@Override serializer.endTag(NAMESPACE_OPF, OPFTags.manifest);
public int compare(Resource resource1, Resource resource2) { }
return resource1.getId().compareToIgnoreCase(resource2.getId());
}
});
return allResources;
}
/** private static List<Resource> getAllResourcesSortById(Book book) {
* Writes a resources as an item element List<Resource> allResources = new ArrayList<>(
* book.getResources().getAll());
* @param resource Collections.sort(allResources, (resource1, resource2) -> resource1.getId().compareToIgnoreCase(resource2.getId()));
* @param serializer return allResources;
* @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;
} }
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() // log.error("resource id must not be empty (href: " + resource.getHref()
// + ", mediatype:" + resource.getMediaType() + ")"); // + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG, "resource id must not be empty (href: " + resource.getHref() Log.e(TAG, "resource id must not be empty (href: " + resource.getHref()
+ ", mediatype:" + resource.getMediaType() + ")"); + ", mediatype:" + resource.getMediaType() + ")");
return; return;
} }
if (StringUtil.isBlank(resource.getHref())) { if (StringUtil.isBlank(resource.getHref())) {
// log.error("resource href must not be empty (id: " + resource.getId() // log.error("resource href must not be empty (id: " + resource.getId()
// + ", mediatype:" + resource.getMediaType() + ")"); // + ", mediatype:" + resource.getMediaType() + ")");
Log.e(TAG,"resource href must not be empty (id: " + resource.getId() Log.e(TAG, "resource href must not be empty (id: " + resource.getId()
+ ", mediatype:" + resource.getMediaType() + ")"); + ", mediatype:" + resource.getMediaType() + ")");
return; return;
} }
if (resource.getMediaType() == null) { if (resource.getMediaType() == null) {
// log.error("resource mediatype must not be empty (id: " + resource.getId() // log.error("resource mediatype must not be empty (id: " + resource.getId()
// + ", href:" + resource.getHref() + ")"); // + ", href:" + resource.getHref() + ")");
Log.e(TAG,"resource mediatype must not be empty (id: " + resource.getId() Log.e(TAG, "resource mediatype must not be empty (id: " + resource.getId()
+ ", href:" + resource.getHref() + ")"); + ", href:" + resource.getHref() + ")");
return; return;
} }
serializer.startTag(NAMESPACE_OPF, OPFTags.item); serializer.startTag(NAMESPACE_OPF, OPFTags.item);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id, serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id,
resource.getId()); resource.getId());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
resource.getHref()); 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()) {
serializer serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear, .attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type,
OPFValues.no); resource.getMediaType().getName());
} serializer.endTag(NAMESPACE_OPF, OPFTags.item);
serializer.endTag(NAMESPACE_OPF, OPFTags.itemref);
} }
}
private static void writeGuide(EpubBook book, EpubWriter epubWriter, /**
XmlSerializer serializer) * List all spine references
throws IllegalArgumentException, IllegalStateException, IOException { *
serializer.startTag(NAMESPACE_OPF, OPFTags.guide); * @throws IOException f
ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter, * @throws IllegalStateException f
serializer); * @throws IllegalArgumentException f
for (GuideReference reference : book.getGuide().getReferences()) { */
writeGuideReference(reference, serializer); @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, private static void writeGuide(Book book, EpubWriter epubWriter,
EpubWriter epubWriter, XmlSerializer serializer) XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException { throws IllegalArgumentException, IllegalStateException, IOException {
if (!(guide.getGuideReferencesByType(GuideReference.COVER).isEmpty())) { serializer.startTag(NAMESPACE_OPF, OPFTags.guide);
return; 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) { @SuppressWarnings("unused")
writeGuideReference( private static void ensureCoverPageGuideReferenceWritten(Guide guide,
new GuideReference(guide.getCoverPage(), GuideReference.COVER, EpubWriter epubWriter, XmlSerializer serializer)
GuideReference.COVER), 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, private static void writeGuideReference(GuideReference reference,
XmlSerializer serializer) XmlSerializer serializer)
throws IllegalArgumentException, IllegalStateException, IOException { throws IllegalArgumentException, IllegalStateException, IOException {
if (reference == null) { if (reference == null) {
return; return;
} }
serializer.startTag(NAMESPACE_OPF, OPFTags.reference); serializer.startTag(NAMESPACE_OPF, OPFTags.reference);
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type, serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type,
reference.getType()); reference.getType());
serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href,
reference.getCompleteHref()); reference.getCompleteHref());
if (StringUtil.isNotBlank(reference.getTitle())) { if (StringUtil.isNotBlank(reference.getTitle())) {
serializer serializer
.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title, .attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title,
reference.getTitle()); 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.domain.Resources;
import me.ag2s.epublib.util.CollectionUtil; import me.ag2s.epublib.util.CollectionUtil;
import me.ag2s.epublib.util.ResourceUtil; import me.ag2s.epublib.util.ResourceUtil;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -26,148 +27,147 @@ import java.util.zip.ZipInputStream;
* Loads Resources from inputStreams, ZipFiles, etc * Loads Resources from inputStreams, ZipFiles, etc
* *
* @author paul * @author paul
*
*/ */
public class ResourcesLoader { public class ResourcesLoader {
private static String TAG=ResourceUtil.class.getName(); private static final String TAG = ResourcesLoader.class.getName();
/** /**
* Loads the entries of the zipFile as resources. * Loads the entries of the zipFile as resources.
* * <p>
* The MediaTypes that are in the lazyLoadedTypes will not get their * The MediaTypes that are in the lazyLoadedTypes will not get their
* contents loaded, but are stored as references to entries into the * contents loaded, but are stored as references to entries into the
* ZipFile and are loaded on demand by the Resource system. * ZipFile and are loaded on demand by the Resource system.
* *
* @param zipFile * @param zipFile import epub zipfile
* @param defaultHtmlEncoding * @param defaultHtmlEncoding epub xhtml default encoding
* @param lazyLoadedTypes * @param lazyLoadedTypes lazyLoadedTypes
* @return * @return Resources
* @throws IOException * @throws IOException IOException
*/ */
public static Resources loadResources(ZipFile zipFile, public static Resources loadResources(ZipFile zipFile,
String defaultHtmlEncoding, String defaultHtmlEncoding,
List<MediaType> lazyLoadedTypes) throws IOException { List<MediaType> lazyLoadedTypes) throws IOException {
LazyResourceProvider resourceProvider = LazyResourceProvider resourceProvider =
new EpubResourceProvider(zipFile.getName()); new EpubResourceProvider(zipFile.getName());
Resources result = new Resources(); Resources result = new Resources();
Enumeration<? extends ZipEntry> entries = zipFile.entries(); Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement(); ZipEntry zipEntry = entries.nextElement();
if (zipEntry == null || zipEntry.isDirectory()) { if (zipEntry == null || zipEntry.isDirectory()) {
continue; continue;
} }
String href = zipEntry.getName(); String href = zipEntry.getName();
Resource resource; Resource resource;
if (shouldLoadLazy(href, lazyLoadedTypes)) { if (shouldLoadLazy(href, lazyLoadedTypes)) {
resource = new LazyResource(resourceProvider, zipEntry.getSize(), href); resource = new LazyResource(resourceProvider, zipEntry.getSize(), href);
} else { } else {
resource = ResourceUtil resource = ResourceUtil
.createResource(zipEntry, zipFile.getInputStream(zipEntry)); .createResource(zipEntry, zipFile.getInputStream(zipEntry));
} }
if (resource.getMediaType() == MediaTypes.XHTML) { if (resource.getMediaType() == MediaTypes.XHTML) {
resource.setInputEncoding(defaultHtmlEncoding); resource.setInputEncoding(defaultHtmlEncoding);
} }
result.add(resource); 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; /**
} * Loads all entries from the ZipInputStream as Resources.
* <p>
/** * Loads the contents of all ZipEntries into memory.
* Whether the given href will load a mediaType that is in the * Is fast, but may lead to memory problems when reading large books
* collection of lazilyLoadedMediaTypes. * on devices with small amounts of memory.
* *
* @param href * @param zipInputStream zipInputStream
* @param lazilyLoadedMediaTypes * @param defaultHtmlEncoding defaultHtmlEncoding
* @return Whether the given href will load a mediaType that is * @return Resources
* in the collection of lazilyLoadedMediaTypes. * @throws IOException IOException
*/ */
private static boolean shouldLoadLazy(String href, public static Resources loadResources(ZipInputStream zipInputStream,
Collection<MediaType> lazilyLoadedMediaTypes) { String defaultHtmlEncoding) throws IOException {
if (CollectionUtil.isEmpty(lazilyLoadedMediaTypes)) { Resources result = new Resources();
return false; 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);
} private static ZipEntry getNextZipEntry(ZipInputStream zipInputStream)
throws IOException {
/** try {
* Loads all entries from the ZipInputStream as Resources. return zipInputStream.getNextEntry();
* } catch (ZipException e) {
* Loads the contents of all ZipEntries into memory. //see <a href="https://github.com/psiegman/epublib/issues/122">Issue #122 Infinite loop</a>.
* Is fast, but may lead to memory problems when reading large books //when reading a file that is not a real zip archive or a zero length file, zipInputStream.getNextEntry()
* on devices with small amounts of memory. //throws an exception and does not advance, so loadResources enters an infinite loop
* //log.error("Invalid or damaged zip file.", e);
* @param zipInputStream Log.e(TAG, e.getLocalizedMessage());
* @param defaultHtmlEncoding try {
* @return zipInputStream.closeEntry();
* @throws IOException } catch (Exception ignored) {
*/ }
public static Resources loadResources(ZipInputStream zipInputStream, throw e;
String defaultHtmlEncoding) throws IOException { }
Resources result = new Resources(); }
ZipEntry zipEntry;
do { /**
// get next valid zipEntry * Loads all entries from the ZipInputStream as Resources.
zipEntry = getNextZipEntry(zipInputStream); * <p>
if ((zipEntry == null) || zipEntry.isDirectory()) { * Loads the contents of all ZipEntries into memory.
continue; * Is fast, but may lead to memory problems when reading large books
} * on devices with small amounts of memory.
*
// store resource * @param zipFile zipFile
Resource resource = ResourceUtil.createResource(zipEntry, zipInputStream); * @param defaultHtmlEncoding defaultHtmlEncoding
if (resource.getMediaType() == MediaTypes.XHTML) { * @return Resources
resource.setInputEncoding(defaultHtmlEncoding); * @throws IOException IOException
} */
result.add(resource); public static Resources loadResources(ZipFile zipFile, String defaultHtmlEncoding) throws IOException {
} while (zipEntry != null); List<MediaType> ls = new ArrayList<>();
return loadResources(zipFile, defaultHtmlEncoding, ls);
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;
} }
}
/**
* 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 static class IteratorEnumerationAdapter<T> implements Enumeration<T> {
private Iterator<T> iterator; private final Iterator<T> iterator;
public IteratorEnumerationAdapter(Iterator<T> iter) { public IteratorEnumerationAdapter(Iterator<T> iter) {
this.iterator = iter; this.iterator = iter;
@ -34,21 +34,22 @@ public class CollectionUtil {
/** /**
* Creates an Enumeration out of the given Iterator. * Creates an Enumeration out of the given Iterator.
* @param <T> * @param <T> g
* @param it * @param it g
* @return an Enumeration created out of the given Iterator. * @return an Enumeration created out of the given Iterator.
*/ */
@SuppressWarnings("unused")
public static <T> Enumeration<T> createEnumerationFromIterator( public static <T> Enumeration<T> createEnumerationFromIterator(
Iterator<T> it) { 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. * Returns the first element of the list, null if the list is null or empty.
* *
* @param <T> * @param <T> f
* @param list * @param list f
* @return the first element of the list, null if the list is null or empty. * @return the first element of the list, null if the list is null or empty.
*/ */
public static <T> T first(List<T> list) { 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. * 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. * @return Whether the given collection is null or has no elements.
*/ */
public static boolean isEmpty(Collection<?> collection) { 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. * Gets the contents of the Reader as a byte[], with the given character encoding.
* *
* @param in * @param in g
* @param encoding * @param encoding g
* @return the contents of the Reader as a byte[], with the given character encoding. * @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) public static byte[] toByteArray(Reader in, String encoding)
throws IOException { throws IOException {
@ -39,9 +39,9 @@ public class IOUtil {
/** /**
* Returns the contents of the InputStream as a byte[] * Returns the contents of the InputStream as a byte[]
* *
* @param in * @param in f
* @return the contents of the InputStream as a byte[] * @return the contents of the InputStream as a byte[]
* @throws IOException * @throws IOException f
*/ */
public static byte[] toByteArray(InputStream in) throws IOException { public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream(); ByteArrayOutputStream result = new ByteArrayOutputStream();
@ -59,7 +59,7 @@ public class IOUtil {
* @param in the stream to read data from * @param in the stream to read data from
* @param size the size of the array to create * @param size the size of the array to create
* @return the array, or null * @return the array, or null
* @throws IOException * @throws IOException f
*/ */
public static byte[] toByteArray(InputStream in, int size) public static byte[] toByteArray(InputStream in, int size)
throws IOException { throws IOException {
@ -89,8 +89,8 @@ public class IOUtil {
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead * (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise. * is returned, -1 otherwise.
* *
* @param nrRead * @param nrRead f
* @param totalNrNread * @param totalNrNread f
* @return if totalNrRead &lt; 0 then totalNrRead is returned, if * @return if totalNrRead &lt; 0 then totalNrRead is returned, if
* (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead * (nrRead + totalNrRead) &lt; Integer.MAX_VALUE then nrRead + totalNrRead
* is returned, -1 otherwise. * is returned, -1 otherwise.
@ -109,15 +109,15 @@ public class IOUtil {
/** /**
* Copies the contents of the InputStream to the OutputStream. * Copies the contents of the InputStream to the OutputStream.
* *
* @param in * @param in f
* @param out * @param out f
* @return the nr of bytes read, or -1 if the amount &gt; Integer.MAX_VALUE * @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) public static int copy(InputStream in, OutputStream out)
throws IOException { throws IOException {
byte[] buffer = new byte[IO_COPY_BUFFER_SIZE]; byte[] buffer = new byte[IO_COPY_BUFFER_SIZE];
int readSize = -1; int readSize ;
int result = 0; int result = 0;
while ((readSize = in.read(buffer)) >= 0) { while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize); out.write(buffer, 0, readSize);
@ -130,14 +130,14 @@ public class IOUtil {
/** /**
* Copies the contents of the Reader to the Writer. * Copies the contents of the Reader to the Writer.
* *
* @param in * @param in f
* @param out * @param out f
* @return the nr of characters read, or -1 if the amount &gt; Integer.MAX_VALUE * @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 { public static int copy(Reader in, Writer out) throws IOException {
char[] buffer = new char[IO_COPY_BUFFER_SIZE]; char[] buffer = new char[IO_COPY_BUFFER_SIZE];
int readSize = -1; int readSize;
int result = 0; int result = 0;
while ((readSize = in.read(buffer)) >= 0) { while ((readSize = in.read(buffer)) >= 0) {
out.write(buffer, 0, readSize); out.write(buffer, 0, readSize);
@ -148,7 +148,7 @@ public class IOUtil {
} }
public static String Stream2String(InputStream inputStream) { public static String Stream2String(InputStream inputStream) {
String str=""; String str;
try { try {
BufferedInputStream bis = new BufferedInputStream(inputStream); BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream(); ByteArrayOutputStream buf = new ByteArrayOutputStream();

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

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

@ -1,10 +1,9 @@
package me.ag2s.epublib.util; package me.ag2s.epublib.util;
import me.ag2s.epublib.Constants; import org.w3c.dom.Document;
import me.ag2s.epublib.domain.MediaType; import org.xml.sax.InputSource;
import me.ag2s.epublib.domain.Resource; import org.xml.sax.SAXException;
import me.ag2s.epublib.epub.EpubProcessorSupport;
import me.ag2s.epublib.domain.MediaTypes;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@ -15,11 +14,12 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document; import me.ag2s.epublib.Constants;
import org.xml.sax.InputSource; import me.ag2s.epublib.domain.MediaType;
import org.xml.sax.SAXException; import me.ag2s.epublib.domain.MediaTypes;
import me.ag2s.epublib.domain.Resource;
import me.ag2s.epublib.epub.EpubProcessorSupport;
/** /**
* Various resource utility methods * Various resource utility methods
@ -28,147 +28,148 @@ import org.xml.sax.SAXException;
*/ */
public class ResourceUtil { public class ResourceUtil {
public static Resource createHTMLResource(String title, String string) { public static Resource createHTMLResource(String title, String string) {
String html=createHtml(title,string); String html = createHtml(title, string);
MediaType mediaTypeProperty = MediaTypes.XHTML; MediaType mediaTypeProperty = MediaTypes.XHTML;
byte[] data = html.getBytes(); byte[] data = html.getBytes();
Resource result = new Resource(data, mediaTypeProperty); return new Resource(data, mediaTypeProperty);
return result; }
}
public static Resource createHTMLResource(String title, String string,String href) { @SuppressWarnings("unused")
String html=createHtml(title,string); public static Resource createHTMLResource(String title, String string, String href) {
MediaType mediaTypeProperty = MediaTypes.XHTML; String html = createHtml(title, string);
byte[] data = html.getBytes(); MediaType mediaTypeProperty = MediaTypes.XHTML;
Resource result = new Resource(null,data,href ,mediaTypeProperty); byte[] data = html.getBytes();
return result; return new Resource(null, data, href, mediaTypeProperty);
} }
private static String createHtml(String title,String txt){
StringBuilder body= new StringBuilder(); @SuppressWarnings("unused")
for(String s:txt.split("\\r?\\n")){ private static String createHtml(String title, String txt) {
s=s.trim(); StringBuilder body = new StringBuilder();
if(s!=null&&s.length()!=0) { for (String s : txt.split("\\r?\\n")) {
body.append("<p>").append(s).append("</p>"); 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\">"; public static Resource createResource(ZipEntry zipEntry,
html+="<head><title>"+title+"</title>" + InputStream zipInputStream) throws IOException {
"<link rel=\"stylesheet\" type=\"text/css\" href=\"css/style.css\"/>" + return new Resource(zipInputStream, zipEntry.getName());
"<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;
} }
MediaType mediaType = MediaTypes.determineMediaType(file.getName());
byte[] data = IOUtil.toByteArray(new FileInputStream(file)); /**
Resource result = new Resource(data, mediaType); * Converts a given string from given input character encoding to the requested output character encoding.
return result; *
} * @param inputEncoding v
* @param outputEncoding v
* @param input v
/** * @return the string from given input character encoding converted to the requested output character encoding.
* Creates a resource with as contents a html page with the given title. * @throws UnsupportedEncodingException v
* */
* @param title @SuppressWarnings("unused")
* @param href public static byte[] recode(String inputEncoding, String outputEncoding,
* @return a resource with as contents a html page with the given title. byte[] input) throws UnsupportedEncodingException {
*/ return new String(input, inputEncoding).getBytes(outputEncoding);
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;
} }
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)
* Reads parses the xml therein and returns the result as a Document throws SAXException, IOException {
*/ return getAsDocument(resource,
public static Document getAsDocument(Resource resource) EpubProcessorSupport.createDocumentBuilder());
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 v
* Reads the given resources inputstream, parses the xml therein and returns the result as a Document * @param documentBuilder v
* * @return the document created from the given resource
* @param resource * @throws UnsupportedEncodingException v
* @param documentBuilder * @throws SAXException v
* @return the document created from the given resource * @throws IOException v
* @throws UnsupportedEncodingException */
* @throws SAXException public static Document getAsDocument(Resource resource,
* @throws IOException DocumentBuilder documentBuilder)
* @throws ParserConfigurationException throws UnsupportedEncodingException, SAXException, IOException {
*/ InputSource inputSource = getInputSource(resource);
public static Document getAsDocument(Resource resource, if (inputSource == null) {
DocumentBuilder documentBuilder) return null;
throws UnsupportedEncodingException, SAXException, IOException, ParserConfigurationException { }
InputSource inputSource = getInputSource(resource); return documentBuilder.parse(inputSource);
if (inputSource == null) {
return null;
} }
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. * Various String utility functions.
* * <p>
* Most of the functions herein are re-implementations of the ones in apache * Most of the functions herein are re-implementations of the ones in apache
* commons StringUtils. The reason for re-implementing this is that the * commons StringUtils. The reason for re-implementing this is that the
* functions are fairly simple and using my own implementation saves the * functions are fairly simple and using my own implementation saves the
* inclusion of a 200Kb jar file. * inclusion of a 200Kb jar file.
* *
* @author paul.siegmann * @author paul.siegmann
*
*/ */
public class StringUtil { public class StringUtil {
/** /**
* Changes a path containing '..', '.' and empty dirs into a path that * 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 * doesn't. X/foo/../Y is changed into 'X/Y', etc. Does not handle invalid
* paths like "../". * paths like "../".
* *
* @param path * @param path path
* @return the normalized path * @return the normalized path
*/ */
public static String collapsePathDots(String path) { public static String collapsePathDots(String path) {
String[] stringParts = path.split("/"); String[] stringParts = path.split("/");
List<String> parts = new ArrayList<String>(Arrays.asList(stringParts)); List<String> parts = new ArrayList<>(Arrays.asList(stringParts));
for (int i = 0; i < parts.size() - 1; i++) { for (int i = 0; i < parts.size() - 1; i++) {
String currentDir = parts.get(i); String currentDir = parts.get(i);
if (currentDir.length() == 0 || currentDir.equals(".")) { if (currentDir.length() == 0 || currentDir.equals(".")) {
parts.remove(i); parts.remove(i);
i--; i--;
} else if (currentDir.equals("..")) { } else if (currentDir.equals("..")) {
parts.remove(i - 1); parts.remove(i - 1);
parts.remove(i - 1); parts.remove(i - 1);
i -= 2; i -= 2;
} }
} }
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
if (path.startsWith("/")) { if (path.startsWith("/")) {
result.append('/'); result.append('/');
} }
for (int i = 0; i < parts.size(); i++) { for (int i = 0; i < parts.size(); i++) {
result.append(parts.get(i)); result.append(parts.get(i));
if (i < (parts.size() - 1)) { if (i < (parts.size() - 1)) {
result.append('/'); result.append('/');
} }
}
return result.toString();
} }
return result.toString();
}
/** /**
* Whether the String is not null, not zero-length and does not contain of * Whether the String is not null, not zero-length and does not contain of
* only whitespace. * only whitespace.
* *
* @param text * @param text text
* @return Whether the String is not null, not zero-length and does not contain of * @return Whether the String is not null, not zero-length and does not contain of
*/ */
public static boolean isNotBlank(String text) { public static boolean isNotBlank(String text) {
return !isBlank(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;
}
} }
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 * Whether the String is null, zero-length and does contain only whitespace.
* case. *
* * @return Whether the String is null, zero-length and does contain only whitespace.
* @param source */
* @param suffix public static boolean isBlank(String text) {
* @return Whether the given source string ends with the given suffix, ignoring case. if (isEmpty(text)) {
*/ return true;
public static boolean endsWithIgnoreCase(String source, String suffix) { }
if (isEmpty(suffix)) { for (int i = 0; i < text.length(); i++) {
return true; 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. * Whether the given string is null or zero-length.
* *
* @param text * @param text the input for this method
* @return If the given text is null "", the original text otherwise. * @return Whether the given string is null or zero-length.
*/ */
public static String defaultIfNull(String text) { public static boolean isEmpty(String text) {
return defaultIfNull(text, ""); return (text == null) || (text.length() == 0);
}
/**
* 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;
} }
return text;
}
/** /**
* Null-safe string comparator * Whether the given source string ends with the given suffix, ignoring
* * case.
* @param text1 *
* @param text2 * @param source source
* @return whether the two strings are equal * @param suffix suffix
*/ * @return Whether the given source string ends with the given suffix, ignoring case.
public static boolean equals(String text1, String text2) { */
if (text1 == null) { public static boolean endsWithIgnoreCase(String source, String suffix) {
return (text2 == null); 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. * If the given text is null return "", the original text otherwise.
* *
* @param keyValues * @param text text
* @return a string representation of the input values * @return If the given text is null "", the original text otherwise.
*/ */
public static String toString(Object... keyValues) { public static String defaultIfNull(String text) {
StringBuilder result = new StringBuilder(); return defaultIfNull(text, "");
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();
}
public static int hashCode(String... values) { /**
int result = 31; * If the given text is null return "", the given defaultValue otherwise.
for (int i = 0; i < values.length; i++) { *
result ^= String.valueOf(values[i]).hashCode(); * @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. * Null-safe string comparator
* *
* If the text does not contain the given separator then the given text is * @param text1 d
* returned. * @param text2 d
* * @return whether the two strings are equal
* @param text */
* @param separator public static boolean equals(String text1, String text2) {
* @return the substring of the given text before the given separator. if (text1 == null) {
*/ return (text2 == null);
public static String substringBefore(String text, char separator) { }
if (isEmpty(text)) { return text1.equals(text2);
return text;
}
int sepPos = text.indexOf(separator);
if (sepPos < 0) {
return text;
} }
return text.substring(0, sepPos);
}
/** /**
* Gives the substring of the given text before the last occurrence of the * Pretty toString printer.
* given separator. *
* * @param keyValues d
* If the text does not contain the given separator then the given text is * @return a string representation of the input values
* returned. */
* public static String toString(Object... keyValues) {
* @param text StringBuilder result = new StringBuilder();
* @param separator result.append('[');
* @return the substring of the given text before the last occurrence of the given separator. for (int i = 0; i < keyValues.length; i += 2) {
*/ if (i > 0) {
public static String substringBeforeLast(String text, char separator) { result.append(", ");
if (isEmpty(text)) { }
return text; 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) { public static int hashCode(String... values) {
return text; 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 * Gives the substring of the given text before the given separator.
* given separator. * <p>
* * If the text does not contain the given separator then the given text is
* If the text does not contain the given separator then "" is returned. * returned.
* *
* @param text * @param text d
* @param separator * @param separator d
* @return the substring of the given text after the last occurrence of the given separator. * @return the substring of the given text before the given separator.
*/ */
public static String substringAfterLast(String text, char separator) { public static String substringBefore(String text, char separator) {
if (isEmpty(text)) { if (isEmpty(text)) {
return 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. * 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. * <p>
* * If the text does not contain the given separator then "" is returned.
* @param text the input text *
* @param c the separator char * @param text d
* @return the substring of the given text after the given separator. * @param separator d
*/ * @return the substring of the given text after the last occurrence of the given separator.
public static String substringAfter(String text, char c) { */
if (isEmpty(text)) { public static String substringAfterLast(String text, char separator) {
return text; 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. * a {@link ByteOrderMark#UTF_8} BOM.
* @param delegate the InputStream to delegate to * @param delegate the InputStream to delegate to
*/ */
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate) { public BOMInputStream(InputStream delegate) {
this(delegate, false, ByteOrderMark.UTF_8); 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 * @param include true to include the UTF-8 BOM or
* false to exclude it * false to exclude it
*/ */
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, boolean include) { public BOMInputStream(InputStream delegate, boolean include) {
this(delegate, include, ByteOrderMark.UTF_8); this(delegate, include, ByteOrderMark.UTF_8);
} }
@ -106,6 +108,7 @@ public class BOMInputStream extends ProxyInputStream {
* @param delegate the InputStream to delegate to * @param delegate the InputStream to delegate to
* @param boms The BOMs to detect and exclude * @param boms The BOMs to detect and exclude
*/ */
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, ByteOrderMark... boms) { public BOMInputStream(InputStream delegate, ByteOrderMark... boms) {
this(delegate, false, boms); this(delegate, false, boms);
} }
@ -118,6 +121,7 @@ public class BOMInputStream extends ProxyInputStream {
* false to exclude them * false to exclude them
* @param boms The BOMs to detect and optionally exclude * @param boms The BOMs to detect and optionally exclude
*/ */
@SuppressWarnings("unused")
public BOMInputStream(InputStream delegate, boolean include, ByteOrderMark... boms) { public BOMInputStream(InputStream delegate, boolean include, ByteOrderMark... boms) {
super(delegate); super(delegate);
if (boms == null || boms.length == 0) { if (boms == null || boms.length == 0) {
@ -134,6 +138,7 @@ public class BOMInputStream extends ProxyInputStream {
* if it does not * if it does not
* @throws IOException if an error reading the first bytes of the stream occurs * @throws IOException if an error reading the first bytes of the stream occurs
*/ */
@SuppressWarnings("unused")
public boolean hasBOM() throws IOException { public boolean hasBOM() throws IOException {
return (getBOM() != null); return (getBOM() != null);
} }
@ -148,6 +153,7 @@ public class BOMInputStream extends ProxyInputStream {
* is configured to detect * is configured to detect
* @throws IOException if an error reading the first bytes of the stream occurs * @throws IOException if an error reading the first bytes of the stream occurs
*/ */
@SuppressWarnings("unused")
public boolean hasBOM(ByteOrderMark bom) throws IOException { public boolean hasBOM(ByteOrderMark bom) throws IOException {
if (!boms.contains(bom)) { if (!boms.contains(bom)) {
throw new IllegalArgumentException("Stream not configure to detect " + 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 * @return the length of the BOM's bytes
*/ */
@Override @Override
@SuppressWarnings("NullableProblems")
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName()); builder.append(getClass().getSimpleName());

@ -197,9 +197,9 @@ public abstract class ProxyInputStream extends FilterInputStream {
* *
* @since Commons IO 2.0 * @since Commons IO 2.0
* @param n number of bytes that the caller asked to be read * @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 * @since Commons IO 2.0
* @param n number of bytes read, or -1 if the end of stream was reached * @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.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -105,6 +106,7 @@ public class XmlStreamReader extends Reader {
* *
* @return the default encoding to use. * @return the default encoding to use.
*/ */
@SuppressWarnings("unused")
public String getDefaultEncoding() { public String getDefaultEncoding() {
return defaultEncoding; return defaultEncoding;
} }
@ -121,6 +123,7 @@ public class XmlStreamReader extends Reader {
* @param file File to create a Reader from. * @param file File to create a Reader from.
* @throws IOException thrown if there is a problem reading the file. * @throws IOException thrown if there is a problem reading the file.
*/ */
@SuppressWarnings("unused")
public XmlStreamReader(File file) throws IOException { public XmlStreamReader(File file) throws IOException {
this(new FileInputStream(file)); 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 * @throws IOException thrown if there is a problem reading the stream of
* the URL. * the URL.
*/ */
@SuppressWarnings("unused")
public XmlStreamReader(URL url) throws IOException { public XmlStreamReader(URL url) throws IOException {
this(url.openConnection(), null); this(url.openConnection(), null);
} }
@ -249,15 +253,16 @@ public class XmlStreamReader extends Reader {
*/ */
public XmlStreamReader(URLConnection conn, String defaultEncoding) throws IOException { public XmlStreamReader(URLConnection conn, String defaultEncoding) throws IOException {
this.defaultEncoding = defaultEncoding; this.defaultEncoding = defaultEncoding;
@SuppressWarnings("unused")
boolean lenient = true; boolean lenient = true;
String contentType = conn.getContentType(); String contentType = conn.getContentType();
InputStream is = conn.getInputStream(); InputStream is = conn.getInputStream();
BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS); BOMInputStream bom = new BOMInputStream(new BufferedInputStream(is, BUFFER_SIZE), false, BOMS);
BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES); BOMInputStream pis = new BOMInputStream(bom, true, XML_GUESS_BYTES);
if (conn instanceof HttpURLConnection || contentType != null) { if (conn instanceof HttpURLConnection || contentType != null) {
this.encoding = doHttpStream(bom, pis, contentType, lenient); this.encoding = doHttpStream(bom, pis, contentType, true);
} else { } else {
this.encoding = doRawStream(bom, pis, lenient); this.encoding = doRawStream(bom, pis, true);
} }
this.reader = new InputStreamReader(pis, encoding); this.reader = new InputStreamReader(pis, encoding);
} }
@ -509,11 +514,11 @@ public class XmlStreamReader extends Reader {
// BOM is UTF-8 // BOM is UTF-8
if (bomEnc.equals(UTF_8)) { if (bomEnc.equals(UTF_8)) {
if (xmlGuessEnc != null && !xmlGuessEnc.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); throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
} }
if (xmlEnc != null && !xmlEnc.equals(UTF_8)) { 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); throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
} }
return bomEnc; return bomEnc;
@ -522,18 +527,18 @@ public class XmlStreamReader extends Reader {
// BOM is UTF-16BE or UTF-16LE // BOM is UTF-16BE or UTF-16LE
if (bomEnc.equals(UTF_16BE) || bomEnc.equals(UTF_16LE)) { if (bomEnc.equals(UTF_16BE) || bomEnc.equals(UTF_16LE)) {
if (xmlGuessEnc != null && !xmlGuessEnc.equals(bomEnc)) { 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); throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
} }
if (xmlEnc != null && !xmlEnc.equals(UTF_16) && !xmlEnc.equals(bomEnc)) { 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); throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
} }
return bomEnc; return bomEnc;
} }
// BOM is something else // 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); throw new XmlStreamReaderException(msg, bomEnc, xmlGuessEnc, xmlEnc);
} }
@ -697,7 +702,7 @@ public class XmlStreamReader extends Reader {
} }
Matcher m = ENCODING_PATTERN.matcher(prolog); Matcher m = ENCODING_PATTERN.matcher(prolog);
if (m.find()) { if (m.find()) {
encoding = m.group(1).toUpperCase(); encoding = Objects.requireNonNull(m.group(1)).toUpperCase();
encoding = encoding.substring(1, encoding.length() - 1); 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 * @return the MIME type in the content-type, null if there was not
* content-type or the encoding detection did not involve HTTP. * content-type or the encoding detection did not involve HTTP.
*/ */
@SuppressWarnings("unused")
public String getContentTypeMime() { public String getContentTypeMime() {
return contentTypeMime; return contentTypeMime;
} }

Loading…
Cancel
Save