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 06fbb0b9397bb99bf7fcc10414096302fa7f8803..1297b0ddc9da90d7ab9a829b1ab6b61c22ed1893 100644 (file)
@@ -7,17 +7,58 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import static com.google.common.base.Preconditions.checkArgument;
 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.AbstractIllegalArgumentCodec;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.common.QName;
 
-final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, Class<?>> implements BindingIdentityCodec {
+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) {
@@ -25,25 +66,28 @@ final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, Class<?>>
     }
 
     @Override
-    protected Class<?> deserializeImpl(final QName input) {
-        return context.getIdentityClass(input);
+    protected BaseIdentity deserializeImpl(final QName input) {
+        return toBinding(input);
     }
 
     @Override
-    protected QName serializeImpl(final Class<?> input) {
-        checkArgument(BaseIdentity.class.isAssignableFrom(input), "%s is not an identity", input);
-        return BindingReflections.findQName(input);
+    protected QName serializeImpl(final BaseIdentity input) {
+        return fromBinding(input);
     }
 
     @Override
-    public Class<? extends BaseIdentity> toBinding(final QName qname) {
-        final Class<?> identity = context.getIdentityClass(requireNonNull(qname));
-        checkArgument(BaseIdentity.class.isAssignableFrom(identity), "%s resolves to non-identity %s", qname, identity);
-        return identity.asSubclass(BaseIdentity.class);
+    @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 fromBinding(final Class<? extends BaseIdentity> bindingClass) {
-        return BindingReflections.getQName(bindingClass);
+    public QName fromBinding(final BaseIdentity bindingValue) {
+        return BindingReflections.getQName(bindingValue.implementedInterface());
     }
 }