All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.siegmann.epublib.domain.LazyResource Maven / Gradle / Ivy

The newest version!
package nl.siegmann.epublib.domain;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import nl.siegmann.epublib.service.MediatypeService;
import nl.siegmann.epublib.util.IOUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Resource that loads its data only on-demand.
 * This way larger books can fit into memory and can be opened faster.
 * 
 */
public class LazyResource extends Resource {
	
		
	/**
	 * 
	 */
	private static final long serialVersionUID = 5089400472352002866L;
	private String filename;
	private long cachedSize;
	
	private static final Logger LOG = LoggerFactory.getLogger(LazyResource.class);
	
	/**
	 * Creates a Lazy resource, by not actually loading the data for this entry.
	 * 
	 * The data will be loaded on the first call to getData()
	 * 
	 * @param fileName the fileName for the epub we're created from.
	 * @param size the size of this resource.
	 * @param href The resource's href within the epub.
	 */
	public LazyResource(String filename, long size, String href) {
		super( null, null, href, MediatypeService.determineMediaType(href));
		this.filename = filename;
		this.cachedSize = size;
	}

    /**
     * Creates a Resource that tries to load the data, but falls back to lazy loading.
     *
     * If the size of the resource is known ahead of time we can use that to allocate
     * a matching byte[]. If this succeeds we can safely load the data.
     *
     * If it fails we leave the data null for now and it will be lazy-loaded when
     * it is accessed.
     *
     * @param in
     * @param fileName
     * @param length
     * @param href
     * @throws IOException
     */
    public LazyResource(InputStream in, String filename, int length, String href) throws IOException {
        super(null, IOUtil.toByteArray(in, length), href, MediatypeService.determineMediaType(href));
        this.filename = filename;
        this.cachedSize = length;
    }
	
	/**
	 * Gets the contents of the Resource as an InputStream.
	 * 
	 * @return The contents of the Resource.
	 * 
	 * @throws IOException
	 */
	public InputStream getInputStream() throws IOException {
		if (isInitialized()) {
			return new ByteArrayInputStream(getData());
		} else {
			return getResourceStream();
		}
	}
	
	/**
	 * Initializes the resource by loading its data into memory.
	 * 
	 * @throws IOException
	 */
	public void initialize() throws IOException {
		getData();
	}

	/**
	 * The contents of the resource as a byte[]
	 * 
	 * If this resource was lazy-loaded and the data was not yet loaded, 
	 * it will be loaded into memory at this point.
	 *  This included opening the zip file, so expect a first load to be slow.
	 * 
	 * @return The contents of the resource
	 */
	public byte[] getData() throws IOException {
		
		if ( data == null ) {
			
			LOG.debug("Initializing lazy resource " + filename + "#" + this.getHref() );
			
			InputStream in = getResourceStream();
			byte[] readData = IOUtil.toByteArray(in, (int) this.cachedSize);
			if ( readData == null ) {
			    throw new IOException("Could not load the contents of entry " + this.getHref() + " from epub file " + filename);
			} else {
			    this.data = readData;
			}
			
			in.close();
		}

		return data;
	}

	
	private InputStream getResourceStream() throws FileNotFoundException,
			IOException {
		ZipFile zipFile = new ZipFile(filename);
		ZipEntry zipEntry = zipFile.getEntry(originalHref);
		if (zipEntry == null) {
			zipFile.close();
			throw new IllegalStateException("Cannot find entry " + originalHref + " in epub file " + filename);
		}
		return new ResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
	}
	
	/**
	 * Tells this resource to release its cached data.
	 * 
	 * If this resource was not lazy-loaded, this is a no-op.
	 */
	public void close() {
		if ( this.filename != null ) {
			this.data = null;
		}
	}
	
	/**
	 * Returns if the data for this resource has been loaded into memory.
	 * 
	 * @return true if data was loaded.
	 */
	public boolean isInitialized() {
		return data != null;
	}

	/**
	 * Returns the size of this resource in bytes.
	 * 
	 * @return the size.
	 */
	public long getSize() {
		if ( data != null ) {
			return data.length;
		}
		
		return cachedSize;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy