572 lines
18 KiB
Java
Executable file
572 lines
18 KiB
Java
Executable file
/* GnomeTransformer.java -
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
package gnu.xml.libxmlj.transform;
|
|
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
|
|
import javax.xml.transform.ErrorListener;
|
|
import javax.xml.transform.Source;
|
|
import javax.xml.transform.SourceLocator;
|
|
import javax.xml.transform.Result;
|
|
import javax.xml.transform.Templates;
|
|
import javax.xml.transform.Transformer;
|
|
import javax.xml.transform.TransformerConfigurationException;
|
|
import javax.xml.transform.TransformerException;
|
|
import javax.xml.transform.URIResolver;
|
|
|
|
import javax.xml.transform.dom.DOMSource;
|
|
import javax.xml.transform.dom.DOMResult;
|
|
import javax.xml.transform.sax.SAXResult;
|
|
import javax.xml.transform.stream.StreamSource;
|
|
import javax.xml.transform.stream.StreamResult;
|
|
|
|
import org.w3c.dom.Node;
|
|
|
|
import org.xml.sax.EntityResolver;
|
|
import org.xml.sax.ErrorHandler;
|
|
|
|
import gnu.xml.libxmlj.dom.GnomeDocument;
|
|
import gnu.xml.libxmlj.sax.GnomeXMLReader;
|
|
import gnu.xml.libxmlj.util.NamedInputStream;
|
|
import gnu.xml.libxmlj.util.StandaloneLocator;
|
|
import gnu.xml.libxmlj.util.XMLJ;
|
|
|
|
/**
|
|
* An implementation of {@link javax.xml.transform.Transformer} which
|
|
* performs XSLT transformation using <code>libxslt</code>.
|
|
*
|
|
* @author Julian Scheid
|
|
* @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
|
|
*/
|
|
public class GnomeTransformer
|
|
extends Transformer
|
|
implements Templates
|
|
{
|
|
|
|
/**
|
|
* The parameters added by the user via {@link setParameter()}.
|
|
*/
|
|
private Map parameters;
|
|
|
|
/**
|
|
* The output properties set by the user.
|
|
*/
|
|
private Properties outputProperties;
|
|
|
|
/**
|
|
* The URI resolver to use during transformation.
|
|
*/
|
|
private URIResolver resolver;
|
|
|
|
/**
|
|
* The error listener for transformation errors.
|
|
*/
|
|
private ErrorListener errorListener;
|
|
|
|
/**
|
|
* Handle to the source stylesheet.
|
|
* This is a native pointer of type xsltStylesheetPtr.
|
|
*/
|
|
private Object stylesheet;
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param source the XSLT stylesheet document source
|
|
* @param resolver the resolver to use during transformation
|
|
* @param errorListener the error listener for transformation errors
|
|
*/
|
|
GnomeTransformer (Source source,
|
|
URIResolver resolver,
|
|
ErrorListener errorListener)
|
|
throws TransformerConfigurationException
|
|
{
|
|
this.resolver = resolver;
|
|
this.errorListener = errorListener;
|
|
parameters = new HashMap ();
|
|
outputProperties = new Properties ();
|
|
|
|
if (source == null)
|
|
{
|
|
stylesheet = newStylesheet ();
|
|
}
|
|
else if (source instanceof StreamSource)
|
|
{
|
|
try
|
|
{
|
|
StreamSource ss = (StreamSource) source;
|
|
NamedInputStream in = XMLJ.getInputStream (ss);
|
|
String systemId = ss.getSystemId ();
|
|
String publicId = ss.getPublicId ();
|
|
String base = XMLJ.getBaseURI (systemId);
|
|
byte[] detectBuffer = in.getDetectBuffer ();
|
|
if (detectBuffer == null)
|
|
{
|
|
String msg = "No document element";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
stylesheet = newStylesheetFromStream (in, detectBuffer, publicId,
|
|
systemId, base,
|
|
(resolver != null),
|
|
(errorListener != null));
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new TransformerConfigurationException (e);
|
|
}
|
|
}
|
|
else if (source instanceof DOMSource)
|
|
{
|
|
DOMSource ds = (DOMSource) source;
|
|
Node node = ds.getNode ();
|
|
if (!(node instanceof GnomeDocument))
|
|
{
|
|
String msg = "Node is not a GnomeDocument";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
GnomeDocument doc = (GnomeDocument) node;
|
|
stylesheet = newStylesheetFromDoc (doc);
|
|
}
|
|
else
|
|
{
|
|
String msg = "Source type not supported (" + source + ")";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy constructor.
|
|
*/
|
|
private GnomeTransformer (Object stylesheet,
|
|
URIResolver resolver,
|
|
ErrorListener errorListener,
|
|
Map parameters,
|
|
Properties outputProperties)
|
|
{
|
|
this.stylesheet = stylesheet;
|
|
this.resolver = resolver;
|
|
this.errorListener = errorListener;
|
|
this.parameters = parameters;
|
|
this.outputProperties = outputProperties;
|
|
}
|
|
|
|
private native Object newStylesheet ()
|
|
throws TransformerConfigurationException;
|
|
|
|
private native Object newStylesheetFromStream (InputStream in,
|
|
byte[] detectBuffer,
|
|
String publicId,
|
|
String systemId,
|
|
String base,
|
|
boolean entityResolver,
|
|
boolean errorHandler)
|
|
throws TransformerConfigurationException;
|
|
|
|
private native Object newStylesheetFromDoc (GnomeDocument doc)
|
|
throws TransformerConfigurationException;
|
|
|
|
//--- Implementation of javax.xml.transform.Transformer follows.
|
|
|
|
// Set, get and clear the parameters to use on transformation
|
|
|
|
public synchronized void setParameter (String parameter, Object value)
|
|
{
|
|
parameters.put (parameter, value);
|
|
}
|
|
|
|
public synchronized Object getParameter (String name)
|
|
{
|
|
return parameters.get (name);
|
|
}
|
|
|
|
public synchronized void clearParameters ()
|
|
{
|
|
parameters.clear ();
|
|
}
|
|
|
|
// Set and get the ErrorListener to use on transformation
|
|
|
|
public void setErrorListener (ErrorListener listener)
|
|
{
|
|
this.errorListener = listener;
|
|
}
|
|
|
|
public ErrorListener getErrorListener ()
|
|
{
|
|
return errorListener;
|
|
}
|
|
|
|
// Set and get the URIResolver to use on transformation
|
|
|
|
public void setURIResolver (URIResolver resolver)
|
|
{
|
|
this.resolver = resolver;
|
|
}
|
|
|
|
public URIResolver getURIResolver ()
|
|
{
|
|
return resolver;
|
|
}
|
|
|
|
// Set the output properties to use on transformation; get default
|
|
// output properties and output properties specified in the
|
|
// stylesheet or by the user.
|
|
|
|
public void setOutputProperties (Properties outputProperties)
|
|
{
|
|
// Note: defensive copying
|
|
this.outputProperties = new Properties (outputProperties);
|
|
}
|
|
|
|
public void setOutputProperty (String name, String value)
|
|
{
|
|
outputProperties.setProperty (name, value);
|
|
}
|
|
|
|
public Properties getOutputProperties ()
|
|
{
|
|
// Note: defensive copying
|
|
return new Properties (this.outputProperties);
|
|
}
|
|
|
|
public String getOutputProperty (String name)
|
|
{
|
|
return outputProperties.getProperty (name);
|
|
}
|
|
|
|
// -- Templates --
|
|
|
|
public Transformer newTransformer ()
|
|
{
|
|
return new GnomeTransformer (stylesheet, resolver, errorListener,
|
|
new HashMap (parameters),
|
|
new Properties (outputProperties));
|
|
}
|
|
|
|
// -- transform --
|
|
|
|
/**
|
|
* Transforms the given source and writes the result to the
|
|
* given target.
|
|
*/
|
|
public void transform (Source source, Result result)
|
|
throws TransformerException
|
|
{
|
|
if (source instanceof StreamSource)
|
|
{
|
|
try
|
|
{
|
|
StreamSource ss = (StreamSource) source;
|
|
NamedInputStream in = XMLJ.getInputStream (ss);
|
|
String publicId = ss.getPublicId ();
|
|
String systemId = ss.getSystemId ();
|
|
String base = XMLJ.getBaseURI (systemId);
|
|
byte[] detectBuffer = in.getDetectBuffer ();
|
|
if (detectBuffer == null)
|
|
{
|
|
throw new TransformerException ("No document element");
|
|
}
|
|
if (result instanceof StreamResult)
|
|
{
|
|
OutputStream out = XMLJ.getOutputStream ((StreamResult) result);
|
|
transformStreamToStream (in, detectBuffer, publicId, systemId,
|
|
base, (resolver != null),
|
|
(errorListener != null), out);
|
|
}
|
|
else if (result instanceof DOMResult)
|
|
{
|
|
DOMResult dr = (DOMResult) result;
|
|
GnomeDocument ret =
|
|
transformStreamToDoc (in, detectBuffer, publicId, systemId,
|
|
base, (resolver != null),
|
|
(errorListener != null));
|
|
dr.setNode (ret);
|
|
dr.setSystemId (null);
|
|
}
|
|
else if (result instanceof SAXResult)
|
|
{
|
|
SAXResult sr = (SAXResult) result;
|
|
transformStreamToSAX (in, detectBuffer, publicId, systemId,
|
|
base, (resolver != null),
|
|
(errorListener != null),
|
|
getSAXContext (sr));
|
|
}
|
|
else
|
|
{
|
|
String msg = "Result type not supported (" + result + ")";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new TransformerException (e);
|
|
}
|
|
}
|
|
else if (source instanceof DOMSource)
|
|
{
|
|
DOMSource ds = (DOMSource) source;
|
|
Node node = ds.getNode ();
|
|
if (!(node instanceof GnomeDocument))
|
|
{
|
|
String msg = "Node is not a GnomeDocument (" + node + ")";
|
|
throw new TransformerException (msg);
|
|
}
|
|
GnomeDocument doc = (GnomeDocument) node;
|
|
if (result instanceof StreamResult)
|
|
{
|
|
try
|
|
{
|
|
OutputStream out = XMLJ.getOutputStream ((StreamResult) result);
|
|
transformDocToStream (doc, out);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new TransformerException (e);
|
|
}
|
|
}
|
|
else if (result instanceof DOMResult)
|
|
{
|
|
DOMResult dr = (DOMResult) result;
|
|
GnomeDocument ret = transformDocToDoc (doc);
|
|
dr.setNode (ret);
|
|
dr.setSystemId (null);
|
|
}
|
|
else if (result instanceof SAXResult)
|
|
{
|
|
SAXResult sr = (SAXResult) result;
|
|
transformDocToSAX (doc, getSAXContext (sr));
|
|
}
|
|
else
|
|
{
|
|
String msg = "Result type not supported";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String msg = "Source type not supported";
|
|
throw new TransformerConfigurationException (msg);
|
|
}
|
|
}
|
|
|
|
private GnomeXMLReader getSAXContext (SAXResult result)
|
|
{
|
|
GnomeXMLReader ctx = new GnomeXMLReader ();
|
|
ctx.setContentHandler (result.getHandler ());
|
|
ctx.setLexicalHandler (result.getLexicalHandler ());
|
|
if (errorListener != null)
|
|
{
|
|
ErrorHandler errorHandler =
|
|
new ErrorListenerErrorHandler (errorListener);
|
|
ctx.setErrorHandler (errorHandler);
|
|
}
|
|
if (resolver != null)
|
|
{
|
|
EntityResolver entityResolver =
|
|
new URIResolverEntityResolver (resolver);
|
|
ctx.setEntityResolver (entityResolver);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
private native void transformStreamToStream (InputStream in,
|
|
byte[] detectBuffer,
|
|
String publicId,
|
|
String systemId,
|
|
String base,
|
|
boolean entityResolver,
|
|
boolean errorHandler,
|
|
OutputStream out)
|
|
throws TransformerException;
|
|
|
|
private native GnomeDocument transformStreamToDoc (InputStream in,
|
|
byte[] detectBuffer,
|
|
String publicId,
|
|
String systemId,
|
|
String base,
|
|
boolean entityResolver,
|
|
boolean errorHandler)
|
|
throws TransformerException;
|
|
|
|
private native void transformStreamToSAX (InputStream in,
|
|
byte[] detectBuffer,
|
|
String publicId,
|
|
String systemId,
|
|
String base,
|
|
boolean entityResolver,
|
|
boolean errorHandler,
|
|
GnomeXMLReader out)
|
|
throws TransformerException;
|
|
|
|
private native void transformDocToStream (GnomeDocument in,
|
|
OutputStream out)
|
|
throws TransformerException;
|
|
|
|
private native GnomeDocument transformDocToDoc (GnomeDocument in)
|
|
throws TransformerException;
|
|
|
|
private native void transformDocToSAX (GnomeDocument in,
|
|
GnomeXMLReader out)
|
|
throws TransformerException;
|
|
|
|
/*
|
|
* Retrieve parameters as a string array.
|
|
* This is a convenience method called from native code.
|
|
*/
|
|
private String[] getParameterArray ()
|
|
{
|
|
String[] parameterArray = new String[parameters.size () * 2];
|
|
int index = 0;
|
|
for (Iterator it = parameters.keySet ().iterator ();
|
|
it.hasNext ();
|
|
++index)
|
|
{
|
|
String parameterKey = (String) it.next ();
|
|
String parameterValue = (String) parameters.get (parameterKey);
|
|
parameterArray[index * 2 + 0] = parameterKey;
|
|
parameterArray[index * 2 + 1] =
|
|
"'" + ((parameterValue != null) ? parameterValue : "") + "'";
|
|
// FIXME encode parameter value correctly for XPath
|
|
}
|
|
return parameterArray;
|
|
}
|
|
|
|
// -- Free xsltStylesheet handle --
|
|
|
|
public void finalize ()
|
|
{
|
|
if (stylesheet != null)
|
|
{
|
|
free ();
|
|
stylesheet = null;
|
|
}
|
|
}
|
|
|
|
private native void free ();
|
|
|
|
// -- Callbacks --
|
|
|
|
private InputStream resolveEntity (String publicId, String systemId)
|
|
throws TransformerException
|
|
{
|
|
if (resolver != null)
|
|
{
|
|
systemId = resolver.resolve (null, systemId).getSystemId ();
|
|
}
|
|
if (systemId == null)
|
|
{
|
|
return null;
|
|
}
|
|
try
|
|
{
|
|
URL url = new URL (systemId);
|
|
return XMLJ.getInputStream (url);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new TransformerException (e);
|
|
}
|
|
}
|
|
|
|
private void setDocumentLocator (Object ctx, Object loc)
|
|
{
|
|
}
|
|
|
|
private void warning (String message,
|
|
int lineNumber,
|
|
int columnNumber,
|
|
String publicId,
|
|
String systemId)
|
|
throws TransformerException
|
|
{
|
|
if (errorListener == null)
|
|
{
|
|
return;
|
|
}
|
|
SourceLocator l = new StandaloneLocator (lineNumber,
|
|
columnNumber,
|
|
publicId,
|
|
systemId);
|
|
errorListener.warning (new TransformerException (message, l));
|
|
}
|
|
|
|
private void error (String message,
|
|
int lineNumber,
|
|
int columnNumber,
|
|
String publicId,
|
|
String systemId)
|
|
throws TransformerException
|
|
{
|
|
if (errorListener == null)
|
|
{
|
|
return;
|
|
}
|
|
SourceLocator l = new StandaloneLocator (lineNumber,
|
|
columnNumber,
|
|
publicId,
|
|
systemId);
|
|
errorListener.error (new TransformerException (message, l));
|
|
}
|
|
|
|
private void fatalError (String message,
|
|
int lineNumber,
|
|
int columnNumber,
|
|
String publicId,
|
|
String systemId)
|
|
throws TransformerException
|
|
{
|
|
if (errorListener == null)
|
|
{
|
|
return;
|
|
}
|
|
SourceLocator l = new StandaloneLocator (lineNumber,
|
|
columnNumber,
|
|
publicId,
|
|
systemId);
|
|
errorListener.fatalError (new TransformerException (message, l));
|
|
}
|
|
|
|
}
|