Rework BindingRuntimeTypes
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / IdentifiableItemCodec.java
index 776cb8e5589158a45b0c08aa2afa82827278abf8..f7ef432da873185892d7d356afd62fe11fc75d5d 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.annotations.Beta;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import java.lang.invoke.MethodHandle;
@@ -21,50 +20,50 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
-import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
 import org.opendaylight.yangtools.util.ImmutableOffsetMapTemplate;
-import org.opendaylight.yangtools.util.SharedSingletonMapTemplate;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Codec support for extracting the {@link Identifiable#key()} method return from a MapEntryNode. This class is public
- * only because implementation restrictions and can change at any time.
+ * Codec support for extracting the {@link Identifiable#key()} method return from a MapEntryNode.
  */
-@Beta
-public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
+abstract class IdentifiableItemCodec
+        extends AbstractIllegalArgumentCodec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
     private static final class SingleKey extends IdentifiableItemCodec {
         private static final MethodType CTOR_TYPE = MethodType.methodType(Identifier.class, Object.class);
 
-        private final SharedSingletonMapTemplate<QName> predicateTemplate;
         private final ValueContext keyContext;
         private final MethodHandle ctor;
         private final QName keyName;
 
-        SingleKey(final ListSchemaNode schema, final Class<? extends Identifier<?>> keyClass,
+        SingleKey(final ListEffectiveStatement schema, final Class<? extends Identifier<?>> keyClass,
                 final Class<?> identifiable, final QName keyName, final ValueContext keyContext) {
             super(schema, keyClass, identifiable);
             this.keyContext = requireNonNull(keyContext);
             this.keyName = requireNonNull(keyName);
-            predicateTemplate = SharedSingletonMapTemplate.ordered(keyName);
-            ctor = getConstructor(keyClass).asType(CTOR_TYPE);
+            ctor = getConstructor(keyClass, 1).asType(CTOR_TYPE);
         }
 
         @Override
-        Identifier<?> deserializeIdentifier(final Map<QName, Object> keyValues) throws Throwable {
-            return (Identifier<?>) ctor.invokeExact(keyContext.deserialize(keyValues.get(keyName)));
+        Identifier<?> deserializeIdentifierImpl(final NodeIdentifierWithPredicates nip) throws Throwable {
+            return (Identifier<?>) ctor.invokeExact(keyContext.deserialize(nip.getValue(keyName)));
         }
 
         @Override
         NodeIdentifierWithPredicates serializeIdentifier(final QName qname, final Identifier<?> key) {
-            return new NodeIdentifierWithPredicates(qname, predicateTemplate.instantiateWithValue(
-                keyContext.getAndSerialize(key)));
+            return NodeIdentifierWithPredicates.of(qname, keyName, keyContext.getAndSerialize(key));
         }
     }
 
@@ -74,19 +73,20 @@ public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithP
         private final ImmutableList<QName> keysInBindingOrder;
         private final MethodHandle ctor;
 
-        MultiKey(final ListSchemaNode schema, final Class<? extends Identifier<?>> keyClass,
+        MultiKey(final ListEffectiveStatement schema, final Class<? extends Identifier<?>> keyClass,
                 final Class<?> identifiable, final Map<QName, ValueContext> keyValueContexts) {
             super(schema, keyClass, identifiable);
 
-            final MethodHandle tmpCtor = getConstructor(keyClass);
+            final MethodHandle tmpCtor = getConstructor(keyClass, keyValueContexts.size());
             final MethodHandle inv = MethodHandles.spreadInvoker(tmpCtor.type(), 0);
-            this.ctor = inv.asType(inv.type().changeReturnType(Identifier.class)).bindTo(tmpCtor);
+            ctor = inv.asType(inv.type().changeReturnType(Identifier.class)).bindTo(tmpCtor);
 
             /*
              * We need to re-index to make sure we instantiate nodes in the order in which they are defined. We will
              * also need to instantiate values in the same order.
              */
-            final List<QName> keyDef = schema.getKeyDefinition();
+            final Set<QName> keyDef = schema.findFirstEffectiveSubstatementArgument(KeyEffectiveStatement.class)
+                .orElseThrow();
             predicateTemplate = ImmutableOffsetMapTemplate.ordered(keyDef);
             this.keyValueContexts = predicateTemplate.instantiateTransformed(keyValueContexts, (key, value) -> value);
 
@@ -99,15 +99,15 @@ public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithP
             final List<QName> tmp = new ArrayList<>(keyDef);
             // This is not terribly efficient but gets the job done
             tmp.sort(Comparator.comparing(qname -> BindingMapping.getPropertyName(qname.getLocalName())));
-            this.keysInBindingOrder = ImmutableList.copyOf(tmp.equals(keyDef) ? keyDef : tmp);
+            keysInBindingOrder = ImmutableList.copyOf(tmp.equals(List.copyOf(keyDef)) ? keyDef : tmp);
         }
 
         @Override
-        Identifier<?> deserializeIdentifier(final Map<QName, Object> keyValues) throws Throwable {
+        Identifier<?> deserializeIdentifierImpl(final NodeIdentifierWithPredicates nip) throws Throwable {
             final Object[] bindingValues = new Object[keysInBindingOrder.size()];
             int offset = 0;
             for (final QName key : keysInBindingOrder) {
-                bindingValues[offset++] = keyValueContexts.get(key).deserialize(keyValues.get(key));
+                bindingValues[offset++] = keyValueContexts.get(key).deserialize(nip.getValue(key));
             }
 
             return (Identifier<?>) ctor.invokeExact(bindingValues);
@@ -121,20 +121,22 @@ public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithP
                 values[offset++] = valueCtx.getAndSerialize(key);
             }
 
-            return new NodeIdentifierWithPredicates(qname, predicateTemplate.instantiateWithValues(values));
+            return NodeIdentifierWithPredicates.of(qname, predicateTemplate.instantiateWithValues(values));
         }
     }
 
+    private static final Logger LOG = LoggerFactory.getLogger(IdentifiableItemCodec.class);
+
     private final Class<?> identifiable;
     private final QName qname;
 
-    IdentifiableItemCodec(final ListSchemaNode schema, final Class<? extends Identifier<?>> keyClass,
+    IdentifiableItemCodec(final ListEffectiveStatement schema, final Class<? extends Identifier<?>> keyClass,
             final Class<?> identifiable) {
         this.identifiable = requireNonNull(identifiable);
-        this.qname = schema.getQName();
+        qname = schema.argument();
     }
 
-    static IdentifiableItemCodec of(final ListSchemaNode schema,
+    static IdentifiableItemCodec of(final ListEffectiveStatement schema,
             final Class<? extends Identifier<?>> keyClass, final Class<?> identifiable,
                     final Map<QName, ValueContext> keyValueContexts) {
         switch (keyValueContexts.size()) {
@@ -149,41 +151,64 @@ public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithP
     }
 
     @Override
-    @SuppressWarnings({ "rawtypes", "unchecked", "checkstyle:illegalCatch" })
-    public final IdentifiableItem<?, ?> deserialize(final NodeIdentifierWithPredicates input) {
-        final Identifier<?> identifier;
-        try {
-            identifier = deserializeIdentifier(input.getKeyValues());
-        } catch (Throwable e) {
-            Throwables.throwIfUnchecked(e);
-            throw new IllegalStateException("Failed to deserialize " + input, e);
-        }
-
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected final IdentifiableItem<?, ?> deserializeImpl(final NodeIdentifierWithPredicates input) {
+        final Identifier<?> identifier = deserializeIdentifier(input);
         return IdentifiableItem.of((Class) identifiable, (Identifier) identifier);
     }
 
     @Override
-    public final NodeIdentifierWithPredicates serialize(final IdentifiableItem<?, ?> input) {
+    protected final NodeIdentifierWithPredicates serializeImpl(final IdentifiableItem<?, ?> input) {
         return serializeIdentifier(qname, input.getKey());
     }
 
+    @SuppressWarnings("checkstyle:illegalCatch")
+    final @NonNull Identifier<?> deserializeIdentifier(final NodeIdentifierWithPredicates input) {
+        try {
+            return deserializeIdentifierImpl(input);
+        } catch (Throwable e) {
+            Throwables.throwIfUnchecked(e);
+            throw new IllegalStateException("Failed to deserialize " + input, e);
+        }
+    }
+
     @SuppressWarnings("checkstyle:illegalThrows")
-    abstract Identifier<?> deserializeIdentifier(Map<QName, Object> keyValues) throws Throwable;
-
-    abstract NodeIdentifierWithPredicates serializeIdentifier(QName qname, Identifier<?> key);
-
-    static MethodHandle getConstructor(final Class<? extends Identifier<?>> clazz) {
-        for (@SuppressWarnings("rawtypes") final Constructor constr : clazz.getConstructors()) {
-            final Class<?>[] parameters = constr.getParameterTypes();
-            if (!clazz.equals(parameters[0])) {
-                // It is not copy constructor;
-                try {
-                    return MethodHandles.publicLookup().unreflectConstructor(constr);
-                } catch (IllegalAccessException e) {
-                    throw new IllegalStateException("Cannot access constructor " + constr + " in class " + clazz, e);
-                }
+    abstract @NonNull Identifier<?> deserializeIdentifierImpl(@NonNull NodeIdentifierWithPredicates nip)
+            throws Throwable;
+
+    abstract @NonNull NodeIdentifierWithPredicates serializeIdentifier(QName qname, Identifier<?> key);
+
+    static MethodHandle getConstructor(final Class<? extends Identifier<?>> clazz, final int nrArgs) {
+        for (final Constructor<?> ctor : clazz.getConstructors()) {
+            // Check argument count
+            if (ctor.getParameterCount() != nrArgs) {
+                LOG.debug("Skipping {} due to argument count mismatch", ctor);
+                continue;
+            }
+
+            // Do not consider deprecated constructors
+            if (isDeprecated(ctor)) {
+                LOG.debug("Skipping deprecated constructor {}", ctor);
+                continue;
+            }
+
+            // Do not consider copy constructors
+            if (clazz.equals(ctor.getParameterTypes()[0])) {
+                LOG.debug("Skipping copy constructor {}", ctor);
+                continue;
+            }
+
+            try {
+                return MethodHandles.publicLookup().unreflectConstructor(ctor);
+            } catch (IllegalAccessException e) {
+                throw new IllegalStateException("Cannot access constructor " + ctor + " in class " + clazz, e);
             }
         }
-        throw new IllegalArgumentException("Supplied class " + clazz + "does not have required constructor.");
+        throw new IllegalArgumentException("Supplied class " + clazz + " does not have required constructor.");
+    }
+
+    // This could be inlined, but then it throws off Eclipse analysis, which thinks the return is always non-null
+    private static boolean isDeprecated(final Constructor<?> ctor) {
+        return ctor.getAnnotation(Deprecated.class) != null;
     }
 }