Map identities to proper objects
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / IdentityCodec.java
index 06c8c2718f8678de26e9ae1354c2c0de6493e8d1..1297b0ddc9da90d7ab9a829b1ab6b61c22ed1893 100644 (file)
@@ -7,29 +7,87 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import com.google.common.base.Preconditions;
-import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
+import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.common.QName;
 
-final class IdentityCodec implements Codec<QName, Class<?>> {
+final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, BaseIdentity> implements BindingIdentityCodec {
+    private final LoadingCache<@NonNull QName, @NonNull BaseIdentity> values = CacheBuilder.newBuilder()
+        .build(new CacheLoader<>() {
+            @Override
+            public BaseIdentity load(final QName key) {
+                final var clazz = context.getIdentityClass(key);
+                final Field field;
+                try {
+                    field = clazz.getField(BindingMapping.VALUE_STATIC_FIELD_NAME);
+                } catch (NoSuchFieldException e) {
+                    throw new LinkageError(clazz + " does not define required field "
+                        + BindingMapping.VALUE_STATIC_FIELD_NAME, e);
+                }
+                if (!Modifier.isStatic(field.getModifiers())) {
+                    throw new LinkageError(field + " is not static");
+                }
+
+                final Object value;
+                try {
+                    value = clazz.cast(field.get(null));
+                } catch (IllegalAccessException e) {
+                    throw new LinkageError(field + " is not accesssible", e);
+                }
+                if (value == null) {
+                    throw new LinkageError(field + " is null");
+                }
+                try {
+                    return clazz.cast(value);
+                } catch (ClassCastException e) {
+                    throw new LinkageError(field + " value " + value + " has illegal type", e);
+                }
+            }
+        });
+
     private final BindingRuntimeContext context;
 
     IdentityCodec(final BindingRuntimeContext context) {
-        this.context = Preconditions.checkNotNull(context);
+        this.context = requireNonNull(context);
+    }
+
+    @Override
+    protected BaseIdentity deserializeImpl(final QName input) {
+        return toBinding(input);
+    }
+
+    @Override
+    protected QName serializeImpl(final BaseIdentity input) {
+        return fromBinding(input);
     }
 
     @Override
-    public Class<?> deserialize(final QName input) {
-        Preconditions.checkArgument(input != null, "Input must not be null.");
-        return context.getIdentityClass(input);
+    @SuppressWarnings("unchecked")
+    public <T extends BaseIdentity> T toBinding(final QName qname) {
+        try {
+            return (T) values.get(requireNonNull(qname));
+        } catch (ExecutionException e) {
+            Throwables.throwIfUnchecked(e.getCause());
+            throw new IllegalStateException("Unexpected error translating " + qname, e);
+        }
     }
 
     @Override
-    public QName serialize(final Class<?> input) {
-        Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
-        return BindingReflections.findQName(input);
+    public QName fromBinding(final BaseIdentity bindingValue) {
+        return BindingReflections.getQName(bindingValue.implementedInterface());
     }
 }