BUG-8004: handle implicit RPC input
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / UnionValueOptionContext.java
index 7c4c14453f8649ae8b55fe8002a656d567de9d2e..06130fb824b773ab3ca53daecc3d5a6365bcefb5 100644 (file)
@@ -11,37 +11,68 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import org.opendaylight.yangtools.concepts.Codec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 final class UnionValueOptionContext {
-    private static final Lookup LOOKUP = MethodHandles.publicLookup();
     private static final MethodType OBJECT_TYPE = MethodType.methodType(Object.class, Object.class);
+    private static final Logger LOG = LoggerFactory.getLogger(UnionValueOptionContext.class);
+
     private final Class<?> bindingType;
-    // FIXME: migrate to invocation
-    private final MethodHandle getter;
     private final Codec<Object,Object> codec;
+    private final MethodHandle getter;
+    private final MethodHandle unionCtor;
 
-    UnionValueOptionContext(final Class<?> valueType, final Method getter, final Codec<Object, Object> codec) {
+    UnionValueOptionContext(final Class<?> unionType, final Class<?> valueType, final Method getter, final Codec<Object, Object> codec) {
         this.bindingType = Preconditions.checkNotNull(valueType);
         this.codec = Preconditions.checkNotNull(codec);
 
         try {
-            this.getter = LOOKUP.unreflect(getter).asType(OBJECT_TYPE);
+            this.getter = MethodHandles.publicLookup().unreflect(getter).asType(OBJECT_TYPE);
         } catch (IllegalAccessException e) {
             throw new IllegalStateException("Failed to access method " + getter, e);
         }
+
+        try {
+            this.unionCtor = MethodHandles.publicLookup().findConstructor(unionType,
+                MethodType.methodType(void.class, valueType)).asType(OBJECT_TYPE);
+        } catch (IllegalAccessException | NoSuchMethodException e) {
+            throw new IllegalStateException(String.format("Failed to access constructor for %s in type %s", valueType,
+                    unionType), e);
+        }
     }
 
     Object serialize(final Object input) {
         final Object baValue = getValueFrom(input);
-        if (baValue == null) {
+        return baValue == null ? null : codec.serialize(baValue);
+    }
+
+    Object deserializeUnion(final Object input) {
+        // Side-step potential exceptions by checking the type if it is available
+        if (codec instanceof EncapsulatedValueCodec && !((EncapsulatedValueCodec) codec).canAcceptObject(input)) {
             return null;
         }
 
-        return codec.serialize(baValue);
+        final Object value;
+        try {
+            value = codec.deserialize(input);
+        } catch (Exception e) {
+            LOG.debug("Codec {} failed to deserialize input {}", codec, input, e);
+            return null;
+        }
+
+        try {
+            return unionCtor.invokeExact(value);
+        } catch (ClassCastException e) {
+            // This case can happen. e.g. NOOP_CODEC
+            LOG.debug("Failed to instantiate {} for input {} value {}", bindingType, input, value, e);
+            return null;
+        } catch (Throwable e) {
+            throw new IllegalArgumentException("Failed to construct union for value " + value, e);
+        }
     }
 
     Object getValueFrom(final Object input) {
@@ -69,4 +100,4 @@ final class UnionValueOptionContext {
         final UnionValueOptionContext other = (UnionValueOptionContext) obj;
         return bindingType.equals(other.bindingType);
     }
-}
\ No newline at end of file
+}