Enable spotbugs in mdsal-binding-spec-util
[mdsal.git] / binding / mdsal-binding-spec-util / src / main / java / org / opendaylight / mdsal / binding / spec / reflect / AugmentationFieldGetter.java
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.mdsal.binding.spec.reflect;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import java.lang.invoke.MethodHandle;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.MethodHandles.Lookup;
18 import java.lang.invoke.MethodType;
19 import java.lang.reflect.Field;
20 import java.security.AccessController;
21 import java.security.PrivilegedActionException;
22 import java.security.PrivilegedExceptionAction;
23 import java.util.Collections;
24 import java.util.Map;
25 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
26 import org.opendaylight.yangtools.yang.binding.Augmentation;
27 import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 abstract class AugmentationFieldGetter {
32
33     private static final Logger LOG = LoggerFactory.getLogger(AugmentationFieldGetter.class);
34
35     private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
36         @Override
37         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
38             return Collections.emptyMap();
39         }
40     };
41
42     private static final AugmentationFieldGetter AUGMENTATION_HOLDER_GETTER = new AugmentationFieldGetter() {
43         @Override
44         @SuppressWarnings({"unchecked", "rawtypes"})
45         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
46             return (Map) ((AugmentationHolder<?>) input).augmentations();
47         }
48     };
49
50     private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
51             CacheBuilder.newBuilder().weakKeys().build(new AugmentationGetterLoader());
52
53     /**
54      * Retrieves augmentations from supplied object.
55      *
56      * @param input Input Data object, from which augmentations should be extracted
57      * @return Map of Augmentation class to augmentation
58      */
59     protected abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(Object input);
60
61     public static AugmentationFieldGetter getGetter(final Class<? extends Object> clz) {
62         if (AugmentationHolder.class.isAssignableFrom(clz)) {
63             return AUGMENTATION_HOLDER_GETTER;
64         }
65         return AUGMENTATION_GETTERS.getUnchecked(clz);
66     }
67
68     private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
69         private static final MethodType GETTER_TYPE = MethodType.methodType(Map.class, Object.class);
70         private static final Lookup LOOKUP = MethodHandles.lookup();
71
72         @Override
73         public AugmentationFieldGetter load(final Class<?> key) throws IllegalAccessException {
74             final Field field;
75             try {
76                 field = AccessController.doPrivileged((PrivilegedExceptionAction<Field>) () -> {
77                     final Field f = key.getDeclaredField(BindingMapping.AUGMENTATION_FIELD);
78                     f.setAccessible(true);
79                     return f;
80                 });
81             } catch (PrivilegedActionException e) {
82                 LOG.warn("Failed to acquire augmentation field {}, ignoring augmentations in class {}",
83                     BindingMapping.AUGMENTATION_FIELD, key, e);
84                 return DUMMY;
85             }
86             if (!Map.class.isAssignableFrom(field.getType())) {
87                 LOG.warn("Class {} field {} is not a Map, ignoring augmentations", key,
88                     BindingMapping.AUGMENTATION_FIELD);
89                 return DUMMY;
90             }
91
92             return new ReflectionAugmentationFieldGetter(LOOKUP.unreflectGetter(field).asType(GETTER_TYPE));
93         }
94     }
95
96     private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
97         private final MethodHandle fieldGetter;
98
99         ReflectionAugmentationFieldGetter(final MethodHandle mh) {
100             this.fieldGetter = requireNonNull(mh);
101         }
102
103         @Override
104         @SuppressWarnings("checkstyle:illegalCatch")
105         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
106             try {
107                 return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) fieldGetter.invokeExact(input);
108             } catch (Throwable e) {
109                 throw new IllegalStateException("Failed to access augmentation field on " + input, e);
110             }
111         }
112     }
113 }