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 com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ArrayListMultimap;
13 import com.google.common.collect.Collections2;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Multimap;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Deque;
20 import java.util.List;
21 import java.util.Map.Entry;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
25 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode;
38 * A node which is composed of multiple simpler nodes.
40 public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
43 * nodes which were added to schema via augmentation and are present in data input
45 private final Multimap<AugmentationSchema, AbstractNodeDataWithSchema> augmentationsToChild = ArrayListMultimap.create();
48 * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
50 private final List<AbstractNodeDataWithSchema> children = new ArrayList<>();
52 public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
56 public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
57 Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
59 // Pop the first node...
60 final DataSchemaNode schema = schemas.pop();
61 if (schemas.isEmpty()) {
62 // Simple, direct node
63 return addChild(schema);
66 // The choice/case mess, reuse what we already popped
67 final DataSchemaNode choiceCandidate = schema;
68 Preconditions.checkArgument(choiceCandidate instanceof ChoiceSchemaNode,
69 "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName());
70 final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
72 final DataSchemaNode caseCandidate = schemas.pop();
73 Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode,
74 "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName());
75 final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate;
77 AugmentationSchema augSchema = null;
78 if (choiceCandidate.isAugmenting()) {
79 augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
82 // looking for existing choice
83 final Collection<AbstractNodeDataWithSchema> childNodes;
84 if (augSchema != null) {
85 childNodes = augmentationsToChild.get(augSchema);
87 childNodes = children;
90 CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
91 if (caseNodeDataWithSchema == null) {
92 ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
93 childNodes.add(choiceNodeDataWithSchema);
94 caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
97 return caseNodeDataWithSchema.addChild(schemas);
100 private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
101 SimpleNodeDataWithSchema newChild = null;
102 if (schema instanceof LeafSchemaNode) {
103 newChild = new LeafNodeDataWithSchema(schema);
104 } else if (schema instanceof AnyXmlSchemaNode) {
105 // YangModeledAnyXmlSchemaNode is handled by addCompositeChild method.
106 if (schema instanceof YangModeledAnyXmlSchemaNode) {
109 newChild = new AnyXmlNodeDataWithSchema(schema);
114 AugmentationSchema augSchema = null;
115 if (schema.isAugmenting()) {
116 augSchema = findCorrespondingAugment(getSchema(), schema);
118 if (augSchema != null) {
119 augmentationsToChild.put(augSchema, newChild);
126 private static CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema> childNodes,
127 final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) {
128 if (childNodes != null) {
129 for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
130 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
131 && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
132 CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
134 Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
135 "Data from case %s are specified but other data from case %s were specified earlier. Data aren't from the same case.",
136 caseCandidate.getQName(), casePrevious.getSchema().getQName());
145 AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
146 final CompositeNodeDataWithSchema newChild;
148 if (schema instanceof ListSchemaNode) {
149 newChild = new ListNodeDataWithSchema(schema);
150 } else if (schema instanceof LeafListSchemaNode) {
151 newChild = new LeafListNodeDataWithSchema(schema);
152 } else if (schema instanceof ContainerSchemaNode) {
153 newChild = new ContainerNodeDataWithSchema(schema);
154 } else if (schema instanceof YangModeledAnyXmlSchemaNode) {
155 newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyXmlSchemaNode)schema);
157 newChild = new CompositeNodeDataWithSchema(schema);
160 addCompositeChild(newChild);
164 void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
165 AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
166 if (augSchema != null) {
167 augmentationsToChild.put(augSchema, newChild);
173 private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
174 AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
175 return newChild == null ? addCompositeChild(schema) : newChild;
178 public void addChild(final AbstractNodeDataWithSchema newChild) {
179 children.add(newChild);
183 * Return a hint about how may children we are going to generate.
184 * @return Size of currently-present node list.
186 protected final int childSizeHint() {
187 return children.size();
191 public void write(final NormalizedNodeStreamWriter writer) throws IOException {
192 for (AbstractNodeDataWithSchema child : children) {
195 for (Entry<AugmentationSchema, Collection<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.asMap().entrySet()) {
196 final Collection<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
197 if (!childsFromAgumentation.isEmpty()) {
198 // FIXME: can we get the augmentation schema?
199 writer.startAugmentationNode(getNodeIdentifierForAugmentation(augmentationToChild.getKey()));
201 for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
202 nodeDataWithSchema.write(writer);
211 * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
212 * node is found then it is returned, else null.
214 * @param parent parent node
215 * @param child child node
216 * @return augmentation schema
218 private static AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
219 if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
220 for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
221 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
222 if (childInAugmentation != null) {
230 public static YangInstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) {
231 final Collection<QName> qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION);
232 return new YangInstanceIdentifier.AugmentationIdentifier(ImmutableSet.copyOf(qnames));
235 private static final Function<DataSchemaNode, QName> QNAME_FUNCTION = input -> input.getQName();