blob: 440968ce6343be3412040f90b7d599085f740851 [file] [log] [blame]
/**
* 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 org.apache.cxf.staxutils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.Location;
import javax.xml.stream.StreamFilter;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.DTD;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.w3c.dom.UserDataHandler;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.PropertyUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.common.util.SystemPropertyAction;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.message.Message;
public final class StaxUtils {
// System properties for defaults, but also contextual properties usable
// for StaxInInterceptor
public static final String MAX_CHILD_ELEMENTS =
"org.apache.cxf.stax.maxChildElements";
public static final String MAX_ELEMENT_DEPTH =
"org.apache.cxf.stax.maxElementDepth";
public static final String MAX_ATTRIBUTE_COUNT =
"org.apache.cxf.stax.maxAttributeCount";
public static final String MAX_ATTRIBUTE_SIZE =
"org.apache.cxf.stax.maxAttributeSize";
public static final String MAX_TEXT_LENGTH =
"org.apache.cxf.stax.maxTextLength";
public static final String MIN_TEXT_SEGMENT =
"org.apache.cxf.stax.minTextSegment";
public static final String MAX_ELEMENT_COUNT =
"org.apache.cxf.stax.maxElementCount";
public static final String MAX_XML_CHARACTERS =
"org.apache.cxf.stax.maxXMLCharacters";
public static final String ALLOW_INSECURE_PARSER =
"org.apache.cxf.stax.allowInsecureParser";
private static final String INNER_ELEMENT_COUNT_SYSTEM_PROP =
"org.apache.cxf.staxutils.innerElementCountThreshold";
private static final String INNER_ELEMENT_LEVEL_SYSTEM_PROP =
"org.apache.cxf.staxutils.innerElementLevelThreshold";
private static final String AUTO_CLOSE_INPUT_SOURCE_PROP =
"org.apache.cxf.staxutils.autoCloseInputSource";
private static final Logger LOG = LogUtils.getL7dLogger(StaxUtils.class);
private static final Queue<XMLInputFactory> NS_AWARE_INPUT_FACTORY_POOL;
private static final XMLInputFactory SAFE_INPUT_FACTORY;
private static final Queue<XMLOutputFactory> OUTPUT_FACTORY_POOL;
private static final XMLOutputFactory SAFE_OUTPUT_FACTORY;
private static final String XML_NS = "http://www.w3.org/2000/xmlns/";
private static final String[] DEF_PREFIXES = new String[] {
"ns1".intern(), "ns2".intern(), "ns3".intern(),
"ns4".intern(), "ns5".intern(), "ns6".intern(),
"ns7".intern(), "ns8".intern(), "ns9".intern()
};
private static final int MAX_ATTR_COUNT_VAL =
getInteger(MAX_ATTRIBUTE_COUNT, 500);
private static final int MAX_ATTR_SIZE_VAL =
getInteger(MAX_ATTRIBUTE_SIZE, 64 * 1024); //64K per attribute, likely just "list" will hit
private static final int MAX_TEXT_LENGTH_VAL =
getInteger(MAX_TEXT_LENGTH, 128 * 1024 * 1024); //128M - more than this should DEFINITELY use MTOM
private static final int MIN_TEXT_SEGMENT_VAL =
getInteger(MIN_TEXT_SEGMENT, 64); // Same default as woodstox
private static final long MAX_ELEMENT_COUNT_VAL =
getLong(MAX_ELEMENT_COUNT, Long.MAX_VALUE);
private static final long MAX_XML_CHARS_VAL =
getLong(MAX_XML_CHARACTERS, Long.MAX_VALUE);
private static final int PARSER_POOL_SIZE_VAL =
getInteger("org.apache.cxf.staxutils.pool-size", 20);
private static final boolean ALLOW_INSECURE_PARSER_VAL;
private static final boolean AUTO_CLOSE_INPUT_SOURCE;
// Here we check old names first and then new names for the threshold properties
private static final int MAX_ELEMENT_DEPTH_VAL =
getInteger(MAX_ELEMENT_DEPTH, getInteger(INNER_ELEMENT_LEVEL_SYSTEM_PROP, 100));
private static final int MAX_CHILD_ELEMENTS_VAL =
getInteger(MAX_CHILD_ELEMENTS, getInteger(INNER_ELEMENT_COUNT_SYSTEM_PROP, 50000));
// Variables from Woodstox
private static final String P_MAX_ATTRIBUTES_PER_ELEMENT = "com.ctc.wstx.maxAttributesPerElement";
private static final String P_MAX_ATTRIBUTE_SIZE = "com.ctc.wstx.maxAttributeSize";
private static final String P_MAX_TEXT_LENGTH = "com.ctc.wstx.maxTextLength";
private static final String P_MAX_ELEMENT_COUNT = "com.ctc.wstx.maxElementCount";
private static final String P_MAX_CHARACTERS = "com.ctc.wstx.maxCharacters";
private static final String P_MAX_ELEMENT_DEPTH = "com.ctc.wstx.maxElementDepth";
private static final String P_MAX_CHILDREN_PER_ELEMENT = "com.ctc.wstx.maxChildrenPerElement";
private static final String P_MIN_TEXT_SEGMENT = "com.ctc.wstx.minTextSegment";
static {
NS_AWARE_INPUT_FACTORY_POOL = new ArrayBlockingQueue<>(PARSER_POOL_SIZE_VAL);
OUTPUT_FACTORY_POOL = new ArrayBlockingQueue<>(PARSER_POOL_SIZE_VAL);
String allowInsecureParser = SystemPropertyAction.getPropertyOrNull(ALLOW_INSECURE_PARSER);
if (!StringUtils.isEmpty(allowInsecureParser)) {
ALLOW_INSECURE_PARSER_VAL = "1".equals(allowInsecureParser) || Boolean.parseBoolean(allowInsecureParser);
} else {
ALLOW_INSECURE_PARSER_VAL = false;
}
String autoCloseInputSource = SystemPropertyAction.getPropertyOrNull(AUTO_CLOSE_INPUT_SOURCE_PROP);
if (!StringUtils.isEmpty(autoCloseInputSource)) {
AUTO_CLOSE_INPUT_SOURCE = "1".equals(autoCloseInputSource) || Boolean.parseBoolean(autoCloseInputSource);
} else {
AUTO_CLOSE_INPUT_SOURCE = false; /* set 'false' by default */
}
XMLInputFactory xif = null;
try {
xif = createXMLInputFactory(true);
String xifClassName = xif.getClass().getName();
if (!xifClassName.contains("ctc.wstx") && !xifClassName.contains("xml.xlxp")
&& !xifClassName.contains("xml.xlxp2") && !xifClassName.contains("bea.core")) {
xif = null;
}
} catch (Throwable t) {
//ignore, can always drop down to the pooled factories
}
SAFE_INPUT_FACTORY = xif;
XMLOutputFactory xof = null;
try {
xof = XMLOutputFactory.newInstance();
String xofClassName = xof.getClass().getName();
if (!xofClassName.contains("ctc.wstx") && !xofClassName.contains("xml.xlxp")
&& !xofClassName.contains("xml.xlxp2") && !xofClassName.contains("bea.core")) {
xof = null;
}
} catch (Throwable t) {
//ignore, can always drop down to the pooled factories
}
SAFE_OUTPUT_FACTORY = xof;
}
private StaxUtils() {
}
private static int getInteger(String prop, int def) {
try {
String s = SystemPropertyAction.getPropertyOrNull(prop);
if (StringUtils.isEmpty(s)) {
return def;
}
int i = Integer.parseInt(s);
if (i < 0) {
i = def;
}
return i;
} catch (Throwable t) {
//ignore
}
return def;
}
private static long getLong(String prop, long def) {
try {
String s = SystemPropertyAction.getPropertyOrNull(prop);
if (StringUtils.isEmpty(s)) {
return def;
}
long i = Long.parseLong(s);
if (i < 0) {
i = def;
}
return i;
} catch (Throwable t) {
//ignore
}
return def;
}
/**
* Return a cached, namespace-aware, factory.
*/
private static XMLInputFactory getXMLInputFactory() {
if (SAFE_INPUT_FACTORY != null) {
return SAFE_INPUT_FACTORY;
}
XMLInputFactory f = NS_AWARE_INPUT_FACTORY_POOL.poll();
if (f == null) {
f = createXMLInputFactory(true);
}
return f;
}
private static void returnXMLInputFactory(XMLInputFactory factory) {
if (SAFE_INPUT_FACTORY != factory) {
NS_AWARE_INPUT_FACTORY_POOL.offer(factory);
}
}
private static XMLOutputFactory getXMLOutputFactory() {
if (SAFE_OUTPUT_FACTORY != null) {
return SAFE_OUTPUT_FACTORY;
}
XMLOutputFactory f = OUTPUT_FACTORY_POOL.poll();
if (f == null) {
f = XMLOutputFactory.newInstance();
}
return f;
}
private static void returnXMLOutputFactory(XMLOutputFactory factory) {
if (SAFE_OUTPUT_FACTORY != factory) {
OUTPUT_FACTORY_POOL.offer(factory);
}
}
/**
* Return a new factory so that the caller can set sticky parameters.
* @param nsAware
* @throws XMLStreamException
*/
public static XMLInputFactory createXMLInputFactory(boolean nsAware) {
XMLInputFactory factory = null;
try {
factory = XMLInputFactory.newInstance();
} catch (Throwable t) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "XMLInputFactory.newInstance() failed with: ", t);
}
}
if (factory == null || !setRestrictionProperties(factory)) {
try {
factory = createWoodstoxFactory();
} catch (Throwable t) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Cannot create Woodstox XMLInputFactory: ", t);
}
}
if (factory == null) {
throw new RuntimeException("Failed to create XMLInputFactory.");
}
if (!setRestrictionProperties(factory)) {
if (ALLOW_INSECURE_PARSER_VAL) {
LOG.log(Level.WARNING, "INSECURE_PARSER_DETECTED", factory.getClass().getName());
} else {
throw new RuntimeException("Cannot create a secure XMLInputFactory, "
+ "you should either add woodstox or set " + ALLOW_INSECURE_PARSER
+ " system property to true if an unsafe mode is acceptable.");
}
}
}
setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, nsAware);
setProperty(factory, XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
setProperty(factory, XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
setProperty(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
factory.setXMLResolver(new XMLResolver() {
public Object resolveEntity(String publicID, String systemID,
String baseURI, String namespace)
throws XMLStreamException {
throw new XMLStreamException("Reading external entities is disabled");
}
});
return factory;
}
private static XMLInputFactory createWoodstoxFactory() {
return WoodstoxHelper.createInputFactory();
}
public static XMLEventFactory createWoodstoxEventFactory() {
return WoodstoxHelper.createEventFactory();
}
private static boolean setRestrictionProperties(XMLInputFactory factory) {
//For now, we can only support Woodstox 4.2.x and newer as none of the other
//stax parsers support these settings
final boolean wstxMaxs = setProperty(factory, P_MAX_ATTRIBUTES_PER_ELEMENT, MAX_ATTR_COUNT_VAL)
&& setProperty(factory, P_MAX_ATTRIBUTE_SIZE, MAX_ATTR_SIZE_VAL)
&& setProperty(factory, P_MAX_CHILDREN_PER_ELEMENT, MAX_CHILD_ELEMENTS_VAL)
&& setProperty(factory, P_MAX_ELEMENT_COUNT, MAX_ELEMENT_COUNT_VAL)
&& setProperty(factory, P_MAX_ELEMENT_DEPTH, MAX_ELEMENT_DEPTH_VAL)
&& setProperty(factory, P_MAX_CHARACTERS, MAX_XML_CHARS_VAL)
&& setProperty(factory, P_MAX_TEXT_LENGTH, MAX_TEXT_LENGTH_VAL);
return wstxMaxs
&& setProperty(factory, P_MIN_TEXT_SEGMENT, MIN_TEXT_SEGMENT_VAL);
}
private static boolean setProperty(XMLInputFactory f, String p, Object o) {
try {
f.setProperty(p, o);
return true;
} catch (Throwable t) {
//ignore
}
return false;
}
public static XMLStreamWriter createXMLStreamWriter(Writer out) {
XMLOutputFactory factory = getXMLOutputFactory();
try {
return factory.createXMLStreamWriter(out);
} catch (XMLStreamException e) {
throw new RuntimeException("Cant' create XMLStreamWriter", e);
} finally {
returnXMLOutputFactory(factory);
}
}
public static XMLStreamWriter createXMLStreamWriter(OutputStream out) {
return createXMLStreamWriter(out, null);
}
public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding) {
XMLOutputFactory factory = getXMLOutputFactory();
try {
return factory.createXMLStreamWriter(out, encoding != null ? encoding : StandardCharsets.UTF_8.name());
} catch (XMLStreamException e) {
throw new RuntimeException("Cant' create XMLStreamWriter", e);
} finally {
returnXMLOutputFactory(factory);
}
}
public static XMLStreamWriter createXMLStreamWriter(Result r) {
if (r instanceof DOMResult) {
//use our own DOM writer to avoid issues with Sun's
//version that doesn't support getNamespaceContext
DOMResult dr = (DOMResult)r;
Node nd = dr.getNode();
if (nd instanceof Document) {
return new W3CDOMStreamWriter((Document)nd);
} else if (nd instanceof Element) {
return new W3CDOMStreamWriter((Element)nd);
} else if (nd instanceof DocumentFragment) {
return new W3CDOMStreamWriter((DocumentFragment)nd);
}
}
XMLOutputFactory factory = getXMLOutputFactory();
try {
return factory.createXMLStreamWriter(r);
} catch (XMLStreamException e) {
throw new RuntimeException("Cant' create XMLStreamWriter", e);
} finally {
returnXMLOutputFactory(factory);
}
}
public static XMLStreamReader createFilteredReader(XMLStreamReader reader, StreamFilter filter) {
XMLInputFactory factory = getXMLInputFactory();
try {
return factory.createFilteredReader(reader, filter);
} catch (XMLStreamException e) {
throw new RuntimeException("Cant' create XMLStreamReader", e);
} finally {
returnXMLInputFactory(factory);
}
}
public static void nextEvent(XMLStreamReader dr) {
try {
dr.next();
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
}
public static boolean toNextText(DepthXMLStreamReader reader) {
if (reader.getEventType() == XMLStreamConstants.CHARACTERS) {
return true;
}
try {
int depth = reader.getDepth();
int event = reader.getEventType();
while (reader.getDepth() >= depth && reader.hasNext()) {
if (event == XMLStreamConstants.CHARACTERS && reader.getDepth() == depth + 1) {
return true;
}
event = reader.next();
}
return false;
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
}
public static boolean toNextTag(XMLStreamReader reader) {
try {
// advance to first tag.
int x = reader.getEventType();
while (x != XMLStreamConstants.START_ELEMENT
&& x != XMLStreamConstants.END_ELEMENT
&& reader.hasNext()) {
x = reader.next();
}
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
return true;
}
public static boolean toNextTag(DepthXMLStreamReader reader, QName endTag) {
try {
int depth = reader.getDepth();
int event = reader.getEventType();
while (reader.getDepth() >= depth && reader.hasNext()) {
if (event == XMLStreamConstants.START_ELEMENT && reader.getName().equals(endTag)
&& reader.getDepth() == depth + 1) {
return true;
}
event = reader.next();
}
return false;
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
}
public static void writeStartElement(XMLStreamWriter writer, String prefix, String name, String namespace)
throws XMLStreamException {
if (prefix == null) {
prefix = "";
}
if (!namespace.isEmpty()) {
writer.writeStartElement(prefix, name, namespace);
if (!prefix.isEmpty()) {
writer.writeNamespace(prefix, namespace);
writer.setPrefix(prefix, namespace);
} else {
writer.writeDefaultNamespace(namespace);
writer.setDefaultNamespace(namespace);
}
} else {
writer.writeStartElement(name);
writer.writeDefaultNamespace("");
writer.setDefaultNamespace("");
}
}
/**
* Returns true if currently at the start of an element, otherwise move
* forwards to the next element start and return true, otherwise false is
* returned if the end of the stream is reached.
*/
public static boolean skipToStartOfElement(XMLStreamReader in) throws XMLStreamException {
for (int code = in.getEventType(); code != XMLStreamConstants.END_DOCUMENT; code = in.next()) {
if (code == XMLStreamConstants.START_ELEMENT) {
return true;
}
}
return false;
}
public static boolean toNextElement(DepthXMLStreamReader dr) {
if (dr.getEventType() == XMLStreamConstants.START_ELEMENT) {
return true;
}
if (dr.getEventType() == XMLStreamConstants.END_ELEMENT) {
return false;
}
try {
int depth = dr.getDepth();
for (int event = dr.getEventType(); dr.getDepth() >= depth && dr.hasNext(); event = dr.next()) {
if (event == XMLStreamConstants.START_ELEMENT && dr.getDepth() == depth + 1) {
return true;
} else if (event == XMLStreamConstants.END_ELEMENT) {
depth--;
}
}
return false;
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
}
public static boolean skipToStartOfElement(DepthXMLStreamReader in) throws XMLStreamException {
for (int code = in.getEventType(); code != XMLStreamConstants.END_DOCUMENT; code = in.next()) {
if (code == XMLStreamConstants.START_ELEMENT) {
return true;
}
}
return false;
}
public static void copy(Source source, OutputStream os) throws XMLStreamException {
XMLStreamWriter writer = createXMLStreamWriter(os);
try {
copy(source, writer);
} finally {
try {
writer.flush();
} catch (XMLStreamException ex) {
//ignore
}
StaxUtils.close(writer);
}
}
public static void copy(Source source, XMLStreamWriter writer) throws XMLStreamException {
if (source instanceof StaxSource) {
StaxSource ss = (StaxSource)source;
if (ss.getXMLStreamReader() == null) {
return;
}
} else if (source instanceof StAXSource) {
StAXSource ss = (StAXSource)source;
if (ss.getXMLStreamReader() == null) {
return;
}
} else if (source instanceof SAXSource) {
SAXSource ss = (SAXSource)source;
InputSource src = ss.getInputSource();
if (src == null || (src.getSystemId() == null && src.getPublicId() == null)) {
if (ss.getXMLReader() != null) {
//OK - reader is OK. We'll use that out
StreamWriterContentHandler ch = new StreamWriterContentHandler(writer);
XMLReader reader = ((SAXSource)source).getXMLReader();
reader.setContentHandler(ch);
try {
try {
reader.setFeature("http://xml.org/sax/features/namespaces", true);
} catch (Throwable t) {
//ignore
}
try {
reader.setProperty("http://xml.org/sax/properties/lexical-handler", ch);
} catch (Throwable t) {
//ignore
}
reader.parse(((SAXSource)source).getInputSource());
return;
} catch (Exception e) {
throw new XMLStreamException(e.getMessage(), e);
}
} else if (ss.getInputSource() == null) {
//nothing to copy, just return
return;
}
}
} else if (source instanceof StreamSource) {
StreamSource ss = (StreamSource)source;
if (ss.getInputStream() == null
&& ss.getReader() == null
&& ss.getSystemId() == null) {
//nothing to copy, just return
return;
}
}
XMLStreamReader reader = createXMLStreamReader(source);
copy(reader, writer);
reader.close();
}
public static Document copy(Document doc)
throws XMLStreamException, ParserConfigurationException {
XMLStreamReader reader = createXMLStreamReader(doc);
W3CDOMStreamWriter writer = new W3CDOMStreamWriter();
copy(reader, writer);
Document d = writer.getDocument();
try {
d.setDocumentURI(doc.getDocumentURI());
} catch (Exception ex) {
//ignore - probably not DOM level 3
}
return d;
}
public static void copy(Document doc, XMLStreamWriter writer) throws XMLStreamException {
XMLStreamReader reader = createXMLStreamReader(doc);
copy(reader, writer);
}
public static void copy(Element node, XMLStreamWriter writer) throws XMLStreamException {
XMLStreamReader reader = createXMLStreamReader(node);
copy(reader, writer);
}
public static void copy(XMLStreamReader reader, OutputStream os)
throws XMLStreamException {
XMLStreamWriter xsw = StaxUtils.createXMLStreamWriter(os);
StaxUtils.copy(reader, xsw);
xsw.close();
}
public static void writeTo(Node node, OutputStream os) throws XMLStreamException {
copy(new DOMSource(node), os);
}
public static void writeTo(Node node, OutputStream os, int indent) throws XMLStreamException {
if (indent > 0) {
XMLStreamWriter writer = new PrettyPrintXMLStreamWriter(createXMLStreamWriter(os), indent);
try {
copy(new DOMSource(node), writer);
} finally {
writer.close();
}
} else {
copy(new DOMSource(node), os);
}
}
public static void writeTo(Node node, Writer os) throws XMLStreamException {
writeTo(node, os, 0);
}
public static void writeTo(Node node, Writer os, int indent) throws XMLStreamException {
XMLStreamWriter writer = createXMLStreamWriter(os);
if (indent > 0) {
writer = new PrettyPrintXMLStreamWriter(writer, indent);
}
try {
copy(new DOMSource(node), writer);
} finally {
writer.close();
}
}
/**
* Copies the reader to the writer. The start and end document methods must
* be handled on the writer manually.
*
* @param reader
* @param writer
* @throws XMLStreamException
*/
public static void copy(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
copy(reader, writer, false, false);
}
public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean fragment)
throws XMLStreamException {
copy(reader, writer, fragment, false);
}
public static void copy(XMLStreamReader reader,
XMLStreamWriter writer,
boolean fragment,
boolean isThreshold) throws XMLStreamException {
// number of elements read in
int read = 0;
int elementCount = 0;
final Deque<Integer> countStack = new ArrayDeque<>();
int event = reader.getEventType();
while (reader.hasNext()) {
switch (event) {
case XMLStreamConstants.START_ELEMENT:
read++;
if (isThreshold) {
elementCount++;
if (MAX_ELEMENT_DEPTH_VAL != -1 && read >= MAX_ELEMENT_DEPTH_VAL) {
throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+ MAX_ELEMENT_DEPTH_VAL);
}
if (MAX_CHILD_ELEMENTS_VAL != -1 && elementCount >= MAX_CHILD_ELEMENTS_VAL) {
throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+ MAX_CHILD_ELEMENTS_VAL);
}
countStack.push(elementCount);
elementCount = 0;
}
writeStartElement(reader, writer);
break;
case XMLStreamConstants.END_ELEMENT:
if (read > 0) {
writer.writeEndElement();
}
read--;
if (read < 0 && fragment) {
return;
}
if (isThreshold && !countStack.isEmpty()) {
elementCount = countStack.pop();
}
break;
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.SPACE:
String s = reader.getText();
if (s != null) {
writer.writeCharacters(s);
}
break;
case XMLStreamConstants.COMMENT:
writer.writeComment(reader.getText());
break;
case XMLStreamConstants.CDATA:
writer.writeCData(reader.getText());
break;
case XMLStreamConstants.START_DOCUMENT:
case XMLStreamConstants.END_DOCUMENT:
case XMLStreamConstants.ATTRIBUTE:
case XMLStreamConstants.NAMESPACE:
break;
default:
break;
}
event = reader.next();
}
}
private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer)
throws XMLStreamException {
String uri = reader.getNamespaceURI();
String prefix = reader.getPrefix();
String local = reader.getLocalName();
if (prefix == null) {
prefix = "";
}
boolean writeElementNS = false;
if (uri != null) {
writeElementNS = true;
Iterator<String> it = CastUtils.cast(writer.getNamespaceContext().getPrefixes(uri));
if (!it.hasNext() && StringUtils.isEmpty(prefix) && StringUtils.isEmpty(uri)
&& StringUtils.isEmpty(writer.getNamespaceContext().getNamespaceURI(""))) {
writeElementNS = false;
}
while (it.hasNext()) {
String s = it.next();
if (s == null) {
s = "";
}
if (s.equals(prefix)) {
writeElementNS = false;
}
}
}
// Write out the element name
if (uri != null) {
if (prefix.isEmpty() && StringUtils.isEmpty(uri)) {
writer.writeStartElement(local);
} else {
writer.writeStartElement(prefix, local, uri);
}
} else {
writer.writeStartElement(local);
}
// Write out the namespaces
for (int i = 0; i < reader.getNamespaceCount(); i++) {
String nsURI = reader.getNamespaceURI(i);
String nsPrefix = reader.getNamespacePrefix(i);
if (nsPrefix == null) {
nsPrefix = "";
}
if (nsURI == null) {
nsURI = "";
}
if (nsPrefix.isEmpty()) {
writer.writeDefaultNamespace(nsURI);
writer.setDefaultNamespace(nsURI);
} else {
writer.writeNamespace(nsPrefix, nsURI);
writer.setPrefix(nsPrefix, nsURI);
}
if (nsURI.equals(uri) && nsPrefix.equals(prefix)) {
writeElementNS = false;
}
}
// Check if the namespace still needs to be written.
// We need this check because namespace writing works
// different on Woodstox and the RI.
if (writeElementNS) {
if (prefix.isEmpty()) {
writer.writeDefaultNamespace(uri);
writer.setDefaultNamespace(uri);
} else {
writer.writeNamespace(prefix, uri);
writer.setPrefix(prefix, uri);
}
}
// Write out attributes
for (int i = 0; i < reader.getAttributeCount(); i++) {
String ns = reader.getAttributeNamespace(i);
String nsPrefix = reader.getAttributePrefix(i);
if (ns == null || ns.isEmpty()) {
writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
} else if (nsPrefix == null || nsPrefix.isEmpty()) {
writer.writeAttribute(reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
} else {
Iterator<String> it = CastUtils.cast(writer.getNamespaceContext().getPrefixes(ns));
boolean writeNs = true;
while (it != null && it.hasNext()) {
String s = it.next();
if (s == null) {
s = "";
}
if (s.equals(nsPrefix)) {
writeNs = false;
}
}
if (writeNs) {
writer.writeNamespace(nsPrefix, ns);
writer.setPrefix(nsPrefix, ns);
}
writer.writeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i), reader
.getAttributeLocalName(i), reader.getAttributeValue(i));
}
}
}
public static void writeDocument(Document d, XMLStreamWriter writer, boolean repairing)
throws XMLStreamException {
writeDocument(d, writer, true, repairing);
}
public static void writeDocument(Document d, XMLStreamWriter writer, boolean writeProlog,
boolean repairing) throws XMLStreamException {
if (writeProlog) {
writer.writeStartDocument();
}
Node node = d.getFirstChild();
while (node != null) {
if (writeProlog || node.getNodeType() == Node.ELEMENT_NODE) {
writeNode(node, writer, repairing);
}
node = node.getNextSibling();
}
if (writeProlog) {
writer.writeEndDocument();
}
}
/**
* Writes an Element to an XMLStreamWriter. The writer must already have
* started the document (via writeStartDocument()). Also, this probably
* won't work with just a fragment of a document. The Element should be the
* root element of the document.
*
* @param e
* @param writer
* @throws XMLStreamException
*/
public static void writeElement(Element e, XMLStreamWriter writer, boolean repairing)
throws XMLStreamException {
writeElement(e, writer, repairing, true);
}
/**
* Writes an Element to an XMLStreamWriter. The writer must already have
* started the document (via writeStartDocument()). Also, this probably
* won't work with just a fragment of a document. The Element should be the
* root element of the document.
*
* @param e
* @param writer
* @param endElement true if the element should be ended
* @throws XMLStreamException
*/
public static void writeElement(Element e,
XMLStreamWriter writer,
boolean repairing,
boolean endElement)
throws XMLStreamException {
String prefix = e.getPrefix();
String ns = e.getNamespaceURI();
String localName = e.getLocalName();
if (prefix == null) {
prefix = "";
}
if (localName == null) {
localName = e.getNodeName();
if (localName == null) {
throw new IllegalStateException("Element's local name cannot be null!");
}
}
String decUri = writer.getNamespaceContext().getNamespaceURI(prefix);
boolean declareNamespace = decUri == null || !decUri.equals(ns);
if (ns == null || ns.isEmpty()) {
writer.writeStartElement(localName);
if (StringUtils.isEmpty(decUri)) {
declareNamespace = false;
}
} else {
writer.writeStartElement(prefix, localName, ns);
}
for (Node attr : sortElementAttributes(e.getAttributes())) {
String name = attr.getLocalName();
String attrPrefix = attr.getPrefix();
if (attrPrefix == null) {
attrPrefix = "";
}
if (name == null) {
name = attr.getNodeName();
}
if ("xmlns".equals(attrPrefix)) {
writer.writeNamespace(name, attr.getNodeValue());
writer.setPrefix(name, attr.getNodeValue());
if (name.equals(prefix) && attr.getNodeValue().equals(ns)) {
declareNamespace = false;
}
} else {
if ("xmlns".equals(name) && "".equals(attrPrefix)) {
writer.writeDefaultNamespace(attr.getNodeValue());
writer.setDefaultNamespace(attr.getNodeValue());
if (attr.getNodeValue().equals(ns)) {
declareNamespace = false;
} else if (StringUtils.isEmpty(attr.getNodeValue())
&& StringUtils.isEmpty(ns)) {
declareNamespace = false;
}
} else {
String attns = attr.getNamespaceURI();
String value = attr.getNodeValue();
if (attns == null || attns.isEmpty()) {
writer.writeAttribute(name, value);
} else if (attrPrefix.isEmpty()) {
writer.writeAttribute(attns, name, value);
} else {
if (repairing && writer.getNamespaceContext().getNamespaceURI(attrPrefix) == null) {
writer.writeNamespace(attrPrefix, attns);
}
writer.writeAttribute(attrPrefix, attns, name, value);
}
}
}
}
if (declareNamespace && repairing) {
if (ns == null) {
writer.writeNamespace(prefix, "");
writer.setPrefix(prefix, "");
} else {
writer.writeNamespace(prefix, ns);
writer.setPrefix(prefix, ns);
}
}
Node nd = e.getFirstChild();
while (nd != null) {
writeNode(nd, writer, repairing);
nd = nd.getNextSibling();
}
if (endElement) {
writer.writeEndElement();
}
}
private static List<Node> sortElementAttributes(NamedNodeMap attrs) {
if (attrs.getLength() == 0) {
return Collections.<Node> emptyList();
}
List<Node> sortedAttrs = new ArrayList<>(attrs.getLength());
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
String name = attr.getLocalName();
if (name == null) {
name = attr.getNodeName();
}
if ("xmlns".equals(attr.getPrefix()) || "xmlns".equals(name)) {
sortedAttrs.add(0, attr);
} else {
sortedAttrs.add(attr);
}
}
return sortedAttrs;
}
public static void writeNode(Node n, XMLStreamWriter writer, boolean repairing)
throws XMLStreamException {
switch (n.getNodeType()) {
case Node.ELEMENT_NODE:
writeElement((Element)n, writer, repairing);
break;
case Node.TEXT_NODE:
writer.writeCharacters(((Text)n).getNodeValue());
break;
case Node.COMMENT_NODE:
writer.writeComment(((Comment)n).getData());
break;
case Node.CDATA_SECTION_NODE:
writer.writeCData(((CDATASection)n).getData());
break;
case Node.ENTITY_REFERENCE_NODE:
writer.writeEntityRef(((EntityReference)n).getNodeValue());
break;
case Node.PROCESSING_INSTRUCTION_NODE:
ProcessingInstruction pi = (ProcessingInstruction)n;
writer.writeProcessingInstruction(pi.getTarget(), pi.getData());
break;
case Node.DOCUMENT_NODE:
writeDocument((Document)n, writer, repairing);
break;
case Node.DOCUMENT_FRAGMENT_NODE: {
DocumentFragment frag = (DocumentFragment)n;
Node child = frag.getFirstChild();
while (child != null) {
writeNode(child, writer, repairing);
child = child.getNextSibling();
}
break;
}
case Node.DOCUMENT_TYPE_NODE:
try {
if (((DocumentType)n).getTextContent() != null) {
writer.writeDTD(((DocumentType)n).getTextContent());
}
} catch (UnsupportedOperationException ex) {
//can we ignore? DOM writers really don't allow this
//as there isn't a way to write a DTD in dom
}
break;
default:
throw new IllegalStateException("Found type: " + n.getClass().getName());
}
}
public static Document read(Source s) throws XMLStreamException {
XMLStreamReader reader = createXMLStreamReader(s);
try {
return read(reader);
} finally {
try {
reader.close();
} catch (Exception ex) {
//ignore
}
}
}
public static Document read(InputStream s) throws XMLStreamException {
XMLStreamReader reader = createXMLStreamReader(s);
try {
return read(reader);
} finally {
try {
reader.close();
} catch (Exception ex) {
//ignore
}
}
}
public static Document read(Reader s) throws XMLStreamException {
XMLStreamReader reader = createXMLStreamReader(s);
try {
return read(reader);
} finally {
try {
reader.close();
} catch (Exception ex) {
//ignore
}
}
}
public static Document read(File is) throws XMLStreamException, IOException {
try (InputStream fin = Files.newInputStream(is.toPath())) {
return read(fin);
}
}
public static Document read(InputSource s) throws XMLStreamException {
XMLStreamReader reader = null;
try {
reader = createXMLStreamReader(s);
return read(reader);
} finally {
StaxUtils.close(reader);
}
}
public static Document read(XMLStreamReader reader) throws XMLStreamException {
return read(reader, false);
}
public static Document read(XMLStreamReader reader, boolean recordLoc) throws XMLStreamException {
Document doc = DOMUtils.createDocument();
if (reader.getLocation().getSystemId() != null) {
try {
doc.setDocumentURI(reader.getLocation().getSystemId());
} catch (Exception e) {
//ignore - probably not DOM level 3
}
}
readDocElements(doc, doc, reader, true, recordLoc);
return doc;
}
public static Document read(DocumentBuilder builder, XMLStreamReader reader, boolean repairing)
throws XMLStreamException {
Document doc = builder == null ? DOMUtils.createDocument() : builder.newDocument();
if (reader.getLocation().getSystemId() != null) {
try {
doc.setDocumentURI(reader.getLocation().getSystemId());
} catch (Exception e) {
//ignore - probably not DOM level 3
}
}
readDocElements(doc, reader, repairing);
return doc;
}
/**
* @param parent
*/
private static Document getDocument(Node parent) {
return (parent instanceof Document) ? (Document)parent : parent.getOwnerDocument();
}
private static boolean isDeclared(Element e, String namespaceURI, String prefix) {
while (e != null) {
Attr att;
if (prefix != null && !prefix.isEmpty()) {
att = e.getAttributeNodeNS(XML_NS, prefix);
} else {
att = e.getAttributeNode("xmlns");
}
if (att != null && att.getNodeValue().equals(namespaceURI)) {
return true;
}
if (e.getParentNode() instanceof Element) {
e = (Element)e.getParentNode();
} else if (StringUtils.isEmpty(prefix) && StringUtils.isEmpty(namespaceURI)) {
//A document that probably doesn't have any namespace qualifies elements
return true;
} else {
break;
}
}
return false;
}
public static void readDocElements(Node parent, XMLStreamReader reader, boolean repairing)
throws XMLStreamException {
Document doc = getDocument(parent);
readDocElements(doc, parent, reader, repairing, false);
}
public static void readDocElements(Node parent, XMLStreamReader reader, boolean repairing,
boolean isThreshold)
throws XMLStreamException {
Document doc = getDocument(parent);
readDocElements(doc, parent, reader, repairing, false, isThreshold);
}
/**
* @param parent
* @param reader
* @throws XMLStreamException
*/
public static void readDocElements(Document doc, Node parent,
XMLStreamReader reader, boolean repairing, boolean recordLoc)
throws XMLStreamException {
readDocElements(doc, parent, reader, repairing, recordLoc, false);
}
/**
* @param parent
* @param reader
* @throws XMLStreamException
*/
public static void readDocElements(Document doc, Node parent,
XMLStreamReader reader, boolean repairing, boolean recordLoc,
boolean isThreshold)
throws XMLStreamException {
final Deque<Node> stack = new ArrayDeque<>();
int event = reader.getEventType();
int elementCount = 0;
while (reader.hasNext()) {
switch (event) {
case XMLStreamConstants.START_ELEMENT: {
elementCount++;
Element e;
if (!StringUtils.isEmpty(reader.getPrefix())) {
e = doc.createElementNS(reader.getNamespaceURI(),
reader.getPrefix() + ':' + reader.getLocalName());
} else {
e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
}
e = (Element)parent.appendChild(e);
recordLoc = addLocation(doc, e, reader, recordLoc);
for (int ns = 0; ns < reader.getNamespaceCount(); ns++) {
String uri = reader.getNamespaceURI(ns);
String prefix = reader.getNamespacePrefix(ns);
declare(e, uri, prefix);
}
for (int att = 0; att < reader.getAttributeCount(); att++) {
String name = reader.getAttributeLocalName(att);
String prefix = reader.getAttributePrefix(att);
if (prefix != null && !prefix.isEmpty()) {
name = prefix + ':' + name;
}
Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(att), name);
attr.setValue(reader.getAttributeValue(att));
e.setAttributeNode(attr);
}
if (repairing && !isDeclared(e, reader.getNamespaceURI(), reader.getPrefix())) {
declare(e, reader.getNamespaceURI(), reader.getPrefix());
}
stack.push(parent);
if (isThreshold && MAX_ELEMENT_DEPTH_VAL != -1
&& stack.size() >= MAX_ELEMENT_DEPTH_VAL) {
throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+ MAX_ELEMENT_DEPTH_VAL);
}
if (isThreshold && MAX_CHILD_ELEMENTS_VAL != -1
&& elementCount >= MAX_CHILD_ELEMENTS_VAL) {
throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+ MAX_CHILD_ELEMENTS_VAL);
}
parent = e;
break;
}
case XMLStreamConstants.END_ELEMENT:
if (stack.isEmpty()) {
return;
}
parent = stack.pop();
if (parent instanceof Document || parent instanceof DocumentFragment) {
return;
}
break;
case XMLStreamConstants.NAMESPACE:
break;
case XMLStreamConstants.ATTRIBUTE:
break;
case XMLStreamConstants.CHARACTERS:
if (parent != null) {
recordLoc = addLocation(doc,
parent.appendChild(doc.createTextNode(reader.getText())),
reader, recordLoc);
}
break;
case XMLStreamConstants.COMMENT:
if (parent != null) {
parent.appendChild(doc.createComment(reader.getText()));
}
break;
case XMLStreamConstants.CDATA:
recordLoc = addLocation(doc,
parent.appendChild(doc.createCDATASection(reader.getText())),
reader, recordLoc);
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
break;
case XMLStreamConstants.ENTITY_REFERENCE:
parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
break;
default:
break;
}
if (reader.hasNext()) {
event = reader.next();
}
}
}
public static class StreamToDOMContext {
private final Deque<Node> stack = new ArrayDeque<>();
private int elementCount;
private boolean repairing;
private boolean recordLoc;
private boolean threshold;
public StreamToDOMContext(boolean repairing, boolean recordLoc, boolean threshold) {
this.repairing = repairing;
this.recordLoc = recordLoc;
this.threshold = threshold;
}
public void setRecordLoc(boolean recordLoc) {
this.recordLoc = recordLoc;
}
public boolean isRecordLoc() {
return this.recordLoc;
}
public boolean isRepairing() {
return this.repairing;
}
public boolean isThreshold() {
return this.threshold;
}
public int incrementCount() {
return ++elementCount;
}
public int decreaseCount() {
return --elementCount;
}
public int getCount() {
return elementCount;
}
public void pushToStack(Node node) {
stack.push(node);
}
public Node popFromStack() {
return stack.pop();
}
public int getStackSize() {
return stack.size();
}
public boolean isStackEmpty() {
return stack.isEmpty();
}
}
public static void readDocElements(Document doc, Node parent, XMLStreamReader reader, StreamToDOMContext context)
throws XMLStreamException {
int event = reader.getEventType();
while (reader.hasNext()) {
switch (event) {
case XMLStreamConstants.START_ELEMENT: {
context.incrementCount();
Element e;
if (!StringUtils.isEmpty(reader.getPrefix())) {
e = doc.createElementNS(reader.getNamespaceURI(),
reader.getPrefix() + ":" + reader.getLocalName());
} else {
e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
}
e = (Element)parent.appendChild(e);
if (context.isRecordLoc()) {
context.setRecordLoc(addLocation(doc, e, reader.getLocation(), context.isRecordLoc()));
}
for (int ns = 0; ns < reader.getNamespaceCount(); ns++) {
String uri = reader.getNamespaceURI(ns);
String prefix = reader.getNamespacePrefix(ns);
declare(e, uri, prefix);
}
for (int att = 0; att < reader.getAttributeCount(); att++) {
String name = reader.getAttributeLocalName(att);
String prefix = reader.getAttributePrefix(att);
if (prefix != null && prefix.length() > 0) {
name = prefix + ":" + name;
}
Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(att), name);
attr.setValue(reader.getAttributeValue(att));
e.setAttributeNode(attr);
}
if (context.isRepairing() && !isDeclared(e, reader.getNamespaceURI(), reader.getPrefix())) {
declare(e, reader.getNamespaceURI(), reader.getPrefix());
}
context.pushToStack(parent);
if (context.isThreshold() && MAX_ELEMENT_DEPTH_VAL != -1
&& context.getStackSize() >= MAX_ELEMENT_DEPTH_VAL) {
throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+ MAX_ELEMENT_DEPTH_VAL);
}
if (context.isThreshold() && MAX_CHILD_ELEMENTS_VAL != -1
&& context.getCount() >= MAX_CHILD_ELEMENTS_VAL) {
throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+ MAX_CHILD_ELEMENTS_VAL);
}
parent = e;
break;
}
case XMLStreamConstants.END_ELEMENT:
if (context.isStackEmpty()) {
return;
}
parent = context.popFromStack();
if (parent instanceof Document || parent instanceof DocumentFragment) {
return;
}
break;
case XMLStreamConstants.NAMESPACE:
break;
case XMLStreamConstants.ATTRIBUTE:
break;
case XMLStreamConstants.CHARACTERS:
if (parent != null) {
context.setRecordLoc(addLocation(doc,
parent.appendChild(doc.createTextNode(reader.getText())),
reader.getLocation(), context.isRecordLoc()));
}
break;
case XMLStreamConstants.COMMENT:
if (parent != null) {
parent.appendChild(doc.createComment(reader.getText()));
}
break;
case XMLStreamConstants.CDATA:
context.setRecordLoc(addLocation(doc,
parent.appendChild(doc.createCDATASection(reader.getText())),
reader.getLocation(), context.isRecordLoc()));
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
break;
case XMLStreamConstants.ENTITY_REFERENCE:
parent.appendChild(doc.createProcessingInstruction(reader.getPITarget(), reader.getPIData()));
break;
default:
break;
}
if (reader.hasNext()) {
event = reader.next();
}
}
}
public static Node readDocElement(Document doc, Node parent, XMLEvent ev, StreamToDOMContext context)
throws XMLStreamException {
switch (ev.getEventType()) {
case XMLStreamConstants.START_ELEMENT: {
context.incrementCount();
Element e;
StartElement startElem = ev.asStartElement();
QName name = startElem.getName();
if (!StringUtils.isEmpty(name.getPrefix())) {
e = doc.createElementNS(name.getNamespaceURI(),
name.getPrefix() + ":" + name.getLocalPart());
} else {
e = doc.createElementNS(name.getNamespaceURI(), name.getLocalPart());
}
e = (Element)parent.appendChild(e);
if (context.isRecordLoc()) {
context.setRecordLoc(addLocation(doc, e, startElem.getLocation(), context.isRecordLoc()));
}
if (context.isRepairing() && !isDeclared(e, name.getNamespaceURI(), name.getPrefix())) {
declare(e, name.getNamespaceURI(), name.getPrefix());
}
context.pushToStack(parent);
if (context.isThreshold() && MAX_ELEMENT_DEPTH_VAL != -1
&& context.getStackSize() >= MAX_ELEMENT_DEPTH_VAL) {
throw new DepthExceededStaxException("reach the innerElementLevelThreshold:"
+ MAX_ELEMENT_DEPTH_VAL);
}
if (context.isThreshold() && MAX_CHILD_ELEMENTS_VAL != -1
&& context.getCount() >= MAX_CHILD_ELEMENTS_VAL) {
throw new DepthExceededStaxException("reach the innerElementCountThreshold:"
+ MAX_CHILD_ELEMENTS_VAL);
}
parent = e;
break;
}
case XMLStreamConstants.END_ELEMENT:
if (context.isStackEmpty()) {
return parent;
}
parent = context.popFromStack();
if (parent instanceof Document || parent instanceof DocumentFragment) {
return parent;
}
break;
case XMLStreamConstants.NAMESPACE:
Namespace ns = (Namespace)ev;
declare((Element)parent, ns.getNamespaceURI(), ns.getPrefix());
break;
case XMLStreamConstants.ATTRIBUTE:
Attribute at = (Attribute)ev;
QName qname = at.getName();
String attName = qname.getLocalPart();
String attPrefix = qname.getPrefix();
if (attPrefix != null && attPrefix.length() > 0) {
attName = attPrefix + ":" + attName;
}
Attr attr = doc.createAttributeNS(qname.getNamespaceURI(), attName);
attr.setValue(at.getValue());
((Element)parent).setAttributeNode(attr);
break;
case XMLStreamConstants.CHARACTERS:
Characters characters = ev.asCharacters();
context.setRecordLoc(addLocation(doc,
parent.appendChild(doc.createTextNode(characters.getData())),
characters.getLocation(), context.isRecordLoc()));
break;
case XMLStreamConstants.COMMENT:
parent.appendChild(doc.createComment(((javax.xml.stream.events.Comment)ev).getText()));
break;
case XMLStreamConstants.CDATA:
Characters cdata = ev.asCharacters();
context.setRecordLoc(addLocation(doc,
parent.appendChild(doc.createCDATASection(cdata.getData())),
cdata.getLocation(), context.isRecordLoc()));
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
parent.appendChild(doc.createProcessingInstruction(((ProcessingInstruction)ev).getTarget(),
((ProcessingInstruction)ev).getData()));
break;
case XMLStreamConstants.ENTITY_REFERENCE:
javax.xml.stream.events.EntityReference er = (javax.xml.stream.events.EntityReference)ev;
parent.appendChild(doc.createEntityReference(er.getName()));
break;
default:
break;
}
return parent;
}
private static boolean addLocation(Document doc, Node node,
Location loc,
boolean recordLoc) {
if (recordLoc && loc != null && (loc.getColumnNumber() != 0 || loc.getLineNumber() != 0)) {
try {
final int charOffset = loc.getCharacterOffset();
final int colNum = loc.getColumnNumber();
final int linNum = loc.getLineNumber();
final String pubId = loc.getPublicId() == null ? doc.getDocumentURI() : loc.getPublicId();
final String sysId = loc.getSystemId() == null ? doc.getDocumentURI() : loc.getSystemId();
Location loc2 = new Location() {
public int getCharacterOffset() {
return charOffset;
}
public int getColumnNumber() {
return colNum;
}
public int getLineNumber() {
return linNum;
}
public String getPublicId() {
return pubId;
}
public String getSystemId() {
return sysId;
}
};
node.setUserData("location", loc2, LocationUserDataHandler.INSTANCE);
} catch (Throwable ex) {
//possibly not DOM level 3, won't be able to record this then
return false;
}
}
return recordLoc;
}
private static boolean addLocation(Document doc, Node node,
XMLStreamReader reader,
boolean recordLoc) {
return addLocation(doc, node, reader.getLocation(), recordLoc);
}
private static class LocationUserDataHandler implements UserDataHandler {
public static final LocationUserDataHandler INSTANCE = new LocationUserDataHandler();
public void handle(short operation, String key, Object data, Node src, Node dst) {
if (operation == NODE_CLONED) {
dst.setUserData(key, data, this);
}
}
}
private static void declare(Element node, String uri, String prefix) {
String qualname;
if (prefix != null && prefix.length() > 0) {
qualname = "xmlns:" + prefix;
} else {
qualname = "xmlns";
}
Attr attr = node.getOwnerDocument().createAttributeNS(XML_NS, qualname);
attr.setValue(uri);
node.setAttributeNodeNS(attr);
}
public static XMLStreamReader createXMLStreamReader(InputSource src) {
String sysId = src.getSystemId() == null ? null : src.getSystemId();
String pubId = src.getPublicId() == null ? null : src.getPublicId();
if (src.getByteStream() != null) {
final InputStream is = src.getByteStream();
if (src.getEncoding() == null) {
final StreamSource ss = new StreamSource(is, sysId);
ss.setPublicId(pubId);
final XMLStreamReader xmlStreamReader = createXMLStreamReader(ss);
if (AUTO_CLOSE_INPUT_SOURCE) {
return new AutoCloseableXMLStreamReader(xmlStreamReader, is);
} else {
return xmlStreamReader;
}
}
return new AutoCloseableXMLStreamReader(createXMLStreamReader(is, src.getEncoding()), is);
} else if (src.getCharacterStream() != null) {
final Reader reader = src.getCharacterStream();
final StreamSource ss = new StreamSource(reader, sysId);
ss.setPublicId(pubId);
final XMLStreamReader xmlStreamReader = createXMLStreamReader(ss);
if (AUTO_CLOSE_INPUT_SOURCE) {
return new AutoCloseableXMLStreamReader(xmlStreamReader, reader);
} else {
return xmlStreamReader;
}
} else {
try {
final URL url = new URL(sysId);
final InputStream is = url.openStream();
final StreamSource ss = new StreamSource(is, sysId);
ss.setPublicId(pubId);
return new AutoCloseableXMLStreamReader(createXMLStreamReader(ss), is);
} catch (Exception ex) {
//ignore - not a valid URL
}
}
throw new IllegalArgumentException("InputSource must have a ByteStream or CharacterStream");
}
/**
* @param in
* @param encoding
*/
public static XMLStreamReader createXMLStreamReader(InputStream in, String encoding) {
XMLInputFactory factory = getXMLInputFactory();
try {
return factory.createXMLStreamReader(in, encoding != null ? encoding : StandardCharsets.UTF_8.name());
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
} finally {
returnXMLInputFactory(factory);
}
}
/**
* @param in
*/
public static XMLStreamReader createXMLStreamReader(InputStream in) {
XMLInputFactory factory = getXMLInputFactory();
try {
return factory.createXMLStreamReader(in);
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
} finally {
returnXMLInputFactory(factory);
}
}
public static XMLStreamReader createXMLStreamReader(String systemId, InputStream in) {
XMLInputFactory factory = getXMLInputFactory();
try {
return factory.createXMLStreamReader(systemId, in);
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
} finally {
returnXMLInputFactory(factory);
}
}
public static XMLStreamReader createXMLStreamReader(Element el) {
return new W3CDOMStreamReader(el);
}
public static XMLStreamReader createXMLStreamReader(Document doc) {
return new W3CDOMStreamReader(doc.getDocumentElement());
}
public static XMLStreamReader createXMLStreamReader(Element el, String sysId) {
return new W3CDOMStreamReader(el, sysId);
}
public static XMLStreamReader createXMLStreamReader(Document doc, String sysId) {
return new W3CDOMStreamReader(doc.getDocumentElement(), sysId);
}
public static XMLStreamReader createXMLStreamReader(Source source) {
try {
if (source instanceof DOMSource) {
DOMSource ds = (DOMSource)source;
Node nd = ds.getNode();
Element el = null;
if (nd instanceof Document) {
el = ((Document)nd).getDocumentElement();
} else if (nd instanceof Element) {
el = (Element)nd;
}
if (null != el) {
return new W3CDOMStreamReader(el, source.getSystemId());
}
} else if (source instanceof StAXSource) {
return ((StAXSource)source).getXMLStreamReader();
} else if (source instanceof StaxSource) {
return ((StaxSource)source).getXMLStreamReader();
} else if (source instanceof SAXSource) {
SAXSource ss = (SAXSource)source;
if (ss.getXMLReader() == null) {
return createXMLStreamReader(((SAXSource)source).getInputSource());
}
}
XMLInputFactory factory = getXMLInputFactory();
try {
XMLStreamReader reader = null;
try {
reader = factory.createXMLStreamReader(source);
} catch (UnsupportedOperationException e) {
//ignore
}
if (reader == null && source instanceof StreamSource) {
//createXMLStreamReader from Source is optional, we'll try and map it
StreamSource ss = (StreamSource)source;
if (ss.getInputStream() != null) {
reader = factory.createXMLStreamReader(ss.getSystemId(),
ss.getInputStream());
} else {
reader = factory.createXMLStreamReader(ss.getSystemId(),
ss.getReader());
}
}
return reader;
} finally {
returnXMLInputFactory(factory);
}
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
}
}
/**
* @param reader
*/
public static XMLStreamReader createXMLStreamReader(Reader reader) {
XMLInputFactory factory = getXMLInputFactory();
try {
return factory.createXMLStreamReader(reader);
} catch (XMLStreamException e) {
throw new RuntimeException("Couldn't parse stream.", e);
} finally {
returnXMLInputFactory(factory);
}
}
/**
* Reads a QName from the element text. Reader must be positioned at the
* start tag.
*/
public static QName readQName(XMLStreamReader reader) throws XMLStreamException {
String value = reader.getElementText();
if (value == null) {
return null;
}
value = value.trim();
int index = value.indexOf(':');
if (index == -1) {
return new QName(value);
}
String prefix = value.substring(0, index);
String localName = value.substring(index + 1);
String ns = reader.getNamespaceURI(prefix);
if (!StringUtils.isEmpty(prefix) && ns == null) {
throw new RuntimeException("Invalid QName in mapping: " + value);
}
if (ns == null) {
return new QName(localName);
}
return new QName(ns, localName, prefix);
}
/**
* Create a unique namespace uri/prefix combination.
*
* @return The namespace with the specified URI. If one doesn't exist, one
* is created.
* @throws XMLStreamException
*/
public static String getUniquePrefix(XMLStreamWriter writer, String namespaceURI, boolean declare)
throws XMLStreamException {
String prefix = writer.getPrefix(namespaceURI);
if (prefix == null) {
prefix = getUniquePrefix(writer);
if (declare) {
writer.setPrefix(prefix, namespaceURI);
writer.writeNamespace(prefix, namespaceURI);
}
}
return prefix;
}
public static String getUniquePrefix(XMLStreamWriter writer, String namespaceURI)
throws XMLStreamException {
return getUniquePrefix(writer, namespaceURI, false);
}
public static String getUniquePrefix(XMLStreamWriter writer) {
NamespaceContext nc = writer.getNamespaceContext();
if (nc == null) {
return DEF_PREFIXES[0];
}
for (String t : DEF_PREFIXES) {
String uri = nc.getNamespaceURI(t);
if (StringUtils.isEmpty(uri)) {
return t;
}
}
int n = 10;
while (true) {
String nsPrefix = "ns" + n;
String uri = nc.getNamespaceURI(nsPrefix);
if (StringUtils.isEmpty(uri)) {
return nsPrefix;
}
n++;
}
}
public static void printXmlFragment(XMLStreamReader reader) {
try {
StringWriter sw = new StringWriter(1024);
XMLStreamWriter writer = null;
try {
writer = new PrettyPrintXMLStreamWriter(createXMLStreamWriter(sw), 4);
copy(reader, writer);
writer.flush();
} finally {
StaxUtils.close(writer);
}
LOG.info(sw.toString());
} catch (XMLStreamException e) {
LOG.severe(e.getMessage());
}
}
private static void writeStartElementEvent(XMLEvent event, XMLStreamWriter writer)
throws XMLStreamException {
StartElement start = event.asStartElement();
QName name = start.getName();
String nsURI = name.getNamespaceURI();
String localName = name.getLocalPart();
String prefix = name.getPrefix();
if (prefix != null) {
writer.writeStartElement(prefix, localName, nsURI);
} else if (nsURI != null) {
writer.writeStartElement(localName, nsURI);
} else {
writer.writeStartElement(localName);
}
Iterator<XMLEvent> it = CastUtils.cast(start.getNamespaces());
while (it != null && it.hasNext()) {
writeEvent(it.next(), writer);
}
it = CastUtils.cast(start.getAttributes());
while (it != null && it.hasNext()) {
writeAttributeEvent(it.next(), writer);
}
}
private static void writeAttributeEvent(XMLEvent event, XMLStreamWriter writer)
throws XMLStreamException {
Attribute attr = (Attribute)event;
QName name = attr.getName();
String nsURI = name.getNamespaceURI();
String localName = name.getLocalPart();
String prefix = name.getPrefix();
String value = attr.getValue();
if (prefix != null) {
writer.writeAttribute(prefix, nsURI, localName, value);
} else if (nsURI != null) {
writer.writeAttribute(nsURI, localName, value);
} else {
writer.writeAttribute(localName, value);
}
}
public static void writeEvent(XMLEvent event, XMLStreamWriter writer)
throws XMLStreamException {
switch (event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
writeStartElementEvent(event, writer);
break;
case XMLStreamConstants.END_ELEMENT:
writer.writeEndElement();
break;
case XMLStreamConstants.ATTRIBUTE:
writeAttributeEvent(event, writer);
break;
case XMLStreamConstants.ENTITY_REFERENCE:
writer.writeEntityRef(((javax.xml.stream.events.EntityReference)event).getName());
break;
case XMLStreamConstants.DTD:
writer.writeDTD(((DTD)event).getDocumentTypeDeclaration());
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
if (((javax.xml.stream.events.ProcessingInstruction)event).getData() != null) {
writer.writeProcessingInstruction(
((javax.xml.stream.events.ProcessingInstruction)event).getTarget(),
((javax.xml.stream.events.ProcessingInstruction)event).getData());
} else {
writer.writeProcessingInstruction(
((javax.xml.stream.events.ProcessingInstruction)event).getTarget());
}
break;
case XMLStreamConstants.NAMESPACE:
if (((Namespace)event).isDefaultNamespaceDeclaration()) {
writer.writeDefaultNamespace(((Namespace)event).getNamespaceURI());
writer.setDefaultNamespace(((Namespace)event).getNamespaceURI());
} else {
writer.writeNamespace(((Namespace)event).getPrefix(),
((Namespace)event).getNamespaceURI());
writer.setPrefix(((Namespace)event).getPrefix(),
((Namespace)event).getNamespaceURI());
}
break;
case XMLStreamConstants.COMMENT:
writer.writeComment(((javax.xml.stream.events.Comment)event).getText());
break;
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.SPACE:
writer.writeCharacters(event.asCharacters().getData());
break;
case XMLStreamConstants.CDATA:
writer.writeCData(event.asCharacters().getData());
break;
case XMLStreamConstants.START_DOCUMENT:
if (((StartDocument)event).encodingSet()) {
writer.writeStartDocument(((StartDocument)event).getCharacterEncodingScheme(),
((StartDocument)event).getVersion());
} else {
writer.writeStartDocument(((StartDocument)event).getVersion());
}
break;
case XMLStreamConstants.END_DOCUMENT:
writer.writeEndDocument();
break;
default:
//shouldn't get here
}
}
public static void print(Node node) {
XMLStreamWriter writer = null;
try {
writer = createXMLStreamWriter(System.out);
copy(new DOMSource(node), writer);
writer.flush();
} catch (XMLStreamException e) {
throw new RuntimeException(e);
} finally {
StaxUtils.close(writer);
}
}
public static String toString(Source src) {
StringWriter sw = new StringWriter(1024);
XMLStreamWriter writer = null;
try {
writer = createXMLStreamWriter(sw);
copy(src, writer);
writer.flush();
} catch (XMLStreamException e) {
throw new RuntimeException(e);
} finally {
StaxUtils.close(writer);
}
return sw.toString();
}
public static String toString(Node src) {
return toString(new DOMSource(src));
}
public static String toString(Document doc) {
StringWriter sw = new StringWriter(1024);
XMLStreamWriter writer = null;
try {
writer = createXMLStreamWriter(sw);
copy(doc, writer);
writer.flush();
} catch (XMLStreamException e) {
throw new RuntimeException(e);
} finally {
StaxUtils.close(writer);
}
return sw.toString();
}
public static String toString(Element el) {
return toString(el, 0);
}
public static String toString(Element el, int indent) {
StringWriter sw = new StringWriter(1024);
XMLStreamWriter writer = null;
try {
writer = createXMLStreamWriter(sw);
if (indent > 0) {
writer = new PrettyPrintXMLStreamWriter(writer, indent);
}
copy(el, writer);
writer.flush();
} catch (XMLStreamException e) {
throw new RuntimeException(e);
} finally {
StaxUtils.close(writer);
}
return sw.toString();
}
public static void close(XMLStreamReader reader) throws XMLStreamException {
if (reader != null) {
reader.close();
}
}
public static void close(XMLStreamWriter writer) {
if (writer != null) {
try {
writer.close();
} catch (Exception e) {
//ignore
}
}
}
public static boolean isSecureReader(XMLStreamReader reader, Message message) {
if (reader instanceof DocumentDepthProperties) {
return true;
}
try {
if (reader.getProperty(P_MAX_CHILDREN_PER_ELEMENT) != null) {
return true;
}
} catch (Exception ex) {
//ignore
}
return false;
}
public static XMLStreamReader configureReader(XMLStreamReader xreader, Message message) throws XMLStreamException {
Integer messageMaxChildElements = PropertyUtils.getInteger(message, MAX_CHILD_ELEMENTS);
Integer messageMaxElementDepth = PropertyUtils.getInteger(message, MAX_ELEMENT_DEPTH);
Integer messageMaxAttributeCount = PropertyUtils.getInteger(message, MAX_ATTRIBUTE_COUNT);
Integer messageMaxAttributeSize = PropertyUtils.getInteger(message, MAX_ATTRIBUTE_SIZE);
Integer messageMaxTextLength = PropertyUtils.getInteger(message, MAX_TEXT_LENGTH);
Long messageMaxElementCount = PropertyUtils.getLong(message, MAX_ELEMENT_COUNT);
Long messageMaxXMLCharacters = PropertyUtils.getLong(message, MAX_XML_CHARACTERS);
return configureReader(xreader, messageMaxChildElements, messageMaxElementDepth,
messageMaxAttributeCount, messageMaxAttributeSize, messageMaxTextLength,
messageMaxElementCount, messageMaxXMLCharacters);
}
//CHECKSTYLE:OFF - lots of params to configure
public static XMLStreamReader configureReader(XMLStreamReader reader, Integer maxChildElements,
Integer maxElementDepth, Integer maxAttributeCount,
Integer maxAttributeSize, Integer maxTextLength,
Long maxElementCount, Long maxXMLCharacters)
throws XMLStreamException {
//CHECKSTYLE:ON
// We currently ONLY support Woodstox 4.2.x for most of this other than a few things
// that we can handle via a wrapper.
try {
DocumentDepthProperties p = null;
if (maxChildElements != null) {
try {
setProperty(reader, P_MAX_CHILDREN_PER_ELEMENT, maxChildElements);
} catch (Throwable t) {
//we can handle this via a wrapper
p = new DocumentDepthProperties();
p.setInnerElementCountThreshold(maxChildElements);
}
}
if (maxElementDepth != null) {
try {
setProperty(reader, P_MAX_ELEMENT_DEPTH, maxElementDepth);
} catch (Throwable t) {
//we can handle this via a wrapper
if (p == null) {
p = new DocumentDepthProperties();
}
p.setInnerElementLevelThreshold(maxElementDepth);
}
}
if (maxAttributeCount != null) {
setProperty(reader, P_MAX_ATTRIBUTES_PER_ELEMENT, maxAttributeCount);
}
if (maxAttributeSize != null) {
setProperty(reader, P_MAX_ATTRIBUTE_SIZE, maxAttributeSize);
}
if (maxTextLength != null) {
setProperty(reader, P_MAX_TEXT_LENGTH, maxTextLength);
}
if (maxElementCount != null) {
try {
setProperty(reader, P_MAX_ELEMENT_COUNT, maxElementCount);
} catch (Throwable t) {
//we can handle this via a wrapper
if (p == null) {
p = new DocumentDepthProperties();
}
p.setElementCountThreshold(maxElementCount.intValue());
}
}
if (maxXMLCharacters != null) {
setProperty(reader, P_MAX_CHARACTERS, maxXMLCharacters);
}
if (p != null) {
reader = new DepthRestrictingStreamReader(reader, p);
}
} catch (ClassCastException cce) {
//not an XMLStreamReader2
if (ALLOW_INSECURE_PARSER_VAL) {
LOG.warning("INSTANCE_NOT_XMLSTREAMREADER2");
} else {
throw new XMLStreamException(cce.getMessage(), cce);
}
} catch (IllegalArgumentException cce) {
//not a property supported by this version of woodstox
if (ALLOW_INSECURE_PARSER_VAL) {
LOG.log(Level.WARNING, "SECURE_PROPERTY_NOT_SUPPORTED", cce.getMessage());
} else {
throw new XMLStreamException(cce.getMessage(), cce);
}
}
return reader;
}
private static void setProperty(XMLStreamReader reader, String p, Object v) {
WoodstoxHelper.setProperty(reader, p, v);
}
}