Package org.apache.xmlgraphics.image.loader.cache

Source Code of org.apache.xmlgraphics.image.loader.cache.ImageCache

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id: ImageCache.java 606580 2007-12-23 17:45:02Z jeremias $ */
package org.apache.xmlgraphics.image.loader.cache;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;

import javax.xml.transform.Source;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.util.SoftMapCache;


/**
* This class provides a cache for images. The main key into the images is the original URI the
* image was accessed with.
* <p>
* Don't use one ImageCache instance in the context of multiple base URIs because relative URIs
* would not work correctly anymore.
*/
public class ImageCache {

    /** logger */
    protected static Log log = LogFactory.getLog(ImageCache.class);
   
    private Set invalidURIs = Collections.synchronizedSet(new java.util.HashSet());
   
    private SoftMapCache imageInfos = new SoftMapCache(true);
    private SoftMapCache images = new SoftMapCache(true);
   
    private ImageCacheListener cacheListener;

    /**
     * Sets an ImageCacheListener instance so the events in the image cache can be observed.
     * @param listener the listener instance
     */
    public void setCacheListener(ImageCacheListener listener) {
        this.cacheListener = listener;
    }
   
    /**
     * Returns an ImageInfo instance for a given URI.
     * @param uri the image's URI
     * @param session the session context
     * @param manager the ImageManager handling the images
     * @return the ImageInfo instance
     * @throws ImageException if an error occurs while parsing image data
     * @throws IOException if an I/O error occurs while loading image data
     */
    public ImageInfo needImageInfo(String uri, ImageSessionContext session, ImageManager manager)
            throws ImageException, IOException {
        //Fetch unique version of the URI and use it for synchronization so we have some sort of
        //"row-level" locking instead of "table-level" locking (to use a database analogy).
        //The fine locking strategy is necessary since preloading an image is a potentially long
        //operation.
        if (isInvalidURI(uri)) {
            throw new FileNotFoundException("Image not found: " + uri);
        }
        String lockURI = uri.intern();
        synchronized (lockURI) {
            ImageInfo info = getImageInfo(uri);
            if (info == null) {
                try {
                    Source src = session.needSource(uri);
                    if (src == null) {
                        registerInvalidURI(uri);
                        throw new FileNotFoundException("Image not found: " + uri);
                    }
                    info = manager.preloadImage(uri, src);
                    session.returnSource(uri, src);
                } catch (IOException ioe) {
                    registerInvalidURI(uri);
                    throw ioe;
                } catch (ImageException e) {
                    registerInvalidURI(uri);
                    throw e;
                }
                putImageInfo(info);
            }
            return info;
        }
    }
   
    /**
     * Indicates whether a URI has previously been identified as an invalid URI.
     * @param uri the image's URI
     * @return true if the URI is invalid
     */
    public boolean isInvalidURI(String uri) {
        if (invalidURIs.contains(uri)) {
            if (cacheListener != null) {
                cacheListener.invalidHit(uri);
            }
            return true;
        }
        return false;
    }
   
    /**
     * Returns an ImageInfo instance from the cache or null if none is found.
     * @param uri the image's URI
     * @return the ImageInfo instance or null if the requested information is not in the cache
     */
    protected ImageInfo getImageInfo(String uri) {
        ImageInfo info = (ImageInfo)imageInfos.get(uri);
        if (cacheListener != null) {
            if (info != null) {
                cacheListener.cacheHitImageInfo(uri);
            } else {
                if (!isInvalidURI(uri)) {
                    cacheListener.cacheMissImageInfo(uri);
                }
            }
        }
        return info;
    }
   
    /**
     * Registers an ImageInfo instance with the cache.
     * @param info the ImageInfo instance
     */
    protected void putImageInfo(ImageInfo info) {
        //An already existing ImageInfo is replaced.
        imageInfos.put(info.getOriginalURI(), info);
    }
   
    /**
     * Registers a URI as invalid so getImageInfo can indicate that quickly with no I/O access.
     * @param uri the URI of the invalid image
     */
    private void registerInvalidURI(String uri) {
        synchronized (invalidURIs) {
            // cap size of invalid list
            if (invalidURIs.size() > 100) {
                invalidURIs.clear();
            }
            invalidURIs.add(uri);
        }
    }
   
    /**
     * Returns an image from the cache or null if it wasn't found.
     * @param info the ImageInfo instance representing the image
     * @param flavor the requested ImageFlavor for the image
     * @return the requested image or null if the image is not in the cache
     */
    public Image getImage(ImageInfo info, ImageFlavor flavor) {
        return getImage(info.getOriginalURI(), flavor);
    }
   
    /**
     * Returns an image from the cache or null if it wasn't found.
     * @param uri the image's URI
     * @param flavor the requested ImageFlavor for the image
     * @return the requested image or null if the image is not in the cache
     */
    public Image getImage(String uri, ImageFlavor flavor) {
        if (uri == null || "".equals(uri)) {
            return null;
        }
        ImageKey key = new ImageKey(uri, flavor);
        Image img = (Image)images.get(key);
        if (cacheListener != null) {
            if (img != null) {
                cacheListener.cacheHitImage(key);
            } else {
                cacheListener.cacheMissImage(key);
            }
        }
        return img;
    }
   
    /**
     * Registers an image with the cache.
     * @param img the image
     */
    public void putImage(Image img) {
        String originalURI = img.getInfo().getOriginalURI();
        if (originalURI == null || "".equals(originalURI)) {
            return; //Don't cache if there's no URI
        }
        //An already existing Image is replaced.
        if (!img.isCacheable()) {
            throw new IllegalArgumentException(
                    "Image is not cacheable! (Flavor: " + img.getFlavor() + ")");
        }
        ImageKey key = new ImageKey(originalURI, img.getFlavor());
        images.put(key, img);
    }

    /**
     * Clears the image cache (all ImageInfo and Image objects).
     */
    public void clearCache() {
        invalidURIs.clear();
        imageInfos.clear();
        images.clear();
        doHouseKeeping();
    }
   
    /**
     * Triggers some house-keeping, i.e. removes stale entries.
     */
    public void doHouseKeeping() {
        imageInfos.doHouseKeeping();
        images.doHouseKeeping();
    }
   
}
TOP

Related Classes of org.apache.xmlgraphics.image.loader.cache.ImageCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.