23654dbb6d18a5024197781864320f128b9e8a40
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / CompositeNodeDataWithSchema.java
1 /*
2  * Copyright (c) 2014 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.codec.gson;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ArrayListMultimap;
12 import com.google.common.collect.Multimap;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Deque;
17 import java.util.List;
18 import java.util.Map.Entry;
19 import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter;
20 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
21 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
23 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
24 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30
31 /**
32  * A node which is composed of multiple simpler nodes.
33  */
34 class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
35
36     /**
37      * nodes which were added to schema via augmentation and are present in data input
38      */
39     private final Multimap<AugmentationSchema, AbstractNodeDataWithSchema> augmentationsToChild = ArrayListMultimap.create();
40
41     /**
42      * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
43      */
44     private final List<AbstractNodeDataWithSchema> children = new ArrayList<>();
45
46     public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
47         super(schema);
48     }
49
50     public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
51         Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
52
53         // Pop the first node...
54         final DataSchemaNode schema = schemas.pop();
55         if (schemas.isEmpty()) {
56             // Simple, direct node
57             return addChild(schema);
58         }
59
60         // The choice/case mess, reuse what we already popped
61         final DataSchemaNode choiceCandidate = schema;
62         Preconditions.checkArgument(choiceCandidate instanceof ChoiceSchemaNode,
63             "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName());
64         final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
65
66         final DataSchemaNode caseCandidate = schemas.pop();
67         Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode,
68             "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName());
69         final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate;
70
71         AugmentationSchema augSchema = null;
72         if (choiceCandidate.isAugmenting()) {
73             augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), choiceCandidate);
74         }
75
76         // looking for existing choice
77         final Collection<AbstractNodeDataWithSchema> childNodes;
78         if (augSchema != null) {
79             childNodes = augmentationsToChild.get(augSchema);
80         } else {
81             childNodes = children;
82         }
83
84         CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
85         if (caseNodeDataWithSchema == null) {
86             ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
87             childNodes.add(choiceNodeDataWithSchema);
88             caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
89         }
90
91         return caseNodeDataWithSchema.addChild(schemas);
92     }
93
94     private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
95         SimpleNodeDataWithSchema newChild = null;
96         if (schema instanceof LeafSchemaNode) {
97             newChild = new LeafNodeDataWithSchema(schema);
98         } else if (schema instanceof AnyXmlSchemaNode) {
99             newChild = new AnyXmlNodeDataWithSchema(schema);
100         } else {
101             return null;
102         }
103
104         AugmentationSchema augSchema = null;
105         if (schema.isAugmenting()) {
106             augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), schema);
107         }
108         if (augSchema != null) {
109             augmentationsToChild.put(augSchema, newChild);
110         } else {
111             addChild(newChild);
112         }
113         return newChild;
114     }
115
116     private static CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema> childNodes,
117             final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) {
118         if (childNodes != null) {
119             for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
120                 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
121                         && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
122                     CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
123
124                     Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
125                         "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.",
126                         caseCandidate.getQName(), casePrevious.getSchema().getQName());
127
128                     return casePrevious;
129                 }
130             }
131         }
132         return null;
133     }
134
135     AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
136         CompositeNodeDataWithSchema newChild;
137         if (schema instanceof ListSchemaNode) {
138             newChild = new ListNodeDataWithSchema(schema);
139         } else if (schema instanceof LeafListSchemaNode) {
140             newChild = new LeafListNodeDataWithSchema(schema);
141         } else if (schema instanceof ContainerSchemaNode) {
142             newChild = new ContainerNodeDataWithSchema(schema);
143         } else {
144             newChild = new CompositeNodeDataWithSchema(schema);
145         }
146         addCompositeChild(newChild);
147         return newChild;
148     }
149
150     void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
151         AugmentationSchema augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), newChild.getSchema());
152         if (augSchema != null) {
153             augmentationsToChild.put(augSchema, newChild);
154         } else {
155             addChild(newChild);
156         }
157     }
158
159     private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
160         AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
161         return newChild == null ? addCompositeChild(schema) : newChild;
162     }
163
164     public void addChild(final AbstractNodeDataWithSchema newChild) {
165         children.add(newChild);
166     }
167
168     /**
169      * Return a hint about how may children we are going to generate.
170      * @return Size of currently-present node list.
171      */
172     protected final int childSizeHint() {
173         return children.size();
174     }
175
176     @Override
177     public void write(final SchemaAwareNormalizedNodeStreamWriter writer) throws IOException {
178         for (AbstractNodeDataWithSchema child : children) {
179             child.write(writer);
180         }
181         for (Entry<AugmentationSchema, Collection<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.asMap().entrySet()) {
182             final Collection<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
183             if (!childsFromAgumentation.isEmpty()) {
184                 // FIXME: can we get the augmentation schema?
185                 writer.startAugmentationNode(SchemaUtils.getNodeIdentifierForAugmentation(augmentationToChild.getKey()));
186
187                 for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
188                     nodeDataWithSchema.write(writer);
189                 }
190
191                 writer.endNode();
192             }
193         }
194     }
195 }