You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2007/02/13 10:31:53 UTC
svn commit: r506927 [2/8] - in /jackrabbit/trunk/contrib/spi: jcr2spi/
jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/
jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/
jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/ jcr2spi...
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/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=506927&r1=506926&r2=506927
==============================================================================
--- 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 Tue Feb 13 01:31:36 2007
@@ -17,11 +17,12 @@
package org.apache.jackrabbit.jcr2spi;
import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyManager;
import org.apache.jackrabbit.jcr2spi.state.UpdatableItemStateManager;
-import org.apache.jackrabbit.jcr2spi.state.ItemStateManager;
import org.apache.jackrabbit.jcr2spi.state.ItemState;
import org.apache.jackrabbit.jcr2spi.state.ItemStateValidator;
import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.jcr2spi.query.QueryManagerImpl;
import org.apache.jackrabbit.jcr2spi.operation.Move;
@@ -91,22 +92,14 @@
*/
private final WorkspaceManager wspManager;
- /**
- * The hierarchy manager that reflects workspace state only
- * (i.e. that is isolated from transient changes made through
- * the session).
- */
- private HierarchyManager hierManager;
private LockManager lockManager;
private ObservationManager obsManager;
private QueryManager qManager;
private VersionManager versionManager;
- private ItemStateValidator validator;
public WorkspaceImpl(String name, SessionImpl session, RepositoryConfig config, SessionInfo sessionInfo) throws RepositoryException {
this.name = name;
this.session = session;
-
wspManager = createManager(config.getRepositoryService(), sessionInfo, session.getCacheBehaviour(), config.getPollingInterval());
}
@@ -261,7 +254,7 @@
session.checkIsAlive();
if (qManager == null) {
qManager = new QueryManagerImpl(session, session.getLocalNamespaceMappings(),
- session.getItemManager(), session.getItemStateManager(), wspManager);
+ session.getItemManager(), session.getHierarchyManager(), wspManager);
}
return qManager;
}
@@ -366,17 +359,7 @@
* @see ManagerProvider#getHierarchyManager()
*/
public HierarchyManager getHierarchyManager() {
- if (hierManager == null) {
- hierManager = new HierarchyManagerImpl(getItemStateManager(), getNamespaceResolver());
- }
- return hierManager;
- }
-
- /**
- * @see ManagerProvider#getItemStateManager()
- */
- public ItemStateManager getItemStateManager() {
- return wspManager;
+ return wspManager.getHierarchyManager();
}
/**
@@ -433,17 +416,18 @@
UpdatableItemStateManager getUpdatableItemStateManager() {
return wspManager;
}
+
+ ItemStateFactory getItemStateFactory() {
+ return wspManager.getItemStateFactory();
+ }
/**
- * Validator for the <code>Workspace</code>. It contrast from {@link SessionImpl#getValidator()}
- * in terms of <code>HierarchyManager</code> and <code>ItemManager</code>.
+ * Returns the validator of the session
+ *
* @return validator
*/
private ItemStateValidator getValidator() {
- if (validator == null) {
- validator = new ItemStateValidator(getNodeTypeRegistry(), this);
- }
- return validator;
+ return session.getValidator();
}
//-----------------------------------------------------< initialization >---
@@ -468,7 +452,7 @@
* @return a new <code>LockManager</code> instance.
*/
protected LockManager createLockManager(WorkspaceManager wspManager, ItemManager itemManager) {
- LockManager lMgr = new LockManagerImpl(wspManager, itemManager);
+ LockManager lMgr = new LockManagerImpl(wspManager, itemManager, session.getCacheBehaviour());
session.addListener((LockManagerImpl) lMgr);
return lMgr;
}
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=506927&r1=506926&r2=506927
==============================================================================
--- 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 Tue Feb 13 01:31:36 2007
@@ -22,7 +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.NoSuchItemStateException;
import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
import org.apache.jackrabbit.jcr2spi.state.PropertyState;
import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
@@ -30,8 +29,8 @@
import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
import org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateFactory;
import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.state.ItemStateManager;
-import org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateManager;
+import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
+import org.apache.jackrabbit.jcr2spi.state.TransientISFactory;
import org.apache.jackrabbit.jcr2spi.operation.OperationVisitor;
import org.apache.jackrabbit.jcr2spi.operation.AddNode;
import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
@@ -58,9 +57,11 @@
import org.apache.jackrabbit.jcr2spi.security.AccessManager;
import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEventListener;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyManager;
+import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyManagerImpl;
import org.apache.jackrabbit.name.Path;
import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.MalformedPathException;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.SessionInfo;
import org.apache.jackrabbit.spi.NodeId;
@@ -105,7 +106,6 @@
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
-import java.util.Collection;
import java.util.Map;
import java.util.Collections;
import java.io.InputStream;
@@ -125,7 +125,8 @@
private final RepositoryService service;
private final SessionInfo sessionInfo;
- private final ItemStateManager cache;
+ private final ItemStateFactory isf;
+ private final HierarchyManager hierarchyManager;
private final CacheBehaviour cacheBehaviour;
private final NamespaceRegistryImpl nsRegistry;
@@ -164,13 +165,15 @@
this.sessionInfo = sessionInfo;
this.cacheBehaviour = cacheBehaviour;
- cache = createItemStateManager();
-
Map repositoryDescriptors = service.getRepositoryDescriptors();
-
nsRegistry = createNamespaceRegistry(repositoryDescriptors);
ntRegistry = createNodeTypeRegistry(nsRegistry, repositoryDescriptors);
changeFeed = createChangeFeed(pollingInterval);
+
+ TransientItemStateFactory stateFactory = createItemStateFactory(ntRegistry);
+ this.isf = stateFactory;
+ this.hierarchyManager = createHierarchyManager(stateFactory, service.getIdFactory());
+ createHierarchyListener(hierarchyManager);
}
public NamespaceRegistryImpl getNamespaceRegistryImpl() {
@@ -181,6 +184,10 @@
return ntRegistry;
}
+ public HierarchyManager getHierarchyManager() {
+ return hierarchyManager;
+ }
+
public String[] getWorkspaceNames() throws RepositoryException {
return service.getWorkspaceNames(sessionInfo);
}
@@ -189,6 +196,10 @@
return service.getIdFactory();
}
+ public ItemStateFactory getItemStateFactory() {
+ return isf;
+ }
+
public LockInfo getLockInfo(NodeId nodeId) throws LockException, RepositoryException {
return service.getLockInfo(sessionInfo, nodeId);
}
@@ -320,10 +331,27 @@
*
* @return
*/
- private ItemStateManager createItemStateManager() {
+ private TransientItemStateFactory createItemStateFactory(NodeTypeRegistry ntReg) {
ItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo, this);
- WorkspaceItemStateManager ism = new WorkspaceItemStateManager(this, cacheBehaviour, isf, service.getIdFactory());
- return ism;
+ TransientItemStateFactory tisf = new TransientISFactory(isf, ntReg);
+ return tisf;
+ }
+
+ /**
+ *
+ * @return
+ */
+ private HierarchyManager createHierarchyManager(TransientItemStateFactory tisf, IdFactory idFactory) {
+ return new HierarchyManagerImpl(tisf, idFactory);
+ }
+
+ /**
+ *
+ * @return
+ */
+ private InternalEventListener createHierarchyListener(HierarchyManager hierarchyMgr) {
+ InternalEventListener listener = new HierarchyEventListener(this, hierarchyMgr, cacheBehaviour);
+ return listener;
}
/**
@@ -390,62 +418,8 @@
return t;
}
- //---------------------------------------------------< ItemStateManager >---
- /**
- * @inheritDoc
- * @see ItemStateManager#getRootState()
- */
- public NodeState getRootState() throws ItemStateException {
- // retrieve through cache
- synchronized (cache) {
- return cache.getRootState();
- }
- }
-
- /**
- * @inheritDoc
- * @see ItemStateManager#getItemState(ItemId)
- */
- public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException {
- // retrieve through cache
- synchronized (cache) {
- return cache.getItemState(id);
- }
- }
-
- /**
- * @inheritDoc
- * @see ItemStateManager#hasItemState(ItemId)
- */
- public boolean hasItemState(ItemId id) {
- synchronized (cache) {
- return cache.hasItemState(id);
- }
- }
-
- /**
- * @inheritDoc
- * @see ItemStateManager#getReferingStates(NodeState)
- * @param nodeState
- */
- public Collection getReferingStates(NodeState nodeState) throws ItemStateException {
- synchronized (cache) {
- return cache.getReferingStates(nodeState);
- }
- }
-
- /**
- * @inheritDoc
- * @see ItemStateManager#hasReferingStates(NodeState)
- * @param nodeState
- */
- public boolean hasReferingStates(NodeState nodeState) {
- synchronized (cache) {
- return cache.hasReferingStates(nodeState);
- }
- }
-
- //------ updatable -:>> review ---------------------------------------------
+ //------------------------------------------< UpdatableItemStateManager >---
+ // TODO: review
/**
* Creates a new batch from the single workspace operation and executes it.
*
@@ -468,7 +442,7 @@
// execute operation and delegate invalidation of affected item
// states to the operation.
new OperationVisitorImpl(sessionInfo).execute(operation);
- operation.persisted();
+ operation.persisted(cacheBehaviour);
}
}
@@ -515,10 +489,11 @@
log.warn("Interrupted while waiting for external change thread to terminate.");
}
}
+ hierarchyManager.dispose();
try {
service.dispose(sessionInfo);
} catch (RepositoryException e) {
- log.warn("Exception while disposing session info: " + e);
+ log.warn("Exception while disposing session info: " + e);
}
}
//------------------------------------------------------< AccessManager >---
@@ -526,34 +501,14 @@
* @see AccessManager#isGranted(NodeState, Path, String[])
*/
public boolean isGranted(NodeState parentState, Path relPath, String[] actions) throws ItemNotFoundException, RepositoryException {
- // TODO: TOBEFIXED.
- ItemState wspState = parentState.getWorkspaceState();
- if (wspState == null) {
- Path.PathBuilder pb = new Path.PathBuilder();
- pb.addAll(relPath.getElements());
- while (wspState == null) {
- pb.addFirst(parentState.getQName());
-
- parentState = parentState.getParent();
- wspState = parentState.getWorkspaceState();
- }
- try {
- relPath = pb.getPath();
- } catch (MalformedPathException e) {
- throw new RepositoryException(e);
- }
- }
-
-
- if (wspState == null) {
- // internal error. should never occur
- throw new RepositoryException("Internal error: Unable to retrieve overlayed state in hierarchy.");
- } else {
- NodeId parentId = ((NodeState)parentState).getNodeId();
- // TODO: 'createNodeId' is basically wrong since isGranted is unspecific for any item.
- ItemId id = getIdFactory().createNodeId(parentId, relPath);
- return service.isGranted(sessionInfo, id, actions);
- }
+ // 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
+ // identified by the resulting id.
+ // the server must be able to deal with paths and with proper ids anyway.
+ // TODO: 'createNodeId' is basically wrong since isGranted is unspecific for any item.
+ ItemId id = getIdFactory().createNodeId(parentState.getNodeId(), relPath);
+ return service.isGranted(sessionInfo, id, actions);
}
/**
@@ -692,7 +647,7 @@
Iterator it = changeLog.getOperations();
while (it.hasNext()) {
Operation op = (Operation) it.next();
- log.info("executing: " + op);
+ log.debug("executing " + op.getName());
op.accept(this);
}
} finally {
@@ -708,7 +663,7 @@
* Executes the operations on the repository service.
*/
private void execute(Operation workspaceOperation) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
- log.info("executing: " + workspaceOperation);
+ log.debug("executing " + workspaceOperation.getName());
workspaceOperation.accept(this);
}
@@ -729,7 +684,6 @@
public void visit(AddProperty operation) throws RepositoryException {
NodeId parentId = operation.getParentState().getNodeId();
QName propertyName = operation.getPropertyName();
- int type = operation.getPropertyType();
if (operation.isMultiValued()) {
batch.addProperty(parentId, propertyName, operation.getValues());
} else {
@@ -892,12 +846,11 @@
*/
public void visit(ResolveMergeConflict operation) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
try {
- NodeId nId = operation.getNodeState().getNodeId();
+ NodeState nState = operation.getNodeState();
+ NodeId nId = nState.getNodeId();
NodeId vId = operation.getVersionState().getNodeId();
- PropertyState mergeFailedState = (PropertyState) cache.getItemState(
- getIdFactory().createPropertyId(nId, QName.JCR_MERGEFAILED));
-
+ PropertyState mergeFailedState = nState.getPropertyState(QName.JCR_MERGEFAILED);
QValue[] vs = mergeFailedState.getValues();
NodeId[] mergeFailedIds = new NodeId[vs.length - 1];
@@ -911,9 +864,7 @@
// part of 'jcr:mergefailed' any more
}
- PropertyState predecessorState = (PropertyState) cache.getItemState(
- getIdFactory().createPropertyId(nId, QName.JCR_PREDECESSORS));
-
+ PropertyState predecessorState = nState.getPropertyState(QName.JCR_PREDECESSORS);
vs = predecessorState.getValues();
boolean resolveDone = operation.resolveDone();
Added: 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=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,841 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.jcr2spi.hierarchy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
+import org.apache.commons.collections.list.AbstractLinkedList;
+import org.apache.commons.collections.iterators.UnmodifiableIterator;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Collections;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+/**
+ * <code>ChildNodeEntries</code> represents an insertion-ordered collection of
+ * <code>NodeEntry</code>s that also maintains the index values of same-name
+ * siblings on insertion and removal.
+ * <p/>
+ * <code>ChildNodeEntries</code> also provides an unmodifiable
+ * <code>Collection</code> view.
+ */
+final class ChildNodeEntries implements Collection {
+
+ private static Logger log = LoggerFactory.getLogger(ChildNodeEntries.class);
+
+ private final NodeEntryImpl parent;
+
+ /**
+ * Linked list of {@link NodeEntry} instances.
+ */
+ private final ChildNodeEntries.LinkedEntries entries = new LinkedEntries();
+
+ /**
+ * map used for lookup by name
+ * (key=name, value=either a single {@link AbstractLinkedList.Node} or a
+ * list of {@link AbstractLinkedList.Node}s which are sns entries)
+ */
+ private final Map nameMap = new HashMap();
+
+ /**
+ * Create <code>ChildNodeEntries</code> for the given node state.
+ *
+ * @param parent
+ */
+ ChildNodeEntries(NodeEntryImpl parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Returns true, if this ChildNodeEntries contains a entry that matches
+ * the given name and either index or uniqueID:<br>
+ * If <code>uniqueID</code> is not <code>null</code> the given index is
+ * ignored since it is not required to identify a child node entry.
+ * Otherwise the given index is used.
+ *
+ * @param name
+ * @param index
+ * @param uniqueID
+ * @return
+ */
+ boolean contains(QName name, int index, String uniqueID) {
+ if (uniqueID == null) {
+ return contains(name, index);
+ } else {
+ return contains(name, uniqueID);
+ }
+ }
+
+ /**
+ *
+ * @param name
+ * @param index
+ * @return
+ */
+ private boolean contains(QName name, int index) {
+ if (!nameMap.containsKey(name) || index < Path.INDEX_DEFAULT) {
+ // no matching child node entry
+ return false;
+ }
+ Object o = nameMap.get(name);
+ if (o instanceof List) {
+ // SNS
+ int listIndex = index - 1;
+ return listIndex < ((List) o).size();
+ } else {
+ // single child node with this name -> matches only if request
+ // index equals the default-index
+ return index == Path.INDEX_DEFAULT;
+ }
+ }
+
+ /**
+ *
+ * @param name
+ * @param uniqueID
+ * @return
+ */
+ private boolean contains(QName name, String uniqueID) {
+ if (uniqueID == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!nameMap.containsKey(name)) {
+ // no matching child node entry
+ return false;
+ }
+ Object o = nameMap.get(name);
+ if (o instanceof List) {
+ // SNS
+ for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
+ ChildNodeEntries.LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ NodeEntry cne = n.getNodeEntry();
+ if (uniqueID.equals(cne.getUniqueID())) {
+ return true;
+ }
+ }
+ } else {
+ // single child node with this name
+ NodeEntry cne = ((ChildNodeEntries.LinkedEntries.LinkNode) o).getNodeEntry();
+ return uniqueID.equals(cne.getUniqueID());
+ }
+ // no matching entry found
+ return false;
+ }
+
+ /**
+ * Returns a <code>List</code> of <code>NodeEntry</code>s for the
+ * given <code>nodeName</code>. This method does <b>not</b> filter out
+ * removed <code>NodeEntry</code>s!
+ *
+ * @param nodeName the child node name.
+ * @return same name sibling nodes with the given <code>nodeName</code>.
+ */
+ List get(QName nodeName) {
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (obj instanceof List) {
+ final List sns = (List) obj;
+ // map entry is a list of siblings
+ return Collections.unmodifiableList(new AbstractList() {
+
+ public Object get(int index) {
+ return ((LinkedEntries.LinkNode) sns.get(index)).getNodeEntry();
+ }
+
+ public int size() {
+ return sns.size();
+ }
+
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private Iterator iter = sns.iterator();
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public Object next() {
+ return ((LinkedEntries.LinkNode) iter.next()).getNodeEntry();
+ }
+ };
+ }
+ });
+ } else {
+ // map entry is a single child node entry
+ return Collections.singletonList(((LinkedEntries.LinkNode) obj).getNodeEntry());
+ }
+ }
+
+ /**
+ * Returns the <code>NodeEntry</code> with the given
+ * <code>nodeName</code> and <code>index</code>. This method ignores
+ * <code>NodeEntry</code>s which are marked removed!
+ *
+ * @param nodeName name of the child node entry.
+ * @param index the index of the child node entry.
+ * @return the <code>NodeEntry</code> or <code>null</code> if there
+ * is no such <code>NodeEntry</code>.
+ */
+ NodeEntry get(QName nodeName, int index) {
+ if (index < Path.INDEX_DEFAULT) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof List) {
+ // map entry is a list of siblings
+ List siblings = (List) obj;
+ // filter out removed states
+ for (Iterator it = siblings.iterator(); it.hasNext(); ) {
+ NodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getNodeEntry();
+ if (cne.isAvailable()) {
+ try {
+ if (cne.getNodeState().isValid()) {
+ index--;
+ } else {
+ // child node removed
+ }
+ } catch (ItemStateException e) {
+ // should never happen, cne.isAvailable() returned true
+ }
+ } else {
+ // then this child node entry has never been accessed
+ // before and is assumed valid // TODO: check if correct.
+ index--;
+ }
+ if (index == 0) {
+ return cne;
+ }
+ }
+ } else {
+ // map entry is a single child node entry
+ if (index == Path.INDEX_DEFAULT) {
+ return ((LinkedEntries.LinkNode) obj).getNodeEntry();
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param nodeName
+ * @param uniqueID
+ * @return
+ * @throws IllegalArgumentException if the given uniqueID is null.
+ */
+ NodeEntry get(QName nodeName, String uniqueID) {
+ if (uniqueID == null) {
+ throw new IllegalArgumentException();
+ }
+ Iterator cneIter = (nodeName != null) ? get(nodeName).iterator() : entries.iterator();
+ while (cneIter.hasNext()) {
+ NodeEntry cne = (NodeEntry) cneIter.next();
+ if (uniqueID.equals(cne.getUniqueID())) {
+ return cne;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a <code>NodeEntry</code> to the end of the list.
+ *
+ * @param cne the <code>NodeEntry</code> 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);
+ }
+ }
+
+ /**
+ * Adds a <code>NodeEntry</code>. If an entry with the given index
+ * already exists, the the new sibling is inserted before.
+ *
+ * @param cne the <code>NodeEntry</code> 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).
+ LinkedEntries.LinkNode existing = (index < Path.INDEX_DEFAULT) ? null : getLinkNode(nodeName, index);
+
+ // add new entry (same as #add(NodeEntry)
+ 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);
+ }
+
+ // if new entry must be inserted instead of appended at the end
+ // reorder entries now
+ if (existing != null) {
+ reorder(obj, ln, existing);
+ }
+ }
+
+ /**
+ * Removes the child node entry with the given <code>nodeName</code> and
+ * <code>index</code>.
+ *
+ * @param nodeName the name of the child node entry to remove.
+ * @param index the index of the child node entry to remove.
+ * @return the removed <code>NodeEntry</code> or <code>null</code>
+ * if there is no matching <code>NodeEntry</code>.
+ */
+ NodeEntry remove(QName nodeName, int index) {
+ if (index < Path.INDEX_DEFAULT) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+
+ if (obj instanceof LinkedEntries.LinkNode) {
+ // map entry is a single child node entry
+ if (index != Path.INDEX_DEFAULT) {
+ return null;
+ }
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) obj;
+ nameMap.remove(nodeName);
+ // remove LinkNode from entries
+ ln.remove();
+ return ln.getNodeEntry();
+ }
+
+ // map entry is a list of siblings
+ List siblings = (List) obj;
+ if (index > siblings.size()) {
+ return null;
+ }
+
+ // remove from siblings list
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) siblings.remove(index - 1);
+ NodeEntry removedEntry = ln.getNodeEntry();
+ // remove from ordered entries
+ ln.remove();
+
+ // clean up name lookup map if necessary
+ if (siblings.size() == 0) {
+ // no more entries with that name left:
+ // remove from name lookup map as well
+ nameMap.remove(nodeName);
+ } else if (siblings.size() == 1) {
+ // just one entry with that name left:
+ // discard siblings list and update name lookup map accordingly
+ nameMap.put(nodeName, siblings.get(0));
+ }
+
+ // we're done
+ return removedEntry;
+ }
+
+ /**
+ * Removes the child node entry refering to the node state.
+ *
+ * @param childEntry the entry to be removed.
+ * @return the removed entry or <code>null</code> if there is no such entry.
+ */
+ NodeEntry remove(NodeEntry childEntry) {
+ NodeEntry entry = null;
+ for (Iterator it = get(childEntry.getQName()).iterator(); it.hasNext(); ) {
+ NodeEntry tmp = (NodeEntry) it.next();
+ if (tmp == childEntry) {
+ entry = tmp;
+ break;
+ }
+ }
+ if (entry != null) {
+ return remove(entry.getQName(), entry.getIndex());
+ }
+ return entry;
+ }
+
+ /**
+ * Reorders an existing <code>NodeState</code> before another
+ * <code>NodeState</code>. If <code>beforeNode</code> is
+ * <code>null</code> <code>insertNode</code> is moved to the end of the
+ * child node entries.
+ *
+ * @param insertNode the NodeEntry to move.
+ * @param beforeNode the NodeEntry where <code>insertNode</code> is
+ * reordered to.
+ * @throws NoSuchElementException if <code>insertNode</code> or
+ * <code>beforeNode</code> does not have a <code>NodeEntry</code>
+ * in this <code>ChildNodeEntries</code>.
+ */
+ boolean 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;
+ }
+ // the link node where insertLN is ordered before
+ LinkedEntries.LinkNode beforeLN = (beforeNode != null) ? getLinkNode(beforeNode) : null;
+ if (beforeNode != null && beforeLN == null) {
+ return false;
+ }
+
+ reorder(insertObj, insertLN, beforeLN);
+ return true;
+ }
+
+ /**
+ *
+ * @param insertObj
+ * @param insertLN
+ * @param beforeLN
+ */
+ private void reorder(Object insertObj, LinkedEntries.LinkNode insertLN, LinkedEntries.LinkNode beforeLN) {
+ if (insertObj instanceof List) {
+ // adapt name lookup lists
+ List insertList = (List) insertObj;
+ if (beforeLN == null) {
+ // simply move to end of list
+ insertList.remove(insertLN);
+ insertList.add(insertLN);
+ } else {
+ // move based on position of beforeLN
+ // count our same name siblings until we reach beforeLN
+ int snsCount = 0;
+ QName insertName = insertLN.getNodeEntry().getQName();
+ for (Iterator it = entries.linkNodeIterator(); it.hasNext(); ) {
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
+ if (ln == beforeLN) {
+ insertList.remove(insertLN);
+ insertList.add(snsCount, insertLN);
+ break;
+ } else if (ln == insertLN) {
+ // do not increment snsCount for node to reorder
+ } else if (ln.getNodeEntry().getQName().equals(insertName)) {
+ snsCount++;
+ }
+ }
+ }
+ } else {
+ // no same name siblings -> nothing to do.
+ }
+
+ // reorder in linked list
+ entries.reorderNode(insertLN, beforeLN);
+ }
+
+ /**
+ * Returns the matching <code>LinkNode</code> from a list or a single
+ * <code>LinkNode</code>. This method will throw <code>NoSuchItemStateException</code>
+ * if none of the entries matches either due to missing entry for given
+ * state name or due to missing availability of the <code>NodeEntry</code>.
+ *
+ * @param nodeEntry the <code>NodeEntry</code> that is compared to the
+ * resolution of any <code>NodeEntry</code> that matches by name.
+ * @return the matching <code>LinkNode</code>.
+ * @throws NoSuchElementException if none of the <code>LinkNode</code>s
+ * matches.
+ */
+ private LinkedEntries.LinkNode getLinkNode(NodeEntry nodeEntry) {
+ Object listOrLinkNode = nameMap.get(nodeEntry.getQName());
+ if (listOrLinkNode == null) {
+ // no matching child node entry
+ return null;
+ }
+
+ if (listOrLinkNode instanceof List) {
+ // has same name sibling
+ for (Iterator it = ((List) listOrLinkNode).iterator(); it.hasNext();) {
+ LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ NodeEntry cne = n.getNodeEntry();
+ // only check available child node entries
+ if (cne.isAvailable() && cne == nodeEntry) {
+ return n;
+ }
+ }
+ } else {
+ // single child node with this name
+ NodeEntry cne = ((LinkedEntries.LinkNode) listOrLinkNode).getNodeEntry();
+ if (cne.isAvailable() && cne == nodeEntry) {
+ return (LinkedEntries.LinkNode) listOrLinkNode;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ * Returns the matching <code>LinkNode</code> from a list or a single
+ * <code>LinkNode</code>. This method will return <code>null</code>
+ * if none of the entries matches.
+ *
+ * @param name
+ * @param index
+ * @return the matching <code>LinkNode</code> or <code>null</code>.
+ */
+ private LinkedEntries.LinkNode getLinkNode(QName name, int index) {
+ Object listOrLinkNode = nameMap.get(name);
+ if (listOrLinkNode == null) {
+ // no matching child node entry
+ return null;
+ }
+
+ if (listOrLinkNode instanceof List) {
+ // has same name sibling -> check if list size matches
+ int listIndex = index - 1;
+ List lnList = (List) listOrLinkNode;
+ if (listIndex < lnList.size()) {
+ return (LinkedEntries.LinkNode) lnList.get(listIndex);
+ }
+ } else if (index == Path.INDEX_DEFAULT) {
+ // single child node with this name -> matches is requested index
+ // equals to the default index.
+ return (LinkedEntries.LinkNode) listOrLinkNode;
+ }
+
+ // no matching entry
+ return null;
+ }
+ //--------------------------------------------< unmodifiable Collection >---
+ /**
+ * @see Collection#contains(Object)
+ */
+ public boolean contains(Object o) {
+ if (o instanceof NodeEntry) {
+ // narrow down to same name sibling nodes and check list
+ return get(((NodeEntry) o).getQName()).contains(o);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @see Collection#containsAll(Collection)
+ */
+ public boolean containsAll(Collection c) {
+ Iterator iter = c.iterator();
+ while (iter.hasNext()) {
+ if (!contains(iter.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @see Collection#isEmpty()
+ */
+ public boolean isEmpty() {
+ return entries.isEmpty();
+ }
+
+ /**
+ * @see Collection#iterator()
+ */
+ public Iterator iterator() {
+ return UnmodifiableIterator.decorate(entries.iterator());
+ }
+
+ /**
+ * @see Collection#size()
+ */
+ public int size() {
+ return entries.size();
+ }
+
+ /**
+ * @see Collection#toArray()
+ */
+ public Object[] toArray() {
+ NodeEntry[] array = new NodeEntry[size()];
+ return toArray(array);
+ }
+
+ /**
+ * @see Collection#toArray(Object[])
+ */
+ public Object[] toArray(Object[] a) {
+ if (!a.getClass().getComponentType().isAssignableFrom(NodeEntry.class)) {
+ throw new ArrayStoreException();
+ }
+ if (a.length < size()) {
+ a = new NodeEntry[size()];
+ }
+ Iterator iter = entries.iterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ a[i++] = iter.next();
+ }
+ while (i < a.length) {
+ a[i++] = null;
+ }
+ return a;
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#add(Object)
+ */
+ public boolean add(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#addAll(Collection)
+ */
+ public boolean addAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#clear()
+ */
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#remove(Object)
+ */
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#removeAll(Collection)
+ */
+ public boolean removeAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Throws <code>UnsupportedOperationException</code>.
+ *
+ * @see Collection#retainAll(Collection)
+ */
+ public boolean retainAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ //-------------------------------------------------< AbstractLinkedList >---
+ /**
+ * An implementation of a linked list which provides access to the internal
+ * LinkNode which links the entries of the list.
+ */
+ private static final class LinkedEntries extends AbstractLinkedList {
+
+ LinkedEntries() {
+ super();
+ init();
+ }
+
+ /**
+ * Adds a child node entry to this list.
+ *
+ * @param cne the child node entry to add.
+ * @return the LinkNode which refers to the added <code>NodeEntry</code>.
+ */
+ LinkedEntries.LinkNode add(NodeEntry cne) {
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) createNode(cne);
+ addNode(ln, header);
+ return ln;
+ }
+
+ /**
+ * Reorders an existing <code>LinkNode</code> before another existing
+ * <code>LinkNode</code>. If <code>before</code> is <code>null</code>
+ * the <code>insert</code> node is moved to the end of the list.
+ *
+ * @param insert the node to reorder.
+ * @param before the node where to reorder node <code>insert</code>.
+ */
+ void reorderNode(LinkedEntries.LinkNode insert, LinkedEntries.LinkNode before) {
+ removeNode(insert);
+ if (before == null) {
+ addNode(insert, header);
+ } else {
+ addNode(insert, before);
+ }
+ }
+
+ /**
+ * Replace the value of the given LinkNode with a new NodeEntry
+ * value.
+ *
+ * @param node
+ * @param value
+ */
+ void replaceNode(LinkedEntries.LinkNode node, NodeEntry value) {
+ updateNode(node, value);
+ }
+
+ /**
+ * Create a new <code>LinkNode</code> for a given {@link NodeEntry}
+ * <code>value</code>.
+ *
+ * @param value a child node entry.
+ * @return a wrapping {@link org.apache.jackrabbit.jcr2spi.hierarchy.ChildNodeEntries.LinkedEntries.LinkNode}.
+ * @see AbstractLinkedList#createNode(Object)
+ */
+ protected Node createNode(Object value) {
+ return new LinkedEntries.LinkNode(value);
+ }
+
+ /**
+ * @return a new <code>LinkNode</code>.
+ * @see AbstractLinkedList#createHeaderNode()
+ */
+ protected Node createHeaderNode() {
+ return new LinkedEntries.LinkNode();
+ }
+
+ /**
+ * Returns an iterator over all
+ * @return
+ */
+ Iterator linkNodeIterator() {
+ return new Iterator() {
+
+ private LinkedEntries.LinkNode next = ((LinkedEntries.LinkNode) header).getNextLinkNode();
+
+ private int expectedModCount = modCount;
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public boolean hasNext() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ return next != header;
+ }
+
+ public Object next() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ LinkedEntries.LinkNode n = next;
+ next = next.getNextLinkNode();
+ return n;
+ }
+ };
+ }
+
+ //----------------------------------------------------------------------
+
+ /**
+ * Extends the <code>AbstractLinkedList.Node</code>.
+ */
+ private final class LinkNode extends Node {
+
+ protected LinkNode() {
+ super();
+ }
+
+ protected LinkNode(Object value) {
+ super(value);
+ }
+
+ /**
+ * @return the wrapped <code>NodeEntry</code>.
+ */
+ public NodeEntry getNodeEntry() {
+ return (NodeEntry) super.getValue();
+ }
+
+ /**
+ * Removes this <code>LinkNode</code> from the linked list.
+ */
+ public void remove() {
+ removeNode(this);
+ }
+
+ /**
+ * @return the next LinkNode.
+ */
+ public LinkedEntries.LinkNode getNextLinkNode() {
+ return (LinkedEntries.LinkNode) super.getNextNode();
+ }
+ }
+ }
+}
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: 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=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,88 @@
+/*
+ * 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.jcr2spi.state.TransientItemStateFactory;
+import org.apache.jackrabbit.spi.IdFactory;
+
+/**
+ * <code>EntryFactory</code>...
+ */
+public class EntryFactory {
+
+ private static Logger log = LoggerFactory.getLogger(EntryFactory.class);
+
+ /**
+ * IdFactory to create an ItemId based on the parent NodeId.
+ */
+ private final IdFactory idFactory;
+
+ private final NodeEntry rootEntry;
+
+ /**
+ *
+ */
+ private final NodeEntryListener listener;
+
+ /**
+ * The item state factory to create the the item state.
+ */
+ private final TransientItemStateFactory isf;
+
+ public EntryFactory(TransientItemStateFactory isf, IdFactory idFactory, NodeEntryListener listener) {
+ this.idFactory = idFactory;
+ this.isf = isf;
+ this.listener = listener;
+ this.rootEntry = NodeEntryImpl.createRootEntry(this);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public NodeEntry createRootEntry() {
+ return rootEntry;
+ }
+
+ public IdFactory getIdFactory() {
+ return idFactory;
+ }
+
+ public TransientItemStateFactory getItemStateFactory() {
+ 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 {
+
+ public void uniqueIdChanged (NodeEntry entry, String previousUniqueID);
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java?view=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,112 @@
+/*
+ * 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.jcr2spi.state.ItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
+
+import java.util.Iterator;
+
+/**
+ * <code>EntryValidation</code>...
+ */
+final class EntryValidation {
+
+ private static Logger log = LoggerFactory.getLogger(EntryValidation.class);
+
+ /**
+ * Returns <code>true</code> if the collection of child node
+ * <code>entries</code> contains at least one valid <code>NodeEntry</code>.
+ *
+ * @param nodeEntries Iterator of NodeEntries to check.
+ * @return <code>true</code> if one of the entries is valid; otherwise
+ * <code>false</code>.
+ */
+ static boolean containsValidNodeEntry(Iterator nodeEntries) {
+ boolean hasValid = false;
+ while (nodeEntries.hasNext() && !hasValid) {
+ NodeEntry cne = (NodeEntry) nodeEntries.next();
+ hasValid = isValidNodeEntry(cne);
+ }
+ return hasValid;
+ }
+
+ /**
+ * Returns <code>true</code> if the given childnode entry is not
+ * <code>null</code> and resolves to a NodeState, that is valid or if the
+ * childnode entry has not been resolved up to now (assuming the corresponding
+ * nodestate is still valid).
+ *
+ * @param cne NodeEntry to check.
+ * @return <code>true</code> if the given entry is valid.
+ */
+ static boolean isValidNodeEntry(NodeEntry cne) {
+ // shortcut.
+ if (cne == null) {
+ return false;
+ }
+ boolean isValid = false;
+ if (cne.isAvailable()) {
+ try {
+ isValid = cne.getNodeState().isValid();
+ } catch (NoSuchItemStateException e) {
+ // may occur if the cached state is marked 'INVALIDATED' and
+ // does not exist any more on the persistent layer -> invalid.
+ } catch (ItemStateException e) {
+ // should not occur, if the cne is available.
+ }
+ } else {
+ // assume entry is valid
+ // TODO: check if this assumption is correct
+ isValid = true;
+ }
+
+ return isValid;
+ }
+
+ /**
+ * Returns <code>true</code> if the given childproperty entry is not
+ * <code>null</code> and resolves to a PropertyState, that is valid or if the
+ * childproperty entry has not been resolved up to now (assuming the corresponding
+ * PropertyState is still valid).
+ *
+ * @param cpe PropertyEntry to check.
+ * @return <code>true</code> if the given entry is valid.
+ */
+ static boolean isValidPropertyEntry(PropertyEntry cpe) {
+ if (cpe == null) {
+ return false;
+ }
+ boolean isValid = false;
+ if (cpe.isAvailable()) {
+ try {
+ isValid = cpe.getPropertyState().isValid();
+ } catch (NoSuchItemStateException e) {
+ // may occur if the cached state is marked 'INVALIDATED' and
+ // does not exist any more on the persistent layer -> invalid.
+ } catch (ItemStateException e) {
+ // probably deleted in the meantime. should not occur.
+ }
+ } else {
+ // assume entry is valid // TODO check if this assumption is correct.
+ isValid = true;
+ }
+ return isValid;
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryValidation.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java?view=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,165 @@
+/*
+ * 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.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
+import org.apache.jackrabbit.jcr2spi.state.StaleItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * <code>HierarchyEntry</code>...
+ */
+public interface HierarchyEntry {
+
+ /**
+ * True if this <code>HierarchyEntry</code> would resolve to a <code>NodeState</code>.
+ *
+ * @return
+ */
+ public boolean denotesNode();
+
+ /**
+ * @return the name of this hierarchy entry.
+ */
+ public QName getQName();
+
+ /**
+ * @return the path of this hierarchy entry.
+ */
+ public Path getPath() throws RepositoryException;
+
+ /**
+ * Returns the <code>NodeEntry</code> being parent to this
+ * <code>HierarchyEntry</code>.
+ *
+ * @return the parent <code>HierarchyEntry</code>
+ */
+ public NodeEntry getParent();
+
+ /**
+ * Unless this <code>HierarchyEntry</code> has been resolved this method
+ * returns {@link Status#_UNDEFINED_} otherwise it returns the status of
+ * the underlying ItemState.
+ *
+ * @return Status of the ItemState or {@link Status#_UNDEFINED_} if this
+ * entry has not been resolved yet.
+ * @see ItemState#getStatus()
+ */
+ public int getStatus();
+
+ /**
+ * Returns <code>true</code> if the referenced <code>ItemState</code> is
+ * available. That is, the referenced <code>ItemState</code> has already
+ * been resolved.<br>
+ * Note, that the validity of the ItemState is not checked.
+ *
+ * @return <code>true</code> if the <code>ItemState</code> is available;
+ * otherwise <code>false</code>.
+ * @see #getItemState()
+ */
+ public boolean isAvailable();
+
+ /**
+ * If this <code>HierarchyEntry</code> has already been resolved before
+ * (see {@link #isAvailable()}), that <code>ItemState</code> is returned.
+ * Note however, that the validity of the State is not asserted.<br>
+ * If the entry has not been resolved yet an attempt is made to resolve this
+ * entry, which may fail if there exists no accessible <code>ItemState</code>
+ * or if the corresponding state has been removed in the mean time.
+ *
+ * @return the referenced <code>ItemState</code>.
+ * @throws NoSuchItemStateException if the <code>ItemState</code> does not
+ * exist anymore.
+ * @throws ItemStateException If an error occurs while retrieving the
+ * <code>ItemState</code>.
+ */
+ public ItemState getItemState() throws NoSuchItemStateException, ItemStateException;
+
+ /**
+ * Invalidates the underlying <code>ItemState</code> if available. If the
+ * <code>recursive</code> flag is true, the hierarchy is traverses and
+ * {@link #invalidate(boolean)} is called on all child entries.<br>
+ * Note, that in contrast to {@link HierarchyEntry#reload(boolean, boolean)}
+ * this method only sets the status of this item state to {@link
+ * Status#INVALIDATED} and does not acutally update it with the persistent
+ * state in the repository.
+ */
+ public void invalidate(boolean recursive);
+
+ /**
+ * Traverses the hierarchy and reverts all transient modifications such as
+ * adding, modifying or removing item states. 'Existing' item states
+ * are reverted to their initial state and their status is reset to {@link Status#EXISTING}.
+ *
+ */
+ public void revert() throws ItemStateException;
+
+ /**
+ * Reloads this hierarchy entry and the corresponding ItemState, if this
+ * entry has already been resolved. If '<code>keepChanges</code>' is true,
+ * states with transient changes are left untouched in order to obtain stale
+ * item states. Otherwise this state gets its data reloaded from the
+ * persistent storage. If '<code>recursive</code>' the complete hierarchy
+ * below this entry is reloaded as well.
+ *
+ * @param keepChanges
+ */
+ public void reload(boolean keepChanges, boolean recursive);
+
+ /**
+ * Traverses the hierarchy and marks all available item states as transiently
+ * removed. They will change their status to either {@link Status#EXISTING_REMOVED} if
+ * the item is existing in the persistent storage or {@link Status#REMOVED}
+ * if the item has been transiently added before. In the latter case, the
+ * corresponding HierarchyEntries can be removed as well from their parent.
+ *
+ * @throws ItemStateException if an error occurs while removing any of the item
+ * state. e.g. an item state is not valid anymore.
+ */
+ public void transientRemove() throws ItemStateException;
+
+ /**
+ * Removes this <code>HierarchyEntry</code> from its parent and sets the
+ * status of the underlying ItemState to {@link Status#REMOVED} or to
+ * {@link Status#STALE_DESTROYED}, respectively. If this entry is a
+ * NodeEntry all descending ItemStates must get their status changed as well.
+ */
+ public void remove();
+
+ /**
+ * Checks if the underlying <code>ItemState</code> is available and if it
+ * has been transiently modified or if is new or stale modified. If either of
+ * the conditions is true, the state is added to the <code>ChangeLog</code>.
+ * If this <code>HierarchyEntry</code> has children it will call
+ * {@link #collectStates(ChangeLog, boolean)} recursively.
+ *
+ * @param changeLog the <code>ChangeLog</code> collecting the transient
+ * item states present in a given tree.
+ * @param throwOnStale If the given flag is true, this methods throws
+ * StaleItemStateException if this state is stale.
+ * @throws StaleItemStateException if <code>throwOnStale</code> is true and
+ * this state is stale.
+ */
+ public void collectStates(ChangeLog changeLog, boolean throwOnStale) throws StaleItemStateException;
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: 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=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,402 @@
+/*
+ * 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.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.NoSuchItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
+import org.apache.jackrabbit.jcr2spi.state.StaleItemStateException;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.lang.ref.WeakReference;
+
+/**
+ * <code>HierarchyEntryImpl</code> implements base functionality for child node
+ * and property references.
+ */
+abstract class HierarchyEntryImpl implements HierarchyEntry {
+
+ private static Logger log = LoggerFactory.getLogger(HierarchyEntryImpl.class);
+
+ /**
+ * Cached weak reference to the target ItemState.
+ * // TODO: check correct?
+ */
+ private WeakReference target;
+
+ /**
+ * The name of the target item state.
+ */
+ protected QName name;
+
+ /**
+ * The parent <code>HierarchyEntry</code>.
+ */
+ protected NodeEntryImpl parent;
+
+ /**
+ * The item state factory to create the the item state.
+ */
+ protected final EntryFactory factory;
+
+ /**
+ * Creates a new <code>HierarchyEntryImpl</code> with the given parent
+ * <code>NodeState</code>.
+ *
+ * @param parent the <code>NodeEntry</code> that owns this child node
+ * reference.
+ * @param name the name of the child item.
+ * @param factory
+ */
+ HierarchyEntryImpl(NodeEntryImpl parent, QName name, EntryFactory factory) {
+ this.parent = parent;
+ this.name = name;
+ this.factory = factory;
+ }
+
+ /**
+ * Resolves this <code>HierarchyEntryImpl</code> and returns the target
+ * <code>ItemState</code> of this reference. This method may return a
+ * cached <code>ItemState</code> if this method was called before already
+ * otherwise this method will forward the call to {@link #doResolve()}
+ * and cache its return value. If an existing state has been invalidated
+ * before, an attempt is made to reload it in order to make sure, that
+ * a call to {@link ItemState#isValid()} does not equivocally return false.
+ *
+ * @return the <code>ItemState</code> where this reference points to.
+ * @throws NoSuchItemStateException if the referenced <code>ItemState</code>
+ * does not exist.
+ * @throws ItemStateException if an error occurs.
+ */
+ ItemState resolve() throws NoSuchItemStateException, ItemStateException {
+ // check if already resolved
+ ItemState state = internalGetItemState();
+ // not yet resolved. retrieve and keep weak reference to state
+ if (state == null) {
+ state = doResolve();
+ target = new WeakReference(state);
+ } else if (state.getStatus() == Status.INVALIDATED) {
+ // completely reload this entry, but don't reload recursively
+ reload(false, false);
+ }
+ return state;
+ }
+
+ /**
+ * Resolves this <code>HierarchyEntryImpl</code> and returns the target
+ * <code>ItemState</code> of this reference.
+ *
+ * @return the <code>ItemState</code> where this reference points to.
+ * @throws NoSuchItemStateException if the referenced <code>ItemState</code>
+ * does not exist.
+ * @throws ItemStateException if an error occurs.
+ */
+ abstract ItemState doResolve() throws NoSuchItemStateException, ItemStateException;
+
+ /**
+ *
+ * @return
+ */
+ ItemState internalGetItemState() {
+ if (target != null) {
+ ItemState state = (ItemState) target.get();
+ if (state != null) {
+ return state;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the target of this HierarchyEntry to the given new ItemState.
+ *
+ * @throws IllegalStateException if this entry has already been resolved.
+ * @throws IllegalArgumentException if the given state is <code>null</code>
+ * or has another Status than {@link Status#NEW} or in case of class mismatch.
+ */
+ void internalSetItemState(ItemState newItemState) {
+ if (target != null || newItemState == null) {
+ throw new IllegalStateException();
+ }
+
+ if ((denotesNode() && newItemState.isNode()) || (!denotesNode() && !newItemState.isNode())) {
+ target = new WeakReference(newItemState);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ //-----------------------------------------------------< HierarchyEntry >---
+ /**
+ * @inheritDoc
+ * @see HierarchyEntry#getQName()
+ */
+ public QName getQName() {
+ return name;
+ }
+
+ /**
+ * @inheritDoc
+ * @see HierarchyEntry#getPath()
+ */
+ public Path getPath() throws RepositoryException {
+ // shortcut for root state
+ if (parent == null) {
+ return Path.ROOT;
+ }
+
+ // build path otherwise
+ try {
+ Path.PathBuilder builder = new Path.PathBuilder();
+ buildPath(builder, this);
+ return builder.getPath();
+ } catch (MalformedPathException e) {
+ String msg = "Failed to build path of " + this;
+ throw new RepositoryException(msg, e);
+ }
+ }
+
+ /**
+ * Adds the path element of an item id to the path currently being built.
+ * On exit, <code>builder</code> contains the path of <code>state</code>.
+ *
+ * @param builder builder currently being used
+ * @param hEntry HierarchyEntry of the state the path should be built for.
+ */
+ private void buildPath(Path.PathBuilder builder, HierarchyEntry hEntry) {
+ NodeEntry parentEntry = hEntry.getParent();
+ // shortcut for root state
+ if (parentEntry == null) {
+ builder.addRoot();
+ return;
+ }
+
+ // recursively build path of parent
+ buildPath(builder, parentEntry);
+
+ QName name = hEntry.getQName();
+ if (hEntry.denotesNode()) {
+ int index = ((NodeEntry) hEntry).getIndex();
+ // add to path
+ if (index == Path.INDEX_DEFAULT) {
+ builder.addLast(name);
+ } else {
+ builder.addLast(name, index);
+ }
+ } else {
+ // property-state: add to path
+ builder.addLast(name);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ * @see HierarchyEntry#getParent()
+ */
+ public NodeEntry getParent() {
+ return parent;
+ }
+
+ /**
+ * @inheritDoc
+ * @see HierarchyEntry#getStatus()
+ */
+ public int getStatus() {
+ ItemState state = internalGetItemState();
+ if (state == null) {
+ return Status._UNDEFINED_;
+ } else {
+ return state.getStatus();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ * @see HierarchyEntry#isAvailable()
+ */
+ public boolean isAvailable() {
+ ItemState state = null;
+ if (target != null) {
+ state = (ItemState) target.get();
+ }
+ return state != null;
+ }
+
+ /**
+ * {@inheritDoc}<br>
+ * @see HierarchyEntry#getItemState()
+ */
+ public ItemState getItemState() throws NoSuchItemStateException, ItemStateException {
+ ItemState state = resolve();
+ return state;
+ }
+
+ /**
+ * {@inheritDoc}<br>
+ * @see HierarchyEntry#invalidate(boolean)
+ */
+ public void invalidate(boolean recursive) {
+ ItemState state = internalGetItemState();
+ if (state != null) {
+ // session-state TODO: only invalidate if existing?
+ if (state.getStatus() == Status.EXISTING) {
+ state.setStatus(Status.INVALIDATED);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see HierarchyEntry#revert()
+ */
+ public void revert() throws ItemStateException {
+ ItemState state = internalGetItemState();
+ if (state == null) {
+ // nothing to do
+ return;
+ }
+
+ int oldStatus = state.getStatus();
+ switch (oldStatus) {
+ case Status.EXISTING_MODIFIED:
+ case Status.STALE_MODIFIED:
+ // revert state from overlayed
+ state.merge(state.getWorkspaceState(), false);
+ state.setStatus(Status.EXISTING);
+ break;
+ case Status.EXISTING_REMOVED:
+ // revert state from overlayed
+ state.merge(state.getWorkspaceState(), false);
+ state.setStatus(Status.EXISTING);
+ if (!denotesNode()) {
+ parent.revertPropertyRemoval((PropertyEntry) this);
+ }
+ break;
+ case Status.NEW:
+ // reverting a NEW state is equivalent to its removal.
+ remove();
+ break;
+ case Status.STALE_DESTROYED:
+ // overlayed does not exist any more -> remove it
+ remove();
+ break;
+ default:
+ // Cannot revert EXISTING, REMOVED, INVALIDATED, MODIFIED states.
+ // State was implicitely reverted
+ log.debug("State with status " + oldStatus + " cannot be reverted.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see HierarchyEntry#reload(boolean, boolean)
+ */
+ public void reload(boolean keepChanges, boolean recursive) {
+ ItemState state = internalGetItemState();
+ if (state == null) {
+ // nothing to do. entry will be validated upon resolution.
+ return;
+ }
+ /*
+ if keepChanges is true only existing or invalidated states must be
+ updated. otherwise the state gets updated and might be marked 'Stale'
+ if transient changes are present and the workspace-state is modified.
+ */
+ // TODO: check again if 'reconnect' is not possible for transiently-modified state
+ if (!keepChanges || state.getStatus() == Status.EXISTING
+ || state.getStatus() == Status.INVALIDATED) {
+ // reload the workspace state from the persistent layer
+ try {
+ state.reconnect(keepChanges);
+ } catch (NoSuchItemStateException e) {
+ // remove hierarchyEntry (including all children and set
+ // state-status to REMOVED (or STALE_DESTROYED)
+ remove();
+ } catch (ItemStateException e) {
+ // TODO: rather throw? remove from parent?
+ log.warn("Exception while reloading property state: " + e);
+ log.debug("Stacktrace: ", e);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see HierarchyEntry#transientRemove()
+ */
+ public void transientRemove() throws ItemStateException {
+ ItemState state = internalGetItemState();
+ 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");
+ }
+ int oldStatus = state.getStatus();
+ if (oldStatus == Status.NEW) {
+ remove();
+ } else {
+ 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.
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see HierarchyEntry#collectStates(ChangeLog, boolean)
+ */
+ public void collectStates(ChangeLog changeLog, boolean throwOnStale) throws StaleItemStateException {
+ ItemState state = internalGetItemState();
+ if (state == null) {
+ // nothing to do
+ return;
+ }
+
+ if (throwOnStale && Status.isStale(state.getStatus())) {
+ String msg = "Cannot save changes: " + state + " has been modified externally.";
+ log.debug(msg);
+ throw new StaleItemStateException(msg);
+ }
+ // only interested in transient modifications or stale-modified states
+ switch (state.getStatus()) {
+ case Status.NEW:
+ changeLog.added(state);
+ break;
+ case Status.EXISTING_MODIFIED:
+ case Status.STALE_MODIFIED:
+ changeLog.modified(state);
+ break;
+ case Status.EXISTING_REMOVED:
+ changeLog.deleted(state);
+ break;
+ default:
+ log.debug("Collecting states: Ignored ItemState with status " + Status.getName(state.getStatus()));
+ }
+ }
+}
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java?view=auto&rev=506927
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java Tue Feb 13 01:31:36 2007
@@ -0,0 +1,182 @@
+/*
+ * 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.jcr2spi.observation.InternalEventListener;
+import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
+import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
+import org.apache.jackrabbit.spi.EventFilter;
+import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventIterator;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.name.Path;
+
+import javax.jcr.RepositoryException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * <code>HierarchyEventListener</code>...
+ */
+public class HierarchyEventListener implements InternalEventListener {
+
+ private static Logger log = LoggerFactory.getLogger(HierarchyEventListener.class);
+
+ private final HierarchyManager hierarchyMgr;
+ private final Collection eventFilter;
+
+ public HierarchyEventListener(WorkspaceManager wspManager,
+ HierarchyManager hierarchyMgr,
+ CacheBehaviour cacheBehaviour) {
+ this.hierarchyMgr = hierarchyMgr;
+ if (cacheBehaviour == CacheBehaviour.OBSERVATION) {
+ EventFilter filter = null;
+ try {
+ // TODO: improve. for now listen to everything
+ filter = wspManager.createEventFilter(Event.ALL_TYPES, Path.ROOT, true, null, null, false);
+ } catch (RepositoryException e) {
+ // spi does not support observation, or another error occurred.
+ }
+ this.eventFilter = (filter == null) ? Collections.EMPTY_LIST : Collections.singletonList(filter);
+ } else {
+ this.eventFilter = Collections.EMPTY_LIST;
+ }
+ wspManager.addEventListener(this);
+ }
+
+ //----------------------------------------------< InternalEventListener >---
+ /**
+ * @see InternalEventListener#getEventFilters()
+ */
+ public Collection getEventFilters() {
+ return eventFilter;
+ }
+
+ /**
+ * Processes <code>events</code> and invalidates cached <code>ItemState</code>s
+ * accordingly. Note that this performed for both local and non-local changes,
+ * since workspace operations are reported as local changes as well and
+ * might have invoked changes (autocreated items etc.).
+ *
+ * @param eventBundle
+ * @see InternalEventListener#onEvent(EventBundle)
+ */
+ public void onEvent(EventBundle eventBundle) {
+ pushEvents(getEventCollection(eventBundle));
+ }
+
+ /**
+ * Retrieve the workspace state(s) affected by the given event and refresh
+ * them accordingly.
+ *
+ * @param events
+ */
+ private void pushEvents(Collection events) {
+ if (events.isEmpty()) {
+ return;
+ }
+ // collect set of removed node ids
+ Set removedEvents = new HashSet();
+ // separately collect the add events
+ Set addEvents = new HashSet();
+
+ for (Iterator it = events.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ int type = event.getType();
+ if (type == Event.NODE_REMOVED) {
+ // remember removed nodes separately for proper handling later on.
+ removedEvents.add(event.getItemId());
+ } else if (type == Event.NODE_ADDED || type == Event.PROPERTY_ADDED) {
+ addEvents.add(event);
+ it.remove();
+ }
+ }
+
+ /* Process ADD-events.
+ In case of persisting transients modifications, the event-set may
+ still contain events that are not covered by the changeLog such as
+ new version-history or other autocreated properties and nodes.
+
+ Add events need to be processed hierarchically, since its not possible
+ to add a new child reference to a state that is not yet present in
+ the state manager.
+ The 'progress' flag is used to make sure, that during each loop at
+ least one event has been processed and removed from the iterator.
+ If this is not the case, there are not parent states present in the
+ state manager that need to be updated and the remaining events may
+ be ignored.
+ */
+ boolean progress = true;
+ while (!addEvents.isEmpty() && progress) {
+ progress = false;
+ for (Iterator it = addEvents.iterator(); it.hasNext();) {
+ Event ev = (Event) it.next();
+ NodeEntry parent = (ev.getParentId() != null) ? (NodeEntry) hierarchyMgr.lookup(ev.getParentId()) : null;
+ if (parent != null) {
+ parent.refresh(ev);
+ it.remove();
+ progress = true;
+ }
+ }
+ }
+
+ /* process all other events (removal, property changed) */
+ for (Iterator it = events.iterator(); it.hasNext(); ) {
+ Event event = (Event) it.next();
+ int type = event.getType();
+
+ NodeId parentId = event.getParentId();
+ NodeEntry parent = (parentId != null) ? (NodeEntry) hierarchyMgr.lookup(parentId) : null;
+ if (type == Event.NODE_REMOVED || type == Event.PROPERTY_REMOVED) {
+ // notify parent about removal if its child-entry.
+ // - if parent is 'null' (i.e. not yet loaded) the child-entry does
+ // not exist either -> no need to inform child-entry
+ // - if parent got removed with the same event-bundle
+ // only remove the parent an skip this event.
+ if (parent != null && !removedEvents.contains(parentId)) {
+ parent.refresh(event);
+ }
+ } else if (type == Event.PROPERTY_CHANGED) {
+ // notify parent in case jcr:mixintypes or jcr:uuid was changed.
+ // if parent is 'null' (i.e. not yet loaded) the prop-entry does
+ // not exist either -> no need to inform propEntry
+ if (parent != null) {
+ parent.refresh(event);
+ }
+ } else {
+ // should never occur
+ throw new IllegalArgumentException("Invalid event type: " + event.getType());
+ }
+ }
+ }
+
+ private static Collection getEventCollection(EventBundle eventBundle) {
+ List evs = new ArrayList();
+ for (EventIterator it = eventBundle.getEvents(); it.hasNext();) {
+ evs.add(it.nextEvent());
+ }
+ return evs;
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEventListener.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url