Switch to Objects.requireNonNull
[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 com.google.common.base.Preconditions;
13 import java.io.IOException;
14 import java.util.AbstractMap;
15 import java.util.ArrayDeque;
16 import java.util.Deque;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import org.opendaylight.yangtools.concepts.Delegator;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
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.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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.schema.stream.NormalizedNodeStreamWriter;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32
33 final class BindingToNormalizedStreamWriter implements BindingStreamEventWriter, Delegator<NormalizedNodeStreamWriter> {
34     private final Deque<NodeCodecContext<?>> schema = new ArrayDeque<>();
35     private final NormalizedNodeStreamWriter delegate;
36     private final NodeCodecContext<?> rootNodeSchema;
37
38     BindingToNormalizedStreamWriter(final NodeCodecContext<?> rootNodeSchema,
39             final NormalizedNodeStreamWriter delegate) {
40         this.rootNodeSchema = requireNonNull(rootNodeSchema);
41         this.delegate = requireNonNull(delegate);
42     }
43
44     static BindingToNormalizedStreamWriter create(final NodeCodecContext<?> schema,
45             final NormalizedNodeStreamWriter delegate) {
46         return new BindingToNormalizedStreamWriter(schema, delegate);
47     }
48
49     private void emitSchema(final Object schemaNode) {
50         delegate.nextDataSchemaNode((DataSchemaNode) schemaNode);
51     }
52
53     NodeCodecContext<?> current() {
54         return schema.peek();
55     }
56
57     private NodeIdentifier duplicateSchemaEnter() {
58         final NodeCodecContext<?> next;
59         if (current() == null) {
60             // Entry of first node
61             next = rootNodeSchema;
62         } else {
63             next = current();
64         }
65         this.schema.push(next);
66         return (NodeIdentifier) current().getDomPathArgument();
67     }
68
69     @SuppressWarnings({"unchecked", "rawtypes"})
70     private <T extends YangInstanceIdentifier.PathArgument> T enter(final Class<?> name, final Class<T> identifier) {
71         final NodeCodecContext<?> next;
72         if (current() == null) {
73             // Entry of first node
74             next = rootNodeSchema;
75         } else {
76             Preconditions.checkArgument(current() instanceof DataContainerCodecContext, "Could not start node %s",
77                     name);
78             next = ((DataContainerCodecContext<?,?>) current()).streamChild((Class) name);
79         }
80         this.schema.push(next);
81         T arg = (T) next.getDomPathArgument();
82         return arg;
83     }
84
85     private <T extends YangInstanceIdentifier.PathArgument> T enter(final String localName, final Class<T> identifier) {
86         NodeCodecContext<?> current = current();
87         NodeCodecContext<?> next = ((DataObjectCodecContext<?,?>) current).getLeafChild(localName);
88         this.schema.push(next);
89         @SuppressWarnings("unchecked")
90         T arg = (T) next.getDomPathArgument();
91         return arg;
92     }
93
94     @Override
95     public NormalizedNodeStreamWriter getDelegate() {
96         return delegate;
97     }
98
99     @Override
100     public void endNode() throws IOException {
101         NodeCodecContext<?> left = schema.pop();
102         // NormalizedNode writer does not have entry into case, but into choice
103         // so for leaving case, we do not emit endNode.
104         if (!(left instanceof CaseNodeCodecContext)) {
105             getDelegate().endNode();
106         }
107     }
108
109     private Map.Entry<NodeIdentifier, Object> serializeLeaf(final String localName, final Object value) {
110         Preconditions.checkArgument(current() instanceof DataObjectCodecContext);
111
112         DataObjectCodecContext<?,?> currentCasted = (DataObjectCodecContext<?,?>) current();
113         LeafNodeCodecContext<?> leafContext = currentCasted.getLeafChild(localName);
114
115         NodeIdentifier domArg = (NodeIdentifier) leafContext.getDomPathArgument();
116         Object domValue = leafContext.getValueCodec().serialize(value);
117         emitSchema(leafContext.getSchema());
118         return new AbstractMap.SimpleEntry<>(domArg, domValue);
119     }
120
121     @Override
122     public void leafNode(final String localName, final Object value) throws IOException {
123         Entry<NodeIdentifier, Object> dom = serializeLeaf(localName, value);
124         getDelegate().leafNode(dom.getKey(), dom.getValue());
125     }
126
127     @Override
128     public void anyxmlNode(final String name, final Object value) throws IOException {
129         Entry<NodeIdentifier, Object> dom = serializeLeaf(name, value);
130         getDelegate().anyxmlNode(dom.getKey(), dom.getValue());
131     }
132
133     @Override
134     public void leafSetEntryNode(final Object value) throws IOException {
135         LeafNodeCodecContext<?> ctx = (LeafNodeCodecContext<?>) current();
136         getDelegate().leafSetEntryNode(ctx.getSchema().getQName(),
137             ctx.getValueCodec().serialize(value));
138     }
139
140     @Override
141     public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType)
142             throws IOException {
143         getDelegate().startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class));
144     }
145
146     @Override
147     public void startCase(final Class<? extends DataObject> caze, final int childSizeHint) {
148         enter(caze, NodeIdentifier.class);
149     }
150
151     @Override
152     public void startChoiceNode(final Class<? extends DataContainer> type, final int childSizeHint)
153             throws IOException {
154         getDelegate().startChoiceNode(enter(type, NodeIdentifier.class), childSizeHint);
155     }
156
157     @Override
158     public void startContainerNode(final Class<? extends DataObject> object, final int childSizeHint)
159             throws IOException {
160         getDelegate().startContainerNode(enter(object, NodeIdentifier.class), childSizeHint);
161     }
162
163     @Override
164     public void startLeafSet(final String localName, final int childSizeHint) throws IOException {
165         final NodeIdentifier id = enter(localName, NodeIdentifier.class);
166         emitSchema(current().getSchema());
167         getDelegate().startLeafSet(id, childSizeHint);
168     }
169
170     @Override
171     public void startOrderedLeafSet(final String localName, final int childSizeHint) throws IOException {
172         getDelegate().startOrderedLeafSet(enter(localName, NodeIdentifier.class), childSizeHint);
173     }
174
175     @Override
176     public void startMapEntryNode(final Identifier<?> key, final int childSizeHint) throws IOException {
177         duplicateSchemaEnter();
178         NodeIdentifierWithPredicates identifier = ((KeyedListNodeCodecContext<?>) current()).serialize(key);
179         getDelegate().startMapEntryNode(identifier, childSizeHint);
180     }
181
182     @Override
183     public <T extends DataObject & Identifiable<?>> void startMapNode(final Class<T> mapEntryType,
184             final int childSizeHint) throws IOException {
185         getDelegate().startMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
186     }
187
188     @Override
189     public <T extends DataObject & Identifiable<?>> void startOrderedMapNode(final Class<T> mapEntryType,
190             final int childSizeHint) throws IOException {
191         getDelegate().startOrderedMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
192     }
193
194     @Override
195     public void startUnkeyedList(final Class<? extends DataObject> obj, final int childSizeHint) throws IOException {
196         getDelegate().startUnkeyedList(enter(obj, NodeIdentifier.class), childSizeHint);
197     }
198
199     @Override
200     public void startUnkeyedListItem(final int childSizeHint) throws IOException {
201         getDelegate().startUnkeyedListItem(duplicateSchemaEnter(), childSizeHint);
202     }
203
204     @Override
205     public void flush() throws IOException {
206         getDelegate().flush();
207     }
208
209     @Override
210     public void close() throws IOException {
211         getDelegate().close();
212     }
213 }