2 * Copyright (c) 2014 Cisco Systems, Inc. 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.yangtools.yang.data.impl.codec.xml;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Strings;
12 import java.io.IOException;
13 import java.io.StringWriter;
15 import javax.xml.XMLConstants;
16 import javax.xml.stream.XMLStreamException;
17 import javax.xml.stream.XMLStreamWriter;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.Node;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
26 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
27 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
35 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
38 * A {@link NormalizedNodeStreamWriter} which translates the events into an
39 * {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding.
41 public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamAttributeWriter {
43 private final XMLStreamWriter writer;
44 private final SchemaTracker tracker;
45 private final XmlStreamUtils streamUtils;
46 private RandomPrefix randomPrefix;
48 private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
49 this.writer = Preconditions.checkNotNull(writer);
50 this.tracker = SchemaTracker.create(context, path);
51 this.streamUtils = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, context);
52 randomPrefix = new RandomPrefix();
56 * Create a new writer with the specified context as its root.
58 * @param writer Output {@link XMLStreamWriter}
59 * @param context Associated {@link SchemaContext}.
60 * @return A new {@link NormalizedNodeStreamWriter}
62 public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context) {
63 return create( writer, context, SchemaPath.ROOT);
67 * Create a new writer with the specified context and rooted in the specified schema path
69 * @param writer Output {@link XMLStreamWriter}
70 * @param context Associated {@link SchemaContext}.
72 * @return A new {@link NormalizedNodeStreamWriter}
74 public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
75 return new XMLStreamNormalizedNodeStreamWriter(writer, context, path);
78 private void writeStartElement( QName qname) throws XMLStreamException {
79 String ns = qname.getNamespace().toString();
80 writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
81 if(writer.getNamespaceContext() != null) {
82 String parentNs = writer.getNamespaceContext().getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
83 if (!ns.equals(parentNs)) {
84 writer.writeDefaultNamespace(ns);
89 private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
91 writeStartElement(qname);
93 streamUtils.writeValue(writer, type, value);
95 writer.writeEndElement();
96 } catch (XMLStreamException e) {
97 throw new IOException("Failed to emit element", e);
101 private void writeElement(final QName qname, final SchemaNode schemaNode, final Object value) throws IOException {
103 writeStartElement(qname);
105 streamUtils.writeValue(writer, schemaNode, value);
107 writer.writeEndElement();
108 } catch (XMLStreamException e) {
109 throw new IOException("Failed to emit element", e);
113 private void writeElement(final QName qname, final SchemaNode schemaNode, final Object value, final Map<QName, String> attributes) throws IOException {
115 writeStartElement(qname);
116 writeAttributes(attributes);
118 streamUtils.writeValue(writer, schemaNode, value);
120 writer.writeEndElement();
121 } catch (XMLStreamException e) {
122 throw new IOException("Failed to emit element", e);
126 private void startElement(final QName qname) throws IOException {
128 writeStartElement(qname);
129 } catch (XMLStreamException e) {
130 throw new IOException("Failed to start element", e);
134 private void startList(final NodeIdentifier name) {
135 tracker.startList(name);
138 private void startListItem(final PathArgument name) throws IOException {
139 tracker.startListItem(name);
140 startElement(name.getNodeType());
144 public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
145 final LeafSchemaNode schema = tracker.leafNode(name);
146 writeElement(schema.getQName(), schema, value);
150 public void leafNode(final NodeIdentifier name, final Object value, final Map<QName, String> attributes) throws IOException {
151 final LeafSchemaNode schema = tracker.leafNode(name);
152 writeElement(schema.getQName(), schema, value, attributes);
156 public void leafSetEntryNode(final Object value, final Map<QName, String> attributes) throws IOException, IllegalArgumentException {
157 final LeafListSchemaNode schema = tracker.leafSetEntryNode();
158 writeElement(schema.getQName(), schema, value, attributes);
162 public void startContainerNode(final NodeIdentifier name, final int childSizeHint, final Map<QName, String> attributes) throws IOException, IllegalArgumentException {
163 startContainerNode(name, childSizeHint);
164 writeAttributes(attributes);
168 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint, final Map<QName, String> attributes) throws IOException, IllegalStateException {
169 startUnkeyedListItem(name, childSizeHint);
170 writeAttributes(attributes);
174 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint, final Map<QName, String> attributes) throws IOException, IllegalArgumentException {
175 startMapEntryNode(identifier, childSizeHint);
176 writeAttributes(attributes);
179 private void writeAttributes(final Map<QName, String> attributes) throws IOException {
180 for (final Map.Entry<QName, String> qNameStringEntry : attributes.entrySet()) {
182 final String namespace = qNameStringEntry.getKey().getNamespace().toString();
183 if(Strings.isNullOrEmpty(namespace)) {
184 writer.writeAttribute(qNameStringEntry.getKey().getLocalName(), qNameStringEntry.getValue());
186 final String prefix = randomPrefix.encodePrefix(qNameStringEntry.getKey().getNamespace());
187 writer.writeAttribute(prefix, namespace, qNameStringEntry.getKey().getLocalName(), qNameStringEntry.getValue());
189 } catch (final XMLStreamException e) {
190 throw new IOException("Unable to emit attribute " + qNameStringEntry, e);
196 public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
197 tracker.startLeafSet(name);
201 public void leafSetEntryNode(final Object value) throws IOException {
202 final LeafListSchemaNode schema = tracker.leafSetEntryNode();
203 writeElement(schema.getQName(), schema, value);
207 public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
208 final SchemaNode schema = tracker.startContainerNode(name);
209 startElement(schema.getQName());
213 public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
218 public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
223 public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
228 public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException {
229 startListItem(identifier);
233 public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
238 public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
239 tracker.startChoiceNode(name);
243 public void startAugmentationNode(final AugmentationIdentifier identifier) {
244 tracker.startAugmentationNode(identifier);
248 public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
249 final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
250 final QName qname = schema.getQName();
252 writeStartElement(qname);
254 streamUtils.writeValue(writer, (Node<?>)value, schema);
256 writer.writeEndElement();
257 } catch (XMLStreamException e) {
258 throw new IOException("Failed to emit element", e);
263 public void endNode() throws IOException {
264 final Object schema = tracker.endNode();
267 if (schema instanceof ListSchemaNode) {
268 // For lists, we only emit end element on the inner frame
269 final Object parent = tracker.getParent();
270 if (parent == schema) {
271 writer.writeEndElement();
273 } else if (schema instanceof ContainerSchemaNode) {
274 // Emit container end element
275 writer.writeEndElement();
277 } catch (XMLStreamException e) {
278 throw new IOException("Failed to end element", e);
283 public void close() throws IOException {
286 } catch (XMLStreamException e) {
287 throw new IOException("Failed to close writer", e);
292 public void flush() throws IOException {
295 } catch (XMLStreamException e) {
296 throw new IOException("Failed to flush writer", e);