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: + *
    + *
  1. If an entry with the given index already exists, the the new sibling + * is inserted before.
  2. + *
  3. If the given index is bigger that the last entry in the siblings list, + * intermediate entries will be created.
  4. + *
* * @param cne the 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();