Remove usage of SchemaPath from converters
[transportpce.git] / test-common / src / main / java / org / opendaylight / transportpce / test / 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.test.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.test.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.schema.ContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
33 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
34 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
35 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
36 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
39 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
40 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
41 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
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.getBindingDOMCodecServices();
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> transformIntoNormalizedNode(@Nonnull InputStream inputStream) {
99         try {
100             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputStream);
101             return parseInputXML(reader);
102         } catch (XMLStreamException e) {
103             LOG.warn("XMLStreamException: {}", e.getMessage());
104             return Optional.empty();
105         }
106     }
107
108     @Override
109     public Optional<NormalizedNode> transformIntoNormalizedNode(@Nonnull Reader inputReader) {
110         try {
111             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
112             return parseInputXML(reader);
113         } catch (XMLStreamException e) {
114             LOG.warn("XMLStreamException: {}", e.getMessage());
115             return Optional.empty();
116         }
117     }
118
119     @Override
120     public Optional<NormalizedNode> transformIntoNormalizedNode(Reader inputReader, SchemaNode parentSchema) {
121         throw new UnsupportedOperationException("Not Implemented yet");
122     }
123
124     /**
125      * Transforms the XML input stream into normalized nodes.
126      *
127      * @param inputReader of the given XML
128      * @return {@link Optional} instance of {@link NormalizedNode}.
129      */
130     public Optional<NormalizedNode> transformInschemaContexttoNormalizedNode(@Nonnull Reader inputReader) {
131         try {
132             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
133             return parseInputXML(reader);
134         } catch (XMLStreamException e) {
135             LOG.warn("XMLStreamException: {}", e.getMessage());
136             return Optional.empty();
137         }
138     }
139
140     @Override
141     public <T extends DataObject> Writer writerFromRpcDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
142             ConvertType<T> convertType, QName rpcOutputQName, String rpcName) {
143         Writer writer = new StringWriter();
144         XMLStreamWriter xmlStreamWriter = createXmlStreamWriter(writer);
145         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(xmlStreamWriter)) {
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).body()) {
152                 normalizedNodeWriter.write(child);
153             }
154             normalizedNodeWriter.flush();
155             xmlStreamWriter.writeEndElement();
156             xmlStreamWriter.flush();
157         } catch (IOException | XMLStreamException ioe) {
158             throw new IllegalStateException(ioe);
159         }
160         return writer;
161     }
162
163     /**
164      * Returns a {@link Writer}.
165      *
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
169      *
170      */
171     @Override
172     public <T extends DataObject> Writer writerFromDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
173             ConvertType<T> convertType) {
174
175         Writer writer = new StringWriter();
176         Optional<NormalizedNode> normalizedNode = convertType
177             .toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass);
178         if (normalizedNode.isEmpty()) {
179             LOG.warn("enable to convert {} to {}", dataObjectClass, object.getClass());
180             return writer;
181         }
182
183         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer)) {
184             normalizedNodeWriter.write(normalizedNode.get());
185             normalizedNodeWriter.flush();
186         } catch (IOException ioe) {
187             throw new IllegalStateException(ioe);
188         }
189         return writer;
190     }
191
192     private Optional<NormalizedNode> parseInputXML(XMLStreamReader reader) {
193         return parseInputXML(reader, getSchemaContext());
194     }
195
196     private Optional<NormalizedNode> parseInputXML(XMLStreamReader reader, SchemaNode parentSchemaNode) {
197         NormalizedNodeResult result = new NormalizedNodeResult();
198         EffectiveStatementInference schema = SchemaInferenceStack.of(getSchemaContext()).toInference();
199         try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
200                 XmlParserStream xmlParser = XmlParserStream
201                     .create(streamWriter, schema)) {
202             xmlParser.parse(reader);
203         } catch (XMLStreamException | URISyntaxException | IOException | SAXException e) {
204             LOG.warn("An error occured during parsing XML input stream", e);
205             return Optional.empty();
206         }
207         return Optional.ofNullable(result.getResult());
208     }
209
210     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(Writer backingWriter) {
211         XMLStreamWriter createXMLStreamWriter = createXmlStreamWriter(backingWriter);
212         NormalizedNodeStreamWriter streamWriter;
213         streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter, getSchemaContext());
214         return NormalizedNodeWriter.forStreamWriter(streamWriter);
215     }
216
217     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter) {
218         Inference rootNode = SchemaInferenceStack.of(getSchemaContext()).toInference();
219         NormalizedNodeStreamWriter streamWriter = XMLStreamNormalizedNodeStreamWriter
220             .create(backingWriter, rootNode);
221         return NormalizedNodeWriter.forStreamWriter(streamWriter);
222     }
223
224     private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
225         XMLStreamWriter xmlStreamWriter;
226         try {
227             XMLOutputFactory factory = XMLOutputFactory.newFactory();
228             factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
229             xmlStreamWriter = factory.createXMLStreamWriter(backingWriter);
230         } catch (XMLStreamException | FactoryConfigurationError e) {
231             LOG.error("Error while creating XML writer: ", e);
232             throw new IllegalStateException(e);
233         }
234         return xmlStreamWriter;
235     }
236 }