diff --git a/src/java/com/threerings/media/tile/TileSet.java b/src/java/com/threerings/media/tile/TileSet.java index e021b049f..d12f18b04 100644 --- a/src/java/com/threerings/media/tile/TileSet.java +++ b/src/java/com/threerings/media/tile/TileSet.java @@ -1,5 +1,5 @@ // -// $Id: TileSet.java,v 1.20 2001/11/18 04:09:21 mdb Exp $ +// $Id: TileSet.java,v 1.21 2001/11/21 02:42:15 mdb Exp $ package com.threerings.media.tile; @@ -68,6 +68,14 @@ public abstract class TileSet _tilesetImg = null; } + /** + * Returns the path to the composite image used by this tileset. + */ + public String getImagePath () + { + return _imagePath; + } + /** * Returns the number of tiles in the tileset. */ diff --git a/src/java/com/threerings/media/tile/TileSetIDBroker.java b/src/java/com/threerings/media/tile/TileSetIDBroker.java index 665455ad4..7c7f409c8 100644 --- a/src/java/com/threerings/media/tile/TileSetIDBroker.java +++ b/src/java/com/threerings/media/tile/TileSetIDBroker.java @@ -1,5 +1,5 @@ // -// $Id: TileSetIDBroker.java,v 1.1 2001/11/18 04:09:21 mdb Exp $ +// $Id: TileSetIDBroker.java,v 1.2 2001/11/21 02:42:15 mdb Exp $ package com.threerings.media.tile; @@ -29,4 +29,13 @@ public interface TileSetIDBroker */ public int getTileSetID (String tileSetName) throws PersistenceException; + + /** + * When the user of a tilset id broker is done obtaining tileset ids, + * it must call this method to give the tileset id broker an + * opportunity to flush any newly created tileset ids back to its + * persistent store. + */ + public void commit () + throws PersistenceException; } diff --git a/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java b/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java index c5d41f1a1..810d65086 100644 --- a/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java +++ b/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java @@ -1,5 +1,5 @@ // -// $Id: BundledTileSetRepository.java,v 1.1 2001/11/20 03:47:38 mdb Exp $ +// $Id: BundledTileSetRepository.java,v 1.2 2001/11/21 02:42:15 mdb Exp $ package com.threerings.media.tile.bundle; @@ -59,13 +59,10 @@ public class BundledTileSetRepository InputStream tbin = rbundles[i].getResource(METADATA_PATH); ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream(tbin)); - TileSetBundle[] tsb = (TileSetBundle[])oin.readObject(); - - // add these to the big fat list and initialize them - for (int t = 0; t < tsb.length; t++) { - tsb[t].init(rbundles[i]); - tbundles.add(tsb[t]); - } + TileSetBundle tsb = (TileSetBundle)oin.readObject(); + // initialize the bundle and add it to the list + tsb.init(rbundles[i]); + tbundles.add(tsb); } catch (Exception e) { Log.warning("Unable to load tileset bundles from resource " + diff --git a/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java b/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java new file mode 100644 index 000000000..487f9d882 --- /dev/null +++ b/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java @@ -0,0 +1,301 @@ +// +// $Id: TileSetBundler.java,v 1.1 2001/11/21 02:42:15 mdb Exp $ + +package com.threerings.media.tools.tile.bundle; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import java.util.ArrayList; +import java.util.Iterator; + +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import org.xml.sax.SAXException; + +import org.apache.commons.digester.Digester; +import org.apache.commons.digester.Rule; +import org.apache.commons.digester.RuleSetBase; + +import org.apache.commons.util.StreamUtils; + +import com.samskivert.io.NestableIOException; +import com.samskivert.io.PersistenceException; + +import com.threerings.media.Log; +import com.threerings.media.tile.TileSet; +import com.threerings.media.tile.TileSetIDBroker; +import com.threerings.media.tile.bundle.BundledTileSetRepository; +import com.threerings.media.tile.bundle.TileSetBundle; +import com.threerings.media.tools.tile.xml.TileSetRuleSet; + +/** + * The tileset bundler is used to create tileset bundles from a set of XML + * tileset descriptions in a bundle description file. The bundles contain + * a serialized representation of the tileset objects along with the + * actual image files referenced by those tilesets. + * + *
The organization of the bundle description file is customizable + * based on the an XML configuration file provided to the tileset bundler + * when constructed. The bundler configuration maps XML paths to tileset + * parsers. An example configuration follows: + * + *
+ *+ * + * This configuration would be used to parse a bundle description that + * looked something like the following: + * + *+ * + *+ * + *bundle.tilesets.uniform + *+ * com.threerings.media.tools.tile.xml.UniformTileSetRuleSet + * + *+ * + *bundle.tilesets.object + *+ * com.threerings.media.tools.tile.xml.ObjectTileSetRuleSet + * + *
+ *+ * + * The class specified in the+ * + * + *+ * + * + *+ * + * + *
ruleset element must derive
+ * from {@link TileSetRuleSet}. The images that will be included in the
+ * bundle must be in the same directory as the bundle description file and
+ * the tileset descriptions must reference the images without a preceding
+ * path.
+ */
+public class TileSetBundler
+{
+ /**
+ * Constructs a tileset bundler with the specified path to a bundler
+ * configuration file. The configuration file will be loaded and used
+ * to configure this tileset bundler.
+ */
+ public TileSetBundler (String configPath)
+ throws IOException
+ {
+ // we parse our configuration with a digester
+ Digester digester = new Digester();
+
+ // push our mappings array onto the stack
+ ArrayList mappings = new ArrayList();
+ digester.push(mappings);
+
+ // create a mapping object for each mapping entry and append it to
+ // our mapping list
+ digester.addObjectCreate("bundler-config/mapping",
+ Mapping.class.getName());
+ digester.addSetNext("bundler-config/mapping",
+ "add", "java.lang.Object");
+
+ // configure each mapping object with the path and ruleset
+ digester.addCallMethod("bundler-config/mapping", "init", 2);
+ digester.addCallParam("bundler-config/mapping/path", 0);
+ digester.addCallParam("bundler-config/mapping/ruleset", 1);
+
+ // now go like the wind
+ FileInputStream fin = new FileInputStream(configPath);
+ try {
+ digester.parse(fin);
+ } catch (SAXException saxe) {
+ String errmsg = "Failure parsing bundler config file.";
+ throw new NestableIOException(errmsg, saxe);
+ }
+ fin.close();
+
+ // create our digester
+ _digester = new Digester();
+
+ // use the mappings we parsed to configure our actual digester
+ int msize = mappings.size();
+ for (int i = 0; i < msize; i++) {
+ Mapping map = (Mapping)mappings.get(i);
+ try {
+ TileSetRuleSet ruleset = (TileSetRuleSet)
+ Class.forName(map.ruleset).newInstance();
+
+ // configure the ruleset
+ ruleset.setPrefix(map.path);
+ // add it to the digester
+ _digester.addRuleSet(ruleset);
+ // and add a rule to stick the parsed tilesets onto the
+ // end of an array list that we'll put on the stack
+ _digester.addSetNext(map.path + TileSetRuleSet.TILESET_PATH,
+ "add", "java.lang.Object");
+
+ } catch (Exception e) {
+ String errmsg = "Unable to create tileset rule set " +
+ "instance [mapping=" + map + "].";
+ throw new NestableIOException(errmsg, e);
+ }
+ }
+ }
+
+ /**
+ * Creates a tileset bundle at the location specified by the
+ * targetPath parameter, based on the description
+ * provided via the bundleDesc parameter.
+ *
+ * @param idBroker the tileset id broker that will be used to map
+ * tileset names to tileset ids.
+ * @param bundleDef a file object pointing to the bundle description
+ * file.
+ * @param targetPath the path of the tileset bundle file that will be
+ * created.
+ *
+ * @exception IOException thrown if an error occurs reading, writing
+ * or processing anything.
+ */
+ public void createBundle (
+ TileSetIDBroker idBroker, File bundleDesc, String targetPath)
+ throws IOException
+ {
+ // stick an array list on the top of the stack into which we will
+ // collect parsed tilesets
+ ArrayList sets = new ArrayList();
+ _digester.push(sets);
+
+ // parse the tilesets
+ FileInputStream fin = new FileInputStream(bundleDesc);
+ try {
+ _digester.parse(fin);
+ } catch (SAXException saxe) {
+ String errmsg = "Failure parsing bundle description file.";
+ throw new NestableIOException(errmsg, saxe);
+ } finally {
+ fin.close();
+ }
+
+ // create a tileset bundle to hold our tilesets
+ TileSetBundle bundle = new TileSetBundle();
+
+ // add all of the parsed tilesets to the tileset bundle
+ try {
+ for (int i = 0; i < sets.size(); i++) {
+ TileSet set = (TileSet)sets.get(i);
+ String name = set.getName();
+
+ // let's be robust
+ if (name == null) {
+ Log.warning("Tileset was parsed, but received no name " +
+ "[set=" + set + "]. Skipping.");
+ continue;
+ }
+
+ // assign a tilset id to the tileset and bundle it
+ try {
+ int tileSetId = idBroker.getTileSetID(name);
+ bundle.addTileSet(tileSetId, set);
+ } catch (PersistenceException pe) {
+ String errmsg = "Failure obtaining a tileset id for " +
+ "tileset [set=" + set + "].";
+ throw new NestableIOException(errmsg, pe);
+ }
+ }
+
+ // clear out our array list in preparation for another go
+ sets.clear();
+
+ } finally {
+ // before we go, we have to commit our brokered tileset ids
+ // back to the broker's persistent store
+ try {
+ idBroker.commit();
+ } catch (PersistenceException pe) {
+ Log.warning("Failure committing brokered tileset ids " +
+ "back to broker's persistent store " +
+ "[error=" + pe + "].");
+ }
+ }
+
+ // now we have to create the actual bundle file
+ File target = new File(targetPath);
+ FileOutputStream fout = new FileOutputStream(target);
+ Manifest manifest = new Manifest();
+ JarOutputStream jar = new JarOutputStream(fout, manifest);
+
+ try {
+ // first write a serialized representation of the tileset
+ // bundle object to the bundle jar file
+ JarEntry entry = new JarEntry(
+ BundledTileSetRepository.METADATA_PATH);
+ jar.putNextEntry(entry);
+ ObjectOutputStream oout = new ObjectOutputStream(jar);
+ oout.writeObject(bundle);
+ oout.flush();
+
+ // now write all of the image files to the bundle
+ Iterator setiter = bundle.enumerateTileSets();
+ while (setiter.hasNext()) {
+ TileSet set = (TileSet)setiter.next();
+ String imagePath = set.getImagePath();
+
+ // sanity checks
+ if (imagePath == null) {
+ Log.warning("Tileset contains no image path " +
+ "[set=" + set + "]. It ain't gonna work.");
+ continue;
+ }
+
+ // open the image and pipe it into the jar file
+ File imgfile = new File(bundleDesc.getParent(), imagePath);
+ FileInputStream imgin = new FileInputStream(imgfile);
+ jar.putNextEntry(new JarEntry(imagePath));
+ StreamUtils.pipe(imgin, jar);
+ }
+
+ // finally close up the jar file and call ourself done
+ jar.close();
+
+ } catch (IOException ioe) {
+ // remove the incomplete jar file and rethrow the exception
+ fout.close();
+ target.delete();
+ throw ioe;
+ }
+ }
+
+ /** Used to parse our configuration. */
+ public static class Mapping
+ {
+ public String path;
+ public String ruleset;
+
+ public void init (String path, String ruleset)
+ {
+ this.path = path;
+ this.ruleset = ruleset;
+ }
+
+ public String toString ()
+ {
+ return "[path=" + path + ", ruleset=" + ruleset + "]";
+ }
+ }
+
+ /** The digester we use to parse bundle descriptions. */
+ protected Digester _digester;
+}
diff --git a/src/java/com/threerings/media/tile/tools/xml/SwissArmyTileSetRuleSet.java b/src/java/com/threerings/media/tile/tools/xml/SwissArmyTileSetRuleSet.java
index 89313cb2f..1b8f727ec 100644
--- a/src/java/com/threerings/media/tile/tools/xml/SwissArmyTileSetRuleSet.java
+++ b/src/java/com/threerings/media/tile/tools/xml/SwissArmyTileSetRuleSet.java
@@ -1,5 +1,5 @@
//
-// $Id: SwissArmyTileSetRuleSet.java,v 1.3 2001/11/20 04:15:44 mdb Exp $
+// $Id: SwissArmyTileSetRuleSet.java,v 1.4 2001/11/21 02:42:15 mdb Exp $
package com.threerings.media.tools.tile.xml;
@@ -36,23 +36,13 @@ import com.threerings.media.tile.SwissArmyTileSet;
*/
public class SwissArmyTileSetRuleSet extends TileSetRuleSet
{
- /**
- * Constructs a uniform tileset rule set that will match tilesets with
- * the specified prefix. See the documentation for {@link
- * TileSetRuleSet#TileSetRuleSet} for more info on matching.
- */
- public SwissArmyTileSetRuleSet (String prefix)
- {
- super(prefix);
- }
-
// documentation inherited
public void addRuleInstances (Digester digester)
{
super.addRuleInstances(digester);
digester.addRule(
- _prefix + "/tileset/widths",
+ _prefix + TILESET_PATH + "/widths",
new CallMethodSpecialRule(digester) {
public void parseAndSet (String bodyText, Object target)
{
@@ -62,7 +52,7 @@ public class SwissArmyTileSetRuleSet extends TileSetRuleSet
});
digester.addRule(
- _prefix + "/tileset/heights",
+ _prefix + TILESET_PATH + "/heights",
new CallMethodSpecialRule(digester) {
public void parseAndSet (String bodyText, Object target)
{
@@ -72,7 +62,7 @@ public class SwissArmyTileSetRuleSet extends TileSetRuleSet
});
digester.addRule(
- _prefix + "/tileset/tileCounts",
+ _prefix + TILESET_PATH + "/tileCounts",
new CallMethodSpecialRule(digester) {
public void parseAndSet (String bodyText, Object target)
{
@@ -83,10 +73,9 @@ public class SwissArmyTileSetRuleSet extends TileSetRuleSet
}
// documentation inherited
- protected TileSet createTileSet (Attributes attributes)
+ protected Class getTileSetClass ()
{
- // we use uniform tilesets
- return new SwissArmyTileSet();
+ return SwissArmyTileSet.class;
}
/**
diff --git a/src/java/com/threerings/media/tile/tools/xml/TileSetRuleSet.java b/src/java/com/threerings/media/tile/tools/xml/TileSetRuleSet.java
index abff00cbf..e8bb099c6 100644
--- a/src/java/com/threerings/media/tile/tools/xml/TileSetRuleSet.java
+++ b/src/java/com/threerings/media/tile/tools/xml/TileSetRuleSet.java
@@ -1,5 +1,5 @@
//
-// $Id: TileSetRuleSet.java,v 1.3 2001/11/20 04:15:44 mdb Exp $
+// $Id: TileSetRuleSet.java,v 1.4 2001/11/21 02:42:15 mdb Exp $
package com.threerings.media.tools.tile.xml;
@@ -18,9 +18,15 @@ import com.threerings.media.tile.TileSet;
*/
public abstract class TileSetRuleSet extends RuleSetBase
{
+ /** The component of the digester path that is appended by the tileset
+ * rule set to match a tileset. This is appended to whatever prefix is
+ * provided to the tileset rule set to obtain the complete XML path to
+ * a matched tileset. */
+ public static final String TILESET_PATH = "/tileset";
+
/**
- * Constructs a tileset rule set which will match tilesets with the
- * supplied prefix. For example, passing a prefix of
+ * Instructs the tileset rule set to match tilesets with the supplied
+ * prefix. For example, passing a prefix of
* tilesets.objectsets will match tilesets in the
* following XML file:
*
@@ -33,21 +39,14 @@ public abstract class TileSetRuleSet extends RuleSetBase
* </objectsets>
* </tilesets>
*
+ *
+ * This must be called before adding the ruleset to a digester.
*/
- public TileSetRuleSet (String prefix)
+ public void setPrefix (String prefix)
{
_prefix = prefix;
}
- /**
- * Called by the {@link XMLTileSetParser} to initialize this tileset
- * rule set with the necessary back references to operate properly.
- */
- protected void init (XMLTileSetParser parser)
- {
- _parser = parser;
- }
-
/**
* Adds the necessary rules to the digester to parse our tilesets.
* Derived classes should override this method, being sure to call the
@@ -59,62 +58,23 @@ public abstract class TileSetRuleSet extends RuleSetBase
{
// this creates the appropriate instance when we encounter a
//
- * _parser.addRuleSet(new UniformTileSetRuleSet("tilesets"));
+ * _parser.addRuleSet("tilesets", new UniformTileSetRuleSet());
*
*/
- public void addRuleSet (TileSetRuleSet ruleset)
+ public void addRuleSet (String prefix, TileSetRuleSet ruleset)
{
- // provide a reference to ourselves to the ruleset
- ruleset.init(this);
+ // configure the ruleset with the appropriate prefix
+ ruleset.setPrefix(prefix);
// and have it set itself up with the digester
_digester.addRuleSet(ruleset);
+
+ // add a set next rule which will put tilesets with this prefix
+ // into the array list that'll be on the top of the stack
+ _digester.addSetNext(prefix + TileSetRuleSet.TILESET_PATH,
+ "add", "java.lang.Object");
}
/**
* Loads all of the tilesets specified in the supplied XML tileset
* description file and places them into the supplied hashmap indexed
- * by tileset name.
+ * by tileset name. This method is not reentrant, so don't go calling
+ * it from multiple threads.
*/
public void loadTileSets (String path, HashMap tilesets)
throws IOException
{
- // save off the tileset hashtable
- _tilesets = tilesets;
+ // stick an array list on the top of the stack for collecting
+ // parsed tilesets
+ ArrayList setlist = new ArrayList();
+ _digester.push(setlist);
// get an input stream for this XML file
InputStream is = ConfigUtil.getStream(path);
@@ -84,14 +93,24 @@ public class XMLTileSetParser
"[error=" + saxe + "].");
Log.logStackTrace(saxe);
}
+
+ // stick the tilesets from the list into the hashtable
+ for (int i = 0; i < setlist.size(); i++) {
+ TileSet set = (TileSet)setlist.get(i);
+ if (set.getName() == null) {
+ Log.warning("Tileset did not receive name during " +
+ "parsing process [set=" + set + "].");
+ } else {
+ tilesets.put(set.getName(), set);
+ }
+ }
+ // and clear out the list for next time
+ setlist.clear();
}
/** Our XML digester. */
protected Digester _digester;
- /** The tilesets constructed thus far. */
- protected HashMap _tilesets;
-
/** Default tileset name. */
protected static final String DEF_NAME = "Untitled";
diff --git a/src/java/com/threerings/miso/tile/tools/xml/BaseTileSetRuleSet.java b/src/java/com/threerings/miso/tile/tools/xml/BaseTileSetRuleSet.java
index 6f5da0172..c18eee38d 100644
--- a/src/java/com/threerings/miso/tile/tools/xml/BaseTileSetRuleSet.java
+++ b/src/java/com/threerings/miso/tile/tools/xml/BaseTileSetRuleSet.java
@@ -1,5 +1,5 @@
//
-// $Id: BaseTileSetRuleSet.java,v 1.3 2001/11/20 04:15:44 mdb Exp $
+// $Id: BaseTileSetRuleSet.java,v 1.4 2001/11/21 02:42:15 mdb Exp $
package com.threerings.miso.tools.tile.xml;
@@ -28,16 +28,6 @@ import com.threerings.miso.tile.MisoTileSet;
*/
public class MisoTileSetRuleSet extends SwissArmyTileSetRuleSet
{
- /**
- * Constructs a uniform tileset rule set that will match tilesets with
- * the specified prefix. See the documentation for {@link
- * TileSetRuleSet#TileSetRuleSet} for more info on matching.
- */
- public MisoTileSetRuleSet (String prefix)
- {
- super(prefix);
- }
-
// documentation inherited
public void addRuleInstances (Digester digester)
{
diff --git a/tests/rsrc/config/resource/manager.properties b/tests/rsrc/config/resource/manager.properties
new file mode 100644
index 000000000..1137e7030
--- /dev/null
+++ b/tests/rsrc/config/resource/manager.properties
@@ -0,0 +1,7 @@
+#
+# $Id: manager.properties,v 1.1 2001/11/21 02:42:16 mdb Exp $
+#
+# Test resource manager configuration
+
+# used to test the BundledTileSetRepository
+resource.set.bundle_test = rsrc/media/tools/tile/bundle.jar
diff --git a/tests/rsrc/media/tile/bundle/tools/building.png b/tests/rsrc/media/tile/bundle/tools/building.png
new file mode 100644
index 000000000..16fca4818
Binary files /dev/null and b/tests/rsrc/media/tile/bundle/tools/building.png differ
diff --git a/tests/rsrc/media/tile/bundle/tools/bundle.xml b/tests/rsrc/media/tile/bundle/tools/bundle.xml
new file mode 100644
index 000000000..6316fee0b
--- /dev/null
+++ b/tests/rsrc/media/tile/bundle/tools/bundle.xml
@@ -0,0 +1,39 @@
+
+
+
+