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