jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mdue...@apache.org
Subject svn commit: r1339630 [1/2] - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-core/src/main/java/org/apac...
Date Thu, 17 May 2012 15:08:03 GMT
Author: mduerig
Date: Thu May 17 15:08:03 2012
New Revision: 1339630

URL: http://svn.apache.org/viewvc?rev=1339630&view=rev
Log:
OAK-102: Expose the branch feature from NodeStore
- initial implementation

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStoreBranch.java
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStore.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitHook.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateBuilder.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ProxyNodeState.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreTest.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java Thu May 17 15:08:03 2012
@@ -64,6 +64,11 @@ public interface Root {
     void rebase();
 
     /**
+     * Clear all changes made to this root
+     */
+    void clear();
+
+    /**
      * Atomically apply all changes made to the tree beneath this root to the
      * underlying store and refreshes this root. After a call to this method,
      * all trees obtained through {@link #getTree(String)} are invalid and fresh

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java Thu May 17 15:08:03 2012
@@ -19,16 +19,16 @@
 package org.apache.jackrabbit.oak.core;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.core.TreeImpl.Listener;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,11 +39,6 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
 
-/**
- * This {@code Root} implementation listens on the root of the underlying
- * {@link Tree} using a {@link Listener}. All changes are directly applied
- * to the {@link NodeStateBuilder} for the relevant sub-tree.
- */
 public class RootImpl implements Root {
     static final Logger log = LoggerFactory.getLogger(RootImpl.class);
 
@@ -53,17 +48,11 @@ public class RootImpl implements Root {
     /** The name of the workspace we are operating on */
     private final String workspaceName;
 
-    /** Listener for changes on the tree */
-    private TreeListener treeListener = new TreeListener();
+    /** Actual root element of the {@code Tree} */
+    private final TreeImpl root;
 
-    /** Base node state of this tree */
-    private NodeState base;
-
-    /** The builder for this root */
-    private NodeStateBuilder nodeStateBuilder;
-
-    /** Root state of this tree */
-    private TreeImpl root;
+    /** Current branch this root operates on */
+    private NodeStoreBranch branch;
 
     /**
      * New instance bases on a given {@link NodeStore} and a workspace
@@ -73,9 +62,8 @@ public class RootImpl implements Root {
     public RootImpl(NodeStore store, String workspaceName) {
         this.store = store;
         this.workspaceName = workspaceName;
-        this.base = store.getRoot().getChildNode(workspaceName);
-        nodeStateBuilder = store.getBuilder(base);
-        this.root = new TreeImpl(store, nodeStateBuilder, treeListener);
+        branch = store.branch();
+        root = TreeImpl.createRoot(this);
     }
 
     @Override
@@ -84,24 +72,29 @@ public class RootImpl implements Root {
         if (source == null) {
             return false;
         }
-
         TreeImpl destParent = getChild(getParentPath(destPath));
+        if (destParent == null) {
+            return false;
+        }
+
         String destName = getName(destPath);
-        return destParent != null && source.move(destParent, destName);
+        if (source.moveTo(destParent, destName)) {
+            branch.move(
+                PathUtils.concat(workspaceName, sourcePath),
+                PathUtils.concat(workspaceName, destPath));
 
+            return true;
+        }
+        else {
+            return false;
+        }
     }
 
     @Override
     public boolean copy(String sourcePath, String destPath) {
-        TreeImpl sourceNode = getChild(sourcePath);
-        if (sourceNode == null) {
-            return false;
-        }
-
-        TreeImpl destParent = getChild(getParentPath(destPath));
-        String destName = getName(destPath);
-        return destParent != null && sourceNode.copy(destParent, destName);
-
+        return branch.copy(
+            PathUtils.concat(workspaceName, sourcePath),
+            PathUtils.concat(workspaceName, destPath));
     }
 
     @Override
@@ -111,22 +104,90 @@ public class RootImpl implements Root {
 
     @Override
     public void rebase() {
-        rebase(true);
+        root.clear();
+        NodeState base = getWorkspaceBaseState();
+        NodeState head = getWorkspaceRootState();
+        branch = store.branch();
+        merge(base, head, getRoot());
+    }
+
+    @Override
+    public void clear() {
+        root.clear();
+        branch = store.branch();
     }
 
     @Override
     public void commit() throws CommitFailedException {
-        store.setRoot(nodeStateBuilder.getNodeState());
-        rebase(false);
+        branch.merge();
+        root.clear();
+        branch = store.branch();
     }
 
     @Override
     public boolean hasPendingChanges() {
-        return treeListener.hasChanges();
+        return !branch.getBase().equals(branch.getRoot());
+    }
+
+    /**
+     * Returns the current root node state of the workspace
+     * @return root node state
+     */
+    public NodeState getWorkspaceRootState() {
+        return branch.getRoot().getChildNode(workspaceName);
+    }
+
+    /**
+     * Returns the node state of the workspace from which
+     * the current branch was created.
+     * @return base node state
+     */
+    public NodeState getWorkspaceBaseState() {
+        return branch.getBase().getChildNode(workspaceName);
+    }
+
+    /**
+     * Returns a builder for constructing a new or modified node state.
+     * The builder is initialized with all the properties and child nodes
+     * from the given base node state.
+     *
+     * @param nodeState  base node state, or {@code null} for building new nodes
+     * @return  builder instance
+     */
+    public NodeStateBuilder getBuilder(NodeState nodeState) {
+        return store.getBuilder(nodeState);
+    }
+
+    /**
+     * Set the node state of the current workspace
+     *
+     * @param nodeState  node state representing the modified workspace
+     */
+    public void setWorkspaceRootState(NodeState nodeState) {
+        NodeStateBuilder builder = getBuilder(branch.getRoot());
+        builder.setNode(workspaceName, nodeState);
+        branch.setRoot(builder.getNodeState());
+    }
+
+    /**
+     * Compares the given two node states. Any found differences are
+     * reported by calling the relevant added, changed or deleted methods
+     * of the given handler.
+     *
+     * @param before node state before changes
+     * @param after node state after changes
+     * @param diffHandler handler of node state differences
+     */
+    public void compare(NodeState before, NodeState after, NodeStateDiff diffHandler) {
+        store.compare(before, after, diffHandler);
     }
 
     //------------------------------------------------------------< private >---
 
+    private TreeImpl getRoot() {
+        return root;
+    }
+
     /**
      * Get a tree for the child identified by {@code path}
      * @param path  the path to the child
@@ -134,7 +195,7 @@ public class RootImpl implements Root {
      *          at {@code path} or {@code null} if no such item exits.
      */
     private TreeImpl getChild(String path) {
-        TreeImpl child = root;
+        TreeImpl child = getRoot();
         for (String name : elements(path)) {
             child = child.getChild(name);
             if (child == null) {
@@ -144,28 +205,6 @@ public class RootImpl implements Root {
         return child;
     }
 
-    private void rebase(boolean mergeChanges) {
-        NodeState oldBase;
-        NodeState oldHead;
-        if (mergeChanges) {
-            oldBase = base;
-            oldHead = nodeStateBuilder.getNodeState();
-        } else {
-            oldBase = null;
-            oldHead = null;
-        }
-
-        treeListener = new TreeListener();
-        base = store.getRoot().getChildNode(workspaceName);
-        nodeStateBuilder = store.getBuilder(base);
-        root = new TreeImpl(store, nodeStateBuilder, treeListener);
-
-        if (mergeChanges) {
-            merge(oldBase, oldHead, root);
-        }
-
-    }
-
     private void merge(NodeState fromState, NodeState toState, final Tree target) {
         store.compare(fromState, toState, new NodeStateDiff() {
             @Override
@@ -231,48 +270,4 @@ public class RootImpl implements Root {
         return l;
     }
 
-    private static class TreeListener implements Listener {
-        private boolean hasChanges;
-
-        @Override
-        public void addChild(TreeImpl parent, String name) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void removeChild(TreeImpl parent, String name) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void setProperty(TreeImpl parent, String name, CoreValue value) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void setProperty(TreeImpl parent, String name, List<CoreValue> values) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void removeProperty(TreeImpl parent, String name) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void move(TreeImpl sourceParent, String sourceName, TreeImpl moved) {
-            hasChanges = true;
-        }
-
-        @Override
-        public void copy(TreeImpl sourceParent, String sourceName, TreeImpl copied) {
-            hasChanges = true;
-        }
-
-        boolean hasChanges() {
-            return hasChanges;
-        }
-
-    }
-
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java Thu May 17 15:08:03 2012
@@ -22,12 +22,10 @@ import org.apache.commons.collections.ma
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.util.Function1;
 import org.apache.jackrabbit.oak.util.Iterators;
 
@@ -38,26 +36,12 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-/**
- * Implementation of tree based on {@link NodeStateBuilder}s. Each subtree
- * has an associated node state builder which is used for building the new
- * trees resulting from calling mutating methods.
- */
-public class TreeImpl implements Tree {
-
-    /** Underlying store */
-    private final NodeStore store;
+import static org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState.EMPTY_NODE;
 
-    private final NodeStateBuilder rootBuilder;
-
-    /**
-     * Underlying persistent state or {@code null} if this instance represents an
-     * added tree
-     */
-    private final NodeState baseState;
+public class TreeImpl implements Tree {
 
-    /** Listener for changes on this tree */
-    private final Listener listener;
+    /** Underlying {@code Root} of this {@code Tree} instance */
+    private final RootImpl root;
 
     /** Parent of this tree */
     private TreeImpl parent;
@@ -67,85 +51,29 @@ public class TreeImpl implements Tree {
 
     private final Children children = new Children();
 
-    private TreeImpl(NodeStore store, NodeState baseState, NodeStateBuilder rootBuilder,
-            TreeImpl parent, String name, Listener listener) {
-
-        this.store = store;
-        this.rootBuilder = rootBuilder;
-        this.baseState = baseState;
-        this.listener = listener;
+    private TreeImpl(RootImpl root, TreeImpl parent, String name) {
+        this.root = root;
         this.parent = parent;
         this.name = name;
     }
 
-    /**
-     * Create a new instance which represents the root of a tree.
-     * @param store  underlying store to the tree
-     * @param rootBuilder  builder for the root
-     * @param listener  change listener for the tree. May be {@code null} if
-     *                  listening to changes is not needed.
-     */
-    TreeImpl(NodeStore store, NodeStateBuilder rootBuilder, Listener listener) {
-        this(store, rootBuilder.getNodeState(), rootBuilder, null, "", listener);
-    }
+    static TreeImpl createRoot(final RootImpl root) {
+        return new TreeImpl(root, null, "") {
+            @Override
+            protected NodeState getNodeState() {
+                return root.getWorkspaceRootState();
+            }
 
-    /**
-     * Listener for changes on {@code Tree}s
-     */
-    interface Listener {
+            @Override
+            protected NodeState getBaseState() {
+                return root.getWorkspaceBaseState();
+            }
 
-        /**
-         * The child of the given {@code name} has been added to {@code tree}.
-         * @param parent  parent to which a child was added
-         * @param name  name of the added child
-         */
-        void addChild(TreeImpl parent, String name);
-
-        /**
-         * The child of the given {@code name} has been removed from {@code tree}
-         * @param parent  parent from which a child was removed
-         * @param name  name of the removed child
-         */
-        void removeChild(TreeImpl parent, String name);
-
-        /**
-         * The property of the given {@code name} and {@code value} has been set.
-         * @param parent  parent on which the property was set.
-         * @param name  name of the property
-         * @param value  value of the property
-         */
-        void setProperty(TreeImpl parent, String name, CoreValue value);
-
-        /**
-         * The property of the given {@code name} and {@code values} has been set.
-         * @param parent  parent on which the property was set.
-         * @param name  name of the property
-         * @param values  values of the property
-         */
-        void setProperty(TreeImpl parent, String name, List<CoreValue> values);
-
-        /**
-         * The property of the given {@code name} has been removed.
-         * @param parent  parent on which the property was removed.
-         * @param name  name of the property
-         */
-        void removeProperty(TreeImpl parent, String name);
-
-        /**
-         * The child with the given {@code name} has been moved.
-         * @param sourceParent  parent from which the child was moved
-         * @param sourceName  name of the moved child
-         * @param moved  moved child
-         */
-        void move(TreeImpl sourceParent, String sourceName, TreeImpl moved);
-
-        /**
-         * The child with the given {@code name} been copied.
-         * @param sourceParent  parent from which the child way copied
-         * @param sourceName  name of the copied child
-         * @param copied  copied child
-         */
-        void copy(TreeImpl sourceParent, String sourceName, TreeImpl copied);
+            @Override
+            protected void updateParentState(NodeState childState) {
+                root.setWorkspaceRootState(childState);
+            }
+        };
     }
 
     @Override
@@ -177,6 +105,7 @@ public class TreeImpl implements Tree {
 
     @Override
     public Status getPropertyStatus(String name) {
+        NodeState baseState = getBaseState();
         if (baseState == null) {
             // This instance is NEW...
             if (hasProperty(name)) {
@@ -243,17 +172,14 @@ public class TreeImpl implements Tree {
             return null;
         }
 
-        NodeState childBaseState = baseState == null
-                ? null
-                : baseState.getChildNode(name);
-
-        child = new TreeImpl(store, childBaseState, rootBuilder, this, name, listener);
+        child = new TreeImpl(root, this, name);
         children.put(name, child);
         return child;
     }
 
     @Override
     public Status getChildStatus(String name) {
+        NodeState baseState = getBaseState();
         if (baseState == null) {
             // This instance is NEW...
             if (hasChild(name)) {
@@ -310,21 +236,18 @@ public class TreeImpl implements Tree {
                 final NodeState nodeState = getNodeState();
 
                 Iterator<? extends ChildNodeEntry> childEntries =
-                        nodeState.getChildNodeEntries().iterator();
+                    nodeState.getChildNodeEntries().iterator();
 
                 return Iterators.map(childEntries, new Function1<ChildNodeEntry, Tree>() {
                     @Override
                     public Tree apply(ChildNodeEntry entry) {
                         String childName = entry.getName();
-                        TreeImpl child = children.get(entry.getName());
-                        if (child != null) {
-                            return child;
+                        TreeImpl child = children.get(childName);
+                        if (child == null) {
+                            child = new TreeImpl(root, TreeImpl.this, childName);
+                            children.put(childName, child);
                         }
-
-                        NodeState childNodeState = nodeState.getChildNode(childName);
-                        child = new TreeImpl(store, childNodeState, rootBuilder, TreeImpl.this, childName, listener);
-                        children.put(childName, child);
-                        return child;
+                        return  child;
                     }
                 });
             }
@@ -333,48 +256,50 @@ public class TreeImpl implements Tree {
 
     @Override
     public Tree addChild(String name) {
-        if (getBuilder().addNode(name) != null) {
-            listener.addChild(this, name);
+        if (!hasChild(name)) {
+            NodeStateBuilder builder = getNodeStateBuilder();
+            builder.setNode(name, EMPTY_NODE);
+            updateParentState(builder.getNodeState());
         }
-        TreeImpl child = getChild(name);
-        children.put(name, child);
-        return child;
+
+        return getChild(name);
     }
 
     @Override
     public boolean removeChild(String name) {
-        boolean result = getBuilder().removeNode(name);
-        if (result) {
-            listener.removeChild(this, name);
+        if (hasChild(name)) {
+            NodeStateBuilder builder = getNodeStateBuilder();
+            builder.removeNode(name);
             children.remove(name);
+            updateParentState(builder.getNodeState());
+            return true;
+        }
+        else {
+            return false;
         }
-        return result;
     }
 
     @Override
     public PropertyState setProperty(String name, CoreValue value) {
-        PropertyState property = getBuilder().setProperty(name, value);
-        if (listener != null) {
-            listener.setProperty(this, name, value);
-        }
-        return property;
+        NodeStateBuilder builder = getNodeStateBuilder();
+        builder.setProperty(name, value);
+        updateParentState(builder.getNodeState());
+        return getProperty(name);
     }
 
     @Override
     public PropertyState setProperty(String name, List<CoreValue> values) {
-        PropertyState property = getBuilder().setProperty(name, values);
-        if (listener != null) {
-            listener.setProperty(this, name, values);
-        }
-        return property;
+        NodeStateBuilder builder = getNodeStateBuilder();
+        builder.setProperty(name, values);
+        updateParentState(builder.getNodeState());
+        return getProperty(name);
     }
 
     @Override
     public void removeProperty(String name) {
-        getBuilder().removeProperty(name);
-        if (listener != null) {
-            listener.removeProperty(this, name);
-        }
+        NodeStateBuilder builder = getNodeStateBuilder();
+        builder.removeProperty(name);
+        updateParentState(builder.getNodeState());
     }
 
     /**
@@ -383,47 +308,44 @@ public class TreeImpl implements Tree {
      *
      * @param destParent  new parent for this tree
      * @param destName  new name for this tree
-     * @return  {@code true} if successful, {@code false otherwise}. I.e.
-     * when {@code destName} already exists at {@code destParent}
      */
-    public boolean move(TreeImpl destParent, String destName) {
-        NodeStateBuilder builder = getBuilder();
-        NodeStateBuilder destParentBuilder = destParent.getBuilder();
-        boolean result = builder.moveTo(destParentBuilder, destName);
-        if (result) {
-            parent.children.remove(name);
-            destParent.children.put(destName, this);
+    public boolean moveTo(TreeImpl destParent, String destName) {
+        if (destParent.hasChild(destName)) {
+            return false;
+        }
 
-            TreeImpl oldParent = parent;
-            String oldName = name;
+        parent.children.remove(name);
+        destParent.children.put(destName, this);
 
-            name = destName;
-            parent = destParent;
+        name = destName;
+        parent = destParent;
+        return true;
+    }
 
-            if (listener != null) {
-                listener.move(oldParent, oldName, this);
-            }
-        }
-        return result;
+    //------------------------------------------------------------< protected >---
+
+    protected NodeState getNodeState() {
+        return parent.getNodeState().getChildNode(name);
     }
 
-    /**
-     * Copy this tree to the parent at {@code destParent} with the name {@code destName}.
-     *
-     * @param destParent  parent for the copied tree
-     * @param destName  name for the copied tree
-     * @return  {@code true} if successful, {@code false otherwise}. I.e.
-     * when {@code destName} already exists at {@code destParent}
-     */
-    public boolean copy(TreeImpl destParent, String destName) {
-        boolean result = getBuilder().copyTo(destParent.getBuilder(), destName);
-        if (result) {
-            if (listener != null) {
-                listener.copy(parent, name, destParent.getChild(destName));
-            }
-            return true;
-        }
-        return result;
+    protected NodeState getBaseState() {
+        return parent.getBaseState().getChildNode(name);
+    }
+
+    protected NodeStateBuilder getNodeStateBuilder() {
+        return root.getBuilder(getNodeState());
+    }
+
+    protected void updateParentState(NodeState childState) {
+        NodeStateBuilder parentBuilder = parent.getNodeStateBuilder();
+        parentBuilder.setNode(name, childState);
+        parent.updateParentState(parentBuilder.getNodeState());
+    }
+
+    //------------------------------------------------------------< internal >---
+
+    void clear() {
+        children.clear();
     }
 
     //------------------------------------------------------------< private >---
@@ -438,25 +360,9 @@ public class TreeImpl implements Tree {
         }
     }
 
-    private NodeStateBuilder getBuilder() {
-        NodeStateBuilder builder = rootBuilder;
-        for (String name : PathUtils.elements(getPath())) {
-            builder = builder.getChildBuilder(name);
-            if (builder == null) {
-                throw new IllegalStateException("Stale NodeStateBuilder for " + getPath());
-            }
-        }
-
-        return builder;
-    }
-
-    private NodeState getNodeState() {
-        return getBuilder().getNodeState();
-    }
-
     private boolean isSame(NodeState state1, NodeState state2) {
         final boolean[] isDirty = {false};
-        store.compare(state1, state2, new NodeStateDiff() {
+        root.compare(state1, state2, new NodeStateDiff() {
             @Override
             public void propertyAdded(PropertyState after) {
                 isDirty[0] = true;
@@ -529,6 +435,13 @@ public class TreeImpl implements Tree {
                 writeLock.unlock();
             }
         }
+
+        public void clear() {
+            for (TreeImpl child : children.values()) {
+                child.clear();
+            }
+            children.clear();
+        }
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java Thu May 17 15:08:03 2012
@@ -17,43 +17,21 @@
 package org.apache.jackrabbit.oak.kernel;
 
 import org.apache.jackrabbit.mk.api.MicroKernel;
-import org.apache.jackrabbit.mk.api.MicroKernelException;
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStateBuilder;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.EmptyCommitHook;
-import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.AbstractNodeStore;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
-import org.apache.jackrabbit.oak.util.Function1;
-import org.apache.jackrabbit.oak.util.Iterators;
-import org.apache.jackrabbit.oak.util.Predicate;
-
-import java.util.Iterator;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
 
 /**
- * {@code NodeStore} implementations which supports batching changes
- * to the content tree up until a certain limit is reached and write them
- * down to the Microkernel in a single operation. The batch size is controlled
- * through {@link #PURGE_LIMIT} which is the number of characters on a commit
- * (i.e. jsop string).
+ * {@code NodeStore} implementations against {@link MicroKernel}.
  */
 public class KernelNodeStore extends AbstractNodeStore {
 
     /**
-     * Maximal size of size of a commit (number of characters of the corresponding
-     * jsop string). When the limit is reached, changes kept in memory are written
-     * back to the private branch in the Microkernel.
-     */
-    private static final int PURGE_LIMIT = 1024;  // TODO make configurable?
-
-    /**
      * The {@link MicroKernel} instance used to store the content tree.
      */
     private final MicroKernel kernel;
@@ -96,38 +74,13 @@ public class KernelNodeStore extends Abs
     }
 
     @Override
-    public NodeStateBuilder getBuilder(NodeState base) {
-        if (base instanceof KernelNodeState) {
-                NodeStateBuilderContext context = new NodeStateBuilderContext((KernelNodeState) base);
-                return KernelNodeStateBuilder.create(context);
-        }
-        else if (base instanceof NodeStateBuilderContext.NodeDecorator) {
-            NodeState root = ((NodeStateBuilderContext.NodeDecorator) base).getRoot();
-            if (root != base) {
-                throw new IllegalArgumentException("Not root:" + base);
-            }
-
-            return KernelNodeStateBuilder.create(((NodeStateBuilderContext.NodeDecorator) base).getContext());
-        }
-        else {
-            throw new IllegalArgumentException("Alien node state " + base.getClass() + ": " + root);
-        }
+    public NodeStoreBranch branch() {
+        return new KernelNodeStoreBranch(this);
     }
 
     @Override
-    public void setRoot(NodeState newRoot) throws CommitFailedException {
-        NodeState baseState = getBaseState(newRoot);
-        NodeState toCommit = commitHook.beforeCommit(this, baseState, newRoot);
-
-        KernelNodeState committed;
-        if (toCommit instanceof NodeStateBuilderContext.NodeDecorator) {
-            committed = ((NodeStateBuilderContext.NodeDecorator) newRoot).applyChanges();
-        }
-        else {
-            throw new CommitFailedException("Alien node state " + newRoot.getClass() + ": " + newRoot);
-        }
-
-        commitHook.afterCommit(this, baseState, committed);
+    public NodeStateBuilder getBuilder(NodeState base) {
+        return new MemoryNodeStateBuilder(base);
     }
 
     @Override
@@ -137,684 +90,11 @@ public class KernelNodeStore extends Abs
 
     //------------------------------------------------------------< internal >---
 
-    private NodeState getBaseState(NodeState newRoot) throws CommitFailedException {
-        if (newRoot instanceof NodeStateBuilderContext.NodeDecorator) {
-            return ((NodeStateBuilderContext.NodeDecorator) newRoot).getBase();
-        }
-        else {
-            throw new CommitFailedException("Could not determine base state for " + newRoot);
-        }
+    MicroKernel getKernel() {
+        return kernel;
     }
 
-    /**
-     * {@code NodeStateBuilderContext} keeps track of all changes to a
-     * {@code KernelNodeStateBuilder} which have not yet been written back to the
-     * Microkernel. It transforms the tree rooted at {@link #root} to reflect these
-     * changes and writes these changes back to the Microkernel when
-     * {@link KernelNodeStore#PURGE_LIMIT} is exceeded.
-     */
-    class NodeStateBuilderContext {
-
-        /** Original root of the subtree */
-        private final KernelNodeState base;
-
-        /** Current root of the subtree */
-        private NodeState root;
-
-        /** Current branch revision */
-        private String revision;
-
-        /** Pending changes */
-        private StringBuilder jsop = new StringBuilder();
-
-        NodeStateBuilderContext(KernelNodeState base) {
-            this.base = base;
-            this.revision = kernel.branch(base.getRevision());
-            this.root = new RootNodeDecorator(
-                    new KernelNodeState(kernel, valueFactory, base.getPath(), revision));
-        }
-
-        /**
-         * Get the node state located at {@code path}
-         * @param path  path relative to {@link #root}
-         * @return  node state at {@code path} or {@code null} if none.
-         */
-        NodeState getNodeState(String path) {
-            NodeState state = root;
-            for (String name : PathUtils.elements(path)) {
-                state = state.getChildNode(name);
-            }
-
-            return state;
-        }
-
-        /**
-         * Add a new, empty node state at {@code path}. The changes to the subtree
-         * are reflected in {@link #root}.
-         * @param relPath  path relative to {@link #root}. All but the last element
-         *                 must resolve to existing node states.
-         */
-        void addNode(String relPath) {
-            jsop.append("+\"").append(relPath).append("\":{}");
-            root = addNode(root, MemoryNodeState.EMPTY_NODE, PathUtils.elements(relPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Add a new node state at {@code path}. The changes to the subtree are reflected
-         * in {@link #root}.
-         * @param node     node state to add
-         * @param relPath  path relative to {@link #root}. All but the last element
-         *                 must resolve to existing node states.
-         */
-        void addNode(NodeState node, String relPath) {
-            buildJsop(relPath, node);
-            root = addNode(root, node, PathUtils.elements(relPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Remove the node state at {@code path}. The changes to the subtree are reflected
-         * in {@link #root}.
-         * @param relPath  path relative to {@link #root}. All elements must resolve to
-         *                 existing node states.
-         */
-        void removeNode(String relPath) {
-            jsop.append("-\"").append(relPath).append('"');
-            root = removeNode(root, PathUtils.elements(relPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Add a new property state. The changes to the subtree are reflected in {@link #root}.
-         * @param property     property state to add
-         * @param parentPath   path to the parent node state relative to {@link #root}.
-         *                     All elements must resolve to existing node states.
-         */
-        void addProperty(PropertyState property, String parentPath) {
-            String path = PathUtils.concat(parentPath, property.getName());
-            String value = property.isArray()
-                    ? CoreValueMapper.toJsonArray(property.getValues())
-                    : CoreValueMapper.toJsonValue(property.getValue());
-            jsop.append("^\"").append(path).append("\":").append(value);
-            root = addProperty(root, property, PathUtils.elements(parentPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Set an existing property state. The changes to the subtree are reflected in
-         * {@link #root}.
-         * @param property     property state to set
-         * @param parentPath   path to the parent node state relative to {@link #root}.
-         *                     All elements must resolve to existing node states.
-         */
-        void setProperty(PropertyState property, String parentPath) {
-            String path = PathUtils.concat(parentPath, property.getName());
-            String value = property.isArray()
-                    ? CoreValueMapper.toJsonArray(property.getValues())
-                    : CoreValueMapper.toJsonValue(property.getValue());
-            jsop.append("^\"").append(path).append("\":").append(value);
-            root = setProperty(root, property, PathUtils.elements(parentPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Remove an existing property state. The changes to the subtree are reflected in
-         * {@link #root}.
-         * @param relPath   path to the property state relative to {@link #root}. All
-         *                  elements must resolve to existing node states.
-         */
-        void removeProperty(String relPath) {
-            jsop.append("^\"").append(relPath).append("\":null");
-            root = removeProperty(root, PathUtils.elements(relPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Move the node from {@code sourcePath} to {@code destPath}. The changes to
-         * the subtree are reflected in {@link #root}.
-         * @param sourcePath  path to the node to move. All elements must resolve to
-         *                    existing node states.
-         * @param destPath    path to the new node. All but the last element must resolve
-         *                    to existing node states.
-         */
-        void moveNode(String sourcePath, String destPath) {
-            jsop.append(">\"").append(sourcePath).append("\":\"").append(destPath).append('"');
-            NodeState moveNode = getChildNode(sourcePath);
-            root = removeNode(root, PathUtils.elements(sourcePath).iterator());
-            root = addNode(root, moveNode, PathUtils.elements(destPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Copy the node from {@code sourcePath} to {@code destPath}. The changes to
-         * the subtree are reflected in {@link #root}.
-         * @param sourcePath  path to the node to copy. All elements must resolve to
-         *                    existing node states.
-         * @param destPath    path to the new node. All but the last element must resolve
-         *                    to existing node states.
-         */
-        void copyNode(String sourcePath, String destPath) {
-            jsop.append("*\"").append(sourcePath).append("\":\"").append(destPath).append('"');
-            NodeState copyNode = getChildNode(sourcePath);
-            root = addNode(root, copyNode, PathUtils.elements(destPath).iterator());
-            purgeOnLimit();
-        }
-
-        /**
-         * Merge back into trunk
-         * @throws CommitFailedException  if merging fails
-         */
-        KernelNodeState applyPendingChanges() throws CommitFailedException {
-            try {
-                purgePendingChanges();
-                String newRevision = kernel.merge(revision, null);
-                revision = null;
-                return new KernelNodeState(kernel, valueFactory, base.getPath(), newRevision);
-            }
-            catch (MicroKernelException e) {
-                throw new CommitFailedException(e);
-            }
-        }
-
-        //------------------------------------------------------------< private >---
-
-        /**
-         * Purge all changes kept in memory to the private branch if
-         * {@link KernelNodeStore#PURGE_LIMIT} is exceeded.
-         * @see #purgePendingChanges()
-         */
-        private void purgeOnLimit() {
-            if (jsop.length() > PURGE_LIMIT) {
-                purgePendingChanges();
-            }
-        }
-
-        /**
-         * Purge all changes kept in memory to the private branch.
-         */
-        private void purgePendingChanges() {
-            if (revision == null) {
-                throw new IllegalStateException("Branch has been merged already");
-            }
-
-            if (jsop.length() > 0) {
-                String path = base.getPath();
-                revision = kernel.commit(path, jsop.toString(), revision, null);
-                root = new RootNodeDecorator(
-                        new KernelNodeState(kernel, valueFactory, path, revision));
-                jsop = new StringBuilder();
-            }
-        }
-
-        /**
-         * Build a jsop statement for adding a node state at a given path.
-         * @param path        path where {@code nodeState} should be added.
-         * @param nodeState   node state to add.
-         */
-        private void buildJsop(String path, NodeState nodeState) {
-            jsop.append("+\"").append(path).append("\":{}");
-
-            for (PropertyState property : nodeState.getProperties()) {
-                String targetPath = PathUtils.concat(path, property.getName());
-                String value = property.isArray()
-                        ? CoreValueMapper.toJsonArray(property.getValues())
-                        : CoreValueMapper.toJsonValue(property.getValue());
-
-                jsop.append("^\"").append(targetPath).append("\":").append(value);
-            }
-
-            for (ChildNodeEntry child : nodeState.getChildNodeEntries()) {
-                String targetPath = PathUtils.concat(path, child.getName());
-                buildJsop(targetPath, child.getNodeState());
-            }
-        }
-
-        /**
-         * Construct a new {@code NodeState} where {@code node} is added to
-         * {@code parent} at {@code path}.
-         * @param parent  parent where {@code node} should be added
-         * @param node    node state to add
-         * @param path    path from {@code parent} where {@code node} should be added
-         * @return  a new {@code NodeState} instance with the added node state.
-         */
-        private NodeState addNode(NodeState parent, NodeState node, Iterator<String> path) {
-            String name = path.next();
-            if (path.hasNext()) {
-                return new SetNodeDecorator(parent, name, addNode(parent.getChildNode(name), node, path));
-            }
-            else {
-                return new AddNodeDecorator(parent, name, node);
-            }
-        }
-
-        /**
-         * Construct a new {@code NodeState} where the node state at {@code path} is
-         * removed from {@code parent}.
-         * @param parent  parent from which the node state should be removed
-         * @param path    path from {@code parent} for the node state to remove
-         * @return  a new {@code NodeState} instance with the remove node state.
-         */
-        private NodeState removeNode(NodeState parent, Iterator<String> path) {
-            String name = path.next();
-            if (path.hasNext()) {
-                return new SetNodeDecorator(parent, name, removeNode(parent.getChildNode(name), path));
-            }
-            else {
-                return new RemoveNodeDecorator(parent, name);
-            }
-        }
-
-        /**
-         * Construct a new {@code NodeState} where {@code property} is added to
-         * {@code parent} at {@code parentPath}.
-         * @param parent      parent where {@code node} should be added
-         * @param property    property state to add
-         * @param parentPath  path from {@code parent} where {@code property} should be
-         *                    added
-         * @return  a new {@code NodeState} instance with the added property state.
-         */
-        private NodeState addProperty(NodeState parent, PropertyState property, Iterator<String> parentPath) {
-            if (parentPath.hasNext()) {
-                String name = parentPath.next();
-                return new SetNodeDecorator(parent, name, addProperty(parent.getChildNode(name), property, parentPath));
-            }
-            else {
-                return new AddPropertyDecorator(property, parent);
-            }
-        }
-
-        /**
-         * Construct a new {@code NodeState} where {@code property} is set to
-         * {@code parent} at {@code parentPath}.
-         * @param parent      parent where {@code node} should be set
-         * @param property    property state to set
-         * @param parentPath  path from {@code parent} where {@code property} should be
-         *                    set
-         * @return  a new {@code NodeState} instance with the new property state.
-         */
-        private NodeState setProperty(NodeState parent, PropertyState property, Iterator<String> parentPath) {
-            if (parentPath.hasNext()) {
-                String name = parentPath.next();
-                return new SetNodeDecorator(parent, name, setProperty(parent.getChildNode(name), property, parentPath));
-            }
-            else {
-                return new SetPropertyDecorator(property, parent);
-            }
-        }
-
-        /**
-         * Construct a new {@code NodeState} where the property state at {@code path} is
-         * removed from {@code parent}.
-         * @param parent  parent from which the property state should be removed
-         * @param path    path from {@code parent} for the property state to remove
-         * @return  a new {@code NodeState} instance with the remove property state.
-         */
-        private NodeState removeProperty(NodeState parent, Iterator<String> path) {
-            String name = path.next();
-            if (path.hasNext()) {
-                return new SetNodeDecorator(parent, name, removeProperty(parent.getChildNode(name), path));
-            }
-            else {
-                return new RemovePropertyDecorator(name, parent);
-            }
-        }
-
-        /**
-         * Get the node state located at {@code relPath} from {@link #root}.
-         * @param relPath  relative path
-         * @return  child node at {@code relPath} or {@code null} if none.
-         */
-        private NodeState getChildNode(String relPath) {
-            NodeState state = root;
-            for (String name : PathUtils.elements(relPath)) {
-                state = state.getChildNode(name);
-            }
-            return state;
-        }
-
-        private abstract class NodeDecorator extends AbstractNodeState {
-            final NodeState decorate;
-
-            protected NodeDecorator(NodeState decorate) {
-                this.decorate = decorate;
-            }
-
-            @Override
-            public PropertyState getProperty(String name) {
-                return decorate.getProperty(name);
-            }
-
-            @Override
-            public long getPropertyCount() {
-                return decorate.getPropertyCount();
-            }
-
-            @Override
-            public NodeState getChildNode(String name) {
-                return decorate.getChildNode(name);
-            }
-
-            @Override
-            public long getChildNodeCount() {
-                return decorate.getChildNodeCount();
-            }
-
-            @Override
-            public Iterable<? extends PropertyState> getProperties() {
-                return decorate.getProperties();
-            }
-
-            @Override
-            public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
-                return decorate.getChildNodeEntries();
-            }
-
-            KernelNodeState applyChanges() throws CommitFailedException {
-                return applyPendingChanges();
-            }
-
-            KernelNodeState getBase() {
-                return base;
-            }
-
-            NodeStateBuilderContext getContext() {
-                return NodeStateBuilderContext.this;
-            }
-
-            NodeState getRoot() {
-                return root;
-            }
-        }
-
-        private class RootNodeDecorator extends NodeDecorator {
-            private RootNodeDecorator(NodeState root) {
-                super(root);
-            }
-        }
-
-        /**
-         * {@code NodeState} decorator adding a new node state.
-         */
-        private class AddNodeDecorator extends NodeDecorator {
-            private final String childName;
-            private final NodeState node;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with {@code node} added
-             * as new child with name {@code childName}.
-             * @param parent
-             * @param childName
-             * @param node
-             * @return
-             */
-            public AddNodeDecorator(NodeState parent, String childName, NodeState node) {
-                super(parent);
-                this.childName = childName;
-                this.node = node;
-            }
-
-            @Override
-            public NodeState getChildNode(String name) {
-                return childName.equals(name) ? node : super.getChildNode(name);
-            }
-
-            @Override
-            public long getChildNodeCount() {
-                return 1 + super.getChildNodeCount();
-            }
-
-            @Override
-            public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
-                return new Iterable<ChildNodeEntry>() {
-                    @Override
-                    public Iterator<ChildNodeEntry> iterator() {
-                        return Iterators.chain(
-                                AddNodeDecorator.super.getChildNodeEntries().iterator(),
-                                Iterators.singleton(new MemoryChildNodeEntry(childName, node)));
-                    }
-                };
-            }
-
-        }
-
-        /**
-         * {@code NodeState} decorator modifying an existing node state to a new node state.
-         */
-        private class SetNodeDecorator extends NodeDecorator {
-            private final String childName;
-            private final NodeState node;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with child node state
-             * {@code childName} replaced with {@code node}.
-             * @param parent
-             * @param childName
-             * @param node
-             * @return
-             */
-            public SetNodeDecorator(NodeState parent, String childName, NodeState node) {
-                super(parent);
-                this.childName = childName;
-                this.node = node;
-            }
-
-            @Override
-            public NodeState getChildNode(String name) {
-                return childName.equals(name) ? node : super.getChildNode(name);
-            }
-
-            @Override
-            public long getChildNodeCount() {
-                return super.getChildNodeCount();
-            }
-
-            @Override
-            public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
-                return new Iterable<ChildNodeEntry>() {
-                    @Override
-                    public Iterator<ChildNodeEntry> iterator() {
-                        return Iterators.map(SetNodeDecorator.super.getChildNodeEntries().iterator(),
-                            new Function1<ChildNodeEntry, ChildNodeEntry>() {
-                                @Override
-                                public ChildNodeEntry apply(ChildNodeEntry cne) {
-                                    return childName.equals(cne.getName())
-                                            ? new MemoryChildNodeEntry(childName, node)
-                                            : cne;
-                                }
-                            });
-                    }
-                };
-            }
-        }
-
-        /**
-         * {@code NodeState} decorator removing a node state
-         */
-        private class RemoveNodeDecorator extends NodeDecorator {
-            private final String childName;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with child node state
-             * {@code childName} removed.
-             * @param parent
-             * @param childName
-             * @return
-             */
-            public RemoveNodeDecorator(NodeState parent, String childName) {
-                super(parent);
-                this.childName = childName;
-            }
-
-            @Override
-            public NodeState getChildNode(String name) {
-                return childName.equals(name) ? null : super.getChildNode(name);
-            }
-
-            @Override
-            public long getChildNodeCount() {
-                return super.getChildNodeCount() - 1;
-            }
-
-            @Override
-            public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
-                return new Iterable<ChildNodeEntry>() {
-                    @Override
-                    public Iterator<ChildNodeEntry> iterator() {
-                        return Iterators.filter(RemoveNodeDecorator.super.getChildNodeEntries().iterator(),
-                            new Predicate<ChildNodeEntry>() {
-                                @Override
-                                public boolean evaluate(ChildNodeEntry cne) {
-                                    return !childName.equals(cne.getName());
-                                }
-                            }
-                        );
-                    }
-                };
-            }
-        }
-
-        /**
-         * {@code NodeState} decorator adding a new property state
-         */
-        private class AddPropertyDecorator extends NodeDecorator {
-            private final PropertyState property;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with {@code property}
-             * added.
-             * @param parent
-             * @param property
-             * @return
-             */
-            public AddPropertyDecorator(PropertyState property, NodeState parent) {
-                super(parent);
-                this.property = property;
-            }
-
-            @Override
-            public PropertyState getProperty(String name) {
-                return property.getName().equals(name)
-                    ? property
-                    : super.getProperty(name);
-            }
-
-            @Override
-            public long getPropertyCount() {
-                return super.getPropertyCount() + 1;
-            }
-
-            @Override
-            public Iterable<? extends PropertyState> getProperties() {
-                return new Iterable<PropertyState>() {
-                    @Override
-                    public Iterator<PropertyState> iterator() {
-                        return Iterators.chain(
-                                AddPropertyDecorator.super.getProperties().iterator(),
-                                Iterators.singleton(property));
-                    }
-                };
-            }
-        }
-
-        /**
-         * {@code NodeState} decorator modifying an existing property state.
-         */
-        private class SetPropertyDecorator extends NodeDecorator {
-            private final PropertyState property;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with {@code property}
-             * replaced.
-             * @param parent
-             * @param property
-             * @return
-             */
-            public SetPropertyDecorator(PropertyState property, NodeState parent) {
-                super(parent);
-                this.property = property;
-            }
-
-            @Override
-            public PropertyState getProperty(String name) {
-                return property.getName().equals(name)
-                        ? property
-                        : super.getProperty(name);
-            }
-
-            @Override
-            public long getPropertyCount() {
-                return super.getPropertyCount();
-            }
-
-            @Override
-            public Iterable<? extends PropertyState> getProperties() {
-                return new Iterable<PropertyState>() {
-                    @Override
-                    public Iterator<PropertyState> iterator() {
-                        return Iterators.map(SetPropertyDecorator.super.getProperties().iterator(),
-                            new Function1<PropertyState, PropertyState>() {
-                                @Override
-                                public PropertyState apply(PropertyState state) {
-                                    return property.getName().equals(state.getName())
-                                            ? property
-                                            : state;
-                                }
-                            }
-                        );
-                    }
-                };
-            }
-        }
-
-        /**
-         * {@code NodeState} decorator removing an existing property state.
-         */
-        private class RemovePropertyDecorator extends NodeDecorator {
-            private final String propertyName;
-
-            /**
-             * Construct a new {@code NodeState} from {@code parent} with {@code propertyName}
-             * removed.
-             * @param parent
-             * @param propertyName
-             * @return
-             */
-            public RemovePropertyDecorator(String propertyName, NodeState parent) {
-                super(parent);
-                this.propertyName = propertyName;
-            }
-
-            @Override
-            public PropertyState getProperty(String name) {
-                return propertyName.equals(name)
-                    ? null
-                    : super.getProperty(name);
-            }
-
-            @Override
-            public long getPropertyCount() {
-                return super.getPropertyCount() - 1;
-            }
-
-            @Override
-            public Iterable<? extends PropertyState> getProperties() {
-                return new Iterable<PropertyState>() {
-                    @Override
-                    public Iterator<PropertyState> iterator() {
-                        return Iterators.filter(RemovePropertyDecorator.super.getProperties().iterator(),
-                            new Predicate<PropertyState>() {
-                                @Override
-                                public boolean evaluate(PropertyState prop) {
-                                    return !propertyName.equals(prop.getName());
-                                }
-                            }
-                        );
-                    }
-                };
-            }
-        }
-
+    CommitHook getCommitHook() {
+        return commitHook;
     }
-
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1339630&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Thu May 17 15:08:03 2012
@@ -0,0 +1,252 @@
+package org.apache.jackrabbit.oak.kernel;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+
+/**
+ * {@code NodeStoreBranch} based on {@link MicroKernel} branching and merging.
+ * This implementation keeps changes in memory up to a certain limit and writes
+ * them back when the to the Microkernel branch when the limit is exceeded.
+ */
+class KernelNodeStoreBranch implements NodeStoreBranch {
+
+    /**
+     * Number of {@link #setRoot(NodeState)} for which changes are kept in memory.
+     */
+    private static final int PURGE_LIMIT = 100;
+
+    /** The underlying store to which this branch belongs */
+    private final KernelNodeStore store;
+
+    /** Base state of this branch */
+    private final NodeState base;
+
+
+    /** Revision of this branch in the Microkernel */
+    private String branchRevision;
+
+    /** Current root state of this branch */
+    private NodeState currentRoot;
+
+    /** Last state which was committed to this branch */
+    private NodeState committed;
+
+    /** Number of {@link #setRoot(NodeState)} occurred so since the lase purge */
+    private int modCount;
+
+    KernelNodeStoreBranch(KernelNodeStore store) {
+        this.store = store;
+
+        MicroKernel kernel = getKernel();
+        this.branchRevision = kernel.branch(null);
+        this.currentRoot = new KernelNodeState(kernel, getValueFactory(), "/", branchRevision);
+        this.base = currentRoot;
+        this.committed = currentRoot;
+    }
+
+    @Override
+    public NodeState getRoot() {
+        return currentRoot;
+    }
+
+    @Override
+    public NodeState getBase() {
+        return base;
+    }
+
+    @Override
+    public void setRoot(NodeState newRoot) {
+        currentRoot = newRoot;
+        modCount++;
+        if (needsPurging()) {
+            purge(buildJsop());
+        }
+    }
+
+    @Override
+    public boolean move(String source, String target) {
+        if (getNode(source) == null) {
+            // source does not exist
+            return false;
+        }
+        NodeState destParent = getNode(getParentPath(target));
+        if (destParent == null) {
+            // parent of destination does not exist
+            return false;
+        }
+        if (destParent.getChildNode(getName(target)) != null) {
+            // destination exists already
+            return false;
+        }
+
+        purge(buildJsop() + ">\"" + source + "\":\"" + target + '"');
+        return true;
+    }
+
+    @Override
+    public boolean copy(String source, String target) {
+        if (getNode(source) == null) {
+            // source does not exist
+            return false;
+        }
+        NodeState destParent = getNode(getParentPath(target));
+        if (destParent == null) {
+            // parent of destination does not exist
+            return false;
+        }
+        if (destParent.getChildNode(getName(target)) != null) {
+            // destination exists already
+            return false;
+        }
+
+        purge(buildJsop() + "*\"" + source + "\":\"" + target + '"');
+        return true;
+    }
+
+    @Override
+    public KernelNodeState merge() throws CommitFailedException {
+        purge(buildJsop());
+        // TODO rebase, call commitHook (OAK-100)
+        MicroKernel kernel = getKernel();
+        String mergedRevision = null;
+        try {
+            kernel.merge(branchRevision, null);
+            branchRevision = null;
+            currentRoot = null;
+            committed = null;
+        }
+        catch (MicroKernelException e) {
+            throw new CommitFailedException(e);
+        }
+        return new KernelNodeState(kernel, getValueFactory(), "/", mergedRevision);
+    }
+
+    //------------------------------------------------------------< private >---
+
+    private MicroKernel getKernel() {
+        return store.getKernel();
+    }
+
+    private CoreValueFactory getValueFactory() {
+        return store.getValueFactory();
+    }
+
+    private NodeState getNode(String path) {
+        NodeState node = getRoot();
+        for (String name : elements(path)) {
+            node = node.getChildNode(name);
+            if (node == null) {
+                break;
+            }
+        }
+
+        return node;
+    }
+
+    private void purge(String jsop) {
+        MicroKernel kernel = getKernel();
+        branchRevision = kernel.commit("/", jsop, branchRevision, null);
+        currentRoot = new KernelNodeState(kernel, getValueFactory(), "/", branchRevision);
+        committed = currentRoot;
+    }
+
+    private String buildJsop() {
+        StringBuilder jsop = new StringBuilder();
+        diffToJsop(committed, currentRoot, "", jsop);
+        return jsop.toString();
+    }
+
+    private void diffToJsop(NodeState before, NodeState after, final String path,
+            final StringBuilder jsop) {
+
+        store.compare(before, after, new NodeStateDiff() {
+            @Override
+            public void propertyAdded(PropertyState after) {
+                jsop.append('^').append(buildPath(after.getName()))
+                        .append(':').append(toJson(after));
+            }
+
+            @Override
+            public void propertyChanged(PropertyState before, PropertyState after) {
+                jsop.append('^').append(buildPath(after.getName()))
+                        .append(':').append(toJson(after));
+            }
+
+            @Override
+            public void propertyDeleted(PropertyState before) {
+                jsop.append('^').append(buildPath(before.getName())).append(":null");
+            }
+
+            @Override
+            public void childNodeAdded(String name, NodeState after) {
+                jsop.append('+').append(buildPath(name)).append(':');
+                toJson(after);
+            }
+
+            @Override
+            public void childNodeDeleted(String name, NodeState before) {
+                jsop.append('-').append(buildPath(name));
+            }
+
+            @Override
+            public void childNodeChanged(String name, NodeState before, NodeState after) {
+                diffToJsop(before, after, PathUtils.concat(path, name), jsop);
+            }
+
+            private String buildPath(String name) {
+                return '"' + PathUtils.concat(path, name) + '"';
+            }
+
+            private String toJson(PropertyState propertyState) {
+                return propertyState.isArray()
+                    ? CoreValueMapper.toJsonArray(propertyState.getValues())
+                    : CoreValueMapper.toJsonValue(propertyState.getValue());
+            }
+
+            private void toJson(NodeState nodeState) {
+                jsop.append('{');
+                String comma = "";
+                for (PropertyState property : nodeState.getProperties()) {
+                    String value = property.isArray()
+                            ? CoreValueMapper.toJsonArray(property.getValues())
+                            : CoreValueMapper.toJsonValue(property.getValue());
+
+                    jsop.append(comma);
+                    comma = ",";
+                    jsop.append('"').append(property.getName()).append("\":").append(value);
+                }
+
+                for (ChildNodeEntry child : nodeState.getChildNodeEntries()) {
+                    jsop.append(comma);
+                    comma = ",";
+                    jsop.append('"').append(child.getName()).append("\":");
+                    toJson(child.getNodeState());
+                }
+                jsop.append('}');
+            }
+        });
+    }
+
+    // TODO better way to determine purge limit
+    private boolean needsPurging() {
+        if (modCount > PURGE_LIMIT) {
+            modCount = 0;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java Thu May 17 15:08:03 2012
@@ -16,16 +16,16 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.kernel.PropertyStateImpl;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Basic in-memory node state builder.
  */
@@ -57,41 +57,25 @@ public class MemoryNodeStateBuilder impl
     }
 
     @Override
-    public NodeStateBuilder getChildBuilder(String name) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public NodeStateBuilder addNode(String name, NodeState nodeState) {
+    public void setNode(String name, NodeState nodeState) {
         nodes.put(name, nodeState);
-        return this;
     }
 
     @Override
-    public NodeStateBuilder addNode(String name) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public boolean removeNode(String name) {
+    public void removeNode(String name) {
         nodes.put(name, null);
-        return false;
     }
 
     @Override
-    public PropertyState setProperty(String name, CoreValue value) {
+    public void setProperty(String name, CoreValue value) {
         PropertyState property = new PropertyStateImpl(name, value);
         properties.put(name, property);
-        return property;
     }
 
     @Override
-    public PropertyState setProperty(String name, List<CoreValue> values) {
+    public void setProperty(String name, List<CoreValue> values) {
         PropertyState property = new PropertyStateImpl(name, values);
         properties.put(name, property);
-        return property;
     }
 
     @Override
@@ -99,16 +83,4 @@ public class MemoryNodeStateBuilder impl
         properties.put(name, null);
     }
 
-    @Override
-    public boolean moveTo(NodeStateBuilder destParent, String destName) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean copyTo(NodeStateBuilder destParent, String destName) {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java Thu May 17 15:08:03 2012
@@ -16,20 +16,17 @@
  */
 package org.apache.jackrabbit.oak.plugins.memory;
 
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
 import org.apache.commons.collections.IteratorUtils;
 import org.apache.commons.collections.Predicate;
 import org.apache.commons.collections.PredicateUtils;
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
-import org.apache.jackrabbit.oak.spi.state.DefaultNodeStateDiff;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.ProxyNodeState;
 
+import java.util.Iterator;
+import java.util.Map;
+
 public class ModifiedNodeState extends ProxyNodeState {
 
     private final Map<String, PropertyState> properties;
@@ -49,58 +46,6 @@ public class ModifiedNodeState extends P
         return delegate;
     }
 
-    ModifiedNodeState rebase(MemoryNodeStore store, NodeState base)
-            throws CommitFailedException {
-        if (delegate.equals(base)) {
-            return this;
-        } else if (nodes.isEmpty()) {
-            return this; // shortcut
-        } else {
-            return new ModifiedNodeState(
-                    base, properties, rebaseChildren(store, base));
-        }
-    }
-
-    private Map<String, NodeState> rebaseChildren(
-            final MemoryNodeStore store, NodeState base)
-            throws CommitFailedException {
-        // TODO: better conflict resolution
-        final Map<String, NodeState> rebasedNodes =
-                new HashMap<String, NodeState>(nodes);
-        final Map<String, CommitFailedException> failures =
-                new HashMap<String, CommitFailedException>();
-        store.compare(delegate, base, new DefaultNodeStateDiff() {
-            @Override
-            public void childNodeAdded(String name, NodeState after) {
-                rebaseChild(name, after);
-            }
-            @Override
-            public void childNodeChanged(
-                    String name, NodeState before, NodeState after) {
-                rebaseChild(name, after);
-            }
-            @Override
-            public void childNodeDeleted(String name, NodeState before) {
-                rebaseChild(name, MemoryNodeState.EMPTY_NODE);
-            }
-            private void rebaseChild(String name, NodeState base) {
-                NodeState child = nodes.get(name);
-                if (child != null) {
-                    try {
-                        rebasedNodes.put(name, store.rebase(child, base));
-                    } catch (CommitFailedException e) {
-                        failures.put(name, e);
-                    }
-                }
-            }
-        });
-        if (failures.isEmpty()) {
-            return rebasedNodes;
-        } else {
-            throw new CommitFailedException("Failed to rebase changes");
-        }
-    }
-
     //---------------------------------------------------------< NodeState >--
 
     @Override
@@ -163,7 +108,7 @@ public class ModifiedNodeState extends P
 
     @Override
     public long getChildNodeCount() {
-        long count = super.getPropertyCount();
+        long count = super.getChildNodeCount();
         for (Map.Entry<String, NodeState> entry : nodes.entrySet()) {
             if (super.getChildNode(entry.getKey()) != null) {
                 if (entry.getValue() == null) {
@@ -229,7 +174,7 @@ public class ModifiedNodeState extends P
 
     }
 
-    private class UndeletedChildNodePredicate implements Predicate {
+    private static class UndeletedChildNodePredicate implements Predicate {
 
         @Override
         public boolean evaluate(Object object) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitHook.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitHook.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/commit/CommitHook.java Thu May 17 15:08:03 2012
@@ -34,7 +34,7 @@ import org.apache.jackrabbit.oak.spi.sta
  *     after = hook.preCommit(store, before, after);
  * }
  *
- * after = store.setRoot(after);
+ * after = branch.merge();
  *
  * for (CommitHook hook : hooks) {
  *     hook.afterCommit(store, before, after);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateBuilder.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStateBuilder.java Thu May 17 15:08:03 2012
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.oak.spi.state;
 
 import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
 
 import java.util.List;
 
@@ -35,55 +34,34 @@ public interface NodeStateBuilder {
     NodeState getNodeState();
 
     /**
-     * Get a builder for a child node
-     *
-     * @param name  name of the child node
-     * @return  builder for the {@code name}d child node
-     */
-    NodeStateBuilder getChildBuilder(String name);
-
-    /**
      * Add a sub-tree
      *
      * @param name  name child node containing the sub-tree
      * @param nodeState  sub-tree
-     * @return  builder for the added sub-tree
      */
-    NodeStateBuilder addNode(String name, NodeState nodeState);
-
-    /**
-     * Add the named child node if it doesn't already exist.
-     *
-     * @param name  name of the child node
-     * @return  a builder for the added child or {@code null} if such a child
-     * already exists
-     */
-    NodeStateBuilder addNode(String name);
+    void setNode(String name, NodeState nodeState);
 
     /**
      * Remove a child node
      * @param name  name of the child node
-     * @return  {@code true} iff the child node existed
      */
-    boolean removeNode(String name);
+    void removeNode(String name);
 
     /**
      * Set a property.
      *
      * @param name property name
      * @param value
-     * @return the affected property state
      */
-    PropertyState setProperty(String name, CoreValue value);
+    void setProperty(String name, CoreValue value);
 
     /**
      * Set a property.
      *
      * @param name property name
      * @param values
-     * @return the affected property state
      */
-    PropertyState setProperty(String name, List<CoreValue> values);
+    void setProperty(String name, List<CoreValue> values);
 
     /**
      * Remove the named property
@@ -91,19 +69,4 @@ public interface NodeStateBuilder {
      */
     void removeProperty(String name);
 
-    /**
-     * Move this node
-     * @param destParent  builder for the parent node of the destination
-     * @param destName  name of the moved node
-     * @return  {@code true} iff the move succeeded
-     */
-    boolean moveTo(NodeStateBuilder destParent, String destName);
-
-    /**
-     * Copy this node
-     * @param destParent  builder for the parent node of the destination
-     * @param destName  name of the copied node
-     * @return  {@code true} iff the copy succeeded
-     */
-    boolean copyTo(NodeStateBuilder destParent, String destName);
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStore.java Thu May 17 15:08:03 2012
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.oak.spi.state;
 
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
 
 /**
@@ -37,6 +36,13 @@ public interface NodeStore {
     NodeState getRoot();
 
     /**
+     * Creates a new branch of the tree to which transient changes can be applied.
+     *
+     * @return branch
+     */
+    NodeStoreBranch branch();
+
+    /**
      * Returns a builder for constructing a new or modified node state.
      * The builder is initialized with all the properties and child nodes
      * from the given base node state.
@@ -54,13 +60,6 @@ public interface NodeStore {
     CoreValueFactory getValueFactory();
 
     /**
-     * Updates the state of the content tree.
-     *
-     * @param newRoot new root node state
-     */
-    void setRoot(NodeState newRoot) throws CommitFailedException;
-
-    /**
      * Compares the given two node states. Any found differences are
      * reported by calling the relevant added, changed or deleted methods
      * of the given handler.

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStoreBranch.java?rev=1339630&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStoreBranch.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/NodeStoreBranch.java Thu May 17 15:08:03 2012
@@ -0,0 +1,71 @@
+/*
+ * 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.spi.state;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+
+public interface NodeStoreBranch {
+
+    /**
+     * Returns the latest state of the branch.
+     *
+     * @return root node state
+     */
+    NodeState getRoot();
+
+    /**
+     * Returns the base state of this branch.
+     *
+     * @return base node state
+     */
+    NodeState getBase();
+
+    /**
+     * Updates the state of the content tree.
+     *
+     * @param newRoot new root node state
+     */
+    void setRoot(NodeState newRoot);
+
+    /**
+     * Moves a node.
+     *
+     * @param source source path
+     * @param target target path
+     * @return  {@code true} iff the move succeeded
+     */
+    boolean move(String source, String target);
+
+    /**
+     * Copies a node.
+     *
+     * @param source source path
+     * @param target target path
+     * @return  {@code true} iff the copy succeeded
+     */
+    boolean copy(String source, String target);
+
+    /**
+     * Merges the changes in this branch to the main content tree.
+     * @return the node state resulting from the merge.
+     *
+     * @throws CommitFailedException if the merge failed
+     */
+    NodeState merge() throws CommitFailedException;
+
+}
+

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ProxyNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ProxyNodeState.java?rev=1339630&r1=1339629&r2=1339630&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ProxyNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/ProxyNodeState.java Thu May 17 15:08:03 2012
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.oak.spi.st
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /**
  * Proxy node state that delegates all method calls to another instance.
  * Mostly useful as a base class for more complicated decorator functionality.
@@ -65,7 +67,22 @@ public class ProxyNodeState implements N
 
     @Override
     public String toString() {
-        return delegate.toString();
+        StringBuilder builder = new StringBuilder("{");
+        AtomicBoolean first = new AtomicBoolean(true);
+        for (PropertyState property : getProperties()) {
+            if (!first.getAndSet(false)) {
+                builder.append(',');
+            }
+            builder.append(' ').append(property);
+        }
+        for (ChildNodeEntry entry : getChildNodeEntries()) {
+            if (!first.getAndSet(false)) {
+                builder.append(',');
+            }
+            builder.append(' ').append(entry);
+        }
+        builder.append(" }");
+        return builder.toString();
     }
 
     @Override



Mime
View raw message