jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r428714 - in /jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state: CachingItemStateManager.java ChildItemReference.java ChildNodeEntry.java ChildNodeReference.java NodeState.java PathResolver.java
Date Fri, 04 Aug 2006 12:47:59 GMT
Author: mreutegg
Date: Fri Aug  4 05:47:58 2006
New Revision: 428714

URL: http://svn.apache.org/viewvc?rev=428714&view=rev
Log:
Remove PathMap from CachingItemStateManager and use linked ItemState hierarchy instead.
Add secondary LRUMap to CachingItemStateManager which keeps a fixed amount of hard references
to most recently used ItemStates. Still nedds some improvements. See todo in NodeState.

Added:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
  (with props)
Modified:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildItemReference.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntry.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeReference.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java?rev=428714&r1=428713&r2=428714&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
(original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
Fri Aug  4 05:47:58 2006
@@ -16,19 +16,19 @@
  */
 package org.apache.jackrabbit.jcr2spi.state;
 
-import org.apache.jackrabbit.util.PathMap;
 import org.apache.jackrabbit.name.Path;
-import org.apache.jackrabbit.name.MalformedPathException;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.EventIterator;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.IdFactory;
-import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
+import org.apache.commons.collections.map.ReferenceMap;
+import org.apache.commons.collections.map.LRUMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Map;
-import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -42,6 +42,11 @@
 public class CachingItemStateManager implements ItemStateManager, InternalEventListener {
 
     /**
+     * The logger instance for this class.
+     */
+    private static Logger log = LoggerFactory.getLogger(CachingItemStateManager.class);
+
+    /**
      * The item state factory to create <code>ItemState</code>s that are not
      * present in the cache.
      */
@@ -50,23 +55,40 @@
     /**
      * Maps a String uuid to a {@link NodeState}.
      */
-    private final Map uuid2PathElement;
+    private final Map uuid2NodeState;
+
+    /**
+     * Map of recently used <code>ItemState</code>.
+     */
+    private final Map recentlyUsed;
 
     /**
-     * Maps a path to an {@link ItemState}.
+     * The root node of the workspace.
      */
-    private final PathMap path2State;
+    private final NodeState root;
 
     /**
      * The Id factory.
      */
     private final IdFactory idFactory;
 
-    public CachingItemStateManager(ItemStateFactory isf, IdFactory idFactory) {
+    /**
+     * Creates a new <code>CachingItemStateManager</code>.
+     *
+     * @param isf       the item state factory to create item state instances.
+     * @param idFactory the id factory.
+     * @throws NoSuchItemStateException if the root node cannot be obtained.
+     * @throws ItemStateException       if any other error occurs while
+     *                                  obtaining the root node.
+     */
+    public CachingItemStateManager(ItemStateFactory isf, IdFactory idFactory)
+            throws ItemStateException, NoSuchItemStateException {
         this.isf = isf;
         this.idFactory = idFactory;
-        this.uuid2PathElement = new HashMap(); // TODO: must use weak references
-        path2State = new PathMap();      // TODO: must use weak references
+        this.uuid2NodeState = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
+        this.recentlyUsed = new LRUMap(1000); // TODO: make configurable
+        // initialize root
+        root = isf.createNodeState(idFactory.createNodeId((String) null, Path.ROOT), this);
     }
 
     //---------------------------------------------------< ItemStateManager >---
@@ -75,7 +97,7 @@
      * @see ItemStateManager#getItemState(ItemId)
      */
     public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException
{
-        return (ItemState) getPathElement(id).get();
+        return resolve(id);
     }
 
     /**
@@ -84,7 +106,7 @@
      */
     public boolean hasItemState(ItemId id) {
         try {
-            getPathElement(id);
+            resolve(id);
         } catch (ItemStateException e) {
             return false;
         }
@@ -142,68 +164,52 @@
             switch (e.getType()) {
                 case Event.NODE_ADDED:
                 case Event.PROPERTY_ADDED:
-                    PathMap.Element elem = lookup(itemId);
-                    if (elem != null) {
+                    state = lookup(itemId);
+                    if (state != null) {
                         // TODO: item already exists ???
                         // remove from cache and invalidate
-                        state = (ItemState) elem.get();
-                        if (itemId.denotesNode()) {
-                            elem.set(null);
-                        } else {
-                            elem.remove();
-                        }
-                        if (state != null) {
-                            state.discard();
-                        }
+                        recentlyUsed.remove(state);
+                        state.discard();
                     }
-                    elem = lookup(parentId);
-                    if (elem != null) {
+                    parent = (NodeState) lookup(parentId);
+                    if (parent != null) {
                         // discard and let wsp manager reload state when accessed next time
-                        parent = (NodeState) elem.get();
-                        if (parent != null) {
-                            parent.discard();
-                            elem.set(null);
-                        }
+                        recentlyUsed.remove(parent);
+                        parent.discard();
                     }
                     break;
                 case Event.NODE_REMOVED:
                 case Event.PROPERTY_REMOVED:
-                    elem = lookup(itemId);
-                    if (elem != null) {
-                        state = (ItemState) elem.get();
+                    state = lookup(itemId);
+                    if (state != null) {
                         if (itemId.denotesNode()) {
                             if (itemId.getRelativePath() == null) {
                                 // also remove mapping from uuid
-                                uuid2PathElement.remove(itemId.getUUID());
+                                uuid2NodeState.remove(itemId.getUUID());
                             }
                         }
-
-                        elem.remove();
-
-                        if (state != null) {
-                            state.notifyStateDestroyed();
-                        }
+                        recentlyUsed.remove(state);
+                        state.notifyStateDestroyed();
                     }
-                    elem = lookup(parentId);
-                    if (elem != null && elem.get() != null) {
-                        parent = (NodeState) elem.get();
+                    state = lookup(parentId);
+                    if (state != null) {
+                        parent = (NodeState) state;
                         // check if removed as well
                         if (removedNodeIds.contains(parent.getId())) {
                             // do not invalidate here
                         } else {
                             // discard and let wsp manager reload state when accessed next
time
+                            recentlyUsed.remove(parent);
                             parent.discard();
-                            elem.set(null);
                         }
                     }
                     break;
                 case Event.PROPERTY_CHANGED:
-                    elem = lookup(itemId);
+                    state = lookup(itemId);
                     // discard and let wsp manager reload state when accessed next time
-                    if (elem != null) {
-                        state = (ItemState) elem.get();
+                    if (state != null) {
+                        recentlyUsed.remove(state);
                         state.discard();
-                        elem.remove();
                     }
             }
         }
@@ -212,139 +218,80 @@
     //------------------------------< internal >--------------------------------
 
     /**
-     * Returns the PathMap.Element which holds the ItemState with
-     * <code>id</code>. The returned <code>PathMap.Element</code>
is guaranteed
-     * to reference an <code>ItemState</code> calling {@link
-     * PathMap.Element#get()};
+     * Called whenever an item state is accessed. Calling this method will update
+     * the LRU map which keeps track of most recently used item states.
      *
-     * @param id the id of the item state.
-     * @return the PathElement.Element.
-     * @throws NoSuchItemStateException if there is no ItemState with this id.
-     * @throws ItemStateException       if another error occurs.
+     * @param state the touched state.
      */
-    private PathMap.Element getPathElement(ItemId id)
-            throws NoSuchItemStateException, ItemStateException {
+    private void touch(ItemState state) {
+        recentlyUsed.put(state, state);
+    }
+
+    /**
+     * Resolves the id into an <code>ItemState</code>.
+     *
+     * @param id the id of the <code>ItemState</code> to resolve.
+     * @return the <code>ItemState</code>.
+     * @throws NoSuchItemStateException if there is no <code>ItemState</code>
+     *                                  with <code>id</code>
+     * @throws ItemStateException       if any other error occurs.
+     */
+    private ItemState resolve(ItemId id) throws NoSuchItemStateException, ItemStateException
{
         String uuid = id.getUUID();
         Path relPath = id.getRelativePath();
-        PathMap.Element elem = null;
 
-        // first get PathElement of uuid part
+        NodeState nodeState = null;
+        // resolve uuid part
         if (uuid != null) {
-            elem = (PathMap.Element) uuid2PathElement.get(uuid);
-            if (elem == null || elem.get() == null) {
+            nodeState = (NodeState) uuid2NodeState.get(uuid);
+            if (nodeState == null) {
                 // state identified by the uuid is not yet cached -> get from ISM
                 NodeId refId = (relPath == null) ? (NodeId) id : idFactory.createNodeId(uuid);
-                NodeState state = isf.createNodeState(refId, this);
-
-                // put this state to cache
-                // but first we have to make sure that the parent of this state
-                // is already cached
-
-                if (state.getParentId() == null) {
-                    // shortcut for the root node
-                    elem = path2State.map(Path.ROOT, true);
-                    elem.set(state);
-                    uuid2PathElement.put(uuid, elem);
-                    return elem;
-                } else {
-                    // get element of parent this will force loading of
-                    // parent into cache if needed
-                    PathMap.Element parentElement = getPathElement(state.getParentId());
-                    // create path element if necessary
-                    if (elem == null) {
-                        Path.PathElement[] elements = new Path.PathElement[]{
-                            getNameElement((NodeState) parentElement.get(), state)};
-                        Path p = null;
-                        try {
-                            p = new Path.PathBuilder(elements).getPath();
-                        } catch (MalformedPathException e) {
-                            // elements is never empty
-                        }
-                        elem = parentElement.getDescendant(p, false);
-                    }
-                    elem.set(state);
-                    // now put current state to cache
-                    uuid2PathElement.put(uuid, elem);
-                }
+                nodeState = isf.createNodeState(refId, this);
+                uuid2NodeState.put(uuid, nodeState);
             }
         }
 
-        // at this point we are guaranteed to have an element
-        // now resolve relative path part of id if there is one
+        ItemState s = nodeState;
         if (relPath != null) {
-            PathMap.Element tmp = elem.getDescendant(relPath, true);
-            if (tmp == null || tmp.get() == null) {
-                // not yet cached, load from isf
-                ItemState state;
-                if (id.denotesNode()) {
-                    state = isf.createNodeState((NodeId) id, this);
-                } else {
-                    state = isf.createPropertyState((PropertyId) id, this);
-                }
-                // put to cache
-                if (tmp == null) {
-                    tmp = elem.getDescendant(relPath, false);
-                }
-                tmp.set(state);
-            }
-            elem = tmp;
+            s = PathResolver.resolve(nodeState, relPath);
         }
-        return elem;
+        touch(s);
+        return s;
     }
 
     /**
-     * Looks up the <code>Element</code> with id but does not try to load the
+     * Looks up the <code>ItemState</code> with id but does not try to load the
      * item if it is not present in the cache.
      *
      * @param id the id of the ItemState to look up.
-     * @return the cached <code>Element</code> or <code>null</code>
if it is not
+     * @return the cached <code>ItemState</code> or <code>null</code>
if it is not
      *         present in the cache.
      */
-    private PathMap.Element lookup(ItemId id) {
-        PathMap.Element elem;
+    private ItemState lookup(ItemId id) {
+        ItemState state;
         // resolve UUID
         if (id.getUUID() != null) {
-            elem = (PathMap.Element) uuid2PathElement.get(id.getUUID());
-            if (elem == null) {
+            state = (ItemState) uuid2NodeState.get(id.getUUID());
+            if (state == null) {
                 // not cached
                 return null;
             }
         } else {
             // start from root
-            elem = path2State.map(Path.ROOT, false);
+            state = root;
         }
 
         // resolve relative path
         if (id.getRelativePath() != null) {
-            elem = elem.getDescendant(id.getRelativePath(), true);
-        }
-
-        return elem;
-    }
-
-    /**
-     * Returns the name of <code>state</code> starting from
-     * <code>parent</code>. This method only works for a direct parent of
-     * <code>state</code>.
-     *
-     * @param parent the parent of <code>state</code>.
-     * @param state the state.
-     * @return the name element for <code>state</code> starting from
-     * <code>parent</code>
-     * @throws ItemStateException if an error occurs
-     */
-    private static Path.PathElement getNameElement(NodeState parent, ItemState state)
-            throws ItemStateException {
-        if (state.isNode()) {
-            ChildNodeEntry entry = parent.getChildNodeEntry((NodeId) state.getId());
-            if (entry == null) {
-                throw new ItemStateException("No child node entry " +
-                        state.getId() + " found in " + parent.getId());
-            } else {
-                return Path.create(entry.getName(), entry.getIndex()).getNameElement();
+            try {
+                state = PathResolver.lookup(state, id.getRelativePath());
+            } catch (ItemStateException e) {
+                log.warn("exception while looking up state with id: " + id);
+                return null;
             }
-        } else {
-            return state.getId().getRelativePath().getNameElement();
         }
+
+        return state;
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildItemReference.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildItemReference.java?rev=428714&r1=428713&r2=428714&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildItemReference.java
(original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildItemReference.java
Fri Aug  4 05:47:58 2006
@@ -67,13 +67,12 @@
      * Resolves this <code>ChildItemReference</code> and returns the target
      * <code>ItemState</code> of this reference.
      *
-     * @param isf the item state factory responsible for creating node states.
      * @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.
      */
-    public ItemState resolve(ItemStateFactory isf)
+    public ItemState resolve()
             throws NoSuchItemStateException, ItemStateException {
         // check if cached
         if (target != null) {
@@ -96,6 +95,18 @@
      */
     public NodeState getParent() {
         return parent;
+    }
+
+    /**
+     * @return <code>true</code> if this reference is resolved;
+     *         <code>false</code> otherwise.
+     */
+    protected boolean isResolved() {
+        ItemState state = null;
+        if (target != null) {
+            state = (ItemState) target.get();
+        }
+        return state != null;
     }
 
     /**

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntry.java?rev=428714&r1=428713&r2=428714&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntry.java
(original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntry.java
Fri Aug  4 05:47:58 2006
@@ -49,4 +49,14 @@
      */
     public NodeState getNodeState()
             throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * Returns <code>true</code> if the referenced <code>NodeState</code>
is
+     * available. That is, the referenced <code>NodeState</code> is already
+     * cached and ready to be returned by {@link #getNodeState()}.
+     *
+     * @return <code>true</code> if the <code>NodeState</code> is
available;
+     *         otherwise <code>false</code>.
+     */
+    public boolean isAvailable();
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeReference.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeReference.java?rev=428714&r1=428713&r2=428714&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeReference.java
(original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeReference.java
Fri Aug  4 05:47:58 2006
@@ -59,6 +59,14 @@
      */
     public NodeState getNodeState()
             throws NoSuchItemStateException, ItemStateException {
-        return (NodeState) resolve(isf);
+        return (NodeState) resolve();
+    }
+
+    /**
+     * @inheritDoc
+     * @see ChildNodeEntry#isAvailable()
+     */
+    public boolean isAvailable() {
+        return isResolved();
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?rev=428714&r1=428713&r2=428714&view=diff
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
(original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
Fri Aug  4 05:47:58 2006
@@ -75,6 +75,7 @@
 
     /**
      * insertion-ordered collection of ChildNodeEntry objects
+     * TODO: cache needs to be notified when a child node entry is traversed or NodeState
is created
      */
     private ChildNodeEntries childNodeEntries = new ChildNodeEntries();
 
@@ -628,7 +629,7 @@
         if (propRef == null) {
             throw new NoSuchItemStateException(idFactory.createPropertyId(getNodeId(), propertyName).toString());
         }
-        return (PropertyState) propRef.resolve(isf);
+        return (PropertyState) propRef.resolve();
     }
 
     /**

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java?rev=428714&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
(added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
Fri Aug  4 05:47:58 2006
@@ -0,0 +1,166 @@
+/*
+ * 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.state;
+
+import org.apache.jackrabbit.name.Path;
+
+/**
+ * <code>PathResolver</code> resolves a relative Path starting at a given
+ * ItemState and returns the Item where the path points to.
+ */
+public class PathResolver {
+
+    /**
+     * The starting point to resolve the path.
+     */
+    private final ItemState start;
+
+    /**
+     * The path to resolve.
+     */
+    private final Path relPath;
+
+    /**
+     * Internal constructor.
+     *
+     * @param start   the starting point.
+     * @param relPath the path to resolve.
+     * @throws IllegalArgumentException if relPath is absolute or not normalized
+     *                                  or starts with a parent ('..') path
+     *                                  element.
+     */
+    private PathResolver(ItemState start, Path relPath) {
+        if (relPath.isAbsolute()
+                || !relPath.isNormalized()
+                || relPath.getElement(0).denotesParent()) {
+            throw new IllegalArgumentException("path must be relative and must " +
+                    "not contain parent path elements");
+        }
+        this.start = start;
+        this.relPath = relPath;
+    }
+
+    /**
+     * Resolves the path starting at <code>start</code>.
+     *
+     * @param start   the starting point.
+     * @param relPath the path to resolve.
+     * @return the resolved item state.
+     * @throws NoSuchItemStateException the the referenced item state does not
+     *                                  exist.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  item state.
+     * @throws IllegalArgumentException if relPath is absolute or not normalized
+     *                                  or starts with a parent ('..') path
+     *                                  element.
+     */
+    public static ItemState resolve(ItemState start, Path relPath)
+            throws NoSuchItemStateException, ItemStateException {
+        return new PathResolver(start, relPath).resolve();
+    }
+
+    /**
+     * Looks up the <code>ItemState</code> at <code>relPath</code>
starting at
+     * <code>start</code>.
+     *
+     * @param start   the starting point.
+     * @param relPath the path to resolve.
+     * @return the resolved item state or <code>null</code> if the item is not
+     *         available.
+     * @throws NoSuchItemStateException the the referenced item state does not
+     *                                  exist.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  item state.
+     * @throws IllegalArgumentException if relPath is absolute or not normalized
+     *                                  or starts with a parent ('..') path
+     *                                  element.
+     */
+    public static ItemState lookup(ItemState start, Path relPath)
+            throws NoSuchItemStateException, ItemStateException {
+        return new PathResolver(start, relPath).lookup();
+    }
+
+    /**
+     * Resolves the path.
+     *
+     * @return the resolved item state.
+     * @throws NoSuchItemStateException the the item state does not exist.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  item state.
+     */
+    private ItemState resolve()
+            throws NoSuchItemStateException, ItemStateException {
+        if (!start.isNode()) {
+            throw new NoSuchItemStateException(relPath.toString());
+        }
+        NodeState state = (NodeState) start;
+        for (int i = 0; i < relPath.getLength(); i++) {
+            Path.PathElement elem = relPath.getElement(i);
+            // first try to resolve node
+            if (state.hasChildNodeEntry(elem.getName(), elem.getNormalizedIndex())) {
+                state = state.getChildNodeEntry(elem.getName(),
+                        elem.getNormalizedIndex()).getNodeState();
+            } else if (elem.getIndex() == 0 // property must not have index
+                    && state.hasPropertyName(elem.getName())
+                    && i == relPath.getLength() - 1) { // property must be final
path element
+                return state.getPropertyState(elem.getName());
+            } else {
+                throw new NoSuchItemStateException(relPath.toString());
+            }
+        }
+        return state;
+    }
+
+    /**
+     * Resolves the path but does not the <code>ItemState</code> if it is not
+     * yet loaded.
+     *
+     * @return the resolved item state or <code>null</code> if the item is not
+     *         available.
+     * @throws NoSuchItemStateException the the item state does not exist.
+     * @throws ItemStateException       if an error occurs while retrieving the
+     *                                  item state.
+     */
+    private ItemState lookup()
+            throws NoSuchItemStateException, ItemStateException {
+        if (!start.isNode()) {
+            throw new NoSuchItemStateException(relPath.toString());
+        }
+        NodeState state = (NodeState) start;
+        for (int i = 0; i < relPath.getLength(); i++) {
+            Path.PathElement elem = relPath.getElement(i);
+            // first try to resolve node
+            if (state.hasChildNodeEntry(elem.getName(), elem.getNormalizedIndex())) {
+                ChildNodeEntry cne = state.getChildNodeEntry(elem.getName(),
+                        elem.getNormalizedIndex());
+                if (cne.isAvailable()) {
+                    state = cne.getNodeState();
+                } else {
+                    return null;
+                }
+            } else if (elem.getIndex() == 0 // property must not have index
+                    && state.hasPropertyName(elem.getName())
+                    && i == relPath.getLength() - 1) { // property must be final
path element
+                // TODO: check if available
+                return state.getPropertyState(elem.getName());
+            } else {
+                throw new NoSuchItemStateException(relPath.toString());
+            }
+        }
+        return state;
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message