7ae433121825ed9b304cb44375bdcbd139a2c9d6
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / BindingToNormalizedStreamWriter.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.mdsal.binding.dom.codec.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.io.IOException;
13 import java.util.AbstractMap;
14 import java.util.ArrayDeque;
15 import java.util.Deque;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import javax.xml.transform.dom.DOMSource;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.yangtools.concepts.Delegator;
21 import org.opendaylight.yangtools.yang.binding.Augmentation;
22 import org.opendaylight.yangtools.yang.binding.DataContainer;
23 import org.opendaylight.yangtools.yang.binding.DataObject;
24 import org.opendaylight.yangtools.yang.binding.Key;
25 import org.opendaylight.yangtools.yang.binding.KeyAware;
26 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33
34 final class BindingToNormalizedStreamWriter implements AnydataBindingStreamWriter,
35         Delegator<NormalizedNodeStreamWriter> {
36     private final Deque<CodecContext> schema = new ArrayDeque<>();
37     private final @NonNull NormalizedNodeStreamWriter delegate;
38     private final CodecContext rootContext;
39
40     BindingToNormalizedStreamWriter(final DataContainerCodecContext<?, ?, ?> rootContext,
41             final NormalizedNodeStreamWriter delegate) {
42         this.rootContext = requireNonNull(rootContext);
43         this.delegate = requireNonNull(delegate);
44     }
45
46     private void emitSchema(final Object schemaNode) {
47         delegate.nextDataSchemaNode((DataSchemaNode) schemaNode);
48     }
49
50     CodecContext current() {
51         return schema.peek();
52     }
53
54     private NodeIdentifier duplicateSchemaEnter() {
55         final var current = current();
56         final CodecContext next;
57         if (current == null) {
58             // Entry of first node
59             next = rootContext;
60         } else {
61             next = current;
62         }
63         schema.push(next);
64         return next.getDomPathArgument();
65     }
66
67     @SuppressWarnings({"unchecked", "rawtypes"})
68     private <T extends YangInstanceIdentifier.PathArgument> T enter(final Class<?> name, final Class<T> identifier) {
69         final var current = current();
70         final CodecContext next;
71         if (current == null) {
72             // Entry of first node
73             next = rootContext;
74         } else if (current instanceof DataContainerCodecContext<?, ?, ?> currentContainer) {
75             next = currentContainer.getStreamChild((Class) name);
76         } else {
77             throw new IllegalArgumentException("Could not start node " + name + " in non-container " + current);
78         }
79         schema.push(next);
80         return identifier.cast(next.getDomPathArgument());
81     }
82
83     private <T extends YangInstanceIdentifier.PathArgument> T enter(final String localName, final Class<T> identifier) {
84         final var current = current();
85         final var next = ((AbstractDataObjectCodecContext<?, ?>) current).getLeafChild(localName);
86         schema.push(next);
87         return identifier.cast(next.getDomPathArgument());
88     }
89
90     @Override
91     public NormalizedNodeStreamWriter getDelegate() {
92         return delegate;
93     }
94
95     @Override
96     public void endNode() throws IOException {
97         CodecContext left = schema.pop();
98         // Due to writer does not start a new node on startCase() and on startAugmentationNode()
99         // node ending should not be triggered when associated endNode() is invoked.
100         if (!(left instanceof CaseCodecContext) && !(left instanceof AugmentationCodecContext)) {
101             delegate.endNode();
102         }
103     }
104
105     private Map.Entry<NodeIdentifier, Object> serializeLeaf(final String localName, final Object value) {
106         final var current = current();
107         if (!(current instanceof AbstractDataObjectCodecContext<?, ?> currentCasted)) {
108             throw new IllegalArgumentException("Unexpected current context " + current);
109         }
110
111         ValueNodeCodecContext leafContext = currentCasted.getLeafChild(localName);
112         NodeIdentifier domArg = leafContext.getDomPathArgument();
113         Object domValue = leafContext.getValueCodec().serialize(value);
114         emitSchema(leafContext.getSchema());
115         return new AbstractMap.SimpleEntry<>(domArg, domValue);
116     }
117
118     @Override
119     public void leafNode(final String localName, final Object value) throws IOException {
120         final Entry<NodeIdentifier, Object> dom = serializeLeaf(localName, value);
121         delegate.startLeafNode(dom.getKey());
122         delegate.scalarValue(dom.getValue());
123         delegate.endNode();
124     }
125
126     @Override
127     public void anydataNode(final String name, final OpaqueObject<?> value) throws IOException {
128         final Entry<NodeIdentifier, Object> dom = serializeLeaf(name, value);
129         if (delegate.startAnydataNode(dom.getKey(), value.getValue().getObjectModel())) {
130             delegate.scalarValue(dom.getValue());
131             delegate.endNode();
132         }
133     }
134
135     @Override
136     public void anyxmlNode(final String name, final Object value) throws IOException {
137         final Entry<NodeIdentifier, Object> dom = serializeLeaf(name, value);
138         // FIXME: this is not quite right -- we should be handling other object models, too
139         if (delegate.startAnyxmlNode(dom.getKey(), DOMSource.class)) {
140             delegate.domSourceValue((DOMSource) dom.getValue());
141             delegate.endNode();
142         }
143     }
144
145     @Override
146     public void leafSetEntryNode(final Object value) throws IOException {
147         final LeafSetNodeCodecContext ctx = (LeafSetNodeCodecContext) current();
148         final Object domValue = ctx.getValueCodec().serialize(value);
149         delegate.startLeafSetEntryNode(new NodeWithValue<>(ctx.getSchema().getQName(), domValue));
150         delegate.scalarValue(domValue);
151         delegate.endNode();
152     }
153
154     @Override
155     public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType) throws IOException {
156         enter(augmentationType, NodeIdentifier.class);
157     }
158
159     @Override
160     public void startCase(final Class<? extends DataObject> caze, final int childSizeHint) {
161         enter(caze, NodeIdentifier.class);
162     }
163
164     @Override
165     public void startChoiceNode(final Class<? extends DataContainer> type, final int childSizeHint)
166             throws IOException {
167         delegate.startChoiceNode(enter(type, NodeIdentifier.class), childSizeHint);
168     }
169
170     @Override
171     public void startContainerNode(final Class<? extends DataObject> object, final int childSizeHint)
172             throws IOException {
173         delegate.startContainerNode(enter(object, NodeIdentifier.class), childSizeHint);
174     }
175
176     @Override
177     public void startLeafSet(final String localName, final int childSizeHint) throws IOException {
178         final NodeIdentifier id = enter(localName, NodeIdentifier.class);
179         emitSchema(current().getSchema());
180         delegate.startLeafSet(id, childSizeHint);
181     }
182
183     @Override
184     public void startOrderedLeafSet(final String localName, final int childSizeHint) throws IOException {
185         delegate.startOrderedLeafSet(enter(localName, NodeIdentifier.class), childSizeHint);
186     }
187
188     @Override
189     public void startMapEntryNode(final Key<?> key, final int childSizeHint) throws IOException {
190         duplicateSchemaEnter();
191         NodeIdentifierWithPredicates identifier = ((MapCodecContext<?, ?>) current()).serialize(key);
192         delegate.startMapEntryNode(identifier, childSizeHint);
193     }
194
195     @Override
196     public <T extends DataObject & KeyAware<?>> void startMapNode(final Class<T> mapEntryType, final int childSizeHint)
197             throws IOException {
198         delegate.startMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
199     }
200
201     @Override
202     public <T extends DataObject & KeyAware<?>> void startOrderedMapNode(final Class<T> mapEntryType,
203             final int childSizeHint) throws IOException {
204         delegate.startOrderedMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
205     }
206
207     @Override
208     public void startUnkeyedList(final Class<? extends DataObject> obj, final int childSizeHint) throws IOException {
209         delegate.startUnkeyedList(enter(obj, NodeIdentifier.class), childSizeHint);
210     }
211
212     @Override
213     public void startUnkeyedListItem(final int childSizeHint) throws IOException {
214         delegate.startUnkeyedListItem(duplicateSchemaEnter(), childSizeHint);
215     }
216
217     @Override
218     public void flush() throws IOException {
219         delegate.flush();
220     }
221
222     @Override
223     public void close() throws IOException {
224         delegate.close();
225     }
226 }