38e3e3a04e6af1b31ba6eaa175a0cd274c6a02bd
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / BitsCodec.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.base.Preconditions;
11 import com.google.common.collect.ImmutableSortedMap;
12 import java.lang.invoke.MethodHandle;
13 import java.lang.invoke.MethodHandles;
14 import java.lang.invoke.MethodType;
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.util.HashSet;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.SortedMap;
23 import java.util.TreeMap;
24 import java.util.concurrent.Callable;
25 import org.opendaylight.yangtools.binding.data.codec.impl.ValueTypeCodec.SchemaUnawareCodec;
26 import org.opendaylight.yangtools.yang.binding.BindingMapping;
27 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
29
30 final class BitsCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
31     private static final MethodType CONSTRUCTOR_INVOKE_TYPE = MethodType.methodType(Object.class, Boolean[].class);
32     private final Map<String, Method> valueGetters;
33     private final MethodHandle constructor;
34
35     private BitsCodec(final Class<?> typeClass, final SortedMap<String, Method> valueGetters,
36             final MethodHandle constructor) {
37         super(typeClass);
38         this.valueGetters = ImmutableSortedMap.copyOf(valueGetters);
39         this.constructor = Preconditions.checkNotNull(constructor);
40     }
41
42     static Callable<BitsCodec> loader(final Class<?> returnType, final BitsTypeDefinition rootType) {
43         return () -> {
44             final SortedMap<String, Method> valueGetters = new TreeMap<>();
45             for (Bit bit : rootType.getBits()) {
46                 final Method valueGetter = returnType.getMethod("is" + BindingMapping.getClassName(bit.getName()));
47                 valueGetters.put(bit.getName(), valueGetter);
48             }
49             Constructor<?> constructor = null;
50             for (Constructor<?> cst : returnType.getConstructors()) {
51                 if (!cst.getParameterTypes()[0].equals(returnType)) {
52                     constructor = cst;
53                 }
54             }
55
56             final MethodHandle ctor = MethodHandles.publicLookup().unreflectConstructor(constructor)
57                     .asSpreader(Boolean[].class, valueGetters.size()).asType(CONSTRUCTOR_INVOKE_TYPE);
58             return new BitsCodec(returnType, valueGetters, ctor);
59         };
60     }
61
62     @Override
63     public Object deserialize(final Object input) {
64         Preconditions.checkArgument(input instanceof Set);
65         @SuppressWarnings("unchecked")
66         Set<String> casted = (Set<String>) input;
67
68         final Boolean args[] = new Boolean[valueGetters.size()];
69         int currentArg = 0;
70         /*
71          * We can do this walk based on field set
72          * sorted by name, since constructor arguments in
73          * Java Binding are sorted by name.
74          *
75          * This means we will construct correct array
76          * for construction of bits object.
77          */
78         for (String value : valueGetters.keySet()) {
79             args[currentArg++] = casted.contains(value);
80         }
81
82         try {
83             return constructor.invokeExact(args);
84         } catch (Throwable e) {
85             throw new IllegalStateException("Failed to instantiate object for " + input, e);
86         }
87     }
88
89     @Override
90     public Object serialize(final Object input) {
91         Set<String> result = new HashSet<>();
92         for (Entry<String, Method> valueGet : valueGetters.entrySet()) {
93             try {
94                 Boolean value = (Boolean) valueGet.getValue().invoke(input);
95                 if (value) {
96                     result.add(valueGet.getKey());
97                 }
98             } catch (IllegalAccessException | InvocationTargetException e) {
99                 throw new IllegalArgumentException(e);
100             }
101         }
102         return result;
103     }
104 }