* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.yangtools.yang.data.codec.xml;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter.TRANSFORMER_FACTORY;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableMap;
-import com.google.common.xml.XmlEscapers;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
-import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.util.StreamReaderDelegate;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stax.StAXSource;
import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
-import org.opendaylight.yangtools.util.xml.UntrustedXML;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
@Beta
@NotThreadSafe
public final class XmlParserStream implements Closeable, Flushable {
+
+ private static final String XML_STANDARD_VERSION = "1.0";
+
private final NormalizedNodeStreamWriter writer;
private final XmlCodecFactory codecs;
private final DataSchemaNode parentNode;
* @throws SAXException
* if an error occurs while parsing the value of an anyxml node
*/
+ // FIXME: 3.0.0 remove ParserConfigurationException
public XmlParserStream parse(final XMLStreamReader reader) throws XMLStreamException, URISyntaxException,
IOException, ParserConfigurationException, SAXException {
if (reader.hasNext()) {
* if an error occurs while parsing the value of an anyxml node
*/
@Beta
+ // FIXME: 3.0.0 remove ParserConfigurationException
public XmlParserStream traverse(final DOMSource src) throws XMLStreamException, URISyntaxException,
IOException, ParserConfigurationException, SAXException {
return parse(new DOMSourceXMLStreamReader(src));
}
- private static Map<QName, String> getElementAttributes(final XMLStreamReader in) {
+ private static ImmutableMap<QName, String> getElementAttributes(final XMLStreamReader in) {
checkState(in.isStartElement(), "Attributes can be extracted only from START_ELEMENT.");
final Map<QName, String> attributes = new LinkedHashMap<>();
return ImmutableMap.copyOf(attributes);
}
- private static String readAnyXmlValue(final XMLStreamReader in) throws XMLStreamException {
- final StringBuilder sb = new StringBuilder();
- final String anyXmlElementName = in.getLocalName();
- sb.append('<').append(anyXmlElementName).append(" xmlns=\"").append(in.getNamespaceURI()).append("\">");
-
- while (in.hasNext()) {
- final int eventType = in.next();
-
- if (eventType == XMLStreamConstants.START_ELEMENT) {
- sb.append('<').append(in.getLocalName()).append('>');
- } else if (eventType == XMLStreamConstants.END_ELEMENT) {
- sb.append("</").append(in.getLocalName()).append('>');
-
- if (in.getLocalName().equals(anyXmlElementName)) {
- break;
+ private static Document readAnyXmlValue(final XMLStreamReader in) throws XMLStreamException {
+ // Underlying reader might return null when asked for version, however when such reader is plugged into
+ // Stax -> DOM transformer, it fails with NPE due to null version. Use default xml version in such case.
+ final XMLStreamReader inWrapper;
+ if (in.getVersion() == null) {
+ inWrapper = new StreamReaderDelegate(in) {
+ @Override
+ public String getVersion() {
+ final String ver = super.getVersion();
+ return ver != null ? ver : XML_STANDARD_VERSION;
}
-
- } else if (eventType == XMLStreamConstants.CHARACTERS) {
- sb.append(XmlEscapers.xmlContentEscaper().escape(in.getText()));
- }
+ };
+ } else {
+ inWrapper = in;
}
- return sb.toString();
+ final DOMResult result = new DOMResult();
+ try {
+ TRANSFORMER_FACTORY.newTransformer().transform(new StAXSource(inWrapper), result);
+ } catch (final TransformerException e) {
+ throw new XMLStreamException("Unable to read anyxml value", e);
+ }
+ return (Document) result.getNode();
}
private void read(final XMLStreamReader in, final AbstractNodeDataWithSchema parent, final String rootElement)
- throws XMLStreamException, URISyntaxException, ParserConfigurationException, SAXException, IOException {
+ throws XMLStreamException, URISyntaxException {
if (!in.hasNext()) {
return;
}
String xmlElementName = in.getLocalName();
while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
read(in, newEntryNode(parent), rootElement);
- if (in.getEventType() == XMLStreamConstants.END_DOCUMENT) {
+ if (in.getEventType() == XMLStreamConstants.END_DOCUMENT
+ || in.getEventType() == XMLStreamConstants.END_ELEMENT) {
break;
}
xmlElementName = in.getLocalName();
switch (in.nextTag()) {
case XMLStreamConstants.START_ELEMENT:
- final Set<String> namesakes = new HashSet<>();
+ // FIXME: why do we even need this tracker? either document it or remove it
+ final Set<Entry<String, String>> namesakes = new HashSet<>();
while (in.hasNext()) {
final String xmlElementName = in.getLocalName();
parentSchema = ((YangModeledAnyXmlSchemaNode) parentSchema).getSchemaOfAnyXmlData();
}
- if (!namesakes.add(xmlElementName)) {
+ final String xmlElementNamespace = in.getNamespaceURI();
+ if (!namesakes.add(new SimpleImmutableEntry<>(xmlElementNamespace, xmlElementName))) {
final Location loc = in.getLocation();
throw new IllegalStateException(String.format(
- "Duplicate element \"%s\" in XML input at: line %s column %s", xmlElementName,
- loc.getLineNumber(), loc.getColumnNumber()));
+ "Duplicate namespace \"%s\" element \"%s\" in XML input at: line %s column %s",
+ xmlElementNamespace, xmlElementName, loc.getLineNumber(), loc.getColumnNumber()));
}
- final String xmlElementNamespace = in.getNamespaceURI();
final Deque<DataSchemaNode> childDataSchemaNodes =
ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, xmlElementName,
new URI(xmlElementNamespace));
}
private static boolean isNextEndDocument(final XMLStreamReader in) throws XMLStreamException {
- return in.next() == XMLStreamConstants.END_DOCUMENT;
+ return !in.hasNext() || in.next() == XMLStreamConstants.END_DOCUMENT;
}
private static boolean isAtElement(final XMLStreamReader in) {
in.nextTag();
}
- private void setValue(final AbstractNodeDataWithSchema parent, final String value, final NamespaceContext nsContext)
- throws ParserConfigurationException, SAXException, IOException {
+
+ private void setValue(final AbstractNodeDataWithSchema parent, final Object value,
+ final NamespaceContext nsContext) {
checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type",
parent.getSchema().getQName());
final SimpleNodeDataWithSchema parentSimpleNode = (SimpleNodeDataWithSchema) parent;
parentSimpleNode.setValue(translateValueByType(value, parentSimpleNode.getSchema(), nsContext));
}
- private Object translateValueByType(final String value, final DataSchemaNode node,
- final NamespaceContext namespaceCtx) throws IOException, SAXException, ParserConfigurationException {
+ private Object translateValueByType(final Object value, final DataSchemaNode node,
+ final NamespaceContext namespaceCtx) {
if (node instanceof AnyXmlSchemaNode) {
+
+ checkArgument(value instanceof Document);
/*
* FIXME: Figure out some YANG extension dispatch, which will
* reuse JSON parsing or XML parsing - anyxml is not well-defined in
* JSON.
*/
- final Document doc = UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(value)));
- doc.normalize();
-
- return new DOMSource(doc.getDocumentElement());
+ return new DOMSource(((Document) value).getDocumentElement());
}
checkArgument(node instanceof TypedDataSchemaNode);
- return codecs.codecFor((TypedDataSchemaNode) node).parseValue(namespaceCtx, value);
+ checkArgument(value instanceof String);
+ return codecs.codecFor((TypedDataSchemaNode) node).parseValue(namespaceCtx, (String) value);
}
private static AbstractNodeDataWithSchema newEntryNode(final AbstractNodeDataWithSchema parent) {