org.fcrepo.utilities.XmlTransformUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fcrepo-common Show documentation
Show all versions of fcrepo-common Show documentation
Common, generally useful utilities
/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.utilities;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.SAXParser;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.pool.impl.SoftReferenceObjectPool;
import org.fcrepo.utilities.xml.PoolableDocumentBuilderFactory;
import org.fcrepo.utilities.xml.PoolableSAXParserFactory;
import org.fcrepo.utilities.xml.PoolableTransformerFactoryFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
*
* @author Edwin Shin
* @version $Id$
*/
public class XmlTransformUtility {
private static final Map> TEMPLATES_CACHE =
new HashMap>();
// A pool of namespace-aware DocumentBuilders
// Using a Stack pool means that objectsare created on demand after the
// pool is exhausted
//TODO how should the default values be configured?
private static final SoftReferenceObjectPool DOCUMENT_BUILDERS =
new SoftReferenceObjectPool(
new PoolableDocumentBuilderFactory(true, false));
private static final SoftReferenceObjectPool TRANSFORM_FACTORIES =
new SoftReferenceObjectPool(
new PoolableTransformerFactoryFactory());
private static final SoftReferenceObjectPool SAX_PARSERS =
new SoftReferenceObjectPool(
new PoolableSAXParserFactory(true, false));
/**
* Convenience method to get a new instance of a TransformerFactory.
* If the {@link #TransformerFactory} is an instance of
* net.sf.saxon.TransformerFactoryImpl, the attribute
* {@link #FeatureKeys.VERSION_WARNING} will be set to false in order to
* suppress the warning about using an XSLT1 stylesheet with an XSLT2
* processor.
*
* @return a new instance of TransformerFactory
* @throws Exception
*/
public static TransformerFactory borrowTransformerFactory() throws Exception {
return (TransformerFactory) TRANSFORM_FACTORIES.borrowObject();
}
public static void returnTransformerFactory(TransformerFactory factory) {
try {
TRANSFORM_FACTORIES.returnObject(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Transformer getTransformer() throws Exception {
return getTransformer(null);
}
public static Transformer getTransformer(Source src) throws Exception {
TransformerFactory factory = null;
Transformer result = null;
try {
factory = TRANSFORM_FACTORIES.borrowObject();
result = (src == null) ? factory.newTransformer()
: factory.newTransformer(src);
} finally {
if (factory != null) {
try {
TRANSFORM_FACTORIES.returnObject(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* Try to cache parsed Templates, but check for changes on disk
* @param src
* @return
*/
public static Templates getTemplates(File src) throws TransformerException {
String key = src.getAbsolutePath();
TimestampedCacheEntry entry = TEMPLATES_CACHE.get(key);
// check to see if it is null or has changed
if (entry == null || entry.timestamp() < src.lastModified()) {
TransformerFactory factory = null;
try {
factory = borrowTransformerFactory();
Templates template = factory.newTemplates(new StreamSource(src));
entry = new TimestampedCacheEntry(src.lastModified(), template);
} catch (Exception e) {
throw new TransformerException(e.getMessage(), e);
} finally {
if (factory != null) returnTransformerFactory(factory);
}
TEMPLATES_CACHE.put(key, entry);
}
return entry.value();
}
public static Templates getTemplates(StreamSource source)
throws TransformerException {
TransformerFactory tf = null;
Templates result = null;
try {
tf = borrowTransformerFactory();
result = tf.newTemplates(source);
} catch (Exception e) {
throw new TransformerException(e.getMessage(), e);
} finally {
if (tf != null) returnTransformerFactory(tf);
}
return result;
}
public static DocumentBuilder borrowDocumentBuilder() throws Exception {
return (DocumentBuilder) DOCUMENT_BUILDERS.borrowObject();
}
public static void returnDocumentBuilder(DocumentBuilder object) {
try {
DOCUMENT_BUILDERS.returnObject(object);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Document parseNamespaceAware(File src)
throws Exception {
return parseNamespaceAware(new FileInputStream(src));
}
public static Document parseNamespaceAware(InputStream src)
throws Exception {
Document result = null;
DocumentBuilder builder =
(DocumentBuilder) DOCUMENT_BUILDERS.borrowObject();
try {
result = builder.parse(src);
} finally {
DOCUMENT_BUILDERS.returnObject(builder);
}
return result;
}
public static void parseWithoutValidating(InputStream in, DefaultHandler handler)
throws SAXException, IOException {
parseWithoutValidating(new InputSource(in), handler);
}
public static void parseWithoutValidating(InputSource in, DefaultHandler handler)
throws SAXException, IOException {
SAXParser parser = null;
try {
parser = (SAXParser) SAX_PARSERS.borrowObject();
} catch (Exception e) {
throw new RuntimeException("Error initializing SAX parser", e);
}
try {
parser.parse(in, handler);
} finally {
if (parser != null) {
try {
SAX_PARSERS.returnObject(parser);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}