Migrate getDataChildByName() users
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / SchemaOrderedNormalizedNodeWriter.java
1 /*
2  * Copyright (c) 2016 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.yangtools.yang.data.impl.schema;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ArrayListMultimap;
12 import java.io.IOException;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.Objects;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
22 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
27 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, this iterates over elements
35  * in the order as they are defined in YANG file.
36  */
37 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
38     private static final Logger LOG = LoggerFactory.getLogger(SchemaOrderedNormalizedNodeWriter.class);
39     private final EffectiveModelContext schemaContext;
40     private final SchemaNode root;
41
42     private SchemaNode currentSchemaNode;
43
44     /**
45      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
46      *
47      * @param writer
48      *            Back-end writer
49      * @param schemaContext
50      *            Schema context
51      * @param path
52      *            path
53      */
54     public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer,
55             final EffectiveModelContext schemaContext, final SchemaPath path) {
56         super(writer);
57         this.schemaContext = schemaContext;
58         final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(schemaContext, path);
59         Preconditions.checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s",
60                 path);
61         if (schemaNodes.size() > 1) {
62             LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
63         }
64         this.root = schemaNodes.iterator().next();
65     }
66
67     @Override
68     public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
69         if (Objects.equals(root, schemaContext)) {
70             currentSchemaNode = schemaContext.dataChildByName(node.getNodeType());
71         } else {
72             currentSchemaNode = root;
73         }
74         return write(node, currentSchemaNode);
75     }
76
77     /**
78      * Iterate over the provided collection and emit write
79      * events to the encapsulated {@link NormalizedNodeStreamWriter}.
80      *
81      * @param nodes nodes
82      * @return NormalizedNodeWriter this
83      * @throws IOException when thrown from the backing writer.
84      */
85     public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
86         currentSchemaNode = root;
87         if (writeChildren(nodes, currentSchemaNode, false)) {
88             return this;
89         }
90
91         throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
92     }
93
94     private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode)
95             throws IOException {
96
97         //Set current schemaNode
98         try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
99             if (node == null) {
100                 return this;
101             }
102
103             if (wasProcessedAsCompositeNode(node)) {
104                 return this;
105             }
106
107             if (wasProcessAsSimpleNode(node)) {
108                 return this;
109             }
110         }
111
112         throw new IllegalStateException("It wasn't possible to serialize node " + node);
113     }
114
115     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
116         for (final NormalizedNode<?, ?> node : nodes) {
117             write(node, dataSchemaNode);
118         }
119     }
120
121     @Override
122     protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
123         return writeChildren(children, currentSchemaNode, true);
124     }
125
126     private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
127             final SchemaNode parentSchemaNode, final boolean endParent) throws IOException {
128         //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
129         final ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
130         for (final NormalizedNode<?, ?> child : children) {
131             if (child instanceof AugmentationNode) {
132                 qNameToNodes.putAll(resolveAugmentations(child));
133             } else {
134                 qNameToNodes.put(child.getNodeType(), child);
135             }
136         }
137
138         if (parentSchemaNode instanceof DataNodeContainer) {
139             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
140                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
141             } else {
142                 for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
143                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
144                 }
145             }
146         } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
147             for (final CaseSchemaNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
148                 for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
149                     if (qNameToNodes.containsKey(dsn.getQName())) {
150                         write(qNameToNodes.get(dsn.getQName()), dsn);
151                     }
152                 }
153             }
154         } else {
155             for (final NormalizedNode<?, ?> child : children) {
156                 writeLeaf(child);
157             }
158         }
159         if (endParent) {
160             getWriter().endNode();
161         }
162         return true;
163     }
164
165     private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
166         if (wasProcessAsSimpleNode(node)) {
167             return this;
168         }
169
170         throw new IllegalStateException("It wasn't possible to serialize node " + node);
171     }
172
173     private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
174         final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
175         for (final NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
176             if (node instanceof AugmentationNode) {
177                 resolvedAugs.putAll(resolveAugmentations(node));
178             } else {
179                 resolvedAugs.put(node.getNodeType(), node);
180             }
181         }
182         return resolvedAugs;
183     }
184
185     private final class SchemaNodeSetter implements AutoCloseable {
186
187         private final SchemaNode previousSchemaNode;
188
189         /**
190          * Sets current schema node new value and store old value for later restore.
191          */
192         SchemaNodeSetter(final SchemaNode schemaNode) {
193             previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode;
194             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode;
195         }
196
197         /**
198          * Restore previous schema node.
199          */
200         @Override
201         public void close() {
202             SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode;
203         }
204     }
205 }