Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / query / DOMQueryMatcher.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.dom.spi.query;
9
10 import java.util.ArrayDeque;
11 import java.util.Deque;
12 import java.util.List;
13 import java.util.Optional;
14 import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
15 import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.Match;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
19 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
23
24 /**
25  * Generalized utility for matching predicates. Split out of {@link DOMQueryIterator} for simplicity.
26  */
27 final class DOMQueryMatcher {
28     private DOMQueryMatcher() {
29         // Utility class
30     }
31
32     static boolean matchesAll(final NormalizedNode data, final List<? extends DOMQueryPredicate> predicates) {
33         // TODO: it would be nice if predicates were somehow structured -- can we perhaps sort them by their
34         //       InstanceIdentifier? If the predicates are sharing a common subpath. Hence if we can guarantee
35         //       predicates are in a certain order, we would not end up in subsequent re-lookups of the same node.
36         Deque<PathArgument> pathArgs = null;
37         for (DOMQueryPredicate pred : predicates) {
38             // So now, dealing with implementations: YangInstanceIdentifier.getLastPathArgument() is always cheap.
39             // If its parent is YangInstanceIdentifier.ROOT (i.e. isEmpty() == true), we are dealing with a last-step
40             // lookup -- in which case we forgo iteration:
41             final YangInstanceIdentifier path = pred.relativePath();
42             if (path.coerceParent().isEmpty()) {
43                 if (!matchesChild(pred.match(), data, path.getLastPathArgument())) {
44                     return false;
45                 }
46                 continue;
47             }
48
49             // We are leaking path arguments in a bid for object reuse: we end up reusing same object as needed
50             if (pathArgs == null) {
51                 pathArgs = new ArrayDeque<>();
52             }
53             pathArgs.addAll(path.getPathArguments());
54
55             // The stage is set, we now have to deal with potential negation.
56             if (!matchesAny(pred.match(), data, pathArgs)) {
57                 return false;
58             }
59
60             pathArgs.clear();
61         }
62         return true;
63     }
64
65     private static boolean matchesAny(final Match match, final NormalizedNode data,
66             final Deque<PathArgument> pathArgs) {
67         // Guaranteed to have at least one item
68         final PathArgument pathArg = pathArgs.pop();
69         // Ultimate item -- reuse lookup & match
70         if (pathArgs.isEmpty()) {
71             pathArgs.push(pathArg);
72             return matchesChild(match, data, pathArg);
73         }
74
75         final Optional<NormalizedNode> direct = NormalizedNodes.getDirectChild(data, pathArg);
76         if (direct.isPresent()) {
77             final boolean ret = matchesAny(match, direct.orElseThrow(), pathArgs);
78             pathArgs.push(pathArg);
79             return ret;
80         }
81
82         // We may be dealing with a wildcard here. NodeIdentifier is a final class, hence this is as fast as it gets.
83         if (pathArg instanceof NodeIdentifier && data instanceof MapNode) {
84             for (MapEntryNode child : ((MapNode) data).body()) {
85                 if (matchesAny(match, child, pathArgs)) {
86                     pathArgs.push(pathArg);
87                     return true;
88                 }
89             }
90         }
91
92         pathArgs.push(pathArg);
93         return false;
94     }
95
96     private static boolean matchesChild(final Match match, final NormalizedNode data, final PathArgument pathArg) {
97         // Try the direct approach...
98         final Optional<NormalizedNode> direct = NormalizedNodes.getDirectChild(data, pathArg);
99         if (direct.isPresent()) {
100             return match.test(direct.orElseThrow());
101         }
102
103         // We may be dealing with a wildcard here. NodeIdentifier is a final class, hence this is as fast as it gets.
104         if (pathArg instanceof NodeIdentifier && data instanceof MapNode) {
105             for (MapEntryNode child : ((MapNode) data).body()) {
106                 if (match.test(child)) {
107                     return true;
108                 }
109             }
110         }
111
112         return match.test(null);
113     }
114 }