| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.ActionContext; |
| import com.opensymphony.xwork2.ObjectFactory; |
| import com.opensymphony.xwork2.util.location.Location; |
| import com.opensymphony.xwork2.util.location.LocationAttributes; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.struts2.StrutsException; |
| 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 = LogManager.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 |
| * |
| * @return the W3C Document |
| */ |
| 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 |
| * |
| * @return the W3C Document |
| */ |
| public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) { |
| |
| SAXParserFactory factory = null; |
| String parserProp = System.getProperty("xwork.saxParserFactory"); |
| if (parserProp != null) { |
| try { |
| ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class); |
| Class clazz = objectFactory.getClassInstance(parserProp); |
| factory = (SAXParserFactory) clazz.newInstance(); |
| } 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; |
| try { |
| parser = factory.newSAXParser(); |
| } catch (Exception ex) { |
| throw new StrutsException("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 StrutsException(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:cziegeler@apache.org">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 { |
| ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class); |
| Class clazz = objectFactory.getClassInstance(parserProp); |
| FACTORY = (SAXTransformerFactory) clazz.newInstance(); |
| } 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. |
| * @param factory the SAX transformer factory |
| */ |
| public DOMBuilder(SAXTransformerFactory factory) { |
| this(factory, null); |
| } |
| |
| /** |
| * Constructs a new instance that appends nodes to the given parent node. |
| * |
| * @param parentNode the parent node |
| */ |
| public DOMBuilder(Node parentNode) { |
| this(null, parentNode); |
| } |
| |
| /** |
| * Construct a new instance of this DOMBuilder. |
| * |
| * @param factory the SAX transformer factory |
| * @param parentNode the parent node |
| */ |
| 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 StrutsException("Fatal-Error: Unable to get transformer handler", local); |
| } |
| } |
| |
| /** |
| * Return the newly built Document. |
| * |
| * @return the W3C 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. |
| * @param dtdMappings map of DTD mappings |
| */ |
| 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 dtdFile = dtdMappings.get(publicId); |
| return new InputSource(ClassLoaderUtil.getResourceAsStream(dtdFile, DomHelper.class)); |
| } else { |
| LOG.warn("Local DTD is missing for publicID: {} - defined mappings: {}", publicId, dtdMappings); |
| } |
| return null; |
| } |
| |
| @Override |
| public void warning(SAXParseException exception) { |
| } |
| |
| @Override |
| public void error(SAXParseException exception) throws SAXException { |
| LOG.error("{} at ({}:{}:{})", exception.getMessage(), exception.getPublicId(), exception.getLineNumber(), exception.getColumnNumber(), exception); |
| throw exception; |
| } |
| |
| @Override |
| public void fatalError(SAXParseException exception) throws SAXException { |
| LOG.fatal("{} at ({}:{}:{})", exception.getMessage(), exception.getPublicId(), exception.getLineNumber(), exception.getColumnNumber(), exception); |
| throw exception; |
| } |
| } |
| |
| } |