/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2006 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.xml.stream;
import java.io.OutputStream;
import java.io.Writer;
import javolution.context.ObjectFactory;
import javolution.lang.Configurable;
/**
* <p> The class represents the factory for getting {@link XMLStreamWriter}
* intances.</p>
*
* <p> The {@link #newInstance() default implementation} automatically
* {@link ObjectFactory#recycle recycles} any writer which has been
* {@link XMLStreamWriter#close() closed}.</p>
*
* <P> Usage example:[code]
*
* // Lets format to an appendable.
* TextBuilder xml = new TextBuilder();
* AppendableWriter out = new AppendableWriter(xml);
*
* // Creates a factory producing writers using tab indentation.
* XMLOutpuFactory factory = XMLOutputFactory.newInstance();
* factory.setProperty(XMLOutputFactory.INDENTATION, "/t");
*
* // Creates a new writer (potentially recycled).
* XMLStreamWriter writer = factory.createXMLStreamReader(out);
*
* // Formats to XML.
* writer.writeStartDocument();
* writer.writeStartElement(...);
* ...
* writer.close(); // Automatically recycles this writer.
* out.close(); // Underlying output should be closed explicitly.
*
* // Displays the formatted output.
* System.out.println(xml);
* [/code]</p>
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 4.0, September 4, 2006
*/
public abstract class XMLOutputFactory {
/**
* Holds the XMLOutputFactory implementation (configurable).
*/
public static final Configurable <Class<? extends XMLOutputFactory>> CLASS
= new Configurable <Class<? extends XMLOutputFactory>> (Default.class) {};
/**
* Property used to set prefix defaulting on the output side
* (type: <code>Boolean</code>, default: <code>FALSE</code>).
*/
public static final String IS_REPAIRING_NAMESPACES = "javolution.xml.stream.isRepairingNamespaces";
/**
* Property used to specify the prefix to be appended by a trailing
* part (a sequence number) in order to make it unique to be usable as
* a temporary non-colliding prefix when repairing namespaces
* (type: <code>String</code>, default: <code>"ns"</code>).
*/
public final static String REPAIRING_PREFIX = "javolution.xml.stream.repairingPrefix";
/**
* Property used to specify an indentation string; non-null indentation
* forces the writer to write elements into separate lines
* (type: <code>String</code>, default: <code>null</code>).
*/
public static final String INDENTATION = "javolution.xml.stream.indentation";
/**
* Property used to specify the new line characters
* (type: <code>String</code>, default: <code>"\n"</code>).
*/
public static final String LINE_SEPARATOR = "javolution.xml.stream.lineSeparator";
/**
* Property indicating if the stream writers are allowed to automatically
* output empty elements when a start element is immediately followed by
* matching end element
* (type: <code>Boolean</code>, default: <code>FALSE</code>).
*/
public final static String AUTOMATIC_EMPTY_ELEMENTS = "javolution.xml.stream.automaticEmptyElements";
/**
* Property indicating if the stream writers are not allowed to use
* empty element tags
* (type: <code>Boolean</code>, default: <code>FALSE</code>).
* When set, this property forces the use of start/end element tag
* (e.g. i.e. "<empty />" replaced by "<empty></empty>"),
* This property takes precedence over {@link #AUTOMATIC_EMPTY_ELEMENTS}.
*/
public final static String NO_EMPTY_ELEMENT_TAG = "javolution.xml.stream.noEmptyElementTag";
/**
* Default constructor.
*/
protected XMLOutputFactory() {
}
/**
* Returns a new instance of the {@link #CLASS} output factory
* implementation which may be configurated by the user
* (see {@link #setProperty(String, Object)}). The output factory
* instance is allocated through {@link ObjectFactory#getInstance(Class)}.
*
* @return a new factory instance.
*/
public static XMLOutputFactory newInstance() {
Class cls = (Class) CLASS.get();
return (XMLOutputFactory) ObjectFactory.getInstance(cls).object();
}
/**
* Returns a XML stream writer to the specified i/o writer.
*
* @param writer the writer to write to.
* @throws XMLStreamException
*/
public abstract XMLStreamWriter createXMLStreamWriter(Writer writer)
throws XMLStreamException;
/**
* Returns a XML stream writer to the specified output stream (UTF-8
* encoding).
*
* @param stream the stream to write to.
* @throws XMLStreamException
*/
public abstract XMLStreamWriter createXMLStreamWriter(OutputStream stream)
throws XMLStreamException;
/**
* Returns a XML stream writer to the specified output stream using the
* specified encoding.
*
* @param stream the stream to write to.
* @param encoding the encoding to use.
* @throws XMLStreamException
*/
public abstract XMLStreamWriter createXMLStreamWriter(OutputStream stream,
String encoding) throws XMLStreamException;
/**
* Allows the user to set specific features/properties on the underlying
* implementation.
*
* @param name the name of the property.
* @param value the value of the property.
* @throws IllegalArgumentException if the property is not supported.
*/
public abstract void setProperty(String name, Object value)
throws IllegalArgumentException;
/**
* Gets a feature/property on the underlying implementation.
*
* @param name the name of the property
* @return the value of the property
* @throws IllegalArgumentException if the property is not supported.
*/
public abstract Object getProperty(String name)
throws IllegalArgumentException;
/**
* Queries the set of properties that this factory supports.
*
* @param name the name of the property (may not be null).
* @return <code>true</code> if the property is supported;
* <code>false</code> otherwise.
*/
public abstract boolean isPropertySupported(String name);
/**
* This class represents the default implementation.
*/
private static final class Default extends XMLOutputFactory {
// Property setting.
private Boolean _isRepairingNamespaces = Boolean.FALSE;
// Property setting.
private String _repairingPrefix = "ns";
// Property setting.
private Boolean _automaticEmptyElements = Boolean.FALSE;
// Property setting.
private Boolean _noEmptyElementTag = Boolean.FALSE;
// Property setting.
private String _indentation;
// Property setting.
private String _lineSeparator = "\n";
// Implements XMLOutputFactory abstract method.
public XMLStreamWriter createXMLStreamWriter(Writer writer)
throws XMLStreamException {
XMLStreamWriterImpl xmlWriter = newWriter();
xmlWriter.setOutput(writer);
return xmlWriter;
}
// Implements XMLOutputFactory abstract method.
public XMLStreamWriter createXMLStreamWriter(OutputStream stream)
throws XMLStreamException {
XMLStreamWriterImpl xmlWriter = newWriter();
xmlWriter.setOutput(stream);
return xmlWriter;
}
// Implements XMLOutputFactory abstract method.
public XMLStreamWriter createXMLStreamWriter(OutputStream stream,
String encoding) throws XMLStreamException {
if ((encoding == null) || encoding.equals("UTF-8")
|| encoding.equals("utf-8"))
return createXMLStreamWriter(stream);
XMLStreamWriterImpl xmlWriter = newWriter();
xmlWriter.setOutput(stream, encoding);
return xmlWriter;
}
private XMLStreamWriterImpl newWriter() {
XMLStreamWriterImpl xmlWriter = (XMLStreamWriterImpl) XML_WRITER_FACTORY
.object();
xmlWriter._objectFactory = XML_WRITER_FACTORY;
xmlWriter.setRepairingNamespaces(_isRepairingNamespaces
.booleanValue());
xmlWriter.setRepairingPrefix(_repairingPrefix);
xmlWriter.setIndentation(_indentation);
xmlWriter.setLineSeparator(_lineSeparator);
xmlWriter.setAutomaticEmptyElements(_automaticEmptyElements
.booleanValue());
xmlWriter.setNoEmptyElementTag(_noEmptyElementTag
.booleanValue());
return xmlWriter;
}
// Implements XMLOutputFactory abstract method.
public void setProperty(String name, Object value)
throws IllegalArgumentException {
if (name.equals(IS_REPAIRING_NAMESPACES)) {
_isRepairingNamespaces = (Boolean) value;
} else if (name.equals(REPAIRING_PREFIX)) {
_repairingPrefix = (String) value;
} else if (name.equals(AUTOMATIC_EMPTY_ELEMENTS)) {
_automaticEmptyElements = (Boolean) value;
} else if (name.equals(NO_EMPTY_ELEMENT_TAG)) {
_noEmptyElementTag = (Boolean) value;
} else if (name.equals(INDENTATION)) {
_indentation = (String) value;
} else if (name.equals(LINE_SEPARATOR)) {
_lineSeparator = (String) value;
} else {
throw new IllegalArgumentException("Property: " + name
+ " not supported");
}
}
// Implements XMLOutputFactory abstract method.
public Object getProperty(String name) throws IllegalArgumentException {
if (name.equals(IS_REPAIRING_NAMESPACES)) {
return _isRepairingNamespaces;
} else if (name.equals(REPAIRING_PREFIX)) {
return _repairingPrefix;
} else if (name.equals(AUTOMATIC_EMPTY_ELEMENTS)) {
return _automaticEmptyElements;
} else if (name.equals(NO_EMPTY_ELEMENT_TAG)) {
return _noEmptyElementTag;
} else if (name.equals(INDENTATION)) {
return _indentation;
} else if (name.equals(LINE_SEPARATOR)) {
return _lineSeparator;
} else {
throw new IllegalArgumentException("Property: " + name
+ " not supported");
}
}
// Implements XMLOutputFactory abstract method.
public boolean isPropertySupported(String name) {
return name.equals(IS_REPAIRING_NAMESPACES)
|| name.equals(REPAIRING_PREFIX)
|| name.equals(AUTOMATIC_EMPTY_ELEMENTS)
|| name.equals(NO_EMPTY_ELEMENT_TAG)
|| name.equals(INDENTATION)
|| name.equals(LINE_SEPARATOR);
}
}
/**
* Holds a factory producing reusable writer instances.
*/
private static final ObjectFactory XML_WRITER_FACTORY = new ObjectFactory() {
protected Object create() {
return new XMLStreamWriterImpl();
}
protected void cleanup(Object obj) {
((XMLStreamWriterImpl) obj).reset();
}
};
// Allows instances of private classes to be factory produced.
static {
ObjectFactory.setInstance(new ObjectFactory() {
protected Object create() {
return new Default();
} }, Default.class);
}
}