Migrate common module to Aluminium Step 2
[transportpce.git] / common / src / main / java / org / opendaylight / transportpce / common / converter / XMLDataObjectConverter.java
1 /*
2  * Copyright © 2016 AT&T and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.common.converter;
9
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;
45
46 public final class XMLDataObjectConverter extends AbstractDataObjectConverter {
47
48     private static final Logger LOG = LoggerFactory.getLogger(XMLDataObjectConverter.class);
49
50     private final XMLInputFactory xmlInputFactory;
51
52     /**
53      * This is the default constructor, which should be used.
54      *
55      * @param schemaContext schema context for converter
56      * @param codecRegistry codec registry used for converting
57      *
58      */
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, "");
65     }
66
67     /**
68      * Extract codec and schema context (?).
69      *
70      * @param dataStoreContextUtil datastore context util used to extract codec and schema context
71      * @return {@link AbstractDataObjectConverter}
72      */
73     public static XMLDataObjectConverter createWithDataStoreUtil(@Nonnull DataStoreContext dataStoreContextUtil) {
74         BindingNormalizedNodeSerializer bindingToNormalizedNodeCodec =
75                 dataStoreContextUtil.getBindingToNormalizedNodeCodec();
76         return new XMLDataObjectConverter(dataStoreContextUtil.getSchemaContext(), bindingToNormalizedNodeCodec);
77     }
78
79     /**
80      * Extract codec and schema context (?).
81      *
82      * @param schemaContext schema context for converter
83      * @param codecRegistry codec registry used for converting
84      * @return new {@link XMLDataObjectConverter}
85      */
86     public static XMLDataObjectConverter createWithSchemaContext(@Nonnull EffectiveModelContext schemaContext,
87             @Nonnull BindingNormalizedNodeSerializer codecRegistry) {
88         return new XMLDataObjectConverter(schemaContext, codecRegistry);
89     }
90
91     /**
92      * Transforms the XML input stream into normalized nodes.
93      *
94      * @param inputStream of the given XML
95      * @return {@link Optional} instance of {@link NormalizedNode}.
96      */
97     @Override
98     public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
99             @Nonnull InputStream inputStream) {
100         try {
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();
106         }
107     }
108
109     public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
110             @Nonnull Reader inputReader, SchemaNode parentSchema) {
111         try {
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();
117         }
118     }
119
120     /**
121      * Transforms the XML input stream into normalized nodes.
122      *
123      * @param inputReader of the given XML
124      * @return {@link Optional} instance of {@link NormalizedNode}.
125      */
126     @Override
127     public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
128             @Nonnull Reader inputReader) {
129         try {
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();
135         }
136     }
137
138     @Override
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),
144                 rpcOutputQName);
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);
154             }
155             normalizedNodeWriter.flush();
156             xmlStreamWriter.writeEndElement();
157             xmlStreamWriter.flush();
158         } catch (IOException | XMLStreamException ioe) {
159             throw new IllegalStateException(ioe);
160         }
161         return writer;
162     }
163
164     /**
165      * Returns a {@link Writer}.
166      *
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
170      *
171      */
172     @Override
173     public <T extends DataObject> Writer writerFromDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
174             ConvertType<T> convertType) {
175         Writer writer = new StringWriter();
176
177         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer, null)) {
178             normalizedNodeWriter
179                     .write(convertType.toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass).get());
180             normalizedNodeWriter.flush();
181         } catch (IOException ioe) {
182             throw new IllegalStateException(ioe);
183         }
184         return writer;
185     }
186
187     private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
188             XMLStreamReader reader) {
189         return parseInputXML(reader, getSchemaContext());
190     }
191
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();
202         }
203         return Optional.ofNullable(result.getResult());
204     }
205
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,
211                     getSchemaContext());
212         } else {
213             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
214                     getSchemaContext(), pathToParent);
215         }
216         return NormalizedNodeWriter.forStreamWriter(streamWriter);
217     }
218
219     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter,
220             SchemaPath pathToParent) {
221         NormalizedNodeStreamWriter streamWriter;
222         if (pathToParent == null) {
223             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
224                     getSchemaContext());
225         } else {
226             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
227                     getSchemaContext(), pathToParent);
228         }
229         return NormalizedNodeWriter.forStreamWriter(streamWriter);
230     }
231
232     private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
233         XMLStreamWriter xmlStreamWriter;
234         try {
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);
241         }
242         return xmlStreamWriter;
243     }
244 }