BUG-2689: Cast exception when serializing L3Address from groupbasedpolicy policy... 58/15558/3
authorMartin Ciglan <mciglan@cisco.com>
Fri, 20 Feb 2015 15:10:15 +0000 (16:10 +0100)
committerRobert Varga <rovarga@cisco.com>
Wed, 25 Feb 2015 11:28:54 +0000 (12:28 +0100)
When deserializing L3AddressKey, alphabetical order of key arguments
didn't match with order of related QNames as defined in yang model.

The binding-independent world retains the ordering specified in model,
while the binding-aware world requires constructor arguments to be
alpha-sorted.

Change-Id: Icb277fed97d0984ca1a60aa7add7817db727facc
Signed-off-by: Martin Ciglan <mciglan@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/IdentifiableItemCodec.java

index e22f1f88fa7d2c0864d9d7ca73710be839f4077e..47a24fb593a40537781dc57994b1f9049325d9f3 100644 (file)
@@ -8,14 +8,17 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import org.opendaylight.yangtools.concepts.Codec;
@@ -26,12 +29,19 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
 final class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
+    private static final Comparator<QName> KEYARG_COMPARATOR = new Comparator<QName>() {
+        @Override
+        public int compare(final QName q1, final QName q2) {
+            return q1.getLocalName().compareToIgnoreCase(q2.getLocalName());
+        }
+    };
     private static final Lookup LOOKUP = MethodHandles.publicLookup();
-    private final MethodHandle ctor;
-    private final MethodHandle ctorInvoker;
     private final Map<QName, ValueContext> keyValueContexts;
+    private final List<QName> keysInBindingOrder;
     private final ListSchemaNode schema;
     private final Class<?> identifiable;
+    private final MethodHandle ctorInvoker;
+    private final MethodHandle ctor;
 
     public IdentifiableItemCodec(final ListSchemaNode schema, final Class<? extends Identifier<?>> keyClass,
             final Class<?> identifiable, final Map<QName, ValueContext> keyValueContexts) {
@@ -55,13 +65,38 @@ final class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates,
             keys.put(qname, keyValueContexts.get(qname));
         }
         this.keyValueContexts = ImmutableMap.copyOf(keys);
+
+        /*
+         * When instantiating binding objects we need to specify constructor arguments
+         * in alphabetic order. We play a couple of tricks here to optimize CPU/memory
+         * trade-offs.
+         *
+         * We do not have to perform a sort if the source collection has less than two
+         * elements.
+
+         * We always perform an ImmutableList.copyOf(), as that will turn into a no-op
+         * if the source is already immutable. It will also produce optimized implementations
+         * for empty and singleton collections.
+         *
+         * BUG-2755: remove this if order is made declaration-order-dependent
+         */
+        final List<QName> unsortedKeys = schema.getKeyDefinition();
+        final List<QName> sortedKeys;
+        if (unsortedKeys.size() > 1) {
+            final List<QName> tmp = new ArrayList<>(unsortedKeys);
+            Collections.sort(tmp, KEYARG_COMPARATOR);
+            sortedKeys = tmp;
+        } else {
+            sortedKeys = unsortedKeys;
+        }
+
+        this.keysInBindingOrder = ImmutableList.copyOf(sortedKeys);
     }
 
     @Override
     public IdentifiableItem<?, ?> deserialize(final NodeIdentifierWithPredicates input) {
-        final Collection<QName> keys = schema.getKeyDefinition();
-        final ArrayList<Object> bindingValues = new ArrayList<>(keys.size());
-        for (final QName key : keys) {
+        final ArrayList<Object> bindingValues = new ArrayList<>(keysInBindingOrder.size());
+        for (final QName key : keysInBindingOrder) {
             final Object yangValue = input.getKeyValues().get(key);
             bindingValues.add(keyValueContexts.get(key).deserialize(yangValue));
         }