/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.awt.Image;
import java.awt.Toolkit;
import java.lang.ref.*;
import java.util.*;
import org.openide.ErrorManager;
/** Registers all loaded images into the AbstractNode, so nothing is loaded twice.
*
* @author Jaroslav Tulach
*/
final class IconManager extends Object {
/** a value that indicates that the icon does not exists */
private static final Object NO_ICON = new Object();
/** map of resource name to loaded icon (String, SoftRefrence (Image)) or (String, NO_ICON) */
private static final HashMap map = new HashMap ();
private static final HashMap reverseMap = new HashMap();
/** Resource paths for which we have had to strip initial slash.
* @see "#20072"
*/
private static final Set extraInitialSlashes = new HashSet(200); // Set<String>
/**
* Get the class loader from lookup.
* Since this is done very frequently, it is wasteful to query lookup each time.
* Instead, remember the last result and just listen for changes.
*/
private static ClassLoader getLoader() {
if (currentLoader == null) {
if (loaderQuery == null) {
loaderQuery = Lookup.getDefault().lookup(new Lookup.Template(ClassLoader.class));
loaderQuery.addLookupListener(new LookupListener() {
public void resultChanged(LookupEvent ev) {
currentLoader = null;
}
});
}
Iterator it = loaderQuery.allInstances().iterator();
if (it.hasNext()) {
currentLoader = (ClassLoader)it.next();
} else if (!noLoaderWarned) {
noLoaderWarned = true;
ErrorManager.getDefault().log(ErrorManager.WARNING, "No ClassLoader instance found in " + Lookup.getDefault());
}
}
return currentLoader;
}
private static ClassLoader currentLoader = null;
private static Lookup.Result loaderQuery = null;
private static boolean noLoaderWarned = false;
static Image getIcon (String name) {
Object img = map.get (name);
// no icon for this name (already tested)
if (img == NO_ICON) return null;
if (img != null) {
// then it is SoftRefrence
img = ((Reference)img).get ();
}
// icon found
if (img != null) return (Image)img;
ClassLoader loader = getLoader();
if (loader == null) return null;
return getIcon(name, loader);
}
/** Finds imager for given resource.
* @param name name of the resource
* @param loader classloader to use for locating it
*/
static Image getIcon (String name, ClassLoader loader) {
Object img = map.get (name);
// no icon for this name (already tested)
if (img == NO_ICON) return null;
if (img != null) {
// then it is SoftRefrence
img = ((Reference)img).get ();
}
// icon found
if (img != null) return (Image)img;
synchronized (map) {
// again under the lock
img = map.get (name);
// no icon for this name (already tested)
if (img == NO_ICON) return null;
if (img != null) {
// then it is SoftRefrence
img = ((Reference)img).get ();
}
if (img != null)
// cannot be NO_ICON, since it never disappears from the map.
return (Image)img;
// path for bug in classloader
String n;
boolean warn;
if (name.startsWith ("/")) { // NOI18N
warn = true;
n = name.substring (1);
} else {
warn = false;
n = name;
}
// we have to load it
java.net.URL url = loader != null ?
loader.getResource (n) : IconManager.class.getClassLoader ().getResource (n);
img = url == null ? null : Toolkit.getDefaultToolkit ().createImage (url);
if (img != null) {
if (warn && extraInitialSlashes.add(name)) {
ErrorManager.getDefault().log(ErrorManager.WARNING, "Initial slashes in Utilities.loadImage deprecated (cf. #20072): " + name); // NOI18N
}
Image img2 = toBufferedImage((Image) img);
//System.err.println("loading icon " + n + " = " + img2);
Reference r = new ActiveRef (img2);
map.put (name, r);
return (Image) img2;
} else {
// no icon found
map.put (name, NO_ICON);
return null;
}
}
}
/**
* Key used for composite images -- it holds image identities
*/
private static class CompositeImageKey {
Image baseImage, overlayImage;
int x, y;
CompositeImageKey(Image base, Image overlay, int x, int y) {
this.x = x;
this.y = y;
this.baseImage = base;
this.overlayImage = overlay;
}
public boolean equals(Object other) {
if (!(other instanceof CompositeImageKey))
return false;
CompositeImageKey k = (CompositeImageKey)other;
return (x == k.x) && (y == k.y) && (baseImage == k.baseImage) &&
(overlayImage == k.overlayImage);
}
public int hashCode() {
int hash = ((x << 3) ^ y) << 4;
hash = hash ^ baseImage.hashCode() ^ overlayImage.hashCode();
return hash;
}
public String toString() {
return "Composite key for " + baseImage + " + " + overlayImage + " at [" + x + ", " + y + "]"; // NOI18N
}
}
/**
* Method that attempts to find the merged image in the cache first, then
* creates the image if it was not found.
*/
static final Image mergeImages(Image im1, Image im2, int x, int y) {
CompositeImageKey k = new CompositeImageKey(im1, im2, x, y);
Image cached;
synchronized (map) {
Reference r = (Reference)map.get(k);
if (r != null) {
cached = (Image)r.get();
if (cached != null)
return cached;
}
cached = doMergeImages(im1, im2, x, y);
r = new ActiveRef (cached);
map.put(k, r);
reverseMap.put(r, k);
return cached;
}
}
/** The method creates a BufferedImage which represents the same Image as the
* parameter but consumes less memory.
*/
static final Image toBufferedImage(Image img) {
// load the image
new javax.swing.ImageIcon(img);
java.awt.image.BufferedImage rep = createBufferedImage(img.getWidth(null), img.getHeight(null));
java.awt.Graphics g = rep.createGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
img.flush();
return rep;
}
private static final Image doMergeImages (Image image1, Image image2, int x, int y) {
int w = 0, h = 0;
//System.err.println("merging images " + image1 + " + " + image2 + " at [" + x + ", " + y + "]");
if (image1 != null) {
w = image1.getWidth(null);
h = image1.getHeight(null);
}
if (image2 != null) {
w = image2.getWidth(null)+x > w ? image2.getWidth(null)+x : w;
h = image2.getHeight(null)+y > h ? image2.getHeight(null)+y : h;
}
if (w < 1) w = 16;
if (h < 1) h = 16;
java.awt.image.ColorModel model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment ().
getDefaultScreenDevice ().getDefaultConfiguration ().
getColorModel (java.awt.Transparency.BITMASK);
java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage (model,
model.createCompatibleWritableRaster (w, h), model.isAlphaPremultiplied (), null);
java.awt.Graphics g = buffImage.createGraphics ();
if (image1 != null) g.drawImage (image1, 0, 0, null);
if (image2 != null) g.drawImage (image2, x, y, null);
g.dispose();
return buffImage;
}
/** Creates BufferedImage 16x16 and Transparency.BITMASK */
static final java.awt.image.BufferedImage createBufferedImage(int width, int height) {
java.awt.image.ColorModel model = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().getColorModel(java.awt.Transparency.BITMASK);
java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage(model,
model.createCompatibleWritableRaster(width, height), model.isAlphaPremultiplied(), null);
return buffImage;
}
/** Cleaning reference. */
private static final class ActiveRef extends SoftReference implements Runnable {
public ActiveRef (Object o) {
super (o, Utilities.activeReferenceQueue());
}
public void run () {
Object k;
synchronized (map) {
k = reverseMap.remove(this);
map.remove(k);
}
}
} // end of ActiveRef
}