You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2008/07/26 14:42:10 UTC

svn commit: r679985 - in /jackrabbit/sandbox/jackrabbit-ngp/src: main/java/org/apache/jackrabbit/ngp/ main/java/org/apache/jackrabbit/ngp/state/ test/java/org/apache/jackrabbit/ngp/

Author: jukka
Date: Sat Jul 26 05:42:10 2008
New Revision: 679985

URL: http://svn.apache.org/viewvc?rev=679985&view=rev
Log:
jackrabbit-ngp: Session.refresh(true), Node.getNodes(String), etc.

Modified:
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/NodeImpl.java
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/PropertyImpl.java
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/SessionImpl.java
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/NodeState.java
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/PersistentNodeState.java
    jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/TransientNodeState.java
    jackrabbit/sandbox/jackrabbit-ngp/src/test/java/org/apache/jackrabbit/ngp/RepositoryTest.java

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/NodeImpl.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/NodeImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/NodeImpl.java Sat Jul 26 05:42:10 2008
@@ -17,9 +17,7 @@
 package org.apache.jackrabbit.ngp;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.Set;
 
 import javax.jcr.Item;
@@ -47,6 +45,7 @@
 import org.apache.jackrabbit.ngp.state.PersistentNodeState;
 import org.apache.jackrabbit.ngp.state.SingleValuedPropertyState;
 import org.apache.jackrabbit.ngp.state.TransientNodeState;
+import org.apache.jackrabbit.util.ChildrenCollectorFilter;
 
 public class NodeImpl extends AbstractNode {
 
@@ -66,22 +65,16 @@
         if (parent == null) {
             return session.getRoot();
         } else {
-            // Guaranteed to exist, but may be PersistentNodeState.DEL
-            return parent.getNodeState().getNodes().get(name);
+            return parent.getNodeState().getNodeState(name);
         }
     }
 
     TransientNodeState getTransientNodeState() {
-        NodeState state = getNodeState();
-        if (!state.isTransient()) {
-            state = new TransientNodeState((PersistentNodeState) state);
-            if (parent == null) {
-                session.setRoot(state);
-            } else {
-                parent.getTransientNodeState().setNode(name, state);
-            }
+        if (parent == null) {
+            return session.getRoot();
+        } else {
+            return parent.getTransientNodeState().getTransientNodeState(name);
         }
-        return (TransientNodeState) state;
     }
 
     /**
@@ -127,7 +120,7 @@
 
     public PropertyIterator getProperties() throws RepositoryException {
         synchronized (session) {
-            Set<String> names = getNodeState().getProperties().keySet();
+            Set<String> names = getNodeState().getPropertyNames();
             Collection<Property> properties =
                 new ArrayList<Property>(names.size());
             for (String name : names) {
@@ -137,10 +130,23 @@
         }
     }
 
+    public PropertyIterator getProperties(String pattern)
+            throws RepositoryException {
+        ArrayList<Property> properties = new ArrayList<Property>();
+        // traverse children using a special filtering 'collector'
+        accept(new ChildrenCollectorFilter(pattern, properties, false, true, 1));
+        return new PropertyIteratorAdapter(properties);
+    }
+
     public Property getProperty(String relPath) throws RepositoryException {
-        // TODO: Support relative paths
+        int slash = relPath.indexOf('/');
+        if (slash != -1) {
+            Node child = getNode(relPath.substring(0, slash));
+            return child.getProperty(relPath.substring(slash + 1));
+        }
+
         synchronized (session) {
-            if (getNodeState().getProperties().containsKey(relPath)) {
+            if (getNodeState().getPropertyState(relPath) != null) {
                 return new PropertyImpl(session, this, relPath);
             } else {
                 throw new PathNotFoundException(relPath);
@@ -151,10 +157,15 @@
     public Property setProperty(String name, Value value)
             throws RepositoryException {
         synchronized (session) {
-            getTransientNodeState().setProperty(
-                    name, new SingleValuedPropertyState(
-                            value.getType(), value.getString()));
-            return new PropertyImpl(session, this, name);
+            if (value != null) {
+                getTransientNodeState().setProperty(
+                        name, new SingleValuedPropertyState(
+                                value.getType(), value.getString()));
+                return new PropertyImpl(session, this, name);
+            } else {
+                getTransientNodeState().setProperty(name, null);
+                return null;
+            }
         }
     }
 
@@ -174,7 +185,7 @@
 
     public NodeIterator getNodes() throws RepositoryException {
         synchronized (session) {
-            Set<String> names = getNodeState().getNodes().keySet();
+            Set<String> names = getNodeState().getNodeNames();
             Collection<Node> nodes = new ArrayList<Node>(names.size());
             for (String name : names) {
                 nodes.add(new NodeImpl(session, this, name));
@@ -183,15 +194,23 @@
         }
     }
 
+    public NodeIterator getNodes(String pattern) throws RepositoryException {
+        ArrayList<Node> nodes = new ArrayList<Node>();
+        // traverse children using a special filtering 'collector'
+        accept(new ChildrenCollectorFilter(pattern, nodes, true, false, 1));
+        return new NodeIteratorAdapter(nodes);
+    }
+
     public Node getNode(String relPath) throws RepositoryException {
         int slash = relPath.indexOf('/');
         if (slash != -1) {
-            getNode(relPath.substring(0, slash)).getNode(relPath.substring(slash + 1));
+            Node child = getNode(relPath.substring(0, slash));
+            return child.getNode(relPath.substring(slash + 1));
         }
-        // TODO: Support relative paths
+
         synchronized (session) {
-            NodeState state = getNodeState().getNodes().get(relPath);
-            if (state != null && state != PersistentNodeState.DEL) {
+            NodeState state = getNodeState().getNodeState(relPath);
+            if (state != null) {
                 return new NodeImpl(session, this, relPath);
             } else {
                 throw new PathNotFoundException(relPath);
@@ -200,17 +219,22 @@
     }
 
     public Node addNode(String relPath) throws RepositoryException {
-        // TODO: Support relative paths
+        int slash = relPath.indexOf('/');
+        if (slash != -1) {
+            Node child = getNode(relPath.substring(0, slash));
+            return child.addNode(relPath.substring(slash + 1));
+        }
+
         synchronized (session) {
-            NodeState state = getNodeState().getNodes().get(relPath);
-            if (state != null && state != PersistentNodeState.DEL) {
+            NodeState state = getNodeState().getNodeState(relPath);
+            if (state != null) {
                 throw new RepositoryException(); // TODO
             }
-            state = new TransientNodeState(PersistentNodeState.NEW);
-            ((TransientNodeState) state).setProperty(
+            TransientNodeState newState = new TransientNodeState(null);
+            newState.setProperty(
                     "jcr:primaryType", new SingleValuedPropertyState(
                             PropertyType.NAME, "nt:unstructured"));
-            getTransientNodeState().setNode(relPath, state);
+            getTransientNodeState().setNode(relPath, newState);
             return new NodeImpl(session, this, relPath);
         }
     }
@@ -225,8 +249,7 @@
             throw new RepositoryException("Can not remove the root node");
         }
         synchronized (session) {
-            parent.getTransientNodeState().setNode(
-                    name, PersistentNodeState.DEL);
+            parent.getTransientNodeState().setNode(name, null);
         }
     }
 
@@ -272,43 +295,11 @@
         throw new UnsupportedRepositoryOperationException();
     }
 
-    public NodeIterator getNodes(String namePattern)
-            throws RepositoryException {
-        // TODO: Support name patterns
-        if (namePattern.equals("*")) {
-            return getNodes();
-        }
-        Collection<Node> nodes = new ArrayList<Node>();
-        for (String name : new HashSet<String>(Arrays.asList(namePattern.split("\\|")))) {
-            try {
-                nodes.add(getNode(name));
-            } catch (PathNotFoundException e) {
-            }
-        }
-        return new NodeIteratorAdapter(nodes);
-    }
-
     public Item getPrimaryItem() throws RepositoryException {
         // TODO: Support primary items
         throw new ItemNotFoundException();
     }
 
-    public PropertyIterator getProperties(String namePattern)
-            throws RepositoryException {
-        // TODO: Support name patterns
-        if (namePattern.equals("*")) {
-            return getProperties();
-        }
-        Collection<Property> properties = new ArrayList<Property>(1);
-        for (String name : new HashSet<String>(Arrays.asList(namePattern.split("\\|")))) {
-            try {
-                properties.add(getProperty(name));
-            } catch (PathNotFoundException e) {
-            }
-        }
-        return new PropertyIteratorAdapter(properties);
-    }
-
     public PropertyIterator getReferences() throws RepositoryException {
         throw new UnsupportedRepositoryOperationException();
     }

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/PropertyImpl.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/PropertyImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/PropertyImpl.java Sat Jul 26 05:42:10 2008
@@ -83,7 +83,7 @@
     public PropertyDefinition getDefinition() {
         synchronized (session) {
             PropertyState state =
-                parent.getNodeState().getProperties().get(name);
+                parent.getNodeState().getPropertyState(name);
             return new PropertyDefinitionImpl(
                     state.getType(), state.isMultiple());
         }
@@ -91,14 +91,14 @@
 
     public Value getValue() throws RepositoryException {
         synchronized (session) {
-            return parent.getNodeState().getProperties().get(name).getValue(
+            return parent.getNodeState().getPropertyState(name).getValue(
                     session.getValueFactory());
         }
     }
 
     public Value[] getValues() throws RepositoryException {
         synchronized (session) {
-            return parent.getNodeState().getProperties().get(name).getValues(
+            return parent.getNodeState().getPropertyState(name).getValues(
                     session.getValueFactory());
         }
     }

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/SessionImpl.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/SessionImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/SessionImpl.java Sat Jul 26 05:42:10 2008
@@ -50,7 +50,7 @@
      */
     private final Set<String> tokens = new HashSet<String>();
 
-    private NodeState root;
+    private TransientNodeState root;
 
     private boolean live = true;
 
@@ -60,18 +60,14 @@
             throws RepositoryException {
         this.repository = repository;
         this.attributes = attributes;
-        this.root = repository.getRoot();
+        this.root = new TransientNodeState(repository.getRoot());
         this.registry = registry;
     }
 
-    NodeState getRoot() {
+    TransientNodeState getRoot() {
         return root;
     }
 
-    void setRoot(NodeState root) {
-        this.root = root;
-    }
-
     /**
      * Returns the repository this session belongs to.
      *
@@ -149,10 +145,10 @@
      */
     public synchronized void refresh(boolean keepChanges)
             throws RepositoryException {
-        if (!keepChanges || !root.isModified()) {
-            root = repository.getRoot();
+        if (keepChanges) {
+            root = root.rebase(repository.getRoot());
         } else {
-            root = ((TransientNodeState) root).merge(repository.getRoot());
+            root = new TransientNodeState(repository.getRoot());
         }
     }
 

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/NodeState.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/NodeState.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/NodeState.java Sat Jul 26 05:42:10 2008
@@ -16,7 +16,7 @@
  */
 package org.apache.jackrabbit.ngp.state;
 
-import java.util.Map;
+import java.util.Set;
 
 
 public interface NodeState {
@@ -31,12 +31,12 @@
 
     boolean isNew(String property);
 
-    PersistentNodeState persist();
+    Set<String> getPropertyNames();
 
-    NodeState refresh(PersistentNodeState state);
+    PropertyState getPropertyState(String name);
 
-    Map<String, PropertyState> getProperties();
+    Set<String> getNodeNames();
 
-    Map<String, NodeState> getNodes();
+    NodeState getNodeState(String name);
 
 }

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/PersistentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/PersistentNodeState.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/PersistentNodeState.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/PersistentNodeState.java Sat Jul 26 05:42:10 2008
@@ -19,6 +19,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.jcr.PropertyType;
 
@@ -29,33 +30,17 @@
                     "jcr:primaryType",
                     (PropertyState) new SingleValuedPropertyState(
                             PropertyType.NAME, "nt:unstructured")),
-            new HashMap<String, NodeState>());
-
-    public static final PersistentNodeState NEW = new PersistentNodeState(
-            new HashMap<String, PropertyState>(),
-            new HashMap<String, NodeState>());
-
-    public static final PersistentNodeState DEL = new PersistentNodeState(
-            new HashMap<String, PropertyState>(),
-            new HashMap<String, NodeState>());
+            new HashMap<String, PersistentNodeState>());
 
     private final Map<String, PropertyState> properties;
 
-    private final Map<String, NodeState> nodes;
+    private final Map<String, PersistentNodeState> nodes;
 
     public PersistentNodeState(
             Map<String, PropertyState> properties,
-            Map<String, NodeState> nodes) {
-        assert properties != null;
-        assert nodes != null;
-        for (NodeState node : nodes.values()) {
-            assert !node.isTransient();
-        }
-
-        this.properties = Collections.unmodifiableMap(
-                new HashMap<String, PropertyState>(properties));
-        this.nodes = Collections.unmodifiableMap(
-                new HashMap<String, NodeState>(nodes));
+            Map<String, PersistentNodeState> nodes) {
+        this.properties = properties;
+        this.nodes = nodes;
     }
 
     //---------------------------------------------------------< NodeState >--
@@ -80,20 +65,26 @@
         return false;
     }
 
-    public PersistentNodeState persist() {
-        return this;
+    public Set<String> getPropertyNames() {
+        return properties.keySet();
+    }
+
+    public PropertyState getPropertyState(String name) {
+        return properties.get(name);
     }
 
-    public NodeState refresh(PersistentNodeState state) {
-        return state;
+    public Set<String> getNodeNames() {
+        return nodes.keySet();
     }
 
-    public Map<String, PropertyState> getProperties() {
-        return properties;
+    public NodeState getNodeState(String name) {
+        return nodes.get(name);
     }
 
-    public Map<String, NodeState> getNodes() {
-        return nodes;
+    //-----------------------------------------------< PersistentNodeState >--
+
+    public PersistentNodeState getPersistentNodeState(String name) {
+        return nodes.get(name);
     }
 
 }

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/TransientNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/TransientNodeState.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/TransientNodeState.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/main/java/org/apache/jackrabbit/ngp/state/TransientNodeState.java Sat Jul 26 05:42:10 2008
@@ -17,21 +17,40 @@
 package org.apache.jackrabbit.ngp.state;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
-
+import java.util.Set;
 
 public class TransientNodeState implements NodeState {
 
     private final PersistentNodeState base;
 
+    /**
+     * The properties of this node that have been modified
+     * (added, updated, removed) in this transient space. Added or updated
+     * properties are represented by the transient PropertyState instances
+     * that contain the new property values. Removed properties are
+     * represented by <code>null</code> values in this map.
+     */
     private final Map<String, PropertyState> properties =
         new HashMap<String, PropertyState>();
 
-    private final Map<String, NodeState> nodes =
-        new HashMap<String, NodeState>();
+    /**
+     * The children of this node that have been modified
+     * (added, updated, removed) in this transient space. Added nodes
+     * are represented by transient node states with a <code>null</code>
+     * base state. Updated nodes are represented by transient node
+     * states with a non-<code>null</code> base state. Removed nodes
+     * are represented by <code>null</code> values in this map.
+     * <p>
+     * Transient moves can be detected as updated child nodes whose
+     * base state differs from the base state of this transient node.
+     * Similar heuristics can be used to detect transient renames.
+     */
+    private final Map<String, TransientNodeState> nodes =
+        new HashMap<String, TransientNodeState>();
 
     public TransientNodeState(PersistentNodeState base) {
-        assert base != null;
         this.base = base;
     }
 
@@ -51,110 +70,148 @@
     }
 
     public boolean isNew() {
-        return base == PersistentNodeState.NEW;
+        return base == null;
     }
 
     public boolean isNew(String property) {
-        return properties.containsKey(property)
-            && !base.getProperties().containsKey(property);
+        return properties.get(property) != null
+            && base.getPropertyState(property) == null;
     }
 
-    public PersistentNodeState persist() {
-        Map<String, NodeState> result = base.getNodes();
-        if (!nodes.isEmpty()) {
-            result = new HashMap<String, NodeState>(result);
-            for (Map.Entry<String, NodeState> entry : nodes.entrySet()) {
-                if (entry.getValue() != null) {
-                    result.put(entry.getKey(), entry.getValue().persist());
+    public Set<String> getPropertyNames() {
+        Set<String> names = base.getPropertyNames();
+        if (!properties.isEmpty()) {
+            names = new HashSet<String>(names);
+            for (String name : properties.keySet()) {
+                if (properties.get(name) != null) {
+                    names.add(name);
                 } else {
-                    result.remove(entry.getKey());
+                    names.remove(name);
                 }
             }
         }
-        return new PersistentNodeState(getProperties(), result);
+        return names;
     }
 
-    public NodeState refresh(PersistentNodeState state) {
-        if (!state.equals(base)) {
-            for (Map.Entry<String, NodeState> entry : getNodes().entrySet()) {
-                NodeState child = state.getNodes().get(entry.getKey());
-                if (child == null) {
-                    
-                }
-              
-            }
+    public PropertyState getPropertyState(String name) {
+        if (properties.containsKey(name)) {
+            return properties.get(name);
+        } else if (base != null) {
+            return base.getPropertyState(name);
+        } else {
+            return null;
         }
-        return this;
     }
 
-    public Map<String, PropertyState> getProperties() {
-        Map<String, PropertyState> result = base.getProperties();
-        if (!properties.isEmpty()) {
-            result = new HashMap<String, PropertyState>(result);
-            for (Map.Entry<String, PropertyState> entry : properties.entrySet()) {
-                if (entry.getValue() != null) {
-                    result.put(entry.getKey(), entry.getValue());
+    public Set<String> getNodeNames() {
+        Set<String> names = base.getNodeNames();
+        if (!nodes.isEmpty()) {
+            names = new HashSet<String>(names);
+            for (String name : nodes.keySet()) {
+                if (nodes.get(name) != null) {
+                    names.add(name);
                 } else {
-                    result.remove(entry.getKey());
+                    names.remove(name);
                 }
             }
         }
-        return result;
+        return names;
     }
 
-    public Map<String, NodeState> getNodes() {
-        Map<String, NodeState> result = base.getNodes();
-        if (!nodes.isEmpty()) {
-            result = new HashMap<String, NodeState>(result);
-            for (Map.Entry<String, NodeState> entry : nodes.entrySet()) {
-                if (entry.getValue() != null) {
-                    result.put(entry.getKey(), entry.getValue());
-                } else {
-                    result.remove(entry.getKey());
-                }
-            }
+    public NodeState getNodeState(String name) {
+        if (nodes.containsKey(name)) {
+            return nodes.get(name);
+        } else if (base != null) {
+            return base.getNodeState(name);
+        } else {
+            return null;
         }
-        return result;
     }
 
     //------------------------------------------------< TransientNodeState >--
 
-    public PersistentNodeState getBase() {
-        return base;
+    public TransientNodeState getTransientNodeState(String name) {
+        if (nodes.containsKey(name)) {
+            return nodes.get(name);
+        } else {
+            TransientNodeState state =
+                new TransientNodeState(base.getPersistentNodeState(name));
+            nodes.put(name, state);
+            return state;
+        }
     }
 
     public void setProperty(String name, PropertyState value) {
-        if (value != null || base.getProperties().containsKey(name)) {
-            properties.put(name, value);
-        } else {
-            properties.remove(value);
-        }
+        properties.put(name, value);
     }
 
-    public void setNode(String name, NodeState value) {
-        if (value != null || base.getNodes().containsKey(name)) {
-            nodes.put(name, value);
-        } else {
-            nodes.remove(value);
+    public void setNode(String name, TransientNodeState value) {
+        nodes.put(name, value);
+    }
+
+    public PersistentNodeState persist() {
+        if (nodes.isEmpty() && properties.isEmpty()) {
+            return base;
+        }
+
+        Map<String, PropertyState> persistentProperties =
+            new HashMap<String, PropertyState>();
+        if (base != null) {
+            for (String name : base.getPropertyNames()) {
+                persistentProperties.put(name, base.getPropertyState(name));
+            }
+        }
+        for (String name : properties.keySet()) {
+            PropertyState value = properties.get(name);
+            if (value != null) {
+                persistentProperties.put(name, value);
+            } else {
+                persistentProperties.remove(name);
+            }
         }
+
+        Map<String, PersistentNodeState> persistentNodes =
+            new HashMap<String, PersistentNodeState>();
+        if (base != null) {
+            for (String name : base.getNodeNames()) {
+                persistentNodes.put(name, base.getPersistentNodeState(name));
+            }
+        }
+        for (String name : nodes.keySet()) {
+            TransientNodeState value = nodes.get(name);
+            if (value != null) {
+                persistentNodes.put(name, value.persist());
+            } else {
+                persistentNodes.remove(name);
+            }
+        }
+
+        return new PersistentNodeState(persistentProperties, persistentNodes);
     }
 
-    public TransientNodeState merge(PersistentNodeState rebase) {
-        if (base.equals(rebase)) {
+    public TransientNodeState rebase(PersistentNodeState newBase) {
+        if (base != null && base.equals(newBase)) {
             return this;
         } else {
-            TransientNodeState result = new TransientNodeState(rebase);
-            for (Map.Entry<String, PropertyState> entry : properties.entrySet()) {
-                result.setProperty(entry.getKey(), entry.getValue());
-            }
-            for (Map.Entry<String, NodeState> entry : nodes.entrySet()) {
-                NodeState child = entry.getValue();
-                if (child instanceof TransientNodeState) {
-                    child = ((TransientNodeState) child).merge(
-                            (PersistentNodeState) rebase.getNodes().get(entry.getKey()));
+            TransientNodeState result = new TransientNodeState(newBase);
+
+            for (String name : properties.keySet()) {
+                result.setProperty(name, properties.get(name));
+            }
+
+            for (String name : nodes.keySet()) {
+                TransientNodeState child = nodes.get(name);
+                if (child != null) {
+                    if (newBase != null) {
+                        child = child.rebase(
+                                newBase.getPersistentNodeState(name));
+                    }
+                    result.setNode(name, child);
+                } else {
+                    result.setNode(name, null);
                 }
-                result.setNode(entry.getKey(), child);
             }
+
             return result;
         }
     }

Modified: jackrabbit/sandbox/jackrabbit-ngp/src/test/java/org/apache/jackrabbit/ngp/RepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-ngp/src/test/java/org/apache/jackrabbit/ngp/RepositoryTest.java?rev=679985&r1=679984&r2=679985&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-ngp/src/test/java/org/apache/jackrabbit/ngp/RepositoryTest.java (original)
+++ jackrabbit/sandbox/jackrabbit-ngp/src/test/java/org/apache/jackrabbit/ngp/RepositoryTest.java Sat Jul 26 05:42:10 2008
@@ -118,6 +118,13 @@
                 assertFalse(sessionB.getRootNode().hasNode("test"));
                 sessionB.refresh(true);
                 assertTrue(sessionB.getRootNode().hasNode("test"));
+
+                assertTrue(sessionB.getRootNode().hasNode("foo"));
+                assertFalse(sessionA.getRootNode().hasNode("foo"));
+                sessionB.save();
+                assertFalse(sessionA.getRootNode().hasNode("foo"));
+                sessionA.refresh(true);
+                assertTrue(sessionA.getRootNode().hasNode("foo"));
             } finally {
                 sessionB.logout();
             }