package vooga.rts.resourcemanager; import java.io.File; import java.io.FileNotFoundException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import vooga.rts.resourcemanager.exceptions.FileNotSupportedException; import vooga.rts.util.TimeIt; /** * This class manages the loading of all resources in the game. * It supports any kind of file and uses the correct ResourceLoader * in order to load the files of a certain type. The type of the file * is determined by the extension. * * @author Jonathan Schmidt * */ public class ResourceManager { private static final String EXTENSION_NOT_SUPPORTED = "The file %s has extension %s which is not supported by the Resource Manager."; private static ResourceManager myInstance = new ResourceManager(); /** * Maps the extension of a file to the appropriate Resource Loader. */ private Map<String, ResourceLoader> myLoaderMap; /** * Stores all of the information that the Resource Manager has loaded. */ private Map<URL, Object> myResourceStorage; /** * The queue of files to process. */ private Queue<URL> myLoadQueue; /** * Thread used to load all the files. */ private Thread myLoadThread; private TimeIt myTime; private String myResourceBase; private ResourceManager () { myLoaderMap = new HashMap<String, ResourceLoader>(); myResourceStorage = new HashMap<URL, Object>(); myLoadQueue = new LinkedList<URL>(); myLoadThread = new Thread(); myResourceBase = ""; } /** * Adds a Resource Loader to the Resource Manager and registers * the types of files that the loader can handle. * * @param loader */ public void registerResourceLoader (ResourceLoader loader) { for (String ext : loader.getSupportedExtensions()) { myLoaderMap.put(ext, loader); } } /** * Sets the base resource folder if you want to specify one. * This is where it will look for relative files first. * * @param base The base resource folder. */ public void setResourceBase (String base) { myResourceBase = base; } /** * Returns whether the Manager is still loading files. * * @return whether it is still loading. */ public boolean isLoading () { synchronized (myLoadThread) { return myLoadThread.isAlive(); } } /** * Queues a file for loading. Adds it to the list but only * loads the file from the disk when load() is called. * * @param filename The file name of the file to load. * @return Whether it was able to queue the file or not. * @throws FileNotSupportedException * @throws FileNotFoundException */ public boolean queueFile (String filename) throws FileNotSupportedException, FileNotFoundException { URL file = getFileName(filename); return queueFile(file); } /** * Queues a file for loading. Adds it to the list but only * loads the file from the disk when load() is called. * * @param filename The file name of the file to load. * @return Whether it was able to queue the file or not. * @throws FileNotSupportedException */ public boolean queueFile (URL filename) throws FileNotSupportedException { if (filename == null) { return false; } String ext = getExtension(filename.getPath()); if (!myLoaderMap.containsKey(ext)) { throw new FileNotSupportedException(String.format(EXTENSION_NOT_SUPPORTED, filename.getPath(), ext)); } synchronized (myResourceStorage) { if (myResourceStorage.containsKey(filename)) { return false; } } synchronized (myLoadQueue) { myLoadQueue.add(filename); return true; } } /** * Starts loading the resources that have been queued. */ public void load () { if (!isLoading()) { myLoadThread = new Thread(new Runnable() { @Override public void run () { loadFiles(); } }); myLoadThread.start(); } } /** * Loads all of the files in the queue. */ private void loadFiles () { synchronized (myLoadQueue) { while (!myLoadQueue.isEmpty()) { URL nextFile = myLoadQueue.poll(); loadFile(nextFile); } } } /** * Loads a file with a specified filename. * Passes it off to the correct resource loader. * * @param filename */ private void loadFile (URL filename) { String ext = getExtension(filename.getPath()); ResourceLoader rl = myLoaderMap.get(ext); Object loaded = rl.loadFile(filename); synchronized (myResourceStorage) { myResourceStorage.put(filename, loaded); } } /** * Returns a file with the specified file name. * If it's already loaded, then it returns the file. * If not, it then loads the files from the disk using * the correct loader and stores it before returning it. * * @param filename The filename to load. * @param resourceType The class type of resource that the file is of. * @return The object that was loaded. */ public <T> T getFile (String filename, Class<T> resourceType) { URL file; try { file = getFileName(filename); } catch (FileNotFoundException e1) { return null; } try { if (queueFile(file)) { load(); try { // Wait for loading to complete myLoadThread.join(); } catch (InterruptedException e) { System.out.println("Error waiting for threads."); } } } catch (FileNotSupportedException e) { return null; } Object loadedFile = myResourceStorage.get(file); if (resourceType.isAssignableFrom(loadedFile.getClass())) { return resourceType.cast(loadedFile); } return null; } /** * Converts the string filename to a URL. * If the file is not relative to the resources folder * then it needs to be an absolute path. * To set the absolute path, call setResourceBase() with * the resource folder location relative to the project. * e.g. "/vooga/rts/resources/" * * @param filename The filename of the file to convert * @return The converted URL * @throws FileNotFoundException */ private URL getFileName (String filename) throws FileNotFoundException { URL f = getClass().getResource(myResourceBase + filename); if (f == null) { f = getClass().getResource(filename); if (f != null) { return f; } else { File file = new File(filename); try { return new URL(file.getPath()); } catch (MalformedURLException e1) { throw new FileNotFoundException(filename); } } } return f; } /** * Returns the instance of the Resource Manager * * @return Reference to the Resource Manager */ public static ResourceManager getInstance () { return myInstance; } /** * Returns the extension of a particular filename. * * @param filename The file name including the extension * @return The extension */ public static String getExtension (String filename) { int index = filename.lastIndexOf("."); if (index > 0) { return filename.substring(index + 1); } return ""; } }