Java tutorial
// // Nenya library - tools for developing networked games // Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved // https://github.com/threerings/nenya // // 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.cast.bundle.tools; import java.util.ArrayList; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.zip.Deflater; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import org.apache.commons.digester.Digester; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.samskivert.util.Tuple; import com.threerings.media.tile.TileSet; import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet; import com.threerings.media.tile.tools.xml.TileSetRuleSet; import com.threerings.media.tile.tools.xml.UniformTileSetRuleSet; import com.threerings.cast.ActionSequence; import com.threerings.cast.ComponentClass; import com.threerings.cast.bundle.BundleUtil; import com.threerings.cast.tools.xml.ActionRuleSet; import com.threerings.cast.tools.xml.ClassRuleSet; /** * Ant task for creating metadata bundles, which contain action sequence * and component class definition information. This task must be * configured with a number of parameters: * * <pre> * actiondef=[path to actions.xml] * classdef=[path to classes.xml] * file=[path to metadata bundle, which will be created] * </pre> */ public class MetadataBundlerTask extends Task { public void setActiondef(File actiondef) { _actionDef = actiondef; } public void setClassdef(File classdef) { _classDef = classdef; } public void setTarget(File target) { _target = target; } /** * Performs the actual work of the task. */ @Override public void execute() throws BuildException { // make sure everythign was set up properly ensureSet(_actionDef, "Must specify the action sequence " + "definitions via the 'actiondef' attribute."); ensureSet(_classDef, "Must specify the component class definitions " + "via the 'classdef' attribute."); ensureSet(_target, "Must specify the path to the target bundle " + "file via the 'target' attribute."); // make sure we can write to the target bundle file OutputStream fout = null; try { // parse our metadata Tuple<Map<String, ActionSequence>, Map<String, TileSet>> tuple = parseActions(); Map<String, ActionSequence> actions = tuple.left; Map<String, TileSet> actionSets = tuple.right; Map<String, ComponentClass> classes = parseClasses(); fout = createOutputStream(_target); // throw the serialized actions table in there fout = nextEntry(fout, BundleUtil.ACTIONS_PATH); ObjectOutputStream oout = new ObjectOutputStream(fout); oout.writeObject(actions); oout.flush(); // throw the serialized action tilesets table in there fout = nextEntry(fout, BundleUtil.ACTION_SETS_PATH); oout = new ObjectOutputStream(fout); oout.writeObject(actionSets); oout.flush(); // throw the serialized classes table in there fout = nextEntry(fout, BundleUtil.CLASSES_PATH); oout = new ObjectOutputStream(fout); oout.writeObject(classes); oout.flush(); // close it up and we're done fout.close(); } catch (IOException ioe) { String errmsg = "Unable to output to target bundle " + "[path=" + _target.getPath() + "]."; throw new BuildException(errmsg, ioe); } finally { if (fout != null) { try { fout.close(); } catch (IOException ioe) { // nothing to complain about here } } } } /** * Creates the base output stream to which to write our bundle's files. */ protected OutputStream createOutputStream(File target) throws IOException { JarOutputStream jout = new JarOutputStream(new FileOutputStream(target)); jout.setLevel(Deflater.BEST_COMPRESSION); return jout; } /** * Advances to the next named entry in the bundle and returns the stream to which to write * that entry. */ protected OutputStream nextEntry(OutputStream lastEntry, String path) throws IOException { ((JarOutputStream) lastEntry).putNextEntry(new JarEntry(path)); return lastEntry; } /** * Configures <code>ruleSet</code> and hooks it into <code>digester</code>. */ protected static void addTileSetRuleSet(Digester digester, TileSetRuleSet ruleSet) { ruleSet.setPrefix("actions" + ActionRuleSet.ACTION_PATH); digester.addRuleSet(ruleSet); digester.addSetNext(ruleSet.getPath(), "add", Object.class.getName()); } protected Tuple<Map<String, ActionSequence>, Map<String, TileSet>> parseActions() throws BuildException { // scan through the XML once to read the actions Digester digester = new Digester(); ActionRuleSet arules = new ActionRuleSet(); arules.setPrefix("actions"); digester.addRuleSet(arules); digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH, "add", Object.class.getName()); ArrayList<?> actlist = parseList(digester, _actionDef); // now go through a second time reading the tileset info digester = new Digester(); addTileSetRuleSet(digester, new SwissArmyTileSetRuleSet()); addTileSetRuleSet(digester, new UniformTileSetRuleSet("/uniformTileset")); ArrayList<?> setlist = parseList(digester, _actionDef); // sanity check if (actlist.size() != setlist.size()) { String errmsg = "An action is missing its tileset " + "definition, or something even wackier is going on."; throw new BuildException(errmsg); } // now create our mappings Map<String, ActionSequence> actmap = Maps.newHashMap(); Map<String, TileSet> setmap = Maps.newHashMap(); // create the action map for (int ii = 0; ii < setlist.size(); ii++) { TileSet set = (TileSet) setlist.get(ii); ActionSequence act = (ActionSequence) actlist.get(ii); // make sure nothing was missing in the action sequence // definition parsed from XML String errmsg = ActionRuleSet.validate(act); if (errmsg != null) { errmsg = "Action sequence invalid [seq=" + act + ", error=" + errmsg + "]."; throw new BuildException(errmsg); } actmap.put(act.name, act); setmap.put(act.name, set); } return new Tuple<Map<String, ActionSequence>, Map<String, TileSet>>(actmap, setmap); } protected Map<String, ComponentClass> parseClasses() throws BuildException { // load up our action and class info Digester digester = new Digester(); // add our action rule set and a a rule to grab parsed actions ClassRuleSet crules = new ClassRuleSet(); crules.setPrefix("classes"); digester.addRuleSet(crules); digester.addSetNext("classes" + ClassRuleSet.CLASS_PATH, "add", Object.class.getName()); ArrayList<?> setlist = parseList(digester, _classDef); Map<String, ComponentClass> clmap = Maps.newHashMap(); // create the action map for (int ii = 0; ii < setlist.size(); ii++) { ComponentClass cl = (ComponentClass) setlist.get(ii); clmap.put(cl.name, cl); } return clmap; } protected ArrayList<?> parseList(Digester digester, File path) throws BuildException { try { FileInputStream fin = new FileInputStream(path); BufferedInputStream bin = new BufferedInputStream(fin); ArrayList<Object> setlist = Lists.newArrayList(); digester.push(setlist); // now fire up the digester to parse the stream try { digester.parse(bin); } catch (Exception e) { throw new BuildException("Parsing error.", e); } return setlist; } catch (FileNotFoundException fnfe) { String errmsg = "Unable to load metadata definition file " + "[path=" + path + "]."; throw new BuildException(errmsg, fnfe); } } protected void ensureSet(Object value, String errmsg) throws BuildException { if (value == null) { throw new BuildException(errmsg); } } protected File _actionDef; protected File _classDef; protected File _target; }