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.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 javax.annotation.Nonnull;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
26 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 * A node which is composed of multiple simpler nodes.
40 class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
41 private static final Function<DataSchemaNode, QName> QNAME_FUNCTION = new Function<DataSchemaNode, QName>() {
43 public QName apply(@Nonnull final DataSchemaNode input) {
44 return input.getQName();
49 * nodes which were added to schema via augmentation and are present in data input
51 private final Multimap<AugmentationSchema, AbstractNodeDataWithSchema> augmentationsToChild = ArrayListMultimap.create();
54 * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
56 private final List<AbstractNodeDataWithSchema> children = new ArrayList<>();
58 public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
62 public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas, final boolean rootListItem) {
63 Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
65 // Pop the first node...
66 final DataSchemaNode schema = schemas.pop();
67 if (schemas.isEmpty()) {
68 // Simple, direct node
69 return addChild(schema,rootListItem);
72 // The choice/case mess, reuse what we already popped
73 final DataSchemaNode choiceCandidate = schema;
74 Preconditions.checkArgument(choiceCandidate instanceof ChoiceSchemaNode,
75 "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName());
76 final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
78 final DataSchemaNode caseCandidate = schemas.pop();
79 Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode,
80 "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName());
81 final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate;
83 AugmentationSchema augSchema = null;
84 if (choiceCandidate.isAugmenting()) {
85 augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
88 // looking for existing choice
89 final Collection<AbstractNodeDataWithSchema> childNodes;
90 if (augSchema != null) {
91 childNodes = augmentationsToChild.get(augSchema);
93 childNodes = children;
96 CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
97 if (caseNodeDataWithSchema == null) {
98 final ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
99 addChild(choiceNodeDataWithSchema);
100 caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode,rootListItem);
103 return caseNodeDataWithSchema.addChild(schemas, rootListItem);
106 private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
107 SimpleNodeDataWithSchema newChild = null;
108 if (schema instanceof LeafSchemaNode) {
109 newChild = new LeafNodeDataWithSchema(schema);
110 } else if (schema instanceof AnyXmlSchemaNode) {
111 newChild = new AnyXmlNodeDataWithSchema(schema);
116 AugmentationSchema augSchema = null;
117 if (schema.isAugmenting()) {
118 augSchema = findCorrespondingAugment(getSchema(), schema);
120 if (augSchema != null) {
121 augmentationsToChild.put(augSchema, newChild);
128 private CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
129 final DataSchemaNode caseCandidate) {
130 if (childNodes != null) {
131 for (final AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
132 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
133 && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
134 final CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
136 Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
137 "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.",
138 caseCandidate.getQName(), casePrevious.getSchema().getQName());
147 AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema, final boolean rootListItem) {
148 CompositeNodeDataWithSchema newChild;
149 if (schema instanceof ListSchemaNode) {
150 newChild = new ListNodeDataWithSchema(schema);
152 * If we are reading root we may want to emit map also for object which represent one list
156 addCompositeChild(newChild);
157 final ListEntryNodeDataWithSchema entry = new ListEntryNodeDataWithSchema(schema);
158 newChild.addChild(entry);
161 } else if (schema instanceof LeafListSchemaNode) {
162 newChild = new LeafListNodeDataWithSchema(schema);
163 } else if (schema instanceof ContainerSchemaNode) {
164 newChild = new ContainerNodeDataWithSchema(schema);
166 newChild = new CompositeNodeDataWithSchema(schema);
168 addCompositeChild(newChild);
172 void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
173 final AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
174 if (augSchema != null) {
175 augmentationsToChild.put(augSchema, newChild);
181 private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema, final boolean rootListItem) {
182 final AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
183 return newChild == null ? addCompositeChild(schema,rootListItem) : newChild;
186 public void addChild(final AbstractNodeDataWithSchema newChild) {
187 children.add(newChild);
191 * Return a hint about how may children we are going to generate.
192 * @return Size of currently-present node list.
194 protected final int childSizeHint() {
195 return children.size();
199 * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
200 * node is found then it is returned, else null.
202 AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
203 if (parent instanceof AugmentationTarget && !((parent instanceof ChoiceCaseNode) || (parent instanceof ChoiceSchemaNode))) {
204 for (final AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
205 final DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
206 if (childInAugmentation != null) {
215 public void write(final NormalizedNodeStreamWriter writer) throws IOException {
216 for (final AbstractNodeDataWithSchema child : children) {
219 for (final Entry<AugmentationSchema, Collection<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.asMap().entrySet()) {
220 final Collection<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
221 if (!childsFromAgumentation.isEmpty()) {
222 writer.startAugmentationNode(toAugmentationIdentifier(augmentationToChild.getKey()));
224 for (final AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
225 nodeDataWithSchema.write(writer);
233 private static AugmentationIdentifier toAugmentationIdentifier(final AugmentationSchema schema) {
234 final Collection<QName> qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION);
235 return new AugmentationIdentifier(ImmutableSet.copyOf(qnames));