/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.util.location.Location;
import com.opensymphony.xwork2.util.location.LocationAttributes;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import java.util.Map;
/**
* Helper class to create and retrieve information from location-enabled
* DOM-trees.
*
* @since 1.2
*/
public class DomHelper {
private static final Logger LOG = LoggerFactory.getLogger(DomHelper.class);
public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
public static Location getLocationObject(Element element) {
return LocationAttributes.getLocation(element);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
* using the {@link #getLocationObject(Element)} method.
*
* @param inputSource the inputSource to read the document from
*/
public static Document parse(InputSource inputSource) {
return parse(inputSource, null);
}
/**
* Creates a W3C Document that remembers the location of each element in
* the source file. The location of element nodes can then be retrieved
* using the {@link #getLocationObject(Element)} method.
*
* @param inputSource the inputSource to read the document from
* @param dtdMappings a map of DTD names and public ids
*/
public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) {
SAXParserFactory factory = null;
String parserProp = System.getProperty("xwork.saxParserFactory");
if (parserProp != null) {
try {
Class clazz = ObjectFactory.getObjectFactory().getClassInstance(parserProp);
factory = (SAXParserFactory) clazz.newInstance();
}
catch (ClassNotFoundException e) {
LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': " + parserProp, e);
}
catch (Exception e) {
LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': " + parserProp, e);
}
}
if (factory == null) {
factory = SAXParserFactory.newInstance();
}
factory.setValidating((dtdMappings != null));
factory.setNamespaceAware(true);
SAXParser parser = null;
try {
parser = factory.newSAXParser();
} catch (Exception ex) {
throw new XWorkException("Unable to create SAX parser", ex);
}
DOMBuilder builder = new DOMBuilder();
// Enhance the sax stream with location information
ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
try {
parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
} catch (Exception ex) {
throw new XWorkException(ex);
}
return builder.getDocument();
}
/**
* The <code>DOMBuilder</code> is a utility class that will generate a W3C
* DOM Document from SAX events.
*
* @author <a href="mailto:[email protected]">Carsten Ziegeler</a>
*/
static public class DOMBuilder implements ContentHandler {
/** The default transformer factory shared by all instances */
protected static SAXTransformerFactory FACTORY;
/** The transformer factory */
protected SAXTransformerFactory factory;
/** The result */
protected DOMResult result;
/** The parentNode */
protected Node parentNode;
protected ContentHandler nextHandler;
static {
String parserProp = System.getProperty("xwork.saxTransformerFactory");
if (parserProp != null) {
try {
Class clazz = ObjectFactory.getObjectFactory().getClassInstance(parserProp);
FACTORY = (SAXTransformerFactory) clazz.newInstance();
}
catch (ClassNotFoundException e) {
LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': " + parserProp, e);
}
catch (Exception e) {
LOG.error("Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': " + parserProp, e);
}
}
if (FACTORY == null) {
FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
}
}
/**
* Construct a new instance of this DOMBuilder.
*/
public DOMBuilder() {
this((Node) null);
}
/**
* Construct a new instance of this DOMBuilder.
*/
public DOMBuilder(SAXTransformerFactory factory) {
this(factory, null);
}
/**
* Constructs a new instance that appends nodes to the given parent node.
*/
public DOMBuilder(Node parentNode) {
this(null, parentNode);
}
/**
* Construct a new instance of this DOMBuilder.
*/
public DOMBuilder(SAXTransformerFactory factory, Node parentNode) {
this.factory = factory == null? FACTORY: factory;
this.parentNode = parentNode;
setup();
}
/**
* Setup this instance transformer and result objects.
*/
private void setup() {
try {
TransformerHandler handler = this.factory.newTransformerHandler();
nextHandler = handler;
if (this.parentNode != null) {
this.result = new DOMResult(this.parentNode);
} else {
this.result = new DOMResult();
}
handler.setResult(this.result);
} catch (javax.xml.transform.TransformerException local) {
throw new XWorkException("Fatal-Error: Unable to get transformer handler", local);
}
}
/**
* Return the newly built Document.
*/
public Document getDocument() {
if (this.result == null || this.result.getNode() == null) {
return null;
} else if (this.result.getNode().getNodeType() == Node.DOCUMENT_NODE) {
return (Document) this.result.getNode();
} else {
return this.result.getNode().getOwnerDocument();
}
}
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
public void endDocument() throws SAXException {
nextHandler.endDocument();
}
public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
nextHandler.startElement(uri, loc, raw, attrs);
}
public void endElement(String arg0, String arg1, String arg2) throws SAXException {
nextHandler.endElement(arg0, arg1, arg2);
}
public void startPrefixMapping(String arg0, String arg1) throws SAXException {
nextHandler.startPrefixMapping(arg0, arg1);
}
public void endPrefixMapping(String arg0) throws SAXException {
nextHandler.endPrefixMapping(arg0);
}
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.characters(arg0, arg1, arg2);
}
public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.ignorableWhitespace(arg0, arg1, arg2);
}
public void processingInstruction(String arg0, String arg1) throws SAXException {
nextHandler.processingInstruction(arg0, arg1);
}
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
}
public static class StartHandler extends DefaultHandler {
private ContentHandler nextHandler;
private Map<String, String> dtdMappings;
/**
* Create a filter that is chained to another handler.
* @param next the next handler in the chain.
*/
public StartHandler(ContentHandler next, Map<String, String> dtdMappings) {
nextHandler = next;
this.dtdMappings = dtdMappings;
}
@Override
public void setDocumentLocator(Locator locator) {
nextHandler.setDocumentLocator(locator);
}
@Override
public void startDocument() throws SAXException {
nextHandler.startDocument();
}
@Override
public void endDocument() throws SAXException {
nextHandler.endDocument();
}
@Override
public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
nextHandler.startElement(uri, loc, raw, attrs);
}
@Override
public void endElement(String arg0, String arg1, String arg2) throws SAXException {
nextHandler.endElement(arg0, arg1, arg2);
}
@Override
public void startPrefixMapping(String arg0, String arg1) throws SAXException {
nextHandler.startPrefixMapping(arg0, arg1);
}
@Override
public void endPrefixMapping(String arg0) throws SAXException {
nextHandler.endPrefixMapping(arg0);
}
@Override
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.characters(arg0, arg1, arg2);
}
@Override
public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
nextHandler.ignorableWhitespace(arg0, arg1, arg2);
}
@Override
public void processingInstruction(String arg0, String arg1) throws SAXException {
nextHandler.processingInstruction(arg0, arg1);
}
@Override
public void skippedEntity(String arg0) throws SAXException {
nextHandler.skippedEntity(arg0);
}
@Override
public InputSource resolveEntity(String publicId, String systemId) {
if (dtdMappings != null && dtdMappings.containsKey(publicId)) {
String val = dtdMappings.get(publicId).toString();
return new InputSource(ClassLoaderUtil.getResourceAsStream(val, DomHelper.class));
}
return null;
}
@Override
public void warning(SAXParseException exception) {
}
@Override
public void error(SAXParseException exception) throws SAXException {
LOG.error(exception.getMessage() + " at (" + exception.getPublicId() + ":" +
exception.getLineNumber() + ":" + exception.getColumnNumber() + ")", exception);
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
LOG.fatal(exception.getMessage() + " at (" + exception.getPublicId() + ":" +
exception.getLineNumber() + ":" + exception.getColumnNumber() + ")", exception);
throw exception;
}
}
}
|