Populate codec/ directory
[yangtools.git] / codec / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / SchemaAwareXMLStreamNormalizedNodeStreamWriter.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  * Copyright (c) 2016 Brocade Communications Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9 package org.opendaylight.yangtools.yang.data.codec.xml;
10
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static com.google.common.base.Preconditions.checkState;
13 import static java.util.Objects.requireNonNull;
14
15 import java.io.IOException;
16 import java.util.Optional;
17 import javax.xml.stream.XMLStreamException;
18 import javax.xml.stream.XMLStreamWriter;
19 import javax.xml.transform.dom.DOMSource;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.util.NormalizedNodeStreamWriterStack;
28 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
38 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
40
41 final class SchemaAwareXMLStreamNormalizedNodeStreamWriter
42         extends XMLStreamNormalizedNodeStreamWriter<TypedDataSchemaNode> implements EffectiveModelContextProvider {
43     private final NormalizedNodeStreamWriterStack tracker;
44     private final SchemaAwareXMLStreamWriterUtils streamUtils;
45
46     SchemaAwareXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final EffectiveModelContext context,
47             final NormalizedNodeStreamWriterStack tracker) {
48         super(writer);
49         this.tracker = requireNonNull(tracker);
50         this.streamUtils = new SchemaAwareXMLStreamWriterUtils(context);
51     }
52
53     @Override
54     String encodeValue(final ValueWriter xmlWriter, final Object value, final TypedDataSchemaNode schemaNode)
55             throws XMLStreamException {
56         return streamUtils.encodeValue(xmlWriter, resolveType(schemaNode.getType()), value,
57             schemaNode.getQName().getModule());
58     }
59
60     @Override
61     String encodeAnnotationValue(final ValueWriter xmlWriter, final QName qname, final Object value)
62             throws XMLStreamException {
63         final Optional<AnnotationSchemaNode> optAnnotation =
64             AnnotationSchemaNode.find(streamUtils.getEffectiveModelContext(), qname);
65         if (optAnnotation.isPresent()) {
66             return streamUtils.encodeValue(xmlWriter, resolveType(optAnnotation.get().getType()), value,
67                 qname.getModule());
68         }
69
70         checkArgument(!qname.getRevision().isPresent(), "Failed to find bound annotation %s", qname);
71         checkArgument(value instanceof String, "Invalid non-string value %s for unbound annotation %s", value, qname);
72         return (String) value;
73     }
74
75     @Override
76     void startList(final NodeIdentifier name) {
77         tracker.startList(name);
78     }
79
80     @Override
81     void startListItem(final PathArgument name) throws IOException {
82         tracker.startListItem(name);
83         startElement(name.getNodeType());
84     }
85
86     @Override
87     public void endNode() throws IOException {
88         final Object schema = tracker.endNode();
89         if (schema instanceof ListSchemaNode || schema instanceof LeafListSchemaNode) {
90             // For lists, we only emit end element on the inner frame
91             final Object parent = tracker.getParent();
92             if (parent == schema) {
93                 endElement();
94             }
95         } else if (schema instanceof ContainerLike || schema instanceof LeafSchemaNode
96                 || schema instanceof AnydataSchemaNode || schema instanceof AnyxmlSchemaNode) {
97             endElement();
98         }
99     }
100
101     @Override
102     public void startLeafNode(final NodeIdentifier name) throws IOException {
103         tracker.startLeafNode(name);
104         startElement(name.getNodeType());
105     }
106
107     @Override
108     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
109         tracker.startLeafSetEntryNode(name);
110         startElement(name.getNodeType());
111     }
112
113     @Override
114     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
115         tracker.startLeafSet(name);
116     }
117
118     @Override
119     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
120         tracker.startLeafSet(name);
121     }
122
123     @Override
124     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
125         final SchemaNode schema = tracker.startContainerNode(name);
126         startElement(schema.getQName());
127     }
128
129     @Override
130     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
131         tracker.startChoiceNode(name);
132     }
133
134     @Override
135     public void startAugmentationNode(final AugmentationIdentifier identifier) {
136         tracker.startAugmentationNode(identifier);
137     }
138
139     @Override
140     public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
141         if (DOMSource.class.isAssignableFrom(objectModel)) {
142             tracker.startAnyxmlNode(name);
143             startElement(name.getNodeType());
144             return true;
145         }
146         return false;
147     }
148
149     @Override
150     public EffectiveModelContext getEffectiveModelContext() {
151         return streamUtils.getEffectiveModelContext();
152     }
153
154     @Override
155     public void scalarValue(final Object value) throws IOException {
156         final Object current = tracker.getParent();
157         if (current instanceof TypedDataSchemaNode) {
158             writeValue(value, (TypedDataSchemaNode) current);
159         } else if (current instanceof AnydataSchemaNode) {
160             anydataValue(value);
161         } else {
162             throw new IllegalStateException("Unexpected scalar value " + value + " with " + current);
163         }
164     }
165
166     @Override
167     public void domSourceValue(final DOMSource value) throws IOException {
168         final Object current = tracker.getParent();
169         checkState(current instanceof AnyxmlSchemaNode, "Unexpected scala value %s with %s", value, current);
170         anyxmlValue(value);
171     }
172
173     @Override
174     void startAnydata(final NodeIdentifier name) {
175         tracker.startAnydataNode(name);
176     }
177
178     private @NonNull TypeDefinition<?> resolveType(final @NonNull TypeDefinition<?> type) throws XMLStreamException {
179         if (type instanceof LeafrefTypeDefinition) {
180             try {
181                 return tracker.resolveLeafref((LeafrefTypeDefinition) type);
182             } catch (IllegalArgumentException e) {
183                 throw new XMLStreamException("Cannot resolved type " + type, e);
184             }
185         }
186         return type;
187     }
188 }