653efb160ad0cdbf12b486fd42d671b9e8c75fe0
[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.util.Optional;
16 import javax.xml.XMLConstants;
17 import javax.xml.parsers.FactoryConfigurationError;
18 import javax.xml.stream.XMLInputFactory;
19 import javax.xml.stream.XMLOutputFactory;
20 import javax.xml.stream.XMLStreamException;
21 import javax.xml.stream.XMLStreamReader;
22 import javax.xml.stream.XMLStreamWriter;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
25 import org.opendaylight.transportpce.test.DataStoreContext;
26 import org.opendaylight.yangtools.yang.binding.DataObject;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
32 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
33 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
34 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
36 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
38 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
39 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
40 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 public final class XMLDataObjectConverter extends AbstractDataObjectConverter {
45
46     private static final Logger LOG = LoggerFactory.getLogger(XMLDataObjectConverter.class);
47
48     private final XMLInputFactory xmlInputFactory;
49
50     /**
51      * This is the default constructor, which should be used.
52      *
53      * @param schemaContext schema context for converter
54      * @param codecRegistry codec registry used for converting
55      *
56      */
57     private XMLDataObjectConverter(EffectiveModelContext schemaContext, BindingNormalizedNodeSerializer codecRegistry) {
58         super(schemaContext, codecRegistry);
59         this.xmlInputFactory = XMLInputFactory.newInstance();
60         // set external DTD and schema to null to avoid vulnerability (sonar report)
61         this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
62         this.xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
63     }
64
65     /**
66      * Extract codec and schema context (?).
67      *
68      * @param dataStoreContextUtil datastore context util used to extract codec and schema context
69      * @return {@link AbstractDataObjectConverter}
70      */
71     public static XMLDataObjectConverter createWithDataStoreUtil(@NonNull DataStoreContext dataStoreContextUtil) {
72         BindingNormalizedNodeSerializer bindingToNormalizedNodeCodec =
73                 dataStoreContextUtil.getBindingDOMCodecServices();
74         return new XMLDataObjectConverter(dataStoreContextUtil.getSchemaContext(), bindingToNormalizedNodeCodec);
75     }
76
77     /**
78      * Extract codec and schema context (?).
79      *
80      * @param schemaContext schema context for converter
81      * @param codecRegistry codec registry used for converting
82      * @return new {@link XMLDataObjectConverter}
83      */
84     public static XMLDataObjectConverter createWithSchemaContext(@NonNull EffectiveModelContext schemaContext,
85             @NonNull BindingNormalizedNodeSerializer codecRegistry) {
86         return new XMLDataObjectConverter(schemaContext, codecRegistry);
87     }
88
89     /**
90      * Transforms the XML input stream into normalized nodes.
91      *
92      * @param inputStream of the given XML
93      * @return {@link Optional} instance of {@link NormalizedNode}.
94      */
95     @Override
96     public Optional<NormalizedNode> transformIntoNormalizedNode(@NonNull InputStream inputStream) {
97         try {
98             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputStream);
99             return parseInputXML(reader);
100         } catch (XMLStreamException e) {
101             LOG.warn("XMLStreamException: {}", e.getMessage());
102             return Optional.empty();
103         }
104     }
105
106     @Override
107     public Optional<NormalizedNode> transformIntoNormalizedNode(@NonNull Reader inputReader) {
108         try {
109             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
110             return parseInputXML(reader);
111         } catch (XMLStreamException e) {
112             LOG.warn("XMLStreamException: {}", e.getMessage());
113             return Optional.empty();
114         }
115     }
116
117     @Override
118     public Optional<NormalizedNode> transformIntoNormalizedNode(Reader inputReader, SchemaNode parentSchema) {
119         throw new UnsupportedOperationException("Not Implemented yet");
120     }
121
122     /**
123      * Transforms the XML input stream into normalized nodes.
124      *
125      * @param inputReader of the given XML
126      * @return {@link Optional} instance of {@link NormalizedNode}.
127      */
128     public Optional<NormalizedNode> transformInschemaContexttoNormalizedNode(@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         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(xmlStreamWriter)) {
144             xmlStreamWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
145                     rpcOutputQName.getLocalName(), rpcOutputQName.getNamespace().toString());
146             xmlStreamWriter.writeDefaultNamespace(rpcOutputQName.getNamespace().toString());
147             NormalizedNode rpcOutputNormalizedNode =
148                 convertType.toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass).orElseThrow();
149             for (final NormalizedNode child : ((ContainerNode)rpcOutputNormalizedNode).body()) {
150                 normalizedNodeWriter.write(child);
151             }
152             normalizedNodeWriter.flush();
153             xmlStreamWriter.writeEndElement();
154             xmlStreamWriter.flush();
155         } catch (IOException | XMLStreamException ioe) {
156             throw new IllegalStateException(ioe);
157         }
158         return writer;
159     }
160
161     /**
162      * Returns a {@link Writer}.
163      *
164      * @param convertType converter used of converting into normalized node
165      * @param dataObjectClass class of converting object
166      * @param object object you want to convert
167      *
168      */
169     @Override
170     public <T extends DataObject> Writer writerFromDataObject(@NonNull DataObject object, Class<T> dataObjectClass,
171             ConvertType<T> convertType) {
172
173         Writer writer = new StringWriter();
174         Optional<NormalizedNode> normalizedNode = convertType
175             .toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass);
176         if (normalizedNode.isEmpty()) {
177             LOG.warn("enable to convert {} to {}", dataObjectClass, object.getClass());
178             return writer;
179         }
180
181         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer)) {
182             normalizedNodeWriter.write(normalizedNode.orElseThrow());
183             normalizedNodeWriter.flush();
184         } catch (IOException ioe) {
185             throw new IllegalStateException(ioe);
186         }
187         return writer;
188     }
189
190     private Optional<NormalizedNode> parseInputXML(XMLStreamReader reader) {
191         return parseInputXML(reader, getSchemaContext());
192     }
193
194     private Optional<NormalizedNode> parseInputXML(XMLStreamReader reader, SchemaNode parentSchemaNode) {
195         NormalizationResultHolder result = new NormalizationResultHolder();
196         EffectiveStatementInference schema = SchemaInferenceStack.of(getSchemaContext()).toInference();
197         try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
198                 XmlParserStream xmlParser = XmlParserStream
199                     .create(streamWriter, schema)) {
200             xmlParser.parse(reader);
201         } catch (XMLStreamException | IOException e) {
202             LOG.warn("An error occured during parsing XML input stream", e);
203             return Optional.empty();
204         }
205         return Optional.ofNullable(result.getResult().data());
206     }
207
208     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(Writer backingWriter) {
209         XMLStreamWriter createXMLStreamWriter = createXmlStreamWriter(backingWriter);
210         NormalizedNodeStreamWriter streamWriter;
211         streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter, getSchemaContext());
212         return NormalizedNodeWriter.forStreamWriter(streamWriter);
213     }
214
215     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter) {
216         Inference rootNode = SchemaInferenceStack.of(getSchemaContext()).toInference();
217         NormalizedNodeStreamWriter streamWriter = XMLStreamNormalizedNodeStreamWriter
218             .create(backingWriter, rootNode);
219         return NormalizedNodeWriter.forStreamWriter(streamWriter);
220     }
221
222     private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
223         XMLStreamWriter xmlStreamWriter;
224         try {
225             XMLOutputFactory factory = XMLOutputFactory.newFactory();
226             factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
227             xmlStreamWriter = factory.createXMLStreamWriter(backingWriter);
228         } catch (XMLStreamException | FactoryConfigurationError e) {
229             LOG.error("Error while creating XML writer: ", e);
230             throw new IllegalStateException(e);
231         }
232         return xmlStreamWriter;
233     }
234 }