Deprecate BindingCodecTreeNode.getSchema()
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / IdentityCodec.java
index 06c8c2718f8678de26e9ae1354c2c0de6493e8d1..3fa1a9c03562da03cc048f9906ba2a39648b3f86 100644 (file)
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import com.google.common.base.Preconditions;
-import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.yangtools.concepts.Codec;
+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.runtime.api.IdentityRuntimeType;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.contract.Naming;
 import org.opendaylight.yangtools.yang.common.QName;
 
-final class IdentityCodec implements Codec<QName, Class<?>> {
+final class IdentityCodec extends AbstractValueCodec<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(Naming.VALUE_STATIC_FIELD_NAME);
+                } catch (NoSuchFieldException e) {
+                    throw new LinkageError(clazz + " does not define required field " + Naming.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 LoadingCache<@NonNull Class<? extends BaseIdentity>, @NonNull QName> qnames =
+        // Note: weak keys because it is the user who is supplying implemented contract
+        CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<>() {
+            @Override
+            public QName load(final Class<? extends BaseIdentity> key) {
+                final var schema = context.getTypeWithSchema(key);
+                if (schema instanceof IdentityRuntimeType identitySchema) {
+                    return identitySchema.statement().argument();
+                }
+                throw new IllegalStateException("Unexpected schema " + schema + " for " + key);
+            }
+        });
+
     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) {
+        try {
+            return qnames.get(bindingValue.implementedInterface());
+        } catch (ExecutionException e) {
+            Throwables.throwIfUnchecked(e.getCause());
+            throw new IllegalStateException("Unexpected error translating " + bindingValue, e);
+        }
     }
 }