Honeynode test tool
[transportpce.git] / tests / honeynode / honeynode-common / src / main / java / io / fd / honeycomb / transportpce / binding / 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 io.fd.honeycomb.transportpce.binding.converter;
9
10 import io.fd.honeycomb.transportpce.test.common.DataStoreContext;
11
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.Reader;
15 import java.io.StringWriter;
16 import java.io.Writer;
17 import java.net.URISyntaxException;
18 import java.util.Optional;
19 import javax.annotation.Nonnull;
20 import javax.xml.XMLConstants;
21 import javax.xml.parsers.FactoryConfigurationError;
22 import javax.xml.parsers.ParserConfigurationException;
23 import javax.xml.stream.XMLInputFactory;
24 import javax.xml.stream.XMLOutputFactory;
25 import javax.xml.stream.XMLStreamException;
26 import javax.xml.stream.XMLStreamReader;
27 import javax.xml.stream.XMLStreamWriter;
28 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
29 import org.opendaylight.yangtools.yang.binding.DataObject;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
35 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
36 import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter;
37 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
38 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
39 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.xml.sax.SAXException;
46
47 public class XMLDataObjectConverter extends AbstractDataObjectConverter {
48
49     private static final Logger LOG = LoggerFactory.getLogger(XMLDataObjectConverter.class);
50
51     private final XMLInputFactory xmlInputFactory;
52
53     /**
54      * This is the default constructor, which should be used.
55      *
56      * @param schemaContext schema context for converter
57      * @param codecRegistry codec registry used for converting
58      *
59      */
60     private XMLDataObjectConverter(SchemaContext schemaContext, BindingNormalizedNodeSerializer codecRegistry) {
61         super(schemaContext, codecRegistry);
62         this.xmlInputFactory = XMLInputFactory.newInstance();
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.getBindingToNormalizedNodeCodec();
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 SchemaContext 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<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
97             @Nonnull InputStream inputStream) {
98         try {
99             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputStream);
100             return parseInputXML(reader);
101         } catch (XMLStreamException e) {
102             LOG.warn(e.getMessage(), e);
103             return Optional.empty();
104         }
105     }
106
107     public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
108             @Nonnull Reader inputReader, SchemaNode parentSchema) {
109         try {
110             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
111             return parseInputXML(reader, parentSchema);
112         } catch (XMLStreamException e) {
113             LOG.warn(e.getMessage(), e);
114             return Optional.empty();
115         }
116     }
117
118     /**
119      * Transforms the XML input stream into normalized nodes.
120      *
121      * @param inputReader of the given XML
122      * @return {@link Optional} instance of {@link NormalizedNode}.
123      */
124     @Override
125     public Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> transformIntoNormalizedNode(
126             @Nonnull Reader inputReader) {
127         try {
128             XMLStreamReader reader = this.xmlInputFactory.createXMLStreamReader(inputReader);
129             return parseInputXML(reader);
130         } catch (XMLStreamException e) {
131             LOG.warn(e.getMessage(), e);
132             return Optional.empty();
133         }
134     }
135
136     @Override
137     public <T extends DataObject> Writer writerFromRpcDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
138             ConvertType<T> convertType, QName rpcOutputQName, String rpcName) {
139         Writer writer = new StringWriter();
140         XMLStreamWriter xmlStreamWriter = createXmlStreamWriter(writer);
141         SchemaPath rpcOutputSchemaPath = SchemaPath.create(true, QName.create(rpcOutputQName.getModule(), rpcName),
142                 rpcOutputQName);
143         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(xmlStreamWriter,
144                 rpcOutputSchemaPath)) {
145             xmlStreamWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
146                     rpcOutputQName.getLocalName(), rpcOutputQName.getNamespace().toString());
147             xmlStreamWriter.writeDefaultNamespace(rpcOutputQName.getNamespace().toString());
148             NormalizedNode<?, ?> rpcOutputNormalizedNode = convertType.toNormalizedNodes(dataObjectClass.cast(object),
149                     dataObjectClass).get();
150             for (final NormalizedNode<?, ?> child : ((ContainerNode)rpcOutputNormalizedNode).getValue()) {
151                 normalizedNodeWriter.write(child);
152             }
153             normalizedNodeWriter.flush();
154             xmlStreamWriter.writeEndElement();
155             xmlStreamWriter.flush();
156         } catch (IOException | XMLStreamException ioe) {
157             throw new IllegalStateException(ioe);
158         }
159         return writer;
160     }
161
162     /**
163      * Returns a {@link Writer}.
164      *
165      * @param convertType converter used of converting into normalized node
166      * @param dataObjectClass class of converting object
167      * @param object object you want to convert
168      *
169      */
170     @Override
171     public <T extends DataObject> Writer writerFromDataObject(@Nonnull DataObject object, Class<T> dataObjectClass,
172             ConvertType<T> convertType) {
173         Writer writer = new StringWriter();
174
175         try (NormalizedNodeWriter normalizedNodeWriter = createWriterBackedNormalizedNodeWriter(writer, null)) {
176             normalizedNodeWriter
177                     .write(convertType.toNormalizedNodes(dataObjectClass.cast(object), dataObjectClass).get());
178             normalizedNodeWriter.flush();
179         } catch (IOException ioe) {
180             throw new IllegalStateException(ioe);
181         }
182         return writer;
183     }
184
185     private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
186             XMLStreamReader reader) {
187         return parseInputXML(reader, getSchemaContext());
188     }
189
190     private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputXML(
191             XMLStreamReader reader, SchemaNode parentSchemaNode) {
192         NormalizedNodeResult result = new NormalizedNodeResult();
193         try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
194              XmlParserStream xmlParser = XmlParserStream.create(streamWriter, getSchemaContext(), parentSchemaNode)) {
195             xmlParser.parse(reader);
196         } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException
197                 | SAXException e) {
198             LOG.warn("An error {} occured during parsing XML input stream", e.getMessage(), e);
199             return Optional.empty();
200         }
201         return Optional.ofNullable(result.getResult());
202     }
203
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,
209                     getSchemaContext());
210         } else {
211             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(createXMLStreamWriter,
212                     getSchemaContext(), pathToParent);
213         }
214         return NormalizedNodeWriter.forStreamWriter(streamWriter);
215     }
216
217     private NormalizedNodeWriter createWriterBackedNormalizedNodeWriter(XMLStreamWriter backingWriter,
218             SchemaPath pathToParent) {
219         NormalizedNodeStreamWriter streamWriter;
220         if (pathToParent == null) {
221             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
222                     getSchemaContext());
223         } else {
224             streamWriter = XMLStreamNormalizedNodeStreamWriter.create(backingWriter,
225                     getSchemaContext(), pathToParent);
226         }
227         return NormalizedNodeWriter.forStreamWriter(streamWriter);
228     }
229
230     private static XMLStreamWriter createXmlStreamWriter(Writer backingWriter) {
231         XMLStreamWriter xmlStreamWriter;
232         try {
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);
239         }
240         return xmlStreamWriter;
241     }
242 }