Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
//
|
||||
// $Id: Handler.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.resource;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
|
||||
import java.security.Permission;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.samskivert.io.ByteArrayOutInputStream;
|
||||
import com.samskivert.net.AttachableURLFactory;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.geom.GeomUtil;
|
||||
|
||||
/**
|
||||
* This class is not used directly, except by a registering ResourceManager
|
||||
* so that we can load data from the resource manager using URLs of the form
|
||||
* <code>resource://<resourceSet>/<path></code>. ResourceSet may
|
||||
* be the empty string to load from the default resource sets.
|
||||
*/
|
||||
public class Handler extends URLStreamHandler
|
||||
{
|
||||
/**
|
||||
* Register this class to handle "resource" urls
|
||||
* ("resource://<i>resourceSet</i>/<i>path</i>") with the specified
|
||||
* ResourceManager.
|
||||
*/
|
||||
public static void registerHandler (ResourceManager rmgr)
|
||||
{
|
||||
// if we already have a resource manager registered; don't
|
||||
// register another one
|
||||
if (_rmgr != null) {
|
||||
Log.warning("Refusing duplicate resource handler registration.");
|
||||
return;
|
||||
}
|
||||
_rmgr = rmgr;
|
||||
|
||||
// wire up our handler with the handy dandy attachable URL factory
|
||||
AttachableURLFactory.attachHandler("resource", Handler.class);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected int hashCode (URL url)
|
||||
{
|
||||
return String.valueOf(url).hashCode();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected boolean equals (URL u1, URL u2)
|
||||
{
|
||||
return String.valueOf(u1).equals(String.valueOf(u2));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected URLConnection openConnection (URL url)
|
||||
throws IOException
|
||||
{
|
||||
return new URLConnection(url) {
|
||||
// documentation inherited
|
||||
public void connect ()
|
||||
throws IOException
|
||||
{
|
||||
// the host is the bundle name
|
||||
String bundle = this.url.getHost();
|
||||
// and we need to remove the leading '/' from path;
|
||||
String path = this.url.getPath().substring(1);
|
||||
try {
|
||||
// if there are query parameters, we need special magic
|
||||
String query = url.getQuery();
|
||||
if (!StringUtil.isBlank(query)) {
|
||||
_stream = getStream(bundle, path, query);
|
||||
} else if (StringUtil.isBlank(bundle)) {
|
||||
_stream = _rmgr.getResource(path);
|
||||
} else {
|
||||
_stream = _rmgr.getResource(bundle, path);
|
||||
}
|
||||
this.connected = true;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Could not find resource [url=" + this.url +
|
||||
", error=" + ioe.getMessage() + "].");
|
||||
throw ioe; // rethrow
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public InputStream getInputStream ()
|
||||
throws IOException
|
||||
{
|
||||
if (!this.connected) {
|
||||
connect();
|
||||
}
|
||||
return _stream;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Permission getPermission ()
|
||||
throws IOException
|
||||
{
|
||||
// We allow anything in the resource bundle to be loaded
|
||||
// without any permission restrictions.
|
||||
return null;
|
||||
}
|
||||
|
||||
protected InputStream _stream;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does some magic to allow a subset of an image to be extracted,
|
||||
* reencoded as a PNG and then spat back out to the Java content
|
||||
* handler system for inclusion in internal documentation.
|
||||
*/
|
||||
protected InputStream getStream (String bundle, String path, String query)
|
||||
throws IOException
|
||||
{
|
||||
// we can only do this with PNGs
|
||||
if (!path.endsWith(".png")) {
|
||||
Log.warning("Requested sub-tile of non-PNG resource " +
|
||||
"[bundle=" + bundle + ", path=" + path +
|
||||
", dims=" + query + "].");
|
||||
return _rmgr.getResource(bundle, path);
|
||||
}
|
||||
|
||||
// parse the query string
|
||||
String[] bits = StringUtil.split(query, "&");
|
||||
int width = -1, height = -1, tidx = -1;
|
||||
try {
|
||||
for (int ii = 0; ii < bits.length; ii++) {
|
||||
if (bits[ii].startsWith("width=")) {
|
||||
width = Integer.parseInt(bits[ii].substring(6));
|
||||
} else if (bits[ii].startsWith("height=")) {
|
||||
height = Integer.parseInt(bits[ii].substring(7));
|
||||
} else if (bits[ii].startsWith("tile=")) {
|
||||
tidx = Integer.parseInt(bits[ii].substring(5));
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
if (width <= 0 || height <= 0 || tidx < 0) {
|
||||
Log.warning("Bogus sub-image dimensions [bundle=" + bundle +
|
||||
", path=" + path + ", dims=" + query + "].");
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
|
||||
// locate the tile image, then write that subimage back out in PNG
|
||||
// format into memory and return an input stream for that
|
||||
ImageInputStream stream =
|
||||
StringUtil.isBlank(bundle) ? _rmgr.getImageResource(path)
|
||||
: _rmgr.getImageResource(bundle, path);
|
||||
BufferedImage src = ImageIO.read(stream);
|
||||
Rectangle trect = GeomUtil.getTile(
|
||||
src.getWidth(), src.getHeight(), width, height, tidx);
|
||||
BufferedImage tile = src.getSubimage(
|
||||
trect.x, trect.y, trect.width, trect.height);
|
||||
ByteArrayOutInputStream data = new ByteArrayOutInputStream();
|
||||
ImageIO.write(tile, "PNG", data);
|
||||
return data.getInputStream();
|
||||
}
|
||||
|
||||
/** Our singleton resource manager. */
|
||||
protected static ResourceManager _rmgr;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.resource;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the resource management package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("resource");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
//
|
||||
// $Id: ResourceBundle.java 3998 2006-04-04 18:46:42Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.resource;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import com.samskivert.util.FileUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import org.apache.commons.io.CopyUtils;
|
||||
|
||||
/**
|
||||
* A resource bundle provides access to the resources in a jar file.
|
||||
*/
|
||||
public class ResourceBundle
|
||||
{
|
||||
/**
|
||||
* Constructs a resource bundle with the supplied jar file.
|
||||
*
|
||||
* @param source a file object that references our source jar file.
|
||||
*/
|
||||
public ResourceBundle (File source)
|
||||
{
|
||||
this(source, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a resource bundle with the supplied jar file.
|
||||
*
|
||||
* @param source a file object that references our source jar file.
|
||||
* @param delay if true, the bundle will wait until someone calls
|
||||
* {@link #sourceIsReady} before allowing access to its resources.
|
||||
* @param unpack if true the bundle will unpack itself into a
|
||||
* temporary directory
|
||||
*/
|
||||
public ResourceBundle (File source, boolean delay, boolean unpack)
|
||||
{
|
||||
_source = source;
|
||||
if (unpack) {
|
||||
String root = stripSuffix(source.getPath());
|
||||
_unpacked = new File(root + ".stamp");
|
||||
_cache = new File(root);
|
||||
}
|
||||
|
||||
if (!delay) {
|
||||
sourceIsReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link File} from which resources are fetched for this
|
||||
* bundle.
|
||||
*/
|
||||
public File getSource ()
|
||||
{
|
||||
return _source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the bundle is fully downloaded and successfully
|
||||
* unpacked.
|
||||
*/
|
||||
public boolean isUnpacked ()
|
||||
{
|
||||
return (_source.exists() && _unpacked != null &&
|
||||
_unpacked.lastModified() == _source.lastModified());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the resource manager once it has ensured that our
|
||||
* resource jar file is up to date and ready for reading.
|
||||
*
|
||||
* @return true if we successfully unpacked our resources, false if we
|
||||
* encountered errors in doing so.
|
||||
*/
|
||||
public boolean sourceIsReady ()
|
||||
{
|
||||
// make a note of our source's last modification time
|
||||
_sourceLastMod = _source.lastModified();
|
||||
|
||||
// if we are unpacking files, the time to do so is now
|
||||
if (_unpacked != null && _unpacked.lastModified() != _sourceLastMod) {
|
||||
try {
|
||||
resolveJarFile();
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Failure resolving jar file '" + _source +
|
||||
"': " + ioe + ".");
|
||||
wipeBundle(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.info("Unpacking into " + _cache + "...");
|
||||
if (!_cache.exists()) {
|
||||
if (!_cache.mkdir()) {
|
||||
Log.warning("Failed to create bundle cache directory '" +
|
||||
_cache + "'.");
|
||||
closeJar();
|
||||
// we are hopelessly fucked
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
FileUtil.recursiveClean(_cache);
|
||||
}
|
||||
|
||||
// unpack the jar file (this will close the jar when it's done)
|
||||
if (!FileUtil.unpackJar(_jarSource, _cache)) {
|
||||
// if something went awry, delete everything in the hopes
|
||||
// that next time things will work
|
||||
wipeBundle(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if everything unpacked smoothly, create our unpack stamp
|
||||
try {
|
||||
_unpacked.createNewFile();
|
||||
if (!_unpacked.setLastModified(_sourceLastMod)) {
|
||||
Log.warning("Failed to set last mod on stamp file '" +
|
||||
_unpacked + "'.");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Failure creating stamp file '" + _unpacked +
|
||||
"': " + ioe + ".");
|
||||
// no need to stick a fork in things at this point
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out everything associated with this resource bundle in the
|
||||
* hopes that we can download it afresh and everything will work the
|
||||
* next time around.
|
||||
*/
|
||||
public void wipeBundle (boolean deleteJar)
|
||||
{
|
||||
// clear out our cache directory
|
||||
if (_cache != null) {
|
||||
FileUtil.recursiveClean(_cache);
|
||||
}
|
||||
|
||||
// delete our unpack stamp file
|
||||
if (_unpacked != null) {
|
||||
_unpacked.delete();
|
||||
}
|
||||
|
||||
// clear out any .jarv file that Getdown might be maintaining so
|
||||
// that we ensure that it is revalidated
|
||||
File vfile = new File(FileUtil.resuffix(_source, ".jar", ".jarv"));
|
||||
if (vfile.exists() && !vfile.delete()) {
|
||||
Log.warning("Failed to delete " + vfile + ".");
|
||||
}
|
||||
|
||||
// close and delete our source jar file
|
||||
if (deleteJar && _source != null) {
|
||||
closeJar();
|
||||
if (!_source.delete()) {
|
||||
Log.warning("Failed to delete " + _source +
|
||||
" [exists=" + _source.exists() + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the named resource from this bundle. The path should be
|
||||
* specified as a relative, platform independent path (forward
|
||||
* slashes). For example <code>sounds/scream.au</code>.
|
||||
*
|
||||
* @param path the path to the resource in this jar file.
|
||||
*
|
||||
* @return an input stream from which the resource can be loaded or
|
||||
* null if no such resource exists.
|
||||
*
|
||||
* @exception IOException thrown if an error occurs locating the
|
||||
* resource in the jar file.
|
||||
*/
|
||||
public InputStream getResource (String path)
|
||||
throws IOException
|
||||
{
|
||||
// unpack our resources into a temp directory so that we can load
|
||||
// them quickly and the file system can cache them sensibly
|
||||
File rfile = getResourceFile(path);
|
||||
return (rfile == null) ? null : new FileInputStream(rfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file from which the specified resource can be loaded.
|
||||
* This method will unpack the resource into a temporary directory and
|
||||
* return a reference to that file.
|
||||
*
|
||||
* @param path the path to the resource in this jar file.
|
||||
*
|
||||
* @return a file from which the resource can be loaded or null if no
|
||||
* such resource exists.
|
||||
*/
|
||||
public File getResourceFile (String path)
|
||||
throws IOException
|
||||
{
|
||||
if (resolveJarFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if we have been unpacked, return our unpacked file
|
||||
if (_cache != null) {
|
||||
File cfile = new File(_cache, path);
|
||||
if (cfile.exists()) {
|
||||
return cfile;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, we unpack resources as needed into a temp directory
|
||||
String tpath = StringUtil.md5hex(_source.getPath() + "%" + path);
|
||||
File tfile = new File(getCacheDir(), tpath);
|
||||
if (tfile.exists() && (tfile.lastModified() > _sourceLastMod)) {
|
||||
return tfile;
|
||||
}
|
||||
|
||||
JarEntry entry = _jarSource.getJarEntry(path);
|
||||
if (entry == null) {
|
||||
// Log.info("Couldn't locate " + path + " in " + _jarSource + ".");
|
||||
return null;
|
||||
}
|
||||
|
||||
// copy the resource into the temporary file
|
||||
BufferedOutputStream fout =
|
||||
new BufferedOutputStream(new FileOutputStream(tfile));
|
||||
InputStream jin = _jarSource.getInputStream(entry);
|
||||
CopyUtils.copy(jin, fout);
|
||||
jin.close();
|
||||
fout.close();
|
||||
|
||||
return tfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this resource bundle contains the resource with the
|
||||
* specified path. This avoids actually loading the resource, in the
|
||||
* event that the caller only cares to know that the resource exists.
|
||||
*/
|
||||
public boolean containsResource (String path)
|
||||
{
|
||||
try {
|
||||
if (resolveJarFile()) {
|
||||
return false;
|
||||
}
|
||||
return (_jarSource.getJarEntry(path) != null);
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this resource bundle.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
try {
|
||||
resolveJarFile();
|
||||
return (_jarSource == null) ? "[file=" + _source + "]" :
|
||||
"[path=" + _jarSource.getName() + "]";
|
||||
|
||||
} catch (IOException ioe) {
|
||||
return "[file=" + _source + ", ioe=" + ioe + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the internal jar file reference if we've not already got
|
||||
* it; we do this lazily so as to avoid any jar- or zip-file-related
|
||||
* antics until and unless doing so is required, and because the
|
||||
* resource manager would like to be able to create bundles before the
|
||||
* associated files have been fully downloaded.
|
||||
*
|
||||
* @return true if the jar file could not yet be resolved because we
|
||||
* haven't yet heard from the resource manager that it is ready for us
|
||||
* to access, false if all is cool.
|
||||
*/
|
||||
protected boolean resolveJarFile ()
|
||||
throws IOException
|
||||
{
|
||||
// if we don't yet have our resource bundle's last mod time, we
|
||||
// have not yet been notified that it is ready
|
||||
if (_sourceLastMod == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_source.exists()) {
|
||||
throw new IOException("Missing jar file for resource bundle: " +
|
||||
_source + ".");
|
||||
}
|
||||
|
||||
try {
|
||||
if (_jarSource == null) {
|
||||
_jarSource = new JarFile(_source);
|
||||
}
|
||||
return false;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
String msg = "Failed to resolve resource bundle jar file '" +
|
||||
_source + "'";
|
||||
Log.warning(msg + ".");
|
||||
Log.logStackTrace(ioe);
|
||||
throw (IOException) new IOException(msg).initCause(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes our (possibly opened) jar file.
|
||||
*/
|
||||
protected void closeJar ()
|
||||
{
|
||||
try {
|
||||
if (_jarSource != null) {
|
||||
_jarSource.close();
|
||||
}
|
||||
} catch (Exception ioe) {
|
||||
Log.warning("Failed to close jar file [path=" + _source +
|
||||
", error=" + ioe + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache directory used for unpacked resources.
|
||||
*/
|
||||
public static File getCacheDir ()
|
||||
{
|
||||
if (_tmpdir == null) {
|
||||
String tmpdir = System.getProperty("java.io.tmpdir");
|
||||
if (tmpdir == null) {
|
||||
Log.info("No system defined temp directory. Faking it.");
|
||||
tmpdir = System.getProperty("user.home");
|
||||
}
|
||||
setCacheDir(new File(tmpdir, ".narcache"));
|
||||
}
|
||||
return _tmpdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the directory in which our temporary resource files
|
||||
* should be stored.
|
||||
*/
|
||||
public static void setCacheDir (File tmpdir)
|
||||
{
|
||||
String rando = Long.toHexString((long)(Math.random() * Long.MAX_VALUE));
|
||||
_tmpdir = new File(tmpdir, rando);
|
||||
if (!_tmpdir.exists()) {
|
||||
Log.info("Creating narya temp cache directory '" + _tmpdir + "'.");
|
||||
_tmpdir.mkdirs();
|
||||
}
|
||||
|
||||
// add a hook to blow away the temp directory when we exit
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
public void run () {
|
||||
Log.info("Clearing narya temp cache '" + _tmpdir + "'.");
|
||||
FileUtil.recursiveDelete(_tmpdir);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Strips the .jar off of jar file paths. */
|
||||
protected static String stripSuffix (String path)
|
||||
{
|
||||
if (path.endsWith(".jar")) {
|
||||
return path.substring(0, path.length()-4);
|
||||
} else {
|
||||
// we have to change the path somehow
|
||||
return path + "-cache";
|
||||
}
|
||||
}
|
||||
|
||||
/** The file from which we construct our jar file. */
|
||||
protected File _source;
|
||||
|
||||
/** The last modified time of our source jar file. */
|
||||
protected long _sourceLastMod = -1;
|
||||
|
||||
/** A file whose timestamp indicates whether or not our existing jar
|
||||
* file has been unpacked. */
|
||||
protected File _unpacked;
|
||||
|
||||
/** A directory into which we unpack files from our bundle. */
|
||||
protected File _cache;
|
||||
|
||||
/** The jar file from which we load resources. */
|
||||
protected JarFile _jarSource;
|
||||
|
||||
/** A directory in which we temporarily unpack our resource files. */
|
||||
protected static File _tmpdir;
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
//
|
||||
// $Id: ResourceManager.java 4000 2006-04-05 19:40:12Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.resource;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import javax.imageio.stream.FileImageInputStream;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
|
||||
import com.samskivert.net.PathUtil;
|
||||
import com.samskivert.util.ResultListener;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The resource manager is responsible for maintaining a repository of
|
||||
* resources that are synchronized with a remote source. This is
|
||||
* accomplished in the form of sets of jar files (resource bundles) that
|
||||
* contain resources and that are updated from a remote resource
|
||||
* repository via HTTP. These resource bundles are organized into
|
||||
* resource sets. A resource set contains one or more resource bundles and
|
||||
* is defined much like a classpath.
|
||||
*
|
||||
* <p> The resource manager can load resources from the default resource
|
||||
* set, and can make available named resource sets to entities that wish
|
||||
* to do their own resource loading. If the resource manager fails to
|
||||
* locate a resource in the default resource set, it falls back to loading
|
||||
* the resource via the classloader (which will search the classpath).
|
||||
*
|
||||
* <p> Applications that wish to make use of resource sets and their
|
||||
* associated bundles must call {@link #initBundles} after constructing
|
||||
* the resource manager, providing the path to a resource definition file
|
||||
* which describes these resource sets. The definition file will be loaded
|
||||
* and the resource bundles defined within will be loaded relative to the
|
||||
* resource directory. The bundles will be cached in the user's home
|
||||
* directory and only reloaded when the source resources have been
|
||||
* updated. The resource definition file looks something like the
|
||||
* following:
|
||||
*
|
||||
* <pre>
|
||||
* resource.set.default = sets/misc/config.jar: \
|
||||
* sets/misc/icons.jar
|
||||
* resource.set.tiles = sets/tiles/ground.jar: \
|
||||
* sets/tiles/objects.jar: \
|
||||
* /global/resources/tiles/ground.jar: \
|
||||
* /global/resources/tiles/objects.jar
|
||||
* resource.set.sounds = sets/sounds/sfx.jar: \
|
||||
* sets/sounds/music.jar: \
|
||||
* /global/resources/sounds/sfx.jar: \
|
||||
* /global/resources/sounds/music.jar
|
||||
* </pre>
|
||||
*
|
||||
* <p> All resource set definitions are prefixed with
|
||||
* <code>resource.set.</code> and all text following that string is
|
||||
* considered to be the name of the resource set. The resource set named
|
||||
* <code>default</code> is the default resource set and is the one that is
|
||||
* searched for resources is a call to {@link #getResource}.
|
||||
*
|
||||
* <p> When a resource is loaded from a resource set, the set is searched
|
||||
* in the order that entries are specified in the definition.
|
||||
*/
|
||||
public class ResourceManager
|
||||
{
|
||||
/**
|
||||
* Provides facilities for notifying an observer of the resource
|
||||
* unpacking process.
|
||||
*/
|
||||
public interface InitObserver
|
||||
{
|
||||
/**
|
||||
* Indicates a percent completion along with an estimated time
|
||||
* remaining in seconds.
|
||||
*/
|
||||
public void progress (int percent, long remaining);
|
||||
|
||||
/**
|
||||
* Indicates that there was a failure unpacking our resource
|
||||
* bundles.
|
||||
*/
|
||||
public void initializationFailed (Exception e);
|
||||
}
|
||||
|
||||
/**
|
||||
* An adapter that wraps an {@link InitObserver} and routes all method
|
||||
* invocations to the AWT thread.
|
||||
*/
|
||||
public static class AWTInitObserver implements InitObserver
|
||||
{
|
||||
public AWTInitObserver (InitObserver obs) {
|
||||
_obs = obs;
|
||||
}
|
||||
|
||||
public void progress (final int percent, final long remaining) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run () {
|
||||
_obs.progress(percent, remaining);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void initializationFailed (final Exception e) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run () {
|
||||
_obs.initializationFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected InitObserver _obs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a resource manager which will load resources via the
|
||||
* classloader, prepending <code>resourceRoot</code> to their path.
|
||||
*
|
||||
* @param resourceRoot the path to prepend to resource paths prior to
|
||||
* attempting to load them via the classloader. When resources are
|
||||
* bundled into the default resource bundle, they don't need this
|
||||
* prefix, but if they're to be loaded from the classpath, it's likely
|
||||
* that they'll live in some sort of <code>resources</code> directory
|
||||
* to isolate them from the rest of the files in the classpath. This
|
||||
* is not a platform dependent path (forward slash is always used to
|
||||
* separate path elements).
|
||||
*/
|
||||
public ResourceManager (String resourceRoot)
|
||||
{
|
||||
this(resourceRoot, ResourceManager.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a resource manager with the specified class loader via
|
||||
* which to load classes. See {@link #ResourceManager(String)} for
|
||||
* further documentation.
|
||||
*/
|
||||
public ResourceManager (String resourceRoot, ClassLoader loader)
|
||||
{
|
||||
_rootPath = resourceRoot;
|
||||
_loader = loader;
|
||||
|
||||
// get our resource directory from resource_dir if possible
|
||||
initResourceDir(null);
|
||||
|
||||
// set up a URL handler so that things can be loaded via urls
|
||||
// with the 'resource' protocol
|
||||
AccessController.doPrivileged(new PrivilegedAction() {
|
||||
public Object run () {
|
||||
Handler.registerHandler(ResourceManager.this);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bundle sets to be made available by this resource
|
||||
* manager. Applications that wish to make use of resource bundles
|
||||
* should call this method after constructing the resource manager.
|
||||
*
|
||||
* @param resourceDir the base directory to which the paths in the
|
||||
* supplied configuration file are relative. If this is null, the
|
||||
* system property <code>resource_dir</code> will be used, if
|
||||
* available.
|
||||
* @param configPath the path (relative to the resource dir) of the
|
||||
* resource definition file.
|
||||
* @param initObs a bundle initialization observer to notify of
|
||||
* unpacking progress and success or failure, or <code>null</code> if
|
||||
* the caller doesn't care to be informed; note that in the latter
|
||||
* case, the calling thread will block until bundle unpacking is
|
||||
* complete.
|
||||
*
|
||||
* @exception IOException thrown if we are unable to read our resource
|
||||
* manager configuration.
|
||||
*/
|
||||
public void initBundles (
|
||||
String resourceDir, String configPath, InitObserver initObs)
|
||||
throws IOException
|
||||
{
|
||||
// reinitialize our resource dir if it was specified
|
||||
if (resourceDir != null) {
|
||||
initResourceDir(resourceDir);
|
||||
}
|
||||
|
||||
// check to see if we're in developer mode in which case we won't
|
||||
// unpack our resources
|
||||
_unpack = !"true".equalsIgnoreCase(
|
||||
System.getProperty("no_unpack_resources"));
|
||||
|
||||
// load up our configuration
|
||||
Properties config = new Properties();
|
||||
try {
|
||||
config.load(new FileInputStream(new File(_rdir, configPath)));
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Unable to load resource manager config " +
|
||||
"[rdir=" + _rdir + ", cpath=" + configPath + "]";
|
||||
Log.warning(errmsg + ".");
|
||||
Log.logStackTrace(e);
|
||||
throw new IOException(errmsg);
|
||||
}
|
||||
|
||||
// resolve the configured resource sets
|
||||
ArrayList dlist = new ArrayList();
|
||||
Enumeration names = config.propertyNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String key = (String)names.nextElement();
|
||||
if (!key.startsWith(RESOURCE_SET_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
String setName = key.substring(RESOURCE_SET_PREFIX.length());
|
||||
resolveResourceSet(setName, config.getProperty(key), dlist);
|
||||
}
|
||||
|
||||
// if an observer was passed in, then we do not need to block
|
||||
// the caller
|
||||
final boolean[] shouldWait = new boolean[] { false };
|
||||
if (initObs == null) {
|
||||
// if there's no observer, we'll need to block the caller
|
||||
shouldWait[0] = true;
|
||||
initObs = new InitObserver() {
|
||||
public void progress (int percent, long remaining) {
|
||||
if (percent >= 100) {
|
||||
synchronized (this) {
|
||||
// turn off shouldWait, in case we reached
|
||||
// 100% progress before the calling thread even
|
||||
// gets a chance to get to the blocking code, below
|
||||
shouldWait[0] = false;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void initializationFailed (Exception e) {
|
||||
synchronized (this) {
|
||||
shouldWait[0] = false;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// start a thread to unpack our bundles
|
||||
Unpacker unpack = new Unpacker(dlist, initObs);
|
||||
unpack.start();
|
||||
|
||||
if (shouldWait[0]) {
|
||||
synchronized (initObs) {
|
||||
if (shouldWait[0]) {
|
||||
try {
|
||||
initObs.wait();
|
||||
} catch (InterruptedException ie) {
|
||||
Log.warning("Interrupted while waiting for bundles " +
|
||||
"to unpack.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path relative to the resource directory, the path is
|
||||
* properly jimmied (assuming we always use /) and combined with the
|
||||
* resource directory to yield a {@link File} object that can be used
|
||||
* to access the resource.
|
||||
*
|
||||
* @return a file referencing the specified resource or null if the
|
||||
* resource manager was never configured with a resource directory.
|
||||
*/
|
||||
public File getResourceFile (String path)
|
||||
{
|
||||
if (_rdir == null) {
|
||||
return null;
|
||||
}
|
||||
if (!"/".equals(File.separator)) {
|
||||
path = StringUtil.replace(path, "/", File.separator);
|
||||
}
|
||||
return new File(_rdir, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the specified bundle exists, is unpacked and is
|
||||
* ready to be used.
|
||||
*/
|
||||
public boolean checkBundle (String path)
|
||||
{
|
||||
File bfile = getResourceFile(path);
|
||||
return (bfile == null) ? false :
|
||||
new ResourceBundle(bfile, true, _unpack).isUnpacked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified bundle (the bundle file must already exist in
|
||||
* the appropriate place on the file system) and return it on the
|
||||
* specified result listener. Note that the result listener may be
|
||||
* notified before this method returns on the caller's thread if the
|
||||
* bundle is already resolved, or it may be notified on a brand new
|
||||
* thread if the bundle requires unpacking.
|
||||
*/
|
||||
public void resolveBundle (String path, final ResultListener listener)
|
||||
{
|
||||
File bfile = getResourceFile(path);
|
||||
if (bfile == null) {
|
||||
String errmsg = "ResourceManager not configured with " +
|
||||
"resource directory.";
|
||||
listener.requestFailed(new IOException(errmsg));
|
||||
return;
|
||||
}
|
||||
|
||||
final ResourceBundle bundle = new ResourceBundle(bfile, true, _unpack);
|
||||
if (bundle.isUnpacked()) {
|
||||
if (bundle.sourceIsReady()) {
|
||||
listener.requestCompleted(bundle);
|
||||
} else {
|
||||
String errmsg = "Bundle initialization failed.";
|
||||
listener.requestFailed(new IOException(errmsg));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// start a thread to unpack our bundles
|
||||
ArrayList list = new ArrayList();
|
||||
list.add(bundle);
|
||||
Unpacker unpack = new Unpacker(list, new InitObserver() {
|
||||
public void progress (int percent, long remaining) {
|
||||
if (percent == 100) {
|
||||
listener.requestCompleted(bundle);
|
||||
}
|
||||
}
|
||||
public void initializationFailed (Exception e) {
|
||||
listener.requestFailed(e);
|
||||
}
|
||||
});
|
||||
unpack.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class loader being used to load resources if/when there
|
||||
* are no resource bundles from which to load them.
|
||||
*/
|
||||
public ClassLoader getClassLoader ()
|
||||
{
|
||||
return _loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the class loader this manager should use to load
|
||||
* resources if/when there are no bundles from which to load them.
|
||||
*/
|
||||
public void setClassLoader (ClassLoader loader)
|
||||
{
|
||||
_loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a resource from the local repository.
|
||||
*
|
||||
* @param path the path to the resource
|
||||
* (ie. "config/miso.properties"). This should not begin with a slash.
|
||||
*
|
||||
* @exception IOException thrown if a problem occurs locating or
|
||||
* reading the resource.
|
||||
*/
|
||||
public InputStream getResource (String path)
|
||||
throws IOException
|
||||
{
|
||||
InputStream in = null;
|
||||
|
||||
// first look for this resource in our default resource bundle
|
||||
for (int i = 0; i < _default.length; i++) {
|
||||
in = _default[i].getResource(path);
|
||||
if (in != null) {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback next to an unpacked resource file
|
||||
File file = getResourceFile(path);
|
||||
if (file != null && file.exists()) {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
|
||||
// if we still didn't find anything, try the classloader
|
||||
final String rpath = PathUtil.appendPath(_rootPath, path);
|
||||
in = (InputStream)AccessController.doPrivileged(new PrivilegedAction() {
|
||||
public Object run () {
|
||||
return _loader.getResourceAsStream(rpath);
|
||||
}
|
||||
});
|
||||
if (in != null) {
|
||||
return in;
|
||||
}
|
||||
|
||||
// if we still haven't found it, we throw an exception
|
||||
String errmsg = "Unable to locate resource [path=" + path + "]";
|
||||
throw new FileNotFoundException(errmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the specified resource as an {@link ImageInputStream} and
|
||||
* one that takes advantage, if possible, of caching of unpacked
|
||||
* resources on the local filesystem.
|
||||
*
|
||||
* @exception FileNotFoundException thrown if the resource could not
|
||||
* be located in any of the bundles in the specified set, or if the
|
||||
* specified set does not exist.
|
||||
* @exception IOException thrown if a problem occurs locating or
|
||||
* reading the resource.
|
||||
*/
|
||||
public ImageInputStream getImageResource (String path)
|
||||
throws IOException
|
||||
{
|
||||
// first look for this resource in our default resource bundle
|
||||
for (int i = 0; i < _default.length; i++) {
|
||||
File file = _default[i].getResourceFile(path);
|
||||
if (file != null) {
|
||||
return new FileImageInputStream(file);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback next to an unpacked resource file
|
||||
File file = getResourceFile(path);
|
||||
if (file != null && file.exists()) {
|
||||
return new FileImageInputStream(file);
|
||||
}
|
||||
|
||||
// if we still didn't find anything, try the classloader
|
||||
final String rpath = PathUtil.appendPath(_rootPath, path);
|
||||
InputStream in = (InputStream)
|
||||
AccessController.doPrivileged(new PrivilegedAction() {
|
||||
public Object run () {
|
||||
return _loader.getResourceAsStream(rpath);
|
||||
}
|
||||
});
|
||||
if (in != null) {
|
||||
return new MemoryCacheImageInputStream(new BufferedInputStream(in));
|
||||
}
|
||||
|
||||
// if we still haven't found it, we throw an exception
|
||||
String errmsg = "Unable to locate image resource [path=" + path + "]";
|
||||
throw new FileNotFoundException(errmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream from which the requested resource can be
|
||||
* loaded. <em>Note:</em> this performs a linear search of all of the
|
||||
* bundles in the set and returns the first resource found with the
|
||||
* specified path, thus it is not extremely efficient and will behave
|
||||
* unexpectedly if you use the same paths in different resource
|
||||
* bundles.
|
||||
*
|
||||
* @exception FileNotFoundException thrown if the resource could not
|
||||
* be located in any of the bundles in the specified set, or if the
|
||||
* specified set does not exist.
|
||||
* @exception IOException thrown if a problem occurs locating or
|
||||
* reading the resource.
|
||||
*/
|
||||
public InputStream getResource (String rset, String path)
|
||||
throws IOException
|
||||
{
|
||||
// grab the resource bundles in the specified resource set
|
||||
ResourceBundle[] bundles = getResourceSet(rset);
|
||||
if (bundles == null) {
|
||||
throw new FileNotFoundException(
|
||||
"Unable to locate resource [set=" + rset +
|
||||
", path=" + path + "]");
|
||||
}
|
||||
|
||||
// look for the resource in any of the bundles
|
||||
int size = bundles.length;
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
InputStream instr = bundles[ii].getResource(path);
|
||||
if (instr != null) {
|
||||
// Log.info("Found resource [rset=" + rset +
|
||||
// ", bundle=" + bundles[ii].getSource().getPath() +
|
||||
// ", path=" + path + ", in=" + instr + "].");
|
||||
return instr;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(
|
||||
"Unable to locate resource [set=" + rset + ", path=" + path + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the specified resource as an {@link ImageInputStream} and
|
||||
* one that takes advantage, if possible, of caching of unpacked
|
||||
* resources on the local filesystem.
|
||||
*
|
||||
* @exception FileNotFoundException thrown if the resource could not
|
||||
* be located in any of the bundles in the specified set, or if the
|
||||
* specified set does not exist.
|
||||
* @exception IOException thrown if a problem occurs locating or
|
||||
* reading the resource.
|
||||
*/
|
||||
public ImageInputStream getImageResource (String rset, String path)
|
||||
throws IOException
|
||||
{
|
||||
// grab the resource bundles in the specified resource set
|
||||
ResourceBundle[] bundles = getResourceSet(rset);
|
||||
if (bundles == null) {
|
||||
throw new FileNotFoundException(
|
||||
"Unable to locate image resource [set=" + rset +
|
||||
", path=" + path + "]");
|
||||
}
|
||||
|
||||
// look for the resource in any of the bundles
|
||||
int size = bundles.length;
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
File file = bundles[ii].getResourceFile(path);
|
||||
if (file != null) {
|
||||
// Log.info("Found image resource [rset=" + rset +
|
||||
// ", bundle=" + bundles[ii].getSource() +
|
||||
// ", path=" + path + ", file=" + file + "].");
|
||||
return new FileImageInputStream(file);
|
||||
}
|
||||
}
|
||||
|
||||
String errmsg = "Unable to locate image resource [set=" + rset +
|
||||
", path=" + path + "]";
|
||||
throw new FileNotFoundException(errmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the resource set with the specified name, or
|
||||
* null if no set exists with that name. Services that wish to load
|
||||
* their own resources can allow the resource manager to load up a
|
||||
* resource set for them, from which they can easily load their
|
||||
* resources.
|
||||
*/
|
||||
public ResourceBundle[] getResourceSet (String name)
|
||||
{
|
||||
return (ResourceBundle[])_sets.get(name);
|
||||
}
|
||||
|
||||
protected void initResourceDir (String resourceDir)
|
||||
{
|
||||
// if none was specified, check the resource_dir system property
|
||||
if (resourceDir == null) {
|
||||
try {
|
||||
resourceDir = System.getProperty("resource_dir");
|
||||
} catch (SecurityException se) {
|
||||
}
|
||||
}
|
||||
|
||||
// if we found no resource directory, don't use one
|
||||
if (resourceDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure there's a trailing slash
|
||||
if (!resourceDir.endsWith(File.separator)) {
|
||||
resourceDir += File.separator;
|
||||
}
|
||||
_rdir = new File(resourceDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up a resource set based on the supplied definition
|
||||
* information.
|
||||
*/
|
||||
protected void resolveResourceSet (
|
||||
String setName, String definition, List dlist)
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(definition, ":");
|
||||
ArrayList set = new ArrayList();
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String path = tok.nextToken().trim();
|
||||
ResourceBundle bundle =
|
||||
new ResourceBundle(getResourceFile(path), true, _unpack);
|
||||
set.add(bundle);
|
||||
if (bundle.isUnpacked() && bundle.sourceIsReady()) {
|
||||
continue;
|
||||
}
|
||||
dlist.add(bundle);
|
||||
}
|
||||
|
||||
// convert our array list into an array and stick it in the table
|
||||
ResourceBundle[] setvec = new ResourceBundle[set.size()];
|
||||
set.toArray(setvec);
|
||||
_sets.put(setName, setvec);
|
||||
|
||||
// if this is our default resource bundle, keep a reference to it
|
||||
if (DEFAULT_RESOURCE_SET.equals(setName)) {
|
||||
_default = setvec;
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to unpack bundles on a separate thread. */
|
||||
protected static class Unpacker extends Thread
|
||||
{
|
||||
public Unpacker (List bundles, InitObserver obs)
|
||||
{
|
||||
_bundles = bundles;
|
||||
_obs = obs;
|
||||
}
|
||||
|
||||
public void run ()
|
||||
{
|
||||
try {
|
||||
int count = 0;
|
||||
for (Iterator iter = _bundles.iterator(); iter.hasNext(); ) {
|
||||
ResourceBundle bundle = (ResourceBundle)iter.next();
|
||||
if (!bundle.sourceIsReady()) {
|
||||
Log.warning("Bundle failed to initialize " +
|
||||
bundle + ".");
|
||||
}
|
||||
if (_obs != null) {
|
||||
int pct = count*100/_bundles.size();
|
||||
if (pct < 100) {
|
||||
_obs.progress(pct, 1);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (_obs != null) {
|
||||
_obs.progress(100, 0);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
if (_obs != null) {
|
||||
_obs.initializationFailed(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List _bundles;
|
||||
protected InitObserver _obs;
|
||||
}
|
||||
|
||||
/** The classloader we use for classpath-based resource loading. */
|
||||
protected ClassLoader _loader;
|
||||
|
||||
/** The directory that contains our resource bundles. */
|
||||
protected File _rdir;
|
||||
|
||||
/** The prefix we prepend to resource paths before attempting to load
|
||||
* them from the classpath. */
|
||||
protected String _rootPath;
|
||||
|
||||
/** Whether or not to unpack our resource bundles. */
|
||||
protected boolean _unpack;
|
||||
|
||||
/** Our default resource set. */
|
||||
protected ResourceBundle[] _default = new ResourceBundle[0];
|
||||
|
||||
/** A table of our resource sets. */
|
||||
protected HashMap _sets = new HashMap();
|
||||
|
||||
/** The prefix of configuration entries that describe a resource
|
||||
* set. */
|
||||
protected static final String RESOURCE_SET_PREFIX = "resource.set.";
|
||||
|
||||
/** The name of the default resource set. */
|
||||
protected static final String DEFAULT_RESOURCE_SET = "default";
|
||||
}
|
||||
Reference in New Issue
Block a user