You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
legado/epublib/src/main/java/me/ag2s/epublib/domain/Resources.java

415 lines
13 KiB

package me.ag2s.epublib.domain;
import me.ag2s.epublib.Constants;
import me.ag2s.epublib.util.StringUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.util.Base64;
/**
* All the resources that make up the book.
* XHTML files, images and epub xml documents must be here.
*
* @author paul
*/
public class Resources implements Serializable {
private static final long serialVersionUID = 2450876953383871451L;
private static final String IMAGE_PREFIX = "image_";
private static final String ITEM_PREFIX = "item_";
private int lastId = 1;
private Map<String, Resource> resources = new HashMap<>();
/**
* Adds a resource to the resources.
* <p>
* Fixes the resources id and href if necessary.
*
* @param resource resource
* @return the newly added resource
*/
public Resource add(Resource resource) {
fixResourceHref(resource);
fixResourceId(resource);
this.resources.put(resource.getHref(), resource);
return resource;
}
/**
* 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 a valid identifier. if not: prepend with valid identifier
*
* @param resource resource
* @return a valid id
*/
private String makeValidId(String resourceId, Resource resource) {
if (StringUtil.isNotBlank(resourceId) && !Character
.isJavaIdentifierStart(resourceId.charAt(0))) {
resourceId = getResourceItemPrefix(resource) + resourceId;
}
return resourceId;
}
private String getResourceItemPrefix(Resource resource) {
String result;
if (MediaTypes.isBitmapImage(resource.getMediaType())) {
result = IMAGE_PREFIX;
} else {
result = ITEM_PREFIX;
}
return result;
}
/**
* Creates a new resource id that is guaranteed to be unique for this set of Resources
*
* @param resource resource
* @return a new resource id that is guaranteed to be unique for this set of Resources
*/
private String createUniqueResourceId(Resource resource) {
int counter = lastId;
if (counter == Integer.MAX_VALUE) {
if (resources.size() == Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Resources contains " + Integer.MAX_VALUE
+ " elements: no new elements can be added");
} else {
counter = 1;
}
}
String prefix = getResourceItemPrefix(resource);
String result = prefix + counter;
while (containsId(result)) {
result = prefix + (++counter);
}
lastId = counter;
return result;
}
/**
* Whether the map of resources already contains a resource with the given id.
*
* @param id id
* @return Whether the map of resources already contains a resource with the given id.
*/
public boolean containsId(String id) {
if (StringUtil.isBlank(id)) {
return false;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return true;
}
}
return false;
}
/**
* Gets the resource with the given id.
*
* @param id id
* @return null if not found
*/
public Resource getById(String id) {
if (StringUtil.isBlank(id)) {
return null;
}
for (Resource resource : resources.values()) {
if (id.equals(resource.getId())) {
return resource;
}
}
return null;
}
public Resource getByProperties(String properties) {
if (StringUtil.isBlank(properties)) {
return null;
}
for (Resource resource : resources.values()) {
if (properties.equals(resource.getProperties())) {
return resource;
}
}
return null;
}
/**
* 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);
}
private void fixResourceHref(Resource resource) {
if (StringUtil.isNotBlank(resource.getHref())
&& !resources.containsKey(resource.getHref())) {
return;
}
if (StringUtil.isBlank(resource.getHref())) {
if (resource.getMediaType() == null) {
throw new IllegalArgumentException(
"Resource must have either a MediaType or a href");
}
int i = 1;
String href = createHref(resource.getMediaType(), i);
while (resources.containsKey(href)) {
href = createHref(resource.getMediaType(), (++i));
}
resource.setHref(href);
}
}
private String createHref(MediaType mediaType, int counter) {
if (MediaTypes.isBitmapImage(mediaType)) {
return IMAGE_PREFIX + counter + mediaType.getDefaultExtension();
} else {
return ITEM_PREFIX + counter + mediaType.getDefaultExtension();
}
}
public boolean isEmpty() {
return resources.isEmpty();
}
/**
* The number of resources
*
* @return The number of resources
*/
public int size() {
return resources.size();
}
/**
* The resources that make up this book.
* Resources can be xhtml pages, images, xml documents, etc.
*
* @return The resources that make up this book.
*/
@SuppressWarnings("unused")
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 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
*
* @param href href
* @return Whether there exists a resource with the given href
*/
@SuppressWarnings("unused")
public boolean containsByHref(String href) {
return !notContainsByHref(href);
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources resources
*/
public void set(Collection<Resource> resources) {
this.resources.clear();
addAll(resources);
}
/**
* Adds all resources from the given Collection of resources to the existing collection.
*
* @param resources resources
*/
public void addAll(Collection<Resource> resources) {
for (Resource resource : resources) {
fixResourceHref(resource);
this.resources.put(resource.getHref(), resource);
}
}
/**
* Sets the collection of Resources to the given collection of resources
*
* @param resources A map with as keys the resources href and as values the Resources
*/
public void set(Map<String, Resource> resources) {
this.resources = new HashMap<>(resources);
}
/**
* First tries to find a resource with as id the given idOrHref, if that
* fails it tries to find one with the idOrHref as href.
*
* @param idOrHref idOrHref
* @return the found Resource
*/
public Resource getByIdOrHref(String idOrHref) {
Resource resource = getById(idOrHref);
if (resource == null) {
resource = getByHref(idOrHref);
}
return resource;
}
/**
* Gets the resource with the given href.
* If the given href contains a fragmentId then that fragment id will be ignored.
*
* @param href href
* @return null if not found.
*/
public Resource getByHref(String href) {
if (StringUtil.isBlank(href)) {
return null;
}
href = StringUtil.substringBefore(href, Constants.FRAGMENT_SEPARATOR_CHAR);
Pattern dataUriRegex = Pattern.compile("data:([\\w/\\-\\.]+);base64,(.*)");
Matcher dataUriMatcher = dataUriRegex.matcher(href);
if (dataUriMatcher.find()) {
String dataUriMediaTypeString = dataUriMatcher.group(1);
MediaType dataUriMediaType = new MediaType(dataUriMediaTypeString, "." + StringUtil.substringAfterLast(dataUriMediaTypeString, '/'));
byte[] dataUriData = Base64.decode(dataUriMatcher.group(2), Base64.DEFAULT);
return new Resource(dataUriData, dataUriMediaType);
} else {
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;
}
/**
* 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;
}
/**
* All Resources that match any of the given list of MediaTypes
*
* @param mediaTypes mediaType
* @return All Resources that match any of the given list of MediaTypes
*/
@SuppressWarnings("unused")
public List<Resource> getResourcesByMediaTypes(MediaType[] mediaTypes) {
List<Resource> result = new ArrayList<>();
if (mediaTypes == null) {
return result;
}
// this is the fastest way of doing this according to
// http://stackoverflow.com/questions/1128723/in-java-how-can-i-test-if-an-array-contains-a-certain-value
List<MediaType> mediaTypesList = Arrays.asList(mediaTypes);
for (Resource resource : getAll()) {
if (mediaTypesList.contains(resource.getMediaType())) {
result.add(resource);
}
}
return result;
}
/**
* All resource hrefs
*
* @return all resource hrefs
*/
public Collection<String> getAllHrefs() {
return resources.keySet();
}
}