.gitignore .pmd files created by PMD Eclipse plugin
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / CompositeNodeDataWithSchema.java
1 /*
2  * Copyright (c) 2016 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.util;
9
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;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter;
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;
36 import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode;
37
38 /**
39  * A node which is composed of multiple simpler nodes.
40  */
41 public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
42
43     /**
44      * nodes which were added to schema via augmentation and are present in data input
45      */
46     private final Multimap<AugmentationSchema, AbstractNodeDataWithSchema> augmentationsToChild = ArrayListMultimap.create();
47
48     /**
49      * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
50      */
51     private final List<AbstractNodeDataWithSchema> children = new ArrayList<>();
52
53     public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
54         super(schema);
55     }
56
57     public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
58         Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
59
60         // Pop the first node...
61         final DataSchemaNode schema = schemas.pop();
62         if (schemas.isEmpty()) {
63             // Simple, direct node
64             return addChild(schema);
65         }
66
67         // The choice/case mess, reuse what we already popped
68         final DataSchemaNode choiceCandidate = schema;
69         Preconditions.checkArgument(choiceCandidate instanceof ChoiceSchemaNode,
70             "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName());
71         final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
72
73         final DataSchemaNode caseCandidate = schemas.pop();
74         Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode,
75             "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName());
76         final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate;
77
78         AugmentationSchema augSchema = null;
79         if (choiceCandidate.isAugmenting()) {
80             augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
81         }
82
83         // looking for existing choice
84         final Collection<AbstractNodeDataWithSchema> childNodes;
85         if (augSchema != null) {
86             childNodes = augmentationsToChild.get(augSchema);
87         } else {
88             childNodes = children;
89         }
90
91         CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
92         if (caseNodeDataWithSchema == null) {
93             ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
94             childNodes.add(choiceNodeDataWithSchema);
95             caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
96         }
97
98         return caseNodeDataWithSchema.addChild(schemas);
99     }
100
101     private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
102         SimpleNodeDataWithSchema newChild = null;
103         if (schema instanceof LeafSchemaNode) {
104             newChild = new LeafNodeDataWithSchema(schema);
105         } else if (schema instanceof AnyXmlSchemaNode) {
106             // YangModeledAnyXmlSchemaNode is handled by addCompositeChild method.
107             if (schema instanceof YangModeledAnyXmlSchemaNode) {
108                 return null;
109             }
110             newChild = new AnyXmlNodeDataWithSchema(schema);
111         } else {
112             return null;
113         }
114
115         AugmentationSchema augSchema = null;
116         if (schema.isAugmenting()) {
117             augSchema = findCorrespondingAugment(getSchema(), schema);
118         }
119         if (augSchema != null) {
120             augmentationsToChild.put(augSchema, newChild);
121         } else {
122             addChild(newChild);
123         }
124         return newChild;
125     }
126
127     private static CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema> childNodes,
128             final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) {
129         if (childNodes != null) {
130             for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
131                 if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
132                         && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
133                     CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
134
135                     Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
136                         "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.",
137                         caseCandidate.getQName(), casePrevious.getSchema().getQName());
138
139                     return casePrevious;
140                 }
141             }
142         }
143         return null;
144     }
145
146     AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
147         final CompositeNodeDataWithSchema newChild;
148
149         if (schema instanceof ListSchemaNode) {
150             newChild = new ListNodeDataWithSchema(schema);
151         } else if (schema instanceof LeafListSchemaNode) {
152             newChild = new LeafListNodeDataWithSchema(schema);
153         } else if (schema instanceof ContainerSchemaNode) {
154             newChild = new ContainerNodeDataWithSchema(schema);
155         } else if (schema instanceof YangModeledAnyXmlSchemaNode) {
156             newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyXmlSchemaNode)schema);
157         } else {
158             newChild = new CompositeNodeDataWithSchema(schema);
159         }
160
161         addCompositeChild(newChild);
162         return newChild;
163     }
164
165     void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
166         AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
167         if (augSchema != null) {
168             augmentationsToChild.put(augSchema, newChild);
169         } else {
170             addChild(newChild);
171         }
172     }
173
174     private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
175         AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
176         return newChild == null ? addCompositeChild(schema) : newChild;
177     }
178
179     public void addChild(final AbstractNodeDataWithSchema newChild) {
180         children.add(newChild);
181     }
182
183     /**
184      * Return a hint about how may children we are going to generate.
185      * @return Size of currently-present node list.
186      */
187     protected final int childSizeHint() {
188         return children.size();
189     }
190
191     @Override
192     public void write(final SchemaAwareNormalizedNodeStreamWriter writer) throws IOException {
193         for (AbstractNodeDataWithSchema child : children) {
194             child.write(writer);
195         }
196         for (Entry<AugmentationSchema, Collection<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.asMap().entrySet()) {
197             final Collection<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
198             if (!childsFromAgumentation.isEmpty()) {
199                 // FIXME: can we get the augmentation schema?
200                 writer.startAugmentationNode(getNodeIdentifierForAugmentation(augmentationToChild.getKey()));
201
202                 for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
203                     nodeDataWithSchema.write(writer);
204                 }
205
206                 writer.endNode();
207             }
208         }
209     }
210
211     /**
212      * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
213      * node is found then it is returned, else null.
214      *
215      * @param parent parent node
216      * @param child child node
217      * @return augmentation schema
218      */
219     private AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
220         if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
221             for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
222                 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
223                 if (childInAugmentation != null) {
224                     return augmentation;
225                 }
226             }
227         }
228         return null;
229     }
230
231     public static YangInstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) {
232         final Collection<QName> qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION);
233         return new YangInstanceIdentifier.AugmentationIdentifier(ImmutableSet.copyOf(qnames));
234     }
235
236     private static final Function<DataSchemaNode, QName> QNAME_FUNCTION = new Function<DataSchemaNode, QName>() {
237         @Override
238         public QName apply(@Nonnull final DataSchemaNode input) {
239             return input.getQName();
240         }
241     };
242 }