org.openstreetmap.osmosis.xml.common.ElementWriter Maven / Gradle / Ivy
// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.xml.common;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
import org.openstreetmap.osmosis.core.domain.common.TimestampFormat;
/**
* Provides common functionality for all classes writing elements to xml.
*
* @author Brett Henderson
*/
public class ElementWriter {
/**
* The number of spaces to indent per indent level.
*/
private static final int INDENT_SPACES_PER_LEVEL = 2;
/**
* Defines the characters that must be replaced by
* an encoded string when writing to XML.
*/
private static final Map XML_ENCODING;
static {
// Define all the characters and their encodings.
XML_ENCODING = new HashMap();
// Non-xml compatible control characters will not be written
// with the exception of tab, carriage return and line feed.
for (int i = 0; i <= 0x1F; i++) {
if (i != 0x9 && i != 0xA && i != 0xD) {
XML_ENCODING.put(new Character((char) i), "");
}
}
XML_ENCODING.put(new Character((char) 0x7F), "");
XML_ENCODING.put(new Character('<'), "<");
XML_ENCODING.put(new Character('>'), ">");
XML_ENCODING.put(new Character('"'), """);
XML_ENCODING.put(new Character('\''), "'");
XML_ENCODING.put(new Character('&'), "&");
XML_ENCODING.put(new Character('\n'), "
");
XML_ENCODING.put(new Character('\r'), "
");
XML_ENCODING.put(new Character('\t'), " ");
}
/**
* The output destination for writing all xml.
*/
private Writer myWriter;
/**
* The name of the element to be written.
*/
private final String myElementName;
/**
* The indent level of the element.
*/
private final int myIndentLevel;
private final TimestampFormat myTimestampFormat;
/**
* Line separator string. This is the value of the line.separator
* property at the moment that the stream was created.
*/
private String myLineSeparator;
/**
* Creates a new instance.
*
* @param anElementName The name of the element to be written.
* @param anIndentionLevel The indent level of the element.
*/
protected ElementWriter(final String anElementName,
final int anIndentionLevel) {
this.myElementName = anElementName;
this.myIndentLevel = anIndentionLevel;
myTimestampFormat = new XmlTimestampFormat();
this.myLineSeparator = System.getProperty("line.separator");
}
/**
* Sets the writer used as the xml output destination.
*
* @param aWriter The writer.
*/
public void setWriter(final Writer aWriter) {
if (aWriter == null) {
throw new IllegalArgumentException("null writer given");
}
this.myWriter = aWriter;
}
/**
* Writes a series of spaces to indent the current line.
*
* @throws IOException if an error occurs.
*/
private void writeIndent() throws IOException {
int indentSpaceCount;
indentSpaceCount = myIndentLevel * INDENT_SPACES_PER_LEVEL;
for (int i = 0; i < indentSpaceCount; i++) {
myWriter.append(' ');
}
}
/**
* A utility method for encoding data in XML format.
*
* @param data The data to be formatted.
* @return The formatted data. This may be the input
* string if no changes are required.
*/
private String escapeData(final String data) {
StringBuilder buffer = null;
for (int i = 0; i < data.length(); ++i) {
char currentChar = data.charAt(i);
String replacement = XML_ENCODING.get(new Character(currentChar));
if (replacement != null) {
if (buffer == null) {
buffer = new StringBuilder(data.substring(0, i));
}
buffer.append(replacement);
} else if (buffer != null) {
buffer.append(currentChar);
}
}
if (buffer == null) {
return data;
} else {
return buffer.toString();
}
}
/**
* Returns a timestamp format suitable for xml files.
*
* @return The timestamp format.
*/
protected TimestampFormat getTimestampFormat() {
return myTimestampFormat;
}
/**
* Writes an element opening line without the final
* closing portion of the tag.
*/
protected void beginOpenElement() {
try {
writeIndent();
myWriter.append('<');
myWriter.append(this.myElementName);
} catch (IOException e) {
throw new OsmosisRuntimeException("Unable to write data.", e);
}
}
/**
* Writes out the opening tag of the element.
*
* @param closeElement If true, the element will be closed
* immediately and written as a single
* tag in the output xml file.
*/
protected void endOpenElement(final boolean closeElement) {
try {
if (closeElement) {
myWriter.append('/');
}
myWriter.append('>');
myWriter.append(this.myLineSeparator);
} catch (IOException e) {
throw new OsmosisRuntimeException("Unable to write data.", e);
}
}
/**
* Adds an attribute to the element.
*
* @param name The name of the attribute.
* @param value The value of the attribute.
*/
protected void addAttribute(final String name, final String value) {
try {
myWriter.append(' ');
myWriter.append(name);
myWriter.append("=\"");
myWriter.append(escapeData(value));
myWriter.append('"');
} catch (IOException e) {
throw new OsmosisRuntimeException("Unable to write data.", e);
}
}
/**
* Writes the closing tag of the element.
*/
protected void closeElement() {
try {
writeIndent();
myWriter.append("");
myWriter.append(myElementName);
myWriter.append('>');
myWriter.append(this.myLineSeparator);
} catch (IOException e) {
throw new OsmosisRuntimeException("Unable to write data.", e);
}
}
}