// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.xml.v0_6.impl; import java.io.Writer; import org.openstreetmap.osmosis.core.OsmosisConstants; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer; import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer; import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor; import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer; import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer; import org.openstreetmap.osmosis.core.container.v0_6.WayContainer; import org.openstreetmap.osmosis.xml.common.ElementWriter; /** * Renders OSM data types as xml. * * @author Brett Henderson */ public class OsmWriter extends ElementWriter { private SubElementWriter subElementWriter; private boolean renderAttributes; /** * Creates a new instance. * * @param elementName * The name of the element to be written. * @param indentLevel * The indent level of the element. * @param renderAttributes * Specifies whether attributes of the top level element should * be rendered. This would typically be set to false if this * element is embedded within a higher level element (eg. * changesets) * @param legacyBound * If true, write the legacy {@literal <bound>} element * instead of the correct {@literal <bounds>} one. * */ public OsmWriter(String elementName, int indentLevel, boolean renderAttributes, boolean legacyBound) { super(elementName, indentLevel); this.renderAttributes = renderAttributes; // Create the sub-element writer which calls the appropriate element // writer based on data type. subElementWriter = new SubElementWriter(indentLevel + 1, legacyBound); } /** * Begins an element. */ public void begin() { beginOpenElement(); if (renderAttributes) { addAttribute("version", XmlConstants.OSM_VERSION); addAttribute("generator", "Osmosis " + OsmosisConstants.VERSION); } endOpenElement(false); } /** * Ends an element. */ public void end() { closeElement(); } /** * Writes the element in the container. * * @param entityContainer * The container holding the entity. */ public void process(EntityContainer entityContainer) { entityContainer.process(subElementWriter); } /** * {@inheritDoc} */ @Override public void setWriter(final Writer writer) { super.setWriter(writer); // Tell the sub element writer that a new writer is available. This will // cause the underlying entity writing classes to be updated. subElementWriter.updateWriter(writer); } /** * Directs data to the appropriate underlying element writer. * * @author Brett Henderson */ private static class SubElementWriter implements EntityProcessor { private NodeWriter nodeWriter; private WayWriter wayWriter; private RelationWriter relationWriter; private BoundWriter boundWriter; private boolean boundWritten = false; // can't write a Bound twice private boolean entitiesWritten = false; // can't write a Bound after any Entities /** * Creates a new instance. * * @param indentLevel * The indent level of the sub-elements. */ public SubElementWriter(int indentLevel, boolean legacyBound) { nodeWriter = new NodeWriter("node", indentLevel); wayWriter = new WayWriter("way", indentLevel); relationWriter = new RelationWriter("relation", indentLevel); if (legacyBound) { boundWriter = new BoundWriter("bound", indentLevel, legacyBound); } else { boundWriter = new BoundWriter("bounds", indentLevel, legacyBound); } } /** * Updates the underlying writer. * * @param writer * The writer to be used for all output xml. */ public void updateWriter(final Writer writer) { nodeWriter.setWriter(writer); wayWriter.setWriter(writer); relationWriter.setWriter(writer); boundWriter.setWriter(writer); // reset the flags indicating which data has been written boundWritten = false; entitiesWritten = false; } /** * {@inheritDoc} */ public void process(NodeContainer node) { nodeWriter.process(node.getEntity()); entitiesWritten = true; } /** * {@inheritDoc} */ public void process(WayContainer way) { wayWriter.process(way.getEntity()); entitiesWritten = true; } /** * {@inheritDoc} */ public void process(RelationContainer relation) { relationWriter.process(relation.getEntity()); entitiesWritten = true; } /** * {@inheritDoc} */ public void process(BoundContainer bound) { if (boundWritten) { throw new OsmosisRuntimeException("Bound element already written and only one allowed."); } if (entitiesWritten) { throw new OsmosisRuntimeException("Can't write bound element after other entities."); } boundWriter.process(bound.getEntity()); boundWritten = true; } } }