2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.dom.api.query;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.collect.ImmutableList;
15 import java.util.Iterator;
16 import java.util.function.Predicate;
17 import java.util.regex.Pattern;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.concepts.Immutable;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 * A single {@link DOMQuery} predicate. It is composed of a relative path and a match. The relative path needs to be
28 * expanded using usual wildcard rules, i.e. NodeIdentifier being used as a 'match all' identifier. For all candidate
29 * nodes selected by the relative path, the corresponding match needs to be invoked.
33 public final class DOMQueryPredicate implements Immutable {
35 * A single match. The primary entrypoint is {@link #test(NormalizedNode)}, but during composition instances may
36 * be combined in a manner similar to {@link Predicate}.
38 public abstract static class Match {
43 public static final Match exists() {
44 return MatchExists.INSTACE;
47 public static final <T extends Comparable<T>> Match greaterThan(final T value) {
48 return new MatchGreaterThan<>(value);
51 public static final <T extends Comparable<T>> Match greaterThanOrEqual(final T value) {
52 return new MatchGreaterThanOrEqual<>(value);
55 public static final <T extends Comparable<T>> Match lessThan(final T value) {
56 return new MatchLessThan<>(value);
59 public static final <T extends Comparable<T>> Match lessThanOrEqual(final T value) {
60 return new MatchLessThanOrEqual<>(value);
63 public static final Match stringMatches(final Pattern pattern) {
64 return new MatchStringMatches(pattern);
67 public static final Match stringStartsWith(final String str) {
68 return new MatchStringStartsWith(str);
71 public static final Match stringEndsWith(final String str) {
72 return new MatchStringEndsWith(str);
75 public static final Match stringContains(final String str) {
76 return new MatchStringContains(str);
79 public static final <V> Match valueEquals(final V value) {
80 return new MatchValueEquals<>(value);
84 * Return a {@link Match} which tests the opposite of this match.
86 * @return Negated match.
88 public Match negate() {
89 return new MatchNot(this);
92 public Match and(final Match other) {
93 return new MatchAll(ImmutableList.of(this, other));
96 public Match or(final Match other) {
97 return new MatchAny(ImmutableList.of(this, other));
100 public abstract boolean test(@Nullable NormalizedNode data);
102 final void appendTo(final StringBuilder sb) {
103 sb.append(op()).append('(');
108 void appendArgument(final StringBuilder sb) {
112 abstract String op();
115 public final String toString() {
116 final var sb = new StringBuilder();
118 return sb.toString();
122 private static final class MatchAll extends CompositeMatch {
123 MatchAll(final ImmutableList<Match> components) {
128 public MatchAll and(final Match other) {
129 return new MatchAll(newComponents(other));
133 public boolean test(final @Nullable NormalizedNode data) {
134 for (Match component : components()) {
135 if (!component.test(data)) {
148 private static final class MatchAny extends CompositeMatch {
149 MatchAny(final ImmutableList<Match> components) {
154 public MatchAny or(final Match other) {
155 return new MatchAny(newComponents(other));
159 public boolean test(final @Nullable NormalizedNode data) {
160 for (Match component : components()) {
161 if (component.test(data)) {
174 private static final class MatchExists extends Match {
175 static final MatchExists INSTACE = new MatchExists();
177 private MatchExists() {
182 public boolean test(final @Nullable NormalizedNode data) {
192 private static final class MatchNot extends Match {
193 private final Match match;
195 MatchNot(final Match match) {
196 this.match = requireNonNull(match);
200 public Match negate() {
205 public boolean test(final @Nullable NormalizedNode data) {
206 return !match.test(data);
215 void appendArgument(final StringBuilder sb) {
220 private static final class MatchValueEquals<T> extends AbstractMatchValue<T> {
221 MatchValueEquals(final T value) {
231 boolean testValue(final @Nullable Object data) {
232 return value().equals(data);
236 private static final class MatchStringContains extends AbstractMatchString {
237 MatchStringContains(final String value) {
247 boolean testString(final String str) {
248 return str.contains(value());
252 private static final class MatchStringMatches extends AbstractMatch {
253 private final Pattern pattern;
255 MatchStringMatches(final Pattern pattern) {
256 this.pattern = requireNonNull(pattern);
265 void appendArgument(final StringBuilder sb) {
270 boolean testValue(final @Nullable Object data) {
271 return data instanceof CharSequence && pattern.matcher((CharSequence) data).matches();
275 private static final class MatchStringStartsWith extends AbstractMatchString {
276 MatchStringStartsWith(final String value) {
286 boolean testString(final String str) {
287 return str.startsWith(value());
291 private static final class MatchStringEndsWith extends AbstractMatchString {
292 MatchStringEndsWith(final String value) {
302 boolean testString(final String str) {
303 return str.endsWith(value());
307 private static final class MatchGreaterThan<T extends Comparable<T>> extends AbstractMatchComparable<T> {
308 MatchGreaterThan(final T value) {
318 boolean testCompare(final int valueToData) {
319 return valueToData < 0;
323 private static final class MatchGreaterThanOrEqual<T extends Comparable<T>> extends AbstractMatchComparable<T> {
324 MatchGreaterThanOrEqual(final T value) {
334 boolean testCompare(final int valueToData) {
335 return valueToData <= 0;
339 private static final class MatchLessThan<T extends Comparable<T>> extends AbstractMatchComparable<T> {
340 MatchLessThan(final T value) {
350 boolean testCompare(final int valueToData) {
351 return valueToData > 0;
355 private static final class MatchLessThanOrEqual<T extends Comparable<T>> extends AbstractMatchComparable<T> {
356 MatchLessThanOrEqual(final T value) {
366 boolean testCompare(final int valueToData) {
367 return valueToData >= 0;
371 private abstract static class CompositeMatch extends Match {
372 private final ImmutableList<Match> components;
374 CompositeMatch(final ImmutableList<Match> components) {
375 this.components = requireNonNull(components);
378 final ImmutableList<Match> components() {
382 final ImmutableList<Match> newComponents(final Match nextComponent) {
383 return ImmutableList.<Match>builderWithExpectedSize(components.size() + 1)
390 final void appendArgument(final StringBuilder sb) {
391 final Iterator<Match> it = components.iterator();
392 sb.append(it.next());
393 while (it.hasNext()) {
394 sb.append(", ").append(it.next());
399 private abstract static class AbstractMatch extends Match {
405 public final boolean test(final @Nullable NormalizedNode data) {
406 return data instanceof LeafNode ? testValue(((LeafNode<?>) data).body()) : testValue(null);
409 abstract boolean testValue(@Nullable Object data);
412 private abstract static class AbstractMatchComparable<T extends Comparable<T>> extends AbstractMatchValue<T> {
413 AbstractMatchComparable(final T value) {
418 @SuppressWarnings("unchecked")
419 final boolean testValue(final @Nullable Object data) {
420 return data != null && testCompare(value().compareTo((T) data));
424 * Evaluate the result of {@code value().compareTo(data)}. Note this result is an inversion of what the match
427 * @param valueToData {@link Comparable#compareTo(Object)} result
428 * @return True if the result indicates a match
430 abstract boolean testCompare(int valueToData);
433 private abstract static class AbstractMatchString extends AbstractMatchValue<String> {
434 AbstractMatchString(final String value) {
439 final boolean testValue(final @Nullable Object data) {
440 return data instanceof String && testString((String) data);
443 abstract boolean testString(String str);
446 private abstract static class AbstractMatchValue<T> extends AbstractMatch {
447 private final @NonNull T value;
449 AbstractMatchValue(final T value) {
450 this.value = requireNonNull(value);
453 final @NonNull T value() {
458 final void appendArgument(final StringBuilder sb) {
463 private final YangInstanceIdentifier relativePath;
464 private final Match match;
466 private DOMQueryPredicate(final YangInstanceIdentifier relativePath, final Match match) {
467 this.relativePath = requireNonNull(relativePath);
468 this.match = requireNonNull(match);
471 public static DOMQueryPredicate of(final YangInstanceIdentifier relativePath, final Match match) {
472 return new DOMQueryPredicate(relativePath, match);
475 public YangInstanceIdentifier relativePath() {
479 public Match match() {
484 public String toString() {
485 return MoreObjects.toStringHelper(this).add("path", relativePath).add("match", match).toString();