AtomixDocumentTree support for filtering notifications by DocumentPath

Change-Id: I3f4f616bc4f2e488e5433e44f72bcd121b564b0d
diff --git a/core/api/src/main/java/org/onosproject/store/service/DocumentPath.java b/core/api/src/main/java/org/onosproject/store/service/DocumentPath.java
index 1c43f4d..46b8415 100644
--- a/core/api/src/main/java/org/onosproject/store/service/DocumentPath.java
+++ b/core/api/src/main/java/org/onosproject/store/service/DocumentPath.java
@@ -17,10 +17,14 @@
 package org.onosproject.store.service;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -103,6 +107,46 @@
         return ImmutableList.copyOf(pathElements);
     }
 
+    /**
+     * Returns if the specified path belongs to a direct ancestor of the node pointed at by this path.
+     * <p>
+     * Example: {@code root.a} is a direct ancestor of {@code r.a.b.c}; while {@code r.a.x} is not.
+     *
+     * @param other other path
+     * @return {@code true} is yes; {@code false} otherwise.
+     */
+    public boolean isAncestorOf(DocumentPath other) {
+        return !other.equals(this) && other.toString().startsWith(toString());
+    }
+
+    /**
+     * Returns if the specified path is belongs to a subtree rooted this path.
+     * <p>
+     * Example: {@code root.a.b} and {@code root.a.b.c.d.e} are descendants of {@code r.a.b};
+     * while {@code r.a.x.c} is not.
+     *
+     * @param other other path
+     * @return {@code true} is yes; {@code false} otherwise.
+     */
+    public boolean isDescendentOf(DocumentPath other) {
+        return other.equals(this) || other.isAncestorOf(this);
+    }
+
+    /**
+     * Returns the path that points to the least common ancestor of the specified
+     * collection of paths.
+     * @param paths collection of path
+     * @return path to least common ancestor
+     */
+    public static DocumentPath leastCommonAncestor(Collection<DocumentPath> paths) {
+        if (CollectionUtils.isEmpty(paths)) {
+            return null;
+        }
+        return DocumentPath.from(StringUtils.getCommonPrefix(paths.stream()
+                    .map(DocumentPath::toString)
+                    .toArray(String[]::new)));
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(pathElements);