Merge "BUG-869: added proper handling of nullable parameter"
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / YangInstanceIdentifier.java
index 60432b512faff22c47d38167c471a22ed17bbf14..e9f940a4555bad2af63797018916240a4209d3a3 100644 (file)
@@ -31,14 +31,15 @@ import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
 import org.opendaylight.yangtools.util.HashCodeBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 
 /**
- * Unique identifier of a partical node instance in the data tree.
+ * Unique identifier of a particular node instance in the data tree.
  *
  * <p>
  * Java representation of YANG Built-in type <code>instance-identifier</code>,
- * which conceptually is XPath expression minimised to uniquely identify element
+ * which conceptually is XPath expression minimized to uniquely identify element
  * in data tree which conforms to constraints maintained by YANG Model,
  * effectively this makes Instance Identifier a path to element in data tree.
  * <p>
@@ -72,19 +73,21 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
     private final Iterable<PathArgument> pathArguments;
     private final int hash;
 
-    private transient ImmutableList<PathArgument> legacyPath = null;
-    private transient String toStringCache = null;
+    private transient volatile ImmutableList<PathArgument> legacyPath = null;
+    private transient volatile String toStringCache = null;
 
     private final ImmutableList<PathArgument> getLegacyPath() {
-        if (legacyPath == null) {
+        // Temporary variable saves a volatile read
+        ImmutableList<PathArgument> ret = legacyPath;
+        if (ret == null) {
             synchronized (this) {
-                if (legacyPath == null) {
-                    legacyPath = ImmutableList.copyOf(pathArguments);
-                }
+                // We could have used a synchronized block, but let's just not bother
+                ret = ImmutableList.copyOf(pathArguments);
+                legacyPath = ret;
             }
         }
 
-        return legacyPath;
+        return ret;
     }
 
     /**
@@ -309,7 +312,7 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
     /**
      * Path argument / component of InstanceIdentifier
      *
-     * Path argument uniquelly identifies node in data tree on particular
+     * Path argument uniquely identifies node in data tree on particular
      * level.
      * <p>
      * This interface itself is used as common parent for actual
@@ -337,6 +340,17 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
          * @return Node type
          */
         QName getNodeType();
+
+        /**
+         * Return the string representation of this object for use in context
+         * provided by a previous object. This method can be implemented in
+         * terms of {@link #toString()}, but implementations are encourage to
+         * reuse any context already emitted by the previous object.
+         *
+         * @param previous Previous path argument
+         * @return String representation
+         */
+        String toRelativeString(PathArgument previous);
     }
 
     private static abstract class AbstractPathArgument implements PathArgument {
@@ -378,6 +392,18 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         public String toString() {
             return getNodeType().toString();
         }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            if (previous instanceof AbstractPathArgument) {
+                final QNameModule mod = ((AbstractPathArgument)previous).getNodeType().getModule();
+                if (getNodeType().getModule().equals(mod)) {
+                    return getNodeType().getLocalName();
+                }
+            }
+
+            return getNodeType().toString();
+        }
     }
 
     /**
@@ -496,6 +522,11 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         public String toString() {
             return super.toString() + '[' + keyValues + ']';
         }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            return super.toRelativeString(previous) + '[' + keyValues + ']';
+        }
     }
 
     /**
@@ -537,6 +568,11 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         public String toString() {
             return super.toString() + '[' + value + ']';
         }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            return super.toRelativeString(previous) + '[' + value + ']';
+        }
     }
 
     /**
@@ -588,7 +624,6 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         }
 
         /**
-         *
          * Returns set of all possible child nodes
          *
          * @return set of all possible child nodes.
@@ -604,6 +639,11 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
             return sb.toString();
         }
 
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            return toString();
+        }
+
         @Override
         public boolean equals(final Object o) {
             if (this == o) {
@@ -729,22 +769,26 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
          * The cache is thread-safe - if multiple computations occurs at the
          * same time, cache will be overwritten with same result.
          */
-        if (toStringCache != null) {
-            return toStringCache;
-        }
+        String ret = toStringCache;
+        if (ret == null) {
+            synchronized (this) {
+                ret = toStringCache;
+                if (ret == null) {
+                    final StringBuilder builder = new StringBuilder("/");
+                    PathArgument prev = null;
+                    for (PathArgument argument : getPathArguments()) {
+                        if (prev != null) {
+                            builder.append('/');
+                        }
+                        builder.append(argument.toRelativeString(prev));
+                        prev = argument;
+                    }
 
-        final StringBuilder builder = new StringBuilder('/');
-        boolean first = true;
-        for (PathArgument argument : getPathArguments()) {
-            if (first) {
-                first = false;
-            } else {
-                builder.append('/');
+                    ret = builder.toString();
+                    toStringCache = ret;
+                }
             }
-            builder.append(argument.toString());
         }
-
-        toStringCache = builder.toString();
-        return toStringCache;
+        return ret;
     }
 }