6143f08ef59262eeb9bdd5c84e45e2676a2aecb2
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / UnionValueOptionContext.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.dom.codec.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Throwables;
13 import java.lang.invoke.MethodHandle;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.MethodType;
16 import java.lang.reflect.Method;
17 import org.opendaylight.yangtools.concepts.Codec;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 final class UnionValueOptionContext {
22     private static final MethodType OBJECT_TYPE = MethodType.methodType(Object.class, Object.class);
23     private static final Logger LOG = LoggerFactory.getLogger(UnionValueOptionContext.class);
24
25     private final Class<?> bindingType;
26     private final Codec<Object,Object> codec;
27     private final MethodHandle getter;
28     private final MethodHandle unionCtor;
29
30     UnionValueOptionContext(final Class<?> unionType, final Class<?> valueType, final Method getter,
31             final Codec<Object, Object> codec) {
32         this.bindingType = requireNonNull(valueType);
33         this.codec = requireNonNull(codec);
34
35         try {
36             this.getter = MethodHandles.publicLookup().unreflect(getter).asType(OBJECT_TYPE);
37         } catch (IllegalAccessException e) {
38             throw new IllegalStateException("Failed to access method " + getter, e);
39         }
40
41         try {
42             this.unionCtor = MethodHandles.publicLookup().findConstructor(unionType,
43                 MethodType.methodType(void.class, valueType)).asType(OBJECT_TYPE);
44         } catch (IllegalAccessException | NoSuchMethodException e) {
45             throw new IllegalStateException(String.format("Failed to access constructor for %s in type %s", valueType,
46                     unionType), e);
47         }
48     }
49
50     Object serialize(final Object input) {
51         final Object baValue = getValueFrom(input);
52         return baValue == null ? null : codec.serialize(baValue);
53     }
54
55     @SuppressWarnings("checkstyle:illegalCatch")
56     Object deserializeUnion(final Object input) {
57         // Side-step potential exceptions by checking the type if it is available
58         if (codec instanceof EncapsulatedValueCodec && !((EncapsulatedValueCodec) codec).canAcceptObject(input)) {
59             return null;
60         }
61
62         final Object value;
63         try {
64             value = codec.deserialize(input);
65         } catch (Exception e) {
66             LOG.debug("Codec {} failed to deserialize input {}", codec, input, e);
67             return null;
68         }
69
70         try {
71             return unionCtor.invokeExact(value);
72         } catch (ClassCastException e) {
73             // This case can happen. e.g. NOOP_CODEC
74             LOG.debug("Failed to instantiate {} for input {} value {}", bindingType, input, value, e);
75             return null;
76         } catch (Throwable e) {
77             throw new IllegalArgumentException("Failed to construct union for value " + value, e);
78         }
79     }
80
81     @SuppressWarnings("checkstyle:illegalCatch")
82     Object getValueFrom(final Object input) {
83         try {
84             return getter.invokeExact(input);
85         } catch (Throwable e) {
86             Throwables.throwIfUnchecked(e);
87             throw new RuntimeException(e);
88         }
89     }
90
91     @Override
92     public int hashCode() {
93         return bindingType.hashCode();
94     }
95
96     @Override
97     public boolean equals(final Object obj) {
98         if (this == obj) {
99             return true;
100         }
101         if (!(obj instanceof UnionValueOptionContext)) {
102             return false;
103         }
104
105         final UnionValueOptionContext other = (UnionValueOptionContext) obj;
106         return bindingType.equals(other.bindingType);
107     }
108 }