Factor out {SharedSingleton,ImmutableOffset}MapTemplate
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / YangInstanceIdentifier.java
index 7ee6cf8a9b396f92fcf10b99450db7c91f0c949e..b26d1b836711374958c2c6daa149f41356f3e730 100644 (file)
@@ -7,16 +7,20 @@
  */
 package org.opendaylight.yangtools.yang.data.api;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 import java.io.Serializable;
 import java.lang.reflect.Array;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -24,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import javax.annotation.Nonnull;
@@ -71,6 +76,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
  *
  * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
  */
+// FIXME: 3.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer
+//               to data nodes -- most notably XPath expressions and {@code default} statement arguments need to be able
+//               to represent these.
 public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
     /**
      * An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual
@@ -228,14 +236,14 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
             return Optional.of(this);
         }
 
-        final Iterator<?> lit = getPathArguments().iterator();
-        final Iterator<?> oit = ancestor.getPathArguments().iterator();
+        final Iterator<PathArgument> lit = getPathArguments().iterator();
+        final Iterator<PathArgument> oit = ancestor.getPathArguments().iterator();
         int common = 0;
 
         while (oit.hasNext()) {
             // Ancestor is not really an ancestor
             if (!lit.hasNext() || !lit.next().equals(oit.next())) {
-                return Optional.absent();
+                return Optional.empty();
             }
 
             ++common;
@@ -257,9 +265,9 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
             return true;
         }
 
-        Preconditions.checkArgument(other != null, "other should not be null");
-        final Iterator<?> lit = getPathArguments().iterator();
-        final Iterator<?> oit = other.getPathArguments().iterator();
+        checkArgument(other != null, "other should not be null");
+        final Iterator<PathArgument> lit = getPathArguments().iterator();
+        final Iterator<PathArgument> oit = other.getPathArguments().iterator();
 
         while (lit.hasNext()) {
             if (!oit.hasNext()) {
@@ -423,7 +431,7 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
         private transient volatile boolean hashGuard = false;
 
         protected AbstractPathArgument(final QName nodeType) {
-            this.nodeType = Preconditions.checkNotNull(nodeType);
+            this.nodeType = requireNonNull(nodeType);
         }
 
         @Override
@@ -526,9 +534,18 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
             this.keyValues = ImmutableOffsetMap.unorderedCopyOf(keyValues);
         }
 
-        public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
+        public NodeIdentifierWithPredicates(final QName node, final ImmutableOffsetMap<QName, Object> keyValues) {
+            super(node);
+            this.keyValues = requireNonNull(keyValues);
+        }
+
+        public NodeIdentifierWithPredicates(final QName node, final SharedSingletonMap<QName, Object> keyValues) {
             super(node);
-            this.keyValues = SharedSingletonMap.unorderedOf(key, value);
+            this.keyValues = requireNonNull(keyValues);
+        }
+
+        public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
+            this(node, SharedSingletonMap.unorderedOf(key, value));
         }
 
         public Map<QName, Object> getKeyValues() {
@@ -548,6 +565,7 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
         }
 
         @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
         public boolean equals(final Object obj) {
             if (!super.equals(obj)) {
                 return false;
@@ -612,6 +630,7 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
         }
 
         @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
         public boolean equals(final Object obj) {
             if (!super.equals(obj)) {
                 return false;
@@ -714,14 +733,19 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
             int thisSize = childNames.size();
             int otherSize = otherChildNames.size();
             if (thisSize == otherSize) {
-                Iterator<QName> otherIterator = otherChildNames.iterator();
-                for (QName name : childNames) {
-                    int child = name.compareTo(otherIterator.next());
-                    if (child != 0) {
-                        return child;
-                    }
+                // Quick Set-based comparison
+                if (childNames.equals(otherChildNames)) {
+                    return 0;
                 }
-                return 0;
+
+                // We already know the sets are not equal, but have equal size, hence the sets differ in their elements,
+                // but potentially share a common set of elements. The most consistent way of comparing them is using
+                // total ordering defined by QName's compareTo. Hence convert both sets to lists ordered
+                // by QName.compareTo() and decide on the first differing element.
+                final List<QName> diff = new ArrayList<>(Sets.symmetricDifference(childNames, otherChildNames));
+                verify(!diff.isEmpty(), "Augmentation identifiers %s and %s report no difference", this, o);
+                diff.sort(QName::compareTo);
+                return childNames.contains(diff.get(0)) ? -1 : 1;
             } else if (thisSize < otherSize) {
                 return 1;
             } else {
@@ -735,7 +759,7 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
      */
     public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
         /**
-         * Adds a {@link PathArgument} to to path arguments of resulting instance identifier.
+         * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
          *
          * @param arg A {@link PathArgument} to be added
          * @return this builder
@@ -770,6 +794,28 @@ public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentif
          */
         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
 
+        /**
+         * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
+         *
+         * @param args {@link PathArgument}s to be added
+         * @return this builder
+         * @throws NullPointerException if any of the arguments is null
+         */
+        @Beta
+        InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
+
+        /**
+         * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
+         *
+         * @param args {@link PathArgument}s to be added
+         * @return this builder
+         * @throws NullPointerException if any of the arguments is null
+         */
+        @Beta
+        default InstanceIdentifierBuilder append(final PathArgument... args) {
+            return append(Arrays.asList(args));
+        }
+
         /**
          * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
          *