2 * Copyright (c) 2016 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.yangtools.yang.data.impl.schema;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ArrayListMultimap;
13 import com.google.common.collect.Multimap;
14 import java.io.IOException;
15 import java.util.Collection;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
20 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
23 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
24 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
28 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
31 * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, this iterates over elements
32 * in the order as they are defined in YANG file.
34 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
35 private final EffectiveModelContext modelContext;
36 private final SchemaNode root;
38 private SchemaNode currentSchemaNode;
41 * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
43 * @param writer Back-end writer
44 * @param modelContext Associated {@link EffectiveModelContext}
46 public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
47 final EffectiveModelContext modelContext) {
49 root = this.modelContext = requireNonNull(modelContext);
52 private SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
53 final SchemaInferenceStack stack) {
55 modelContext = stack.modelContext();
57 if (!stack.isEmpty()) {
58 final var current = stack.currentStatement();
59 // FIXME: this should be one of NormalizedNodeContainer/NotificationDefinition/OperationDefinition
60 if (current instanceof SchemaNode schemaNode) {
63 throw new IllegalArgumentException("Instantiating at " + current + " is not supported");
71 * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
73 * @param writer Back-end writer
74 * @param schemaContext Associated {@link EffectiveModelContext}
75 * @param path root path
77 public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
78 final EffectiveModelContext schemaContext, final Absolute path) {
79 this(writer, SchemaInferenceStack.of(schemaContext, path));
83 * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
85 * @param writer Back-end writer
86 * @param rootInference A SchemaTreeInference pointing to the root element
88 public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
89 final SchemaTreeInference rootInference) {
90 this(writer, SchemaInferenceStack.ofInference(rootInference));
94 public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node) throws IOException {
95 if (modelContext.equals(root)) {
96 currentSchemaNode = modelContext.dataChildByName(node.name().getNodeType());
98 currentSchemaNode = root;
100 return write(node, currentSchemaNode);
104 * Iterate over the provided collection and emit write
105 * events to the encapsulated {@link NormalizedNodeStreamWriter}.
108 * @return NormalizedNodeWriter this
109 * @throws IOException when thrown from the backing writer.
111 public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild> nodes) throws IOException {
112 currentSchemaNode = root;
113 if (writeChildren(nodes, currentSchemaNode, false)) {
117 throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
120 private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode node, final SchemaNode dataSchemaNode)
123 //Set current schemaNode
124 try (var sns = new SchemaNodeSetter(dataSchemaNode)) {
129 if (wasProcessedAsCompositeNode(node)) {
133 if (wasProcessAsSimpleNode(node)) {
138 throw new IllegalStateException("It wasn't possible to serialize node " + node);
141 private void write(final Collection<NormalizedNode> nodes, final SchemaNode dataSchemaNode) throws IOException {
142 for (var node : nodes) {
143 write(node, dataSchemaNode);
148 protected boolean writeChildren(final Iterable<? extends NormalizedNode> children) throws IOException {
149 return writeChildren(children, currentSchemaNode, true);
152 private boolean writeChildren(final Iterable<? extends NormalizedNode> children, final SchemaNode parentSchemaNode,
153 final boolean endParent) throws IOException {
154 // Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
155 final var qnameToNodes = ArrayListMultimap.<QName, NormalizedNode>create();
156 for (var child : children) {
157 putChild(qnameToNodes, child);
160 if (parentSchemaNode instanceof DataNodeContainer parentContainer) {
161 if (parentContainer instanceof ListSchemaNode && qnameToNodes.containsKey(parentSchemaNode.getQName())) {
162 write(qnameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
164 for (var schemaNode : parentContainer.getChildNodes()) {
165 write(qnameToNodes.get(schemaNode.getQName()), schemaNode);
168 } else if (parentSchemaNode instanceof ChoiceSchemaNode parentChoice) {
169 for (var childCase : parentChoice.getCases()) {
170 for (var childCaseChild : childCase.getChildNodes()) {
171 final var node = qnameToNodes.asMap().get(childCaseChild.getQName());
173 write(node, childCaseChild);
178 for (var child : children) {
183 getWriter().endNode();
188 private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode node) throws IOException {
189 if (wasProcessAsSimpleNode(node)) {
193 throw new IllegalStateException("It wasn't possible to serialize node " + node);
196 private static void putChild(final Multimap<QName, NormalizedNode> qnameToNodes, final NormalizedNode child) {
197 qnameToNodes.put(child.name().getNodeType(), child);
200 private final class SchemaNodeSetter implements AutoCloseable {
201 private final SchemaNode previousSchemaNode;
204 * Sets current schema node new value and store old value for later restore.
206 SchemaNodeSetter(final SchemaNode schemaNode) {
207 previousSchemaNode = currentSchemaNode;
208 currentSchemaNode = schemaNode;
212 * Restore previous schema node.
215 public void close() {
216 currentSchemaNode = previousSchemaNode;