package jas.util;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import javax.swing.ImageIcon;
/**
* A convenience class for creating small icons where the source file is stored in the
* CLASSPATH of the application, typically in the same JAR file as the class creating the
* image
*
* @author Tony Johnson
*/
public class JASIcon extends ImageIcon
{
/**
* Create a JASIcon, for use within the jas.util package
* The file is interpreted to be relative to jas.util
*/
JASIcon(String file) throws ImageException
{
this(null,file);
}
/**
* Create a JASIcon from the CLASSPATH.
* @param obj The object used as the root of the file path
* @param file The path to the image source file, relative to obj
* @exception ImageException Thrown if the image source file can not be found
* @see #create(Object obj, String file)
*/
public JASIcon(Object obj,String file) throws ImageException
{
this(obj == null ? null : obj.getClass(),file);
}
/**
* Create a JASIcon from the CLASSPATH.
* @param c The class used as the root of the file path
* @param file The path to the image source file, relative to the root class
* @exception ImageException Thrown if the image source file can not be found
* @see #create(Object c, String file)
*/
public JASIcon(Class c, String file) throws ImageException
{
m_c = c == null ? JASIcon.class : c;
setImageImpl(file);
}
/**
* Create a JASIcon without specifing the source
* @param Class c The class used as the root of the file path when setImage is called
* @see #setImage(String file)
*/
public JASIcon(Class c)
{
m_c = c == null ? JASIcon.class : c;
}
/**
* This method sets the image of the JASIcon to be that referenced by file.
* The image will display as "broken" if the file can not be found, no exception is thrown
* @param file The path to the image source file, relative to the root class
*
*/
public void setImage(String file)
{
try
{
setImageImpl(file);
}
catch (ImageException x)
{
setImage(brokenIcon.getImage());
}
}
private void setImageImpl(String file) throws ImageException
{
m_file = file;
Image image = (Image) imageCache.get(this);
if (image == null)
{
try
{
// First try getting a URL for the image, and loading the image using the URL.
java.net.URL url = m_c.getResource(file);
if (url != null)
{
image = Toolkit.getDefaultToolkit().getImage(url);
}
// Some classloaders (notably our own custom class loader for JAR files) cannot
// return a resource URL but can return the resource as an InputStream, so....
else
{
byte[] data = this.getImageBytes(m_c,m_file);
if (data == null) throw new ImageException();
image = Toolkit.getDefaultToolkit().createImage(data);
}
imageCache.put(this,image);
}
catch (ImageException x)
{
imageCache.put(this,noImage);
throw x;
}
}
else if (image == noImage) throw new ImageException();
setImage(image);
}
/**
* Read a string of byes from a file.
* We do this to work around a limitation in our custom class loader which results in them
* being able to suport getResourceAsStream but not getResource
* @see jas.loader.ClassPathLoader#getResource
*/
private byte[] getImageBytes(Class c, String file) throws ImageException
{
try
{
//System.out.println("JASIcon: Using fallback method of loading image "+file);
InputStream stream = c.getResourceAsStream(file);
if (stream == null) return null;
//TODO: There is no realiable way to tell how many bytes are in the stream
// so we have this horrible hack here.
byte[] result = new byte[Math.min(10000,stream.available())];
int size = 0;
for (;;)
{
if (size == result.length) throw new ImageException("Image too big for buffer");
int rc = stream.read(result,result.length-size,size);
if (rc<=0) break;
size += rc;
}
stream.close();
return result;
}
catch (IOException x)
{
return null;
}
}
/**
* Override Objects hashCode method
*/
public int hashCode()
{
return m_file.hashCode() + m_c.hashCode();
}
/**
* Override Objects equals method. Two images are considered equal if they have the same
* class and file.
*/
public boolean equals(Object in)
{
if (!(in instanceof JASIcon)) return false;
JASIcon icon = (JASIcon) in;
if (icon.m_c != m_c) return false;
return icon.m_file.equals(m_file);
}
/**
* Create a JASIcon but do not throw an exception if the source cannot be found
* (just displays a "broken" icon instead)
* @param obj The object used as the root of the file path
* @param file The path to the image source file, relative to obj
* @return The resulting JASIcon
*/
public static JASIcon create(Object obj,String file)
{
try
{
return new JASIcon(obj,file);
}
catch (ImageException x)
{
return brokenIcon;
}
}
/**
* Create a JASIcon but do not throw an exception if the source cannot be found
* (just displays a "broken" icon instead)
* //TODO: Should create return a cached version of the JASIcon, instead of a cached
* // version of the image with a new JASIcon object?
* @param obj The class used as the root of the file path
* @param file The path to the image source file, relative to obj
* @return The resulting JASIcon
*/
public static JASIcon create(Class c,String file)
{
try
{
return new JASIcon(c,file);
}
catch (ImageException x)
{
return brokenIcon;
}
}
private Class m_c;
private String m_file;
private static Hashtable imageCache = new Hashtable();
private static JASIcon brokenIcon;
private static Image noImage; // Used to flag non existent image in cache
static
{
try
{
brokenIcon = new JASIcon("brokenIcon.gif");
noImage = brokenIcon.getImage();
}
catch (ImageException x)
{
System.err.println("Could not load brokenIcon .. this looks bad!");
}
}
}