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