/*
* Copyright (c) 2005 Thomas Weise
*
* E-Mail : tweise@gmx.de
* Creation Date : 2005-07-25 15:24:27
* Original Filename: org.sfc.xml.XML.java
* Version : 1.0.3
* Last modification: 2006-04-20
* by: Thomas Weise
*
* License : GNU LESSER GENERAL PUBLIC LICENSE
* Version 2.1, February 1999
* You should have received a copy of this license along
* with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307, USA or download the license under
* http://www.gnu.org/copyleft/lesser.html.
*
* Warranty : This software is provided "as is" 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.
*/
package org.sfc.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.sfc.io.IO;
import org.sfc.scoped.AlreadyDisposedException;
import org.sfc.scoped.IReferenceCounted;
import org.sfc.utils.Typesafe;
import org.sfc.xml.dom.DOMDocument;
import org.sfc.xml.sax.SAXImplementor;
/**
* The xml-singleton class comes with some useful helper functions.
*
* @author Thomas Weise
*/
public final class XML
{
/**
* The line break character changed to \0a in xml.
*/
public static final char LINE_BREAK = '\n';
/**
* Separator for prefix to namespace.
*/
public static final char NAMESPACE_SEPARATOR = ':';
/**
* The prefix for namespace-attributes.
*/
public static final String XMLNS = "xmlns";
/**
* The prefix for namespace-attributes.
*/
public static final String XMLNSQ =
XMLNS + NAMESPACE_SEPARATOR;
/**
* The preferred encoding for xml-data.
*/
public static final String PREFERED_ENCODING = "UTF-8";
/**
* The shared transformer factory.
*/
private static final TransformerFactory TRANSFORMER_FACTORY ;
/**
* The shared sax parser factory.
*/
private static final SAXParserFactory PARSER_FACTORY ;
/**
* The shared document builder factory.
*/
private static final DocumentBuilderFactory BUILDER_FACTORY ;
static
{
PARSER_FACTORY = SAXParserFactory.newInstance();
PARSER_FACTORY.setNamespaceAware(true);
TRANSFORMER_FACTORY = TransformerFactory.newInstance();
BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
BUILDER_FACTORY.setNamespaceAware(true);
BUILDER_FACTORY.setIgnoringComments(true);
BUILDER_FACTORY.setXIncludeAware(true);
}
/**
* Prevent anyone from instantiating this class.
*/
private XML()
{
Typesafe.do_not_call();
}
/**
* The method creates a new xml document.
* @param p_namespace_uri The namespace to use for this document
* @param p_qualified_name The qualified name of the document's root
* element.
* @return A new dom document, null on error.
*
* @exception ParserConfigurationException If the underlying parser does
* not support a feature/
* property needed.
*/
public static final DOMDocument create_document(
final String p_namespace_uri,
final String p_qualified_name)
throws ParserConfigurationException
{
DocumentBuilder l_b;
Document l_d;
l_b = BUILDER_FACTORY.newDocumentBuilder();
if( (p_namespace_uri == null) ||
(p_namespace_uri.length() <= 0))
{
if((p_qualified_name != null) ||
(p_qualified_name.length() >= 0))
{
l_d = l_b.getDOMImplementation()
.createDocument(null, p_qualified_name, null);
}
else
{
l_d = l_b.newDocument();
}
}
else
{
l_d = l_b.getDOMImplementation()
.createDocument(p_namespace_uri, p_qualified_name, null);
}
return DOMDocument.get_dom_document(l_d, p_namespace_uri);
}
/**
* This method returns a DOM-Document extracted from the InputStream
* p_is.
* @param p_source The source to read the document from or null
* if a new document should be created.
* In the first case, p_source can be a instance of
* InputStream, File, InputSource or a String identifying
* an url.
* @return The DOM-Document tree read from p_source or
* a newly generated one (if p_source == null).
* @exception IOException If the source could not be read fully.
* @exception SAXException If the document loaded was invalid.
* @exception ParserConfigurationException if a DocumentBuilder cannot
* be created which satisfies
* the configuration requested.
*/
public static final DOMDocument load_document (final Object p_source)
throws IOException, SAXException, ParserConfigurationException
{
ReferenceCountedInputSource l_is;
URI l_u;
Object l_uri;
DOMDocument l_d;
if(p_source != null)
{
if( (l_is = get_input_source(p_source)) != null )
{
l_uri = l_is.getSystemId();
if(l_uri == null) l_uri = p_source;
try
{
l_d = DOMDocument.get_dom_document(BUILDER_FACTORY
.newDocumentBuilder().parse(l_is), null);
if(l_d != null)
{
if((l_u = IO.get_uri(p_source)) != null)
{
l_d.set_base_uri(l_u);
}
return l_d;
}
}
finally
{
l_is.do_release();
}
}
else IO.close(p_source);
}
return create_document(null, null);
}
/**
* With that method you can write a DOM-Tree Document to an OutputStream.
* @param p_doc The document to be written.
* @param p_dest The destination to write to.
* Can be an instance of OutputStream, File,
* StreamResult or Writer.
* @exception TransformerConfigurationException May throw this during
* the parse when it is constructing the
* Templates object and fails.
* @exception TransformerException If an unrecoverable error occurs
* during the course of the transformation.
*/
public static final void store_document (final Document p_doc,
final Object p_dest)
throws TransformerConfigurationException, TransformerException
{
Result l_s;
if(p_dest != null)
{
if(p_doc != null)
{
p_doc.normalize();
l_s = get_result(p_dest);
try
{
TRANSFORMER_FACTORY.newTransformer().transform(
new DOMSource(p_doc), l_s );
}
finally
{
if(l_s instanceof IReferenceCounted)
{
((IReferenceCounted)l_s).release();
}
}
}
else
{
IO.close(p_dest);
}
}
}
/**
* Converts any given object to an input source. The returned input source
* can be closed by IO.close()
to ensure that all open streams
* are well, erm, closed.
*
* @param p_object The object to convert into an input source.
* @return The InputSource
represented by this object, or
* null if the object could not be converted into an input source.
*/
public static final ReferenceCountedInputSource get_input_source
(final Object p_object)
{
URL l_url;
InputStream l_is;
Reader l_r;
Object l_check;
ReferenceCountedInputSource l_rci;
if(p_object instanceof ReferenceCountedInputSource)
{
return ((ReferenceCountedInputSource)p_object);
}
l_rci = new ReferenceCountedInputSource();
if(p_object instanceof InputSource)
{
l_rci.set_input_source((InputSource)p_object);
return l_rci;
}
if(p_object instanceof StreamSource)
{
l_rci.set_stream_source((StreamSource)p_object);
return l_rci;
}
l_url = IO.get_url(p_object);
if(l_url != null)
{
l_check = l_url;
l_rci.setSystemId(l_url.toExternalForm());
}
else
{
l_check = p_object;
}
l_is = IO.get_input_stream(l_check);
if(l_is != null)
{
l_rci.setByteStream(l_is);
return l_rci;
}
l_r = IO.get_reader(l_check);
if(l_r != null)
{
l_rci.setCharacterStream(l_r);
return l_rci;
}
if(l_url == null)
{
l_url = IO.get_resource_url(p_object, 1);
if(l_url != null)
{
l_check = l_url;
l_rci.setSystemId(l_url.toExternalForm());
}
else
{
l_check = p_object;
}
}
l_is = IO.get_resource_input_stream(l_check, 1);
if(l_is != null)
{
l_rci.setByteStream(l_is);
return l_rci;
}
return (l_url != null) ? l_rci : null;
}
/**
* Converts any given object to a source. The returned source should be
* closed by IO.close()
to ensure that all open streams
* are well, erm, closed when done with it.
*
* @param p_object The object to convert into a source.
* @return The Source
represented by this object, or
* null if the object could not be converted into a result.
*/
public static final Source get_source (final Object p_object)
{
URL l_url;
InputStream l_is;
Reader l_r;
Object l_check;
ReferenceCountedStreamSource l_x;
ReferenceCountedSAXSource l_ssx;
if(p_object instanceof ReferenceCountedStreamSource)
{
return ((ReferenceCountedStreamSource)p_object);
}
if(p_object instanceof Document)
{
return new DOMSource((Document)p_object);
}
if(p_object instanceof DOMSource)
{
return ((DOMSource)p_object);
}
if(p_object instanceof ReferenceCountedSAXSource)
{
return ((ReferenceCountedSAXSource)p_object);
}
if(p_object instanceof SAXSource)
{
l_ssx = new ReferenceCountedSAXSource();
l_ssx.set_sax_source((SAXSource)p_object);
return l_ssx;
}
l_x = new ReferenceCountedStreamSource();
if(p_object instanceof StreamSource)
{
l_x.set_stream_source((StreamSource)p_object);
return l_x;
}
if(p_object instanceof InputSource)
{
l_x.set_input_source((InputSource)p_object);
return l_x;
}
l_url = IO.get_url(p_object);
if(l_url != null)
{
l_check = l_url;
l_x.setSystemId(l_url.toExternalForm());
}
else
{
l_check = p_object;
}
l_is = IO.get_input_stream(l_check);
if(l_is != null)
{
l_x.setInputStream(l_is);
return l_x;
}
l_r = IO.get_reader(l_check);
if(l_r != null)
{
l_x.setReader(l_r);
return l_x;
}
if(l_url == null)
{
l_url = IO.get_resource_url(p_object, 1);
if(l_url != null)
{
l_check = l_url;
l_x.setSystemId(l_url.toExternalForm());
}
else
{
l_check = p_object;
}
}
l_is = IO.get_resource_input_stream(l_check, 1);
if(l_is != null)
{
l_x.setInputStream(l_is);
return l_x;
}
if(p_object instanceof Source) return ((Source)p_object);
return ((l_url != null) ? l_x : null);
}
/**
* Converts any given object to a result. The returned result should be
* closed by IO.close()
to ensure that all open streams
* are well, erm, closed when done with it.
*
* @param p_object The object to convert into a result.
* @return The Result
represented by this object, or
* null if the object could not be converted into a result.
*/
public static final Result get_result (final Object p_object)
{
URL l_url;
OutputStream l_os;
Writer l_w;
Object l_check;
ReferenceCountedStreamResult l_x;
if(p_object instanceof ReferenceCountedStreamResult)
{
return ((ReferenceCountedStreamResult)p_object);
}
l_x = new ReferenceCountedStreamResult();
if(p_object instanceof StreamResult)
{
l_x.set_stream_result((StreamResult)p_object);
return l_x;
}
l_url = IO.get_url(p_object);
if(l_url != null)
{
l_check = l_url;
l_x.setSystemId(l_url.toExternalForm());
}
else
{
l_check = p_object;
}
l_os = IO.get_output_stream(l_check);
if(l_os != null)
{
l_x.setOutputStream(l_os);
return l_x;
}
l_w = IO.get_writer(l_check, PREFERED_ENCODING);
if(l_w != null)
{
l_x.setWriter(l_w);
return l_x;
}
if(p_object instanceof Result) return ((Result)p_object);
return ((l_url != null) ? l_x : null);
}
/**
* Close the specified xml io-object.
* @param p_object The object to be closed.
* @return The IOException
thrown during the process, or
* null
if all went ok.
*/
public static final IOException close (Object p_object)
{
Reader l_r;
Writer l_w;
InputStream l_is;
OutputStream l_os;
InputSource l_xis;
StreamSource l_xss;
StreamResult l_xsr;
IOException l_io1, l_io2;
if(p_object instanceof ReferenceCountedInputSource)
{
return ((ReferenceCountedInputSource)p_object).do_release();
}
if(p_object instanceof ReferenceCountedSAXSource)
{
return ((ReferenceCountedSAXSource)p_object).do_release();
}
if(p_object instanceof ReferenceCountedStreamSource)
{
return ((ReferenceCountedStreamSource)p_object).do_release();
}
if(p_object instanceof ReferenceCountedStreamResult)
{
return ((ReferenceCountedStreamResult)p_object).do_release();
}
l_r = null;
l_w = null;
l_is = null;
l_os = null;
if(p_object instanceof SAXSource)
{
p_object = ((SAXSource)p_object).getInputSource();
}
if(p_object instanceof InputSource)
{
l_xis = ((InputSource)p_object);
l_r = l_xis.getCharacterStream();
l_is = l_xis.getByteStream();
}
else
{
if(p_object instanceof StreamResult)
{
l_xsr = ((StreamResult)p_object);
l_w = l_xsr.getWriter();
l_os = l_xsr.getOutputStream();
}
else
{
if(p_object instanceof StreamSource)
{
l_xss = ((StreamSource)p_object);
l_r = l_xss.getReader();
l_is = l_xss.getInputStream();
}
else return null;
}
}
l_io1 = ((l_r != null) ? IO.close(l_r) : null);
l_io2 = ((l_w != null) ? IO.close(l_w) : null);
if(l_io1 == null) l_io1 = l_io2;
l_io2 = ((l_is != null) ? IO.close(l_is) : null);
if(l_io1 == null) l_io1 = l_io2;
l_io2 = ((l_os != null) ? IO.close(l_os) : null);
return ((l_io1 != null) ? l_io1 : l_io2);
}
/**
* This method transforms the xml data p_xml_source_data
with
* the xslt style sheet p_xslt_transform_data
and pushes the
* result into the destionation p_xml_dest_data
.
* All those objects can be files, streams or file names, their type
* will automatically be detected and handled.
* @param p_xml_source_data The source xml data to be transformed.
* @param p_xslt_transform_data The xslt style sheet to transform the
* xml source data.
* @param p_xml_dest_data The destination to put the resulting data
* in.
* @param p_resolver An optional resolver for uris. This might
* be needed if one of the data sources'
* origin is a stream inside another jar
* archive, for example. You can leave this
* parameter null if you don't want to specify
* an external uri resolver.
* @throws TransformerException If an unrecoverable error occurs
* during the course of the transformation.
* @throws TransformerFactoryConfigurationError
* @throws IOException If something goes wrong with io.
* if the implmentation is not available or cannot be instantiated.
*/
public static final void xslt_transform(
final Object p_xml_source_data,
final Object p_xslt_transform_data,
final Object p_xml_dest_data,
final URIResolver p_resolver)
throws TransformerException, TransformerFactoryConfigurationError,
IOException
{
Source l_xml_source, l_xslt_source;
Result l_dest;
TransformerFactory l_tf;
Transformer l_t;
IOException l_ioex, l_ioex2;
try
{
l_dest = get_result(p_xml_dest_data);
if(l_dest != null)
{
try
{
l_xml_source = get_source(p_xml_source_data);
if(l_xml_source != null)
{
try
{
l_xslt_source = get_source(p_xslt_transform_data);
if(l_xslt_source != null)
{
try
{
if(p_resolver != null)
{
l_tf = TransformerFactory.newInstance();
l_tf.setURIResolver(p_resolver);
}
else
{
l_tf = TRANSFORMER_FACTORY;
}
l_t = l_tf.newTransformer(l_xslt_source);
if(l_t != null)
{
l_t.transform(l_xml_source, l_dest);
}
}
finally
{
l_ioex = close(l_xslt_source);
if(l_ioex != null) throw l_ioex;
}
}
}
finally
{
l_ioex = close(l_xml_source);
if(l_ioex != null) throw l_ioex;
}
}
}
finally
{
l_ioex = close(l_dest);
if(l_ioex != null) throw l_ioex;
}
}
}
finally
{
l_ioex = close(p_xml_source_data);
l_ioex2 = close(p_xslt_transform_data);
if(l_ioex2 != null) l_ioex = l_ioex2;
l_ioex2 = close(p_xml_dest_data);
if(l_ioex2 != null) throw l_ioex2;
if(l_ioex != null) throw l_ioex;
}
}
/**
* Parse a file with a sax parser using this method.
* The sax-parser will use the interfaces supplied by
* p_implementor
.
* Use an XMLImplementer
object or one of its derivates here.
*
* @param p_source The data sourceto read and parse data from.
* Can be a Reader, a File, an InputSource
* or an InputStream.
* @param p_implementor A SAXImplementor object that comes with all
* the standard sax handlers. The generated events
* will all be tunnelled to that object.
*
* @exception SAXException Any SAX exception, possibly
* wrapping another exception.
* @exception IOException An IO exception from the parser,
* possibly from a byte stream or character
* stream supplied by the application.
* @exception ParserConfigurationException if a parser cannot
* be created which satisfies
* the requested configuration.
* @exception SAXNotRecognizedException When the underlying XMLReader
* does not recognize the
* property name.
* @exception SAXNotSupportedException When the underlying XMLReader
* recognizes the property name
* but doesn't support the
* property.
*/
public static final void sax_parse(final Object p_source,
final SAXImplementor p_implementor)
throws SAXNotRecognizedException, SAXNotSupportedException,
SAXException, ParserConfigurationException, IOException
{
XMLReader l_reader;
InputSource l_is;
IOException l_ioex;
try
{
l_is = get_input_source(p_source);
if(l_is != null)
{
try
{
l_reader = PARSER_FACTORY.newSAXParser().getXMLReader();
p_implementor.install_to(l_reader);
l_reader.parse(l_is);
}
finally
{
try
{
l_ioex = close(l_is);
if(l_ioex != null) throw l_ioex;
}
catch(AlreadyDisposedException l_ade)
{
//This will normally happen
}
}
}
}
finally
{
l_ioex = close(p_source);
if(l_ioex != null) throw l_ioex;
}
}
}