Index getter methods by String
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / LazyDataObject.java
index 6c61e91258c36ed57d663af08bafe73f4c151d05..e42e387eb07c532814beb338295b241745544c80 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.util.AugmentationReader;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
@@ -41,8 +42,11 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     private static final String EQUALS = "equals";
     private static final String HASHCODE = "hashCode";
     private static final String AUGMENTATIONS = "augmentations";
-    private static final Object NULL_VALUE = new Object();
 
+    private static final @NonNull Object NULL_VALUE = new Object();
+
+    // Method.getName() is guaranteed to be interned and all getter methods have zero arguments, name is sufficient to
+    // identify the data, skipping Method.hashCode() computation.
     private final ConcurrentHashMap<String, Object> cachedData = new ConcurrentHashMap<>();
     private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data;
     private final DataObjectCodecContext<D,?> context;
@@ -60,7 +64,8 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     public Object invoke(final Object proxy, final Method method, final Object[] args) {
         switch (method.getParameterCount()) {
             case 0:
-                switch (method.getName()) {
+                final String methodName = method.getName();
+                switch (methodName) {
                     case DATA_CONTAINER_GET_IMPLEMENTED_INTERFACE_NAME:
                         return context.getBindingClass();
                     case TO_STRING:
@@ -70,7 +75,7 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
                     case AUGMENTATIONS:
                         return getAugmentationsImpl();
                     default:
-                        return getBindingData(method);
+                        return getBindingData(methodName);
                 }
             case 1:
                 switch (method.getName()) {
@@ -97,13 +102,13 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
             return false;
         }
         try {
-            for (final Method m : context.getHashCodeAndEqualsMethods()) {
-                final Object thisValue = getBindingData(m);
+            for (final Method m : context.propertyMethods()) {
+                final Object thisValue = getBindingData(m.getName());
                 final Object otherValue = m.invoke(other);
                 /*
-                *   added for valid byte array comparison, when list key type is binary
-                *   deepEquals is not used since it does excessive amount of instanceof calls.
-                */
+                 *   added for valid byte array comparison, when list key type is binary
+                 *   deepEquals is not used since it does excessive amount of instanceof calls.
+                 */
                 if (thisValue instanceof byte[] && otherValue instanceof byte[]) {
                     if (!Arrays.equals((byte[]) thisValue, (byte[]) otherValue)) {
                         return false;
@@ -143,8 +148,8 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
 
         final int prime = 31;
         int result = 1;
-        for (final Method m : context.getHashCodeAndEqualsMethods()) {
-            final Object value = getBindingData(m);
+        for (final Method m : context.propertyMethods()) {
+            final Object value = getBindingData(m.getName());
             result = prime * result + Objects.hashCode(value);
         }
         if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {
@@ -155,22 +160,21 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
         return ret;
     }
 
-    private Object getBindingData(final Method method) {
-        // Guaranteed to be interned and since method has zero arguments, name is sufficient to identify the data,
-        // skipping Method.hashCode() computation.
-        final String methodName = method.getName();
-        Object cached = cachedData.get(methodName);
-        if (cached == null) {
-            final Object readedValue = context.getBindingChildValue(method, data);
-            if (readedValue == null) {
-                cached = NULL_VALUE;
-            } else {
-                cached = readedValue;
-            }
-            cachedData.putIfAbsent(methodName, cached);
+    // Internal invocation, can only target getFoo() methods
+    private Object getBindingData(final String methodName) {
+        final Object cached = cachedData.get(methodName);
+        if (cached != null) {
+            return unmaskNull(cached);
         }
 
-        return cached == NULL_VALUE ? null : cached;
+        final Object value = context.getBindingChildValue(methodName, data);
+        final Object raced = cachedData.putIfAbsent(methodName, value == null ? NULL_VALUE : value);
+        // If we raced we need to return previously-stored value
+        return raced != null ? unmaskNull(raced) : value;
+    }
+
+    private static Object unmaskNull(final @NonNull Object masked) {
+        return masked == NULL_VALUE ? null : masked;
     }
 
     private Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentationsImpl() {
@@ -226,8 +230,9 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     public String bindingToString() {
         final ToStringHelper helper = MoreObjects.toStringHelper(context.getBindingClass()).omitNullValues();
 
-        for (final Method m : context.getHashCodeAndEqualsMethods()) {
-            helper.add(m.getName(), getBindingData(m));
+        for (final Method m : context.propertyMethods()) {
+            final String methodName = m.getName();
+            helper.add(methodName, getBindingData(methodName));
         }
         if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {
             helper.add("augmentations", getAugmentationsImpl());