commit
fb2d43837b
@ -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> |
@ -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,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); |
|
||||||
} |
|
||||||
} |
|
@ -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(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
Loading…
Reference in new issue