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.yangtools.yang.data.codec.gson;
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.Collections2;
13 import com.google.common.collect.ImmutableSet;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Deque;
19 import java.util.LinkedHashMap;
20 import java.util.List;
22 import java.util.Map.Entry;
24 import javax.annotation.Nonnull;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
29 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
31 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
33 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
34 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
41 * A node which is composed of multiple simpler nodes.
43 class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
44 private static final Function<DataSchemaNode, QName> QNAME_FUNCTION = new Function<DataSchemaNode, QName>() {
46 public QName apply(@Nonnull final DataSchemaNode input) {
47 return input.getQName();
52 * nodes which were added to schema via augmentation and are present in data input
54 private final Map<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationsToChild = new LinkedHashMap<>();
57 * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
59 private final List<AbstractNodeDataWithSchema> children = new ArrayList<>();
61 public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
65 public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
66 Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
68 // Pop the first node...
69 final DataSchemaNode schema = schemas.pop();
70 if (schemas.isEmpty()) {
71 // Simple, direct node
72 return addChild(schema);
75 // The choice/case mess, reuse what we already popped
76 final DataSchemaNode choiceCandidate = schema;
77 Preconditions.checkArgument(choiceCandidate instanceof ChoiceNode,
78 "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName());
79 final ChoiceNode choiceNode = (ChoiceNode) choiceCandidate;
81 final DataSchemaNode caseCandidate = schemas.pop();
82 Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode,
83 "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName());
84 final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate;
86 AugmentationSchema augSchema = null;
87 if (choiceCandidate.isAugmenting()) {
88 augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
91 // looking for existing choice
92 final List<AbstractNodeDataWithSchema> childNodes;
93 if (augSchema != null) {
94 childNodes = augmentationsToChild.get(augSchema);
96 childNodes = children;
99 CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
100 if (caseNodeDataWithSchema == null) {
101 ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
102 addChild(choiceNodeDataWithSchema);
103 caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
106 return caseNodeDataWithSchema.addChild(schemas);
109 private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
110 SimpleNodeDataWithSchema newChild = null;
111 if (schema instanceof LeafSchemaNode) {
112 newChild = new LeafNodeDataWithSchema(schema);
113 } else if (schema instanceof AnyXmlSchemaNode) {
114 newChild = new AnyXmlNodeDataWithSchema(schema);
117 if (newChild != null) {
119 AugmentationSchema augSchema = null;
120 if (schema.isAugmenting()) {
121 augSchema = findCorrespondingAugment(getSchema(), schema);
123 if (augSchema != null) {
124 addChildToAugmentation(augSchema, newChild);
133 private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) {
134 List<AbstractNodeDataWithSchema> childsInAugment = augmentationsToChild.get(augSchema);
135 if (childsInAugment == null) {
136 childsInAugment = new ArrayList<>();
137 augmentationsToChild.put(augSchema, childsInAugment);
139 childsInAugment.add(newChild);
142 private CaseNodeDataWithSchema findChoice(final List<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
143 final DataSchemaNode caseCandidate) {
144 if (childNodes != null) {
145 for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
146 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
147 && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
148 CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
150 Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
151 "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.",
152 caseCandidate.getQName(), casePrevious.getSchema().getQName());
161 AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
162 CompositeNodeDataWithSchema newChild;
163 if (schema instanceof ListSchemaNode) {
164 newChild = new ListNodeDataWithSchema(schema);
165 } else if (schema instanceof LeafListSchemaNode) {
166 newChild = new LeafListNodeDataWithSchema(schema);
167 } else if (schema instanceof ContainerSchemaNode) {
168 newChild = new ContainerNodeDataWithSchema(schema);
170 newChild = new CompositeNodeDataWithSchema(schema);
172 addCompositeChild(newChild);
176 void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
177 AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
178 if (augSchema != null) {
179 addChildToAugmentation(augSchema, newChild);
185 private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
186 AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
187 return newChild == null ? addCompositeChild(schema) : newChild;
190 public void addChild(final AbstractNodeDataWithSchema newChild) {
191 children.add(newChild);
195 * Return a hint about how may children we are going to generate.
196 * @return Size of currently-present node list.
198 protected final int childSizeHint() {
199 return children.size();
203 * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
204 * node is found then it is returned, else null.
206 AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
207 if (parent instanceof AugmentationTarget) {
208 for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
209 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
210 if (childInAugmentation != null) {
219 public void write(final NormalizedNodeStreamWriter writer) throws IOException {
220 for (AbstractNodeDataWithSchema child : children) {
223 for (Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.entrySet()) {
224 final List<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
225 if (!childsFromAgumentation.isEmpty()) {
226 writer.startAugmentationNode(toAugmentationIdentifier(augmentationToChild.getKey()));
228 for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
229 nodeDataWithSchema.write(writer);
237 private static AugmentationIdentifier toAugmentationIdentifier(final AugmentationSchema schema) {
238 final Collection<QName> qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION);
239 return new AugmentationIdentifier(ImmutableSet.copyOf(qnames));