BUG-1281: introduce XML event stream writer
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / SchemaUtils.java
1 /*
2  * Copyright (c) 2013 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.impl.schema;
9
10
11 import com.google.common.base.Function;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.Collections2;
15 import com.google.common.collect.Maps;
16 import com.google.common.collect.Sets;
17
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.Map;
21 import java.util.Set;
22
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
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.ChoiceNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33
34 public final class SchemaUtils {
35
36     private SchemaUtils() {
37     }
38
39     public static final Optional<DataSchemaNode> findFirstSchema(final QName qname, final Set<DataSchemaNode> dataSchemaNode) {
40         if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) {
41             for (DataSchemaNode dsn : dataSchemaNode) {
42                 if (qname.isEqualWithoutRevision(dsn.getQName())) {
43                     return Optional.<DataSchemaNode> of(dsn);
44                 } else if (dsn instanceof ChoiceNode) {
45                     for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
46                         Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
47                         if (foundDsn != null && foundDsn.isPresent()) {
48                             return foundDsn;
49                         }
50                     }
51                 }
52             }
53         }
54         return Optional.absent();
55     }
56
57     public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname) {
58         Set<DataSchemaNode> childNodes = schema.getChildNodes();
59         return findSchemaForChild(schema, qname, childNodes);
60     }
61
62     public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname, final Set<DataSchemaNode> childNodes) {
63         Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
64         Preconditions.checkState(childSchema.isPresent(),
65                 "Unknown child(ren) node(s) detected, identified by: %s, in: %s", qname, schema);
66         return childSchema.get();
67     }
68
69     public static AugmentationSchema findSchemaForAugment(final AugmentationTarget schema, final Set<QName> qNames) {
70         Optional<AugmentationSchema> schemaForAugment = findAugment(schema, qNames);
71         Preconditions.checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
72                 qNames, schema);
73         return schemaForAugment.get();
74     }
75
76     public static AugmentationSchema findSchemaForAugment(final ChoiceNode schema, final Set<QName> qNames) {
77         Optional<AugmentationSchema> schemaForAugment = Optional.absent();
78
79         for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
80             schemaForAugment = findAugment(choiceCaseNode, qNames);
81             if(schemaForAugment.isPresent()) {
82                 break;
83             }
84         }
85
86         Preconditions.checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
87                 qNames, schema);
88         return schemaForAugment.get();
89     }
90
91     private static Optional<AugmentationSchema> findAugment(final AugmentationTarget schema, final Set<QName> qNames) {
92         for (AugmentationSchema augment : schema.getAvailableAugmentations()) {
93
94             HashSet<QName> qNamesFromAugment = Sets.newHashSet(Collections2.transform(augment.getChildNodes(), new Function<DataSchemaNode, QName>() {
95                 @Override
96                 public QName apply(final DataSchemaNode input) {
97                     return input.getQName();
98                 }
99             }));
100
101             if(qNamesFromAugment.equals(qNames)) {
102                 return Optional.of(augment);
103             }
104         }
105
106         return Optional.absent();
107     }
108
109     public static DataSchemaNode findSchemaForChild(final ChoiceNode schema, final QName childPartialQName) {
110         for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
111             Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName, choiceCaseNode.getChildNodes());
112             if (childSchema.isPresent()) {
113                 return childSchema.get();
114             }
115         }
116
117
118         throw new IllegalStateException(String.format("Unknown child(ren) node(s) detected, identified by: %s, in: %s",
119                 childPartialQName, schema));
120     }
121
122     /**
123      * Recursively find all child nodes that come from choices.
124      *
125      * @return Map with all child nodes, to their most top augmentation
126      */
127     public static Map<QName, ChoiceNode> mapChildElementsFromChoices(final DataNodeContainer schema) {
128         Set<DataSchemaNode> childNodes = schema.getChildNodes();
129
130         return mapChildElementsFromChoices(schema, childNodes);
131     }
132
133     private static Map<QName, ChoiceNode> mapChildElementsFromChoices(final DataNodeContainer schema, final Set<DataSchemaNode> childNodes) {
134         Map<QName, ChoiceNode> mappedChoices = Maps.newLinkedHashMap();
135
136         for (final DataSchemaNode childSchema : childNodes) {
137             if(childSchema instanceof ChoiceNode) {
138
139                 if(isFromAugment(schema, childSchema)) {
140                     continue;
141                 }
142
143                 for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) childSchema).getCases()) {
144
145                     for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
146                         mappedChoices.put(qName, (ChoiceNode) childSchema);
147                     }
148                 }
149             }
150         }
151
152         return mappedChoices;
153     }
154
155     private static boolean isFromAugment(final DataNodeContainer schema, final DataSchemaNode childSchema) {
156         if(schema instanceof AugmentationTarget == false) {
157             return false;
158         }
159
160         for (AugmentationSchema augmentationSchema : ((AugmentationTarget) schema).getAvailableAugmentations()) {
161             if(augmentationSchema.getDataChildByName(childSchema.getQName()) != null) {
162                 return true;
163             }
164         }
165
166         return false;
167     }
168
169     /**
170      * Recursively find all child nodes that come from augmentations.
171      *
172      * @return Map with all child nodes, to their most top augmentation
173      */
174     public static Map<QName, AugmentationSchema> mapChildElementsFromAugments(final AugmentationTarget schema) {
175
176         Map<QName, AugmentationSchema> childNodesToAugmentation = Maps.newLinkedHashMap();
177
178         // Find QNames of augmented child nodes
179         Map<QName, AugmentationSchema> augments = Maps.newHashMap();
180         for (final AugmentationSchema augmentationSchema : schema.getAvailableAugmentations()) {
181             for (DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
182                 augments.put(dataSchemaNode.getQName(), augmentationSchema);
183             }
184         }
185
186         // Augmented nodes have to be looked up directly in augmentationTarget
187         // because nodes from augment do not contain nodes from other augmentations
188         if (schema instanceof DataNodeContainer) {
189
190             for (DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
191                 // If is not augmented child, continue
192                 if (augments.containsKey(child.getQName()) == false) {
193                     continue;
194                 }
195
196                 AugmentationSchema mostTopAugmentation = augments.get(child.getQName());
197
198                 // recursively add all child nodes in case of augment, case and choice
199                 if (child instanceof AugmentationSchema || child instanceof ChoiceCaseNode) {
200                     for (QName qName : getChildNodesRecursive((DataNodeContainer) child)) {
201                         childNodesToAugmentation.put(qName, mostTopAugmentation);
202                     }
203                 } else if (child instanceof ChoiceNode) {
204                     for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) child).getCases()) {
205                         for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
206                             childNodesToAugmentation.put(qName, mostTopAugmentation);
207                         }
208                     }
209                 } else {
210                     childNodesToAugmentation.put(child.getQName(), mostTopAugmentation);
211                 }
212             }
213         }
214
215         // Choice Node has to map child nodes from all its cases
216         if (schema instanceof ChoiceNode) {
217             for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) schema).getCases()) {
218                 if (augments.containsKey(choiceCaseNode.getQName()) == false) {
219                     continue;
220                 }
221
222                 for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
223                     childNodesToAugmentation.put(qName, augments.get(choiceCaseNode.getQName()));
224                 }
225             }
226         }
227
228         return childNodesToAugmentation;
229     }
230
231     /**
232      * Recursively list all child nodes.
233      *
234      * In case of choice, augment and cases, step in.
235      */
236     public static Set<QName> getChildNodesRecursive(final DataNodeContainer nodeContainer) {
237         Set<QName> allChildNodes = Sets.newHashSet();
238
239         for (DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
240             if(childSchema instanceof ChoiceNode) {
241                 for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) childSchema).getCases()) {
242                     allChildNodes.addAll(getChildNodesRecursive(choiceCaseNode));
243                 }
244             } else if(childSchema instanceof AugmentationSchema || childSchema instanceof ChoiceCaseNode) {
245                 allChildNodes.addAll(getChildNodesRecursive((DataNodeContainer) childSchema));
246             }
247             else {
248                 allChildNodes.add(childSchema.getQName());
249             }
250         }
251
252         return allChildNodes;
253     }
254
255     /**
256      * Retrieves real schemas for augmented child node.
257      *
258      * Schema of the same child node from augment, and directly from target is not the same.
259      * Schema of child node from augment is incomplete, therefore its useless for xml <-> normalizedNode translation.
260      *
261      */
262     public static Set<DataSchemaNode> getRealSchemasForAugment(final AugmentationTarget targetSchema, final AugmentationSchema augmentSchema) {
263         if(targetSchema.getAvailableAugmentations().contains(augmentSchema) == false) {
264             return Collections.emptySet();
265         }
266
267         Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
268
269         if(targetSchema instanceof DataNodeContainer) {
270             realChildNodes = getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
271         } else if(targetSchema instanceof ChoiceNode) {
272             for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
273                 for (ChoiceCaseNode choiceCaseNode : ((ChoiceNode) targetSchema).getCases()) {
274                     if(getChildNodesRecursive(choiceCaseNode).contains(dataSchemaNode.getQName())) {
275                         realChildNodes.add(choiceCaseNode.getDataChildByName(dataSchemaNode.getQName()));
276                     }
277                 }
278             }
279         }
280
281         return realChildNodes;
282     }
283
284     public static Set<DataSchemaNode> getRealSchemasForAugment(final DataNodeContainer targetSchema,
285             final AugmentationSchema augmentSchema) {
286         Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
287         for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
288             DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
289             realChildNodes.add(realChild);
290         }
291         return realChildNodes;
292     }
293
294     public static Optional<ChoiceCaseNode> detectCase(final ChoiceNode schema, final DataContainerChild<?, ?> child) {
295         for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
296             if (child instanceof AugmentationNode
297                     && belongsToCaseAugment(choiceCaseNode,
298                             (InstanceIdentifier.AugmentationIdentifier) child.getIdentifier())) {
299                 return Optional.of(choiceCaseNode);
300             } else if (choiceCaseNode.getDataChildByName(child.getNodeType()) != null) {
301                 return Optional.of(choiceCaseNode);
302             }
303         }
304
305         return Optional.absent();
306     }
307
308     public static boolean belongsToCaseAugment(final ChoiceCaseNode caseNode, final InstanceIdentifier.AugmentationIdentifier childToProcess) {
309         for (AugmentationSchema augmentationSchema : caseNode.getAvailableAugmentations()) {
310
311             Set<QName> currentAugmentChildNodes = Sets.newHashSet();
312             for (DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
313                 currentAugmentChildNodes.add(dataSchemaNode.getQName());
314             }
315
316             if(childToProcess.getPossibleChildNames().equals(currentAugmentChildNodes)){
317                 return true;
318             }
319         }
320
321         return false;
322     }
323
324     public static InstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) {
325         return new InstanceIdentifier.AugmentationIdentifier(getChildQNames(schema));
326     }
327
328     public static Set<QName> getChildQNames(final AugmentationSchema schema) {
329         Set<QName> qnames = Sets.newHashSet();
330
331         for (DataSchemaNode dataSchemaNode : schema.getChildNodes()) {
332             qnames.add(dataSchemaNode.getQName());
333         }
334
335         return qnames;
336     }
337 }