summaryrefslogtreecommitdiffstats
path: root/core/sli/provider/src/main/java/org/onap/ccsdk/sli/core/sli/provider/YangUtils.java
blob: e4d7618fe64d4f447b563589114ed6097c48b6c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
package org.onap.ccsdk.sli.core.sli.provider;

import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;

public final class YangUtils {

    private YangUtils() {
    }
    /**
     * Find the first schema with specified QName.
     *
     * @param qname schema node to find
     * @param dataSchemaNode Iterable of schemaNodes to look through
     * @return schema node with newest revision or absent if no schema node with matching qname is found
     */
    public static Optional<DataSchemaNode> findFirstSchema(final QName qname,
                                                           final Iterable<? extends DataSchemaNode> dataSchemaNode) {
        DataSchemaNode schema = null;
        if (dataSchemaNode != null && qname != null) {
            for (final DataSchemaNode dsn : dataSchemaNode) {
                if (qname.isEqualWithoutRevision(dsn.getQName())) {
                    if (schema == null || Revision.compare(schema.getQName().getRevision(),
                            dsn.getQName().getRevision()) < 0) {
                        schema = dsn;
                    }
                } else if (dsn instanceof ChoiceSchemaNode) {
                    for (final CaseSchemaNode choiceCase : ((ChoiceSchemaNode) dsn).getCases()) {
                        final Optional<DataSchemaNode> dataChildByName = choiceCase.findDataChildByName(qname);
                        if (dataChildByName.isPresent()) {
                            return dataChildByName;
                        }
                        final Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
                        if (foundDsn.isPresent()) {
                            return foundDsn;
                        }
                    }
                }
            }
        }
        return Optional.ofNullable(schema);
    }
    /**
     * Find child schema node identified by its QName within a provided schema node.
     *
     * @param schema schema for parent node - search root
     * @param qname qname(with or without a revision) of a child node to be found in the parent schema
     * @return found schema node
     * @throws java.lang.IllegalStateException if the child was not found in parent schema node
     */
    public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname) {
        // Try to find child schema node directly, but use a fallback that compares QNames without revisions
        // and auto-expands choices
        final Optional<DataSchemaNode> dataChildByName = schema.findDataChildByName(qname);
        return dataChildByName.isPresent() ? dataChildByName.get()
                : findSchemaForChild(schema, qname, schema.getChildNodes());
    }
    public static @Nullable DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname,
                                                              final boolean strictMode) {
        if (strictMode) {
            return findSchemaForChild(schema, qname);
        }
        final Optional<DataSchemaNode> childSchemaOptional = findFirstSchema(qname, schema.getChildNodes());
        if (!childSchemaOptional.isPresent()) {
            return null;
        }
        return childSchemaOptional.get();
    }
    public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname,
                                                    final Iterable<? extends DataSchemaNode> childNodes) {
        final Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
        checkState(childSchema.isPresent(), "Unknown child(ren) node(s) detected, identified by: %s, in: %s", qname,
                schema);
        return childSchema.get();
    }
    public static DataSchemaNode findSchemaForChild(final ChoiceSchemaNode schema, final QName childPartialQName) {
        for (final CaseSchemaNode choiceCaseNode : schema.getCases()) {
            final Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName,
                    choiceCaseNode.getChildNodes());
            if (childSchema.isPresent()) {
                return childSchema.get();
            }
        }
        throw new IllegalStateException(String.format("Unknown child(ren) node(s) detected, identified by: %s, in: %s",
                childPartialQName, schema));
    }
    public static AugmentationSchemaNode findSchemaForAugment(final AugmentationTarget schema,
                                                              final Set<QName> qnames) {
        final Optional<AugmentationSchemaNode> schemaForAugment = findAugment(schema, qnames);
        checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
                qnames, schema);
        return schemaForAugment.get();
    }
    public static AugmentationSchemaNode findSchemaForAugment(final ChoiceSchemaNode schema, final Set<QName> qnames) {
        for (final CaseSchemaNode choiceCaseNode : schema.getCases()) {
            final Optional<AugmentationSchemaNode> schemaForAugment = findAugment(choiceCaseNode, qnames);
            if (schemaForAugment.isPresent()) {
                return schemaForAugment.get();
            }
        }
        throw new IllegalStateException(String.format("Unknown augmentation node detected, identified by: %s, in: %s",
                qnames, schema));
    }
    private static Optional<AugmentationSchemaNode> findAugment(final AugmentationTarget schema,
                                                                final Set<QName> qnames) {
        for (final AugmentationSchemaNode augment : schema.getAvailableAugmentations()) {
            final Set<QName> qNamesFromAugment = ImmutableSet.copyOf(Collections2.transform(augment.getChildNodes(),
                    DataSchemaNode::getQName));
            if (qnames.equals(qNamesFromAugment)) {
                return Optional.of(augment);
            }
        }
        return Optional.empty();
    }
    /**
     * Recursively find all child nodes that come from choices.
     *
     * @param schema schema
     * @return Map with all child nodes, to their most top augmentation
     */
    public static Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(final DataNodeContainer schema) {
        return mapChildElementsFromChoices(schema, schema.getChildNodes());
    }
    private static Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(final DataNodeContainer schema,
                                                                            final Iterable<? extends DataSchemaNode> childNodes) {
        final Map<QName, ChoiceSchemaNode> mappedChoices = new LinkedHashMap<>();
        for (final DataSchemaNode childSchema : childNodes) {
            if (childSchema instanceof ChoiceSchemaNode) {
                if (isFromAugment(schema, childSchema)) {
                    continue;
                }
                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
                    for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
                        mappedChoices.put(qname, (ChoiceSchemaNode) childSchema);
                    }
                }
            }
        }
        return mappedChoices;
    }
    private static boolean isFromAugment(final DataNodeContainer schema, final DataSchemaNode childSchema) {
        if (!(schema instanceof AugmentationTarget)) {
            return false;
        }
        for (final AugmentationSchemaNode augmentation : ((AugmentationTarget) schema).getAvailableAugmentations()) {
            if (augmentation.findDataChildByName(childSchema.getQName()).isPresent()) {
                return true;
            }
        }
        return false;
    }
    /**
     * Recursively find all child nodes that come from augmentations.
     *
     * @param schema schema
     * @return Map with all child nodes, to their most top augmentation
     */
    public static Map<QName, AugmentationSchemaNode> mapChildElementsFromAugments(final AugmentationTarget schema) {
        final Map<QName, AugmentationSchemaNode> childNodesToAugmentation = new LinkedHashMap<>();
        // Find QNames of augmented child nodes
        final Map<QName, AugmentationSchemaNode> augments = new HashMap<>();
        for (final AugmentationSchemaNode augmentationSchema : schema.getAvailableAugmentations()) {
            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
                augments.put(dataSchemaNode.getQName(), augmentationSchema);
            }
        }
        // Augmented nodes have to be looked up directly in augmentationTarget
        // because nodes from augment do not contain nodes from other augmentations
        if (schema instanceof DataNodeContainer) {
            for (final DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
                // If is not augmented child, continue
                if (!augments.containsKey(child.getQName())) {
                    continue;
                }
                final AugmentationSchemaNode mostTopAugmentation = augments.get(child.getQName());
                // recursively add all child nodes in case of augment, case and choice
                if (child instanceof AugmentationSchemaNode || child instanceof CaseSchemaNode) {
                    for (final QName qname : getChildNodesRecursive((DataNodeContainer) child)) {
                        childNodesToAugmentation.put(qname, mostTopAugmentation);
                    }
                } else if (child instanceof ChoiceSchemaNode) {
                    for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) child).getCases()) {
                        for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
                            childNodesToAugmentation.put(qname, mostTopAugmentation);
                        }
                    }
                } else {
                    childNodesToAugmentation.put(child.getQName(), mostTopAugmentation);
                }
            }
        }
        // Choice Node has to map child nodes from all its cases
        if (schema instanceof ChoiceSchemaNode) {
            for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) schema).getCases()) {
                if (!augments.containsKey(choiceCaseNode.getQName())) {
                    continue;
                }
                for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
                    childNodesToAugmentation.put(qname, augments.get(choiceCaseNode.getQName()));
                }
            }
        }
        return childNodesToAugmentation;
    }
    /**
     * Recursively list all child nodes. In case of choice, augment and cases, step in.
     *
     * @param nodeContainer node container
     * @return set of QNames
     */
    public static Set<QName> getChildNodesRecursive(final DataNodeContainer nodeContainer) {
        final Set<QName> allChildNodes = new HashSet<>();
        for (final DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
            if (childSchema instanceof ChoiceSchemaNode) {
                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
                    allChildNodes.addAll(getChildNodesRecursive(choiceCaseNode));
                }
            } else if (childSchema instanceof AugmentationSchemaNode || childSchema instanceof CaseSchemaNode) {
                allChildNodes.addAll(getChildNodesRecursive((DataNodeContainer) childSchema));
            } else {
                allChildNodes.add(childSchema.getQName());
            }
        }
        return allChildNodes;
    }
    /**
     * Retrieves real schemas for augmented child node.
     *
     * <p>
     * Schema of the same child node from augment, and directly from target is not the same.
     * Schema of child node from augment is incomplete, therefore its useless for XML/NormalizedNode translation.
     *
     * @param targetSchema target schema
     * @param augmentSchema augment schema
     * @return set of nodes
     */
    public static Set<DataSchemaNode> getRealSchemasForAugment(final AugmentationTarget targetSchema,
                                                               final AugmentationSchemaNode augmentSchema) {
        if (!targetSchema.getAvailableAugmentations().contains(augmentSchema)) {
            return ImmutableSet.of();
        }
        if (targetSchema instanceof DataNodeContainer) {
            return getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
        }
        final Set<DataSchemaNode> realChildNodes = new HashSet<>();
        if (targetSchema instanceof ChoiceSchemaNode) {
            for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) targetSchema).getCases()) {
                    if (getChildNodesRecursive(choiceCaseNode).contains(dataSchemaNode.getQName())) {
                        realChildNodes.add(choiceCaseNode.getDataChildByName(dataSchemaNode.getQName()));
                    }
                }
            }
        }
        return realChildNodes;
    }
    public static Set<DataSchemaNode> getRealSchemasForAugment(final DataNodeContainer targetSchema,
                                                               final AugmentationSchemaNode augmentSchema) {
        final Set<DataSchemaNode> realChildNodes = new HashSet<>();
        for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
            final DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
            realChildNodes.add(realChild);
        }
        return realChildNodes;
    }

    public static boolean belongsToCaseAugment(final CaseSchemaNode caseNode,
                                               final AugmentationIdentifier childToProcess) {
        for (final AugmentationSchemaNode augmentationSchema : caseNode.getAvailableAugmentations()) {
            final Set<QName> currentAugmentChildNodes = new HashSet<>();
            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
                currentAugmentChildNodes.add(dataSchemaNode.getQName());
            }
            if (childToProcess.getPossibleChildNames().equals(currentAugmentChildNodes)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
     * node is found then it is returned, else null.
     *
     * @param parent parent node
     * @param child child node
     * @return augmentation schema
     */
    public static AugmentationSchemaNode findCorrespondingAugment(final DataSchemaNode parent,
                                                                  final DataSchemaNode child) {
        if (!(parent instanceof AugmentationTarget) || parent instanceof ChoiceSchemaNode) {
            return null;
        }
        for (final AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
            final Optional<DataSchemaNode> childInAugmentation = augmentation.findDataChildByName(child.getQName());
            if (childInAugmentation.isPresent()) {
                return augmentation;
            }
        }
        return null;
    }
    /**
     * Find child data schema node identified by its QName within a provided schema node. This method performs lookup
     * in the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions, notifications, anydatas
     * and anyxmls according to RFC6050/RFC7950 section 6.2.1.
     *
     * @param node
     *            schema node
     * @param qname
     *            QName
     * @return data child schema node
     * @throws IllegalArgumentException
     *             if the schema node does not allow children
     */
    public static @Nullable SchemaNode findDataChildSchemaByQName(final SchemaNode node, final QName qname) {
        if (node instanceof DataNodeContainer) {
            SchemaNode child = ((DataNodeContainer) node).dataChildByName(qname);
            if (child == null && node instanceof SchemaContext) {
                child = tryFind(((SchemaContext) node).getOperations(), qname).orElse(null);
            }
            if (child == null && node instanceof NotificationNodeContainer) {
                child = tryFind(((NotificationNodeContainer) node).getNotifications(), qname).orElse(null);
            }
            if (child == null && node instanceof ActionNodeContainer) {
                child = tryFind(((ActionNodeContainer) node).getActions(), qname).orElse(null);
            }
            return child;
        }
        if (node instanceof ChoiceSchemaNode) {
            return ((ChoiceSchemaNode) node).findCase(qname).orElse(null);
        }
        if (node instanceof OperationDefinition) {
            switch (qname.getLocalName()) {
                case "input":
                    return ((OperationDefinition) node).getInput();
                case "output":
                    return ((OperationDefinition) node).getOutput();
                default:
                    return null;
            }
        }
        throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", node));
    }
    /**
     * Find child schema node identified by its QName within a provided schema node. This method performs lookup in both
     * the namespace of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
     * actions, notifications, anydatas and anyxmls according to RFC6050/RFC7950 section 6.2.1.
     *
     * <p>
     * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
     * of groupings and namespace of data nodes. This method finds and collects all schema nodes with supplied QName
     * and returns them all as collection of schema nodes.
     *
     * @param node
     *            schema node
     * @param qname
     *            QName
     * @return collection of child schema nodes
     * @throws IllegalArgumentException
     *             if the schema node does not allow children
     */
    public static Collection<SchemaNode> findChildSchemaNodesByQName(final SchemaNode node, final QName qname) {
        final List<SchemaNode> childNodes = new ArrayList<>();
        final SchemaNode dataNode = findDataChildSchemaByQName(node, qname);
        if (dataNode != null) {
            childNodes.add(dataNode);
        }
        if (node instanceof DataNodeContainer) {
            tryFind(((DataNodeContainer) node).getGroupings(), qname).ifPresent(childNodes::add);
        }
        return childNodes.isEmpty() ? ImmutableList.of() : ImmutableList.copyOf(childNodes);
    }
    private static <T extends SchemaNode> Optional<T> tryFind(final Collection<T> nodes, final QName qname) {
        return nodes.stream().filter(node -> qname.equals(node.getQName())).findFirst();
    }

    public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
        //final QName qname = schemaNode.getPath().getLastComponent();
        final QName qname = schemaNode.getQName();
        checkState(qname != null, "Schema Path contains invalid state of path parts. "
                + "The Schema Path MUST contain at least ONE QName  which defines namespace and Local name of path.");
        return context.findModule(qname.getModule()).orElse(null);
    }

}