nl.siegmann.epublib.domain.TableOfContents Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of epublib-core Show documentation
Show all versions of epublib-core Show documentation
A java library for reading/writing/manipulating epub files
The newest version!
package nl.siegmann.epublib.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* The table of contents of the book.
* The TableOfContents is a tree structure at the root it is a list of TOCReferences, each if which may have as children another list of TOCReferences.
*
* The table of contents is used by epub as a quick index to chapters and sections within chapters.
* It may contain duplicate entries, may decide to point not to certain chapters, etc.
*
* See the spine for the complete list of sections in the order in which they should be read.
*
* @see nl.siegmann.epublib.domain.Spine
*
* @author paul
*
*/
public class TableOfContents implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3147391239966275152L;
public static final String DEFAULT_PATH_SEPARATOR = "/";
private List tocReferences;
public TableOfContents() {
this(new ArrayList());
}
public TableOfContents(List tocReferences) {
this.tocReferences = tocReferences;
}
public List getTocReferences() {
return tocReferences;
}
public void setTocReferences(List tocReferences) {
this.tocReferences = tocReferences;
}
/**
* Calls addTOCReferenceAtLocation after splitting the path using the DEFAULT_PATH_SEPARATOR.
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path) {
return addSection(resource, path, DEFAULT_PATH_SEPARATOR);
}
/**
* Calls addTOCReferenceAtLocation after splitting the path using the given pathSeparator.
*
* @param resource
* @param path
* @param pathSeparator
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path, String pathSeparator) {
String[] pathElements = path.split(pathSeparator);
return addSection(resource, pathElements);
}
/**
* Finds the first TOCReference in the given list that has the same title as the given Title.
*
* @param title
* @param tocReferences
* @return null if not found.
*/
private static TOCReference findTocReferenceByTitle(String title, List tocReferences) {
for (TOCReference tocReference: tocReferences) {
if (title.equals(tocReference.getTitle())) {
return tocReference;
}
}
return null;
}
/**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements.
*
* Example:
* Calling this method with a Resource and new String[] {"chapter1", "paragraph1"} will result in the following:
*
* - a TOCReference with the title "chapter1" at the root level.
* If this TOCReference did not yet exist it will have been created and does not point to any resource
* - A TOCReference that has the title "paragraph1". This TOCReference will be the child of TOCReference "chapter1" and
* will point to the given Resource
*
*
* @param resource
* @param pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String[] pathElements) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
String currentTitle = pathElements[i];
result = findTocReferenceByTitle(currentTitle, currentTocReferences);
if (result == null) {
result = new TOCReference(currentTitle, null);
currentTocReferences.add(result);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
}
/**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements.
*
* Example:
* Calling this method with a Resource and new int[] {0, 0} will result in the following:
*
* - a TOCReference at the root level.
* If this TOCReference did not yet exist it will have been created with a title of "" and does not point to any resource
* - A TOCReference that points to the given resource and is a child of the previously created TOCReference.
* If this TOCReference didn't exist yet it will be created and have a title of ""
*
*
* @param resource
* @param pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, int[] pathElements, String sectionTitlePrefix, String sectionNumberSeparator) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
int currentIndex = pathElements[i];
if (currentIndex > 0 && currentIndex < (currentTocReferences.size() - 1)) {
result = currentTocReferences.get(currentIndex);
} else {
result = null;
}
if (result == null) {
paddTOCReferences(currentTocReferences, pathElements, i, sectionTitlePrefix, sectionNumberSeparator);
result = currentTocReferences.get(currentIndex);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
}
private void paddTOCReferences(List currentTocReferences,
int[] pathElements, int pathPos, String sectionPrefix, String sectionNumberSeparator) {
for (int i = currentTocReferences.size(); i <= pathElements[pathPos]; i++) {
String sectionTitle = createSectionTitle(pathElements, pathPos, i, sectionPrefix,
sectionNumberSeparator);
currentTocReferences.add(new TOCReference(sectionTitle, null));
}
}
private String createSectionTitle(int[] pathElements, int pathPos, int lastPos,
String sectionPrefix, String sectionNumberSeparator) {
StringBuilder title = new StringBuilder(sectionPrefix);
for (int i = 0; i < pathPos; i++) {
if (i > 0) {
title.append(sectionNumberSeparator);
}
title.append(pathElements[i] + 1);
}
if (pathPos > 0) {
title.append(sectionNumberSeparator);
}
title.append(lastPos + 1);
return title.toString();
}
public TOCReference addTOCReference(TOCReference tocReference) {
if (tocReferences == null) {
tocReferences = new ArrayList();
}
tocReferences.add(tocReference);
return tocReference;
}
/**
* All unique references (unique by href) in the order in which they are referenced to in the table of contents.
*
* @return All unique references (unique by href) in the order in which they are referenced to in the table of contents.
*/
public List getAllUniqueResources() {
Set uniqueHrefs = new HashSet();
List result = new ArrayList();
getAllUniqueResources(uniqueHrefs, result, tocReferences);
return result;
}
private static void getAllUniqueResources(Set uniqueHrefs, List result, List tocReferences) {
for (TOCReference tocReference: tocReferences) {
Resource resource = tocReference.getResource();
if (resource != null && ! uniqueHrefs.contains(resource.getHref())) {
uniqueHrefs.add(resource.getHref());
result.add(resource);
}
getAllUniqueResources(uniqueHrefs, result, tocReference.getChildren());
}
}
/**
* The total number of references in this table of contents.
*
* @return The total number of references in this table of contents.
*/
public int size() {
return getTotalSize(tocReferences);
}
private static int getTotalSize(Collection tocReferences) {
int result = tocReferences.size();
for (TOCReference tocReference: tocReferences) {
result += getTotalSize(tocReference.getChildren());
}
return result;
}
/**
* The maximum depth of the reference tree
* @return The maximum depth of the reference tree
*/
public int calculateDepth() {
return calculateDepth(tocReferences, 0);
}
private int calculateDepth(List tocReferences, int currentDepth) {
int maxChildDepth = 0;
for (TOCReference tocReference: tocReferences) {
int childDepth = calculateDepth(tocReference.getChildren(), 1);
if (childDepth > maxChildDepth) {
maxChildDepth = childDepth;
}
}
return currentDepth + maxChildDepth;
}
}