Sorry for the long post, but here is my latest resource loading code. It seems to work fine for loading resources from any jar file. I too keep my resources in a different jar than my code.
Here goes!
/**
* $Id$
*
* (c) 2004 Swizel Studios.
*/
package com.swizel.utils;
import com.swizel.exceptions.ResourceNotFoundException;
import com.swizel.exceptions.ResourceNotLoadedException;
import java.awt.Image;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.ImageIcon;
/**
* Resource loader and managment class.
*
* @author $author$
* @version $revision$
*/
public class ResourceUtils {
private static final float ONE_HUNDRED_PERCENT = 100.0f;
private static Hashtable<String, Object> resources = new Hashtable<String, Object>();
private static int resourceCount = 0;
private static int resourcesLoaded = 0;
/**
* Load the list of specified resources ready for use.
* @param resourceFile The file that lists the resources we want to load.
* @throws ResourceNotLoadedException If there is a problem loading one of the resources.
* @throws FileNotFoundException If the specified resource file can't be found.
* @throws IOException If the specified resource file can't be loaded.
*/
public static void loadResources(String resourceFile) throws FileNotFoundException, IOException, ResourceNotLoadedException {
Properties resourceProperties = new Properties();
resourceProperties.load(loadInputStream(resourceFile));
resourceCount = resourceProperties.size();
resourcesLoaded = 0;
Enumeration names = resourceProperties.propertyNames();
String[] propNames = new String[resourceProperties.size()];
int i=0;
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
propNames[i++] = name;
}
Arrays.sort(propNames, new ResourceComparator());
for (int j=0; j< propNames.length; j++) {
String propertyName = propNames[j].toLowerCase();
String propertyValue = resourceProperties.getProperty(propertyName);
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
}
// System.out.println("PropertyName : " + propertyName);
// System.out.println("PropertyValue : " + propertyValue);
if ((propertyValue != null) && (propertyValue.trim().length() > 0)) {
if (propertyName.indexOf("image") != -1) {
Image img = loadImage(propertyValue);
if (img != null) {
resources.put(propertyName, img);
} else {
throw new ResourceNotLoadedException(propertyName);
}
} else {
resources.put(propertyName, loadInputStream(propertyValue));
}
} else {
throw new ResourceNotLoadedException(propertyName + " has no value.");
}
resourcesLoaded ++;
}
}
/**
* Calculate how many resources have been loaded.
* @return The percentage of resources that have been loaded.
*/
public static int getloadResourcesProgress() {
return Math.round((ONE_HUNDRED_PERCENT / Math.max(1, resourceCount)) * resourcesLoaded);
}
/**
* Retrive a resource from the cache of resources.
* @param resourceName The name of the resource as specified inside the resource properties file.
* @throws ResourceNotFoundException If the specified resource can't be found.
* @throws ResourceNotLoadedException If the specified resource can't be found, but loading of resources is still taking place.
* @return The specified resource.
*/
public static Object getResource(String resourceName) throws ResourceNotFoundException, ResourceNotLoadedException {
String resourceNm = new String(resourceName.toLowerCase());
if (resources.containsKey(resourceNm)) {
return resources.get(resourceNm);
} else {
if (getloadResourcesProgress() != ONE_HUNDRED_PERCENT) {
throw new ResourceNotLoadedException(resourceNm);
} else {
throw new ResourceNotFoundException(resourceNm);
}
}
}
/**
* Remove a resource from the cache of resources, if the resource can't be found no further action takes place.
* @param resourceName The name of the resource to be deleted.
*/
public static void deleteResource(String resourceName) {
String resourceNm = new String(resourceName.toLowerCase());
if (resources.containsKey(resourceNm)) {
resources.remove(resourceNm);
}
}
/**
* Load an image, this method can load resources from jar files
* as well as from the local file system.
* @param pPath The path to the images being loaded.
* @return The loaded image.
*/
public static Image loadImage(String pPath) {
ClassLoader cl = ResourceUtils.class.getClassLoader();
ImageIcon lvIcon = null;
URL lvURL = cl.getResource(pPath);
if (lvURL == null) {
lvURL = ClassLoader.getSystemResource(pPath);
}
if (lvURL == null) {
lvIcon = new ImageIcon(pPath);
} else {
lvIcon = new ImageIcon(lvURL);
}
if (lvIcon == null) {
System.out.println("File not found : " + pPath);
return null;
} else {
return lvIcon.getImage();
}
}
/**
* Load a binary stream, this method can load resources from jar files
* as well as from the local file system.
* @param pPath The path to the resource being loaded.
* @return The InputStream of the resource.
*/
public static InputStream loadInputStream(String pPath) {
ClassLoader cl = ResourceUtils.class.getClassLoader();
try {
InputStream is = cl.getResourceAsStream(pPath);
if (is == null) {
is = ClassLoader.getSystemResourceAsStream(pPath);
}
if (is == null) {
is = new FileInputStream(pPath);
}
return is;
} catch (FileNotFoundException fnfe) {
System.out.println("File not found : " + pPath);
return null;
}
}
} // class
/**
* This class is used to place in sequence the resources
* loaded from a properties file.
*
* Fonts are loaded first, followed by the Splash screen,
* finally all other resources are loaded alphabetically.
*
*/
class ResourceComparator implements Comparator {
/**
* Compare two objects and order them according to the rules above.
* @param o1 The first object being compared.
* @param o2 The second object being compared.
* @return -1, 0 or 1 depending on tthe order of these two objects.
*/
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
if (s1.indexOf("font") != -1) {
return -1;
} else {
if (s2.indexOf("font") != -1) {
return 1;
} else {
if (s1.indexOf("splash") != -1) {
return -1;
} else {
if (s2.indexOf("splash") != -1) {
return 1;
} else {
return s1.compareTo(s2);
}
}
}
}
}
/**
* Test to see if a given object is equal to another object.
* @param obj The object being compared to this object.
* @return true if the objects are equal, false otherwise.
*/
public boolean equals(Object obj) {
return true;
}
} // class
/**
* Changelog
* ---------
* $log$
*
*/
loadResources() does most of the work, I load my resources in a certain order (hence the comparator), they’re listed in a properties file. This code also allows me to load them in the background in a seperate Thread so I can have a splash screen and progress bar. Later when I need a resource, I just call getResource().
Regards,
Andy.