--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value.ValueTypeCodec.SchemaUnawareCodec;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+@Beta
+final class BitsCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
+
+ private static final MethodType CONSTRUCTOR_INVOKE_TYPE = MethodType.methodType(Object.class, Boolean[].class);
+ // Ordered by position
+ private final Map<String, Method> getters;
+ // Ordered by lexical name
+ private final Set<String> ctorArgs;
+ private final MethodHandle ctor;
+
+ private BitsCodec(final Class<?> typeClass, final MethodHandle ctor, final Set<String> ctorArgs,
+ final Map<String, Method> getters) {
+
+ super(typeClass);
+ this.ctor = Preconditions.checkNotNull(ctor);
+ this.ctorArgs = ImmutableSet.copyOf(ctorArgs);
+ this.getters = ImmutableMap.copyOf(getters);
+ }
+
+ static Callable<BitsCodec> loader(final Class<?> returnType, final BitsTypeDefinition rootType) {
+ return () -> {
+ final Map<String, Method> getters = new LinkedHashMap<>();
+ final Set<String> ctorArgs = new TreeSet<>();
+
+ for (Bit bit : rootType.getBits()) {
+ final Method valueGetter = returnType.getMethod("is" + JavaIdentifierNormalizer
+ .normalizeSpecificIdentifier(bit.getName(), JavaIdentifier.CLASS));
+ ctorArgs.add(bit.getName());
+ getters.put(bit.getName(), valueGetter);
+ }
+ Constructor<?> constructor = null;
+ for (Constructor<?> cst : returnType.getConstructors()) {
+ if (!cst.getParameterTypes()[0].equals(returnType)) {
+ constructor = cst;
+ }
+ }
+
+ final MethodHandle ctor = MethodHandles.publicLookup().unreflectConstructor(constructor)
+ .asSpreader(Boolean[].class, ctorArgs.size()).asType(CONSTRUCTOR_INVOKE_TYPE);
+ return new BitsCodec(returnType, ctor, ctorArgs, getters);
+ };
+ }
+
+ @Override
+ public Object deserialize(Object input) {
+ Preconditions.checkArgument(input instanceof Set);
+ @SuppressWarnings("unchecked")
+ final Set<String> casted = (Set<String>) input;
+
+ /*
+ * We can do this walk based on field set sorted by name,
+ * since constructor arguments in Java Binding are sorted by name.
+ *
+ * This means we will construct correct array for construction
+ * of bits object.
+ */
+ final Boolean args[] = new Boolean[ctorArgs.size()];
+ int currentArg = 0;
+ for (String value : ctorArgs) {
+ args[currentArg++] = casted.contains(value);
+ }
+
+ try {
+ return ctor.invokeExact(args);
+ } catch (Throwable e) {
+ throw new IllegalStateException("Failed to instantiate object for " + input, e);
+ }
+ }
+
+ @Override
+ public Object serialize(Object input) {
+ final Collection<String> result = new ArrayList<>(getters.size());
+ for (Entry<String, Method> valueGet : getters.entrySet()) {
+ final Boolean value;
+ try {
+ value = (Boolean) valueGet.getValue().invoke(input);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Failed to get bit " + valueGet.getKey(), e);
+ }
+
+ if (value) {
+ result.add(valueGet.getKey());
+ }
+ }
+ return result.size() == getters.size() ? getters.keySet() : ImmutableSet.copyOf(result);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Codec;
+
+@Beta
+final class CompositeValueCodec extends ValueTypeCodec {
+
+ private final SchemaUnawareCodec bindingToSimpleType;
+
+ @SuppressWarnings("rawtypes")
+ private final Codec bindingToDom;
+
+ CompositeValueCodec(final SchemaUnawareCodec extractor, @SuppressWarnings("rawtypes") final Codec delegate) {
+ this.bindingToSimpleType = extractor;
+ this.bindingToDom = delegate;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object deserialize(Object input) {
+ return bindingToSimpleType.deserialize(bindingToDom.deserialize(input));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object serialize(Object input) {
+ return bindingToDom.serialize(bindingToSimpleType.serialize(input));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+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 java.util.concurrent.Callable;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value.ValueTypeCodec.SchemaUnawareCodec;
+
+/**
+ * Derived YANG types are just immutable value holders for simple value
+ * types, which are same as in NormalizedNode model.
+ */
+@Beta
+final class EncapsulatedValueCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
+
+ private static final Lookup LOOKUP = MethodHandles.publicLookup();
+ private static final MethodType OBJ_METHOD = MethodType.methodType(Object.class, Object.class);
+ private final MethodHandle constructor;
+ private final MethodHandle getter;
+ private final Class<?> valueType;
+
+ private EncapsulatedValueCodec(final Class<?> typeClz, final MethodHandle constructor, final MethodHandle getter,
+ final Class<?> valueType) {
+
+ super(typeClz);
+ this.constructor = Preconditions.checkNotNull(constructor);
+ this.getter = Preconditions.checkNotNull(getter);
+ this.valueType = Preconditions.checkNotNull(valueType);
+ }
+
+ static Callable<EncapsulatedValueCodec> loader(final Class<?> typeClz) {
+ return () -> {
+ final Method m = typeClz.getMethod("getValue");
+ final MethodHandle getter = LOOKUP.unreflect(m).asType(OBJ_METHOD);
+ final Class<?> valueType = m.getReturnType();
+
+ final MethodHandle constructor = LOOKUP.findConstructor(typeClz,
+ MethodType.methodType(void.class, valueType)).asType(OBJ_METHOD);
+ return new EncapsulatedValueCodec(typeClz, constructor, getter, valueType);
+ };
+ }
+
+ /**
+ * Quick check if a value object has a chance to deserialize using {@link #deserialize(Object)}.
+ *
+ * @param value Value to be checked
+ * @return True if the value can be encapsulated
+ */
+ boolean canAcceptObject(final Object value) {
+ return valueType.isInstance(value);
+ }
+
+ @Override
+ public Object deserialize(Object input) {
+ try {
+ return constructor.invokeExact(input);
+ } catch (Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public Object serialize(Object input) {
+ try {
+ return getter.invokeExact(input);
+ } catch (Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableBiMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value.ValueTypeCodec.SchemaUnawareCodec;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+@Beta
+final class EnumerationCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
+
+ private final ImmutableBiMap<String, Enum<?>> yangValueToBinding;
+
+ private EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> schema) {
+ super(enumeration);
+ yangValueToBinding = ImmutableBiMap.copyOf(schema);
+ }
+
+ static Callable<EnumerationCodec> loader(final Class<?> returnType, final EnumTypeDefinition enumSchema) {
+ Preconditions.checkArgument(Enum.class.isAssignableFrom(returnType));
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final Class<? extends Enum<?>> enumType = (Class) returnType;
+ return () -> {
+ Map<String, Enum<?>> nameToValue = new HashMap<>();
+ for (Enum<?> enumValue : enumType.getEnumConstants()) {
+ nameToValue.put(enumValue.toString(), enumValue);
+ }
+ Map<String, Enum<?>> yangNameToBinding = new HashMap<>();
+ for (EnumPair yangValue : enumSchema.getValues()) {
+ final String bindingName = JavaIdentifierNormalizer.normalizeSpecificIdentifier(yangValue.getName(),
+ JavaIdentifier.CLASS);
+ final Enum<?> bindingVal = nameToValue.get(bindingName);
+ yangNameToBinding.put(yangValue.getName(), bindingVal);
+ }
+ return new EnumerationCodec(enumType, yangNameToBinding);
+ };
+ }
+
+ @Override
+ public Object deserialize(Object input) {
+ Enum<?> value = yangValueToBinding.get(input);
+ Preconditions.checkArgument(value != null, "Invalid enumeration value %s. Valid values are %s", input,
+ yangValueToBinding.keySet());
+ return value;
+ }
+
+ @Override
+ public Object serialize(Object input) {
+ Preconditions.checkArgument(getTypeClass().isInstance(input), "Input must be instance of %s", getTypeClass());
+ return yangValueToBinding.inverse().get(input);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+@Beta
+abstract class ReflectionBasedCodec extends ValueTypeCodec {
+
+ private final Class<?> typeClass;
+
+ ReflectionBasedCodec(final Class<?> typeClass) {
+ this.typeClass = Preconditions.checkNotNull(typeClass);
+ }
+
+ protected final Class<?> getTypeClass() {
+ return typeClass;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.value;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.mdsal.binding.javav2.spec.util.BindingReflections;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+/**
+ * Value codec, which serializes / de-serializes values from DOM simple values.
+ */
+@Beta
+abstract class ValueTypeCodec implements Codec<Object, Object> {
+
+ private static final Cache<Class<?>, SchemaUnawareCodec> staticCodecs = CacheBuilder.newBuilder().weakKeys()
+ .build();
+
+ /**
+ * Marker interface for codecs, which functionality will not be
+ * affected by schema change (introduction of new YANG modules)
+ * they may have one static instance generated when
+ * first time needed.
+ */
+ interface SchemaUnawareCodec extends Codec<Object,Object> {
+ }
+
+ /**
+ *
+ * No-op Codec, Java YANG Binding uses same types as NormalizedNode model
+ * for base YANG types, representing numbers, binary and strings.
+ */
+ public static final SchemaUnawareCodec NOOP_CODEC = new SchemaUnawareCodec() {
+
+ @Override
+ public Object serialize(final Object input) {
+ return input;
+ }
+
+ @Override
+ public Object deserialize(final Object input) {
+ return input;
+ }
+ };
+
+ public static final SchemaUnawareCodec EMPTY_CODEC = new SchemaUnawareCodec() {
+
+ @Override
+ public Object serialize(final Object arg0) {
+ // Empty type has null value in NormalizedNode and Composite Node
+ // representation
+ return null;
+ }
+
+ @Override
+ public Object deserialize(final Object arg0) {
+ /* Empty type has boolean.TRUE representation in Binding-aware world
+ * otherwise it is null / false.
+ * So when codec is triggered, empty leaf is present, that means we
+ * are safe to return true.
+ */
+ return Boolean.TRUE;
+ }
+ };
+
+ private static final Callable<? extends SchemaUnawareCodec> EMPTY_LOADER = new Callable<SchemaUnawareCodec>() {
+
+ @Override
+ public SchemaUnawareCodec call() {
+ return EMPTY_CODEC;
+ }
+ };
+
+ public static SchemaUnawareCodec getCodecFor(final Class<?> typeClz, final TypeDefinition<?> def) {
+ if (BindingReflections.isBindingClass(typeClz)) {
+ return getCachedSchemaUnawareCodec(typeClz, getCodecLoader(typeClz, def));
+ }
+ return def instanceof EmptyTypeDefinition ? EMPTY_CODEC : NOOP_CODEC;
+ }
+
+ private static SchemaUnawareCodec getCachedSchemaUnawareCodec(final Class<?> typeClz, final Callable<? extends SchemaUnawareCodec> loader) {
+ try {
+ return staticCodecs.get(typeClz, loader);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private static Callable<? extends SchemaUnawareCodec> getCodecLoader(final Class<?> typeClz, final TypeDefinition<?> def) {
+
+ TypeDefinition<?> rootType = def;
+ while (rootType.getBaseType() != null) {
+ rootType = rootType.getBaseType();
+ }
+ if (rootType instanceof EnumTypeDefinition) {
+ return EnumerationCodec.loader(typeClz, (EnumTypeDefinition) rootType);
+ } else if (rootType instanceof BitsTypeDefinition) {
+ return BitsCodec.loader(typeClz, (BitsTypeDefinition) rootType);
+ } else if (rootType instanceof EmptyTypeDefinition) {
+ return EMPTY_LOADER;
+ }
+ return EncapsulatedValueCodec.loader(typeClz);
+ }
+
+ @SuppressWarnings("rawtypes")
+ static ValueTypeCodec encapsulatedValueCodecFor(final Class<?> typeClz, final Codec delegate) {
+ SchemaUnawareCodec extractor = getCachedSchemaUnawareCodec(typeClz, EncapsulatedValueCodec.loader(typeClz));
+ return new CompositeValueCodec(extractor, delegate);
+ }
+}
\ No newline at end of file