Cache mismatched augmentations
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataObjectCodecContext.java
index bd51c11e80f1a8d115e628ef8ddaafdd16e38ad1..637585a95048b84521cd82ec89752cc7636f1a85 100644 (file)
@@ -11,6 +11,7 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSortedMap;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -50,11 +51,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer>
+abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer & WithStatus>
         extends DataContainerCodecContext<D, T> {
     private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
@@ -73,8 +75,9 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
             new ConcurrentHashMap<>();
     private final ConcurrentMap<Class<?>, DataContainerCodecPrototype<?>> byStreamAugmented = new ConcurrentHashMap<>();
 
+    private volatile ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> mismatchedAugmented = ImmutableMap.of();
 
-    protected DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
+    DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
         super(prototype);
 
         this.leafChild = factory().getLeafNodes(getBindingClass(), getSchema());
@@ -236,7 +239,7 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
             }
         } else {
             // We are looking for instantiation via uses in other module
-            final QName instantiedName = QName.create(namespace(), origDef.getQName().getLocalName());
+            final QName instantiedName = origDef.getQName().withModule(namespace());
             final DataSchemaNode potential = getSchema().getDataChildByName(instantiedName);
             // We check if it is really instantiated from same
             // definition as class was derived
@@ -281,25 +284,46 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
         }
 
         /*
-         * It is potentially mismatched valid augmentation - we look up equivalent augmentation
-         * using reflection and walk all stream child and compare augmenations classes if they are
-         * equivalent.
-         *
-         * FIXME: Cache mapping of mismatched augmentation to real one, to speed up lookup.
+         * It is potentially mismatched valid augmentation - we look up equivalent augmentation using reflection
+         * and walk all stream child and compare augmentations classes if they are equivalent. When we find a match
+         * we'll cache it so we do not need to perform reflection operations again.
          */
+        final DataContainerCodecPrototype<?> mismatched = mismatchedAugmented.get(childClass);
+        if (mismatched != null) {
+            return mismatched;
+        }
+
         @SuppressWarnings("rawtypes")
         final Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
         if (getBindingClass().equals(augTarget)) {
             for (final DataContainerCodecPrototype<?> realChild : byStreamAugmented.values()) {
                 if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
                         && BindingReflections.isSubstitutionFor(childClass, realChild.getBindingClass())) {
-                    return realChild;
+                    return cacheMismatched(childClass, realChild);
                 }
             }
         }
         return null;
     }
 
+    private synchronized DataContainerCodecPrototype<?> cacheMismatched(final Class<?> childClass,
+            final DataContainerCodecPrototype<?> prototype) {
+        // Original access was unsynchronized, we need to perform additional checking
+        final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> local = mismatchedAugmented;
+        final DataContainerCodecPrototype<?> existing = local.get(childClass);
+        if (existing != null) {
+            return existing;
+        }
+
+        final Builder<Class<?>, DataContainerCodecPrototype<?>> builder = ImmutableMap.builderWithExpectedSize(
+            local.size() + 1);
+        builder.putAll(local);
+        builder.put(childClass, prototype);
+
+        mismatchedAugmented = builder.build();
+        return prototype;
+    }
+
     private DataContainerCodecPrototype<?> getAugmentationPrototype(final Type value) {
         final ClassLoadingStrategy loader = factory().getRuntimeContext().getStrategy();
         @SuppressWarnings("rawtypes")
@@ -342,7 +366,7 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
     }
 
     @SuppressWarnings("unchecked")
-    public Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+    Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
             final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
 
         @SuppressWarnings("rawtypes")
@@ -367,7 +391,7 @@ abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeCo
         return map;
     }
 
-    public Collection<Method> getHashCodeAndEqualsMethods() {
+    Collection<Method> getHashCodeAndEqualsMethods() {
         return byMethod.keySet();
     }