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);
}