You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2007/02/13 10:31:53 UTC

svn commit: r506927 [6/8] - in /jackrabbit/trunk/contrib/spi: jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/ jcr2spi...

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Tue Feb 13 01:31:36 2007
@@ -17,28 +17,22 @@
 package org.apache.jackrabbit.jcr2spi.state;
 
 import org.apache.commons.collections.iterators.IteratorChain;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
-import org.apache.jackrabbit.jcr2spi.state.entry.PropertyReference;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildItemEntry;
 import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
-import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
+import org.apache.jackrabbit.jcr2spi.util.StateUtility;
+import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.QNodeDefinition;
-import org.apache.jackrabbit.spi.QValue;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
 import javax.jcr.RepositoryException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
+
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -52,21 +46,12 @@
 
     private static Logger log = LoggerFactory.getLogger(NodeState.class);
 
-    /**
-     * the name of this node's primary type
-     */
-    private QName nodeTypeName;
-
-    /**
-     * The unique ID of this node state or <code>null</code> if this node state
-     * cannot be identified with a unique ID.
-     */
-    private String uniqueID;
+    private NodeEntry hierarchyEntry;
 
     /**
-     * The name of this node state
+     * the name of this node's primary type
      */
-    private QName name;
+    private final QName nodeTypeName;
 
     /**
      * Definition of this node state
@@ -79,49 +64,20 @@
     private QName[] mixinTypeNames = QName.EMPTY_ARRAY;
 
     /**
-     * insertion-ordered collection of ChildNodeEntry objects
-     */
-    private ChildNodeEntries childNodeEntries;
-
-    /**
-     * Map of properties. Key = {@link QName} of property. Value = {@link
-     * PropertyReference}.
-     */
-    private HashMap properties = new HashMap();
-
-    /**
-     * Map of properties which are deleted and have been re-created as transient
-     * property with the same name.
-     */
-    private HashMap propertiesInAttic = new HashMap();
-
-    /**
-     * NodeReferences for this node state.
-     */
-    private NodeReferences references;
-
-    /**
      * Constructs a new node state that is not connected.
      *
-     * @param name          the name of this NodeState
-     * @param uniqueID      the uniqueID of this NodeState or <code>null</code> if
-     *                      this node state cannot be identified with a UUID.
-     * @param parent        the parent of this NodeState
      * @param nodeTypeName  node type of this node
-     * @param definition
+     * @param entry
      * @param initialStatus the initial status of the node state object
-     * @param isf           the item state factory responsible for creating node
-     *                      states.
-     * @param idFactory     the <code>IdFactory</code> to create new id
-     */
-    protected NodeState(QName name, String uniqueID, NodeState parent,
-                        QName nodeTypeName, QNodeDefinition definition,
-                        int initialStatus, ItemStateFactory isf,
-                        IdFactory idFactory, boolean isWorkspaceState) {
-        super(parent, initialStatus, isf, idFactory, isWorkspaceState);
-        this.name = name;
-        this.uniqueID = uniqueID;
+     */
+    protected NodeState(NodeEntry entry, QName nodeTypeName, QName[] mixinTypeNames,
+                        QNodeDefinition definition, int initialStatus,
+                        boolean isWorkspaceState,
+                        ItemStateFactory isf, NodeTypeRegistry ntReg) {
+        super(initialStatus, isWorkspaceState, isf, ntReg);
+        this.hierarchyEntry = entry;
         this.nodeTypeName = nodeTypeName;
+        setMixinTypeNames(mixinTypeNames);
         this.definition = definition;
     }
 
@@ -130,63 +86,29 @@
      * overlayed state.
      *
      * @param overlayedState the backing node state being overlayed
-     * @param parent         the parent of this NodeState
      * @param initialStatus  the initial status of the node state object
-     * @param idFactory      the <code>IdFactory</code> to create new id
-     *                       instance.
      */
-    protected NodeState(NodeState overlayedState, NodeState parent,
-                        int initialStatus, ItemStateFactory isf,
-                        IdFactory idFactory) {
-        super(overlayedState, parent, initialStatus, isf, idFactory);
-        if (overlayedState != null) {
-            synchronized (overlayedState) {
-                NodeState wspState = overlayedState;
-                name = wspState.name;
-                uniqueID = wspState.uniqueID;
-                nodeTypeName = wspState.nodeTypeName;
-                definition = wspState.definition;
+    protected NodeState(NodeState overlayedState, int initialStatus, ItemStateFactory isf) {
+        super(overlayedState, initialStatus, isf);
 
-                init(wspState.getMixinTypeNames(), wspState.getPropertyNames(), wspState.getNodeReferences());
+        synchronized (overlayedState) {
+            hierarchyEntry = overlayedState.hierarchyEntry;
+            nodeTypeName = overlayedState.nodeTypeName;
+            definition = overlayedState.definition;
+            if (mixinTypeNames != null) {
+                this.mixinTypeNames = overlayedState.mixinTypeNames;
             }
         }
     }
 
+    //----------------------------------------------------------< ItemState >---
     /**
-     *
-     * @param mixinTypeNames
-     * @param propertyNames
-     * @param references
+     * @see ItemState#getHierarchyEntry()
      */
-    void init(QName[] mixinTypeNames, Collection propertyNames, NodeReferences references) {
-        if (mixinTypeNames != null) {
-            this.mixinTypeNames = mixinTypeNames;
-        }
-        // set the node references
-        this.references = references;
-        // add property references
-        propertiesInAttic.clear();
-        properties.clear();
-        Iterator it = propertyNames.iterator();
-        while (it.hasNext()) {
-            QName propName = (QName) it.next();
-            ChildPropertyEntry pe = PropertyReference.create(this, propName, isf, idFactory);
-            properties.put(propName, pe);
-        }
+    public HierarchyEntry getHierarchyEntry() {
+        return hierarchyEntry;
     }
 
-    private ChildNodeEntries childNodeEntries() {
-        if (childNodeEntries == null) {
-            try {
-                childNodeEntries = isf.getChildNodeEntries(this);
-            } catch (ItemStateException e) {
-                // TODO improve
-                throw new IllegalStateException();
-            }
-        }
-        return childNodeEntries;
-    }
-    //----------------------------------------------------------< ItemState >---
     /**
      * Determines if this item state represents a node.
      *
@@ -198,76 +120,17 @@
     }
 
     /**
-     * @see ItemState#getQName()
-     */
-    public final QName getQName() {
-        return name;
-    }
-
-    /**
      * {@inheritDoc}
      * @see ItemState#getId()
      */
     public ItemId getId() {
         return getNodeId();
     }
-
+    
     /**
-     * {@inheritDoc}
-     * @see ItemState#reload(boolean)
+     * @see ItemState#merge(ItemState, boolean)
      */
-    public void reload(boolean keepChanges) {
-        // recursivly reload states from peristent storage including props
-        // that are in the attic.
-        for (Iterator it = getAllChildEntries(true, true); it.hasNext();) {
-            ChildItemEntry ce = (ChildItemEntry) it.next();
-            if (ce.isAvailable()) {
-                try {
-                    ce.getItemState().reload(keepChanges);
-                } catch (ItemStateException e) {
-                    // should not occur
-                }
-            }
-        }
-
-        if (isWorkspaceState()) {
-            // reload from persistent storage ('keepChanges' not relevant).
-            try {
-                /* TODO: TOBEFIXED.
-                   recreating nodestate not correct. parent still has entry pointing
-                   to this state and subsequently retrieving the childentries fails,
-                   since id of tmp state cannot be resolved.
-                   -> add workaround until state hierarchy has been modified
-                */
-                NodeState tmp = isf.createNodeState(getNodeId(), getParent());
-                tmp.childNodeEntries = isf.getChildNodeEntries(this);
-
-                if (merge(tmp, false) || getStatus() == Status.INVALIDATED) {
-                    setStatus(Status.MODIFIED);
-                }
-            } catch (NoSuchItemStateException e) {
-                // remove entry from parent
-                getParent().childNodeEntries().remove(this);
-                // inform overlaying state and listeners
-                setStatus(Status.REMOVED);
-            } catch (ItemStateException e) {
-                // TODO rather throw? remove from parent?
-                log.warn("Exception while refreshing property state: " + e);
-                log.debug("Stacktrace: ", e);
-            }
-        } else {
-            /* session-state: if keepChanges is true only existing or invalidated
-               states must be updated. otherwise the state gets updated and might
-               be marked 'Stale' if transient changes are present and the
-               workspace-state is modified. */
-            if (!keepChanges || getStatus() == Status.EXISTING || getStatus() == Status.INVALIDATED) {
-                // calling refresh on the workspace state will in turn reset this state
-                overlayedState.reload(keepChanges);
-            }
-        }
-    }
-
-    boolean merge(ItemState another, boolean keepChanges) {
+    public boolean merge(ItemState another, boolean keepChanges) {
         if (another == null || another == this) {
             return false;
         }
@@ -277,150 +140,42 @@
         boolean modified = false;
         synchronized (another) {
             NodeState nState = (NodeState) another;
-            name = nState.name;
-            setUniqueID(nState.uniqueID);
-            nodeTypeName = nState.nodeTypeName;
-            definition = nState.definition;
 
-            // refs, mixin-types can be copied. they are never transiently changed.
-            references = nState.references;
-            List mixN = Arrays.asList(nState.mixinTypeNames);
-            modified = (mixN.size() != mixinTypeNames.length || !mixN.containsAll(Arrays.asList(mixinTypeNames)));
-            mixinTypeNames = nState.mixinTypeNames;
-
-            if (!keepChanges && !propertiesInAttic.isEmpty()) {
-                // remove all entries in the attic
+            if (hierarchyEntry != nState.hierarchyEntry) {
+                hierarchyEntry = nState.hierarchyEntry;
                 modified = true;
-                propertiesInAttic.clear();
             }
-
-            /* merge child entries without loosing valid entries and connected states. */
-            // add all entry from wspState that are missing in this state
-            for (Iterator it = nState.getAllChildEntries(false, false); it.hasNext();) {
-                ChildItemEntry ce = (ChildItemEntry) it.next();
-                QName childName = ce.getName();
-                if (ce.denotesNode()) {
-                    ChildNodeEntry cne = (ChildNodeEntry) ce;
-                    int index = cne.getIndex();
-                    if (!childNodeEntries().contains(childName, index, cne.getUniqueID())) {
-                        modified = true;
-                        childNodeEntries().add(childName, cne.getUniqueID(), index);
-                    }
-                } else {
-                    if (!hasPropertyName(childName)) {
-                        modified = true;
-                        addPropertyEntry(PropertyReference.create(this, childName, isf, idFactory));
-                    }
-                }
+            if (nState.definition != null && !nState.definition.equals(definition)) {
+                definition = nState.definition;
+                modified = true;
             }
 
-            // if keepChanges is false, remove all entries from this state,
-            // that are missing in the given other state.
-            if (!keepChanges) {
-                for (Iterator it = getAllChildEntries(true, false); it.hasNext();) {
-                    ChildItemEntry ce = (ChildItemEntry) it.next();
-                    QName childName = ce.getName();
-                    boolean toRemove = false;
-                    if (ce.denotesNode()) {
-                        ChildNodeEntry cne = (ChildNodeEntry) ce;
-                        int index = cne.getIndex();
-                        toRemove = !nState.childNodeEntries().contains(childName, index, cne.getUniqueID());
-                    } else {
-                        toRemove = !nState.properties.containsKey(childName);
-                    }
-
-                    if (toRemove) {
-                        modified = true;
-                        if (ce.isAvailable()) {
-                            // TODO: check if correct.
-                            try {
-                                ItemState st = ce.getItemState();
-                                if (st.getStatus() == Status.EXISTING_MODIFIED) {
-                                    st.setStatus(Status.STALE_DESTROYED);
-                                } else {
-                                    st.setStatus(Status.REMOVED);
-                                }
-                            } catch (ItemStateException e) {
-                                log.error("Internal error", e);
-                            }
-                        }
-                        // and remove the corresponding entry
-                        if (ce.denotesNode()) {
-                            childNodeEntries().remove(childName, ((ChildNodeEntry)ce).getIndex());
-                        } else {
-                            properties.remove(childName);
-                        }
-                    }
-                }
+            // since 'mixinTypeNames' are modified upon save only, no special
+            // merging is required here. just reset the mixinTypeNames.
+            List mixN = Arrays.asList(nState.mixinTypeNames);
+            if (mixN.size() != mixinTypeNames.length || !mixN.containsAll(Arrays.asList(mixinTypeNames))) {
+                setMixinTypeNames(nState.mixinTypeNames);
+                modified = true;
             }
         }
-
         return modified;
     }
 
+    //----------------------------------------------------------< NodeState >---
     /**
-     * {@inheritDoc}
-     * @see ItemState#invalidate(boolean)
+     * @return The <code>NodeEntry</code> associated with this state.
      */
-    public void invalidate(boolean recursive) {
-        if (recursive) {
-            // invalidate all child entries including properties present in the
-            // attic (removed props shadowed by a new property with the same name).
-            for (Iterator it = getAllChildEntries(false, true); it.hasNext();) {
-                ChildItemEntry ce = (ChildItemEntry) it.next();
-                if (ce.isAvailable()) {
-                    try {
-                        ce.getItemState().invalidate(true);
-                    } catch (ItemStateException e) {
-                        // should not occur
-                    }
-                }
-            }
-        }
-        // ... and invalidate this state
-        if (isWorkspaceState()) {
-            // workspace state
-            setStatus(Status.INVALIDATED);
-        } else {
-            // TODO only invalidate if existing?
-            if (getStatus() == Status.EXISTING) {
-                // set workspace state invalidated, this will in turn invalidate
-                // this (session) state as well
-                overlayedState.setStatus(Status.INVALIDATED);
-            }
-        }
+    public NodeEntry getNodeEntry() {
+        return (NodeEntry) getHierarchyEntry();
     }
-    //----------------------------------------------------------< NodeState >---
+
     /**
      * Returns the id of this node state.
      *
      * @return the id of this node state.
      */
     public NodeId getNodeId() {
-        if (uniqueID != null) {
-            return idFactory.createNodeId(uniqueID);
-        }
-
-        NodeState parent = getParent();
-        if (parent == null) {
-           // root node
-            return idFactory.createNodeId((String) null, Path.ROOT);
-        } else {
-            // find this in parent child node entries
-            for (Iterator it = parent.childNodeEntries().get(name).iterator(); it.hasNext(); ) {
-                ChildNodeEntry cne = (ChildNodeEntry) it.next();
-                try {
-                    if (cne.getNodeState() == this) {
-                        Path relPath = Path.create(cne.getName(), cne.getIndex());
-                        return idFactory.createNodeId(parent.getNodeId(), relPath);
-                    }
-                } catch (ItemStateException e) {
-                    log.warn("Unable to access child node entry: " + cne.getId());
-                }
-            }
-        }
-        // TODO: replace with ItemStateException instead of error.
-        throw new InternalError("Unable to retrieve NodeId for NodeState");
+        return getNodeEntry().getId();
     }
 
     /**
@@ -428,49 +183,16 @@
      * node cannot be identified with a unique ID.
      */
     public String getUniqueID() {
-        return uniqueID;
-    }
-
-    /**
-     * Modify the uniqueID of this state and make sure, that the parent state
-     * contains a proper childNodeEntry for this state. If the given uniqueID is
-     * not different from the uniqueID of this state, the method returns silently
-     * without changing neither the parent nor this state.
-     *
-     * @param uniqueID
-     */
-    private void setUniqueID(String uniqueID) {
-        String oldUniqueID = this.uniqueID;
-        boolean mod = (oldUniqueID == null) ? uniqueID != null : !oldUniqueID.equals(uniqueID);
-        if (mod) {
-            this.uniqueID = uniqueID;
-            if (getParent() != null) {
-                getParent().childNodeEntries().replaceEntry(this);
-            }
-        }
+        return getNodeEntry().getUniqueID();
     }
 
     /**
-     * Returns the index of this node state.
+     * Returns true, if this <code>NodeState</code> represent the root node.
      *
-     * @return the index.
+     * @return true if this <code>NodeState</code> represent the root node.
      */
-    public int getIndex() throws ItemNotFoundException {
-        if (getParent() == null) {
-            // the root state may never have siblings
-            return Path.INDEX_DEFAULT;
-        }
-
-        if (getDefinition().allowsSameNameSiblings()) {
-            ChildNodeEntry entry = getParent().childNodeEntries().get(this);
-            if (entry == null) {
-                String msg = "Unable to retrieve index for: " + this;
-                throw new ItemNotFoundException(msg);
-            }
-            return entry.getIndex();
-        } else {
-            return Path.INDEX_DEFAULT;
-        }
+    public boolean isRoot() {
+        return getHierarchyEntry().getParent() == null;
     }
 
     /**
@@ -487,11 +209,23 @@
      *
      * @return a set of the names of this node's mixin types.
      */
-    public synchronized QName[] getMixinTypeNames() {
+    public QName[] getMixinTypeNames() {
         return mixinTypeNames;
     }
 
     /**
+     * TODO improve
+     * !! Used by NodeEntryImpl and NodeState only
+     *
+     * @param mixinTypeNames
+     */
+    public void setMixinTypeNames(QName[] mixinTypeNames) {
+        if (mixinTypeNames != null) {
+            this.mixinTypeNames = mixinTypeNames;
+        }
+    }
+
+    /**
      * Return all nodetype names that are defined to this <code>NodeState</code>
      * including the primary nodetype and the mixins.
      *
@@ -514,365 +248,91 @@
      *
      * @return definition of this state
      */
-    public QNodeDefinition getDefinition() {
+    public QNodeDefinition getDefinition() throws RepositoryException {
+        if (definition == null) {
+            definition = getEffectiveNodeType().getApplicableNodeDefinition(getQName(), getNodeTypeName(), getNodeTypeRegistry());
+        }
         return definition;
     }
 
-
     /**
      * Return the <code>NodeReferences</code> present on this state or
      * <code>null</code>.
      *
      * @return references
      */
-    NodeReferences getNodeReferences() {
-        return references;
-    }
-
-    /**
-     * Determines if there are any valid child node entries.
-     *
-     * @return <code>true</code> if there are child node entries,
-     * <code>false</code> otherwise.
-     */
-    public boolean hasChildNodeEntries() {
-        return containsValidChildNodeEntry(childNodeEntries());
+    public NodeReferences getNodeReferences() {
+        return isf.getNodeReferences(this);
     }
 
     /**
-     * Determines if there is a valid <code>ChildNodeEntry</code> with the
-     * specified <code>name</code>.
-     *
-     * @param name <code>QName</code> object specifying a node name
-     * @return <code>true</code> if there is a <code>ChildNodeEntry</code> with
-     *         the specified <code>name</code>.
-     */
-    public synchronized boolean hasChildNodeEntry(QName name) {
-        return containsValidChildNodeEntry(childNodeEntries().get(name));
-    }
-
-    /**
-     * Determines if there is a valid <code>ChildNodeEntry</code> with the
+     * Utility
+     * Determines if there is a valid <code>NodeEntry</code> with the
      * specified <code>name</code> and <code>index</code>.
      *
      * @param name  <code>QName</code> object specifying a node name.
      * @param index 1-based index if there are same-name child node entries.
-     * @return <code>true</code> if there is a <code>ChildNodeEntry</code> with
+     * @return <code>true</code> if there is a <code>NodeEntry</code> with
      * the specified <code>name</code> and <code>index</code>.
      */
-    public synchronized boolean hasChildNodeEntry(QName name, int index) {
-        return isValidChildNodeEntry(childNodeEntries().get(name, index));
+    public boolean hasChildNodeEntry(QName name, int index) {
+        return getNodeEntry().hasNodeEntry(name, index);
     }
 
     /**
-     * Returns the valid <code>ChildNodeEntry</code> with the specified name
-     * and index or <code>null</code> if there's no matching entry.
+     * Utility
+     * Returns the child <code>NodeState</code> with the specified name
+     * and index or <code>null</code> if there's no matching, valid entry.
      *
      * @param nodeName <code>QName</code> object specifying a node name.
      * @param index 1-based index if there are same-name child node entries.
-     * @return The <code>ChildNodeEntry</code> with the specified name and index
-     * or <code>null</code> if there's no matching entry.
+     * @return The <code>NodeState</code> with the specified name and index
+     * @throws NoSuchItemStateException
+     * @throws ItemStateException
      */
-    public synchronized ChildNodeEntry getChildNodeEntry(QName nodeName, int index) {
-        ChildNodeEntry cne = childNodeEntries().get(nodeName, index);
-        if (isValidChildNodeEntry(cne)) {
-            return cne;
+    public NodeState getChildNodeState(QName nodeName, int index) throws NoSuchItemStateException, ItemStateException {
+        NodeEntry child = getNodeEntry().getNodeEntry(nodeName, index);
+        if (child != null) {
+            return child.getNodeState();
         } else {
-            return null;
+            // TODO: correct?
+            throw new NoSuchItemStateException("Child node "+ nodeName +" with index " + index + " does not exist.");
         }
     }
 
     /**
-     * Returns the <code>ChildNodeEntry</code> with the specified
-     * <code>NodeId</code> or <code>null</code> if there's no matching
-     * entry.
-     *
-     * @param nodeId the id of the child node state.
-     * @return the <code>ChildNodeEntry</code> with the specified
-     * <code>NodeId</code> or <code>null</code> if there's no matching entry.
-     */
-    synchronized ChildNodeEntry getChildNodeEntry(NodeId nodeId) {
-        String uid = nodeId.getUniqueID();
-        Path path = nodeId.getPath();
-        ChildNodeEntry cne;
-        if (uid != null && path == null) {
-            // retrieve child-entry by uid
-            cne = childNodeEntries().get(null, uid);
-        } else {
-           // retrieve child-entry by name and index
-            Path.PathElement nameElement = path.getNameElement();
-            cne = childNodeEntries().get(nameElement.getName(), nameElement.getIndex());
-        }
-
-        if (isValidChildNodeEntry(cne)) {
-            return cne;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a unmodifiable collection of <code>ChildNodeEntry</code> objects
-     * denoting the child nodes of this node.
-     *
-     * @return collection of <code>ChildNodeEntry</code> objects
-     */
-    public synchronized Collection getChildNodeEntries() {
-        Collection entries = new ArrayList();
-        for (Iterator it = childNodeEntries().iterator(); it.hasNext();) {
-            ChildNodeEntry cne = (ChildNodeEntry) it.next();
-            if (isValidChildNodeEntry(cne)) {
-                entries.add(cne);
-            }
-        }
-        return Collections.unmodifiableCollection(entries);
-    }
-
-    /**
-     * Returns a unmodifiable list of <code>ChildNodeEntry</code>s with the
-     * specified name.
-     *
-     * @param nodeName name of the child node entries that should be returned
-     * @return list of <code>ChildNodeEntry</code> objects
-     */
-    public synchronized List getChildNodeEntries(QName nodeName) {
-        List entries = new ArrayList();
-        for (Iterator it = childNodeEntries().get(nodeName).iterator(); it.hasNext();) {
-            ChildNodeEntry cne = (ChildNodeEntry) it.next();
-            if (isValidChildNodeEntry(cne)) {
-                entries.add(cne);
-            }
-        }
-        return Collections.unmodifiableList(entries);
-    }
-
-    /**
-     * Determines if there is a property entry with the specified
-     * <code>QName</code>.
+     * Utility
      *
      * @param propName <code>QName</code> object specifying a property name
-     * @return <code>true</code> if there is a property entry with the specified
-     *         <code>QName</code>.
+     * @return <code>true</code> if there is a valid property entry with the
+     * specified <code>QName</code>.
      */
-    public synchronized boolean hasPropertyName(QName propName) {
-        ChildPropertyEntry entry = (ChildPropertyEntry) properties.get(propName);
-        return isValidChildPropertyEntry(entry);
+    public boolean hasPropertyName(QName propName) {
+        return getNodeEntry().hasPropertyEntry(propName);
     }
 
     /**
-     * Returns the names of this node's properties as a set of
-     * <code>QNames</code> objects.
-     *
-     * @return set of <code>QNames</code> objects
-     */
-    public synchronized Collection getPropertyNames() {
-        Collection names;
-        if (getStatus() == Status.EXISTING_MODIFIED) {
-            names = new ArrayList();
-            for (Iterator it = getPropertyEntries().iterator(); it.hasNext(); ) {
-                names.add(((ChildPropertyEntry) it.next()).getName());
-            }
-        } else {
-            // this node state is unmodified, return all
-            names = properties.keySet();
-        }
-        return Collections.unmodifiableCollection(names);
-    }
-
-    /**
-     * Returns the complete collection of {@link ChildPropertyEntry}s.
-     *
-     * @return unmodifiable collection of <code>ChildPropertyEntry</code> objects
-     */
-    public synchronized Collection getPropertyEntries() {
-        Collection props;
-        if (getStatus() == Status.EXISTING_MODIFIED) {
-            // filter out removed properties
-            props = new ArrayList();
-            for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
-                ChildPropertyEntry propEntry = (ChildPropertyEntry) it.next();
-                if (isValidChildPropertyEntry(propEntry)) {
-                    props.add(propEntry);
-                }
-            }
-        } else {
-            // no need to filter out properties, there are no removed properties
-            props = properties.values();
-        }
-        return Collections.unmodifiableCollection(props);
-    }
-
-    /*
-     * Returns the property state with the given name.
+     * Utility method that returns the property state with the given name.
      *
      * @param propertyName The name of the property state to return.
      * @throws NoSuchItemStateException If there is no (valid) property state
      * with the given name.
      * @throws ItemStateException If an error occurs while retrieving the
      * property state.
-     */
-    public synchronized PropertyState getPropertyState(QName propertyName)
-        throws NoSuchItemStateException, ItemStateException {
-
-        ChildPropertyEntry propEntry = (ChildPropertyEntry) properties.get(propertyName);
-        if (propEntry == null) {
-            throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
-        } else {
-            PropertyState propState = propEntry.getPropertyState();
-            if (propState.isValid()) {
-                return propState;
-            } else {
-                throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
-            }
-        }
-    }
-
-    /**
      *
-     * @param propEntry
+     * @see NodeEntry#getPropertyEntry(QName)
+     * @see PropertyEntry#getPropertyState()
      */
-    private void addPropertyEntry(ChildPropertyEntry propEntry) {
-        QName propName = propEntry.getName();
-        properties.put(propName, propEntry);
-        try {
-            if (isWorkspaceState() && isUuidOrMixin(propName)) {
-                if (QName.JCR_UUID.equals(propName) && uniqueID == null) {
-                    PropertyState ps = propEntry.getPropertyState();
-                    setUniqueID(ps.getValue().getString());
-                } else if (QName.JCR_MIXINTYPES.equals(propName) && (mixinTypeNames == null || mixinTypeNames.length == 0)) {
-                    PropertyState ps = propEntry.getPropertyState();
-                    mixinTypeNames = getMixinNames(ps);
-                }
-            }
-        } catch (ItemStateException e) {
-            log.error("Internal Error", e);
-        } catch (RepositoryException e) {
-            log.error("Internal Error", e);
-        }
-    }
-
-    /**
-     *
-     * @param propName
-     */
-    private ChildPropertyEntry removePropertyEntry(QName propName) {
-        ChildPropertyEntry cpe = (ChildPropertyEntry) properties.remove(propName);
-        if (cpe != null) {
-            if (isWorkspaceState()) {
-                if (QName.JCR_UUID.equals(propName)) {
-                    setUniqueID(null);
-                } else if (QName.JCR_MIXINTYPES.equals(propName)) {
-                    mixinTypeNames = QName.EMPTY_ARRAY;
-                }
-            }
-        }
-        return cpe;
-    }
-
-    /**
-     * TODO: find a better way to provide the index of a child node entry
-     * Returns the index of the given <code>ChildNodeEntry</code> and with
-     * <code>name</code>.
-     *
-     * @param cne  the <code>ChildNodeEntry</code> instance.
-     * @return the index of the child node entry or <code>Path.INDEX_UNDEFINED</code>
-     * if it is not found in this <code>NodeState</code>.
-     */
-    public int getChildNodeIndex(ChildNodeEntry cne) {
-        List sns = childNodeEntries().get(cne.getName());
-        // index is one based
-        int index = 1;
-        for (Iterator it = sns.iterator(); it.hasNext(); ) {
-            ChildNodeEntry entry = (ChildNodeEntry) it.next();
-            if (entry == cne) {
-                return index;
-            }
-            // skip entries that belong to removed or invalidated states.
-            // NOTE, that in this case the nodestate must be available from the cne.
-            if (isValidChildNodeEntry(entry)) {
-                index++;
-            }
-        }
-        // not found
-        return Path.INDEX_UNDEFINED;
-    }
-    //--------------------------------------------------< Workspace - State >---
-    /**
-     *
-     * @param event
-     * @see ItemState#refresh(Event)
-     */
-    synchronized void refresh(Event event) {
-        checkIsWorkspaceState();
-
-        NodeId id = getNodeId();
-        QName name = event.getQPath().getNameElement().getName();
-        switch (event.getType()) {
-            case Event.NODE_ADDED:
-                int index = event.getQPath().getNameElement().getNormalizedIndex();
-                NodeId evId = (NodeId) event.getItemId();
-                String uniqueID = (evId.getPath() != null) ? null : evId.getUniqueID();
-
-                // add new childNodeEntry if it has not been added by
-                // some earlier 'add' event
-                // TODO: TOBEFIXED for SNSs
-                ChildNodeEntry cne = (uniqueID != null) ? childNodeEntries().get(name, uniqueID) : childNodeEntries().get(name, index);
-                if (cne == null) {
-                    cne = childNodeEntries().add(name, uniqueID, index);
-                }
-                // and let the transiently modified session state now, that
-                // its workspace state has been touched.
-                setStatus(Status.MODIFIED);
-                break;
-
-            case Event.PROPERTY_ADDED:
-                // create a new property reference if it has not been
-                // added by some earlier 'add' event
-                if (!hasPropertyName(name)) {
-                    ChildPropertyEntry re = PropertyReference.create(this, name, isf, idFactory);
-                    addPropertyEntry(re);
-                }
-                // and let the transiently modified session state now, that
-                // its workspace state has been touched.
-                setStatus(Status.MODIFIED);
-                break;
-
-            case Event.NODE_REMOVED:
-                if (id.equals(event.getParentId())) {
-                    index = event.getQPath().getNameElement().getNormalizedIndex();
-                    childNodeEntries().remove(name, index);
-                    setStatus(Status.MODIFIED);
-                } else if (id.equals(event.getItemId())) {
-                    setStatus(Status.REMOVED);
-                } else {
-                    // ILLEGAL
-                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
-                }
-                break;
-
-            case Event.PROPERTY_REMOVED:
-                removePropertyEntry(name);
-                setStatus(Status.MODIFIED);
-                break;
-
-            case Event.PROPERTY_CHANGED:
-                if (QName.JCR_UUID.equals(name) || QName.JCR_MIXINTYPES.equals(name)) {
-                    try {
-                        PropertyState ps = getPropertyState(name);
-                        adjustNodeState(this, new PropertyState[] {ps});
-                    } catch (ItemStateException e) {
-                        // should never occur.
-                        log.error("Internal error while updating node state.", e);
-                    }
-                }
-                break;
-            default:
-                // ILLEGAL
-                throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+    public PropertyState getPropertyState(QName propertyName) throws NoSuchItemStateException, ItemStateException {
+        PropertyEntry child = getNodeEntry().getPropertyEntry(propertyName);
+        if (child != null) {
+            return child.getPropertyState();
+        } else {
+            // TODO; correct?
+            throw new NoSuchItemStateException("Child Property with name " + propertyName + " does not exist.");
         }
     }
 
-    //----------------------------------------------------< Session - State >---
     /**
      * {@inheritDoc}
      * @see ItemState#persisted(ChangeLog, CacheBehaviour)
@@ -887,22 +347,19 @@
 
         // process deleted states from the changelog
         for (Iterator it = changeLog.deletedStates(); it.hasNext();) {
-            ItemState state = (ItemState) it.next();
-            state.setStatus(Status.REMOVED);
-            state.overlayedState.setStatus(Status.REMOVED);
+            ItemState delState = (ItemState) it.next();
+
+            // delState.overlayedState.getHierarchyEntry().remove();
+            delState.getHierarchyEntry().remove();
 
             // adjust parent states unless the parent is removed as well
-            NodeState parent = state.getParent();
-            if (!changeLog.containsDeletedState(parent)) {
-                NodeState overlayedParent = (NodeState) parent.overlayedState;
-                if (state.isNode()) {
-                    overlayedParent.childNodeEntries().remove((NodeState)state.overlayedState);
-                    parent.childNodeEntries().remove((NodeState)state);
-                } else {
-                    overlayedParent.removePropertyEntry(state.overlayedState.getQName());
-                    parent.removePropertyEntry(state.getQName());
+            try {
+                NodeState parent = delState.getParent();
+                if (!changeLog.containsDeletedState(parent)) {
+                    modifiedParent(parent, delState, modParents);
                 }
-                modifiedParent(parent, state, modParents);
+            } catch (ItemStateException e) {
+                // ignore. if parent state does not exist it doesn't need to be adjusted
             }
         }
 
@@ -911,84 +368,58 @@
         // entry before its NEW parent.
         for (Iterator it = changeLog.addedStates(); it.hasNext();) {
             ItemState addedState = (ItemState) it.next();
-            NodeState parent = addedState.getParent();
-            // TODO: improve. only retrieve overlayed state, if necessary
             try {
-                // adjust parent child-entries
-                NodeState overlayedParent = (NodeState) parent.overlayedState;
-                QName addedName = addedState.getQName();
-                if (addedState.isNode()) {
-                    int index = parent.childNodeEntries().get((NodeState)addedState).getIndex();
-                    ChildNodeEntry cne;
-                    // check for existing, valid child-node-entry
-                    if (overlayedParent.hasChildNodeEntry(addedName, index)) {
-                        cne = overlayedParent.getChildNodeEntry(addedName, index);
-                    } else {
-                        cne = overlayedParent.childNodeEntries().add(addedState.getQName(), null, index);
-                    }
-                    NodeState overlayed = cne.getNodeState();
-                    if (overlayed.getUniqueID() != null) {
-                        overlayedParent.childNodeEntries().replaceEntry(overlayed);
-                    }
-                    addedState.connect(overlayed);
-                } else {
-                    ChildPropertyEntry pe;
-                    if (overlayedParent.hasPropertyName(addedName)) {
-                        pe = (ChildPropertyEntry) overlayedParent.properties.get(addedName);
-                    } else {
-                        pe = PropertyReference.create(overlayedParent, addedName, overlayedParent.isf,  overlayedParent.idFactory);
-                        overlayedParent.addPropertyEntry(pe);
-                    }
-                    addedState.connect(pe.getPropertyState());
-                }
+                NodeState parent = addedState.getParent();
+                // connect the new state to its overlayed state (including update
+                // via merging in order to be aware of autocreated values,
+                // changed definition etc.
+                addedState.reconnect(false);
 
-                // make sure the new state gets updated (e.g. uniqueID created by server)
-                addedState.merge(addedState.overlayedState, true);
-                // and mark the added-state existing
-                addedState.setStatus(Status.EXISTING);
                 // if parent is modified -> remember for final status reset
                 if (parent.getStatus() == Status.EXISTING_MODIFIED) {
                     modifiedParent(parent, addedState, modParents);
                 }
                 it.remove();
             } catch (ItemStateException e) {
-                log.error("Internal error.", e);
+                // should never occur
+                log.error("Internal error", e);
             }
         }
 
         for (Iterator it = changeLog.modifiedStates(); it.hasNext();) {
             ItemState modState = (ItemState) it.next();
             if (modState.isNode()) {
-                NodeState modNodeState = (NodeState) modState;
-                // handle moved nodes
-                if (isMovedState(modNodeState)) {
-                    // move overlayed state as well
-                    NodeState newParent = (NodeState) modState.getParent().overlayedState;
+                if (StateUtility.isMovedState((NodeState) modState)) {
+                    // move overlayed state as well by setting NodeEntry and
+                    // definition according to session-state
                     NodeState overlayed = (NodeState) modState.overlayedState;
-                    try {
-                        overlayed.getParent().moveEntry(newParent, overlayed, modNodeState.getQName(), modNodeState.getDefinition());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        log.error("Internal error while moving childnode entries.", e);
-                    }
+                    overlayed.hierarchyEntry = ((NodeState) modState).hierarchyEntry;
+                    overlayed.definition = ((NodeState) modState).definition;
+
                     // and mark the moved state existing
-                    modNodeState.setStatus(Status.EXISTING);
+                    modState.setStatus(Status.EXISTING);
                     it.remove();
                 } else {
-                    modifiedParent((NodeState)modState, null, modParents);
+                    // remember state as modified only for later processing
+                    if (!modParents.containsKey(modState)) {
+                        modParents.put(modState, new ArrayList(2));
+                    }
                 }
             } else {
-                // push changes down to overlayed state
-                int type = ((PropertyState) modState).getType();
-                QValue[] values = ((PropertyState) modState).getValues();
-                ((PropertyState) modState.overlayedState).init(type, values);
-
+                // Properties: push changes down to overlayed state
+                ((PropertyState) modState.overlayedState).merge(modState, false);
                 modState.setStatus(Status.EXISTING);
-                // if property state defines a modified jcr:mixinTypes
-                // the parent is listed as modified state and needs to be
-                // processed at the end.
-                if (isUuidOrMixin(modState.getQName())) {
-                    modifiedParent(modState.getParent(), modState, modParents);
+
+                // if property state defines a modified jcr:mixinTypes the parent
+                // is listed as modified state and needs to be processed at the end.
+                if (QName.JCR_MIXINTYPES.equals(modState.getQName())) {
+                    try {
+                        modifiedParent(modState.getParent(), modState, modParents);
+                    } catch (ItemStateException e) {
+                        // should never occur. since parent must be available otherwise
+                        // the mixin could not been added/removed.
+                        log.error("Internal error", e);
+                    }
                 }
                 it.remove();
             }
@@ -1000,12 +431,7 @@
         for (Iterator it = modParents.keySet().iterator(); it.hasNext();) {
             NodeState parent = (NodeState) it.next();
             List l = (List) modParents.get(parent);
-            if (cacheBehaviour == CacheBehaviour.OBSERVATION) {
-                adjustNodeState(parent, (PropertyState[]) l.toArray(new PropertyState[l.size()]));
-            } else {
-                // TODO: improve. invalidate necessary states only
-                parent.invalidate(false);
-            }
+            adjustNodeState(parent, (PropertyState[]) l.toArray(new PropertyState[l.size()]), cacheBehaviour);
         }
 
         /* finally check if all entries in the changelog have been processed
@@ -1019,182 +445,9 @@
                   state.getStatus() == Status.REMOVED ||
                   state.getStatus() == Status.INVALIDATED)) {
                 log.info("State " + state + " with Status " + Status.getName(state.getStatus()) + " has not been processed upon ChangeLog.persisted => invalidate");
-                state.invalidate(false);
-            }
-        }
-    }
-
-    /**
-     * Recursively removes all child states and then calls {@link ItemState#remove()}.
-     *
-     * @inheritDoc
-     * @see ItemState#remove()
-     */
-    void remove() throws ItemStateException {
-        checkIsSessionState();
-        if (!isValid()) {
-            throw new ItemStateException("cannot remove an invalid NodeState");
-        }
-        for (Iterator it = getAllChildEntries(true, false); it.hasNext();) {
-            ChildItemEntry ce = (ChildItemEntry) it.next();
-            if (ce.isAvailable()) {
-                ItemState childState = ce.getItemState();
-                if (childState.isValid()) {
-                    childState.remove();
-                } else if (!ce.denotesNode()) {
-                    // remove invalid property state from properties map
-                    it.remove();
-                    // TODO: check if for node-entries no action is required
-                }
-            } else if (!ce.denotesNode()) {
-                // remove unresolved entry from properties map
-                it.remove();
-                // TODO check if for node entries no action required
-            }
-        }
-
-        if (!propertiesInAttic.isEmpty()) {
-            // move all properties from attic back to properties map
-            properties.putAll(propertiesInAttic);
-            propertiesInAttic.clear();
-        }
-
-        // process this state as well.
-        super.remove();
-    }
-
-    /**
-     * Calls {@link ItemState#revert()} and moves all properties from the attic
-     * back into th properties map.
-     *
-     * @inheritDoc
-     * @see ItemState#revert()
-     */
-    void revert() throws ItemStateException {
-        super.revert();
-        if (!propertiesInAttic.isEmpty()) {
-            // move all properties from attic back to properties map
-            properties.putAll(propertiesInAttic);
-            propertiesInAttic.clear();
-        }
-    }
-
-    /**
-     * Adds this state to the changeLog if it is transiently modified, new or stale
-     * and subsequently calls this method on all child states including those
-     * property states that have been moved to the attic.
-     *
-     * @inheritDoc
-     * @see ItemState#collectStates(ChangeLog, boolean)
-     */
-    void collectStates(ChangeLog changeLog, boolean throwOnStale) throws StaleItemStateException {
-        super.collectStates(changeLog, throwOnStale);
-
-        // collect transient child states including properties in attic.
-        for (Iterator it = getAllChildEntries(false, true); it.hasNext();) {
-            ChildItemEntry ce = (ChildItemEntry) it.next();
-            if (ce.isAvailable()) {
-                try {
-                    ce.getItemState().collectStates(changeLog, throwOnStale);
-                } catch (ItemStateException e) {
-                    // should not happen because ref is available
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds a child node state to this node state.
-     *
-     * @param child the node state to add.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>child</code>.
-     */
-    synchronized void addChildNodeState(NodeState child) {
-        checkIsSessionState();
-        if (child.getParent() != this) {
-            throw new IllegalArgumentException("This NodeState is not the parent of child");
-        }
-        childNodeEntries().add(child);
-        markModified();
-    }
-
-    /**
-     * Adds a property state to this node state.
-     *
-     * @param propState the property state to add.
-     * @throws ItemExistsException If <code>this</code> node state already
-     * contains a valid property state with the same name as <code>propState</code>.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     *                                  of <code>propState</code>.
-     */
-    synchronized void addPropertyState(PropertyState propState) throws ItemExistsException {
-        checkIsSessionState();
-        if (propState.getParent() != this) {
-            throw new IllegalArgumentException("This NodeState is not the parent of propState");
-        }
-        QName propertyName = propState.getQName();
-        // check for an existing property
-        PropertyReference ref = (PropertyReference) properties.get(propertyName);
-        if (ref != null) {
-            PropertyState existingState = null;
-            try {
-                existingState = ref.getPropertyState();
-            } catch (ItemStateException e) {
-                // probably does not exist anymore, remove from properties map
-                removePropertyEntry(propertyName);
-            }
-            if (existingState != null) {
-                if (existingState.getStatus() == Status.EXISTING_REMOVED) {
-                    // move to attic
-                    propertiesInAttic.put(propertyName, ref);
-                } else {
-                    throw new ItemExistsException(propertyName.toString());
-                }
+                state.setStatus(Status.EXISTING);
             }
         }
-        addPropertyEntry(PropertyReference.create(propState, isf, idFactory));
-        markModified();
-    }
-
-    /**
-     * Notifies this node state that a child item state has been removed or
-     * otherwise modified.
-     *
-     * @param childState the child item state that has been removed or modified.
-     * @throws IllegalArgumentException if <code>this</code> is not the parent
-     * of the given <code>ItemState</code>.
-     */
-    synchronized void childStatusChanged(ItemState childState, int previousStatus) {
-        checkIsSessionState();
-        if (childState.getParent() != this) {
-            throw new IllegalArgumentException("This NodeState is not the parent of propState");
-        }
-
-        switch (childState.getStatus()) {
-            case Status.EXISTING_REMOVED:
-                markModified();
-                break;
-            case Status.REMOVED:
-                if (childState.isNode()) {
-                    childNodeEntries().remove((NodeState) childState);
-                } else {
-                    removePropertyEntry(childState.getQName());
-                }
-                // TODO: TOBEFIXED. removing a NEW state may even remove the 'modified'
-                // flag from the parent, if this NEW state was the only modification.
-                if (previousStatus != Status.NEW) {
-                    markModified();
-                }
-                break;
-            case Status.EXISTING:
-                if (previousStatus == Status.EXISTING_REMOVED && !childState.isNode()) {
-                    QName propName = childState.getQName();
-                    if (propertiesInAttic.containsKey(propName)) {
-                        properties.put(childState.getQName(), propertiesInAttic.remove(propName));
-                    }
-                }
-        }
     }
 
     /**
@@ -1203,23 +456,23 @@
      *
      * @param insertNode the child node to reorder.
      * @param beforeNode the child node where to insert the node before. If
-     *                   <code>null</code> the child node <code>insertNode</code>
-     *                   is moved to the end of the child node entries.
+     * <code>null</code> the child node <code>insertNode</code> is moved to the
+     * end of the child node entries.
      * @throws NoSuchItemStateException if <code>insertNode</code> or
-     *                                  <code>beforeNode</code> is not a child
-     *                                  node of this <code>NodeState</code>.
+     * <code>beforeNode</code> is not a child node of this <code>NodeState</code>.
      */
     synchronized void reorderChildNodeEntries(NodeState insertNode, NodeState beforeNode)
         throws NoSuchItemStateException {
         checkIsSessionState();
 
-        childNodeEntries().reorder(insertNode, beforeNode);
+        NodeEntry before = (beforeNode == null) ? null : beforeNode.getNodeEntry();
+        insertNode.getNodeEntry().orderBefore(before);
         // mark this state as modified
         markModified();
     }
 
     /**
-     * Moves a <code>ChildNodeEntry</code> to a new parent. If the new parent
+     * Moves a <code>NodeEntry</code> to a new parent. If the new parent
      * is this <code>NodeState</code>, the child state is renamed and moved
      * to the end of the child entries collection.
      *
@@ -1234,7 +487,14 @@
         throws RepositoryException {
         checkIsSessionState();
 
-        moveEntry(newParent, childState, newName, newDefinition);
+        // move child entry
+        NodeEntry newEntry = getNodeEntry().moveNodeEntry(childState, newName, newParent.getNodeEntry());
+
+        // set new NodeEntry on child state, that differs from the HE of the workspaceState
+        // TODO: check again
+        childState.hierarchyEntry = newEntry;
+        childState.definition = newDefinition;
+
         // mark both this and newParent modified
         markModified();
         childState.markModified();
@@ -1243,153 +503,10 @@
 
     /**
      *
-     * @param newParent
      * @param childState
-     * @param newName
-     * @param newDefinition
-     * @throws RepositoryException
-     */
-    private void moveEntry(NodeState newParent, NodeState childState, QName newName, QNodeDefinition newDefinition) throws RepositoryException {
-        ChildNodeEntry oldEntry = childNodeEntries().remove(childState);
-        if (oldEntry != null) {
-            childState.name = newName;
-            // re-parent target node
-            childState.parent = newParent;
-            // set definition according to new definition required by the new parent
-            childState.definition = newDefinition;
-            // add child node entry to new parent
-            newParent.childNodeEntries().add(childState);
-        } else {
-            throw new RepositoryException("Unexpected error: Child state to be moved does not exist.");
-        }
-    }
-
-    /**
-     *
-     * @param createNewList if true, both properties and childNodeEntries are
-     * copied to new list, since recursive calls may call this node state to
-     * inform the removal of a child entry.
-     *
-     * @return
+     * @param modParents
      */
-    private Iterator getAllChildEntries(boolean createNewList, boolean includeAttic) {
-        Iterator[] its;
-        if (createNewList) {
-            List props = new ArrayList(properties.values());
-            List children = new ArrayList(childNodeEntries());
-            if (includeAttic) {
-                List attic = new ArrayList(propertiesInAttic.values());
-                its = new Iterator[] {attic.iterator(), props.iterator(), children.iterator()};
-            } else {
-                its = new Iterator[] {props.iterator(), children.iterator()};
-            }
-        } else {
-            if (includeAttic) {
-                its = new Iterator[] {propertiesInAttic.values().iterator(), properties.values().iterator(), childNodeEntries().iterator()};
-            } else {
-                its = new Iterator[] {properties.values().iterator(), childNodeEntries().iterator()};
-            }
-        }
-        IteratorChain chain = new IteratorChain(its);
-        return chain;
-    }
-    //-------------------------------< internal >-------------------------------
-    /**
-     * Returns <code>true</code> if the collection of child node
-     * <code>entries</code> contains at least one valid <code>ChildNodeEntry</code>.
-     *
-     * @param entries the collection to check.
-     * @return <code>true</code> if one of the entries is valid; otherwise
-     *         <code>false</code>.
-     */
-    private static boolean containsValidChildNodeEntry(Collection entries) {
-        boolean hasValid = false;
-        for (Iterator it = entries.iterator(); it.hasNext() && !hasValid; ) {
-            ChildNodeEntry cne = (ChildNodeEntry) it.next();
-            hasValid = isValidChildNodeEntry(cne);
-        }
-        return hasValid;
-    }
-
-    /**
-     * Returns <code>true</code> if the given childnode entry is not
-     * <code>null</code> and resolves to a NodeState, that is valid or if the
-     * childnode entry has not been resolved up to now (assuming the corresponding
-     * nodestate is still valid).
-     *
-     * @param cne ChildNodeEntry to check.
-     * @return <code>true</code> if the given entry is valid.
-     */
-    private static boolean isValidChildNodeEntry(ChildNodeEntry cne) {
-        // shortcut.
-        if (cne == null) {
-            return false;
-        }
-        boolean isValid = false;
-        if (cne.isAvailable()) {
-            try {
-                isValid = cne.getNodeState().isValid();
-            } catch (ItemStateException e) {
-                // should not occur, if the cne is available.
-            }
-        } else {
-            // then it has never been accessed and must exist
-            // TODO: check if this assumption is correct
-            isValid = true;
-        }
-
-        return isValid;
-    }
-
-    /**
-     * Returns <code>true</code> if the given childproperty entry is not
-     * <code>null</code> and resolves to a PropertyState, that is valid or if the
-     * childproperty entry has not been resolved up to now (assuming the corresponding
-     * PropertyState is still valid).
-     *
-     * @param cpe ChildPropertyEntry to check.
-     * @return <code>true</code> if the given entry is valid.
-     */
-    private static boolean isValidChildPropertyEntry(ChildPropertyEntry cpe) {
-        if (cpe == null) {
-            return false;
-        }
-        boolean isValid = false;
-        if (cpe.isAvailable()) {
-            try {
-                isValid = cpe.getPropertyState().isValid();
-            } catch (ItemStateException e) {
-                // probably deleted in the meantime. should not occur.
-            }
-        } else {
-            // then it must be valid // TODO check if this assumption is correct.
-            isValid = true;
-        }
-        return isValid;
-    }
-
-    /**
-     *
-     * @param ps
-     * @return
-     * @throws RepositoryException
-     */
-    private static QName[] getMixinNames(PropertyState ps) throws RepositoryException {
-        assert QName.JCR_MIXINTYPES.equals(ps.getQName());
-
-        QValue[] values = ps.getValues();
-        QName[] newMixins = new QName[values.length];
-        for (int i = 0; i < values.length; i++) {
-            newMixins[i] = QName.valueOf(values[i].getString());
-        }
-        return newMixins;
-    }
-
-    private static boolean isUuidOrMixin(QName propName) {
-        return QName.JCR_UUID.equals(propName) || QName.JCR_MIXINTYPES.equals(propName);
-    }
-
-    private static void modifiedParent(NodeState parent, ItemState child, Map modParents) {
+    private static void modifiedParent(NodeState parent, ItemState childState, Map modParents) {
         List l;
         if (modParents.containsKey(parent)) {
             l = (List) modParents.get(parent);
@@ -1397,8 +514,8 @@
             l = new ArrayList(2);
             modParents.put(parent, l);
         }
-        if (child != null && !child.isNode() && isUuidOrMixin(child.getQName())) {
-            l.add(child);
+        if (childState != null && !childState.isNode() && StateUtility.isUuidOrMixin(childState.getQName())) {
+            l.add(childState);
         }
     }
 
@@ -1407,48 +524,45 @@
      * @param parent
      * @param props
      */
-    private static void adjustNodeState(NodeState parent, PropertyState[] props) {
-        NodeState overlayed = (parent.isWorkspaceState()) ? parent : (NodeState) parent.overlayedState;
-        NodeState sState = (parent.isWorkspaceState()) ? (NodeState) overlayed.getSessionState() : parent;
-
+    private static void adjustNodeState(NodeState parent, PropertyState[] props,
+                                        CacheBehaviour cacheBehaviour) {
+        NodeState overlayed = (NodeState) parent.overlayedState;
         if (overlayed != null) {
             for (int i = 0; i < props.length; i++) {
-                try {
-                    if (QName.JCR_UUID.equals(props[i].getQName())) {
-                        String uniqueID = (props[i].getStatus() == Status.REMOVED) ? null : props[i].getValue().getString();
-                        sState.setUniqueID(uniqueID);
-                        overlayed.setUniqueID(uniqueID);
-                    } else if (QName.JCR_MIXINTYPES.equals(props[i].getQName())) {
-                        QName[] mixins = (props[i].getStatus() == Status.REMOVED) ? QName.EMPTY_ARRAY : getMixinNames(props[i]);
-
-                        sState.mixinTypeNames = mixins;
-                        overlayed.mixinTypeNames = mixins;
-                    } // else: ignore.
-                } catch (RepositoryException e) {
-                    // should never occur.
-                    log.error("Internal error while updating node state.", e);
-                }
+                PropertyState propState = props[i];
+                if (QName.JCR_UUID.equals(propState.getQName())) {
+                    if (propState.getStatus() == Status.REMOVED) {
+                        parent.getNodeEntry().setUniqueID(null);
+                    } else {
+                        // retrieve uuid from persistent layer
+                        try {
+                            propState.reconnect(false);
+                        } catch (ItemStateException e) {
+                            // TODO: handle properly
+                            log.error("Internal error", e);
+                        }
+                    }
+                } else if (QName.JCR_MIXINTYPES.equals(propState.getQName())) {
+                    QName[] mixins = StateUtility.getMixinNames(propState);
+                    parent.setMixinTypeNames(mixins);
+                    overlayed.setMixinTypeNames(mixins);
+                } // else: ignore.
             }
 
-            // make sure all other modifications on the overlayed state are
-            // reflected on the session-state.
-            sState.merge(overlayed, false);
-            // make sure, the session-state gets its status reset to Existing.
-            if (sState.getStatus() == Status.EXISTING_MODIFIED) {
-                sState.setStatus(Status.EXISTING);
+            // set parent status to 'existing'
+            parent.setStatus(Status.EXISTING);
+            if (cacheBehaviour != CacheBehaviour.OBSERVATION) {
+                // TODO: really necessary???
+                try {
+                    parent.reconnect(false);
+                } catch (ItemStateException e) {
+                    // TODO: handle properly
+                    log.error("Internal error", e);
+                }
             }
         } else {
             // should never occur.
             log.warn("Error while adjusting nodestate: Overlayed state is missing.");
-        }
-    }
-
-    private static boolean isMovedState(NodeState modState) {
-        if (modState.getParent() == null) {
-            // the root state cannot be moved
-            return false;
-        } else {
-            return modState.overlayedState.getParent() != modState.getParent().overlayedState;
         }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java?view=diff&rev=506927&r1=506926&r2=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java Tue Feb 13 01:31:36 2007
@@ -21,15 +21,15 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
 
-import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.jcr2spi.nodetype.ValueConstraint;
+import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,14 +43,14 @@
     private static Logger log = LoggerFactory.getLogger(PropertyState.class);
 
     /**
-     * The name of this property state.
+     * The PropertyEntry associated with the state
      */
-    private final QName name;
+    private final PropertyEntry hierarchyEntry;
 
     /**
      * Property definition
      */
-    private final QPropertyDefinition def;
+    private QPropertyDefinition definition;
 
     /**
      * The internal value(s)
@@ -63,19 +63,24 @@
     private int type;
 
     /**
+     * True if this Property is multiValued
+     */
+    private final boolean multiValued;
+
+    /**
      * Constructs a new property state that is initially connected to an
      * overlayed state.
      *
      * @param overlayedState
-     * @param parent
      * @param initialStatus
-     * @param idFactory
      */
-    protected PropertyState(PropertyState overlayedState, NodeState parent,
-                            int initialStatus, ItemStateFactory isf, IdFactory idFactory) {
-        super(overlayedState, parent, initialStatus, isf, idFactory);
-        this.name = overlayedState.name;
-        this.def = overlayedState.def;
+    protected PropertyState(PropertyState overlayedState, int initialStatus,
+                            ItemStateFactory isf) {
+        super(overlayedState, initialStatus, isf);
+
+        this.hierarchyEntry = overlayedState.hierarchyEntry;
+        this.definition = overlayedState.definition;
+        this.multiValued = overlayedState.multiValued;
 
         init(overlayedState.getType(), overlayedState.getValues());
     }
@@ -83,18 +88,18 @@
     /**
      * Create a new <code>PropertyState</code>
      *
-     * @param name
-     * @param parent
-     * @param definition
+     * @param entry
      * @param initialStatus
-     * @param idFactory
+     * @param isWorkspaceState
      */
-    protected PropertyState(QName name, NodeState parent, QPropertyDefinition definition,
-                            int initialStatus, ItemStateFactory isf, IdFactory idFactory,
-                            boolean isWorkspaceState) {
-        super(parent, initialStatus, isf, idFactory, isWorkspaceState);
-        this.name = name;
-        this.def = definition;
+    protected PropertyState(PropertyEntry entry, boolean multiValued, QPropertyDefinition definition,
+                            int initialStatus, boolean isWorkspaceState,
+                            ItemStateFactory isf, NodeTypeRegistry ntReg) {
+        super(initialStatus, isWorkspaceState, isf, ntReg);
+
+        this.hierarchyEntry = entry;
+        this.definition = definition;
+        this.multiValued = multiValued;
         init(PropertyType.UNDEFINED, QValue.EMPTY_ARRAY);
     }
 
@@ -120,8 +125,16 @@
         this.values = (values == null) ? QValue.EMPTY_ARRAY : values;
     }
 
+
     //----------------------------------------------------------< ItemState >---
     /**
+     * @see ItemState#getHierarchyEntry()
+     */
+    public HierarchyEntry getHierarchyEntry() {
+        return hierarchyEntry;
+    }
+
+    /**
      * Always returns false.
      *
      * @return always false
@@ -132,16 +145,6 @@
     }
 
     /**
-     * Returns the name of this property.
-     *
-     * @return the name of this property.
-     * @see ItemState#getQName()
-     */
-    public QName getQName() {
-        return name;
-    }
-
-    /**
      * {@inheritDoc}
      * @see ItemState#getId()
      */
@@ -150,39 +153,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     * @see ItemState#reload(boolean)
-     */
-    public void reload(boolean keepChanges) {
-        if (isWorkspaceState()) {
-            // refresh from persistent storage ('keepChanges' not relevant).
-            try {
-                PropertyState tmp = isf.createPropertyState(getPropertyId(), getParent());
-                if (merge(tmp, false) || getStatus() == Status.INVALIDATED) {
-                    setStatus(Status.MODIFIED);
-                }
-            } catch (NoSuchItemStateException e) {
-                // TODO: improve. make sure the property-entry is removed from the parent state
-                // inform overlaying state and listeners
-                setStatus(Status.REMOVED);
-            } catch (ItemStateException e) {
-                // TODO: rather throw? remove from parent?
-                log.warn("Exception while refreshing property state: " + e);
-                log.debug("Stacktrace: ", e);
-            }
-        } else {
-            /* session-state: if keepChanges is true only existing or invalidated
-               states must be updated. otherwise the state gets updated and might
-               be marked 'Stale' if transient changes are present and the
-               workspace-state is modified. */
-            if (!keepChanges || getStatus() == Status.EXISTING || getStatus() == Status.INVALIDATED) {
-                // calling refresh on the workspace state will in turn reset this state
-                overlayedState.reload(keepChanges);
-            }
-        }
-    }
-
-    /**
      * If <code>keepChanges</code> is true, this method does nothing and returns
      * false. Otherwise type and values of the other property state are compared
      * to this state. If they differ, they will be copied to this state and
@@ -190,7 +160,7 @@
      *
      * @see ItemState#merge(ItemState, boolean)
      */
-    boolean merge(ItemState another, boolean keepChanges) {
+    public boolean merge(ItemState another, boolean keepChanges) {
         if (another == null || another == this) {
             return false;
         }
@@ -209,24 +179,6 @@
         return true;
     }
 
-    /**
-     * {@inheritDoc}
-     * @see ItemState#invalidate(boolean)
-     */
-    public void invalidate(boolean recursive) {
-        if (isWorkspaceState()) {
-            // workspace state
-            setStatus(Status.INVALIDATED);
-        } else {
-            // TODO: only invalidate if existing?
-            if (getStatus() == Status.EXISTING) {
-                // set workspace state invalidated, this will in turn invalidate
-                // this (session) state as well
-                overlayedState.invalidate(recursive);
-            }
-        }
-    }
-
     //------------------------------------------------------< PropertyState >---
     /**
      * Returns the identifier of this property.
@@ -234,7 +186,7 @@
      * @return the id of this property.
      */
     public PropertyId getPropertyId() {
-        return idFactory.createPropertyId(getParent().getNodeId(), getQName());
+        return getPropertyEntry().getId();
     }
 
     /**
@@ -256,7 +208,7 @@
      * @return true if this property is multi-valued, otherwise false.
      */
     public boolean isMultiValued() {
-        return def.isMultiple();
+        return multiValued;
     }
 
     /**
@@ -266,8 +218,11 @@
      *
      * @return definition of this state
      */
-    public QPropertyDefinition getDefinition() {
-        return def;
+    public QPropertyDefinition getDefinition() throws RepositoryException {
+        if (definition == null) {
+            definition = getEffectiveNodeType().getApplicablePropertyDefinition(getQName(), getType(), isMultiValued());
+        }
+        return definition;
     }
 
     /**
@@ -296,32 +251,6 @@
         }
     }
 
-    //----------------------------------------------------< Workspace State >---
-    /**
-     * @see ItemState#refresh(Event)
-     */
-    synchronized void refresh(Event event) {
-        checkIsWorkspaceState();
-
-        switch (event.getType()) {
-            case Event.PROPERTY_REMOVED:
-                setStatus(Status.REMOVED);
-                break;
-
-            case Event.PROPERTY_CHANGED:
-                // retrieve modified property value and type from server.
-                reload(false);
-                break;
-
-            case Event.PROPERTY_ADDED:
-            case Event.NODE_ADDED:
-            case Event.NODE_REMOVED:
-            default:
-                throw new IllegalArgumentException("Event type " + event.getType() + " cannot be applied to a PropertyState");
-        }
-    }
-
-    //----------------------------------------------------< Session - State >---
     /**
      * {@inheritDoc}
      * @see ItemState#persisted(ChangeLog, CacheBehaviour)
@@ -354,12 +283,21 @@
         checkIsSessionState();
         // make sure the arguements are consistent and do not violate the
         // given property definition.
-        validate(values, type, def);
+        validate(values, type, getDefinition());
         init(type, values);
 
         markModified();
     }
 
+    //------------------------------------------------------------< private >---
+    /**
+     *
+     * @return
+     */
+    private PropertyEntry getPropertyEntry() {
+        return (PropertyEntry) getHierarchyEntry();
+    }
+
     /**
      * Checks whether the given property parameters are consistent and satisfy
      * the constraints specified by the given definition. The following
@@ -385,13 +323,13 @@
         if (propertyType == PropertyType.UNDEFINED) {
             throw new RepositoryException("'Undefined' is not a valid property type for existing values.");
         }
-        if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != propertyType) {
-            throw new ConstraintViolationException("RequiredType constraint is not satisfied");
-        }
         for (int i = 0; i < values.length; i++) {
             if (values[i] != null && propertyType != values[i].getType()) {
                 throw new ConstraintViolationException("Inconsistent value types: Required type = " + PropertyType.nameFromValue(propertyType) + "; Found value with type = " + PropertyType.nameFromValue(values[i].getType()));
             }
+        }
+        if (definition.getRequiredType() != PropertyType.UNDEFINED && definition.getRequiredType() != propertyType) {
+            throw new ConstraintViolationException("RequiredType constraint is not satisfied");
         }
         ValueConstraint.checkValueConstraints(definition, values);
     }