Binding v2 runtime - adapters - extractors
[mdsal.git] / binding2 / mdsal-binding2-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / adapter / extractor / ContextReferenceExtractor.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.adapter.extractor;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.cache.CacheBuilder;
12 import com.google.common.cache.CacheLoader;
13 import com.google.common.cache.LoadingCache;
14 import java.lang.reflect.Method;
15 import javax.annotation.Nonnull;
16 import javax.annotation.Nullable;
17 import org.opendaylight.mdsal.binding.javav2.api.annotation.RoutingContext;
18 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
19 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Tree node reference extractor.
25  */
26 @Beta
27 public abstract class ContextReferenceExtractor {
28
29     private static final Logger LOG = LoggerFactory.getLogger(ContextReferenceExtractor.class);
30     private static final String GET_VALUE_NAME = "getValue";
31
32     private static final ContextReferenceExtractor NULL_EXTRACTOR = new ContextReferenceExtractor() {
33
34         @Nullable
35         @Override
36         public InstanceIdentifier<? extends TreeNode> extract(final TreeNode obj) {
37             return null;
38         }
39     };
40
41     private static final LoadingCache<Class<?>, ContextReferenceExtractor> EXTRACTORS =
42             CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<Class<?>, ContextReferenceExtractor>() {
43
44                 @Nonnull
45                 @Override
46                 public ContextReferenceExtractor load(@Nonnull final Class<?> key) throws Exception {
47                     return create(key);
48                 }
49
50                 private ContextReferenceExtractor create(final Class<?> key) {
51                     final Method contextGetter = getContextGetter(key);
52                     if (contextGetter == null) {
53                         return NULL_EXTRACTOR;
54                     }
55                     final Class<?> returnType = contextGetter.getReturnType();
56                     try {
57                         if (InstanceIdentifier.class.isAssignableFrom(returnType)) {
58                             return DirectGetterRouteContextExtractor.create(contextGetter);
59                         }
60                         final Method getValueMethod = findGetValueMethod(returnType);
61                         if (getValueMethod != null) {
62                             return GetValueRouteContextExtractor.create(contextGetter, getValueMethod);
63                         } else {
64                             LOG.warn("Class {} can not be used to determine context, falling back to NULL_EXTRACTOR.",
65                                     returnType);
66                         }
67                     } catch (final IllegalAccessException e) {
68                         LOG.warn("Class {} does not conform to Binding Specification. Falling back to NULL_EXTRACTOR",
69                                 e);
70                     }
71                     return NULL_EXTRACTOR;
72                 }
73
74                 private Method getContextGetter(final Class<?> key) {
75                     for (final Method method : key.getMethods()) {
76                         if (method.getAnnotation(RoutingContext.class) != null) {
77                             return method;
78                         }
79                     }
80                     return null;
81                 }
82             });
83
84     /**
85      * Extract context-reference (Instance Identifier) from Binding TreeNode.
86      *
87      * @param obj
88      *            - TreeNode from which context reference should be extracted.
89      *
90      * @return Instance Identifier representing context reference or null, if tree node does not contain
91      *         context reference.
92      */
93     @Nullable
94     public abstract InstanceIdentifier<? extends TreeNode> extract(TreeNode obj);
95
96     /**
97      * Method for return specific extractor of input object.
98      *
99      * @param obj
100      *            - object for get specific extractor
101      * @return specific extractor
102      */
103     public static ContextReferenceExtractor from(final Class<?> obj) {
104         return EXTRACTORS.getUnchecked(obj);
105     }
106
107     private static Method findGetValueMethod(final Class<?> type) {
108         try {
109             final Method method = type.getMethod(GET_VALUE_NAME);
110             if (InstanceIdentifier.class.equals(method.getReturnType())) {
111                 return method;
112             }
113         } catch (final NoSuchMethodException e) {
114             LOG.warn("Value class {} does not comform to Binding Specification.", type, e);
115         }
116         return null;
117     }
118 }
119