2 * Copyright © 2016 AT&T and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.transportpce.common.converter;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.Reader;
13 import java.io.StringWriter;
14 import java.io.Writer;
15 import java.net.URISyntaxException;
16 import java.util.Optional;
17 import javax.annotation.Nonnull;
18 import javax.xml.XMLConstants;
19 import javax.xml.parsers.FactoryConfigurationError;
20 import javax.xml.stream.XMLInputFactory;
21 import javax.xml.stream.XMLOutputFactory;
22 import javax.xml.stream.XMLStreamException;
23 import javax.xml.stream.XMLStreamReader;
24 import javax.xml.stream.XMLStreamWriter;
25 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
26 import org.opendaylight.transportpce.common.DataStoreContext;
27 import org.opendaylight.yangtools.yang.binding.DataObject;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
33 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
34 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.xml.sax.SAXException;
45 public final class XMLDataObjectConverter extends AbstractDataObjectConverter {
47 private static final Logger LOG = LoggerFactory.getLogger(XMLDataObjectConverter.class);
49 private final XMLInputFactory xmlInputFactory;
52 * This is the default constructor, which should be used.
54 * @param schemaContext schema context for converter
55 * @param codecRegistry codec registry used for converting
58 private XMLDataObjectConverter(SchemaContext schemaContext, BindingNormalizedNodeSerializer codecRegistry) {
59 super(schemaContext, codecRegistry);
60 this.xmlInputFactory = XMLInputFactory.newInstance();
61 // set external DTD and schema to null to avoid vulnerability (sonar report)
62 this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
63 this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
67 * Extract codec and schema context (?).
69 * @param dataStoreContextUtil datastore context util used to extract codec and schema context
70 * @return {@link AbstractDataObjectConverter}
72 public static XMLDataObjectConverter createWithDataStoreUtil(@Nonnull DataStoreContext dataStoreContextUtil) {
73 BindingNormalizedNodeSerializer bindingToNormalizedNodeCodec =
74 dataStoreContextUtil.getBindingToNormalizedNodeCodec();
75 return new XMLDataObjectConverter(dataStoreContextUtil.getSchemaContext(), bindingToNormalizedNodeCodec);
79 * Extract codec and schema context (?).
81 * @param schemaContext schema context for converter
82 * @param codecRegistry codec registry used for converting
83 * @return new {@link XMLDataObjectConverter}
85 public static XMLDataObjectConverter createWithSchemaContext(@Nonnull SchemaContext schemaContext,
86 @Nonnull BindingNormalizedNodeSerializer codecRegistry) {
87 return new XMLDataObjectConverter(schemaContext, codecRegistry);
91 * Transforms the XML input stream into normalized nodes.
93 * @param inputStream of the given XML
94 * @return {@link Optional} instance of {@link NormalizedNode}.
97 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
98 @Nonnull InputStream inputStream) {
100 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputStream);
101 return parseInputXML(reader);
102 } catch (XMLStreamException e) {
103 LOG.warn(e.getMessage(), e);
104 return Optional.empty();
108 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
109 @Nonnull Reader inputReader, SchemaNode parentSchema) {
111 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
112 return parseInputXML(reader, parentSchema);
113 } catch (XMLStreamException e) {
114 LOG.warn(e.getMessage(), e);
115 return Optional.empty();
120 * Transforms the XML input stream into normalized nodes.
122 * @param inputReader of the given XML
123 * @return {@link Optional} instance of {@link NormalizedNode}.
126 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
127 @Nonnull Reader inputReader) {
129 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
130 return parseInputXML(reader);
131 } catch (XMLStreamException e) {
132 LOG.warn(e.getMessage(), e);
133 return Optional.empty();
138 public <T extends DataObject> Writer writerFromRpcDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
139 ConvertType<T> convertType, QName rpcOutputQName, String rpcName) {
140 Writer writer = new StringWriter();
141 XMLStreamWriter xmlStreamWriter = createXmlStreamWriter(writer);
142 SchemaPath rpcOutputSchemaPath = SchemaPath.create(true, QName.create(rpcOutputQName.getModule(), rpcName),
144 try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(xmlStreamWriter,
145 rpcOutputSchemaPath)) {
146 xmlStreamWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
147 rpcOutputQName.getLocalName(), rpcOutputQName.getNamespace().toString());
148 xmlStreamWriter.writeDefaultNamespace(rpcOutputQName.getNamespace().toString());
149 NormalizedNode<?, ?> rpcOutputNormalizedNode = convertType.toNormalizedNodes(dataObjectClass.cast(object),
150 dataObjectClass).get();
151 for (final NormalizedNode<?, ?> child : ((ContainerNode)rpcOutputNormalizedNode).getValue()) {
152 normalizedNodeWriter.write(child);
154 normalizedNodeWriter.flush();
155 xmlStreamWriter.writeEndElement();
156 xmlStreamWriter.flush();
157 } catch (IOException | XMLStreamException ioe) {
158 throw new IllegalStateException(ioe);
164 * Returns a {@link Writer}.
166 * @param convertType converter used of converting into normalized node
167 * @param dataObjectClass class of converting object
168 * @param object object you want to convert
172 public <T extends DataObject> Writer writerFromDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
173 ConvertType<T> convertType) {
174 Writer writer = new StringWriter();
176 try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer, null)) {
178 .write(convertType.toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass).get());
179 normalizedNodeWriter.flush();
180 } catch (IOException ioe) {
181 throw new IllegalStateException(ioe);
186 private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
187 XMLStreamReader reader) {
188 return parseInputXML(reader, getSchemaContext());
191 private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
192 XMLStreamReader reader, SchemaNode parentSchemaNode) {
193 NormalizedNodeResult result = new NormalizedNodeResult();
194 try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
195 XmlParserStream xmlParser = XmlParserStream.create(streamWriter, getSchemaContext(), parentSchemaNode)) {
196 xmlParser.parse(reader);
197 } catch (XMLStreamException | URISyntaxException | IOException | SAXException e) {
198 LOG.warn("An error {} occured during parsing XML input stream", e.getMessage(), e);
199 return Optional.empty();
201 return Optional.ofNullable(result.getResult());
204 private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(Writer backingWriter, SchemaPath pathToParent) {
205 XMLStreamWriter createXMLStreamWriter = createXmlStreamWriter(backingWriter);
206 NormalizedNodeStreamWriter streamWriter;
207 if (pathToParent == null) {
208 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
211 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
212 getSchemaContext(), pathToParent);
214 return NormalizedNodeWriter.forStreamWriter(streamWriter);
217 private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter,
218 SchemaPath pathToParent) {
219 NormalizedNodeStreamWriter streamWriter;
220 if (pathToParent == null) {
221 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
224 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
225 getSchemaContext(), pathToParent);
227 return NormalizedNodeWriter.forStreamWriter(streamWriter);
230 private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
231 XMLStreamWriter xmlStreamWriter;
233 XMLOutputFactory factory = XMLOutputFactory.newFactory();
234 factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
235 xmlStreamWriter = factory.createXMLStreamWriter(backingWriter);
236 } catch (XMLStreamException | FactoryConfigurationError e) {
237 LOG.error("Error [{}] while creating XML writer", e.getMessage(), e);
238 throw new IllegalStateException(e);
240 return xmlStreamWriter;