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