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