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.util;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.collect.ArrayListMultimap;
13 import com.google.common.collect.Multimap;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Deque;
18 import java.util.List;
19 import java.util.Map.Entry;
20 import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
21 import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
23 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
27 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 * Utility class used for tracking parser state as needed by a StAX-like parser.
37 * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
40 * Represents a node which is composed of multiple simpler nodes.
42 public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends AbstractNodeDataWithSchema<T> {
44 * nodes which were added to schema via augmentation and are present in data input.
46 private final Multimap<AugmentationSchemaNode, AbstractNodeDataWithSchema<?>> augmentationsToChild =
47 ArrayListMultimap.create();
50 * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
52 private final List<AbstractNodeDataWithSchema<?>> children = new ArrayList<>();
54 public CompositeNodeDataWithSchema(final T schema) {
58 private AbstractNodeDataWithSchema<?> addChild(final DataSchemaNode schema) {
59 AbstractNodeDataWithSchema<?> newChild = addSimpleChild(schema);
60 return newChild == null ? addCompositeChild(schema) : newChild;
63 public void addChild(final AbstractNodeDataWithSchema<?> newChild) {
64 children.add(newChild);
67 public AbstractNodeDataWithSchema<?> addChild(final Deque<DataSchemaNode> schemas) {
68 checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
70 // Pop the first node...
71 final DataSchemaNode schema = schemas.pop();
72 if (schemas.isEmpty()) {
73 // Simple, direct node
74 return addChild(schema);
77 // The choice/case mess, reuse what we already popped
78 final DataSchemaNode choiceCandidate = schema;
79 checkArgument(choiceCandidate instanceof ChoiceSchemaNode, "Expected node of type ChoiceNode but was %s",
80 choiceCandidate.getClass());
81 final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
83 final DataSchemaNode caseCandidate = schemas.pop();
84 checkArgument(caseCandidate instanceof CaseSchemaNode, "Expected node of type ChoiceCaseNode but was %s",
85 caseCandidate.getClass());
86 final CaseSchemaNode caseNode = (CaseSchemaNode) caseCandidate;
88 AugmentationSchemaNode augSchema = null;
89 if (choiceCandidate.isAugmenting()) {
90 augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
93 // looking for existing choice
94 final Collection<AbstractNodeDataWithSchema<?>> childNodes;
95 if (augSchema != null) {
96 childNodes = augmentationsToChild.get(augSchema);
98 childNodes = children;
101 CompositeNodeDataWithSchema<?> caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
102 if (caseNodeDataWithSchema == null) {
103 ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
104 childNodes.add(choiceNodeDataWithSchema);
105 caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
108 return caseNodeDataWithSchema.addChild(schemas);
111 private AbstractNodeDataWithSchema<?> addSimpleChild(final DataSchemaNode schema) {
112 SimpleNodeDataWithSchema<?> newChild = null;
113 if (schema instanceof LeafSchemaNode) {
114 newChild = new LeafNodeDataWithSchema((LeafSchemaNode) schema);
115 } else if (schema instanceof AnyxmlSchemaNode) {
116 // YangModeledAnyxmlSchemaNode is handled by addCompositeChild method.
117 if (schema instanceof YangModeledAnyxmlSchemaNode) {
120 newChild = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) schema);
121 } else if (schema instanceof AnydataSchemaNode) {
122 newChild = new AnydataNodeDataWithSchema((AnydataSchemaNode) schema);
127 AugmentationSchemaNode augSchema = null;
128 if (schema.isAugmenting()) {
129 augSchema = findCorrespondingAugment(getSchema(), schema);
131 if (augSchema != null) {
132 augmentationsToChild.put(augSchema, newChild);
139 private static CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema<?>> childNodes,
140 final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) {
141 if (childNodes != null) {
142 for (AbstractNodeDataWithSchema<?> nodeDataWithSchema : childNodes) {
143 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
144 && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
145 CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
147 checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
148 "Data from case %s are specified but other data from case %s were specified earlier."
149 + " Data aren't from the same case.", caseCandidate.getQName(),
150 casePrevious.getSchema().getQName());
159 AbstractNodeDataWithSchema<?> addCompositeChild(final DataSchemaNode schema) {
160 final CompositeNodeDataWithSchema<?> newChild;
162 if (schema instanceof ListSchemaNode) {
163 newChild = new ListNodeDataWithSchema((ListSchemaNode) schema);
164 } else if (schema instanceof LeafListSchemaNode) {
165 newChild = new LeafListNodeDataWithSchema((LeafListSchemaNode) schema);
166 } else if (schema instanceof ContainerSchemaNode) {
167 newChild = new ContainerNodeDataWithSchema((ContainerSchemaNode) schema);
168 } else if (schema instanceof YangModeledAnyxmlSchemaNode) {
169 newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyxmlSchemaNode)schema);
171 newChild = new CompositeNodeDataWithSchema<>(schema);
174 addCompositeChild(newChild);
178 void addCompositeChild(final CompositeNodeDataWithSchema<?> newChild) {
179 AugmentationSchemaNode augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
180 if (augSchema != null) {
181 augmentationsToChild.put(augSchema, newChild);
188 * Return a hint about how may children we are going to generate.
189 * @return Size of currently-present node list.
191 protected final int childSizeHint() {
192 return children.size();
196 public void write(final NormalizedNodeStreamWriter writer, final NormalizedMetadataStreamWriter metaWriter)
198 for (AbstractNodeDataWithSchema<?> child : children) {
199 child.write(writer, metaWriter);
201 for (Entry<AugmentationSchemaNode, Collection<AbstractNodeDataWithSchema<?>>> augmentationToChild
202 : augmentationsToChild.asMap().entrySet()) {
203 final Collection<AbstractNodeDataWithSchema<?>> childsFromAgumentation = augmentationToChild.getValue();
204 if (!childsFromAgumentation.isEmpty()) {
205 // FIXME: can we get the augmentation schema?
206 writer.startAugmentationNode(DataSchemaContextNode.augmentationIdentifierFrom(
207 augmentationToChild.getKey()));
209 for (AbstractNodeDataWithSchema<?> nodeDataWithSchema : childsFromAgumentation) {
210 nodeDataWithSchema.write(writer, metaWriter);
219 * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
220 * node is found then it is returned, else null.
222 * @param parent parent node
223 * @param child child node
224 * @return augmentation schema
226 private static AugmentationSchemaNode findCorrespondingAugment(final DataSchemaNode parent,
227 final DataSchemaNode child) {
228 if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
229 for (AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
230 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
231 if (childInAugmentation != null) {