--- /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 com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+@Beta
+public interface QueryExecutor {
+
+ <T extends DataObject> ListenableFuture<? extends QueryResult<T>> executeQuery(QueryExpression<T> 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.query.binding.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.ValueMatch;
+import org.opendaylight.mdsal.binding.api.query.ValueMatchBuilder;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.Exists;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.ValueEquals;
+import org.opendaylight.mdsal.query.binding.adapter.QueryBuilderState.BoundMethod;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractValueMatchBuilder<T extends DataObject, V> implements ValueMatchBuilder<T, V> {
+ private final QueryBuilderState builder;
+ private final InstanceIdentifier<T> select;
+ private final BoundMethod method;
+
+ AbstractValueMatchBuilder(final QueryBuilderState builder, final InstanceIdentifier<T> select,
+ final BoundMethod method) {
+ this.builder = requireNonNull(builder);
+ this.select = requireNonNull(select);
+ this.method = requireNonNull(method);
+ }
+
+ @Override
+ public final ValueMatch<T> nonNull() {
+ return withPredicate(new Exists(relativePath()));
+ }
+
+ @Override
+ public final ValueMatch<T> valueEquals(final V value) {
+ return withPredicate(new ValueEquals<>(relativePath(), value));
+ }
+
+ final YangInstanceIdentifier relativePath() {
+ return method.parentPath.node(((DataSchemaNode) method.methodCodec.getSchema()).getQName());
+ }
+
+ final @NonNull ValueMatch<T> withPredicate(final DOMQueryPredicate predicate) {
+ // FIXME: this does not quite take value codec into account :(
+ builder.addPredicate(predicate);
+ return new DefaultValueMatch<>(builder, select);
+ }
+}
--- /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.query.binding.adapter;
+
+import org.opendaylight.mdsal.binding.api.query.ComparableMatchBuilder;
+import org.opendaylight.mdsal.binding.api.query.ValueMatch;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.GreaterThan;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.GreaterThanOrEqual;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.LessThan;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.LessThanOrEqual;
+import org.opendaylight.mdsal.query.binding.adapter.QueryBuilderState.BoundMethod;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DefaultComparableMatchBuilder<T extends DataObject, V extends Comparable<V>>
+ extends AbstractValueMatchBuilder<T, V> implements ComparableMatchBuilder<T, V> {
+ DefaultComparableMatchBuilder(final QueryBuilderState builder, final InstanceIdentifier<T> select,
+ final BoundMethod method) {
+ super(builder, select, method);
+ }
+
+ @Override
+ public ValueMatch<T> lessThan(final V value) {
+ return withPredicate(new LessThan<>(relativePath(), value));
+ }
+
+ @Override
+ public ValueMatch<T> lessThanOrEqual(final V value) {
+ return withPredicate(new LessThanOrEqual<>(relativePath(), value));
+ }
+
+ @Override
+ public ValueMatch<T> greaterThan(final V value) {
+ return withPredicate(new GreaterThan<>(relativePath(), value));
+ }
+
+ @Override
+ public ValueMatch<T> greaterThanOrEqual(final V value) {
+ return withPredicate(new GreaterThanOrEqual<>(relativePath(), 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.query.binding.adapter;
+
+import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
+import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+
+final class DefaultDescendantQueryBuilder<R extends DataObject, T extends DataObject>
+ implements DescendantQueryBuilder<T> {
+ private final InstanceIdentifierBuilder<T> childPath;
+ private final QueryBuilderState builder;
+
+ DefaultDescendantQueryBuilder(final BindingCodecTree codec, final InstanceIdentifier<T> rootPath) {
+ this.builder = new QueryBuilderState(codec, rootPath);
+ this.childPath = rootPath.builder();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <N extends ChildOf<? super T>> DescendantQueryBuilder<N> extractChild(final Class<N> childClass) {
+ childPath.child(childClass);
+ return (DefaultDescendantQueryBuilder<R, N>) this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
+ DescendantQueryBuilder<N> extractChild(final Class<C> caseClass, final Class<N> childClass) {
+ childPath.child(caseClass, childClass);
+ return (DefaultDescendantQueryBuilder<R, N>) this;
+ }
+
+ @Override
+ public MatchBuilderPath<T, T> matching() {
+ final InstanceIdentifier<T> selectPath = childPath.build();
+ builder.setSelectPath(selectPath);
+ return new DefaultMatchBuilderPath<>(builder, selectPath, childPath);
+ }
+
+ @Override
+ public QueryExpression<T> build() throws QueryStructureException {
+ builder.setSelectPath(childPath.build());
+ return builder.buildQuery();
+ }
+}
--- /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.query.binding.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.ComparableMatchBuilder;
+import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath;
+import org.opendaylight.mdsal.binding.api.query.StringMatchBuilder;
+import org.opendaylight.mdsal.binding.api.query.ValueMatchBuilder;
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+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;
+
+final class DefaultMatchBuilderPath<O extends DataObject, T extends DataObject> implements MatchBuilderPath<O, T> {
+ private final QueryBuilderState builder;
+ private final InstanceIdentifier<O> select;
+ private final InstanceIdentifierBuilder<T> target;
+
+ DefaultMatchBuilderPath(final QueryBuilderState builder, final InstanceIdentifier<O> select,
+ final InstanceIdentifierBuilder<T> target) {
+ this.builder = requireNonNull(builder);
+ this.select = requireNonNull(select);
+ this.target = requireNonNull(target);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <N extends ChildOf<? super T>> MatchBuilderPath<O, N> childObject(final Class<N> childClass) {
+ target.child(childClass);
+ return (MatchBuilderPath<O, N>) this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
+ MatchBuilderPath<O, N> extractChild(final Class<C> caseClass, final Class<N> childClass) {
+ target.child(caseClass, childClass);
+ return (MatchBuilderPath<O, N>) this;
+ }
+
+ @Override
+ public ValueMatchBuilder<O, Empty> leaf(final EmptyLeafReference<T> methodRef) {
+ return defaultFor(methodRef);
+ }
+
+ @Override
+ public StringMatchBuilder<O> leaf(final StringLeafReference<T> methodRef) {
+ return new DefaultStringMatchBuilder<>(builder, select, builder.bindMethod(target.build(), methodRef));
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Byte> leaf(final Int8LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Short> leaf(final Int16LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Integer> leaf(final Int32LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Long> leaf(final Int64LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Uint8> leaf(final Uint8LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Uint16> leaf(final Uint16LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Uint32> leaf(final Uint32LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public ComparableMatchBuilder<O, Uint64> leaf(final Uint64LeafReference<T> methodRef) {
+ return comparableFor(methodRef);
+ }
+
+ @Override
+ public <I extends BaseIdentity> ValueMatchBuilder<O, I> leaf(final IdentityLeafReference<T, I> methodRef) {
+ return defaultFor(methodRef);
+ }
+
+ @Override
+ public <C extends TypeObject> ValueMatchBuilder<O, C> leaf(final TypeObjectLeafReference<T, C> methodRef) {
+ return defaultFor(methodRef);
+ }
+
+ private <F> @NonNull ValueMatchBuilder<O, F> defaultFor(final LeafReference<T, F> ref) {
+ return new DefaultValueMatchBuilder<>(builder, select, builder.bindMethod(target.build(), ref));
+ }
+
+ private <F extends Comparable<F>> @NonNull ComparableMatchBuilder<O, F> comparableFor(
+ final LeafReference<T, F> ref) {
+ return new DefaultComparableMatchBuilder<>(builder, select, builder.bindMethod(target.build(), ref));
+ }
+}
--- /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.query.binding.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.dom.api.query.DOMQuery;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryLike;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+final class DefaultQuery<T extends DataObject> implements QueryExpression<T>, DOMQueryLike {
+ private final @NonNull DOMQuery domQuery;
+
+ DefaultQuery(final DOMQuery domQuery) {
+ this.domQuery = requireNonNull(domQuery);
+ }
+
+ @Override
+ public DOMQuery asDOMQuery() {
+ return domQuery;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("dom", domQuery).toString();
+ }
+}
--- /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.query.binding.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.ServiceLoader;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
+import org.opendaylight.mdsal.binding.api.query.QueryFactory;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Beta
+@MetaInfServices
+@Singleton
+public final class DefaultQueryFactory implements QueryFactory {
+ private final BindingCodecTree codec;
+
+ public DefaultQueryFactory() {
+ this(ServiceLoader.load(BindingCodecTree.class).findFirst().orElseThrow());
+ }
+
+ @Inject
+ public DefaultQueryFactory(final BindingCodecTree codec) {
+ this.codec = requireNonNull(codec);
+ }
+
+ @Override
+ public <T extends DataObject> DescendantQueryBuilder<T> querySubtree(final InstanceIdentifier<T> rootPath) {
+ return new DefaultDescendantQueryBuilder<>(codec, 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.query.binding.adapter;
+
+import java.util.regex.Pattern;
+import org.opendaylight.mdsal.binding.api.query.StringMatchBuilder;
+import org.opendaylight.mdsal.binding.api.query.ValueMatch;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.Contains;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.EndsWith;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.MatchesPattern;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate.StartsWith;
+import org.opendaylight.mdsal.query.binding.adapter.QueryBuilderState.BoundMethod;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DefaultStringMatchBuilder<T extends DataObject> extends AbstractValueMatchBuilder<T, String>
+ implements StringMatchBuilder<T> {
+ DefaultStringMatchBuilder(final QueryBuilderState builder, final InstanceIdentifier<T> select,
+ final BoundMethod method) {
+ super(builder, select, method);
+ }
+
+ @Override
+ public ValueMatch<T> startsWith(final String str) {
+ return withPredicate(new StartsWith(relativePath(), str));
+ }
+
+ @Override
+ public ValueMatch<T> endsWith(final String str) {
+ return withPredicate(new EndsWith(relativePath(), str));
+ }
+
+ @Override
+ public ValueMatch<T> contains(final String str) {
+ return withPredicate(new Contains(relativePath(), str));
+ }
+
+ @Override
+ public ValueMatch<T> matchesPattern(final Pattern pattern) {
+ return withPredicate(new MatchesPattern(relativePath(), 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.query.binding.adapter;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.ValueMatch;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DefaultValueMatch<T extends DataObject> implements ValueMatch<T> {
+ private final QueryBuilderState builder;
+ private final InstanceIdentifier<T> select;
+
+ DefaultValueMatch(final QueryBuilderState builder, final InstanceIdentifier<T> select) {
+ this.builder = requireNonNull(builder);
+ this.select = requireNonNull(select);
+ }
+
+ @Override
+ public MatchBuilderPath<T, T> and() {
+ return new DefaultMatchBuilderPath<>(builder, select, select.builder());
+ }
+
+ @Override
+ public QueryExpression<T> build() {
+ return builder.buildQuery();
+ }
+}
--- /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.query.binding.adapter;
+
+import org.opendaylight.mdsal.query.binding.adapter.QueryBuilderState.BoundMethod;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DefaultValueMatchBuilder<T extends DataObject, V> extends AbstractValueMatchBuilder<T, V> {
+ DefaultValueMatchBuilder(final QueryBuilderState builder, final InstanceIdentifier<T> select,
+ final BoundMethod method) {
+ super(builder, select, method);
+ }
+}
--- /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.query.binding.adapter;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.invoke.SerializedLambda;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath.LeafReference;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Utility class for forcing decoding lambda instances to the method being invoked. The theory here is that
+ * {@code MatchBuilderPath.leaf()} methods are expected to be used with {@code method references}, which are converted
+ * to {@link LeafReference} lambdas.
+ *
+ * <p>
+ * We then assume runtime is following guidance around {@link SerializedLambda}, thus Serializable lambdas have a
+ * {@code writeReplace()} method and that it produces {@link SerializedLambda} -- which we use to get the information
+ * about what the lambda does at least in the single case we support.
+ *
+ * <p>
+ * An alternative approach to cracking the lambda would be to generate a dynamic proxy implementation of the base
+ * DataObject (we have the Class to do that), back it by a invocation handler which throws a private RuntimeException
+ * subclass containing the name of the invoked method. We then would invoke the lambda on such a proxy and intercept
+ * the exception raised. This unfortunately has multiple downsides:
+ * <ul>
+ * <li>it requires a properly-managed ClassLoader (or pollutes original classloader with the proxy class)</li>
+ * <li>it makes it appear we support something else than method references, which we do not</li>
+ * <li>it creates additional implementation of the interface, bringing the system-wide total to 3, which can hurt
+ * JIT's decisions</li>
+ * </ul>
+ */
+final class LambdaDecoder {
+ static final class LambdaTarget implements Immutable {
+ final String targetClass;
+ final String targetMethod;
+
+ LambdaTarget(final String targetClass, final String targetMethod) {
+ this.targetClass = requireNonNull(targetClass);
+ this.targetMethod = requireNonNull(targetMethod);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("class", targetClass).add("method", targetMethod).toString();
+ }
+ }
+
+ private static final LoadingCache<Class<?>, Method> REPLACE_CACHE = CacheBuilder.newBuilder()
+ .weakKeys().weakValues().build(new CacheLoader<Class<?>, Method>() {
+ @Override
+ public Method load(final Class<?> key) throws PrivilegedActionException {
+ return AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () -> {
+ final Method method = key.getDeclaredMethod("writeReplace");
+ method.setAccessible(true);
+ return method;
+ });
+ }
+ });
+ private static final LoadingCache<LeafReference<?, ?>, LambdaTarget> LAMBDA_CACHE = CacheBuilder.newBuilder()
+ .weakKeys().build(new CacheLoader<LeafReference<?, ?>, LambdaTarget>() {
+ @Override
+ public LambdaTarget load(final LeafReference<?, ?> ref) throws Exception {
+ final Object replaced = REPLACE_CACHE.get(ref.getClass()).invoke(ref);
+ verify(replaced instanceof SerializedLambda, "Unexpected replaced object %s", replaced);
+ final SerializedLambda serialized = (SerializedLambda) replaced;
+ return new LambdaTarget(serialized.getImplClass().replace('/', '.'),
+ serialized.getImplMethodName());
+ }
+ });
+
+ private LambdaDecoder() {
+ // Hidden on purpose
+ }
+
+ static LambdaTarget resolveLambda(final LeafReference<?, ?> lambda) {
+ return LAMBDA_CACHE.getUnchecked(lambda);
+ }
+}
--- /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.query.binding.adapter;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
+import org.opendaylight.mdsal.binding.api.query.QueryFactory;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+@Component(immediate = true)
+public final class OSGiQueryFactory implements QueryFactory {
+ private static final Logger LOG = LoggerFactory.getLogger(OSGiQueryFactory.class);
+
+ @Reference
+ BindingCodecTree codec;
+
+ private DefaultQueryFactory delegate;
+
+ @Override
+ public <T extends DataObject> DescendantQueryBuilder<T> querySubtree(final InstanceIdentifier<T> rootPath) {
+ return delegate.querySubtree(rootPath);
+ }
+
+ @Activate
+ void activate() {
+ delegate = new DefaultQueryFactory(codec);
+ LOG.info("Binding Query activated");
+ }
+
+ @Deactivate
+ void deactivate() {
+ delegate = null;
+ LOG.info("Binding Query deactivated");
+ }
+}
--- /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.query.binding.adapter;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath.LeafReference;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
+import org.opendaylight.mdsal.dom.api.query.DOMQuery;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
+import org.opendaylight.mdsal.query.binding.adapter.LambdaDecoder.LambdaTarget;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+final class QueryBuilderState {
+ static final class BoundMethod implements Immutable {
+ final @NonNull YangInstanceIdentifier parentPath;
+ final @NonNull BindingCodecTreeNode methodCodec;
+
+ BoundMethod(final YangInstanceIdentifier parentPath, final BindingCodecTreeNode methodCodec) {
+ this.parentPath = requireNonNull(parentPath);
+ this.methodCodec = requireNonNull(methodCodec);
+ }
+ }
+
+ private final BindingCodecTree codec;
+
+ private final List<DOMQueryPredicate> predicates = new ArrayList<>();
+ private final YangInstanceIdentifier root;
+ private YangInstanceIdentifier absoluteSelect;
+ private YangInstanceIdentifier relativeSelect;
+
+ QueryBuilderState(final BindingCodecTree codec, final InstanceIdentifier<?> root) {
+ this.codec = requireNonNull(codec);
+ this.root = fromBinding(root);
+ }
+
+ void setSelectPath(final @NonNull InstanceIdentifier<?> selectPath) {
+ checkState(root != null, "Root path has not been set yet");
+ checkState(relativeSelect == null, "Select path has already been set to %s", relativeSelect);
+
+ absoluteSelect = fromBinding(selectPath);
+ relativeSelect = absoluteSelect.relativeTo(root)
+ .orElseThrow(() -> new IllegalStateException(root + " is not an ancestor of " + absoluteSelect));
+ }
+
+ @NonNull BoundMethod bindMethod(final @NonNull InstanceIdentifier<?> bindingPath,
+ final @NonNull LeafReference<?, ?> ref) {
+ // Verify bindingPath, which will give us something to fish in
+ final BindingDataObjectCodecTreeNode<?> targetCodec = codec.getSubtreeCodec(bindingPath);
+ checkState(targetCodec != null, "Failed to find codec for %s", bindingPath);
+
+ final WithStatus targetSchema = targetCodec.getSchema();
+ verify(targetSchema instanceof DataNodeContainer, "Unexpected target schema %s", targetSchema);
+
+ final LambdaTarget targetLeaf = LambdaDecoder.resolveLambda(ref);
+ verify(targetLeaf.targetClass.equals(bindingPath.getTargetType().getName()), "Mismatched target %s and path %s",
+ targetLeaf, bindingPath);
+ final DataSchemaNode child = findChild((DataNodeContainer) targetSchema, targetLeaf.targetMethod);
+ final YangInstanceIdentifier absTarget = fromBinding(bindingPath);
+ final YangInstanceIdentifier relTarget = absTarget.relativeTo(absoluteSelect)
+ .orElseThrow(() -> new IllegalStateException(absoluteSelect + " is not an ancestor of " + absTarget));
+
+ return new BoundMethod(relTarget, targetCodec.yangPathArgumentChild(new NodeIdentifier(child.getQName())));
+ }
+
+ void addPredicate(final DOMQueryPredicate predicate) {
+ predicates.add(requireNonNull(predicate));
+ }
+
+ <T extends DataObject> @NonNull QueryExpression<T> buildQuery() {
+ return new DefaultQuery<>(new DOMQuery(root, relativeSelect, predicates));
+ }
+
+ private @NonNull YangInstanceIdentifier fromBinding(final InstanceIdentifier<?> bindingId) {
+ return codec.getInstanceIdentifierCodec().fromBinding(bindingId);
+ }
+
+ private static DataSchemaNode findChild(final DataNodeContainer parent, final String methodName) {
+ for (DataSchemaNode child : parent.getChildNodes()) {
+ if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) {
+ return child;
+ }
+ }
+ throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent);
+ }
+}
--- /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.query.binding.adapter;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.binding.runtime.spi.BindingRuntimeHelpers;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryFactory;
+import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.mdsal.binding.dom.codec.impl.DefaultBindingCodecTreeFactory;
+import org.opendaylight.mdsal.binding.generator.impl.DefaultBindingRuntimeGenerator;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class QueryBuilderTest {
+ private static BindingCodecTree CODEC;
+
+ private final QueryFactory factory = new DefaultQueryFactory(CODEC);
+
+ @BeforeClass
+ public static void beforeClass() {
+ CODEC = new DefaultBindingCodecTreeFactory().create(BindingRuntimeHelpers.createRuntimeContext(
+ new DefaultBindingRuntimeGenerator()));
+ }
+
+ @Test
+ public void bar() throws QueryStructureException {
+ final QueryExpression<TopLevelList> query = factory.querySubtree(InstanceIdentifier.builder(Top.class).build())
+ .extractChild(TopLevelList.class)
+ .matching()
+ .childObject(NestedList.class)
+ .leaf(NestedList::getName).contains("foo")
+ .and().leaf(TopLevelList::getName).valueEquals("bar")
+ .build();
+//
+// // Execution start
+// final Result<TopLevelList> result = query.getResult();
+// // Execution fetch
+// final TopLevelList value = result.getValue();
+ }
+}
--- /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.dom.api.query;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+@Beta
+public final class DOMQuery implements Immutable {
+ private final @NonNull YangInstanceIdentifier root;
+ // Note: relative to root
+ private final @NonNull YangInstanceIdentifier select;
+ private final @NonNull ImmutableList<DOMQueryPredicate> predicates;
+
+ public DOMQuery(final YangInstanceIdentifier root, final YangInstanceIdentifier select,
+ final List<? extends DOMQueryPredicate> predicates) {
+ this.root = requireNonNull(root);
+ this.select = requireNonNull(select);
+ this.predicates = ImmutableList.copyOf(predicates);
+ }
+
+ public @NonNull YangInstanceIdentifier getRoot() {
+ return root;
+ }
+
+ public @NonNull YangInstanceIdentifier getSelect() {
+ return select;
+ }
+
+ public @NonNull List<? extends DOMQueryPredicate> getPredicates() {
+ return predicates;
+ }
+}
--- /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.dom.api.query;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Trait for objects which can be formulated in terms of a {@link DOMQuery}.
+ */
+@Beta
+@NonNullByDefault
+public interface DOMQueryLike extends Immutable {
+ /**
+ * Return a {@link DOMQuery} view of this object.
+ *
+ * @return A DOMQuery
+ */
+ DOMQuery asDOMQuery();
+}
--- /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.dom.api.query;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+@Beta
+public abstract class DOMQueryPredicate implements Immutable, Predicate<Object> {
+ abstract static class AbstractValueDOMQueryPredicate<T> extends DOMQueryPredicate {
+ private final @NonNull T value;
+
+ AbstractValueDOMQueryPredicate(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath);
+ this.value = requireNonNull(value);
+ }
+
+ final @NonNull T value() {
+ return value;
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("value", value);
+ }
+ }
+
+ abstract static class AbstractComparableDOMQueryPredicate<T extends Comparable<T>>
+ extends AbstractValueDOMQueryPredicate<T> {
+ AbstractComparableDOMQueryPredicate(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final boolean test(final Object data) {
+ return data != null && test(value().compareTo((T) data));
+ }
+
+ abstract boolean test(int valueToData);
+ }
+
+ abstract static class AbstractStringDOMQueryPredicate extends AbstractValueDOMQueryPredicate<String> {
+ AbstractStringDOMQueryPredicate(final YangInstanceIdentifier relativePath, final String value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ public final boolean test(final Object data) {
+ return data instanceof String && test((String) data);
+ }
+
+ abstract boolean test(@NonNull String str);
+ }
+
+ public static final class Exists extends DOMQueryPredicate {
+ public Exists(final YangInstanceIdentifier relativePath) {
+ super(relativePath);
+ }
+
+ @Override
+ public boolean test(final Object data) {
+ return data != null;
+ }
+ }
+
+ public static final class ValueEquals<T> extends AbstractValueDOMQueryPredicate<T> {
+ public ValueEquals(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ public boolean test(final Object data) {
+ return value().equals(data);
+ }
+ }
+
+ public static final class GreaterThan<T extends Comparable<T>> extends AbstractComparableDOMQueryPredicate<T> {
+ public GreaterThan(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ boolean test(final int valueToData) {
+ return valueToData <= 0;
+ }
+ }
+
+ public static final class GreaterThanOrEqual<T extends Comparable<T>>
+ extends AbstractComparableDOMQueryPredicate<T> {
+ public GreaterThanOrEqual(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ boolean test(final int valueToData) {
+ return valueToData < 0;
+ }
+ }
+
+ public static final class LessThan<T extends Comparable<T>> extends AbstractComparableDOMQueryPredicate<T> {
+ public LessThan(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ boolean test(final int valueToData) {
+ return valueToData >= 0;
+ }
+ }
+
+ public static final class LessThanOrEqual<T extends Comparable<T>> extends AbstractComparableDOMQueryPredicate<T> {
+ public LessThanOrEqual(final YangInstanceIdentifier relativePath, final T value) {
+ super(relativePath, value);
+ }
+
+ @Override
+ boolean test(final int valueToData) {
+ return valueToData > 0;
+ }
+ }
+
+ public static final class StartsWith extends AbstractStringDOMQueryPredicate {
+ public StartsWith(final YangInstanceIdentifier relativePath, final String str) {
+ super(relativePath, str);
+ }
+
+ @Override
+ boolean test(final String str) {
+ return str.startsWith(value());
+ }
+ }
+
+ public static final class EndsWith extends AbstractStringDOMQueryPredicate {
+ public EndsWith(final YangInstanceIdentifier relativePath, final String str) {
+ super(relativePath, str);
+ }
+
+ @Override
+ boolean test(final String str) {
+ return str.endsWith(value());
+ }
+ }
+
+ public static final class Contains extends AbstractStringDOMQueryPredicate {
+ public Contains(final YangInstanceIdentifier relativePath, final String str) {
+ super(relativePath, str);
+ }
+
+ @Override
+ boolean test(final String str) {
+ return str.contains(value());
+ }
+ }
+
+ public static final class MatchesPattern extends DOMQueryPredicate {
+ private final Pattern pattern;
+
+ public MatchesPattern(final YangInstanceIdentifier relativePath, final Pattern pattern) {
+ super(relativePath);
+ this.pattern = requireNonNull(pattern);
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("pattern", pattern);
+ }
+
+ @Override
+ public boolean test(final Object data) {
+ return data instanceof CharSequence && pattern.matcher((CharSequence) data).matches();
+ }
+ }
+
+ private final @NonNull YangInstanceIdentifier relativePath;
+
+ DOMQueryPredicate(final YangInstanceIdentifier relativePath) {
+ this.relativePath = requireNonNull(relativePath);
+ }
+
+ public final @NonNull YangInstanceIdentifier getPath() {
+ return relativePath;
+ }
+
+ @Override
+ public abstract boolean test(@Nullable Object data);
+
+ @Override
+ public String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this).add("path", relativePath)).toString();
+ }
+
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper;
+ }
+}
--- /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.dom.spi.query;
+
+import com.google.common.collect.ImmutableList;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import org.opendaylight.mdsal.dom.api.query.DOMQuery;
+import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+
+public final class DOMQueryEvaluator {
+ private DOMQueryEvaluator() {
+
+ }
+
+ public static List<? extends Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> evaluate(final DOMQuery query,
+ final NormalizedNode<?, ?> root) {
+ final YangInstanceIdentifier path = query.getSelect();
+ return path.isEmpty() ? evalSingle(root, query)
+ : evalPath(new ArrayDeque<>(path.getPathArguments()), root, query);
+ }
+
+ private static List<? extends Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> evalPath(
+ final ArrayDeque<PathArgument> remaining, final NormalizedNode<?,?> data, final DOMQuery query) {
+ final List<Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> result = new ArrayList<>();
+ evalPath(result, query.getRoot(), remaining, data, query);
+ return result;
+ }
+
+ private static void evalPath(final List<Entry<YangInstanceIdentifier, NormalizedNode<?,?>>> result,
+ final YangInstanceIdentifier path, final ArrayDeque<PathArgument> remaining,
+ final NormalizedNode<?, ?> data, final DOMQuery query) {
+ final PathArgument next = remaining.poll();
+ if (next == null) {
+ if (matches(data, query)) {
+ result.add(new SimpleImmutableEntry<>(query.getRoot(), data));
+ }
+ return;
+ }
+
+ // TODO: this is probably insufficient
+ NormalizedNodes.findNode(data, next)
+ .ifPresent(child -> evalPath(result, path.node(next), remaining, child, query));
+ remaining.push(next);
+ }
+
+ private static List<? extends Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> evalSingle(
+ final NormalizedNode<?, ?> data, final DOMQuery query) {
+ return matches(data, query) ? ImmutableList.of()
+ : ImmutableList.of(new SimpleImmutableEntry<>(query.getRoot(), data));
+ }
+
+ private static boolean matches(final NormalizedNode<?, ?> data, final DOMQuery query) {
+ for (DOMQueryPredicate pred : query.getPredicates()) {
+ if (!pred.test(data)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}