Bug 6006 - UnionTypeCodec fails to handle indentityref
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / UnionTypeCodec.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.binding.data.codec.impl;
9
10 import com.google.common.collect.ImmutableSet;
11 import com.google.common.io.BaseEncoding;
12 import com.google.common.util.concurrent.ExecutionError;
13 import com.google.common.util.concurrent.UncheckedExecutionException;
14 import java.lang.reflect.Constructor;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.HashSet;
18 import java.util.Set;
19 import java.util.concurrent.Callable;
20 import javax.annotation.Nullable;
21 import org.opendaylight.yangtools.concepts.Codec;
22 import org.opendaylight.yangtools.yang.binding.BindingMapping;
23 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
24 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
25
26 final class UnionTypeCodec extends ReflectionBasedCodec {
27
28     private final Codec<Object, Object> identityrefCodec;
29     private final ImmutableSet<UnionValueOptionContext> typeCodecs;
30     private final Constructor<?> charConstructor;
31
32     private UnionTypeCodec(final Class<?> unionCls,final Set<UnionValueOptionContext> codecs,
33                            @Nullable Codec<Object, Object> identityrefCodec) {
34         super(unionCls);
35         this.identityrefCodec = identityrefCodec;
36         try {
37             charConstructor = unionCls.getConstructor(char[].class);
38             typeCodecs = ImmutableSet.copyOf(codecs);
39         } catch (NoSuchMethodException | SecurityException e) {
40            throw new IllegalStateException("Required constructor is not available.",e);
41         }
42     }
43
44     static Callable<UnionTypeCodec> loader(final Class<?> unionCls, final UnionTypeDefinition unionType,
45                                            BindingCodecContext bindingCodecContext) {
46         return new Callable<UnionTypeCodec>() {
47             @Override
48             public UnionTypeCodec call() throws NoSuchMethodException, SecurityException {
49                 Codec<Object, Object> identityrefCodec = null;
50                 Set<UnionValueOptionContext> values = new HashSet<>();
51                 for (TypeDefinition<?> subtype : unionType.getTypes()) {
52                     String methodName = "get" + BindingMapping.getClassName(subtype.getQName());
53                     Method valueGetter = unionCls.getMethod(methodName);
54                     Class<?> valueType = valueGetter.getReturnType();
55                     Codec<Object, Object> valueCodec = bindingCodecContext.getCodec(valueType, subtype);
56                     if (Class.class.equals(valueType)) {
57                         identityrefCodec = valueCodec;
58                     }
59                     values.add(new UnionValueOptionContext(valueType,valueGetter, valueCodec));
60                 }
61                 return new UnionTypeCodec(unionCls, values, identityrefCodec);
62             }
63         };
64     }
65
66     @Override
67     public Object deserialize(final Object input) {
68         if (identityrefCodec != null) {
69             try {
70                 Object identityref = identityrefCodec.deserialize(input);
71                 return typeClass.getConstructor(Class.class).newInstance(identityref);
72             } catch (UncheckedExecutionException | ExecutionError e) {
73                 // ignore this exception caused by deserialize()
74             } catch (NoSuchMethodException e) {
75                 // caused by getContructor(). this case shouldn't happen.
76                 throw new IllegalStateException("Could not construct instance", e);
77             } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
78                 // ignore this exception caused by newInstance()
79             }
80         }
81         try {
82             if (input instanceof byte[]) {
83                 return charConstructor.newInstance(BaseEncoding.base64().encode((byte[]) input).toCharArray());
84             } else {
85                 return charConstructor.newInstance((input.toString().toCharArray()));
86             }
87         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
88             throw new IllegalStateException("Could not construct instance",e);
89         }
90     }
91
92     @Override
93     public Object serialize(final Object input) {
94         if (input != null) {
95             for (UnionValueOptionContext valCtx : typeCodecs) {
96                 Object domValue = valCtx.serialize(input);
97                 if (domValue != null) {
98                     return domValue;
99                 }
100             }
101         }
102         return null;
103     }
104
105 }