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.mdsal.binding.dom.codec.impl;
10 import static java.util.Objects.requireNonNull;
12 import java.io.IOException;
13 import java.util.AbstractMap;
14 import java.util.ArrayDeque;
15 import java.util.Deque;
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;
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;
40 BindingToNormalizedStreamWriter(final DataContainerCodecContext<?, ?, ?> rootContext,
41 final NormalizedNodeStreamWriter delegate) {
42 this.rootContext = requireNonNull(rootContext);
43 this.delegate = requireNonNull(delegate);
46 private void emitSchema(final Object schemaNode) {
47 delegate.nextDataSchemaNode((DataSchemaNode) schemaNode);
50 CodecContext current() {
54 private NodeIdentifier duplicateSchemaEnter() {
55 final var current = current();
56 final CodecContext next;
57 if (current == null) {
58 // Entry of first node
64 return next.getDomPathArgument();
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
74 } else if (current instanceof DataContainerCodecContext<?, ?, ?> currentContainer) {
75 next = currentContainer.getStreamChild((Class) name);
77 throw new IllegalArgumentException("Could not start node " + name + " in non-container " + current);
80 return identifier.cast(next.getDomPathArgument());
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);
87 return identifier.cast(next.getDomPathArgument());
91 public NormalizedNodeStreamWriter getDelegate() {
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)) {
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);
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);
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());
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());
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());
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);
155 public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType) throws IOException {
156 enter(augmentationType, NodeIdentifier.class);
160 public void startCase(final Class<? extends DataObject> caze, final int childSizeHint) {
161 enter(caze, NodeIdentifier.class);
165 public void startChoiceNode(final Class<? extends DataContainer> type, final int childSizeHint)
167 delegate.startChoiceNode(enter(type, NodeIdentifier.class), childSizeHint);
171 public void startContainerNode(final Class<? extends DataObject> object, final int childSizeHint)
173 delegate.startContainerNode(enter(object, NodeIdentifier.class), childSizeHint);
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);
184 public void startOrderedLeafSet(final String localName, final int childSizeHint) throws IOException {
185 delegate.startOrderedLeafSet(enter(localName, NodeIdentifier.class), childSizeHint);
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);
196 public <T extends DataObject & KeyAware<?>> void startMapNode(final Class<T> mapEntryType, final int childSizeHint)
198 delegate.startMapNode(enter(mapEntryType, NodeIdentifier.class), childSizeHint);
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);
208 public void startUnkeyedList(final Class<? extends DataObject> obj, final int childSizeHint) throws IOException {
209 delegate.startUnkeyedList(enter(obj, NodeIdentifier.class), childSizeHint);
213 public void startUnkeyedListItem(final int childSizeHint) throws IOException {
214 delegate.startUnkeyedListItem(duplicateSchemaEnter(), childSizeHint);
218 public void flush() throws IOException {
223 public void close() throws IOException {