Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
 Wed Apr 10 11:13:19 2019
@@ -21,19 +21,17 @@ import static com.google.common.base.Pre
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Lists.reverse;
 import static java.util.Collections.singletonList;
 import static java.util.concurrent.TimeUnit.MICROSECONDS;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.OAK;
-import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH;
-import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.JOURNAL;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.MANY_CHILDREN_THRESHOLD;
 import static 
org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS_RESOLUTION;
+import static org.apache.jackrabbit.oak.plugins.document.Path.ROOT;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
 import static 
org.apache.jackrabbit.oak.plugins.document.util.Utils.alignWithExternalRevisions;
@@ -100,7 +98,6 @@ import org.apache.jackrabbit.oak.commons
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.cache.CacheStats;
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.json.BlobSerializer;
 import 
org.apache.jackrabbit.oak.plugins.document.util.LeaseCheckDocumentStoreWrapper;
 import 
org.apache.jackrabbit.oak.plugins.document.util.LoggingDocumentStoreWrapper;
@@ -386,7 +383,7 @@ public final class DocumentNodeStore
      *
      * Key: PathRev, value: Children
      */
-    private final Cache<PathRev, DocumentNodeState.Children> nodeChildrenCache;
+    private final Cache<NamePathRev, DocumentNodeState.Children> 
nodeChildrenCache;
     private final CacheStats nodeChildrenCacheStats;
 
     /**
@@ -515,10 +512,10 @@ public final class DocumentNodeStore
      */
     private final Set<Revision> inDoubtTrunkCommits = 
Sets.newConcurrentHashSet();
 
-    private final Predicate<String> nodeCachePredicate;
+    private final Predicate<Path> nodeCachePredicate;
 
     public DocumentNodeStore(DocumentNodeStoreBuilder<?> builder) {
-        this.nodeCachePredicate = builder.getNodeCachePredicate();
+        this.nodeCachePredicate = builder.getNodeCachePathPredicate();
         this.updateLimit = builder.getUpdateLimit();
         this.commitValueResolver = new CachingCommitValueResolver(
                 builder.getCommitValueCacheSize(), this::getSweepRevisions);
@@ -591,7 +588,7 @@ public final class DocumentNodeStore
         this.lastRevRecoveryAgent = new LastRevRecoveryAgent(store, this,
                 lastRevSeeker, clusterId -> this.signalClusterStateChange());
         this.disableBranches = builder.isDisableBranches();
-        this.missing = new DocumentNodeState(this, "MISSING",
+        this.missing = new DocumentNodeState(this, new Path("missing"),
                 new RevisionVector(new Revision(0, 0, 0))) {
             @Override
             public int getMemory() {
@@ -612,7 +609,7 @@ public final class DocumentNodeStore
         diffCache = builder.getDiffCache(this.clusterId);
 
         // check if root node exists
-        NodeDocument rootDoc = store.find(NODES, Utils.getIdFromPath("/"));
+        NodeDocument rootDoc = store.find(NODES, Utils.getIdFromPath(ROOT));
         if (rootDoc == null) {
             if (readOnlyMode) {
                 throw new DocumentStoreException("Unable to initialize a " +
@@ -623,7 +620,7 @@ public final class DocumentNodeStore
             Revision commitRev = newRevision();
             RevisionVector head = new RevisionVector(commitRev);
             Commit commit = new CommitBuilder(this, commitRev, null)
-                    .addNode("/")
+                    .addNode(ROOT)
                     .build();
             try {
                 commit.applyToDocumentStore();
@@ -631,12 +628,12 @@ public final class DocumentNodeStore
                 commit.rollback();
                 throw new IllegalStateException("Conflict while creating root 
document", e);
             }
-            unsavedLastRevisions.put("/", commitRev);
+            unsavedLastRevisions.put(ROOT, commitRev);
             sweepRevisions = sweepRevisions.pmax(head);
             setRoot(head);
             // make sure _lastRev is written back to store
             backgroundWrite();
-            rootDoc = store.find(NODES, Utils.getIdFromPath("/"));
+            rootDoc = store.find(NODES, Utils.getIdFromPath(ROOT));
             // at this point the root document must exist
             if (rootDoc == null) {
                 throw new IllegalStateException("Root document does not 
exist");
@@ -653,7 +650,7 @@ public final class DocumentNodeStore
                             "missing revision for clusterId " + clusterId +
                                     ": " + rootRev);
                 }
-                unsavedLastRevisions.put("/", initialRev);
+                unsavedLastRevisions.put(ROOT, initialRev);
                 // set initial sweep revision
                 sweepRevisions = sweepRevisions.pmax(new 
RevisionVector(initialRev));
                 if (!readOnlyMode) {
@@ -752,7 +749,7 @@ public final class DocumentNodeStore
                     @Override
                     public void headOfQueue(@NotNull Revision revision) {
                         setRoot(getHeadRevision().update(revision));
-                        unsavedLastRevisions.put(ROOT_PATH, revision);
+                        unsavedLastRevisions.put(ROOT, revision);
                     }
                 });
             } catch (DocumentStoreException e) {
@@ -1074,11 +1071,11 @@ public final class DocumentNodeStore
         return nodeCache;
     }
 
-    public Cache<PathRev, DocumentNodeState.Children> getNodeChildrenCache() {
+    public Cache<NamePathRev, DocumentNodeState.Children> 
getNodeChildrenCache() {
         return nodeChildrenCache;
     }
 
-    public Predicate<String> getNodeCachePredicate() {
+    public Predicate<Path> getNodeCachePredicate() {
         return nodeCachePredicate;
     }
 
@@ -1097,7 +1094,7 @@ public final class DocumentNodeStore
     }
 
     void invalidateNodeCache(String path, RevisionVector revision){
-        nodeCache.invalidate(new PathRev(path, revision));
+        nodeCache.invalidate(new PathRev(Path.fromString(path), revision));
     }
 
     public int getPendingWriteCount() {
@@ -1118,14 +1115,15 @@ public final class DocumentNodeStore
     }
 
     @Nullable
-    AbstractDocumentNodeState getSecondaryNodeState(@NotNull final String path,
+    AbstractDocumentNodeState getSecondaryNodeState(@NotNull final Path path,
                               @NotNull final RevisionVector rootRevision,
                               @NotNull final RevisionVector rev) {
         //Check secondary cache first
         return nodeStateCache.getDocumentNodeState(path, rootRevision, rev);
     }
 
-    PropertyState createPropertyState(String name, String value){
+    @NotNull
+    public PropertyState createPropertyState(String name, String value){
         return new DocumentPropertyState(this, name, checkNotNull(value));
     }
 
@@ -1139,8 +1137,8 @@ public final class DocumentNodeStore
      *          given revision.
      */
     @Nullable
-    public DocumentNodeState getNode(@NotNull final String path,
-                              @NotNull final RevisionVector rev) {
+    public DocumentNodeState getNode(@NotNull final Path path,
+                                     @NotNull final RevisionVector rev) {
         checkNotNull(rev);
         checkNotNull(path);
         final long start = PERFLOG.start();
@@ -1173,16 +1171,16 @@ public final class DocumentNodeStore
 
     @NotNull
     DocumentNodeState.Children getChildren(@NotNull final 
AbstractDocumentNodeState parent,
-                              @Nullable final String name,
-                              final int limit)
+                                           @NotNull final String name,
+                                           final int limit)
             throws DocumentStoreException {
         if (checkNotNull(parent).hasNoChildren()) {
             return DocumentNodeState.NO_CHILDREN;
         }
-        final String path = checkNotNull(parent).getPath();
+        final Path path = checkNotNull(parent).getPath();
         final RevisionVector readRevision = parent.getLastRevision();
         try {
-            PathRev key = childNodeCacheKey(path, readRevision, name);
+            NamePathRev key = childNodeCacheKey(path, readRevision, name);
             DocumentNodeState.Children children = nodeChildrenCache.get(key, 
new Callable<DocumentNodeState.Children>() {
                 @Override
                 public DocumentNodeState.Children call() throws Exception {
@@ -1216,15 +1214,15 @@ public final class DocumentNodeStore
      * ascending order.
      *
      * @param parent the parent node.
-     * @param name the name of the lower bound child node (exclusive) or
-     *              {@code null} if no lower bound is given.
+     * @param name the name of the lower bound child node (exclusive) or the
+     *              empty {@code String} if no lower bound is given.
      * @param limit the maximum number of child nodes to return.
      * @return the children of {@code parent}.
      */
-    DocumentNodeState.Children readChildren(AbstractDocumentNodeState parent,
-                                            String name, int limit) {
+    DocumentNodeState.Children readChildren(@NotNull AbstractDocumentNodeState 
parent,
+                                            @NotNull String name, int limit) {
         String queriedName = name;
-        String path = parent.getPath();
+        Path path = parent.getPath();
         RevisionVector rev = parent.getLastRevision();
         LOG.trace("Reading children for [{}] at rev [{}]", path, rev);
         Iterable<NodeDocument> docs;
@@ -1238,10 +1236,10 @@ public final class DocumentNodeStore
             int numReturned = 0;
             for (NodeDocument doc : docs) {
                 numReturned++;
-                String p = doc.getPath();
+                Path p = doc.getPath();
                 // remember name of last returned document for
                 // potential next round of readChildDocs()
-                name = PathUtils.getName(p);
+                name = p.getName();
                 // filter out deleted children
                 DocumentNodeState child = getNode(p, rev);
                 if (child == null) {
@@ -1249,7 +1247,7 @@ public final class DocumentNodeStore
                 }
                 if (c.children.size() < limit) {
                     // add to children until limit is reached
-                    c.children.add(PathUtils.getName(p));
+                    c.children.add(p.getName());
                 } else {
                     // enough collected and we know there are more
                     c.hasMore = true;
@@ -1261,7 +1259,7 @@ public final class DocumentNodeStore
                 // fewer documents returned than requested
                 // -> no more documents
                 c.hasMore = false;
-                if (queriedName == null) {
+                if (queriedName.isEmpty()) {
                     //we've got to the end of list and we started from the top
                     //This list is complete and can be sorted
                     Collections.sort(c.children);
@@ -1279,20 +1277,21 @@ public final class DocumentNodeStore
      * lower exclusive bound.
      *
      * @param path the path of the parent document.
-     * @param name the lower exclusive bound or {@code null}.
+     * @param name the name of the lower bound child node (exclusive) or the
+     *              empty {@code String} if no lower bound is given.
      * @param limit the maximum number of child documents to return.
      * @return the child documents.
      */
     @NotNull
-    private Iterable<NodeDocument> readChildDocs(@NotNull final String path,
-                                                 @Nullable String name,
+    private Iterable<NodeDocument> readChildDocs(@NotNull final Path path,
+                                                 @NotNull String name,
                                                  final int limit) {
         final String to = Utils.getKeyUpperLimit(checkNotNull(path));
         final String from;
-        if (name != null) {
-            from = Utils.getIdFromPath(concat(path, name));
-        } else {
+        if (name.isEmpty()) {
             from = Utils.getKeyLowerLimit(path);
+        } else {
+            from = Utils.getIdFromPath(new Path(path, name));
         }
         return store.query(Collection.NODES, from, to, limit);
     }
@@ -1302,15 +1301,15 @@ public final class DocumentNodeStore
      * {@code name} (exclusive).
      *
      * @param parent the parent node.
-     * @param name the name of the lower bound child node (exclusive) or
-     *             {@code null}, if the method should start with the first 
known
-     *             child node.
+     * @param name the name of the lower bound child node (exclusive) or the
+     *             empty {@code String}, if the method should start with the
+     *             first known child node.
      * @param limit the maximum number of child nodes to return.
      * @return the child nodes.
      */
     @NotNull
     Iterable<DocumentNodeState> getChildNodes(@NotNull final DocumentNodeState 
parent,
-                    @Nullable final String name,
+                                              @NotNull final String name,
                     final int limit) {
         // Preemptive check. If we know there are no children then
         // return straight away
@@ -1322,7 +1321,7 @@ public final class DocumentNodeStore
         return transform(getChildren(parent, name, limit).children, new 
Function<String, DocumentNodeState>() {
             @Override
             public DocumentNodeState apply(String input) {
-                String p = concat(parent.getPath(), input);
+                Path p = new Path(parent.getPath(), input);
                 DocumentNodeState result = getNode(p, readRevision);
                 if (result == null) {
                     // This is very unexpected situation - parent's child list
@@ -1366,7 +1365,7 @@ public final class DocumentNodeStore
     }
 
     @Nullable
-    DocumentNodeState readNode(String path, RevisionVector readRevision) {
+    private DocumentNodeState readNode(Path path, RevisionVector readRevision) 
{
         final long start = PERFLOG.start();
         String id = Utils.getIdFromPath(path);
         Revision lastRevision = getPendingModifications().get(path);
@@ -1400,9 +1399,9 @@ public final class DocumentNodeStore
      *
      */
     void applyChanges(RevisionVector before, RevisionVector after,
-                      Revision rev, String path,
-                      boolean isNew, List<String> added,
-                      List<String> removed, List<String> changed) {
+                      Revision rev, Path path,
+                      boolean isNew, List<Path> added,
+                      List<Path> removed, List<Path> changed) {
         if (isNew) {
             // determine the revision for the nodeChildrenCache entry when
             // the node is new. Fallback to after revision in case document
@@ -1417,18 +1416,18 @@ public final class DocumentNodeStore
                 // this is a leaf node.
                 // check if it has the children flag set
                 if (doc != null && doc.hasChildren()) {
-                    PathRev key = childNodeCacheKey(path, afterLastRev, null);
+                    NamePathRev key = childNodeCacheKey(path, afterLastRev, 
"");
                     LOG.debug("nodeChildrenCache.put({},{})", key, 
"NO_CHILDREN");
                     nodeChildrenCache.put(key, DocumentNodeState.NO_CHILDREN);
                 }
             } else {
                 DocumentNodeState.Children c = new 
DocumentNodeState.Children();
                 Set<String> set = Sets.newTreeSet();
-                for (String p : added) {
-                    set.add(PathUtils.getName(p));
+                for (Path p : added) {
+                    set.add(p.getName());
                 }
                 c.children.addAll(set);
-                PathRev key = childNodeCacheKey(path, afterLastRev, null);
+                NamePathRev key = childNodeCacheKey(path, afterLastRev, "");
                 LOG.debug("nodeChildrenCache.put({},{})", key, c);
                 nodeChildrenCache.put(key, c);
             }
@@ -1437,9 +1436,9 @@ public final class DocumentNodeStore
             DocumentNodeState beforeState = getRoot(before);
             // do we have a cached before state that can be used
             // to calculate the new children?
-            int depth = PathUtils.getDepth(path);
+            int depth = path.getDepth();
             for (int i = 1; i <= depth && beforeState != null; i++) {
-                String p = PathUtils.getAncestorPath(path, depth - i);
+                Path p = path.getAncestor(depth - i);
                 RevisionVector lastRev = beforeState.getLastRevision();
                 PathRev key = new PathRev(p, lastRev);
                 beforeState = nodeCache.getIfPresent(key);
@@ -1447,10 +1446,10 @@ public final class DocumentNodeStore
                     // This is unexpected. The before state should exist.
                     // Invalidate the relevant cache entries. (OAK-6294)
                     LOG.warn("Before state is missing {}. Invalidating " +
-                            "affected cache entries.", key.asString());
+                            "affected cache entries.", key);
                     store.invalidateCache(NODES, Utils.getIdFromPath(p));
                     nodeCache.invalidate(key);
-                    nodeChildrenCache.invalidate(childNodeCacheKey(path, 
lastRev, null));
+                    nodeChildrenCache.invalidate(childNodeCacheKey(path, 
lastRev, ""));
                     beforeState = null;
                 }
             }
@@ -1459,12 +1458,12 @@ public final class DocumentNodeStore
                 if (beforeState.hasNoChildren()) {
                     children = DocumentNodeState.NO_CHILDREN;
                 } else {
-                    PathRev key = childNodeCacheKey(path, 
beforeState.getLastRevision(), null);
+                    NamePathRev key = childNodeCacheKey(path, 
beforeState.getLastRevision(), "");
                     children = nodeChildrenCache.getIfPresent(key);
                 }
             }
             if (children != null) {
-                PathRev afterKey = new PathRev(path, 
beforeState.getLastRevision().update(rev));
+                NamePathRev afterKey = childNodeCacheKey(path, 
beforeState.getLastRevision().update(rev), "");
                 // are there any added or removed children?
                 if (added.isEmpty() && removed.isEmpty()) {
                     // simply use the same list
@@ -1473,11 +1472,11 @@ public final class DocumentNodeStore
                 } else if (!children.hasMore){
                     // list is complete. use before children as basis
                     Set<String> afterChildren = 
Sets.newTreeSet(children.children);
-                    for (String p : added) {
-                        afterChildren.add(PathUtils.getName(p));
+                    for (Path p : added) {
+                        afterChildren.add(p.getName());
                     }
-                    for (String p : removed) {
-                        afterChildren.remove(PathUtils.getName(p));
+                    for (Path p : removed) {
+                        afterChildren.remove(p.getName());
                     }
                     DocumentNodeState.Children c = new 
DocumentNodeState.Children();
                     c.children.addAll(afterChildren);
@@ -1492,8 +1491,8 @@ public final class DocumentNodeStore
                     // incomplete list, but we only removed nodes
                     // use linked hash set to retain order
                     Set<String> afterChildren = 
Sets.newLinkedHashSet(children.children);
-                    for (String p : removed) {
-                        afterChildren.remove(PathUtils.getName(p));
+                    for (Path p : removed) {
+                        afterChildren.remove(p.getName());
                     }
                     DocumentNodeState.Children c = new 
DocumentNodeState.Children();
                     c.children.addAll(afterChildren);
@@ -1617,7 +1616,7 @@ public final class DocumentNodeStore
      */
     @NotNull
     DocumentNodeState getRoot(@NotNull RevisionVector revision) {
-        DocumentNodeState root = getNode("/", revision);
+        DocumentNodeState root = getNode(ROOT, revision);
         if (root == null) {
             throw new IllegalStateException(
                     "root node does not exist at revision " + revision);
@@ -1681,9 +1680,9 @@ public final class DocumentNodeStore
             revs.add(ancestorRev);
         }
         revs.addAll(b.getCommits().tailSet(ancestorRev));
-        UpdateOp rootOp = new UpdateOp(Utils.getIdFromPath("/"), false);
+        UpdateOp rootOp = new UpdateOp(Utils.getIdFromPath(ROOT), false);
         // reset each branch commit in reverse order
-        Map<String, UpdateOp> operations = Maps.newHashMap();
+        Map<Path, UpdateOp> operations = Maps.newHashMap();
         AtomicReference<Revision> currentRev = new AtomicReference<>();
         for (Revision r : reverse(revs)) {
             operations.clear();
@@ -1733,7 +1732,7 @@ public final class DocumentNodeStore
         MergeCommit commit = newMergeCommit(base, numBranchCommits);
         try {
             // make branch commits visible
-            UpdateOp op = new UpdateOp(Utils.getIdFromPath("/"), false);
+            UpdateOp op = new UpdateOp(Utils.getIdFromPath(ROOT), false);
             NodeDocument.setModified(op, commit.getRevision());
             if (b != null) {
                 // check the branch age and fail the commit
@@ -1817,7 +1816,7 @@ public final class DocumentNodeStore
         } else {
             return new LastRevTracker() {
                 @Override
-                public void track(String path) {
+                public void track(Path path) {
                     unsavedLastRevisions.put(path, r);
                 }
             };
@@ -2278,7 +2277,7 @@ public final class DocumentNodeStore
                         // was successful -> apply them to the diff cache
                         try {
                             JournalEntry.applyTo(changedPaths, diffCache,
-                                    PathUtils.ROOT_PATH, oldHead, newHead);
+                                    ROOT, oldHead, newHead);
                         } catch (Exception e1) {
                             LOG.error("backgroundRead: Exception while 
processing external changes from journal: " + e1, e1);
                         }
@@ -2323,7 +2322,7 @@ public final class DocumentNodeStore
         while ((b = branches.pollOrphanedBranch()) != null) {
             LOG.debug("Cleaning up orphaned branch with base revision: {}, " + 
                     "commits: {}", b.getBase(), b.getCommits());
-            UpdateOp op = new UpdateOp(Utils.getIdFromPath("/"), false);
+            UpdateOp op = new UpdateOp(Utils.getIdFromPath(ROOT), false);
             for (Revision r : b.getCommits()) {
                 r = r.asTrunkRevision();
                 NodeDocument.removeRevision(op, r);
@@ -2334,7 +2333,7 @@ public final class DocumentNodeStore
     }
 
     private void cleanRootCollisions() {
-        String id = Utils.getIdFromPath("/");
+        String id = Utils.getIdFromPath(ROOT);
         NodeDocument root = store.find(NODES, id);
         if (root != null) {
             cleanCollisions(root, Integer.MAX_VALUE);
@@ -2496,7 +2495,7 @@ public final class DocumentNodeStore
 
             Revision newSweepRev = sweeper.sweep(docs, new 
NodeDocumentSweepListener() {
                 @Override
-                public void sweepUpdate(final Map<String, UpdateOp> updates)
+                public void sweepUpdate(final Map<Path, UpdateOp> updates)
                         throws DocumentStoreException {
                     // create a synthetic commit. this commit does not have any
                     // changes, we just use it to create a journal entry for
@@ -2523,7 +2522,7 @@ public final class DocumentNodeStore
                     }
                 }
 
-                private void writeUpdates(Map<String, UpdateOp> updates,
+                private void writeUpdates(Map<Path, UpdateOp> updates,
                                           Revision revision)
                         throws DocumentStoreException {
                     // create journal entry
@@ -2537,7 +2536,7 @@ public final class DocumentNodeStore
                         throw new DocumentStoreException(msg);
                     }
                     changes.invalidate(Collections.singleton(r));
-                    unsavedLastRevisions.put(ROOT_PATH, revision);
+                    unsavedLastRevisions.put(ROOT, revision);
                     RevisionVector newHead = 
getHeadRevision().update(revision);
                     setRoot(newHead);
                     commitQueue.headRevisionChanged();
@@ -2774,22 +2773,22 @@ public final class DocumentNodeStore
     /**
      * Search for presence of child node as denoted by path in the children 
cache of parent
      *
-     * @param path
+     * @param path the path of the child node
      * @param rev revision at which check is performed
      * @return <code>true</code> if and only if the children cache entry for 
parent path is complete
      * and that list does not have the given child node. A <code>false</code> 
indicates that node <i>might</i>
      * exist
      */
-    private boolean checkNodeNotExistsFromChildrenCache(String path,
+    private boolean checkNodeNotExistsFromChildrenCache(Path path,
                                                         RevisionVector rev) {
-        if (PathUtils.denotesRoot(path)) {
+        final Path parentPath = path.getParent();
+        if (parentPath == null) {
             return false;
         }
 
-        final String parentPath = PathUtils.getParentPath(path);
-        PathRev key = childNodeCacheKey(parentPath, rev, null);//read first 
child cache entry
+        NamePathRev key = childNodeCacheKey(parentPath, rev, "");//read first 
child cache entry
         DocumentNodeState.Children children = 
nodeChildrenCache.getIfPresent(key);
-        String lookupChildName = PathUtils.getName(path);
+        String lookupChildName = path.getName();
 
         //Does not know about children so cannot say for sure
         if (children == null) {
@@ -2855,8 +2854,8 @@ public final class DocumentNodeStore
 
             if (continueDiff) {
                 DocumentNodeState.Children fromChildren, toChildren;
-                fromChildren = getChildren(from, null, max);
-                toChildren = getChildren(to, null, max);
+                fromChildren = getChildren(from, "", max);
+                toChildren = getChildren(to, "", max);
                 getChildrenDoneIn = debug ? now() : 0;
 
                 if (!fromChildren.hasMore && !toChildren.hasMore) {
@@ -2872,8 +2871,8 @@ public final class DocumentNodeStore
                     } else {
                         diffAlgo = "diffAllChildren";
                         max = Integer.MAX_VALUE;
-                        fromChildren = getChildren(from, null, max);
-                        toChildren = getChildren(to, null, max);
+                        fromChildren = getChildren(from, "", max);
+                        toChildren = getChildren(to, "", max);
                         diffFewChildren(w, from.getPath(), fromChildren,
                                 fromRev, toChildren, toRev);
                     }
@@ -2894,7 +2893,7 @@ public final class DocumentNodeStore
         return diff;
     }
 
-    private void diffManyChildren(JsopWriter w, String path,
+    private void diffManyChildren(JsopWriter w, Path path,
                                   RevisionVector fromRev,
                                   RevisionVector toRev) {
         long minTimestamp = Utils.getMinTimestampForDiff(
@@ -2910,7 +2909,7 @@ public final class DocumentNodeStore
         long minValue = NodeDocument.getModifiedInSecs(minTimestamp);
         String fromKey = Utils.getKeyLowerLimit(path);
         String toKey = Utils.getKeyUpperLimit(path);
-        Set<String> paths = Sets.newHashSet();
+        Set<Path> paths = Sets.newHashSet();
 
         LOG.debug("diffManyChildren: path: {}, fromRev: {}, toRev: {}", path, 
fromRev, toRev);
 
@@ -2932,10 +2931,10 @@ public final class DocumentNodeStore
                 }
             }
         }
-        for (String p : paths) {
+        for (Path p : paths) {
             DocumentNodeState fromNode = getNode(p, fromRev);
             DocumentNodeState toNode = getNode(p, toRev);
-            String name = PathUtils.getName(p);
+            String name = p.getName();
 
             LOG.trace("diffManyChildren: Changed Path {}", path);
 
@@ -2968,21 +2967,21 @@ public final class DocumentNodeStore
         }
     }
 
-    private static void addPathsForDiff(String path,
-                                        Set<String> paths,
-                                        Iterable<String> modified) {
-        for (String p : modified) {
-            if (PathUtils.denotesRoot(p)) {
+    private static void addPathsForDiff(Path path,
+                                        Set<Path> paths,
+                                        Iterable<Path> modified) {
+        for (Path p : modified) {
+            if (p.isRoot()) {
                 continue;
             }
-            String parent = PathUtils.getParentPath(p);
+            Path parent = p.getParent();
             if (path.equals(parent)) {
                 paths.add(p);
             }
         }
     }
 
-    private void diffFewChildren(JsopWriter w, String parentPath,
+    private void diffFewChildren(JsopWriter w, Path parentPath,
                                  DocumentNodeState.Children fromChildren,
                                  RevisionVector fromRev,
                                  DocumentNodeState.Children toChildren,
@@ -2992,7 +2991,7 @@ public final class DocumentNodeStore
             if (!childrenSet.contains(n)) {
                 w.tag('-').value(n);
             } else {
-                String path = concat(parentPath, n);
+                Path path = new Path(parentPath, n);
                 DocumentNodeState n1 = getNode(path, fromRev);
                 DocumentNodeState n2 = getNode(path, toRev);
                 // this is not fully correct:
@@ -3014,11 +3013,10 @@ public final class DocumentNodeStore
         }
     }
 
-    private static PathRev childNodeCacheKey(@NotNull String path,
-                                             @NotNull RevisionVector 
readRevision,
-                                             @Nullable String name) {
-        String p = (name == null ? "" : name) + path;
-        return new PathRev(p, readRevision);
+    private static NamePathRev childNodeCacheKey(@NotNull Path path,
+                                                 @NotNull RevisionVector 
readRevision,
+                                                 @NotNull String name) {
+        return new NamePathRev(name, path, readRevision);
     }
 
     private static DocumentRootBuilder asDocumentRootBuilder(NodeBuilder 
builder)

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
 Wed Apr 10 11:13:19 2019
@@ -16,7 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.document;
 
-import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -151,8 +151,7 @@ public class DocumentNodeStoreBuilder<T
     private CacheStats blobStoreCacheStats;
     private DocumentStoreStatsCollector documentStoreStatsCollector;
     private DocumentNodeStoreStatsCollector nodeStoreStatsCollector;
-    private Map<CacheType, PersistentCacheStats> persistentCacheStats =
-            new EnumMap<CacheType, PersistentCacheStats>(CacheType.class);
+    private Map<String, PersistentCacheStats> persistentCacheStats = new 
HashMap<>();
     private boolean bundlingDisabled;
     private JournalPropertyHandlerFactory journalPropertyHandlerFactory =
             new JournalPropertyHandlerFactory();
@@ -161,7 +160,7 @@ public class DocumentNodeStoreBuilder<T
     private long maxRevisionAgeMillis = DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS;
     private GCMonitor gcMonitor = new LoggingGCMonitor(
             LoggerFactory.getLogger(VersionGarbageCollector.class));
-    private Predicate<String> nodeCachePredicate = Predicates.alwaysTrue();
+    private Predicate<Path> nodeCachePredicate = Predicates.alwaysTrue();
 
     /**
      * @return a new {@link DocumentNodeStoreBuilder}.
@@ -479,7 +478,7 @@ public class DocumentNodeStoreBuilder<T
     }
 
     @NotNull
-    public Map<CacheType, PersistentCacheStats> getPersistenceCacheStats() {
+    public Map<String, PersistentCacheStats> getPersistenceCacheStats() {
         return persistentCacheStats;
     }
 
@@ -591,11 +590,11 @@ public class DocumentNodeStoreBuilder<T
         return buildCache(CacheType.NODE, getNodeCacheSize(), store, null);
     }
 
-    public Cache<PathRev, DocumentNodeState.Children> 
buildChildrenCache(DocumentNodeStore store) {
+    public Cache<NamePathRev, DocumentNodeState.Children> 
buildChildrenCache(DocumentNodeStore store) {
         return buildCache(CacheType.CHILDREN, getChildrenCacheSize(), store, 
null);
     }
 
-    public Cache<PathRev, StringValue> buildMemoryDiffCache() {
+    public Cache<CacheValue, StringValue> buildMemoryDiffCache() {
         return buildCache(CacheType.DIFF, getMemoryDiffCacheSize(), null, 
null);
     }
 
@@ -621,12 +620,29 @@ public class DocumentNodeStoreBuilder<T
         return new NodeDocumentCache(nodeDocumentsCache, 
nodeDocumentsCacheStats, prevDocumentsCache, prevDocumentsCacheStats, locks);
     }
 
+    /**
+     * @deprecated Use {@link #setNodeCachePathPredicate(Predicate)} instead.
+     */
+    @Deprecated
     public T setNodeCachePredicate(Predicate<String> p){
-        this.nodeCachePredicate = p;
+        this.nodeCachePredicate = input -> input != null && 
p.apply(input.toString());
         return thisBuilder();
     }
 
+    /**
+     * @deprecated Use {@link #getNodeCachePathPredicate()} instead.
+     */
+    @Deprecated
     public Predicate<String> getNodeCachePredicate() {
+        return input -> input != null && 
nodeCachePredicate.apply(Path.fromString(input));
+    }
+
+    public T setNodeCachePathPredicate(Predicate<Path> p){
+        this.nodeCachePredicate = p;
+        return thisBuilder();
+    }
+
+    public Predicate<Path> getNodeCachePathPredicate() {
         return nodeCachePredicate;
     }
 
@@ -654,7 +670,7 @@ public class DocumentNodeStoreBuilder<T
             }
             PersistentCacheStats stats = 
PersistentCache.getPersistentCacheStats(cache);
             if (stats != null) {
-                persistentCacheStats.put(cacheType, stats);
+                persistentCacheStats.put(cacheType.name(), stats);
             }
         }
         return cache;

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
 Wed Apr 10 11:13:19 2019
@@ -20,7 +20,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Collections.emptyList;
 import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
 import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.DEFAULT_MEMORY_CACHE_SIZE;
@@ -79,7 +78,6 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore;
 import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobIdTracker;
 import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils;
-import org.apache.jackrabbit.oak.plugins.document.persistentCache.CacheType;
 import 
org.apache.jackrabbit.oak.plugins.document.persistentCache.PersistentCacheStats;
 import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
 import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
@@ -88,7 +86,6 @@ import org.apache.jackrabbit.oak.spi.blo
 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
 import org.apache.jackrabbit.oak.spi.blob.stats.BlobStoreStatsMBean;
 import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
-import org.apache.jackrabbit.oak.spi.filter.PathFilter;
 import org.apache.jackrabbit.oak.spi.gc.DelegatingGCMonitor;
 import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
 import org.apache.jackrabbit.oak.spi.gc.GCMonitorTracker;
@@ -460,7 +457,7 @@ public class DocumentNodeStoreService {
                 setPrefetchExternalChanges(config.prefetchExternalChanges()).
                 setUpdateLimit(config.updateLimit()).
                 setJournalGCMaxAge(config.journalGCMaxAge()).
-                setNodeCachePredicate(createCachePredicate());
+                setNodeCachePathPredicate(createCachePredicate());
 
         if (!Strings.isNullOrEmpty(persistentCache)) {
             builder.setPersistentCache(persistentCache);
@@ -481,7 +478,7 @@ public class DocumentNodeStoreService {
         return customBlobStore && blobStore instanceof BlobStoreWrapper;
     }
 
-    private Predicate<String> createCachePredicate() {
+    private Predicate<Path> createCachePredicate() {
         if (config.persistentCacheIncludes().length == 0) {
             return Predicates.alwaysTrue();
         }
@@ -489,16 +486,24 @@ public class DocumentNodeStoreService {
             return Predicates.alwaysTrue();
         }
 
-        Set<String> paths = new HashSet<>();
+        Set<Path> paths = new HashSet<>();
         for (String p : config.persistentCacheIncludes()) {
             p = p != null ? Strings.emptyToNull(p.trim()) : null;
             if (p != null) {
-                paths.add(p);
+                paths.add(Path.fromString(p));
             }
         }
-        PathFilter pf = new PathFilter(paths, emptyList());
         log.info("Configuring persistent cache to only cache nodes under paths 
{}", paths);
-        return path -> path != null && pf.filter(path) == 
PathFilter.Result.INCLUDE;
+        return input -> {
+            if (input != null) {
+                for (Path p : paths) {
+                    if (p.isAncestorOf(input)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        };
     }
 
     private boolean isNodeStoreProvider() {
@@ -731,7 +736,7 @@ public class DocumentNodeStoreService {
         }
 
         // register persistent cache stats
-        Map<CacheType, PersistentCacheStats> persistenceCacheStats = 
mkBuilder.getPersistenceCacheStats();
+        Map<String, PersistentCacheStats> persistenceCacheStats = 
mkBuilder.getPersistenceCacheStats();
         for (PersistentCacheStats pcs: persistenceCacheStats.values()) {
             addRegistration(
                     registerMBean(whiteboard,

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ExternalChange.java
 Wed Apr 10 11:13:19 2019
@@ -97,7 +97,7 @@ abstract class ExternalChange {
         Clock clock = store.getClock();
         int clusterId = store.getClusterId();
         long time = clock.getTime();
-        String id = Utils.getIdFromPath("/");
+        String id = Utils.getIdFromPath(Path.ROOT);
         NodeDocument doc = store.getDocumentStore().find(NODES, id, 
store.getAsyncDelay());
         if (doc == null) {
             return stats;
@@ -145,7 +145,7 @@ abstract class ExternalChange {
                         // add changes for this particular clusterId to the 
externalSort
                         try {
                             fillExternalChanges(externalSort, invalidate,
-                                    PathUtils.ROOT_PATH, last, r,
+                                    Path.ROOT, last, r,
                                     store.getDocumentStore(), 
journalEntryConsumer,
                                     changeSetBuilder, journalPropertyHandler);
                         } catch (Exception e1) {

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalDiffLoader.java
 Wed Apr 10 11:13:19 2019
@@ -65,13 +65,13 @@ class JournalDiffLoader implements DiffC
 
     @Override
     public String call() {
-        String path = node.getPath();
         RevisionVector afterRev = node.getRootRevision();
         RevisionVector beforeRev = base.getRootRevision();
-        stats = new Stats(path, beforeRev, afterRev);
+        stats = new Stats(node.getPath(), beforeRev, afterRev);
 
         StringSort changes = JournalEntry.newSorter();
         try {
+            Path path = node.getPath();
             readTrunkChanges(path, beforeRev, afterRev, changes);
 
             readBranchChanges(path, beforeRev, changes);
@@ -80,7 +80,7 @@ class JournalDiffLoader implements DiffC
             changes.sort();
             DiffCache df = ns.getDiffCache();
             WrappedDiffCache wrappedCache = new 
WrappedDiffCache(node.getPath(), df, stats);
-            JournalEntry.applyTo(changes, wrappedCache, path, beforeRev, 
afterRev);
+            JournalEntry.applyTo(changes, wrappedCache, node.getPath(), 
beforeRev, afterRev);
 
             return wrappedCache.changes;
         } catch (IOException e) {
@@ -91,7 +91,7 @@ class JournalDiffLoader implements DiffC
         }
     }
 
-    private void readBranchChanges(String path,
+    private void readBranchChanges(Path path,
                                    RevisionVector rv,
                                    StringSort changes) throws IOException {
         if (!rv.isBranch() || ns.isDisableBranches()) {
@@ -119,7 +119,7 @@ class JournalDiffLoader implements DiffC
         }
     }
 
-    private void readTrunkChanges(String path,
+    private void readTrunkChanges(Path path,
                                   RevisionVector beforeRev,
                                   RevisionVector afterRev,
                                   StringSort changes) throws IOException {
@@ -214,14 +214,14 @@ class JournalDiffLoader implements DiffC
     private static class Stats {
 
         private final Stopwatch sw = Stopwatch.createStarted();
-        private final String path;
+        private final Path path;
         private final RevisionVector from, to;
         private long numJournalEntries;
         private long numDiffEntries;
         private long keyMemory;
         private long valueMemory;
 
-        Stats(String path, RevisionVector from, RevisionVector to) {
+        Stats(Path path, RevisionVector from, RevisionVector to) {
             this.path = path;
             this.from = from;
             this.to = to;
@@ -241,12 +241,12 @@ class JournalDiffLoader implements DiffC
 
     private static class WrappedDiffCache extends DiffCache {
 
-        private final String path;
+        private final Path path;
         private String changes = "";
         private final DiffCache cache;
         private Stats stats;
 
-        WrappedDiffCache(String path,
+        WrappedDiffCache(Path path,
                          DiffCache cache,
                          Stats stats) {
             this.path = path;
@@ -262,7 +262,7 @@ class JournalDiffLoader implements DiffC
         @Override
         String getChanges(@NotNull RevisionVector from,
                           @NotNull RevisionVector to,
-                          @NotNull String path,
+                          @NotNull Path path,
                           @Nullable Loader loader) {
             return cache.getChanges(from, to, path, loader);
         }
@@ -275,7 +275,7 @@ class JournalDiffLoader implements DiffC
             final Entry entry = cache.newEntry(from, to, local);
             return new Entry() {
                 @Override
-                public void append(@NotNull String path,
+                public void append(@NotNull Path path,
                                    @NotNull String changes) {
                     trackStats(path, from, to, changes);
                     entry.append(path, changes);
@@ -291,12 +291,12 @@ class JournalDiffLoader implements DiffC
             };
         }
 
-        private void trackStats(String path,
+        private void trackStats(Path path,
                                 RevisionVector from,
                                 RevisionVector to,
                                 String changes) {
             stats.numDiffEntries++;
-            stats.keyMemory += new StringValue(path).getMemory();
+            stats.keyMemory += path.getMemory();
             stats.keyMemory += from.getMemory();
             stats.keyMemory += to.getMemory();
             stats.valueMemory += new StringValue(changes).getMemory();

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/JournalEntry.java
 Wed Apr 10 11:13:19 2019
@@ -26,6 +26,7 @@ import java.util.Set;
 import java.util.function.Consumer;
 
 import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -45,8 +46,6 @@ import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH;
-import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.JOURNAL;
 
 /**
@@ -128,7 +127,7 @@ public final class JournalEntry extends
 
     static void applyTo(@NotNull Iterable<String> changedPaths,
                         @NotNull DiffCache diffCache,
-                        @NotNull String path,
+                        @NotNull Path path,
                         @NotNull RevisionVector from,
                         @NotNull RevisionVector to) throws IOException {
         LOG.debug("applyTo: starting for {} from {} to {}", path, from, to);
@@ -206,12 +205,14 @@ public final class JournalEntry extends
         LOG.debug("applyTo: done. totalCnt: {}, deDuplicatedCnt: {}", 
totalCnt, deDuplicatedCnt);
     }
 
-    private static boolean inScope(TreeNode node, String path) {
-        if (PathUtils.denotesRoot(path)) {
+    private static boolean inScope(TreeNode node, Path path) {
+        if (path.isRoot()) {
             return true;
         }
-        String p = node.getPath();
-        return p.startsWith(path) && (p.length() == path.length() || 
p.charAt(path.length()) == '/');
+        Path p = node.getPath();
+        int depthDiff = p.getDepth() - path.getDepth();
+        return depthDiff >= 0
+                && Iterables.elementsEqual(path.elements(), 
p.getAncestor(depthDiff).elements());
     }
 
     /**
@@ -238,7 +239,7 @@ public final class JournalEntry extends
                                    @NotNull Revision to,
                                    @NotNull DocumentStore store)
             throws IOException {
-        return fillExternalChanges(externalChanges, invalidate, ROOT_PATH,
+        return fillExternalChanges(externalChanges, invalidate, Path.ROOT,
                 from, to, store, entry -> {}, null, null);
     }
 
@@ -270,7 +271,7 @@ public final class JournalEntry extends
      */
     static int fillExternalChanges(@NotNull StringSort externalChanges,
                                    @NotNull StringSort invalidate,
-                                   @NotNull String path,
+                                   @NotNull Path path,
                                    @NotNull Revision from,
                                    @NotNull Revision to,
                                    @NotNull DocumentStore store,
@@ -343,7 +344,7 @@ public final class JournalEntry extends
 
     private static void fillFromJournalEntry(@NotNull StringSort 
externalChanges,
                                              @NotNull StringSort invalidate,
-                                             @NotNull String path,
+                                             @NotNull Path path,
                                              @Nullable ChangeSetBuilder 
changeSetBuilder,
                                              @Nullable JournalPropertyHandler 
journalPropertyHandler,
                                              @NotNull JournalEntry d,
@@ -365,9 +366,9 @@ public final class JournalEntry extends
         return Long.parseLong(parts[1], 16);
     }
 
-    void modified(String path) {
+    void modified(Path path) {
         TreeNode node = getChanges();
-        for (String name : PathUtils.elements(path)) {
+        for (String name : path.elements()) {
             if (node.get(name) == null) {
                 numChangedNodes++;
             }
@@ -375,8 +376,8 @@ public final class JournalEntry extends
         }
     }
 
-    void modified(Iterable<String> paths) {
-        for (String p : paths) {
+    void modified(Iterable<Path> paths) {
+        for (Path p : paths) {
             modified(p);
         }
     }
@@ -489,11 +490,11 @@ public final class JournalEntry extends
      * @throws IOException if an exception occurs while adding a path to
      *          {@code sort}. In this case only some paths may have been added.
      */
-    void addTo(final StringSort sort, String path) throws IOException {
+    void addTo(final StringSort sort, Path path) throws IOException {
         TraversingVisitor v = new TraversingVisitor() {
             @Override
-            public void node(TreeNode node, String p) throws IOException {
-                sort.add(p);
+            public void node(TreeNode node, Path p) throws IOException {
+                sort.add(p.toString());
             }
         };
         TreeNode n = getNode(path);
@@ -538,12 +539,12 @@ public final class JournalEntry extends
         TraversingVisitor v = new TraversingVisitor() {
 
             @Override
-            public void node(TreeNode node, String path) throws IOException {
-                sort.add(path);
+            public void node(TreeNode node, Path path) throws IOException {
+                sort.add(path.toString());
             }
         };
         for (JournalEntry e : getInvalidateOnly()) {
-            e.getChanges().accept(v, "/");
+            e.getChanges().accept(v, Path.ROOT);
         }
     }
 
@@ -613,9 +614,9 @@ public final class JournalEntry extends
     }
 
     @Nullable
-    private TreeNode getNode(String path) {
+    private TreeNode getNode(Path path) {
         TreeNode node = getChanges();
-        for (String name : PathUtils.elements(path)) {
+        for (String name : path.elements()) {
             node = node.get(name);
             if (node == null) {
                 return null;
@@ -692,23 +693,15 @@ public final class JournalEntry extends
             return n;
         }
 
-        private String getPath() {
-            return buildPath(new StringBuilder()).toString();
-        }
-
-        private StringBuilder buildPath(StringBuilder sb) {
+        private Path getPath() {
+            Path p;
             if (parent != null) {
-                parent.buildPath(sb);
-                if (parent.parent != null) {
-                    // only add slash if parent is not the root
-                    sb.append("/");
-                }
+                p = new Path(parent.getPath(), name);
             } else {
                 // this is the root
-                sb.append("/");
+                p = Path.ROOT;
             }
-            sb.append(name);
-            return sb;
+            return p;
         }
 
         void parse(JsopReader reader) {
@@ -741,10 +734,10 @@ public final class JournalEntry extends
             return children.get(name);
         }
 
-        void accept(TraversingVisitor visitor, String path) throws IOException 
{
+        void accept(TraversingVisitor visitor, Path path) throws IOException {
             visitor.node(this, path);
             for (Map.Entry<String, TreeNode> entry : children.entrySet()) {
-                entry.getValue().accept(visitor, concat(path, entry.getKey()));
+                entry.getValue().accept(visitor, new Path(path, 
entry.getKey()));
             }
         }
 
@@ -773,7 +766,7 @@ public final class JournalEntry extends
 
     private interface TraversingVisitor {
 
-        void node(TreeNode node, String path) throws IOException;
+        void node(TreeNode node, Path path) throws IOException;
     }
 
     private interface MapFactory {

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevRecoveryAgent.java
 Wed Apr 10 11:13:19 2019
@@ -22,7 +22,6 @@ import static com.google.common.collect.
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.filterKeys;
 import static java.util.Collections.singletonList;
-import static org.apache.jackrabbit.oak.commons.PathUtils.ROOT_PATH;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.JOURNAL;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static 
org.apache.jackrabbit.oak.plugins.document.util.Utils.PROPERTY_OR_DELETED;
@@ -40,7 +39,6 @@ import com.google.common.base.Supplier;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.document.util.MapFactory;
 import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 import org.apache.jackrabbit.oak.stats.Clock;
@@ -239,7 +237,7 @@ public class LastRevRecoveryAgent {
             final NodeDocumentSweeper sweeper = new 
NodeDocumentSweeper(context, true);
             sweeper.sweep(suspects, new NodeDocumentSweepListener() {
                 @Override
-                public void sweepUpdate(Map<String, UpdateOp> updates)
+                public void sweepUpdate(Map<Path, UpdateOp> updates)
                         throws DocumentStoreException {
                     if (dryRun) {
                         log.info("Dry run of sweeper identified [{}] documents 
for " +
@@ -276,7 +274,7 @@ public class LastRevRecoveryAgent {
         UnsavedModifications unsavedParents = new UnsavedModifications();
 
         //Map of known last rev of checked paths
-        Map<String, Revision> knownLastRevOrModification = 
MapFactory.getInstance().create();
+        Map<Path, Revision> knownLastRevOrModification = 
MapFactory.getInstance().create();
         final JournalEntry changes = JOURNAL.newDocument(store);
 
         long count = 0;
@@ -308,19 +306,19 @@ public class LastRevRecoveryAgent {
 
             //2. Update lastRev for parent paths aka rollup
             if (lastRevForParents != null) {
-                String path = doc.getPath();
+                Path path = doc.getPath();
                 changes.modified(path); // track all changes
                 while (true) {
-                    if (PathUtils.denotesRoot(path)) {
+                    path = path.getParent();
+                    if (path == null) {
                         break;
                     }
-                    path = PathUtils.getParentPath(path);
                     unsavedParents.put(path, lastRevForParents);
                 }
             }
         }
 
-        for (String parentPath : unsavedParents.getPaths()) {
+        for (Path parentPath : unsavedParents.getPaths()) {
             Revision calcLastRev = unsavedParents.get(parentPath);
             Revision knownLastRev = knownLastRevOrModification.get(parentPath);
             if (knownLastRev == null) {
@@ -348,11 +346,11 @@ public class LastRevRecoveryAgent {
         }
 
         if (sweepRev.get() != null) {
-            unsaved.put(ROOT_PATH, sweepRev.get());
+            unsaved.put(Path.ROOT, sweepRev.get());
         }
 
         // take the root's lastRev
-        final Revision lastRootRev = unsaved.get(ROOT_PATH);
+        final Revision lastRootRev = unsaved.get(Path.ROOT);
 
         //Note the size before persist as persist operation
         //would empty the internal state

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LastRevTracker.java
 Wed Apr 10 11:13:19 2019
@@ -27,5 +27,5 @@ public interface LastRevTracker {
      *
      * @param path the path of the document to update.
      */
-    public void track(String path);
+    public void track(Path path);
 }

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java
 Wed Apr 10 11:13:19 2019
@@ -58,7 +58,7 @@ public class LocalDiffCache extends Diff
     @Override
     public String getChanges(@NotNull RevisionVector from,
                              @NotNull RevisionVector to,
-                             @NotNull String path,
+                             @NotNull Path path,
                              @Nullable Loader loader) {
         RevisionsKey key = new RevisionsKey(from, to);
         Diff diff = diffCache.getIfPresent(key);
@@ -78,14 +78,14 @@ public class LocalDiffCache extends Diff
                           final @NotNull RevisionVector to,
                           boolean local /*ignored*/) {
         return new Entry() {
-            private final Map<String, String> changesPerPath = 
Maps.newHashMap();
+            private final Map<Path, String> changesPerPath = Maps.newHashMap();
             private long size;
             @Override
-            public void append(@NotNull String path, @NotNull String changes) {
+            public void append(@NotNull Path path, @NotNull String changes) {
                 if (exceedsSize()){
                     return;
                 }
-                size += size(path) + size(changes);
+                size += path.getMemory() + size(changes);
                 changesPerPath.put(path, changes);
             }
 
@@ -117,16 +117,16 @@ public class LocalDiffCache extends Diff
 
     public static final class Diff implements CacheValue {
 
-        private final Map<String, String> changes;
+        private final Map<Path, String> changes;
         private long memory;
 
-        public Diff(Map<String, String> changes, long memory) {
+        public Diff(Map<Path, String> changes, long memory) {
             this.changes = changes;
             this.memory = memory;
         }
 
         public static Diff fromString(String value) {
-            Map<String, String> map = Maps.newHashMap();
+            Map<Path, String> map = Maps.newHashMap();
             JsopReader reader = new JsopTokenizer(value);
             while (true) {
                 if (reader.matches(JsopReader.END)) {
@@ -135,7 +135,7 @@ public class LocalDiffCache extends Diff
                 String k = reader.readString();
                 reader.read(':');
                 String v = reader.readString();
-                map.put(k, v);
+                map.put(Path.fromString(k), v);
                 if (reader.matches(JsopReader.END)) {
                     break;
                 }
@@ -146,14 +146,14 @@ public class LocalDiffCache extends Diff
 
         public String asString(){
             JsopBuilder builder = new JsopBuilder();
-            for (Map.Entry<String, String> entry : changes.entrySet()) {
-                builder.key(entry.getKey());
+            for (Map.Entry<Path, String> entry : changes.entrySet()) {
+                builder.key(entry.getKey().toString());
                 builder.value(entry.getValue());
             }
             return builder.toString();
         }
 
-        public Map<String, String> getChanges() {
+        public Map<Path, String> getChanges() {
             return Collections.unmodifiableMap(changes);
         }
 
@@ -161,8 +161,8 @@ public class LocalDiffCache extends Diff
         public int getMemory() {
             if (memory == 0) {
                 long m = 0;
-                for (Map.Entry<String, String> e : changes.entrySet()){
-                    m += size(e.getKey()) + size(e.getValue());
+                for (Map.Entry<Path, String> e : changes.entrySet()){
+                    m += e.getKey().getMemory() + size(e.getValue());
                 }
                 memory = m;
             }
@@ -174,7 +174,7 @@ public class LocalDiffCache extends Diff
             }
         }
 
-        String get(String path) {
+        String get(Path path) {
             return changes.get(path);
         }
 

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
 Wed Apr 10 11:13:19 2019
@@ -21,6 +21,7 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
 import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.cache.CacheValue;
 import org.apache.jackrabbit.oak.plugins.document.util.StringValue;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -30,9 +31,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot;
-import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
-import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
 
 /**
  * An in-memory diff cache implementation.
@@ -53,7 +51,7 @@ public class MemoryDiffCache extends Dif
      *
      * Key: PathRev, value: StringValue
      */
-    protected final Cache<PathRev, StringValue> diffCache;
+    protected final Cache<CacheValue, StringValue> diffCache;
     protected final CacheStats diffCacheStats;
 
 
@@ -67,9 +65,9 @@ public class MemoryDiffCache extends Dif
     @Override
     public String getChanges(@NotNull final RevisionVector from,
                              @NotNull final RevisionVector to,
-                             @NotNull final String path,
+                             @NotNull final Path path,
                              @Nullable final Loader loader) {
-        PathRev key = diffCacheKey(path, from, to);
+        Key key = new Key(path, from, to);
         StringValue diff;
         if (loader == null) {
             diff = diffCache.getIfPresent(key);
@@ -121,8 +119,8 @@ public class MemoryDiffCache extends Dif
         }
 
         @Override
-        public void append(@NotNull String path, @NotNull String changes) {
-            PathRev key = diffCacheKey(path, from, to);
+        public void append(@NotNull Path path, @NotNull String changes) {
+            Key key = new Key(path, from, to);
             if (changes.length() > CACHE_VALUE_LIMIT) {
                 LOG.warn("Not caching entry for {} from {} to {}. Length of 
changes is {}.",
                         path, from, to, changes.length());
@@ -138,12 +136,6 @@ public class MemoryDiffCache extends Dif
         }
     }
 
-    private static PathRev diffCacheKey(@NotNull String path,
-                                        @NotNull RevisionVector from,
-                                        @NotNull RevisionVector to) {
-        return new PathRev(from + path, to);
-    }
-
     /**
      * Returns {@code true} if it can be inferred from cache entries on
      * ancestors of the given {@code path} that the node was not changed 
between
@@ -159,27 +151,28 @@ public class MemoryDiffCache extends Dif
      */
     private boolean isUnchanged(@NotNull final RevisionVector from,
                                 @NotNull final RevisionVector to,
-                                @NotNull final String path) {
-        return !denotesRoot(path)
-                && isChildUnchanged(from, to, getParentPath(path), 
getName(path));
+                                @NotNull final Path path) {
+        Path parent = path.getParent();
+        return parent != null
+                && isChildUnchanged(from, to, parent, path.getName());
     }
 
     private boolean isChildUnchanged(@NotNull final RevisionVector from,
                                      @NotNull final RevisionVector to,
-                                     @NotNull final String parent,
+                                     @NotNull final Path parent,
                                      @NotNull final String name) {
-        PathRev parentKey = diffCacheKey(parent, from, to);
+        Key parentKey = new Key(parent, from, to);
         StringValue parentCachedEntry = diffCache.getIfPresent(parentKey);
         boolean unchanged;
         if (parentCachedEntry == null) {
-            if (denotesRoot(parent)) {
+            if (parent.getParent() == null) {
                 // reached root and we don't know whether name
                 // changed between from and to
                 unchanged = false;
             } else {
                 // recurse down
                 unchanged = isChildUnchanged(from, to,
-                        getParentPath(parent), getName(parent));
+                        parent.getParent(), parent.getName());
             }
         } else {
             unchanged = parseJsopDiff(parentCachedEntry.asString(), new Diff() 
{
@@ -201,4 +194,107 @@ public class MemoryDiffCache extends Dif
         }
         return unchanged;
     }
+
+    public static final class Key implements CacheValue, Comparable<Key> {
+
+        private final Path path;
+
+        private final RevisionVector from;
+
+        private final RevisionVector to;
+
+        public Key(@NotNull Path path,
+                   @NotNull RevisionVector from,
+                   @NotNull RevisionVector to) {
+            this.path = checkNotNull(path);
+            this.from = checkNotNull(from);
+            this.to = checkNotNull(to);
+        }
+
+        @NotNull
+        public Path getPath() {
+            return path;
+        }
+
+        @NotNull
+        public RevisionVector getFromRevision() {
+            return from;
+        }
+
+        @NotNull
+        public RevisionVector getToRevision() {
+            return to;
+        }
+
+        public String asString() {
+            return toString();
+        }
+
+        public static Key fromString(@NotNull String s) {
+            int idx1 = s.indexOf('/');
+            int idx2 = s.lastIndexOf('@');
+            if (idx1 == -1 || idx2 == -1) {
+                throw new IllegalArgumentException("Malformed "
+                        + MemoryDiffCache.Key.class.getSimpleName() + ": " + 
s);
+            }
+            return new Key(
+                    Path.fromString(s.substring(idx1, idx2)),
+                    RevisionVector.fromString(s.substring(0, idx1)),
+                    RevisionVector.fromString(s.substring(idx2 + 1))
+            );
+        }
+
+        @Override
+        public int getMemory() {
+            return 32 + path.getMemory() + from.getMemory() + to.getMemory();
+        }
+
+        @Override
+        public int compareTo(@NotNull MemoryDiffCache.Key other) {
+            if (this == other) {
+                return 0;
+            }
+            int compare = this.from.compareTo(other.from);
+            if (compare != 0) {
+                return compare;
+            }
+            compare = this.path.compareTo(other.path);
+            if (compare != 0) {
+                return compare;
+            }
+            return this.to.compareTo(other.to);
+        }
+
+        @Override
+        public String toString() {
+            int dim = from.getDimensions() + to.getDimensions();
+            StringBuilder sb = new StringBuilder(path.length() + 
(Revision.REV_STRING_APPROX_SIZE + 1) * dim);
+            from.toStringBuilder(sb);
+            path.toStringBuilder(sb).append('@');
+            to.toStringBuilder(sb);
+            return sb.toString();
+        }
+
+        @Override
+        public int hashCode() {
+            int h = 17;
+            h = 37 * h + path.hashCode();
+            h = 37 * h + from.hashCode();
+            h = 37 * h + to.hashCode();
+            return h;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            } else if (obj instanceof Key) {
+                Key other = (Key) obj;
+                return from.equals(other.from)
+                        && to.equals(other.to)
+                        && path.equals(other.path);
+            }
+            return false;
+        }
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MissingLastRevSeeker.java
 Wed Apr 10 11:13:19 2019
@@ -39,8 +39,6 @@ import static org.apache.jackrabbit.oak.
  */
 public class MissingLastRevSeeker {
 
-    protected final String ROOT_PATH = "/";
-
     private final DocumentStore store;
 
     protected final Clock clock;
@@ -125,7 +123,7 @@ public class MissingLastRevSeeker {
     }
 
     public NodeDocument getRoot() {
-        return store.find(Collection.NODES, Utils.getIdFromPath(ROOT_PATH));
+        return store.find(Collection.NODES, Utils.getIdFromPath(Path.ROOT));
     }
 
     /**

Added: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java?rev=1857240&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java
 Wed Apr 10 11:13:19 2019
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+import org.apache.jackrabbit.oak.cache.CacheValue;
+import org.apache.jackrabbit.oak.commons.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A cache key implementation, which is a combination of a name, path and a
+ * revision vector.
+ */
+public final class NamePathRev implements CacheValue, Comparable<NamePathRev> {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(NamePathRev.class);
+
+    @NotNull
+    private final String name;
+
+    @NotNull
+    private final Path path;
+
+    @NotNull
+    private final RevisionVector revision;
+
+    public NamePathRev(@NotNull String name,
+                       @NotNull Path path,
+                       @NotNull RevisionVector revision) {
+        this.name = checkNotNull(name);
+        this.path = checkNotNull(path);
+        this.revision = checkNotNull(revision);
+    }
+
+    @NotNull
+    public Path getPath() {
+        return path;
+    }
+
+    @NotNull
+    public String getName() {
+        return name;
+    }
+
+    @NotNull
+    public RevisionVector getRevision() {
+        return revision;
+    }
+
+    @Override
+    public int getMemory() {
+        long size = 24L // shallow size
+                + path.getMemory()
+                + StringUtils.estimateMemoryUsage(name)
+                + revision.getMemory();
+        if (size > Integer.MAX_VALUE) {
+            LOG.debug("Estimated memory footprint larger than 
Integer.MAX_VALUE: {}.", size);
+            size = Integer.MAX_VALUE;
+        }
+        return (int) size;
+    }
+
+    //---------------------------< Comparable 
>---------------------------------
+
+    public int compareTo(@NotNull NamePathRev other) {
+        if (this == other) {
+            return 0;
+        }
+        int compare = this.name.compareTo(other.name);
+        if (compare != 0) {
+            return compare;
+        }
+        compare = this.path.compareTo(other.path);
+        if (compare != 0) {
+            return compare;
+        }
+        return this.revision.compareTo(other.revision);
+    }
+
+    //----------------------------< Object 
>------------------------------------
+
+    @Override
+    public int hashCode() {
+        int h = 17;
+        h = 37 * h + name.hashCode();
+        h = 37 * h + path.hashCode();
+        h = 37 * h + revision.hashCode();
+        return h;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof NamePathRev) {
+            NamePathRev other = (NamePathRev) obj;
+            return revision.equals(other.revision)
+                    && name.equals(other.name)
+                    && path.equals(other.path);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        int dim = revision.getDimensions();
+        int len = path.length() + name.length();
+        StringBuilder sb = new StringBuilder(len + 
(Revision.REV_STRING_APPROX_SIZE + 1) * dim);
+        sb.append(name);
+        path.toStringBuilder(sb).append('@');
+        revision.toStringBuilder(sb);
+        return sb.toString();
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NamePathRev.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
 Wed Apr 10 11:13:19 2019
@@ -249,6 +249,11 @@ public final class NodeDocument extends
     public static final String SD_MAX_REV_TIME_IN_SECS = "_sdMaxRevTime";
 
     /**
+     * The path prefix for previous documents.
+     */
+    private static final Path PREVIOUS_PREFIX = new Path("p");
+
+    /**
      * Return time in seconds with 5 second resolution
      *
      * @param timestamp time in millis to convert
@@ -496,18 +501,17 @@ public final class NodeDocument extends
      * @return the path of the main document.
      */
     @NotNull
-    public String getMainPath() {
-        String p = getPath();
+    public Path getMainPath() {
+        String p = getPathString();
         if (p.startsWith("p")) {
             p = PathUtils.getAncestorPath(p, 2);
             if (p.length() == 1) {
-                return "/";
+                return Path.ROOT;
             } else {
-                return p.substring(1);
+                p = p.substring(1);
             }
-        } else {
-            return p;
         }
+        return Path.fromString(p);
     }
 
     /**
@@ -647,7 +651,7 @@ public final class NodeDocument extends
      * @return the commit root path or <code>null</code>.
      */
     @Nullable
-    public String getCommitRootPath(Revision revision) {
+    public Path getCommitRootPath(Revision revision) {
         String depth = getCommitRootDepth(revision);
         if (depth != null) {
             return getPathAtDepth(depth);
@@ -917,7 +921,7 @@ public final class NodeDocument extends
             // deleted
             return null;
         }
-        String path = getPath();
+        Path path = getPath();
         List<PropertyState> props = Lists.newArrayList();
         for (String key : keySet()) {
             if (!Utils.isPropertyName(key)) {
@@ -1119,7 +1123,7 @@ public final class NodeDocument extends
      * @return if conflicting change in {@link #DELETED} property is allowed
      */
     private boolean allowConflictingDeleteChange(UpdateOp op) {
-        String path = getPath();
+        String path = getPathString();
         if (!Utils.isHiddenPath(path)) {
             return false;
         }
@@ -1255,7 +1259,7 @@ public final class NodeDocument extends
         if (revision == null) {
             return new PropertyHistory(this, property);
         } else {
-            final String mainPath = getMainPath();
+            final Path mainPath = getMainPath();
             // first try to lookup revision directly
             Map.Entry<Revision, Range> entry = 
getPreviousRanges().floorEntry(revision);
             if (entry != null) {
@@ -1863,7 +1867,7 @@ public final class NodeDocument extends
         // main document may be stale, evict it from the cache if it is
         // older than one minute. We don't want to invalidate a document
         // too frequently if the document structure is really broken.
-        String path = getMainPath();
+        Path path = getMainPath();
         String id = Utils.getIdFromPath(path);
         NodeDocument doc = store.getIfCached(NODES, id);
         long now = Revision.getCurrentTimestamp();
@@ -1974,7 +1978,7 @@ public final class NodeDocument extends
         if (getLocalRevisions().containsKey(rev)) {
             return this;
         }
-        String commitRootPath;
+        Path commitRootPath;
         String depth = getLocalCommitRoot().get(rev);
         if (depth != null) {
             commitRootPath = getPathAtDepth(depth);
@@ -2004,12 +2008,12 @@ public final class NodeDocument extends
      *              integer.
      */
     @NotNull
-    private String getPathAtDepth(@NotNull String depth) {
+    private Path getPathAtDepth(@NotNull String depth) {
         if (checkNotNull(depth).equals("0")) {
-            return "/";
+            return Path.ROOT;
         }
-        String p = getPath();
-        return PathUtils.getAncestorPath(p, PathUtils.getDepth(p) - 
Integer.parseInt(depth));
+        Path p = getPath();
+        return p.getAncestor(p.getDepth() - Integer.parseInt(depth));
     }
 
     /**
@@ -2173,7 +2177,13 @@ public final class NodeDocument extends
         return null;
     }
 
-    public String getPath() {
+    @NotNull
+    public Path getPath() {
+        return Path.fromString(getPathString());
+    }
+
+    @NotNull
+    private String getPathString() {
         String p = (String) get(PATH);
         if (p != null) {
             return p;

Modified: 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java?rev=1857240&r1=1857239&r2=1857240&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweepListener.java
 Wed Apr 10 11:13:19 2019
@@ -32,6 +32,6 @@ interface NodeDocumentSweepListener {
      *                of the documents to update.
      * @throws DocumentStoreException if the operation fails.
      */
-    void sweepUpdate(Map<String, UpdateOp> updates) throws 
DocumentStoreException;
+    void sweepUpdate(Map<Path, UpdateOp> updates) throws 
DocumentStoreException;
 
 }


Reply via email to