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.XmlCodecFactory;
36 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
37 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
38 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.xml.sax.SAXException;
46 public final class XMLDataObjectConverter extends AbstractDataObjectConverter {
48 private static final Logger LOG = LoggerFactory.getLogger(XMLDataObjectConverter.class);
50 private final XMLInputFactory xmlInputFactory;
53 * This is the default constructor, which should be used.
55 * @param schemaContext schema context for converter
56 * @param codecRegistry codec registry used for converting
59 private XMLDataObjectConverter(EffectiveModelContext schemaContext, BindingNormalizedNodeSerializer codecRegistry) {
60 super(schemaContext, codecRegistry);
61 this.xmlInputFactory = XMLInputFactory.newInstance();
62 // set external DTD and schema to null to avoid vulnerability (sonar report)
63 this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
64 this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
68 * Extract codec and schema context (?).
70 * @param dataStoreContextUtil datastore context util used to extract codec and schema context
71 * @return {@link AbstractDataObjectConverter}
73 public static XMLDataObjectConverter createWithDataStoreUtil(@Nonnull DataStoreContext dataStoreContextUtil) {
74 BindingNormalizedNodeSerializer bindingToNormalizedNodeCodec =
75 dataStoreContextUtil.getBindingToNormalizedNodeCodec();
76 return new XMLDataObjectConverter(dataStoreContextUtil.getSchemaContext(), bindingToNormalizedNodeCodec);
80 * Extract codec and schema context (?).
82 * @param schemaContext schema context for converter
83 * @param codecRegistry codec registry used for converting
84 * @return new {@link XMLDataObjectConverter}
86 public static XMLDataObjectConverter createWithSchemaContext(@Nonnull EffectiveModelContext schemaContext,
87 @Nonnull BindingNormalizedNodeSerializer codecRegistry) {
88 return new XMLDataObjectConverter(schemaContext, codecRegistry);
92 * Transforms the XML input stream into normalized nodes.
94 * @param inputStream of the given XML
95 * @return {@link Optional} instance of {@link NormalizedNode}.
98 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
99 @Nonnull InputStream inputStream) {
101 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputStream);
102 return parseInputXML(reader);
103 } catch (XMLStreamException e) {
104 LOG.warn("XMLStreamException: {}", e.getMessage());
105 return Optional.empty();
109 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
110 @Nonnull Reader inputReader, SchemaNode parentSchema) {
112 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
113 return parseInputXML(reader, parentSchema);
114 } catch (XMLStreamException e) {
115 LOG.warn("XMLStreamException: {}", e.getMessage());
116 return Optional.empty();
121 * Transforms the XML input stream into normalized nodes.
123 * @param inputReader of the given XML
124 * @return {@link Optional} instance of {@link NormalizedNode}.
127 public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
128 @Nonnull Reader inputReader) {
130 XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
131 return parseInputXML(reader);
132 } catch (XMLStreamException e) {
133 LOG.warn("XMLStreamException: {}", e.getMessage());
134 return Optional.empty();
139 public <T extends DataObject> Writer writerFromRpcDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
140 ConvertType<T> convertType, QName rpcOutputQName, String rpcName) {
141 Writer writer = new StringWriter();
142 XMLStreamWriter xmlStreamWriter = createXmlStreamWriter(writer);
143 SchemaPath rpcOutputSchemaPath = SchemaPath.create(true, QName.create(rpcOutputQName.getModule(), rpcName),
145 try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(xmlStreamWriter,
146 rpcOutputSchemaPath)) {
147 xmlStreamWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
148 rpcOutputQName.getLocalName(), rpcOutputQName.getNamespace().toString());
149 xmlStreamWriter.writeDefaultNamespace(rpcOutputQName.getNamespace().toString());
150 NormalizedNode<?, ?> rpcOutputNormalizedNode = convertType.toNormalizedNodes(dataObjectClass.cast(object),
151 dataObjectClass).get();
152 for (final NormalizedNode<?, ?> child : ((ContainerNode)rpcOutputNormalizedNode).getValue()) {
153 normalizedNodeWriter.write(child);
155 normalizedNodeWriter.flush();
156 xmlStreamWriter.writeEndElement();
157 xmlStreamWriter.flush();
158 } catch (IOException | XMLStreamException ioe) {
159 throw new IllegalStateException(ioe);
165 * Returns a {@link Writer}.
167 * @param convertType converter used of converting into normalized node
168 * @param dataObjectClass class of converting object
169 * @param object object you want to convert
173 public <T extends DataObject> Writer writerFromDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
174 ConvertType<T> convertType) {
175 Writer writer = new StringWriter();
177 try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer, null)) {
179 .write(convertType.toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass).get());
180 normalizedNodeWriter.flush();
181 } catch (IOException ioe) {
182 throw new IllegalStateException(ioe);
187 private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
188 XMLStreamReader reader) {
189 return parseInputXML(reader, getSchemaContext());
192 private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
193 XMLStreamReader reader, SchemaNode parentSchemaNode) {
194 NormalizedNodeResult result = new NormalizedNodeResult();
195 try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
196 XmlParserStream xmlParser = XmlParserStream
197 .create(streamWriter, XmlCodecFactory.create(getSchemaContext()), parentSchemaNode)) {
198 xmlParser.parse(reader);
199 } catch (XMLStreamException | URISyntaxException | IOException | SAXException e) {
200 LOG.warn("An error occured during parsing XML input stream", e);
201 return Optional.empty();
203 return Optional.ofNullable(result.getResult());
206 private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(Writer backingWriter, SchemaPath pathToParent) {
207 XMLStreamWriter createXMLStreamWriter = createXmlStreamWriter(backingWriter);
208 NormalizedNodeStreamWriter streamWriter;
209 if (pathToParent == null) {
210 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
213 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
214 getSchemaContext(), pathToParent);
216 return NormalizedNodeWriter.forStreamWriter(streamWriter);
219 private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter,
220 SchemaPath pathToParent) {
221 NormalizedNodeStreamWriter streamWriter;
222 if (pathToParent == null) {
223 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
226 streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
227 getSchemaContext(), pathToParent);
229 return NormalizedNodeWriter.forStreamWriter(streamWriter);
232 private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
233 XMLStreamWriter xmlStreamWriter;
235 XMLOutputFactory factory = XMLOutputFactory.newFactory();
236 factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
237 xmlStreamWriter = factory.createXMLStreamWriter(backingWriter);
238 } catch (XMLStreamException | FactoryConfigurationError e) {
239 LOG.error("Error while creating XML writer: ", e);
240 throw new IllegalStateException(e);
242 return xmlStreamWriter;