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