// // $Id: XMLSceneWriter.java,v 1.9 2001/08/15 01:08:49 mdb Exp $ package com.threerings.miso.scene.xml; import java.awt.Point; import java.io.*; import java.util.ArrayList; import org.xml.sax.*; import org.xml.sax.helpers.AttributesImpl; import com.megginson.sax.DataWriter; import com.threerings.miso.Log; import com.threerings.miso.scene.*; import com.threerings.miso.tile.Tile; /** * The XMLSceneWriter writes a {@link * com.threerings.miso.scene.MisoScene} object to an XML file. * *

The scene id is omitted as the scene id is assigned when the * scene template is actually loaded into a server. Similarly, exit * points are specified by scene name rather than scene id. */ public class XMLSceneWriter extends DataWriter { /** * Construct an XMLSceneWriter object. */ public XMLSceneWriter () { setIndentStep(2); } /** * Write the scenes to the specified output file in XML format. * * @param fname the file to write to. */ public void saveScene (MisoScene scene, String fname) throws IOException { FileOutputStream fos = new FileOutputStream(fname); setOutput(new OutputStreamWriter(fos)); try { startDocument(); startElement("scene"); dataElement("name", scene.getName()); dataElement("version", "" + MisoScene.VERSION); dataElement("locations", getLocationData(scene)); startElement("clusters"); writeClusters(scene); endElement("clusters"); dataElement("exits", getExitData(scene)); startElement("tiles"); for (int lnum = 0; lnum < MisoScene.NUM_LAYERS; lnum++) { writeLayer(scene, lnum); } endElement("tiles"); endElement("scene"); endDocument(); } catch (SAXException saxe) { Log.warning("Exception writing scene to file " + "[scene=" + scene + ", fname=" + fname + ", saxe=" + saxe + "]."); } } /** * Output XML detailing the cluster objects. Clusters are * described by a list of location object indexes that are * contained within the cluster. * * @param scene the scene object. */ protected void writeClusters (MisoScene scene) throws SAXException { ArrayList clusters = scene.getClusters(); ArrayList locs = scene.getLocations(); int size = clusters.size(); for (int ii = 0; ii < size; ii++) { StringBuffer buf = new StringBuffer(); Cluster cluster = (Cluster)clusters.get(ii); ArrayList clusterlocs = cluster.getLocations(); int clustersize = clusterlocs.size(); for (int jj = 0; jj < clustersize; jj++) { buf.append(locs.indexOf(clusterlocs.get(jj))); if (jj < clustersize - 1) buf.append(","); } dataElement("cluster", buf.toString()); } } /** * Output XML detailing the tiles at the specified layer in the * given scene. The first layer is outputted in its entirety, * whereas subsequent layers are outputted in a sparse notation * that details each contiguous horizontal chunk of tiles in each * row separately. * * @param scene the scene object. * @param lnum the layer number. */ protected void writeLayer (MisoScene scene, int lnum) throws SAXException { AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("", "lnum", "", "CDATA", "" + lnum); startElement("", "layer", "", attrs); for (int yy = 0; yy < MisoScene.TILE_HEIGHT; yy++) { if (lnum == MisoScene.LAYER_BASE) { writeTileRowData(scene, yy, lnum); } else { writeSparseTileRowData(scene, yy, lnum); } } endElement("layer"); } /** * Return a string representation of the exits in the scene. Each * exit is specified by a comma-delimited tuple of (location * index, scene name) values. * * @param scene the scene object. * * @return the exits in String format. */ protected String getExitData (MisoScene scene) { ArrayList locs = scene.getLocations(); ArrayList exits = scene.getExits(); StringBuffer buf = new StringBuffer(); int size = exits.size(); for (int ii = 0; ii < size; ii++) { Exit exit = (Exit)exits.get(ii); buf.append(locs.indexOf(exit)).append(","); buf.append(exit.name); if (ii < size - 1) buf.append(","); } return buf.toString(); } /** * Return a string representation of the locations in the scene. * Each location is specified by a comma-delimited triplet of * (x, y, orientation) values. * * @return the locations in String format. */ protected String getLocationData (MisoScene scene) { ArrayList locs = scene.getLocations(); StringBuffer buf = new StringBuffer(); int size = locs.size(); for (int ii = 0; ii < size; ii++) { Location loc = (Location)locs.get(ii); buf.append(loc.x).append(","); buf.append(loc.y).append(","); buf.append(loc.orient); if (ii < size - 1) buf.append(","); } return buf.toString(); } /** * Return a string representation of the tiles at the specified * row and layer in the given scene. Only len tiles * starting at column colstart are included in the * string. * * @param scene the scene object. * @param rownum the row number. * @param lnum the layer number. * @param colstart the first column of data. * @param len the number of columns of data. * * @return the tile data in String format. */ protected String getTileData (MisoScene scene, int rownum, int lnum, int colstart, int len) { StringBuffer buf = new StringBuffer(); int numtiles = colstart + len; for (int ii = colstart; ii < numtiles; ii++) { Tile tile = scene.tiles[ii][rownum][lnum]; if (tile == null) { Log.warning("Null tile [x=" + ii + ", rownum=" + rownum + ", lnum=" + lnum + "]."); continue; } buf.append(tile.tsid).append(","); buf.append(tile.tid); if (ii != numtiles - 1) buf.append(","); } return buf.toString(); } /** * Write the row data for a specified row in the scene tile array. * *

The row is written as a row element. Each * tile is specified by a comma-delimited tuple of (tile set id, * tile id) numbers, with the associated row number detailed in * the rownum attribute of the row * element. * * @param scene the scene object. * @param rownum the row in the scene tile array. * @param lnum the layer number in the scene tile array. */ protected void writeTileRowData (MisoScene scene, int rownum, int lnum) throws SAXException { // set up the attributes for this row AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("", "rownum", "", "CDATA", "" + rownum); // output the full row data element String data = getTileData(scene, rownum, lnum, 0, MisoScene.TILE_WIDTH); dataElement("", "row", "", attrs, data); } /** * Utility routine used by writeSparseTileRowData to * obtain the sets of contiguous tile sets in each row. * *

The search for contiguous tiles starts at the column * specified in info[0] Results are returned in the info array as * { colstart, len }. colstart is -1 if no * tiles were found in the rest of the row. * * @param scene the scene object. * @param rownum the row number. * @param lnum the layer number. * @param info the info array. * * @return true if any tiles were found, false if not. */ protected boolean getSparseColumn ( MisoScene scene, int rownum, int lnum, int info[]) { int start = -1, len = 0; for (int xx = info[0]; xx < MisoScene.TILE_WIDTH; xx++) { Tile tile = scene.tiles[xx][rownum][lnum]; if (tile == null) { if (start == -1) continue; else break; } if (start == -1) start = xx; len++; } info[0] = start; info[1] = len; return (start != -1); } /** * Write the row data for a specified row in the scene tile array * in sparse row format. * *

Sparse row format is identical to the format detailed in * writeTileRowData() except that a separate * row element is outputted for each contiguous set * of tiles, with the starting column detailed in the * colstart attribute of the row * element. * * @param scene the scene object. * @param rownum the row in the scene tile array. * @param lnum the layer number in the scene tile array. */ protected void writeSparseTileRowData (MisoScene scene, int rownum, int lnum) throws SAXException { // set up the attributes for this row AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute("", "rownum", "", "CDATA", "" + rownum); attrs.addAttribute("", "colstart", "", "CDATA", "0"); int info[] = new int[] { 0, 0 }; while (getSparseColumn(scene, rownum, lnum, info)) { // update the colstart attribute attrs.setAttribute(1, "", "colstart", "", "CDATA", "" + info[0]); // output the partial row data element String data = getTileData(scene, rownum, lnum, info[0], info[1]); dataElement("", "row", "", attrs, data); // update the colstart value info[0] = info[0] + info[1]; } } }