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 2006/10/26 13:02:04 UTC
svn commit: r467956 [2/3] - in
/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi:
./ state/ version/
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=467956&r1=467955&r2=467956
==============================================================================
--- 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 Thu Oct 26 04:02:02 2006
@@ -18,6 +18,7 @@
import org.apache.commons.collections.list.AbstractLinkedList;
import org.apache.commons.collections.iterators.UnmodifiableIterator;
+import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.name.Path;
@@ -25,7 +26,6 @@
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.ItemId;
import org.apache.jackrabbit.spi.Event;
-import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
import org.apache.jackrabbit.jcr2spi.state.entry.PropertyReference;
@@ -80,7 +80,7 @@
/**
* the names of this node's mixin types
*/
- private QName[] mixinTypeNames = new QName[0];
+ private QName[] mixinTypeNames = QName.EMPTY_ARRAY;
/**
* insertion-ordered collection of ChildNodeEntry objects
@@ -106,12 +106,6 @@
private NodeReferences references;
/**
- * The <code>ItemStateFactory</code> which is used to create new
- * <code>ItemState</code> instances.
- */
- private final ItemStateFactory isf;
-
- /**
* Constructs a new node state that is not connected.
*
* @param name the name of this NodeState
@@ -129,12 +123,12 @@
QName nodeTypeName, QNodeDefinition definition,
int initialStatus, ItemStateFactory isf,
IdFactory idFactory, boolean isWorkspaceState) {
- super(parent, initialStatus, idFactory, isWorkspaceState);
+ super(parent, initialStatus, isf, idFactory, isWorkspaceState);
this.name = name;
this.uuid = uuid;
this.nodeTypeName = nodeTypeName;
this.definition = definition;
- this.isf = isf;
+ assertAvailability();
}
/**
@@ -150,11 +144,28 @@
protected NodeState(NodeState overlayedState, NodeState parent,
int initialStatus, ItemStateFactory isf,
IdFactory idFactory) {
- super(overlayedState, parent, initialStatus, idFactory);
- this.isf = isf;
- reset();
+ super(overlayedState, parent, initialStatus, isf, idFactory);
+ if (overlayedState != null) {
+ synchronized (overlayedState) {
+ NodeState wspState = (NodeState) overlayedState;
+ name = wspState.name;
+ uuid = wspState.uuid;
+ nodeTypeName = wspState.nodeTypeName;
+ definition = wspState.definition;
+
+ init(wspState.getMixinTypeNames(), wspState.getChildNodeEntries(), wspState.getPropertyNames(), wspState.getNodeReferences());
+ }
+ }
+ assertAvailability();
}
+ /**
+ *
+ * @param mixinTypeNames
+ * @param childEntries
+ * @param propertyNames
+ * @param references
+ */
void init(QName[] mixinTypeNames, Collection childEntries, Collection propertyNames, NodeReferences references) {
if (mixinTypeNames != null) {
this.mixinTypeNames = mixinTypeNames;
@@ -165,19 +176,34 @@
Iterator it = propertyNames.iterator();
while (it.hasNext()) {
QName propName = (QName) it.next();
- properties.put(propName, PropertyReference.create(this, propName, isf, idFactory));
+ addPropertyEntry(PropertyReference.create(this, propName, isf, idFactory));
}
// re-create child node entries
childNodeEntries.removeAll();
it = childEntries.iterator();
while (it.hasNext()) {
ChildNodeEntry cne = (ChildNodeEntry) it.next();
- childNodeEntries.add(cne.getName(), cne.getUUID());
+ childNodeEntries.add(cne.getName(), cne.getUUID(), cne.getIndex());
}
// set the node references
this.references = references;
}
+ private void assertAvailability() {
+ // TODO: improve this.
+ if (uuid != null) {
+ // make sure this state is connected to its childNode-entry
+ ChildNodeEntry cne = parent.childNodeEntries.get(uuid);
+ if (!cne.isAvailable()) {
+ try {
+ cne.getNodeState();
+ } catch (ItemStateException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
//----------------------------------------------------------< ItemState >---
/**
* Determines if this item state represents a node.
@@ -211,6 +237,7 @@
* @return the id of this node state.
*/
public NodeId getNodeId() {
+ NodeState parent = getParent();
if (uuid != null) {
return idFactory.createNodeId(uuid);
} else if (parent != null) {
@@ -238,11 +265,30 @@
* @return the UUID of this node state or <code>null</code> if this
* node cannot be identified with a UUID.
*/
- public final String getUUID() {
+ public String getUUID() {
return uuid;
}
/**
+ * Modify the uuid of this state and make sure, that the parent state
+ * contains a proper childNodeEntry for this state. If the given uuid is
+ * not different from the uuid of this state, the method returns silently
+ * without changing neither the parent nor this state.
+ *
+ * @param uuid
+ */
+ private void setUUID(String uuid) {
+ String oldUUID = this.uuid;
+ boolean mod = (oldUUID == null) ? uuid != null : !oldUUID.equals(uuid);
+ if (mod) {
+ this.uuid = uuid;
+ if (getParent() != null) {
+ getParent().childNodeEntries.replaceEntry(this);
+ }
+ }
+ }
+
+ /**
* Returns the name of this node's node type.
*
* @return the name of this node's node type.
@@ -295,11 +341,7 @@
* @return references
*/
NodeReferences getNodeReferences() {
- if (getStatus() == Status.NEW) {
- return null;
- } else {
- return references;
- }
+ return references;
}
/**
@@ -504,6 +546,46 @@
}
/**
+ *
+ * @param propEntry
+ */
+ private void addPropertyEntry(ChildPropertyEntry propEntry) {
+ QName propName = propEntry.getName();
+ properties.put(propName, propEntry);
+ try {
+ if (isWorkspaceState() && isUuidOrMixin(propName)) {
+ if (QName.JCR_UUID.equals(propName) && uuid == null) {
+ PropertyState ps = propEntry.getPropertyState();
+ setUUID(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 void removePropertyEntry(QName propName) {
+ if (properties.remove(propName) != null) {
+ if (isWorkspaceState()) {
+ if (QName.JCR_UUID.equals(propName)) {
+ setUUID(null);
+ } else if (QName.JCR_MIXINTYPES.equals(propName)) {
+ mixinTypeNames = QName.EMPTY_ARRAY;
+ }
+ }
+ }
+ }
+
+ /**
* 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>.
@@ -538,77 +620,37 @@
/**
*
* @param event
- * @param changeLog
- * @see ItemState#refresh(Event, ChangeLog)
+ * @see ItemState#refresh(Event)
*/
- synchronized void refresh(Event event, ChangeLog changeLog) {
+ synchronized void refresh(Event event) {
checkIsWorkspaceState();
NodeId id = getNodeId();
+ QName name = event.getQPath().getNameElement().getName();
switch (event.getType()) {
case Event.NODE_ADDED:
- case Event.PROPERTY_ADDED:
- if (!id.equals(event.getParentId())) {
- // TODO: TOBEFIXED. this should never occur and indicates severe consistency issue.
- throw new IllegalArgumentException("Event parent (" + event.getParentId() + ") does not match this state with id: " + id);
- }
- ItemId evId = event.getItemId();
- ItemState newState = null;
-
- if (evId.denotesNode()) {
- QName name = event.getQPath().getNameElement().getName();
- int index = event.getQPath().getNameElement().getNormalizedIndex();
- String uuid = (((NodeId)evId).getPath() != null) ? null : ((NodeId)evId).getUUID();
-
- // add new childNodeEntry if it has not been added by
- // some earlier 'add' event
- // TODO: TOBEFIXED for SNSs
- ChildNodeEntry cne = getChildNodeEntry(name, index);
- if (cne == null || ((uuid == null) ? cne.getUUID() != null : !uuid.equals(cne.getUUID()))) {
- cne = childNodeEntries.add(name, uuid);
- }
- try {
- newState = cne.getNodeState();
- } catch (ItemStateException e) {
- log.error("Internal error", e);
- }
- } else {
- QName pName = ((PropertyId) event.getItemId()).getQName();
- // create a new property reference if it has not been
- // added by some earlier 'add' event
- ChildPropertyEntry re;
- if (hasPropertyName(pName)) {
- re = (ChildPropertyEntry) properties.get(pName);
- } else {
- re = PropertyReference.create(this, pName, isf, idFactory);
- properties.put(pName, re);
- }
- try {
- newState = re.getPropertyState();
- } catch (ItemStateException e) {
- log.error("Internal error", e);
- }
- // make sure this state is up to date (uuid/mixins)
- refresh(pName, event.getType());
+ int index = event.getQPath().getNameElement().getNormalizedIndex();
+ NodeId evId = (NodeId) event.getItemId();
+ String uuid = (evId.getPath() != null) ? null : evId.getUUID();
+
+ // add new childNodeEntry if it has not been added by
+ // some earlier 'add' event
+ // TODO: TOBEFIXED for SNSs
+ ChildNodeEntry cne = childNodeEntries.get(name, index);
+ if (cne == null || ((uuid == null) ? cne.getUUID() != null : !uuid.equals(cne.getUUID()))) {
+ cne = childNodeEntries.add(name, uuid, index);
}
+ // and let the transiently modified session state now, that
+ // its workspace state has been touched.
+ setStatus(Status.MODIFIED);
+ break;
- // connect the added state from the transient layer to the
- // new workspaceState and make sure its data are updated.
- if (newState != null && changeLog != null) {
- for (Iterator it = changeLog.addedStates(); it.hasNext();) {
- ItemState added = (ItemState) it.next();
- if (added.hasOverlayedState()) {
- // already connected
- continue;
- }
- // TODO: TOBEFIXED. may fail (produce wrong results) for SNSs, since currently events upon 'save' are not garantied to be 'local' changes only
- // TODO: TOBEFIXED. equals to false if added-state is referenceable.
- if (added.getId().equals(evId)) {
- added.connect(newState);
- added.merge();
- 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.
@@ -617,9 +659,8 @@
case Event.NODE_REMOVED:
if (id.equals(event.getParentId())) {
- QName qName = event.getQPath().getNameElement().getName();
- int index = event.getQPath().getNameElement().getNormalizedIndex();
- childNodeEntries.remove(qName, index);
+ index = event.getQPath().getNameElement().getNormalizedIndex();
+ childNodeEntries.remove(name, index);
setStatus(Status.MODIFIED);
} else if (id.equals(event.getItemId())) {
setStatus(Status.REMOVED);
@@ -630,27 +671,19 @@
break;
case Event.PROPERTY_REMOVED:
- if (id.equals(event.getParentId())) {
- QName pName = ((PropertyId) event.getItemId()).getQName();
- properties.remove(pName);
- // make sure this state is up to date (uuid/mixins)
- refresh(pName, event.getType());
- setStatus(Status.MODIFIED);
- } else {
- // ILLEGAL
- throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
- }
+ removePropertyEntry(name);
+ setStatus(Status.MODIFIED);
break;
case Event.PROPERTY_CHANGED:
- if (id.equals(event.getParentId())) {
- QName pName = ((PropertyId) event.getItemId()).getQName();
- if (refresh(pName, event.getType())) {
- setStatus(Status.MODIFIED);
+ 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);
}
- } else {
- // ILLEGAL
- throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
}
break;
default:
@@ -659,143 +692,191 @@
}
}
+ //----------------------------------------------------< Session - State >---
/**
- * Returns true, if the uuid or the mixin types of this state have been
- * modified.
- *
- * @param propertyName
- * @param eventType
- * @return
+ * {@inheritDoc}
+ * @see ItemState#refresh(Collection,ChangeLog)
*/
- private boolean refresh(QName propertyName, int eventType) {
- if (QName.JCR_UUID.equals(propertyName)) {
- // TODO: to be fixed.
- } else if (QName.JCR_MIXINTYPES.equals(propertyName)) {
- if (eventType == Event.PROPERTY_REMOVED) {
- mixinTypeNames = QName.EMPTY_ARRAY;
- } else { // added or changed
- try {
- PropertyState ps = getPropertyState(propertyName);
- 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());
- }
- mixinTypeNames = newMixins;
- } catch (ItemStateException e) {
- // should never occur.
- log.error("Internal error while updating mixin types.", e);
- } catch (RepositoryException e) {
- // should never occur.
- log.error("Internal error while updating mixin types.", e);
+ void refresh(Collection events, ChangeLog changeLog) throws IllegalStateException {
+
+ // remember parent states that have need to adjust their uuid/mixintypes
+ // or that got a new child entry added or existing entries removed.
+ HashMap modParents = new HashMap();
+
+ // 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);
+
+ // adjust parent states unless the parent is removed as well
+ NodeState parent = state.getParent();
+ if (!changeLog.deletedStates.contains(parent)) {
+ NodeState overlayedParent = (NodeState) parent.overlayedState;
+ if (state.isNode()) {
+ overlayedParent.childNodeEntries.remove((NodeState)state.overlayedState);
+ } else {
+ overlayedParent.removePropertyEntry(state.overlayedState.getQName());
}
+ modifiedParent(parent, state, modParents);
}
- return true;
- }
- return false;
- }
+ // don't remove processed state from changelog, but from event list
+ // state on changelog is used for check if parent is deleted as well.
+ removeEvent(events, state);
+ }
+
+ // process added states from the changelog. since the changlog maintains
+ // LinkedHashSet for its entries, the iterator will not return a added
+ // entry before its NEW parent.
+ for (Iterator it = changeLog.addedStates(); it.hasNext();) {
+ ItemState addedState = (ItemState) it.next();
+ NodeState parent = addedState.getParent();
+ // TODO: 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.getChildNodeEntry((NodeState) addedState).getIndex();
+ ChildNodeEntry cne;
+ 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.getUUID() != 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());
+ }
- //----------------------------------------------------< Session - State >---
- /**
- * {@inheritDoc}
- * @see ItemState#reset()
- */
- synchronized void reset() {
- checkIsSessionState();
+ // make sure the new state gets updated (e.g. uuid created by server)
+ addedState.reset();
+ // 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);
+ }
- if (overlayedState != null) {
- synchronized (overlayedState) {
- NodeState wspState = (NodeState) overlayedState;
- name = wspState.name;
- uuid = wspState.uuid;
- nodeTypeName = wspState.nodeTypeName;
- definition = wspState.definition;
+ it.remove();
+ removeEvent(events, addedState);
+ } catch (ItemStateException e) {
+ log.error("Internal error.", e);
+ }
+ }
- init(wspState.getMixinTypeNames(), wspState.getChildNodeEntries(), wspState.getPropertyNames(), wspState.getNodeReferences());
+ for (Iterator it = changeLog.modifiedStates(); it.hasNext();) {
+ ItemState modState = (ItemState) it.next();
+ if (modState.isNode()) {
+ continue;
+ }
+ // push changes down to overlayed state
+ int type = ((PropertyState) modState).getType();
+ QValue[] values = ((PropertyState) modState).getValues();
+ ((PropertyState) modState.overlayedState).init(type, values);
+
+ 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(this, modState, modParents);
+ }
+ // remove the processed event from the set
+ it.remove();
+ removeEvent(events, modState);
+ }
+
+ /* process all parent states that need their uuid or mixin-types being
+ adjusted because that property has been added or modified */
+ for (Iterator it = modParents.keySet().iterator(); it.hasNext();) {
+ NodeState parent = (NodeState) it.next();
+ List l = (List) modParents.get(parent);
+ adjustNodeState(parent, (PropertyState[]) l.toArray(new PropertyState[l.size()]));
+ }
+
+ /* finally check if all entries in the changelog have been processed
+ and eventually force a reload in order not to have any states with
+ wrong transient status floating around. */
+ Iterator[] its = new Iterator[] {changeLog.addedStates(), changeLog.deletedStates(), changeLog.modifiedStates()};
+ IteratorChain chain = new IteratorChain(its);
+ while (chain.hasNext()) {
+ ItemState state = (ItemState) chain.next();
+ if (!(state.getStatus() == Status.EXISTING || state.getStatus() == Status.REMOVED)) {
+ // error: state has not been processed
+ // TODO: discard state and force reload of all data
}
}
}
/**
* {@inheritDoc}
- * @see ItemState#merge()
+ * @see ItemState#reset()
*/
- synchronized void merge() {
+ synchronized void reset() {
checkIsSessionState();
if (overlayedState != null) {
synchronized (overlayedState) {
NodeState wspState = (NodeState) overlayedState;
name = wspState.name;
- uuid = wspState.uuid;
+ setUUID(wspState.uuid);
nodeTypeName = wspState.nodeTypeName;
definition = wspState.definition;
mixinTypeNames = wspState.mixinTypeNames;
- references = wspState.getNodeReferences();
- // search for removed properties
- Collection wspProps = wspState.getPropertyNames();
+ // remove all entries in the attic
+ propertiesInAttic.clear();
+
+ // merge prop-names
+ Collection wspPropNames = wspState.getPropertyNames();
+ for (Iterator it = wspPropNames.iterator(); it.hasNext();) {
+ QName propName = (QName) it.next();
+ if (!hasPropertyName(propName)) {
+ addPropertyEntry(PropertyReference.create(this, propName, isf, idFactory));
+ }
+ }
for (Iterator it = properties.keySet().iterator(); it.hasNext();) {
- ChildPropertyEntry pe = (ChildPropertyEntry) properties.get((QName) it.next());
- if (pe.isAvailable()) {
- try {
- PropertyState ps = getPropertyState(pe.getName());
- if (ps.getStatus() == Status.REMOVED || ps.getStatus() == Status.STALE_DESTROYED) {
- it.remove();
- }
- } catch (ItemStateException e) {
- log.error("Internal error while merging item node states.", e);
- }
- } else if (!wspProps.contains(pe.getName())) {
- // not available and not present in wsp-layer any more.
+ // remove all prop-entries in the session state that are
+ // not present in the wsp-state.
+ if (!wspPropNames.contains(it.next())) {
it.remove();
}
}
- // add missing property entries
- for (Iterator it = wspProps.iterator(); it.hasNext();) {
- QName propName = (QName) it.next();
- if (!hasPropertyName(propName)) {
- properties.put(propName, PropertyReference.create(this, propName, isf, idFactory));
- } // else property is already listed
- }
- Collection wspEntries = wspState.getChildNodeEntries();
- // remove child entries, that are 'REMOVED' in the wsp layer
- Set toRemove = new HashSet();
+ // merge child node entries
+ for (Iterator it = wspState.getChildNodeEntries().iterator(); it.hasNext();) {
+ ChildNodeEntry cne = (ChildNodeEntry) it.next();
+ int index = cne.getIndex();
+ if (!childNodeEntries.contains(cne.getName(), index, cne.getUUID())) {
+ childNodeEntries.add(cne.getName(), cne.getUUID(), index);
+ }
+ }
+ List toRemove = new ArrayList();
for (Iterator it = getChildNodeEntries().iterator(); it.hasNext();) {
ChildNodeEntry cne = (ChildNodeEntry) it.next();
- if (cne.isAvailable()) {
- try {
- NodeState ns = cne.getNodeState();
- if (ns.getStatus() == Status.REMOVED) {
- toRemove.add(cne);
- }
- } catch (ItemStateException e) {
- // should not occur
- log.error("Internal error while merging item node states.", e);
- }
- } else if (wspState.getChildNodeEntries(cne.getName()).isEmpty()) {
+ if (!wspState.childNodeEntries.contains(cne.getName(), cne.getIndex(), cne.getUUID())) {
toRemove.add(cne);
- } // TODO: clean up same-named siblings
+ }
}
for (Iterator it = toRemove.iterator(); it.hasNext();) {
ChildNodeEntry cne = (ChildNodeEntry) it.next();
childNodeEntries.remove(cne.getName(), cne.getIndex());
}
-
- // add missing child entries
- for (Iterator it = wspEntries.iterator(); it.hasNext();) {
- ChildNodeEntry wspEntry = (ChildNodeEntry) it.next();
- List namedEntries = getChildNodeEntries(wspEntry.getName());
- if (namedEntries.isEmpty()) {
- // simple case: no cne with the given name
- childNodeEntries.add(wspEntry.getName(), wspEntry.getUUID());
- } else {
- List wspCnes = wspState.getChildNodeEntries(wspEntry.getName());
- // TODO: compare sn-siblings an add missing ones
- }
- }
+ // set the node references
+ references = wspState.references;
}
}
}
@@ -839,7 +920,7 @@
setStatus(Status.REMOVED);
}
// now inform parent
- parent.childNodeStateRemoved(this);
+ getParent().childNodeStateRemoved(this);
}
/**
@@ -919,18 +1000,18 @@
// set removed
setStatus(Status.REMOVED);
// remove from parent
- parent.childNodeStateRemoved(this);
+ getParent().childNodeStateRemoved(this);
affectedItemStates.add(this);
break;
case Status.REMOVED:
// shouldn't happen actually, because a 'removed' state is not
// accessible anymore
log.warn("trying to revert an already removed node state");
- parent.childNodeStateRemoved(this);
+ getParent().childNodeStateRemoved(this);
break;
case Status.STALE_DESTROYED:
// overlayed state does not exist anymore
- parent.childNodeStateRemoved(this);
+ getParent().childNodeStateRemoved(this);
affectedItemStates.add(this);
break;
}
@@ -938,9 +1019,9 @@
/**
* @inheritDoc
- * @see ItemState#collectTransientStates(Set)
+ * @see ItemState#collectTransientStates(Collection)
*/
- void collectTransientStates(Set transientStates) {
+ void collectTransientStates(Collection transientStates) {
checkIsSessionState();
switch (getStatus()) {
@@ -978,38 +1059,20 @@
}
/**
- * Sets the names of this node's mixin types.
- *
- * @param mixinTypeNames set of names of mixin types
- */
- synchronized void setMixinTypeNames(QName[] mixinTypeNames) {
- checkIsSessionState();
-
- if (mixinTypeNames != null) {
- this.mixinTypeNames = mixinTypeNames;
- } else {
- this.mixinTypeNames = new QName[0];
- }
- markModified();
- }
-
- /**
* Adds a child node state to this node state.
*
* @param child the node state to add.
- * @param uuid the uuid of the child node state or <code>null</code> if
- * <code>child</code> cannot be identified with a uuid.
* @throws IllegalArgumentException if <code>this</code> is not the parent
* of <code>child</code>.
*/
- synchronized void addChildNodeState(NodeState child, String uuid) {
+ synchronized void addChildNodeState(NodeState child) {
checkIsSessionState();
-
if (child.getParent() != this) {
throw new IllegalArgumentException("This NodeState is not the parent of child");
}
ChildNodeEntry cne = ChildNodeReference.create(child, isf, idFactory);
childNodeEntries.add(cne);
+
markModified();
}
@@ -1069,7 +1132,7 @@
existingState = ref.getPropertyState();
} catch (ItemStateException e) {
// probably does not exist anymore, remove from properties map
- properties.remove(propertyName);
+ removePropertyEntry(propertyName);
}
if (existingState != null) {
if (existingState.getStatus() == Status.EXISTING_REMOVED) {
@@ -1080,7 +1143,7 @@
}
}
}
- properties.put(propertyName, PropertyReference.create(propState, isf, idFactory));
+ addPropertyEntry(PropertyReference.create(propState, isf, idFactory));
markModified();
}
@@ -1099,11 +1162,10 @@
// remove property state from map of properties if it does not exist
// anymore, otherwise leave the property state in the map
if (propState.getStatus() == Status.REMOVED) {
- properties.remove(propState.getQName());
+ removePropertyEntry(propState.getQName());
}
markModified();
}
-
/**
* Reorders the child node <code>insertNode</code> before the child node
* <code>beforeNode</code>.
@@ -1167,8 +1229,7 @@
*/
private synchronized void rename(QName newName) {
checkIsSessionState();
-
- if (parent == null) {
+ if (getParent() == null) {
throw new IllegalStateException("root node cannot be renamed");
}
name = newName;
@@ -1302,6 +1363,80 @@
return false;
}
+ /**
+ *
+ * @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) {
+ List l;
+ if (modParents.containsKey(parent)) {
+ l = (List) modParents.get(parent);
+ } else {
+ l = new ArrayList(2);
+ modParents.put(parent, l);
+ }
+ if (child != null && !child.isNode() && isUuidOrMixin(child.getQName())) {
+ l.add(child);
+ }
+ }
+
+ /**
+ *
+ * @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;
+
+ if (overlayed != null) {
+ for (int i = 0; i < props.length; i++) {
+ try {
+ if (QName.JCR_UUID.equals(props[i].getQName())) {
+ String uuid = (props[i].getStatus() == Status.REMOVED) ? null : props[i].getValue().getString();
+ sState.setUUID(uuid);
+ overlayed.setUUID(uuid);
+ } 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);
+ }
+ }
+
+ // make sure all other modifications on the overlayed state are
+ // reflected on the session-state.
+ sState.reset();
+ // make sure, the session-state gets its status reset to Existing.
+ if (sState.getStatus() == Status.EXISTING_MODIFIED) {
+ sState.setStatus(Status.EXISTING);
+ }
+ } else {
+ // should never occur.
+ log.warn("Error while adjusting nodestate: Overlayed state is missing.");
+ }
+ }
//------------------------------------------------------< inner classes >---
/**
* <code>ChildNodeEntries</code> represents an insertion-ordered
@@ -1326,6 +1461,61 @@
private final Map nameMap = new HashMap();
/**
+ *
+ * @param name
+ * @param index
+ * @param uuid
+ * @return
+ */
+ private boolean contains(QName name, int index, String uuid) {
+ if (!nameMap.containsKey(name)) {
+ // no matching child node entry
+ return false;
+ }
+ Object o = nameMap.get(name);
+ if (o instanceof List) {
+ // SNS
+ for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
+ LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ ChildNodeEntry cne = n.getChildNodeEntry();
+ if (uuid == null) {
+ if (cne.getIndex() == index) {
+ return true;
+ }
+ } else if (uuid.equals(cne.getUUID())) {
+ return true;
+ }
+ }
+ // no matching entry found
+ return false;
+ } else {
+ // single child node with this name
+ ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
+ if (uuid == null) {
+ return cne.getUUID() == null;
+ } else {
+ return uuid.equals(cne.getUUID());
+ }
+ }
+ }
+
+ /**
+ *
+ * @param uuid
+ * @return
+ */
+ private ChildNodeEntry get(String uuid) {
+ for (Iterator it = entries.iterator(); it.hasNext();) {
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
+ ChildNodeEntry cne = ln.getChildNodeEntry();
+ if (cne.getUUID() == uuid) {
+ return cne;
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns the <code>ChildNodeEntry</code> for the given
* <code>nodeState</code>.
*
@@ -1346,7 +1536,7 @@
ChildNodeEntry cne = n.getChildNodeEntry();
// only check available child node entries
try {
- if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ if (cne.getNodeState() == nodeState) {
return cne;
}
} catch (ItemStateException e) {
@@ -1357,7 +1547,7 @@
// single child node with this name
ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
try {
- if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ if (cne.getNodeState() == nodeState) {
return cne;
}
} catch (ItemStateException e) {
@@ -1415,8 +1605,7 @@
});
} else {
// map entry is a single child node entry
- return Collections.singletonList(
- ((LinkedEntries.LinkNode) obj).getChildNodeEntry());
+ return Collections.singletonList(((LinkedEntries.LinkNode) obj).getChildNodeEntry());
}
}
@@ -1483,31 +1672,35 @@
* @return the created ChildNodeEntry.
*/
ChildNodeEntry add(QName nodeName, String uuid) {
- List siblings = null;
- Object obj = nameMap.get(nodeName);
- if (obj != null) {
- if (obj instanceof List) {
- // map entry is a list of siblings
- siblings = (List) obj;
- } else {
- // map entry is a single child node entry,
- // convert to siblings list
- siblings = new ArrayList();
- siblings.add(obj);
- nameMap.put(nodeName, siblings);
- }
- }
-
- ChildNodeEntry entry = ChildNodeReference.create(NodeState.this, nodeName, uuid, isf, idFactory);
- LinkedEntries.LinkNode ln = entries.add(entry);
+ ChildNodeEntry cne = ChildNodeReference.create(NodeState.this, nodeName, uuid, isf, idFactory);
+ add(cne);
+ return cne;
+ }
- if (siblings != null) {
- siblings.add(ln);
- } else {
- nameMap.put(nodeName, ln);
- }
+ /**
+ * Insert a new childnode entry at the position indicated by index.
+ * @param nodeName
+ * @param uuid
+ * @param index
+ * @return
+ */
+ ChildNodeEntry add(QName nodeName, String uuid, int index) {
+ ChildNodeEntry cne = add(nodeName, uuid);
+ // TODO: in case of SNS, move new cne to the right position.
+ return cne;
+ }
- return entry;
+ /**
+ * Adds a <code>childNode</code> to the end of the list.
+ *
+ * @param childState the <code>NodeState</code> to add.
+ * @return the <code>ChildNodeEntry</code> which was created for
+ * <code>childNode</code>.
+ */
+ ChildNodeEntry add(NodeState childState) {
+ ChildNodeEntry cne = ChildNodeReference.create(childState, isf, idFactory);
+ add(cne);
+ return cne;
}
/**
@@ -1542,19 +1735,6 @@
}
/**
- * Adds a <code>childNode</code> to the end of the list.
- *
- * @param childNode the <code>NodeState</code> to add.
- * @return the <code>ChildNodeEntry</code> which was created for
- * <code>childNode</code>.
- */
- ChildNodeEntry add(NodeState childNode) {
- ChildNodeEntry cne = ChildNodeReference.create(childNode, isf, idFactory);
- add(cne);
- return cne;
- }
-
- /**
* Appends a list of <code>ChildNodeEntry</code>s to this list.
*
* @param entriesList the list of <code>ChildNodeEntry</code>s to add.
@@ -1667,11 +1847,10 @@
*
* @param insertNode the node state to move.
* @param beforeNode the node state where <code>insertNode</code> is
- * reordered to.
+ * reordered to.
* @throws NoSuchItemStateException if <code>insertNode</code> or
- * <code>beforeNode</code> does not
- * have a <code>ChildNodeEntry</code>
- * in this <code>ChildNodeEntries</code>.
+ * <code>beforeNode</code> does not have a <code>ChildNodeEntry</code>
+ * in this <code>ChildNodeEntries</code>.
*/
public void reorder(NodeState insertNode, NodeState beforeNode)
throws NoSuchItemStateException {
@@ -1731,6 +1910,24 @@
}
/**
+ * If the given child state got a (new) uuid assigned or its removed,
+ * its childEntry must be adjusted.
+ *
+ * @param childState
+ */
+ private void replaceEntry(NodeState childState) {
+ // NOTE: test if child-state needs to get a new entry not checked here.
+ try {
+ Object replaceObj = nameMap.get(childState.getQName());
+ LinkedEntries.LinkNode ln = getLinkNode(replaceObj, childState);
+ ChildNodeEntry newCne = ChildNodeReference.create(childState, isf, idFactory);
+ entries.replaceNode(ln, newCne);
+ } catch (NoSuchItemStateException e) {
+ log.error("Internal error.", e);
+ }
+ }
+
+ /**
* Returns the matching <code>LinkNode</code> from a list or a single
* <code>LinkNode</code>.
*
@@ -1893,6 +2090,17 @@
} else {
addNode(insert, before);
}
+ }
+
+ /**
+ * Replace the value of the given LinkNode with a new childNodeEntry
+ * value.
+ *
+ * @param node
+ * @param value
+ */
+ void replaceNode(LinkNode node, ChildNodeEntry value) {
+ updateNode(node, value);
}
/**
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=467956&r1=467955&r2=467956
==============================================================================
--- 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 Thu Oct 26 04:02:02 2006
@@ -33,6 +33,8 @@
import org.slf4j.LoggerFactory;
import java.util.Set;
+import java.util.Collection;
+import java.util.Iterator;
/**
* <code>PropertyState</code> represents the state of a <code>Property</code>.
@@ -44,7 +46,12 @@
/**
* The name of this property state.
*/
- private QName name;
+ private final QName name;
+
+ /**
+ * Property definition
+ */
+ private final QPropertyDefinition def;
/**
* The internal value(s)
@@ -57,11 +64,6 @@
private int type;
/**
- * Property definition
- */
- private QPropertyDefinition def;
-
- /**
* Constructs a new property state that is initially connected to an
* overlayed state.
*
@@ -71,8 +73,11 @@
* @param idFactory
*/
protected PropertyState(PropertyState overlayedState, NodeState parent,
- int initialStatus, IdFactory idFactory) {
- super(overlayedState, parent, initialStatus, idFactory);
+ int initialStatus, ItemStateFactory isf, IdFactory idFactory) {
+ super(overlayedState, parent, initialStatus, isf, idFactory);
+ this.name = overlayedState.name;
+ this.def = overlayedState.def;
+
reset();
}
@@ -86,13 +91,12 @@
* @param idFactory
*/
protected PropertyState(QName name, NodeState parent, QPropertyDefinition definition,
- int initialStatus, IdFactory idFactory, boolean isWorkspaceState) {
- super(parent, initialStatus, idFactory, isWorkspaceState);
+ int initialStatus, ItemStateFactory isf, IdFactory idFactory,
+ boolean isWorkspaceState) {
+ super(parent, initialStatus, isf, idFactory, isWorkspaceState);
this.name = name;
this.def = definition;
-
- type = PropertyType.UNDEFINED;
- values = QValue.EMPTY_ARRAY;
+ init(PropertyType.UNDEFINED, QValue.EMPTY_ARRAY);
}
/**
@@ -101,6 +105,18 @@
* @param values
*/
void init(int type, QValue[] values) {
+ // free old values as necessary
+ QValue[] oldValues = this.values;
+ if (oldValues != null) {
+ for (int i = 0; i < oldValues.length; i++) {
+ QValue old = oldValues[i];
+ if (old != null) {
+ // make sure temporarily allocated data is discarded
+ // before overwriting it (see QValue#discard())
+ old.discard();
+ }
+ }
+ }
this.type = type;
this.values = (values == null) ? QValue.EMPTY_ARRAY : values;
}
@@ -141,7 +157,7 @@
* @return the id of this property.
*/
public PropertyId getPropertyId() {
- return idFactory.createPropertyId(parent.getNodeId(), getQName());
+ return idFactory.createPropertyId(getParent().getNodeId(), getQName());
}
/**
@@ -205,27 +221,30 @@
//----------------------------------------------------< Workspace State >---
/**
- * @see ItemState#refresh(Event, ChangeLog)
+ * @see ItemState#refresh(Event)
*/
- synchronized void refresh(Event event, ChangeLog changeLog) {
+ synchronized void refresh(Event event) {
checkIsWorkspaceState();
switch (event.getType()) {
case Event.PROPERTY_REMOVED:
- if (event.getItemId().equals(getId())) {
- setStatus(Status.REMOVED);
- } else {
- // ILLEGAL
- throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
- }
+ setStatus(Status.REMOVED);
break;
case Event.PROPERTY_CHANGED:
- if (event.getItemId().equals(getId())) {
+ // TODO: improve.
+ /* retrieve property value and type from server even if
+ changes were issued from this session (changelog).
+ this is currently the only way to update the workspace
+ state, which is not connected to its overlaying session-state.
+ */
+ try {
+ PropertyState tmp = isf.createPropertyState(getPropertyId(), parent);
+ init(tmp.getType(), tmp.getValues());
setStatus(Status.MODIFIED);
- } else {
- // ILLEGAL
- throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
+ } catch (ItemStateException e) {
+ // TODO: rather throw?
+ log.error("Internal Error", e);
}
break;
@@ -240,6 +259,29 @@
//----------------------------------------------------< Session - State >---
/**
* {@inheritDoc}
+ * @see ItemState#refresh(Collection,ChangeLog)
+ */
+ void refresh(Collection events, ChangeLog changeLog) throws IllegalStateException {
+ for (Iterator it = changeLog.modifiedStates(); it.hasNext();) {
+ ItemState modState = (ItemState) it.next();
+ if (modState == this) {
+ /*
+ NOTE: overlayedState must be existing, otherwise save was not
+ possible on prop. Similarly a property can only be the changelog
+ target, if it was modified. removal, add must be persisted on parent.
+ */
+ // push changes to overlayed state and reset status
+ ((PropertyState) overlayedState).init(getType(), getValues());
+ setStatus(Status.EXISTING);
+ // parent must not be informed, since all properties that
+ // affect the parent state (uuid, mixins) are protected.
+ removeEvent(events, modState);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
* @see ItemState#reset()
*/
synchronized void reset() {
@@ -247,31 +289,23 @@
if (overlayedState != null) {
synchronized (overlayedState) {
PropertyState wspState = (PropertyState) overlayedState;
- name = wspState.name;
- type = wspState.type;
- def = wspState.def;
- values = wspState.values;
+ init(wspState.type, wspState.values);
}
}
}
- synchronized void merge() {
- reset();
- }
-
/**
* @inheritDoc
* @see ItemState#remove()
*/
void remove() {
checkIsSessionState();
-
if (getStatus() == Status.NEW) {
setStatus(Status.REMOVED);
} else {
setStatus(Status.EXISTING_REMOVED);
}
- parent.propertyStateRemoved(this);
+ getParent().propertyStateRemoved(this);
}
/**
@@ -297,18 +331,18 @@
// set removed
setStatus(Status.REMOVED);
// and remove from parent
- parent.propertyStateRemoved(this);
+ getParent().propertyStateRemoved(this);
affectedItemStates.add(this);
break;
case Status.REMOVED:
// shouldn't happen actually, because a 'removed' state is not
// accessible anymore
log.warn("trying to revert an already removed property state");
- parent.propertyStateRemoved(this);
+ getParent().propertyStateRemoved(this);
break;
case Status.STALE_DESTROYED:
// overlayed does not exist anymore
- parent.propertyStateRemoved(this);
+ getParent().propertyStateRemoved(this);
affectedItemStates.add(this);
break;
}
@@ -316,9 +350,9 @@
/**
* @inheritDoc
- * @see ItemState#collectTransientStates(Set)
+ * @see ItemState#collectTransientStates(Collection)
*/
- void collectTransientStates(Set transientStates) {
+ void collectTransientStates(Collection transientStates) {
checkIsSessionState();
switch (getStatus()) {
@@ -346,12 +380,11 @@
*/
void setValues(QValue[] values, int type) throws RepositoryException {
checkIsSessionState();
-
// make sure the arguements are consistent and do not violate the
// given property definition.
validate(values, type, this.def);
- this.values = values;
- this.type = type;
+ init(type, values);
+
markModified();
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?view=diff&rev=467956&r1=467955&r2=467956
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Thu Oct 26 04:02:02 2006
@@ -18,8 +18,6 @@
import org.apache.jackrabbit.jcr2spi.HierarchyManager;
import org.apache.jackrabbit.jcr2spi.HierarchyManagerImpl;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
import org.apache.jackrabbit.jcr2spi.util.ReferenceChangeTracker;
import org.apache.jackrabbit.jcr2spi.util.LogUtil;
import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
@@ -56,6 +54,7 @@
import org.apache.jackrabbit.spi.ItemId;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.value.QValue;
+import org.apache.jackrabbit.uuid.UUID;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemNotFoundException;
@@ -74,14 +73,13 @@
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.lock.LockException;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import java.util.HashSet;
-import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
import java.io.InputStream;
import java.io.IOException;
@@ -126,7 +124,6 @@
// create hierarchy manager
hierMgr = new HierarchyManagerImpl(this, nsResolver);
-
}
/**
@@ -193,8 +190,33 @@
* @param nodeState
*/
public Collection getReferingStates(NodeState nodeState) throws ItemStateException {
- // TODO: not correct. ItemManager later on expectes overlaying state
- return workspaceItemStateMgr.getReferingStates(nodeState);
+ NodeState wspState = (NodeState) nodeState.getWorkspaceState();
+ if (wspState == null) {
+ // new state => unable to determine references
+ return Collections.EMPTY_SET;
+ }
+
+ Collection rs = workspaceItemStateMgr.getReferingStates(wspState);
+ if (rs.isEmpty()) {
+ return rs;
+ } else {
+ // retrieve session-propertystates
+ Set refStates = new HashSet();
+ for (Iterator it = rs.iterator(); it.hasNext();) {
+ PropertyState wState = (PropertyState) it.next();
+ ItemState sState = wState.getSessionState();
+ if (sState == null) {
+ // overlaying state has not been build up to now
+ sState = getItemState(wState.getPropertyId());
+ }
+ // add property state to list of refering states unless it has
+ // be removed in the transient layer.
+ if (sState.isValid()) {
+ refStates.add(sState);
+ }
+ }
+ return Collections.unmodifiableCollection(refStates);
+ }
}
/**
@@ -206,7 +228,12 @@
* @param nodeState
*/
public boolean hasReferingStates(NodeState nodeState) {
- return workspaceItemStateMgr.hasReferingStates(nodeState);
+ try {
+ return !getReferingStates(nodeState).isEmpty();
+ } catch (ItemStateException e) {
+ log.warn("Internal error", e);
+ return false;
+ }
}
//------------------------------------------< UpdatableItemStateManager >---
@@ -367,33 +394,20 @@
private void collectTransientStates(ItemState state, ChangeLog changeLog, boolean throwOnStale)
throws StaleItemStateException, ItemStateException {
// fail-fast test: check status of this item's state
- switch (state.getStatus()) {
- case Status.NEW:
- {
- String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": cannot save a new item.";
- log.debug(msg);
- throw new ItemStateException(msg);
- }
+ if (state.getStatus() == Status.NEW) {
+ String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": cannot save a new item.";
+ log.debug(msg);
+ throw new ItemStateException(msg);
}
- if (throwOnStale) {
- switch (state.getStatus()) {
- case Status.STALE_MODIFIED:
- {
- String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been modified externally.";
- log.debug(msg);
- throw new StaleItemStateException(msg);
- }
- case Status.STALE_DESTROYED:
- {
- String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been deleted externally.";
- log.debug(msg);
- throw new StaleItemStateException(msg);
- }
- }
+
+ if (throwOnStale && Status.isStale(state.getStatus())) {
+ String msg = LogUtil.safeGetJCRPath(state, nsResolver) + ": the item cannot be saved because it has been modified/removed externally.";
+ log.debug(msg);
+ throw new StaleItemStateException(msg);
}
// Set of transient states that should be persisted
- Set transientStates = new HashSet();
+ Set transientStates = new LinkedHashSet();
state.collectTransientStates(transientStates);
for (Iterator it = transientStates.iterator(); it.hasNext();) {
@@ -522,23 +536,12 @@
* @inheritDoc
*/
public void visit(SetMixin operation) throws ConstraintViolationException, AccessDeniedException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
- // remember if an existing mixin is being removed.
- boolean anyRemoved;
-
+ // NOTE: nodestate is only modified upon save of the changes!
QName[] mixinNames = operation.getMixinNames();
NodeState nState = operation.getNodeState();
- // mixin-names to be execute on the nodestate (and corresponding property state)
+ // new array of mixinNames to be set on the nodestate (and corresponding property state)
if (mixinNames != null && mixinNames.length > 0) {
- // find out if any of the existing mixins is removed
- List originalMixins = new ArrayList();
- originalMixins.addAll(Arrays.asList(nState.getMixinTypeNames()));
- originalMixins.removeAll(Arrays.asList(mixinNames));
- anyRemoved = originalMixins.size() > 0;
-
- // update nodestate
- nState.setMixinTypeNames(mixinNames);
-
// update/create corresponding property state
if (nState.hasPropertyName(QName.JCR_MIXINTYPES)) {
// execute value of existing property
@@ -559,10 +562,6 @@
addPropertyState(nState, pd.getQName(), pd.getRequiredType(), mixinValue, pd, options);
}
} else {
- anyRemoved = nState.getMixinTypeNames().length > 0;
- // remove all mixins
- nState.setMixinTypeNames(null);
-
// remove the jcr:mixinTypes property state if already present
if (nState.hasPropertyName(QName.JCR_MIXINTYPES)) {
try {
@@ -576,53 +575,7 @@
}
}
- // make sure, the modification of the mixin set did not left child-item
- // states defined by the removed mixin type(s)
- // TODO: the following block should be delegated to 'server' - side.
- if (anyRemoved) {
- EffectiveNodeType ent = validator.getEffectiveNodeType(nState);
- // use temp set to avoid ConcurrentModificationException
- Iterator childProps = new HashSet(nState.getPropertyEntries()).iterator();
- while (childProps.hasNext()) {
- try {
- ChildPropertyEntry entry = (ChildPropertyEntry) childProps.next();
- PropertyState childState = entry.getPropertyState();
- QName declNtName = childState.getDefinition().getDeclaringNodeType();
- // check if property has been defined by mixin type (or one of its supertypes)
- if (!ent.includesNodeType(declNtName)) {
- // the remaining effective node type doesn't include the
- // node type that declared this property, it is thus safe
- // to remove it
- int options = 0; // no checks required
- removeItemState(childState, options);
- }
- } catch (ItemStateException e) {
- // ignore. cleanup will occure upon save anyway
- log.error("Error while removing child node defined by removed mixin: {0}", e.getMessage());
- }
- }
- // use temp array to avoid ConcurrentModificationException
- Iterator childNodes = new ArrayList(nState.getChildNodeEntries()).iterator();
- while (childNodes.hasNext()) {
- try {
- ChildNodeEntry entry = (ChildNodeEntry) childNodes.next();
- NodeState childState = entry.getNodeState();
- // check if node has been defined by mixin type (or one of its supertypes)
- QName declNtName = childState.getDefinition().getDeclaringNodeType();
- if (!ent.includesNodeType(declNtName)) {
- // the remaining effective node type doesn't include the
- // node type that declared this child node, it is thus safe
- // to remove it.
- int options = 0; // NOTE: referencial intergrity checked upon save.
- removeItemState(childState, options);
- }
- } catch (ItemStateException e) {
- // ignore. cleanup will occure upon save anyway
- log.error("Error while removing child property defined by removed mixin: {0}", e.getMessage());
- }
- }
- }
-
+ nState.markModified();
transientStateMgr.addOperation(operation);
}
@@ -734,13 +687,14 @@
validator.checkAddProperty(parent, propertyName, pDef, options);
// create property state
- PropertyState propState = transientStateMgr.createNewPropertyState(propertyName, parent, pDef);
-
- // NOTE: callers must make sure, the property type is not 'undefined'
- propState.setValues(values, propertyType);
+ PropertyState propState = transientStateMgr.createNewPropertyState(propertyName, parent, pDef, values, propertyType);
}
- private void addNodeState(NodeState parent, QName nodeName, QName nodeTypeName, String uuid, QNodeDefinition definition, int options) throws RepositoryException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, NoSuchNodeTypeException, ItemExistsException, VersionException {
+ private void addNodeState(NodeState parent, QName nodeName, QName nodeTypeName,
+ String uuid, QNodeDefinition definition, int options)
+ throws RepositoryException, ConstraintViolationException, AccessDeniedException,
+ UnsupportedRepositoryOperationException, NoSuchNodeTypeException,
+ ItemExistsException, VersionException {
// TODO: improve...
// check if add node is possible. note, that the options differ if
@@ -773,7 +727,7 @@
QPropertyDefinition pd = pda[i];
QValue[] autoValue = computeSystemGeneratedPropertyValues(nodeState, pd);
if (autoValue != null) {
- int propOptions = 0; // nothing to check
+ int propOptions = ItemStateValidator.CHECK_NONE;
// execute 'addProperty' without adding operation.
addPropertyState(nodeState, pd.getQName(), pd.getRequiredType(), autoValue, pd, propOptions);
}
@@ -824,19 +778,6 @@
throws ValueFormatException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
// assert that the property can be modified.
validator.checkSetProperty(propState, options);
-
- // free old values as necessary
- QValue[] oldValues = propState.getValues();
- if (oldValues != null) {
- for (int i = 0; i < oldValues.length; i++) {
- QValue old = oldValues[i];
- if (old != null) {
- // make sure temporarily allocated data is discarded
- // before overwriting it (see QValue#discard())
- old.discard();
- }
- }
- }
propState.setValues(iva, valueType);
}
@@ -874,7 +815,11 @@
QName name = def.getQName();
if (QName.MIX_REFERENCEABLE.equals(declaringNT) && QName.JCR_UUID.equals(name)) {
// mix:referenceable node type defines jcr:uuid
- genValues = new QValue[]{QValue.create(parent.getNodeId().getUUID().toString())};
+ String uuid = parent.getUUID();
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ }
+ genValues = new QValue[]{QValue.create(uuid)};
} else if (QName.NT_BASE.equals(declaringNT)) {
// nt:base node type
if (QName.JCR_PRIMARYTYPE.equals(name)) {
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java?view=diff&rev=467956&r1=467955&r2=467956
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/Status.java Thu Oct 26 04:02:02 2006
@@ -62,11 +62,41 @@
*/
public static final int REMOVED = 8;
-
- public static boolean isTerminalStatus(int status) {
+ /**
+ * Returns <code>true</code> if the given status is a terminal status, i.e.
+ * the given status one of:
+ * <ul>
+ * <li>{@link #REMOVED}</li>
+ * <li>{@link #STALE_DESTROYED}</li>
+ * </ul>
+ *
+ * @param status
+ * @return
+ */
+ public static boolean isTerminal(int status) {
return status == REMOVED || status == STALE_DESTROYED;
}
+ /**
+ * Returns <code>true</code> if this item state is valid, that is its status
+ * is one of:
+ * <ul>
+ * <li>{@link #EXISTING}</li>
+ * <li>{@link #EXISTING_MODIFIED}</li>
+ * <li>{@link #NEW}</li>
+ * </ul>
+ *
+ * @param status
+ * @return
+ */
+ public static boolean isValid(int status) {
+ return status == EXISTING || status == EXISTING_MODIFIED || status == NEW;
+ }
+
+ public static boolean isStale(int status) {
+ return status == STALE_DESTROYED || status == STALE_MODIFIED;
+ }
+
public static boolean isValidStatusChange(int oldStatus, int newStatus,
boolean isWorkspaceState) {
if (oldStatus == newStatus) {
@@ -112,10 +142,9 @@
case REMOVED:
isValid = (oldStatus == NEW || oldStatus == EXISTING || oldStatus == EXISTING_REMOVED);
break;
- /* default:
- NEW cannot change state to NEW -> false
- MODIFIED never applicable to session state -> false */
-
+ /* default:
+ NEW cannot change state to NEW -> false
+ MODIFIED never applicable to session state -> false */
}
}
return isValid;
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java?view=diff&rev=467956&r1=467955&r2=467956
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientISFactory.java Thu Oct 26 04:02:02 2006
@@ -35,16 +35,14 @@
private final IdFactory idFactory;
private final ItemStateManager parent;
- private ItemStateLifeCycleListener listener;
+ private ItemStateCache cache;
+ private ItemStateCreationListener listener;
TransientISFactory(IdFactory idFactory, ItemStateManager parent) {
this.idFactory = idFactory;
this.parent = parent;
}
- void setListener(ItemStateLifeCycleListener listener) {
- this.listener = listener;
- }
//------------------------------------------< TransientItemStateFactory >---
/**
* @inheritDoc
@@ -55,10 +53,15 @@
QNodeDefinition definition) {
NodeState nodeState = new NodeState(name, uuid, parent, nodetypeName,
definition, Status.NEW, this, idFactory, false);
- // get a notification when this item state is saved or invalidated
+
+ // notify listeners when this item state is saved or invalidated
+ nodeState.addListener(cache);
nodeState.addListener(listener);
- // notify listener that a node state has been created
- listener.statusChanged(nodeState, Status.NEW);
+
+ // notify listeners that a node state has been created
+ cache.created(nodeState);
+ listener.created(nodeState);
+
return nodeState;
}
@@ -68,14 +71,27 @@
*/
public PropertyState createNewPropertyState(QName name, NodeState parent, QPropertyDefinition definition) {
PropertyState propState = new PropertyState(name, parent,
- definition, Status.NEW, idFactory, false);
+ definition, Status.NEW, this, idFactory, false);
+
// get a notification when this item state is saved or invalidated
+ propState.addListener(cache);
propState.addListener(listener);
- // notify listener that a property state has been created
- listener.statusChanged(propState, Status.NEW);
+
+ // notify listeners that a property state has been created
+ cache.created(propState);
+ listener.created(propState);
+
return propState;
}
+ /**
+ * @inheritDoc
+ * @see TransientItemStateFactory#setListener(ItemStateCreationListener)
+ */
+ public void setListener(ItemStateCreationListener listener) {
+ this.listener = listener;
+ }
+
//---------------------------------------------------< ItemStateFactory >---
/**
* @inheritDoc
@@ -84,9 +100,10 @@
public NodeState createRootState(ItemStateManager ism) throws ItemStateException {
// retrieve state to overlay
NodeState overlayedState = (NodeState) parent.getRootState();
- NodeState nodeState = new NodeState(overlayedState, null,
- Status.EXISTING, this, idFactory);
- nodeState.addListener(listener);
+ NodeState nodeState = new NodeState(overlayedState, null, Status.EXISTING, this, idFactory);
+
+ nodeState.addListener(cache);
+ cache.created(nodeState);
return nodeState;
}
@@ -96,16 +113,22 @@
*/
public NodeState createNodeState(NodeId nodeId, ItemStateManager ism)
throws NoSuchItemStateException, ItemStateException {
- // retrieve state to overlay
- NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
- NodeState overlayedParent = overlayedState.getParent();
- NodeState parentState = null;
- if (overlayedParent != null) {
- parentState = (NodeState) ism.getItemState(overlayedParent.getId());
+
+ NodeState nodeState = cache.getNodeState(nodeId);
+ if (nodeState == null) {
+ // retrieve state to overlay
+ NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
+ NodeState overlayedParent = overlayedState.getParent();
+
+ NodeState parentState = null;
+ if (overlayedParent != null) {
+ parentState = (NodeState) ism.getItemState(overlayedParent.getId());
+ }
+
+ nodeState = new NodeState(overlayedState, parentState, Status.EXISTING, this, idFactory);
+ nodeState.addListener(cache);
+ cache.created(nodeState);
}
- NodeState nodeState = new NodeState(overlayedState, parentState,
- Status.EXISTING, this, idFactory);
- nodeState.addListener(listener);
return nodeState;
}
@@ -115,11 +138,16 @@
*/
public NodeState createNodeState(NodeId nodeId, NodeState parentState)
throws NoSuchItemStateException, ItemStateException {
- // retrieve state to overlay
- NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
- NodeState nodeState = new NodeState(overlayedState, parentState,
- Status.EXISTING, this, idFactory);
- nodeState.addListener(listener);
+
+ NodeState nodeState = cache.getNodeState(nodeId);
+ if (nodeState == null) {
+ // retrieve state to overlay
+ NodeState overlayedState = (NodeState) parent.getItemState(nodeId);
+ nodeState = new NodeState(overlayedState, parentState, Status.EXISTING, this, idFactory);
+
+ nodeState.addListener(cache);
+ cache.created(nodeState);
+ }
return nodeState;
}
@@ -130,11 +158,24 @@
public PropertyState createPropertyState(PropertyId propertyId,
NodeState parentState)
throws NoSuchItemStateException, ItemStateException {
- // retrieve state to overlay
- PropertyState overlayedState = (PropertyState) parent.getItemState(propertyId);
- PropertyState propState = new PropertyState(overlayedState, parentState,
- Status.EXISTING, idFactory);
- propState.addListener(listener);
+
+ PropertyState propState = cache.getPropertyState(propertyId);
+ if (propState == null) {
+ // retrieve state to overlay
+ PropertyState overlayedState = (PropertyState) parent.getItemState(propertyId);
+ propState = new PropertyState(overlayedState, parentState, Status.EXISTING, this, idFactory);
+
+ propState.addListener(cache);
+ cache.created(propState);
+ }
return propState;
+ }
+
+ /**
+ * @inheritDoc
+ * @see ItemStateFactory#setCache(ItemStateCache)
+ */
+ public void setCache(ItemStateCache cache) {
+ this.cache = cache;
}
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java?view=diff&rev=467956&r1=467955&r2=467956
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateFactory.java Thu Oct 26 04:02:02 2006
@@ -55,4 +55,11 @@
public PropertyState createNewPropertyState(QName name,
NodeState parent,
QPropertyDefinition definition);
+
+ /**
+ * Set the listener that gets informed about NEW states.
+ *
+ * @param listener
+ */
+ public void setListener(ItemStateCreationListener listener);
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?view=diff&rev=467956&r1=467955&r2=467956
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Thu Oct 26 04:02:02 2006
@@ -22,10 +22,13 @@
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.value.QValue;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import javax.jcr.ItemExistsException;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
import java.util.Iterator;
import java.util.Collection;
@@ -38,10 +41,10 @@
* item states. While all other modifications can be invoked on the item state
* instances itself, creating a new node state is done using
* {@link #createNewNodeState(QName, String, QName, QNodeDefinition, NodeState)}
- * and {@link #createNewPropertyState(QName, NodeState, QPropertyDefinition)}.
+ * and {@link #createNewPropertyState(QName, NodeState, QPropertyDefinition, QValue[], int)}.
*/
public class TransientItemStateManager extends CachingItemStateManager
- implements ItemStateLifeCycleListener {
+ implements ItemStateCreationListener {
/**
* Logger instance for this class.
@@ -55,21 +58,19 @@
private final ChangeLog changeLog;
/**
- * The parent item state manager, which return item states that are then
- * overlayed by transient item states created by this TransientItemStateManager.
- */
- private final ItemStateManager parent;
-
- /**
* The root node state or <code>null</code> if it hasn't been retrieved yet.
*/
private NodeState rootNodeState;
+ /**
+ *
+ * @param idFactory
+ * @param parent
+ */
TransientItemStateManager(IdFactory idFactory, ItemStateManager parent) {
super(new TransientISFactory(idFactory, parent), idFactory);
- this.parent = parent;
this.changeLog = new ChangeLog(null);
- ((TransientISFactory) getTransientFactory()).setListener(this);
+ getTransientFactory().setListener(this);
}
@@ -128,9 +129,8 @@
QNodeDefinition definition, NodeState parent) {
NodeState nodeState = getTransientFactory().createNewNodeState(nodeName, uuid, parent, nodeTypeName, definition);
- parent.addChildNodeState(nodeState, uuid);
+ parent.addChildNodeState(nodeState);
changeLog.added(nodeState);
- nodeState.addListener(this);
return nodeState;
}
@@ -143,15 +143,20 @@
* @param definition
* @return the created property state.
* @throws ItemExistsException if <code>parent</code> already has a property
- * with the given name.
- */
- PropertyState createNewPropertyState(QName propName, NodeState parent, QPropertyDefinition definition)
- throws ItemExistsException {
+ * with the given name.
+ * @throws ConstraintViolationException
+ * @throws RepositoryException
+ */
+ PropertyState createNewPropertyState(QName propName, NodeState parent,
+ QPropertyDefinition definition,
+ QValue[] values, int propertyType)
+ throws ItemExistsException, ConstraintViolationException, RepositoryException {
PropertyState propState = getTransientFactory().createNewPropertyState(propName, parent, definition);
+ // NOTE: callers must make sure, the property type is not 'undefined'
+ propState.init(propertyType, values);
parent.addPropertyState(propState);
changeLog.added(propState);
- propState.addListener(this);
return propState;
}
@@ -186,7 +191,6 @@
public NodeState getRootState() throws ItemStateException {
if (rootNodeState == null) {
rootNodeState = getItemStateFactory().createRootState(this);
- rootNodeState.addListener(this);
}
return rootNodeState;
}
@@ -246,6 +250,11 @@
* @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
*/
public void statusChanged(ItemState state, int previousStatus) {
+ if (!Status.isValidStatusChange(previousStatus, state.getStatus(), false)) {
+ log.error("ItemState has invalid status: " + state.getStatus());
+ return;
+ }
+
// TODO: depending on status of state adapt change log
// e.g. a revert on states will reset the status from
// 'existing modified' to 'existing'.
@@ -301,12 +310,20 @@
case Status.STALE_MODIFIED:
// state is now stale. keep in modified. wait until refreshed
break;
- case Status.NEW:
- // new state has been created
- changeLog.added(state);
- break;
default:
log.error("ItemState has invalid status: " + state.getStatus());
+ }
+ }
+
+ //-----------------------------------------< ItemStateCreationListener >---
+
+ /**
+ * @see ItemStateCreationListener#created(ItemState)
+ */
+ public void created(ItemState state) {
+ // new state has been created
+ if (state.getStatus() == Status.NEW) {
+ changeLog.added(state);
}
}
}