001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.cache;
003
004import java.awt.image.BufferedImage;
005import java.io.ByteArrayInputStream;
006import java.io.IOException;
007
008import javax.imageio.ImageIO;
009
010/**
011 * Cache Entry that has methods to get the BufferedImage, that will be cached along in memory
012 * but will be not serialized when saved to the disk (to avoid duplication of data)
013 *
014 * @author Wiktor Niesiobędzki
015 */
016public class BufferedImageCacheEntry extends CacheEntry {
017    private static final long serialVersionUID = 1L; //version
018    // transient to avoid serialization, volatile to avoid synchronization of whole getImage() method
019    private transient volatile BufferedImage img;
020    // we need to have separate control variable, to know, if we already tried to load the image, as img might be null
021    // after we loaded image, as for example, when image file is malformed (eg. HTML file)
022    private transient volatile boolean imageLoaded;
023
024    /**
025     *
026     * @param content byte array containing image
027     */
028    public BufferedImageCacheEntry(byte[] content) {
029        super(content);
030    }
031
032    /**
033     * Returns BufferedImage from for the content. Subsequent calls will return the same instance,
034     * to reduce overhead of ImageIO
035     *
036     * @return BufferedImage of cache entry content
037     * @throws IOException if an error occurs during reading.
038     */
039    public BufferedImage getImage() throws IOException {
040        if (imageLoaded)
041            return img;
042        synchronized (this) {
043            if (imageLoaded)
044                return img;
045            byte[] content = getContent();
046            if (content.length > 0) {
047                img = ImageIO.read(new ByteArrayInputStream(content));
048                imageLoaded = true;
049            }
050        }
051        return img;
052    }
053
054    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
055        /*
056         * This method below will be needed, if Apache Commons JCS (or any other caching system), will update
057         * disk representation of object from memory, once it is put into the cache (for example - at closing the cache)
058         *
059         * For now it is not the case, as we use DiskUsagePattern.UPDATE, which on JCS shutdown doesn't write again memory
060         * contents to file, so the fact, that we've cleared never gets saved to the disk
061         *
062         * This method is commented out, as it will convert all cache entries to PNG files regardless of what was returned.
063         * It might cause recompression/change of format which may result in decreased quality of imagery
064         */
065        /* synchronized (this) {
066            if (content == null && img != null) {
067                ByteArrayOutputStream restoredData = new ByteArrayOutputStream();
068                ImageIO.write(img, "png", restoredData);
069                content = restoredData.toByteArray();
070            }
071            out.writeObject(this);
072        }
073         */
074        synchronized (this) {
075            if (content == null && img != null) {
076                throw new AssertionError("Trying to serialize (save to disk?) an BufferedImageCacheEntry " +
077                        "that was converted to BufferedImage and no raw data is present anymore");
078            }
079            out.writeObject(this);
080
081            if (img != null) {
082                content = null;
083            }
084        }
085    }
086}