<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-test-model</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Builder for a match of a leaf value which define a total ordering by implementing the {@link Comparable} interface.
+ *
+ * <p>
+ * Note that value comparison is preconditioned on target leaf existence. If the leaf does not exist any total ordering
+ * checks will not match it -- thus a non-existent leaf does not match {@link #greaterThan(Comparable)} and at the same
+ * time it does not match {@link #lessThanOrEqual(Comparable)}.
+ *
+ * @param <T> query result type
+ * @param <V> value type
+ */
+@Beta
+public interface ComparableMatchBuilder<T extends DataObject, V extends Comparable<V>> extends ValueMatchBuilder<T, V> {
+ /**
+ * Match if the leaf exists and its value is less than the specified value.
+ *
+ * @param value value to check against
+ * @return A ValueMatch
+ * @throws NullPointerException if value is null
+ */
+ @NonNull ValueMatch<T> lessThan(V value);
+
+ /**
+ * Match if the leaf exists and its value is less than, or equal to, the specified value.
+ *
+ * @param value value to check against
+ * @return A ValueMatch
+ * @throws NullPointerException if value is null
+ */
+ @NonNull ValueMatch<T> lessThanOrEqual(V value);
+
+ /**
+ * Match if the leaf exists and its value is greater than the specified value.
+ *
+ * @param value value to check against
+ * @return A ValueMatch
+ * @throws NullPointerException if value is null
+ */
+ @NonNull ValueMatch<T> greaterThan(V value);
+
+ /**
+ * Match if the leaf exists and its value is greater than, or equal to the specified value.
+ *
+ * @param value value to check against
+ * @return A ValueMatch
+ * @throws NullPointerException if value is null
+ */
+ @NonNull ValueMatch<T> greaterThanOrEqual(V value);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Intermediate Query builder stage, which allows the specification of the query result type to be built up via
+ * {@link #extractChild(Class)} and {@link #extractChild(Class, Class)} methods. Once completed, use either
+ * {@link #build()} to create a simple query, or {@link #matching()} to transition to specify predicates.
+ *
+ * @param <T> Query result type
+ */
+@Beta
+public interface DescendantQueryBuilder<T extends DataObject> extends StructuralBuilder<QueryExpression<T>> {
+ /**
+ * Add a child path component to the specification of what needs to be extracted. This method, along with its
+ * alternatives, can be used to specify which object type to select from the root path.
+ *
+ * @param <N> Container type
+ * @param childClass child container class
+ * @return This builder
+ * @throws NullPointerException if childClass is null
+ */
+ <N extends ChildOf<? super T>> @NonNull DescendantQueryBuilder<N> extractChild(Class<N> childClass);
+
+ /**
+ * Add a child path component to the specification of what needs to be extracted. This method, along with its
+ * alternatives, can be used to specify which object type to select from the root path.
+ *
+ * @param <C> Case type
+ * @param <N> Container type
+ * @param caseClass child case class
+ * @param childClass child container class
+ * @return This builder
+ * @throws NullPointerException if any argument is null
+ */
+ <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
+ @NonNull DescendantQueryBuilder<N> extractChild(Class<C> caseClass, Class<N> childClass);
+
+ /**
+ * Start specifying type match predicates.
+ *
+ * @return A predicate match builder based on current result type
+ */
+ @NonNull MatchBuilderPath<T, T> matching();
+
+ @Override
+ QueryExpression<T> build();
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.TypeObject;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+
+@Beta
+public interface MatchBuilderPath<O extends DataObject, T extends DataObject> extends Mutable {
+ /**
+ * Descend match into a child container.
+ *
+ * @param <N> Child container type
+ * @param childClass Child container type class
+ * @return This builder
+ * @throws NullPointerException if childClass is null
+ */
+ <N extends ChildOf<? super T>> @NonNull MatchBuilderPath<O, N> childObject(Class<N> childClass);
+
+ /**
+ * Descend match into a child container in a particular case.
+ *
+ * @param <C> Case type
+ * @param <N> Child container type
+ * @param childClass Child container type class
+ * @return This builder
+ * @throws NullPointerException if any argument is null
+ */
+ <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
+ @NonNull MatchBuilderPath<O, N> extractChild(Class<C> caseClass, Class<N> childClass);
+
+ /**
+ * Match an {@code empty} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ValueMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ValueMatchBuilder<O, Empty> leaf(EmptyLeafReference<T> methodRef);
+
+ /**
+ * Match an {@code string} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link StringMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull StringMatchBuilder<O> leaf(StringLeafReference<T> methodRef);
+
+ /**
+ * Match an {@code int8} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Byte> leaf(Int8LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code int16} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Short> leaf(Int16LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code int32} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Integer> leaf(Int32LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code int64} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Long> leaf(Int64LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code uint8} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Uint8> leaf(Uint8LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code uint16} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Uint16> leaf(Uint16LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code uint32} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Uint32> leaf(Uint32LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code uint64} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ComparableMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ @NonNull ComparableMatchBuilder<O, Uint64> leaf(Uint64LeafReference<T> methodRef);
+
+ /**
+ * Match an {@code identityref} leaf's value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ValueMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ <I extends BaseIdentity> @NonNull ValueMatchBuilder<O, I> leaf(IdentityLeafReference<T, I> methodRef);
+
+ /**
+ * Match a generic leaf value.
+ *
+ * @param methodRef method reference to the getter method
+ * @return A {@link ValueMatchBuilder}
+ * @throws NullPointerException if methodRef is null
+ */
+ <C extends TypeObject> @NonNull ValueMatchBuilder<O, C> leaf(TypeObjectLeafReference<T, C> methodRef);
+
+ /**
+ * Base interface for capturing binding getter method references through lambda expressions. This interface should
+ * never be used directly, but rather through one of its specializations.
+ *
+ * <p>
+ * This interface uncharacteristically extends {@link Serializable} for the purposes of making the resulting lambda
+ * also Serializable. This part is critical for the process of introspection into the lambda and identifying the
+ * method being invoked.
+ *
+ * @param <P> Parent type
+ * @param <C> Child type
+ */
+ @FunctionalInterface
+ public interface LeafReference<P, C> extends Serializable {
+ /**
+ * Dummy method to express the method signature of a typical getter. This method
+ *
+ * @param parent Parent object
+ * @return Leaf value
+ * @deprecated This method is present only for technical realization of taking the method reference and should
+ * never be involved directly.
+ */
+ @Deprecated(forRemoval = true)
+ C dummyMethod(P parent);
+ }
+
+ @FunctionalInterface
+ public interface EmptyLeafReference<P> extends LeafReference<P, Empty> {
+
+ }
+
+ @FunctionalInterface
+ public interface StringLeafReference<P> extends LeafReference<P, String> {
+
+ }
+
+ @FunctionalInterface
+ public interface Int8LeafReference<P> extends LeafReference<P, Byte> {
+
+ }
+
+ @FunctionalInterface
+ public interface Int16LeafReference<P> extends LeafReference<P, Short> {
+
+ }
+
+ @FunctionalInterface
+ public interface Int32LeafReference<P> extends LeafReference<P, Integer> {
+
+ }
+
+ @FunctionalInterface
+ public interface Int64LeafReference<P> extends LeafReference<P, Long> {
+
+ }
+
+ @FunctionalInterface
+ public interface Uint8LeafReference<P> extends LeafReference<P, Uint8> {
+
+ }
+
+ @FunctionalInterface
+ public interface Uint16LeafReference<P> extends LeafReference<P, Uint16> {
+
+ }
+
+ @FunctionalInterface
+ public interface Uint32LeafReference<P> extends LeafReference<P, Uint32> {
+
+ }
+
+ @FunctionalInterface
+ public interface Uint64LeafReference<P> extends LeafReference<P, Uint64> {
+
+ }
+
+ @FunctionalInterface
+ public interface IdentityLeafReference<P, T extends BaseIdentity> extends LeafReference<P, T> {
+
+ }
+
+ @FunctionalInterface
+ public interface TypeObjectLeafReference<P, T extends TypeObject> extends LeafReference<P, T> {
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * An opaque query expression. A query execution results in a {@link QueryResult}, which is composed of zero or more
+ * objects of the same type. Implementations of this interface are expected to be effectively-immutable and therefore
+ * thread-safe and reusable.
+ *
+ * <p>
+ * While this interface does not expose any useful methods, it represents a well-defined concept, which is composed of
+ * three distinct parts:
+ * <ul>
+ * <li>root path, which defines the subtree on which the expression is executed</li>
+ * <li>select path, which is a strict subset of the root path and defines which objects should be selected</li>
+ * <li>a set of predicates, which are to be evaluated on selected objects</li>
+ * </ul>
+ * When the expression is evaluated, its QueryResult will contain only those selected objects which also match all
+ * predicates in the expression.
+ *
+ * <p>
+ * For the purposes of illustration of how these three parts work together, let's imagine the following simple model:
+ *
+ * <pre>
+ * module foo {
+ * list foo {
+ * key name;
+ *
+ * leaf name {
+ * type string;
+ * }
+ *
+ * leaf alias {
+ * type string;
+ * }
+ *
+ * container bar {
+ * list baz {
+ * key id;
+ *
+ * leaf id {
+ * type uint64;
+ * }
+ *
+ * leaf value {
+ * type string;
+ * }
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * We have two nested lists, each having two leaves -- one addressable as a key, one a plain property. There is a number
+ * of different queries we could perform on such a model:
+ * <ol>
+ * <li>select all {@code baz}es which have {@code value="foo"}</li>
+ * <li>select all {@code baz}es under {@code foo[name="xyzzy"]}, which have {@code value="foo"}</li>
+ * <li>select all {@code foo}s which have {@code alias="xyzzy"}</li>
+ * <li>select all {@code foo}s which have {@code alias="xyzzy"} and contain a {@code baz[value="foo"]}</li>
+ * </ol>
+ *
+ * <p>
+ * Note how the first and second options differ in what is being searched:
+ * <ul>
+ * <li>search for all {@code baz} entries needs to traverse all {@code foo} entries</li>
+ * <li>search for all {@code baz} entries for {@code foo[name="xyzzy"]} needs to traverse only a single
+ * directly-addressable entry.</li>
+ * </ul>
+ * The distinction here is made by selecting different root paths: the first will specify the entire {@code foo} list,
+ * while the second will select a specific {@code foo} entry.
+ *
+ *
+ *
+ * @param <T> Result object type
+ */
+@Beta
+public interface QueryExpression<T extends DataObject> extends Immutable {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Primary entry point to creating {@link QueryExpression} instances.
+ */
+@Beta
+public interface QueryFactory {
+ /**
+ * Create a new {@link DescendantQueryBuilder} for a specified root path. Root path must be a non-wildcard
+ * InstanceIdentifier in general sense. If the target type represents a list, the last path argument may be a
+ * wildcard, in which case the path is interpreted to search the specified list. Inner path elements have to be
+ * always non-wildcarded.
+ *
+ * @param <T> Target object type
+ * @param rootPath Subtree root
+ * @return a subtree query instance
+ * @throws IllegalArgumentException if rootPath is incorrect
+ * @throws NullPointerException if rootPath is null
+ */
+ <T extends DataObject> @NonNull DescendantQueryBuilder<T> querySubtree(InstanceIdentifier<T> rootPath);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Result of executing a {@link QueryExpression}. It is composed of one or more result values, which can be accessed via
+ * {@link #spliterator()}, {@link #stream()} and {@link #getValues()} methods.
+ *
+ * @param <T> Result object type
+ */
+@Beta
+public interface QueryResult<T extends DataObject> {
+ /**
+ * Returns a spliterator over values of the result.
+ *
+ * @return Returns the a spliterator which visits query results.
+ */
+ // TODO: @throws IllegalStateException if values have been already been consumed?
+ // FIXME: we really may want to wrap each entry in a CheckedValue, so that we can communicate fetch problems
+ @NonNull Spliterator<? extends T> spliterator();
+
+ /**
+ * Returns a sequential {@link Stream} of values from the result.
+ *
+ * @return A stream of non-null values.
+ */
+ default @NonNull Stream<? extends T> stream() {
+ return StreamSupport.stream(spliterator(), false);
+ }
+
+ /**
+ * Returns a parallel {@link Stream} of values from the result.
+ *
+ * @return A stream of non-null values.
+ */
+ default @NonNull Stream<? extends T> parallelStream() {
+ return StreamSupport.stream(spliterator(), true);
+ }
+
+ default @NonNull List<? extends T> getValues() {
+ return stream().collect(Collectors.toUnmodifiableList());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception reported when the proposed query has a structural problem. This may be either a mismatch with underlying
+ * schema, a value type problem, or a general DTO relationship issue.
+ */
+@Beta
+public class QueryStructureException extends IllegalStateException {
+ private static final long serialVersionUID = 1L;
+
+ public QueryStructureException(final String message) {
+ super(message);
+ }
+
+ public QueryStructureException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Builder for a match of a String leaf value.
+ *
+ * @param <T> query result type
+ */
+@Beta
+public interface StringMatchBuilder<T extends DataObject> extends ValueMatchBuilder<T, String> {
+ /**
+ * Match if the leaf exists and its value starts with specified string, i.e. its {@link String#startsWith(String)}
+ * would return true.
+ *
+ * @param str string to match as prefix
+ * @return A ValueMatch
+ * @throws NullPointerException if str is null
+ */
+ @NonNull ValueMatch<T> startsWith(String str);
+
+ /**
+ * Match if the leaf exists and its value ends with specified string. i.e. its {@link String#endsWith(String)}
+ * would return true.
+ *
+ * @param str string to match as suffix
+ * @return A ValueMatch
+ * @throws NullPointerException if str is null
+ */
+ @NonNull ValueMatch<T> endsWith(String str);
+
+ /**
+ * Match if the leaf exists and its value contains specified string, i.e. its {@link String#contains(CharSequence)}
+ * would return true.
+ *
+ * @param str the string to search for
+ * @return A ValueMatch
+ * @throws NullPointerException if str is null
+ */
+ @NonNull ValueMatch<T> contains(String str);
+
+ /**
+ * Match if the leaf exists and its value matches with specified pattern.
+ *
+ * @param pattern pattern to check against
+ * @return A ValueMatch
+ * @throws NullPointerException if pattern is null
+ */
+ @NonNull ValueMatch<T> matchesPattern(Pattern pattern);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.CheckedBuilder;
+
+/**
+ * A specialization of {@link CheckedBuilder}, whose {@code build()} method throws a {@link QueryStructureException}.
+ *
+ * @param <P> Product of builder
+ */
+@Beta
+public interface StructuralBuilder<P> extends CheckedBuilder<P, QueryStructureException> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * A value-based match executed from some point in the data tree.
+ *
+ * @param <T> query result type
+ */
+@Beta
+public interface ValueMatch<T extends DataObject> extends StructuralBuilder<QueryExpression<T>> {
+ /**
+ * Start chaining an additional match for the query. Query results are guaranteed both this match and that
+ * additional match at the same time.
+ *
+ * @return A {@link MatchBuilderPath}
+ */
+ @NonNull MatchBuilderPath<T, T> and();
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Basic builder for a match of a leaf value.
+ *
+ * @param <T> query result type
+ * @param <V> value type
+ */
+@Beta
+public interface ValueMatchBuilder<T extends DataObject, V> {
+ /**
+ * Match any existing value.
+ *
+ * @return A ValueMatch
+ */
+ @NonNull ValueMatch<T> nonNull();
+
+ /**
+ * Match exact value.
+ *
+ * @param value value to match
+ * @return A ValueMatch
+ * @throws NullPointerException if value is null
+ */
+ @NonNull ValueMatch<T> valueEquals(@NonNull V value);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Simple type-safe query language based on YANG concepts as manifested by its Java Bindings.
+ */
+package org.opendaylight.mdsal.binding.api.query;
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api.query;
+
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.Foo;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.first.grp.System;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.first.grp.SystemKey;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.second.grp.Alarms;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.third.grp.AffectedUsers;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Uint64;
+
+public class QueryBuilderExamples {
+ @Mock
+ private QueryFactory factory;
+
+ /*
+ * Return all of /foo. Equivalent to a read() of the same identifier.
+ */
+ public QueryExpression<Foo> selectFoo() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .build();
+ }
+
+ /*
+ * Read all of /foo/system[name="some"]. Equivalent to a read() of the same identifier.
+ */
+ public QueryExpression<System> selectFooSystemSome() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class).child(System.class, new SystemKey("some")))
+ .build();
+ }
+
+ /*
+ * Read all entries in /foo/system. Equivalent to a read(/foo).get().nonnullSystem().
+ */
+ public QueryExpression<System> selectFooSystem() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .build();
+ }
+
+ /*
+ * Read all entries in /foo/system, which have 'alias' set to 'some'.
+ */
+ public QueryExpression<System> selectFooSystemAliasSome() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .matching()
+ .leaf(System::getAlias)
+ .valueEquals("some")
+ .build();
+ }
+
+ /*
+ * Read all entries in /foo/system, which have 'alias' containing the string 'needle'.
+ */
+ public QueryExpression<System> selectFooSystemAliasWithNeedle() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .matching()
+ .leaf(System::getAlias)
+ .contains("needle")
+ .build();
+ }
+
+ /*
+ * Read all entries in /foo/system/alarms, which have 'critical' leaf present.
+ */
+ public QueryExpression<Alarms> selectFooSystemAlarmsCritical() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .extractChild(Alarms.class)
+ .matching()
+ .leaf(Alarms::getCritical)
+ .nonNull()
+ .build();
+ }
+
+ /*
+ * Read all entries in /foo/system/alarms, which have 'critical' leaf present and have an entry in 'affected-users'
+ * with 'uid' larger than 10.
+ *
+ * Note this is the same expression as selectFooSystemCriticalUid(), but selects Alarms objects.
+ */
+ public QueryExpression<Alarms> selectFooSystemAlarmsCriticalUid() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .extractChild(Alarms.class)
+ .matching()
+ .leaf(Alarms::getCritical)
+ .nonNull()
+ .and()
+ .childObject(AffectedUsers.class)
+ .leaf(AffectedUsers::getUid)
+ .greaterThan(Uint64.TEN)
+ .build();
+ }
+
+
+ /*
+ * Read all entries in /foo/system, which have 'critical' leaf present and have an entry in 'affected-users'
+ * with 'uid' larger than 10.
+ *
+ * Note this is the same expression as selectFooSystemAlarmsCriticalUid(), but selects System objects.
+ */
+ public QueryExpression<System> selectFooSystemCriticalUid() {
+ return factory
+ .querySubtree(InstanceIdentifier.create(Foo.class))
+ .extractChild(System.class)
+ .matching()
+ .childObject(Alarms.class)
+ .leaf(Alarms::getCritical)
+ .nonNull()
+ .and()
+ .childObject(Alarms.class)
+ .childObject(AffectedUsers.class)
+ .leaf(AffectedUsers::getUid)
+ .greaterThan(Uint64.TEN)
+ .build();
+ }
+}
--- /dev/null
+module mdsal-query {
+ namespace "mdsal-query";
+ prefix mq;
+
+ grouping first-grp {
+ list system {
+ key name;
+
+ leaf name {
+ type string;
+ }
+
+ leaf alias {
+ type string;
+ }
+
+ uses second-grp;
+ }
+ }
+
+ grouping second-grp {
+ list alarms {
+ key id;
+
+ leaf id {
+ type uint64;
+ }
+
+ leaf critical {
+ type empty;
+ }
+
+ uses third-grp;
+ }
+ }
+
+ grouping third-grp {
+ list affected-users {
+ key uid;
+
+ leaf uid {
+ type uint64;
+ }
+
+ leaf attr {
+ type uint8;
+ }
+ }
+ }
+
+ container foo {
+ uses first-grp;
+ }
+}
+