Merge "BUG 1440 - json stream to normalized node stream writer"
[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 java.io.IOException;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Deque;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
21
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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.ChoiceNode;
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
36 class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
37
38     /**
39      * nodes which were added to schema via augmentation and are present in data input
40      */
41     protected Map<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationsToChild = new HashMap<>();
42
43     /**
44      * remaining data nodes (which aren't added via augment). Every of them should have the same QName
45      */
46     protected List<AbstractNodeDataWithSchema> childs = new ArrayList<>();
47
48     public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
49         super(schema);
50     }
51
52     public AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
53         SimpleNodeDataWithSchema newChild = null;
54         if (schema instanceof LeafSchemaNode) {
55             newChild = new LeafNodeDataWithSchema(schema);
56         } else if (schema instanceof AnyXmlSchemaNode) {
57             newChild = new AnyXmlNodeDataWithSchema(schema);
58         }
59
60         if (newChild != null) {
61
62             AugmentationSchema augSchema = null;
63             if (schema.isAugmenting()) {
64                 augSchema = findCorrespondingAugment(getSchema(), schema);
65             }
66             if (augSchema != null) {
67                 addChildToAugmentation(augSchema, newChild);
68             } else {
69                 addChild(newChild);
70             }
71             return newChild;
72         }
73         return null;
74     }
75
76     private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) {
77         List<AbstractNodeDataWithSchema> childsInAugment = augmentationsToChild.get(augSchema);
78         if (childsInAugment == null) {
79             childsInAugment = new ArrayList<>();
80             augmentationsToChild.put(augSchema, childsInAugment);
81         }
82         childsInAugment.add(newChild);
83     }
84
85     public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
86         if (schemas.size() == 1) {
87             final DataSchemaNode childDataSchemaNode = schemas.pop();
88             return addChild(childDataSchemaNode);
89         } else {
90             DataSchemaNode choiceCandidate = schemas.pop();
91             DataSchemaNode caseCandidate = schemas.pop();
92             ChoiceNode choiceNode = null;
93             ChoiceCaseNode caseNode = null;
94             if (choiceCandidate instanceof ChoiceNode) {
95                 choiceNode = (ChoiceNode) choiceCandidate;
96             } else {
97                 throw new IllegalArgumentException("Awaited node of type ChoiceNode but was "
98                         + choiceCandidate.getClass().getSimpleName());
99             }
100
101             if (caseCandidate instanceof ChoiceCaseNode) {
102                 caseNode = (ChoiceCaseNode) caseCandidate;
103             } else {
104                 throw new IllegalArgumentException("Awaited node of type ChoiceCaseNode but was "
105                         + caseCandidate.getClass().getSimpleName());
106             }
107
108             AugmentationSchema augSchema = null;
109             if (choiceCandidate.isAugmenting()) {
110                 augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
111             }
112
113             // looking for existing choice
114             List<AbstractNodeDataWithSchema> childNodes = Collections.emptyList();
115             if (augSchema != null) {
116                 childNodes = augmentationsToChild.get(augSchema);
117             } else {
118                 childNodes = childs;
119             }
120
121             CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
122             if (caseNodeDataWithSchema == null) {
123                 ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
124                 addChild(choiceNodeDataWithSchema);
125                 caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
126             }
127
128             return caseNodeDataWithSchema.addChild(schemas);
129         }
130
131     }
132
133     private CaseNodeDataWithSchema findChoice(final List<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
134             final DataSchemaNode caseCandidate) {
135         if (childNodes == null) {
136             return null;
137         }
138         for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
139             if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
140                     && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
141                 CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
142                 if (casePrevious.getSchema().getQName() != caseCandidate.getQName()) {
143                     throw new IllegalArgumentException("Data from case " + caseCandidate.getQName()
144                             + " are specified but other data from case " + casePrevious.getSchema().getQName()
145                             + " were specified erlier. Data aren't from the same case.");
146                 }
147                 return casePrevious;
148             }
149         }
150         return null;
151     }
152
153     public AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
154         CompositeNodeDataWithSchema newChild;
155         if (schema instanceof ListSchemaNode) {
156             newChild = new ListNodeDataWithSchema(schema);
157         } else if (schema instanceof LeafListSchemaNode) {
158             newChild = new LeafListNodeDataWithSchema(schema);
159         } else if (schema instanceof ContainerSchemaNode) {
160             newChild = new ContainerNodeDataWithSchema(schema);
161         } else {
162             newChild = new CompositeNodeDataWithSchema(schema);
163         }
164         addCompositeChild(newChild);
165         return newChild;
166     }
167
168     public void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
169         AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
170         if (augSchema != null) {
171             addChildToAugmentation(augSchema, newChild);
172         } else {
173             addChild(newChild);
174         }
175     }
176
177     private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
178         AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
179         return newChild == null ? addCompositeChild(schema) : newChild;
180     }
181
182     public void addChild(final AbstractNodeDataWithSchema newChild) {
183         childs.add(newChild);
184     }
185
186     /**
187      * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
188      * node is found then it is returned, else null.
189      */
190     protected AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
191         if (parent instanceof AugmentationTarget) {
192             for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
193                 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
194                 if (childInAugmentation != null) {
195                     return augmentation;
196                 }
197             }
198         }
199         return null;
200     }
201
202     @Override
203     protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
204         for (AbstractNodeDataWithSchema child : childs) {
205             child.writeToStream(nnStreamWriter);
206         }
207         for (Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.entrySet()) {
208
209             final List<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
210
211             if (!childsFromAgumentation.isEmpty()) {
212                 nnStreamWriter.startAugmentationNode(toAugmentationIdentifier(augmentationToChild));
213
214                 for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
215                     nodeDataWithSchema.writeToStream(nnStreamWriter);
216                 }
217
218                 nnStreamWriter.endNode();
219             }
220         }
221     }
222
223     private static AugmentationIdentifier toAugmentationIdentifier(
224             final Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild) {
225         Collection<DataSchemaNode> nodes = augmentationToChild.getKey().getChildNodes();
226         Set<QName> nodesQNames = new HashSet<>();
227         for (DataSchemaNode node : nodes) {
228             nodesQNames.add(node.getQName());
229         }
230
231         return new AugmentationIdentifier(nodesQNames);
232     }
233
234 }