2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.binding.data.codec.impl;
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.invoke.MethodHandle;
15 import java.lang.invoke.MethodHandles;
16 import java.lang.invoke.MethodType;
17 import java.lang.reflect.Method;
18 import java.util.HashSet;
20 import java.util.concurrent.Callable;
21 import javax.annotation.Nullable;
22 import org.opendaylight.yangtools.concepts.Codec;
23 import org.opendaylight.yangtools.yang.binding.BindingMapping;
24 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 final class UnionTypeCodec extends ReflectionBasedCodec {
30 private static final MethodType CHARARRAY_LOOKUP_TYPE = MethodType.methodType(void.class, char[].class);
31 private static final MethodType CHARARRAY_INVOKE_TYPE = MethodType.methodType(Object.class, char[].class);
32 private static final MethodType CLASS_LOOKUP_TYPE = MethodType.methodType(void.class, Class.class);
33 private static final MethodType CLASS_INVOKE_TYPE = MethodType.methodType(Object.class, Object.class);
34 private static final Logger LOG = LoggerFactory.getLogger(UnionTypeCodec.class);
36 private final Codec<Object, Object> idRefCodec;
37 private final MethodHandle idrefConstructor;
39 private final ImmutableSet<UnionValueOptionContext> typeCodecs;
40 private final MethodHandle charConstructor;
42 private UnionTypeCodec(final Class<?> unionCls,final Set<UnionValueOptionContext> codecs,
43 @Nullable final Codec<Object, Object> identityrefCodec) {
45 this.idRefCodec = identityrefCodec;
46 if (idRefCodec != null) {
48 idrefConstructor = MethodHandles.publicLookup().findConstructor(unionCls, CLASS_LOOKUP_TYPE)
49 .asType(CLASS_INVOKE_TYPE);
50 } catch (IllegalAccessException | NoSuchMethodException e) {
51 throw new IllegalStateException("Failed to get identityref constructor", e);
54 idrefConstructor = null;
58 charConstructor = MethodHandles.publicLookup().findConstructor(unionCls, CHARARRAY_LOOKUP_TYPE)
59 .asType(CHARARRAY_INVOKE_TYPE);
60 } catch (IllegalAccessException | NoSuchMethodException e) {
61 throw new IllegalStateException("Failed to instantiate handle for constructor", e);
64 typeCodecs = ImmutableSet.copyOf(codecs);
67 static Callable<UnionTypeCodec> loader(final Class<?> unionCls, final UnionTypeDefinition unionType,
68 final BindingCodecContext bindingCodecContext) {
69 return new Callable<UnionTypeCodec>() {
71 public UnionTypeCodec call() throws NoSuchMethodException, SecurityException {
72 Codec<Object, Object> identityrefCodec = null;
73 Set<UnionValueOptionContext> values = new HashSet<>();
74 for (TypeDefinition<?> subtype : unionType.getTypes()) {
75 String methodName = "get" + BindingMapping.getClassName(subtype.getQName());
76 Method valueGetter = unionCls.getMethod(methodName);
77 Class<?> valueType = valueGetter.getReturnType();
78 Codec<Object, Object> valueCodec = bindingCodecContext.getCodec(valueType, subtype);
79 if (Class.class.equals(valueType)) {
80 identityrefCodec = valueCodec;
82 values.add(new UnionValueOptionContext(valueType,valueGetter, valueCodec));
84 return new UnionTypeCodec(unionCls, values, identityrefCodec);
89 private Object deserializeString(final Object input) {
90 final String str = input instanceof byte[] ? BaseEncoding.base64().encode((byte[]) input) : input.toString();
92 return charConstructor.invokeExact(str.toCharArray());
93 } catch (Throwable e) {
94 throw new IllegalStateException("Could not construct instance", e);
99 public Object deserialize(final Object input) {
100 if (idRefCodec != null) {
101 final Object identityref;
103 identityref = idRefCodec.deserialize(input);
104 } catch (UncheckedExecutionException | ExecutionError | ClassCastException e) {
105 LOG.debug("Deserialization of {} as identityref failed", e);
106 return deserializeString(input);
110 return idrefConstructor.invokeExact(identityref);
111 } catch (Throwable e) {
112 LOG.debug("Failed to instantiate based on identityref {}", identityref, e);
116 return deserializeString(input);
120 public Object serialize(final Object input) {
122 for (UnionValueOptionContext valCtx : typeCodecs) {
123 Object domValue = valCtx.serialize(input);
124 if (domValue != null) {