Author: angela
Date: Thu Mar 1 03:17:22 2007
New Revision: 513279
URL: http://svn.apache.org/viewvc?view=rev&rev=513279
Log:
fixed: move, reorder (work in progress)
fixed: locktoken transfer
fixed: recursive transient removal of invalidated tree fails (restoreTests)
improve: simplify usage of workspace operations
fixed: store original itemId with transient operations in order to properly reflect the changelog upon batch creation
fixed: workspace import doesn't invalidate
fixed: session import uuid handling
Added:
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (with props)
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/WorkspaceImport.java
Modified:
jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AbstractCopy.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddLabel.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddNode.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/AddProperty.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkin.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Checkout.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Clone.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockOperation.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRefresh.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/LockRelease.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Merge.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Move.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/OperationVisitor.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Remove.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/RemoveLabel.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/ReorderNodes.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/ResolveMergeConflict.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Restore.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/SetMixin.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/SetPropertyValue.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/operation/Update.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateValidator.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/ReferenceChangeTracker.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/util/StateUtility.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SessionImporter.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/WorkspaceContentHandler.java
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/TODO.txt Thu Mar 1 03:17:22 2007
@@ -9,23 +9,14 @@
- TODO: test Observation
-- TODO: test Merge
-
-- BUG: NodeEntryImpl needs an attic for transiently moved and removed
- child entries in order not to access those from the persistent
- storage again.
- In addition: having an attic allows to properly handle events
- notifying external modifications for those moved/removed
- NodeEntries, that are not identified by a uniqueID
+- TODO: test Merge, Workspace.restore, Workspace.copy, Workspace.clone
+ (missing clone impl with spi2dav)
- BUG: Node.hasProperty currently returns false, if prop-entry has
not been loaded yet -> should use same mechanism as Node.getProperty.
-
-- BUG: Restore (testcase failed)
-- BUG: LockToken transfer with Session.addLockToken, Session.removeLockToken
-
-- BUG: Workspace.importXML does not invalidate
+- BUG: Locks that have been accessed by another session are not informed
+ if the lock is released by the lock-holder.
- IMPROVE: Replace ItemState-duality by copy on write behaviour
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java Thu Mar 1 03:17:22 2007
@@ -116,7 +116,7 @@
}
}
- //-----------------------------------------------------< Item interface >---
+ //---------------------------------------------------------------< Item >---
/**
* @see Item#getName()
*/
@@ -155,7 +155,7 @@
return true;
}
- //-----------------------------------------------------< Node interface >---
+ //---------------------------------------------------------------< Node >---
/**
* @see Node#addNode(String)
*/
@@ -730,7 +730,7 @@
*/
private List getMixinTypes() {
QName[] mixinValue = new QName[0];
- if (getNodeEntry().hasPropertyEntry(QName.JCR_MIXINTYPES)) {
+ if (hasProperty(QName.JCR_MIXINTYPES)) {
if (getNodeState().getStatus() == Status.EXISTING) {
mixinValue = getNodeState().getMixinTypeNames();
} else {
@@ -1209,7 +1209,12 @@
}
// check effective node type
- return getEffectiveNodeType().includesNodeType(qName);
+ try {
+ EffectiveNodeType effnt = session.getValidator().getEffectiveNodeType(getNodeState().getNodeTypeNames());
+ return effnt.includesNodeType(qName);
+ } catch (NodeTypeConflictException e) {
+ throw new RepositoryException(e);
+ }
}
//-----------------------------------------------------------< ItemImpl >---
@@ -1669,19 +1674,6 @@
throw new RepositoryException(msg, e);
}
return targetEntry;
- }
-
- /**
- * Returns the effective (i.e. merged and resolved) node type representation
- * of this node's primary and mixin node types.
- *
- * @return the effective node type
- * @throws RepositoryException
- */
- private EffectiveNodeType getEffectiveNodeType() throws RepositoryException {
- // build effective node type of mixins & primary type
- ItemStateValidator validator = session.getValidator();
- return validator.getEffectiveNodeType(getNodeState());
}
/**
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java Thu Mar 1 03:17:22 2007
@@ -29,6 +29,7 @@
import org.apache.jackrabbit.jcr2spi.operation.Copy;
import org.apache.jackrabbit.jcr2spi.operation.Clone;
import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.operation.WorkspaceImport;
import org.apache.jackrabbit.jcr2spi.security.AccessManager;
import org.apache.jackrabbit.jcr2spi.lock.LockManager;
import org.apache.jackrabbit.jcr2spi.lock.LockManagerImpl;
@@ -327,8 +328,7 @@
/**
* @see javax.jcr.Workspace#importXML(String, InputStream, int)
*/
- public void importXML(String parentAbsPath, InputStream in,
- int uuidBehavior)
+ public void importXML(String parentAbsPath, InputStream in, int uuidBehavior)
throws IOException, PathNotFoundException, ItemExistsException,
ConstraintViolationException, InvalidSerializedDataException,
LockException, RepositoryException {
@@ -345,7 +345,7 @@
getValidator().checkIsWritable(parentState, options);
// run the import
- wspManager.importXml(parentState, in, uuidBehavior);
+ wspManager.execute(WorkspaceImport.create(parentState, in, uuidBehavior));
} else {
throw new PathNotFoundException("No node at path " + parentAbsPath);
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Thu Mar 1 03:17:22 2007
@@ -22,8 +22,6 @@
import org.apache.jackrabbit.jcr2spi.name.NamespaceStorage;
import org.apache.jackrabbit.jcr2spi.name.NamespaceRegistryImpl;
import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
-import org.apache.jackrabbit.jcr2spi.state.PropertyState;
import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
import org.apache.jackrabbit.jcr2spi.state.UpdatableItemStateManager;
import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
@@ -31,6 +29,7 @@
import org.apache.jackrabbit.jcr2spi.state.NodeState;
import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
import org.apache.jackrabbit.jcr2spi.state.TransientISFactory;
+import org.apache.jackrabbit.jcr2spi.state.Status;
import org.apache.jackrabbit.jcr2spi.operation.OperationVisitor;
import org.apache.jackrabbit.jcr2spi.operation.AddNode;
import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
@@ -54,6 +53,7 @@
import org.apache.jackrabbit.jcr2spi.operation.AddLabel;
import org.apache.jackrabbit.jcr2spi.operation.RemoveLabel;
import org.apache.jackrabbit.jcr2spi.operation.RemoveVersion;
+import org.apache.jackrabbit.jcr2spi.operation.WorkspaceImport;
import org.apache.jackrabbit.jcr2spi.security.AccessManager;
import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
@@ -109,7 +109,6 @@
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
-import java.io.InputStream;
import EDU.oswego.cs.dl.util.concurrent.Sync;
import EDU.oswego.cs.dl.util.concurrent.Mutex;
@@ -209,8 +208,6 @@
* determine, which lock this token belongs to. The latter would be
* necessary in order to build the 'Lock' object properly.
*
- * TODO: check if throwing an exception would be more appropriate
- *
* @param lt
* @throws LockException
* @throws RepositoryException
@@ -236,7 +233,16 @@
* @throws RepositoryException
*/
public void removeLockToken(String lt) throws LockException, RepositoryException {
- sessionInfo.removeLockToken(lt);
+ String[] tokems = sessionInfo.getLockTokens();
+ for (int i = 0; i < tokems.length; i++) {
+ if (tokems[i].equals(lt)) {
+ sessionInfo.removeLockToken(lt);
+ return;
+ }
+ }
+ // sessionInfo doesn't contain the given lock token and is therefore
+ // not the lock holder
+ throw new RepositoryException("Unable to remove locktoken '" + lt + "' from Session.");
}
/**
@@ -481,6 +487,10 @@
* @see AccessManager#isGranted(NodeState, Path, String[])
*/
public boolean isGranted(NodeState parentState, Path relPath, String[] actions) throws ItemNotFoundException, RepositoryException {
+ // TODO: correct?
+ if (parentState.getStatus() == Status.NEW) {
+ return true;
+ }
// TODO: check again.
// build itemId from the given state and the relative path without
// making an attempt to retrieve the proper id of the item possibly
@@ -495,37 +505,34 @@
* @see AccessManager#isGranted(ItemState, String[])
*/
public boolean isGranted(ItemState itemState, String[] actions) throws ItemNotFoundException, RepositoryException {
- ItemState wspState = itemState.getWorkspaceState();
// a 'new' state can always be read, written and removed
// TODO: correct?
- if (wspState == null) {
+ if (itemState.getStatus() == Status.NEW) {
return true;
}
- return service.isGranted(sessionInfo, wspState.getId(), actions);
+ return service.isGranted(sessionInfo, itemState.getId(), actions);
}
/**
* @see AccessManager#canRead(ItemState)
*/
public boolean canRead(ItemState itemState) throws ItemNotFoundException, RepositoryException {
- ItemState wspState = itemState.getWorkspaceState();
// a 'new' state can always be read
- if (wspState == null) {
+ if (itemState.getStatus() == Status.NEW) {
return true;
}
- return service.isGranted(sessionInfo, wspState.getId(), AccessManager.READ);
+ return service.isGranted(sessionInfo, itemState.getId(), AccessManager.READ);
}
/**
* @see AccessManager#canRemove(ItemState)
*/
public boolean canRemove(ItemState itemState) throws ItemNotFoundException, RepositoryException {
- ItemState wspState = itemState.getWorkspaceState();
// a 'new' state can always be removed again
- if (wspState == null) {
+ if (itemState.getStatus() == Status.NEW) {
return true;
}
- return service.isGranted(sessionInfo, wspState.getId(), AccessManager.REMOVE);
+ return service.isGranted(sessionInfo, itemState.getId(), AccessManager.REMOVE);
}
/**
@@ -541,12 +548,6 @@
return false;
}
- //---------------------------------------------------------< XML import >---
- public void importXml(NodeState parentState, InputStream xmlStream, int uuidBehaviour) throws RepositoryException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, ItemExistsException, VersionException {
- NodeId parentId = parentState.getNodeId();
- service.importXml(sessionInfo, parentId, xmlStream, uuidBehaviour);
- }
-
//---------------------------------------------------< NamespaceStorage >---
public Map getRegisteredNamespaces() throws RepositoryException {
@@ -698,7 +699,7 @@
* @see OperationVisitor#visit(AddNode)
*/
public void visit(AddNode operation) throws RepositoryException {
- NodeId parentId = operation.getParentState().getNodeId();
+ NodeId parentId = operation.getParentId();
batch.addNode(parentId, operation.getNodeName(), operation.getNodeTypeName(), operation.getUuid());
}
@@ -707,7 +708,7 @@
* @see OperationVisitor#visit(AddProperty)
*/
public void visit(AddProperty operation) throws RepositoryException {
- NodeId parentId = operation.getParentState().getNodeId();
+ NodeId parentId = operation.getParentId();
QName propertyName = operation.getPropertyName();
if (operation.isMultiValued()) {
batch.addProperty(parentId, propertyName, operation.getValues());
@@ -722,8 +723,8 @@
* @see OperationVisitor#visit(Clone)
*/
public void visit(Clone operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
- NodeId destParentId = operation.getDestinationParentState().getNodeId();
+ NodeId nId = operation.getNodeId();
+ NodeId destParentId = operation.getDestinationParentId();
service.clone(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName(), operation.isRemoveExisting());
}
@@ -732,8 +733,8 @@
* @see OperationVisitor#visit(Copy)
*/
public void visit(Copy operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
- NodeId destParentId = operation.getDestinationParentState().getNodeId();
+ NodeId nId = operation.getNodeId();
+ NodeId destParentId = operation.getDestinationParentId();
service.copy(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName());
}
@@ -743,7 +744,8 @@
*/
public void visit(Move operation) throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
NodeId moveId = operation.getSourceId();
- NodeId destParentId = operation.getDestinationParentState().getNodeId();
+ NodeId destParentId = operation.getDestinationParentId();
+
if (batch == null) {
service.move(sessionInfo, moveId, destParentId, operation.getDestinationName());
} else {
@@ -756,7 +758,7 @@
* @see OperationVisitor#visit(Update)
*/
public void visit(Update operation) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
+ NodeId nId = operation.getNodeId();
service.update(sessionInfo, nId, operation.getSourceWorkspaceName());
}
@@ -765,8 +767,7 @@
* @see OperationVisitor#visit(Remove)
*/
public void visit(Remove operation) throws RepositoryException {
- ItemId id = operation.getRemoveState().getId();
- batch.remove(id);
+ batch.remove(operation.getRemoveId());
}
/**
@@ -774,7 +775,7 @@
* @see OperationVisitor#visit(SetMixin)
*/
public void visit(SetMixin operation) throws RepositoryException {
- batch.setMixins(operation.getNodeState().getNodeId(), operation.getMixinNames());
+ batch.setMixins(operation.getNodeId(), operation.getMixinNames());
}
/**
@@ -782,9 +783,8 @@
* @see OperationVisitor#visit(SetPropertyValue)
*/
public void visit(SetPropertyValue operation) throws RepositoryException {
- PropertyState pState = operation.getPropertyState();
- PropertyId id = pState.getPropertyId();
- if (pState.isMultiValued()) {
+ PropertyId id = operation.getPropertyId();
+ if (operation.isMultiValued()) {
batch.setValue(id, operation.getValues());
} else {
batch.setValue(id, operation.getValues()[0]);
@@ -796,12 +796,9 @@
* @see OperationVisitor#visit(ReorderNodes)
*/
public void visit(ReorderNodes operation) throws RepositoryException {
- NodeId parentId = operation.getParentState().getNodeId();
- NodeId insertId = operation.getInsertNode().getNodeId();
- NodeId beforeId = null;
- if (operation.getBeforeNode() != null) {
- beforeId = operation.getBeforeNode().getNodeId() ;
- }
+ NodeId parentId = operation.getParentId();
+ NodeId insertId = operation.getInsertId();
+ NodeId beforeId = operation.getBeforeId();
batch.reorderNodes(parentId, insertId, beforeId);
}
@@ -810,7 +807,7 @@
* @see OperationVisitor#visit(Checkout)
*/
public void visit(Checkout operation) throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
- service.checkout(sessionInfo, operation.getNodeState().getNodeId());
+ service.checkout(sessionInfo, operation.getNodeId());
}
/**
@@ -818,7 +815,7 @@
* @see OperationVisitor#visit(Checkin)
*/
public void visit(Checkin operation) throws UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
- service.checkin(sessionInfo, operation.getNodeState().getNodeId());
+ service.checkin(sessionInfo, operation.getNodeId());
}
/**
@@ -826,32 +823,19 @@
* @see OperationVisitor#visit(Restore)
*/
public void visit(Restore operation) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
- NodeState nState = operation.getNodeState();
- NodeState[] versionStates = operation.getVersionStates();
- if (versionStates == null || versionStates.length == 0) {
- throw new IllegalArgumentException("Restore must specify at least a singe version.");
- }
-
- NodeId[] vIds = new NodeId[versionStates.length];
- for (int i = 0; i < vIds.length; i++) {
- vIds[i] = versionStates[i].getNodeId();
- }
-
- if (nState == null) {
- service.restore(sessionInfo, vIds, operation.removeExisting());
+ NodeId nId = operation.getNodeId();
+ if (nId == null) {
+ service.restore(sessionInfo, operation.getVersionIds(), operation.removeExisting());
} else {
- if (vIds.length > 1) {
- throw new IllegalArgumentException("Restore from a single node must specify but one single Version.");
- }
-
NodeId targetId;
Path relPath = operation.getRelativePath();
if (relPath != null) {
- targetId = getIdFactory().createNodeId(nState.getNodeId(), relPath);
+ targetId = getIdFactory().createNodeId(nId, relPath);
} else {
- targetId = nState.getNodeId();
+ targetId = nId;
}
- service.restore(sessionInfo, targetId, vIds[0], operation.removeExisting());
+ NodeId versionId = operation.getVersionIds()[0];
+ service.restore(sessionInfo, targetId, versionId, operation.removeExisting());
}
}
@@ -860,7 +844,7 @@
* @see OperationVisitor#visit(Merge)
*/
public void visit(Merge operation) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
+ NodeId nId = operation.getNodeId();
IdIterator failed = service.merge(sessionInfo, nId, operation.getSourceWorkspaceName(), operation.bestEffort());
operation.setFailedIds(failed);
}
@@ -870,45 +854,10 @@
* @see OperationVisitor#visit(ResolveMergeConflict)
*/
public void visit(ResolveMergeConflict operation) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
- try {
- NodeState nState = operation.getNodeState();
- NodeId nId = nState.getNodeId();
- NodeId vId = operation.getVersionState().getNodeId();
-
- PropertyState mergeFailedState = nState.getPropertyState(QName.JCR_MERGEFAILED);
- QValue[] vs = mergeFailedState.getValues();
-
- NodeId[] mergeFailedIds = new NodeId[vs.length - 1];
- for (int i = 0, j = 0; i < vs.length; i++) {
- NodeId id = getIdFactory().createNodeId(vs[i].getString());
- if (!id.equals(vId)) {
- mergeFailedIds[j] = id;
- j++;
- }
- // else: the version id is being solved by this call and not
- // part of 'jcr:mergefailed' any more
- }
-
- PropertyState predecessorState = nState.getPropertyState(QName.JCR_PREDECESSORS);
- vs = predecessorState.getValues();
-
- boolean resolveDone = operation.resolveDone();
- int noOfPredecessors = (resolveDone) ? vs.length + 1 : vs.length;
- NodeId[] predecessorIds = new NodeId[noOfPredecessors];
-
- int i = 0;
- while (i < vs.length) {
- predecessorIds[i] = getIdFactory().createNodeId(vs[i].getString());
- i++;
- }
- if (resolveDone) {
- predecessorIds[i] = vId;
- }
- service.resolveMergeConflict(sessionInfo, nId, mergeFailedIds, predecessorIds);
- } catch (ItemStateException e) {
- // should not occur.
- throw new RepositoryException(e);
- }
+ NodeId nId = operation.getNodeId();
+ NodeId[] mergedFailedIds = operation.getMergeFailedIds();
+ NodeId[] predecessorIds = operation.getPredecessorIds();
+ service.resolveMergeConflict(sessionInfo, nId, mergedFailedIds, predecessorIds);
}
/**
@@ -916,8 +865,7 @@
* @see OperationVisitor#visit(LockOperation)
*/
public void visit(LockOperation operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
- LockInfo lInfo = service.lock(sessionInfo, nId, operation.isDeep(), operation.isSessionScoped());
+ LockInfo lInfo = service.lock(sessionInfo, operation.getNodeId(), operation.isDeep(), operation.isSessionScoped());
operation.setLockInfo(lInfo);
}
@@ -926,8 +874,7 @@
* @see OperationVisitor#visit(LockRefresh)
*/
public void visit(LockRefresh operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
- service.refreshLock(sessionInfo, nId);
+ service.refreshLock(sessionInfo, operation.getNodeId());
}
/**
@@ -935,8 +882,7 @@
* @see OperationVisitor#visit(LockRelease)
*/
public void visit(LockRelease operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
- NodeId nId = operation.getNodeState().getNodeId();
- service.unlock(sessionInfo, nId);
+ service.unlock(sessionInfo, operation.getNodeId());
}
/**
@@ -944,8 +890,8 @@
* @see OperationVisitor#visit(AddLabel)
*/
public void visit(AddLabel operation) throws VersionException, RepositoryException {
- NodeId vhId = operation.getVersionHistoryState().getNodeId();
- NodeId vId = operation.getVersionState().getNodeId();
+ NodeId vhId = operation.getVersionHistoryId();
+ NodeId vId = operation.getVersionId();
service.addVersionLabel(sessionInfo, vhId, vId, operation.getLabel(), operation.moveLabel());
}
@@ -954,8 +900,8 @@
* @see OperationVisitor#visit(RemoveLabel)
*/
public void visit(RemoveLabel operation) throws VersionException, RepositoryException {
- NodeId vhId = operation.getVersionHistoryState().getNodeId();
- NodeId vId = operation.getVersionState().getNodeId();
+ NodeId vhId = operation.getVersionHistoryId();
+ NodeId vId = operation.getVersionId();
service.removeVersionLabel(sessionInfo, vhId, vId, operation.getLabel());
}
@@ -964,9 +910,17 @@
* @see OperationVisitor#visit(RemoveVersion)
*/
public void visit(RemoveVersion operation) throws VersionException, AccessDeniedException, ReferentialIntegrityException, RepositoryException {
- NodeState versionState = (NodeState) operation.getRemoveState();
+ NodeId versionId = (NodeId) operation.getRemoveId();
NodeState vhState = operation.getParentState();
- service.removeVersion(sessionInfo, vhState.getNodeId(), versionState.getNodeId());
+ service.removeVersion(sessionInfo, vhState.getNodeId(), versionId);
+ }
+
+ /**
+ * @inheritDoc
+ * @see OperationVisitor#visit(WorkspaceImport)
+ */
+ public void visit(WorkspaceImport operation) throws RepositoryException {
+ service.importXml(sessionInfo, operation.getNodeId(), operation.getXmlStream(), operation.getUuidBehaviour());
}
}
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java?view=auto&rev=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java Thu Mar 1 03:17:22 2007
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.hierarchy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.name.QName;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * ChildNodeAttic...
+ */
+class ChildNodeAttic {
+
+ private static Logger log = LoggerFactory.getLogger(ChildNodeAttic.class);
+
+ private Set attic = new HashSet();
+
+ ChildNodeAttic() {
+ }
+
+ boolean contains(QName name, int index) {
+ for (Iterator it = attic.iterator(); it.hasNext();) {
+ NodeEntryImpl ne = (NodeEntryImpl) it.next();
+ if (ne.matches(name, index)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ List get(QName name) {
+ List l = new ArrayList();
+ for (Iterator it = attic.iterator(); it.hasNext();) {
+ NodeEntryImpl ne = (NodeEntryImpl) it.next();
+ if (ne.matches(name)) {
+ l.add(ne);
+ }
+ }
+ return l;
+ }
+
+ /**
+ *
+ * @param name The original name of the NodeEntry before it has been moved.
+ * @param index The original index of the NodeEntry before it has been moved.
+ * @return
+ */
+ NodeEntry get(QName name, int index) {
+ for (Iterator it = attic.iterator(); it.hasNext();) {
+ NodeEntryImpl ne = (NodeEntryImpl) it.next();
+ if (ne.matches(name, index)) {
+ return ne;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ *
+ * @param uniqueId
+ * @return
+ */
+ NodeEntry get(String uniqueId) {
+ if (uniqueId == null) {
+ throw new IllegalArgumentException();
+ }
+ for (Iterator it = attic.iterator(); it.hasNext();) {
+ NodeEntryImpl ne = (NodeEntryImpl) it.next();
+ if (uniqueId.equals(ne.getUniqueID())) {
+ return ne;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ void add(NodeEntryImpl movedEntry) {
+ attic.add(movedEntry);
+ }
+
+ void remove(NodeEntryImpl movedEntry) {
+ if (attic.contains(movedEntry)) {
+ attic.remove(movedEntry);
+ }
+ }
+
+ void clear() {
+ if (attic != null) {
+ attic.clear();
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeAttic.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java Thu Mar 1 03:17:22 2007
@@ -142,7 +142,7 @@
// no matching entry found
return false;
}
-
+
/**
* Returns a List of NodeEntrys for the
* given nodeName. This method does not filter out
@@ -216,6 +216,10 @@
if (obj instanceof List) {
// map entry is a list of siblings
List siblings = (List) obj;
+ // shortcut if index can never match
+ if (index > siblings.size()) {
+ return null;
+ }
// filter out removed states
for (Iterator it = siblings.iterator(); it.hasNext(); ) {
NodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getNodeEntry();
@@ -269,49 +273,46 @@
}
/**
- * Adds a NodeEntry to the end of the list.
+ * Adds a NodeEntry to the end of the list. Same as
+ * {@link #add(NodeEntry, int)}, where the index is {@link Path#INDEX_UNDEFINED}.
*
* @param cne the NodeEntry to add.
*/
void add(NodeEntry cne) {
- QName nodeName = cne.getQName();
- List siblings = null;
- Object obj = nameMap.get(nodeName);
- if (obj != null) {
- if (obj instanceof List) {
- // map entry is a list of siblings
- siblings = (ArrayList) obj;
- } else {
- // map entry is a single child node entry,
- // convert to siblings list
- siblings = new ArrayList();
- siblings.add(obj);
- nameMap.put(nodeName, siblings);
- }
- }
-
- LinkedEntries.LinkNode ln = entries.add(cne);
-
- if (siblings != null) {
- siblings.add(ln);
- } else {
- nameMap.put(nodeName, ln);
- }
+ add(cne, Path.INDEX_UNDEFINED);
}
/**
- * Adds a NodeEntry. If an entry with the given index
- * already exists, the the new sibling is inserted before.
+ * Adds a NodeEntry.
+ * Note the following special cases:
+ *
NodeEntry to add.
*/
void add(NodeEntry cne, int index) {
QName nodeName = cne.getQName();
- // retrieve ev. sibling node with same index
- // if index is 'undefined' behave just as '#add(NodeEntry).
+ // retrieve ev. sibling node with same index if index is 'undefined'
+ // the existing entry is always null and no reordering occur.
LinkedEntries.LinkNode existing = (index < Path.INDEX_DEFAULT) ? null : getLinkNode(nodeName, index);
+ // in case index greater than default -> make sure all intermediate
+ // entries exist.
+ if (index > Path.INDEX_DEFAULT) {
+ int previousIndex = index - 1;
+ LinkedEntries.LinkNode previous = getLinkNode(nodeName, previousIndex);
+ if (previous == null) {
+ // add missing entry (or entries)
+ parent.addNodeEntry(nodeName, null, previousIndex);
+
+ } // else: all intermediate entries exist
+ } // else: undefined or default index are not affected
+
// add new entry (same as #add(NodeEntry)
List siblings = null;
Object obj = nameMap.get(nodeName);
@@ -335,8 +336,8 @@
nameMap.put(nodeName, ln);
}
- // if new entry must be inserted instead of appended at the end
- // reorder entries now
+ // reorder the child entries if, the new entry must be inserted rather
+ // than appended at the end of the list.
if (existing != null) {
reorder(obj, ln, existing);
}
@@ -430,25 +431,27 @@
* @param insertNode the NodeEntry to move.
* @param beforeNode the NodeEntry where insertNode is
* reordered to.
+ * @return the NodeEntry that followed the 'insertNode' before the reordering.
* @throws NoSuchElementException if insertNode or
* beforeNode does not have a NodeEntry
* in this ChildNodeEntries.
*/
- boolean reorder(NodeEntry insertNode, NodeEntry beforeNode) {
+ NodeEntry reorder(NodeEntry insertNode, NodeEntry beforeNode) {
Object insertObj = nameMap.get(insertNode.getQName());
// the link node to move
LinkedEntries.LinkNode insertLN = getLinkNode(insertNode);
if (insertLN == null) {
- return false;
+ throw new NoSuchElementException();
}
// the link node where insertLN is ordered before
LinkedEntries.LinkNode beforeLN = (beforeNode != null) ? getLinkNode(beforeNode) : null;
if (beforeNode != null && beforeLN == null) {
- return false;
+ throw new NoSuchElementException();
}
+ NodeEntry previousBefore = insertLN.getNextLinkNode().getNodeEntry();
reorder(insertObj, insertLN, beforeLN);
- return true;
+ return previousBefore;
}
/**
@@ -483,9 +486,7 @@
}
}
}
- } else {
- // no same name siblings -> nothing to do.
- }
+ } // else: no same name siblings -> no special handling required
// reorder in linked list
entries.reorderNode(insertLN, beforeLN);
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java Thu Mar 1 03:17:22 2007
@@ -68,17 +68,10 @@
return isf;
}
+
public void notifyIdChange(NodeEntry entry, String previousUniqueID) {
listener.uniqueIdChanged(entry, previousUniqueID);
}
-
- public void notifyEntryMoved(NodeEntry old, NodeEntry newEntry) {
- // TODO: check if correct
- if (old.getUniqueID() != null) {
- listener.uniqueIdChanged(newEntry, old.getUniqueID());
- }
- }
-
//--------------------------------------------------------------------------
public interface NodeEntryListener {
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java Thu Mar 1 03:17:22 2007
@@ -350,20 +350,32 @@
if (state == null) {
// nothing to do -> correct status must be set upon resolution.
return;
- } else {
- state.checkIsSessionState();
- if (!state.isValid()) {
- throw new ItemStateException("Cannot remove an invalid ItemState");
+ }
+
+ state.checkIsSessionState();
+ // if during recursive removal an invalidated entry is found, reload
+ // it in order to determine the current status.
+ if (state.getStatus() == Status.INVALIDATED) {
+ reload(false, false);
+ // check if upon reload the item has been removed -> nothing to do
+ if (Status.isTerminal(state.getStatus())) {
+ return;
}
- int oldStatus = state.getStatus();
- if (oldStatus == Status.NEW) {
+ }
+
+ switch (state.getStatus()) {
+ case Status.NEW:
remove();
- } else {
+ break;
+ case Status.EXISTING:
+ case Status.EXISTING_MODIFIED:
state.setStatus(Status.EXISTING_REMOVED);
// NOTE: parent does not need to be informed. an transiently
// removed propertyEntry is automatically moved to the 'attic'
// if a conflict with a new entry occurs.
- }
+ break;
+ default:
+ throw new ItemStateException("Cannot transiently remove an ItemState with status " + Status.getName(state.getStatus()));
}
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManager.java Thu Mar 1 03:17:22 2007
@@ -41,15 +41,18 @@
public NodeEntry getRootEntry();
/**
- * If the Hierarchy already lists the entry with the given itemId it is
+ * Lookup of HierarchyEntry workspace Id, that may be different
+ * if a entry (or any of its ancestors) has been transiently moved or
+ * reordered.
+ * If the Hierarchy already lists the entry with the given workspaceItemId it is
* returned otherwise null. See {@link #getHierarchyEntry(ItemId)}
* for a method that resolves the ItemId including lookup in the persistence
* layer if the entry has not been loaded yet.
*
- * @param itemId
+ * @param workspaceItemId
* @return
*/
- public HierarchyEntry lookup(ItemId itemId);
+ public HierarchyEntry lookup(ItemId workspaceItemId);
/**
* Resolves a itemId into a HierarchyEntry.
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyManagerImpl.java Thu Mar 1 03:17:22 2007
@@ -64,16 +64,17 @@
/**
* @see HierarchyManager#lookup(ItemId)
*/
- public HierarchyEntry lookup(ItemId itemId) {
- String uniqueID = itemId.getUniqueID();
+ public HierarchyEntry lookup(ItemId workspaceItemId) {
+ String uniqueID = workspaceItemId.getUniqueID();
if (uniqueID == null) {
- return PathResolver.lookup(rootEntry, itemId.getPath());
+ return rootEntry.lookupDeepEntry(workspaceItemId.getPath());
} else {
NodeEntry nEntry = uniqueIdResolver.lookup(idFactory.createNodeId(uniqueID));
- if (itemId.getPath() == null) {
+ Path path = workspaceItemId.getPath();
+ if (path == null) {
return nEntry;
} else {
- return PathResolver.lookup(nEntry, itemId.getPath());
+ return nEntry.lookupDeepEntry(path);
}
}
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntry.java Thu Mar 1 03:17:22 2007
@@ -45,6 +45,17 @@
public NodeId getId();
/**
+ * Returns the ID that must be used for resolving this entry OR loading its
+ * children entries from the persistent layer. This is the same as
+ * getId() unless this entry or any of its ancestors has been
+ * transiently moved.
+ *
+ * @return
+ * @see #getId()
+ */
+ public NodeId getWorkspaceId();
+
+ /**
* @return the unique ID of the node state which is referenced by this
* child node entry or null if the node state cannot be
* identified with a unique ID.
@@ -86,6 +97,17 @@
public HierarchyEntry getDeepEntry(Path path) throws PathNotFoundException, RepositoryException;
/**
+ * Traverse the tree below this entry and return the child entry matching
+ * the given 'workspacePath', i.e. transient modifications and new entries
+ * are ignored.
+ * If no matching entry can be found, null is return.
+ *
+ * @param workspacePath
+ * @return matching entry or null.
+ */
+ public HierarchyEntry lookupDeepEntry(Path workspacePath);
+
+ /**
* Determines if there is a valid NodeEntry with the
* specified nodeName.
*
@@ -157,18 +179,16 @@
/**
* Adds a new, transient child NodeEntry
*
+ * @param nodeName
+ * @param uniqueID
+ * @param primaryNodeType
+ * @param definition
* @return
+ * @throws ItemExistsException
*/
public NodeState addNewNodeEntry(QName nodeName, String uniqueID, QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException;
/**
- * @param newName
- * @param newParent
- * @return
- */
- public NodeEntry moveNodeEntry(NodeState childState, QName newName, NodeEntry newParent) throws RepositoryException;
-
- /**
* Determines if there is a property entry with the specified QName.
*
* @param propName QName object specifying a property name
@@ -226,10 +246,20 @@
*
* @param beforeEntry the child node where to insert the node before. If
* null this entry is moved to the end of its parents child node entries.
- * @return true if the reordering was successful. False if either of the
- * given entry does not exist in the listed child entries..
*/
- public boolean orderBefore(NodeEntry beforeEntry) ;
+ public void orderBefore(NodeEntry beforeEntry) ;
+
+ /**
+ * @param newName
+ * @param newParent
+ * @return
+ */
+ public NodeEntry move(QName newName, NodeEntry newParent, boolean transientMove) throws RepositoryException;
+
+ /**
+ * @return true if this NodeEntry is transiently moved.
+ */
+ public boolean isTransientlyMoved();
/**
* The parent entry of a external event gets informed about the modification.
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java Thu Mar 1 03:17:22 2007
@@ -35,6 +35,7 @@
import org.apache.jackrabbit.jcr2spi.state.StaleItemStateException;
import org.apache.jackrabbit.jcr2spi.state.Status;
import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateLifeCycleListener;
import org.apache.jackrabbit.jcr2spi.util.StateUtility;
import org.apache.commons.collections.iterators.IteratorChain;
import org.slf4j.Logger;
@@ -51,6 +52,8 @@
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
+import java.util.Map;
+import java.util.LinkedHashMap;
/**
* NodeEntryImpl implements common functionality for child
@@ -58,8 +61,6 @@
*/
public class NodeEntryImpl extends HierarchyEntryImpl implements NodeEntry {
- // TODO: TOBEFIXED attic for transiently removed or moved child-node-entries required, in order 'know' that they are remove instead of retrieving them from the server again.
-
private static Logger log = LoggerFactory.getLogger(NodeEntryImpl.class);
/**
@@ -75,6 +76,12 @@
private ChildNodeEntries childNodeEntries;
/**
+ * Map used to remember transiently removed or moved childNodeEntries, that
+ * must not be retrieved from the persistent storage.
+ */
+ private ChildNodeAttic childNodeAttic;
+
+ /**
* Map of properties. Key = {@link QName} of property. Value = {@link
* PropertyEntry}.
*/
@@ -87,6 +94,18 @@
private final HashMap propertiesInAttic = new HashMap();
/**
+ * Upon transient 'move' ('rename') or 'reorder' of SNSs this
+ * NodeEntry remembers the original parent, name and index
+ * for later revert as well as for the creation of the
+ * {@link #getWorkspaceId() workspace id}. Finally the revertInfo is
+ * used to find the target of an Event indicating external
+ * modification.
+ *
+ * @see #refresh(Event)
+ */
+ private RevertInfo revertInfo;
+
+ /**
* Creates a new NodeEntryImpl
*
* @param parent the NodeEntry that owns this child item
@@ -97,6 +116,7 @@
NodeEntryImpl(NodeEntryImpl parent, QName name, String uniqueID, EntryFactory factory) {
super(parent, name, factory);
this.uniqueID = uniqueID; // NOTE: don't use setUniqueID (for mod only)
+ this.childNodeAttic = new ChildNodeAttic();
}
/**
@@ -161,6 +181,7 @@
* Calls {@link HierarchyEntryImpl#revert()} and moves all properties from the
* attic back into th properties map. If this HierarchyEntry has been
* transiently moved, it is in addition moved back to its old parent.
+ * Similarly reordering of child node entries is reverted.
*
* @inheritDoc
* @see HierarchyEntry#revert()
@@ -171,16 +192,9 @@
properties.putAll(propertiesInAttic);
propertiesInAttic.clear();
}
- // if this entry has been moved before -> move back
- NodeState state = (NodeState) internalGetItemState();
- if (state != null && StateUtility.isMovedState(state)) {
- // move NodeEntry back to its original parent
- parent.childNodeEntries().remove(this);
- NodeEntryImpl oldEntry = (NodeEntryImpl) state.getWorkspaceState().getHierarchyEntry();
- oldEntry.parent.childNodeEntries().add(oldEntry);
- factory.notifyEntryMoved(this, oldEntry);
- }
+ revertTransientChanges();
+
// now make sure the underlying state is reverted to the original state
super.revert();
}
@@ -267,12 +281,30 @@
// root node
return idFactory.createNodeId((String) null, Path.ROOT);
} else {
- return factory.getIdFactory().createNodeId(parent.getId(), Path.create(getQName(), getIndex()));
+ return idFactory.createNodeId(parent.getId(), Path.create(getQName(), getIndex()));
}
}
}
/**
+ * @see NodeEntry#getWorkspaceId()
+ */
+ public NodeId getWorkspaceId() {
+ IdFactory idFactory = factory.getIdFactory();
+ if (uniqueID != null || parent == null) {
+ // uniqueID and root-node -> internal id is always the same as getId().
+ return getId();
+ } else if (revertInfo != null) {
+ NodeId parentId = revertInfo.oldParent.getWorkspaceId();
+ Path path = Path.create(revertInfo.oldName, revertInfo.oldIndex);
+ return idFactory.createNodeId(parentId, path);
+ } else {
+ NodeId parentId = parent.getWorkspaceId();
+ return idFactory.createNodeId(parentId, Path.create(getQName(), getIndex()));
+ }
+ }
+
+ /**
* @inheritDoc
* @see NodeEntry#getUniqueID()
*/
@@ -353,10 +385,20 @@
} else if (index == Path.INDEX_DEFAULT && entry.properties.containsKey(name)
&& i == path.getLength() - 1) {
// property must not have index && must be final path element
- PropertyEntry pe = (PropertyEntry) entry.properties.get(name);
- return pe;
+ return (PropertyEntry) entry.properties.get(name);
} else {
- /*
+ // no valid entry
+ // -> check for moved child entry in node-attic
+ // -> check if child points to a removed sns
+ if (entry.childNodeAttic.contains(name, index)) {
+ throw new PathNotFoundException(path.toString());
+ } else if (entry.childNodeEntries != null) {
+ int noSNS = entry.childNodeEntries.get(name).size() + entry.childNodeAttic.get(name).size();
+ if (index <= noSNS) {
+ throw new PathNotFoundException(path.toString());
+ }
+ }
+ /*
* Unknown entry (not-existing or not yet loaded):
* Skip all intermediate entries and directly try to load the ItemState
* (including building the itermediate entries. If that fails
@@ -409,6 +451,40 @@
}
/**
+ * @see NodeEntry#lookupDeepEntry(Path)
+ */
+ public HierarchyEntry lookupDeepEntry(Path workspacePath) {
+ NodeEntryImpl entry = this;
+ for (int i = 0; i < workspacePath.getLength(); i++) {
+ Path.PathElement elem = workspacePath.getElement(i);
+ // check for root element
+ if (elem.denotesRoot()) {
+ if (getParent() != null) {
+ log.warn("NodeEntry out of 'hierarchy'" + workspacePath.toString());
+ return null;
+ } else {
+ continue;
+ }
+ }
+
+ int index = elem.getNormalizedIndex();
+ QName childName = elem.getName();
+
+ // first try to resolve node
+ NodeEntry cne = entry.lookupNodeEntry(childName, index);
+ if (cne != null) {
+ entry = (NodeEntryImpl) cne;
+ } else if (index == Path.INDEX_DEFAULT && i == workspacePath.getLength() - 1) {
+ // property must not have index && must be final path element
+ return entry.lookupPropertyEntry(childName);
+ } else {
+ return null;
+ }
+ }
+ return entry;
+ }
+
+ /**
* @inheritDoc
* @see NodeEntry#hasNodeEntry(QName)
*/
@@ -525,7 +601,8 @@
* @inheritDoc
* @see NodeEntry#addNewNodeEntry(QName, String, QName, QNodeDefinition)
*/
- public NodeState addNewNodeEntry(QName nodeName, String uniqueID, QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException {
+ public NodeState addNewNodeEntry(QName nodeName, String uniqueID,
+ QName primaryNodeType, QNodeDefinition definition) throws ItemExistsException {
NodeEntryImpl entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED, childNodeEntries());
NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType, definition);
entry.internalSetItemState(state);
@@ -540,32 +617,14 @@
* @param childEntries
* @return
*/
- private NodeEntryImpl internalAddNodeEntry(QName nodeName, String uniqueID, int index, ChildNodeEntries childEntries) {
+ private NodeEntryImpl internalAddNodeEntry(QName nodeName, String uniqueID,
+ int index, ChildNodeEntries childEntries) {
NodeEntryImpl entry = new NodeEntryImpl(this, nodeName, uniqueID, factory);
childEntries.add(entry, index);
return entry;
}
/**
- * @see NodeEntry#moveNodeEntry(NodeState, QName, NodeEntry)
- */
- public NodeEntry moveNodeEntry(NodeState childState, QName newName, NodeEntry newParent) throws RepositoryException {
- NodeEntry oldEntry = childNodeEntries().remove(childState.getNodeEntry());
- if (oldEntry != null) {
- NodeEntryImpl movedEntry = (NodeEntryImpl) newParent.addNodeEntry(newName, oldEntry.getUniqueID(), Path.INDEX_UNDEFINED);
- movedEntry.internalSetItemState(childState);
-
- factory.notifyEntryMoved(oldEntry, movedEntry);
- return movedEntry;
- } else {
- // should never occur
- String msg = "Internal error. Attempt to move NodeEntry (" + childState + ") which is not connected to its parent.";
- log.error(msg);
- throw new RepositoryException(msg);
- }
- }
-
- /**
* @inheritDoc
* @see NodeEntry#hasPropertyEntry(QName)
*/
@@ -717,8 +776,56 @@
* @inheritDoc
* @see NodeEntry#orderBefore(NodeEntry)
*/
- public boolean orderBefore(NodeEntry beforeEntry) {
- return parent.childNodeEntries().reorder(this, beforeEntry);
+ public void orderBefore(NodeEntry beforeEntry) {
+ if (Status.NEW == getStatus()) {
+ // new states get remove upon revert
+ parent.childNodeEntries().reorder(this, beforeEntry);
+ } else {
+ createSiblingRevertInfos();
+ parent.createRevertInfo();
+ // now reorder child entries on parent
+ NodeEntry previousBefore = parent.childNodeEntries().reorder(this, beforeEntry);
+ parent.revertInfo.reordered(this, previousBefore);
+ }
+ }
+
+ /**
+ * @see NodeEntry#move(QName, NodeEntry, boolean)
+ */
+ public NodeEntry move(QName newName, NodeEntry newParent, boolean transientMove) throws RepositoryException {
+ if (parent == null) {
+ // the root may never be moved
+ throw new RepositoryException("Root cannot be moved.");
+ }
+
+ // for existing nodeEntry that are 'moved' for the first time, the
+ // original data must be stored and this entry is moved to the attic.
+ if (transientMove && !isTransientlyMoved() && Status.NEW != getStatus()) {
+ createSiblingRevertInfos();
+ createRevertInfo();
+ parent.childNodeAttic.add(this);
+ }
+
+ NodeEntryImpl entry = (NodeEntryImpl) parent.childNodeEntries().remove(this);
+ if (entry != this) {
+ // should never occur
+ String msg = "Internal error. Attempt to move NodeEntry (" + getQName() + ") which is not connected to its parent.";
+ log.error(msg);
+ throw new RepositoryException(msg);
+ }
+ // set name and parent to new values
+ parent = (NodeEntryImpl) newParent;
+ name = newName;
+ // register entry with its new parent
+ parent.childNodeEntries().add(this);
+ return this;
+ }
+
+ /**
+ * @see NodeEntry#isTransientlyMoved()
+ */
+ public boolean isTransientlyMoved() {
+ return revertInfo != null && revertInfo.isMoved();
}
/**
@@ -731,10 +838,18 @@
switch (childEvent.getType()) {
case Event.NODE_ADDED:
int index = childEvent.getQPath().getNameElement().getNormalizedIndex();
- String uniqueChildID = (childEvent.getItemId().getPath() == null) ? childEvent.getItemId().getUniqueID() : null;
+ String uniqueChildID = null;
+ if (childEvent.getItemId().getPath() == null) {
+ uniqueChildID = childEvent.getItemId().getUniqueID();
+ }
// first check if no matching child entry exists.
// TODO: TOBEFIXED for SNSs
- NodeEntry cne = (uniqueChildID != null) ? childNodeEntries().get(eventName, uniqueChildID) : childNodeEntries().get(eventName, index);
+ NodeEntry cne;
+ if (uniqueChildID != null) {
+ cne = childNodeEntries().get(eventName, uniqueChildID);
+ } else {
+ cne = childNodeEntries().get(eventName, index);
+ }
if (cne == null) {
cne = internalAddNodeEntry(eventName, uniqueChildID, index, childNodeEntries());
modified = true;
@@ -764,7 +879,7 @@
case Event.NODE_REMOVED:
case Event.PROPERTY_REMOVED:
- HierarchyEntry child = getEntryForExternalEvent(childEvent.getItemId(), childEvent.getQPath());
+ HierarchyEntry child = lookupEntry(childEvent.getItemId(), childEvent.getQPath());
if (child != null) {
child.remove();
modified = true;
@@ -772,7 +887,7 @@
break;
case Event.PROPERTY_CHANGED:
- child = getEntryForExternalEvent(childEvent.getItemId(), childEvent.getQPath());
+ child = lookupEntry(childEvent.getItemId(), childEvent.getQPath());
if (child != null) {
// Reload data from server and try to merge them with the
// current session-state. if the latter is transiently
@@ -802,8 +917,8 @@
// TODO: check if status of THIS_state must be marked modified...
}
-
- //------------------------------------------------------< HierarchyEntryImpl >---
+
+ //-------------------------------------------------< HierarchyEntryImpl >---
/**
* @inheritDoc
* @see HierarchyEntryImpl#doResolve()
@@ -812,7 +927,7 @@
*/
ItemState doResolve()
throws NoSuchItemStateException, ItemStateException {
- return factory.getItemStateFactory().createNodeState(getId(), this);
+ return factory.getItemStateFactory().createNodeState((NodeId) getWorkspaceId(), this);
}
//-----------------------------------------------< private || protected >---
@@ -831,6 +946,33 @@
}
/**
+ *
+ * @param oldName
+ * @param oldIndex
+ * @return
+ */
+ boolean matches(QName oldName, int oldIndex) {
+ if (revertInfo != null) {
+ return revertInfo.oldName.equals(oldName) && revertInfo.oldIndex == oldIndex;
+ } else {
+ return getQName().equals(oldName) && getIndex() == oldIndex;
+ }
+ }
+
+ /**
+ *
+ * @param oldName
+ * @return
+ */
+ boolean matches(QName oldName) {
+ if (revertInfo != null) {
+ return revertInfo.oldName.equals(oldName);
+ } else {
+ return getQName().equals(oldName);
+ }
+ }
+
+ /**
* Searches the child-entries of this NodeEntry for a matching child.
* Since {@link #refresh(Event)} must always be called on the parent
* NodeEntry, there is no need to check if a given event id would point
@@ -840,25 +982,25 @@
* @param eventPath
* @return
*/
- private HierarchyEntry getEntryForExternalEvent(ItemId eventId, Path eventPath) {
+ private HierarchyEntry lookupEntry(ItemId eventId, Path eventPath) {
QName childName = eventPath.getNameElement().getName();
HierarchyEntry child = null;
if (eventId.denotesNode()) {
String uniqueChildID = (eventId.getPath() == null) ? eventId.getUniqueID() : null;
+ // for external node-removal the attic must be consulted first
+ // in order to be able to apply the changes to the proper node-entry.
if (uniqueChildID != null) {
- child = childNodeEntries().get(childName, uniqueID);
+ child = childNodeAttic.get(uniqueChildID);
+ if (child == null && childNodeEntries != null) {
+ child = childNodeEntries.get(childName, uniqueChildID);
+ }
}
if (child == null) {
- child = childNodeEntries().get(childName, eventPath.getNameElement().getNormalizedIndex());
+ int index = eventPath.getNameElement().getNormalizedIndex();
+ child = lookupNodeEntry(childName, index);
}
} else {
- // for external prop-removal the attic must be consulted first
- // in order not access a NEW prop shadowing a transiently removed
- // property with the same name.
- child = (HierarchyEntry) propertiesInAttic.get(childName);
- if (child == null) {
- child = (HierarchyEntry) properties.get(childName);
- }
+ child = lookupPropertyEntry(childName);
}
if (child != null) {
// a NEW hierarchyEntry may never be affected by an external
@@ -871,6 +1013,32 @@
return child;
}
+ private NodeEntryImpl lookupNodeEntry(QName childName, int index) {
+ NodeEntryImpl child = (NodeEntryImpl) childNodeAttic.get(childName, index);
+ if (child == null && childNodeEntries != null) {
+ List namedChildren = childNodeEntries.get(childName);
+ for (Iterator it = namedChildren.iterator(); it.hasNext(); ) {
+ NodeEntryImpl c = (NodeEntryImpl) it.next();
+ if (c.matches(childName, index)) {
+ child = c;
+ break;
+ }
+ }
+ }
+ return child;
+ }
+
+ private PropertyEntry lookupPropertyEntry(QName childName) {
+ // for external prop-removal the attic must be consulted first
+ // in order not access a NEW prop shadowing a transiently removed
+ // property with the same name.
+ PropertyEntry child = (PropertyEntry) propertiesInAttic.get(childName);
+ if (child == null) {
+ child = (PropertyEntry) properties.get(childName);
+ }
+ return child;
+ }
+
/**
* Deals with modified jcr:uuid and jcr:mixinTypes property.
* See {@link #notifyUUIDorMIXINRemoved(QName)}
@@ -919,43 +1087,28 @@
*/
private ChildNodeEntries childNodeEntries() {
if (childNodeEntries == null) {
+ childNodeEntries = new ChildNodeEntries(this);
ItemState state = internalGetItemState();
- if (state != null) {
- if (state.getStatus() == Status.NEW) {
- childNodeEntries = new ChildNodeEntries(this);
- } else if (StateUtility.isMovedState((NodeState) state)) {
- // TODO: TOBEFIXED need to retrieve the original id. currently this will fail in case of SNS
- // since, the index cannot be determined from the original parent any more
- NodeId originalID = ((NodeState) state.getWorkspaceState()).getNodeId();
- childNodeEntries = loadChildNodeEntries(originalID);
- } else {
- childNodeEntries = loadChildNodeEntries(getId());
+ if (state == null || state.getStatus() != Status.NEW) {
+ try {
+ NodeId id = (NodeId) getWorkspaceId();
+ Iterator it = factory.getItemStateFactory().getChildNodeInfos(id);
+ while (it.hasNext()) {
+ ChildInfo ci = (ChildInfo) it.next();
+ internalAddNodeEntry(ci.getName(), ci.getUniqueID(), ci.getIndex(), childNodeEntries);
+ }
+ } catch (NoSuchItemStateException e) {
+ log.error("Cannot retrieve child node entries.", e);
+ // ignore (TODO correct?)
+ } catch (ItemStateException e) {
+ log.error("Cannot retrieve child node entries.", e);
+ // ignore (TODO correct?)
}
- } else {
- childNodeEntries = loadChildNodeEntries(getId());
}
}
return childNodeEntries;
}
- private ChildNodeEntries loadChildNodeEntries(NodeId id) {
- ChildNodeEntries cnes = new ChildNodeEntries(this);
- try {
- Iterator it = factory.getItemStateFactory().getChildNodeInfos(id);
- while (it.hasNext()) {
- ChildInfo ci = (ChildInfo) it.next();
- internalAddNodeEntry(ci.getName(), ci.getUniqueID(), ci.getIndex(), cnes);
- }
- } catch (NoSuchItemStateException e) {
- log.error("Cannot retrieve child node entries.", e);
- // ignore (TODO correct?)
- } catch (ItemStateException e) {
- log.error("Cannot retrieve child node entries.", e);
- // ignore (TODO correct?)
- }
- return cnes;
- }
-
/**
* Returns an Iterator over all children entries, that currently are loaded
* with this NodeEntry. NOTE, that if the childNodeEntries have not been
@@ -986,8 +1139,7 @@
its = new Iterator[] {properties.values().iterator(), children};
}
}
- IteratorChain chain = new IteratorChain(its);
- return chain;
+ return new IteratorChain(its);
}
/**
@@ -1014,5 +1166,197 @@
}
// not found (should not occur)
return Path.INDEX_UNDEFINED;
+ }
+
+ /**
+ * If 'revertInfo' is null it gets created from the current information
+ * present on this entry.
+ */
+ private void createRevertInfo() {
+ if (revertInfo == null) {
+ revertInfo = new RevertInfo(parent, name, getIndex());
+ }
+ }
+
+ /**
+ * Special handling for MOVE and REORDER with same-name-siblings
+ */
+ private void createSiblingRevertInfos() {
+ if (revertInfo != null) {
+ return; // nothing to do
+ }
+ // for SNSs without UniqueID remember original index in order to
+ // be able to build the workspaceID TODO: improve
+ List sns = parent.childNodeEntries().get(name);
+ if (sns.size() > 1) {
+ for (Iterator it = sns.iterator(); it.hasNext();) {
+ NodeEntryImpl sibling = (NodeEntryImpl) it.next();
+ if (sibling.getUniqueID() == null && Status.NEW != sibling.getStatus()) {
+ sibling.createRevertInfo();
+ }
+ }
+ }
+ }
+
+ /**
+ * Revert a transient move and reordering of child entries
+ */
+ private void revertTransientChanges() {
+ if (revertInfo == null) {
+ return; // nothing to do
+ }
+
+ if (isTransientlyMoved()) {
+ // move NodeEntry back to its original parent
+ // TODO improve for simple renaming
+ parent.childNodeEntries().remove(this);
+ revertInfo.oldParent.childNodeAttic.remove(this);
+
+ // now restore moved entry with the old name and index and re-add
+ // it to its original parent (unless it got destroyed)
+ parent = revertInfo.oldParent;
+ name = revertInfo.oldName;
+ ItemState state = internalGetItemState();
+ if (state != null && !Status.isTerminal(state.getStatus())) {
+ parent.childNodeEntries().add(this, revertInfo.oldIndex);
+ }
+ }
+ // revert reordering of child-node-entries
+ revertInfo.revertReordering();
+
+ revertInfo.dispose();
+ revertInfo = null;
+ }
+
+ /**
+ * This entry has be set to 'EXISTING' again -> move and/or reordering of
+ * child entries has been completed and the 'revertInfo' needs to be
+ * reset/removed.
+ */
+ private void completeTransientChanges() {
+ // old parent can forget this one
+ revertInfo.oldParent.childNodeAttic.remove(this);
+ revertInfo.dispose();
+ revertInfo = null;
+ }
+
+ //--------------------------------------------------------< inner class >---
+ /**
+ * Upon move of this entry or upon reorder of its child-entries store
+ * original hierarchy information for later revert and in order to be able
+ * to build the workspace id(s).
+ */
+ private class RevertInfo implements ItemStateLifeCycleListener {
+
+ private NodeEntryImpl oldParent;
+ private QName oldName;
+ private int oldIndex;
+
+ private Map reorderedChildren;
+
+ private RevertInfo(NodeEntryImpl oldParent, QName oldName, int oldIndex) {
+ this.oldParent = oldParent;
+ this.oldName = oldName;
+ this.oldIndex = oldIndex;
+
+ ItemState state = internalGetItemState();
+ if (state != null) {
+ state.addListener(this);
+ } // else: should never be null.
+ }
+
+ private void dispose() {
+ ItemState state = internalGetItemState();
+ if (state != null) {
+ state.removeListener(this);
+ }
+
+ if (reorderedChildren != null) {
+ // special handling of SNS-children TODO: improve
+ // since reordered sns-children are not marked modified (unless they
+ // got modified by some other action, their revertInfo
+ // must be disposed manually
+ for (Iterator it = reorderedChildren.keySet().iterator(); it.hasNext();) {
+ NodeEntry ne = (NodeEntry) it.next();
+ List sns = childNodeEntries.get(ne.getQName());
+ if (sns.size() > 1) {
+ for (Iterator snsIt = sns.iterator(); snsIt.hasNext();) {
+ NodeEntryImpl sibling = (NodeEntryImpl) snsIt.next();
+ if (sibling.revertInfo != null && Status.EXISTING == sibling.getStatus()) {
+ sibling.revertInfo.dispose();
+ sibling.revertInfo = null;
+ }
+ }
+ }
+ }
+ reorderedChildren.clear();
+ }
+ }
+
+ private boolean isMoved() {
+ return oldParent != getParent() || !getQName().equals(oldName);
+ }
+
+ private void reordered(NodeEntry insertEntry, NodeEntry previousBefore) {
+ if (reorderedChildren == null) {
+ reorderedChildren = new LinkedHashMap();
+ }
+ reorderedChildren.put(insertEntry, previousBefore);
+ }
+
+ private void revertReordering() {
+ if (reorderedChildren == null) {
+ return; // nothing to do
+ }
+ // revert all 'reorder' calls in in reverse other they were performed
+ NodeEntry[] reordered = (NodeEntry[]) reorderedChildren.keySet().toArray(new NodeEntry[reorderedChildren.size()]);
+ for (int i = reordered.length-1; i >= 0; i--) {
+ NodeEntry ordered = reordered[i];
+ if (isValidReorderedChild(ordered)) {
+ NodeEntry previousBefore = (NodeEntry) reorderedChildren.get(ordered);
+ if (previousBefore == null || isValidReorderedChild(previousBefore)) {
+ childNodeEntries.reorder(ordered, previousBefore);
+ }
+ }
+ }
+ }
+
+ private boolean isValidReorderedChild(NodeEntry child) {
+ if (Status.isTerminal(child.getStatus())) {
+ log.warn("Cannot revert reordering. 'previousBefore' does not exist any more.");
+ return false;
+ }
+ if (child.isTransientlyMoved()) {
+ // child has been moved away -> move back
+ try {
+ child.revert();
+ } catch (ItemStateException e) {
+ log.error("Internal error", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
+ */
+ public void statusChanged(ItemState state, int previousStatus) {
+ switch (state.getStatus()) {
+ case Status.EXISTING:
+ // stop listening
+ state.removeListener(this);
+ completeTransientChanges();
+ break;
+
+ case Status.REMOVED:
+ case Status.STALE_DESTROYED:
+ // stop listening
+ state.removeListener(this);
+ // remove from the attic
+ revertTransientChanges();
+ break;
+ }
+ }
}
}
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntry.java Thu Mar 1 03:17:22 2007
@@ -32,6 +32,17 @@
public PropertyId getId();
/**
+ * Returns the ID that must be used for resolving this entry OR loading its
+ * children entries from the persistent layer. This is the same as
+ * getId() unless any of its ancestors has been transiently
+ * moved.
+ *
+ * @return
+ * @see #getId()
+ */
+ public PropertyId getWorkspaceId();
+
+ /**
* @return the referenced PropertyState.
* @throws NoSuchItemStateException if the PropertyState does not
* exist anymore.
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?view=diff&rev=513279&r1=513278&r2=513279
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java Thu Mar 1 03:17:22 2007
@@ -23,16 +23,12 @@
import org.apache.jackrabbit.jcr2spi.state.ItemState;
import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* PropertyEntryImpl implements a reference to a property state.
*/
public class PropertyEntryImpl extends HierarchyEntryImpl implements PropertyEntry {
- private static Logger log = LoggerFactory.getLogger(PropertyEntryImpl.class);
-
/**
* Creates a new PropertyEntryImpl.
*
@@ -64,21 +60,27 @@
*
* Returns a PropertyState.
*/
- ItemState doResolve()
- throws NoSuchItemStateException, ItemStateException {
- return factory.getItemStateFactory().createPropertyState(getId(), this);
+ ItemState doResolve() throws NoSuchItemStateException, ItemStateException {
+ return factory.getItemStateFactory().createPropertyState(getWorkspaceId(), this);
}
//------------------------------------------------------< PropertyEntry >---
/**
- * @inheritDoc
+ * @see PropertyEntry#getId()
*/
public PropertyId getId() {
return factory.getIdFactory().createPropertyId(parent.getId(), getQName());
}
/**
- * @inheritDoc
+ * @see PropertyEntry#getWorkspaceId()
+ */
+ public PropertyId getWorkspaceId() {
+ return factory.getIdFactory().createPropertyId(parent.getWorkspaceId(), getQName());
+ }
+
+ /**
+ * @see PropertyEntry#getPropertyState()
*/
public PropertyState getPropertyState() throws NoSuchItemStateException, ItemStateException {
return (PropertyState) getItemState();