c085b1cda22b676edd10b44a91085fca55e4f76d
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / query / DefaultQueryFactory.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.binding.dom.adapter.query;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Throwables;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import java.util.ServiceLoader;
18 import java.util.concurrent.ExecutionException;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.kohsuke.MetaInfServices;
23 import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
24 import org.opendaylight.mdsal.binding.api.query.QueryFactory;
25 import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
26 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
27 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
28 import org.opendaylight.yangtools.yang.binding.DataObject;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 @Beta
37 @MetaInfServices
38 @Singleton
39 public final class DefaultQueryFactory implements QueryFactory {
40     private record MethodId(@NonNull DataNodeContainer parent, @NonNull String methodName) {
41         MethodId {
42             requireNonNull(parent);
43             requireNonNull(methodName);
44         }
45     }
46
47     private static final Logger LOG = LoggerFactory.getLogger(DefaultQueryFactory.class);
48
49     private final LoadingCache<MethodId, NodeIdentifier> knownMethods = CacheBuilder.newBuilder().build(
50         new CacheLoader<MethodId, NodeIdentifier>() {
51             @Override
52             public NodeIdentifier load(final MethodId key) {
53                 final DataNodeContainer parent = key.parent;
54                 final String methodName = key.methodName;
55
56                 for (DataSchemaNode child : parent.getChildNodes()) {
57                     if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) {
58                         return NodeIdentifier.create(child.getQName());
59                     }
60                 }
61                 throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent);
62             }
63         });
64     private final @NonNull BindingCodecTree codec;
65
66     public DefaultQueryFactory() {
67         this(ServiceLoader.load(BindingCodecTree.class).findFirst().orElseThrow());
68     }
69
70     @Inject
71     public DefaultQueryFactory(final BindingCodecTree codec) {
72         this.codec = requireNonNull(codec);
73     }
74
75     @Override
76     public <T extends DataObject> DescendantQueryBuilder<T> querySubtree(final InstanceIdentifier<T> rootPath) {
77         return new DefaultDescendantQueryBuilder<>(this, rootPath);
78     }
79
80     @NonNull BindingCodecTree codec() {
81         return codec;
82     }
83
84     @NonNull NodeIdentifier findChild(final DataNodeContainer parent, final String methodName) {
85         try {
86             return knownMethods.get(new MethodId(parent, methodName));
87         } catch (ExecutionException e) {
88             LOG.debug("Failed to find method for {}", methodName, e);
89             final Throwable cause = e.getCause();
90             Throwables.throwIfUnchecked(cause);
91             throw new IllegalStateException("Failed to load cache", e);
92         }
93     }
94 }