jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ang...@apache.org
Subject svn commit: r421270 [13/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/ commons/src/ commons/src/main/ commons/src/main/java/ commons/src/main/java/org/ commons/src/main/java/org/apache/ commons/src/main/java/org/apache/jackrabbit/ commons/src/main...
Date Wed, 12 Jul 2006 13:33:27 GMT
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,256 @@
+/*
+ * 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.query;
+
+import org.apache.jackrabbit.jcr2spi.ItemManager;
+import org.apache.jackrabbit.jcr2spi.SessionImpl;
+import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Query;
+import javax.jcr.version.VersionException;
+
+/**
+ * Provides the default implementation for a JCR query.
+ */
+public class QueryImpl implements Query {
+
+    /**
+     * The session of the user executing this query
+     */
+    protected SessionImpl session;
+
+    /**
+     * The namespace resolver of the session that executes this query.
+     */
+    // DIFF JR: added
+    protected NamespaceResolver resolver;
+
+    /**
+     * The item manager of the session that executes this query.
+     */
+    protected ItemManager itemManager;
+
+    /**
+     * The query statement
+     */
+    protected String statement;
+
+    /**
+     * The syntax of the query statement
+     */
+    protected String language;
+
+    /**
+     * The node where this query is persisted. Only set when this is a persisted
+     * query.
+     */
+    protected Node node;
+
+    /**
+     * The query handler for this query.
+     */
+    // DIFF JR: use WorkspaceManager (-> RepositoryService) instead
+    //protected QueryHandler handler;
+    protected WorkspaceManager wspManager;
+
+    /**
+     * Flag indicating whether this query is initialized.
+     */
+    private boolean initialized = false;
+
+    /**
+     * Initializes this query.
+     *
+     * @param session    the session that created this query.
+     * @param itemMgr    the item manager of that session.
+     * @param wspManager the workspace manager that belongs to the session.
+     * @param statement  the query statement.
+     * @param language   the language of the query statement.
+     * @throws InvalidQueryException if the query is invalid.
+     */
+    // DIFF JR: uses WorkspaceManager instead of QueryHandler
+    public void init(SessionImpl session,
+                     NamespaceResolver resolver,
+                     ItemManager itemMgr,
+                     WorkspaceManager wspManager,
+                     String statement,
+                     String language) throws InvalidQueryException {
+        checkNotInitialized();
+        this.session = session;
+        this.resolver = resolver;
+        this.itemManager = itemMgr;
+        this.statement = statement;
+        this.language = language;
+        this.wspManager = wspManager;
+        // DIFF JR: todo validate statement
+        //this.query = handler.createExecutableQuery(session, itemMgr, statement, language);
+        initialized = true;
+    }
+
+    /**
+     * Initializes this query from a node.
+     *
+     * @param session    the session that created this query.
+     * @param itemMgr    the item manager of that session.
+     * @param wspManager the workspace manager that belongs to the session.
+     * @param node       the node from where to read the query.
+     * @throws InvalidQueryException if the query is invalid.
+     * @throws RepositoryException   if another error occurs while reading from
+     *                               the node.
+     */
+    // DIFF JR: uses WorkspaceManager instead of QueryHandler
+    public void init(SessionImpl session,
+                     NamespaceResolver resolver,
+                     ItemManager itemMgr,
+                     WorkspaceManager wspManager,
+                     Node node)
+            throws InvalidQueryException, RepositoryException {
+        checkNotInitialized();
+        this.session = session;
+        this.resolver = resolver;
+        this.itemManager = itemMgr;
+        this.node = node;
+        this.wspManager = wspManager;
+
+        try {
+            if (!node.isNodeType(resolver.getJCRName(QName.NT_QUERY))) {
+                throw new InvalidQueryException("node is not of type nt:query");
+            }
+            statement = node.getProperty(resolver.getJCRName(QName.JCR_STATEMENT)).getString();
+            language = node.getProperty(resolver.getJCRName(QName.JCR_LANGUAGE)).getString();
+            // DIFF JR: todo validate statement
+            //query = handler.createExecutableQuery(session, itemMgr, statement, language);
+        } catch (NoPrefixDeclaredException e) {
+            throw new RepositoryException(e.getMessage(), e);
+        }
+        initialized = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public QueryResult execute() throws RepositoryException {
+        checkInitialized();
+        return new QueryResultImpl(itemManager,
+                wspManager.executeQuery(statement, language), resolver);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getStatement() {
+        checkInitialized();
+        return statement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getLanguage() {
+        checkInitialized();
+        return language;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getStoredQueryPath()
+            throws ItemNotFoundException, RepositoryException {
+        checkInitialized();
+        if (node == null) {
+            throw new ItemNotFoundException("not a persistent query");
+        }
+        return node.getPath();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Node storeAsNode(String absPath)
+            throws ItemExistsException,
+            PathNotFoundException,
+            VersionException,
+            ConstraintViolationException,
+            LockException,
+            UnsupportedRepositoryOperationException,
+            RepositoryException {
+
+        checkInitialized();
+        try {
+            Path p = resolver.getQPath(absPath).getNormalizedPath();
+            if (!p.isAbsolute()) {
+                throw new RepositoryException(absPath + " is not an absolute path");
+            }
+            if (session.itemExists(absPath)) {
+                throw new ItemExistsException(absPath);
+            }
+            if (!session.itemExists(resolver.getJCRPath(p.getAncestor(1)))) {
+                throw new PathNotFoundException(resolver.getJCRPath(p.getAncestor(1)));
+            }
+            String relPath = resolver.getJCRPath(p).substring(1);
+            String ntName = resolver.getJCRName(QName.NT_QUERY);
+            Node queryNode = session.getRootNode().addNode(relPath, ntName);
+            // set properties
+            queryNode.setProperty(resolver.getJCRName(QName.JCR_LANGUAGE), language);
+            queryNode.setProperty(resolver.getJCRName(QName.JCR_STATEMENT), statement);
+            node = queryNode;
+            return node;
+        } catch (MalformedPathException e) {
+            throw new RepositoryException(e.getMessage(), e);
+        } catch (NoPrefixDeclaredException e) {
+            throw new RepositoryException(e.getMessage(), e);
+        }
+    }
+
+    //-----------------------------< internal >---------------------------------
+
+    /**
+     * Checks if this query is not yet initialized and throws an
+     * <code>IllegalStateException</code> if it is already initialized.
+     */
+    protected void checkNotInitialized() {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+    }
+
+    /**
+     * Checks if this query is initialized and throws an
+     * <code>IllegalStateException</code> if it is not yet initialized.
+     */
+    protected void checkInitialized() {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+    }
+}
+

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryManagerImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryManagerImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryManagerImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,116 @@
+/*
+ * 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.query;
+
+import org.apache.jackrabbit.jcr2spi.ItemManager;
+import org.apache.jackrabbit.jcr2spi.SessionImpl;
+import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
+import org.apache.jackrabbit.name.NamespaceResolver;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+
+/**
+ * This class implements the {@link QueryManager} interface.
+ */
+public class QueryManagerImpl implements QueryManager {
+
+    /**
+     * The <code>Session</code> for this QueryManager.
+     */
+    private final SessionImpl session;
+
+    /**
+     * The namespace resolver for this query manager.
+     */
+    // DIFF JR: added
+    private final NamespaceResolver resolver;
+
+    /**
+     * The <code>ItemManager</code> of for item retrieval in search results
+     */
+    private final ItemManager itemMgr;
+
+    /**
+     * The <code>WorkspaceManager</code> where queries are executed.
+     */
+    private final WorkspaceManager wspManager;
+
+    /**
+     * Creates a new <code>QueryManagerImpl</code> for the passed
+     * <code>session</code>
+     *
+     * @param session
+     * @param resolver
+     * @param itemMgr
+     * @param wspManager
+     */
+    public QueryManagerImpl(SessionImpl session,
+                            NamespaceResolver resolver,
+                            ItemManager itemMgr,
+                            WorkspaceManager wspManager) {
+        this.session = session;
+        this.resolver = resolver;
+        this.itemMgr = itemMgr;
+        this.wspManager = wspManager;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Query createQuery(String statement, String language)
+            throws InvalidQueryException, RepositoryException {
+        sanityCheck();
+        QueryImpl query = new QueryImpl();
+        query.init(session, resolver, itemMgr, wspManager, statement, language);
+        return query;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Query getQuery(Node node)
+            throws InvalidQueryException, RepositoryException {
+        sanityCheck();
+        QueryImpl query = new QueryImpl();
+        query.init(session, resolver, itemMgr, wspManager, node);
+        return query;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] getSupportedQueryLanguages() throws RepositoryException {
+        return wspManager.getSupportedQueryLanguages();
+    }
+
+    /**
+     * Checks if this <code>QueryManagerImpl</code> instance is still usable,
+     * otherwise throws a {@link javax.jcr.RepositoryException}.
+     *
+     * @throws RepositoryException if this query manager is not usable anymore,
+     *                             e.g. the corresponding session is closed.
+     */
+    private void sanityCheck() throws RepositoryException {
+        if (!session.isLive()) {
+            throw new RepositoryException("corresponding session has been closed");
+        }
+    }
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryManagerImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryResultImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryResultImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryResultImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryResultImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,114 @@
+/*
+ * 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.query;
+// DIFF JR: this class uses a different package than the jackrabbit original
+
+import org.apache.jackrabbit.jcr2spi.ItemManager;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.QueryInfo;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+
+/**
+ * Implements the <code>javax.jcr.query.QueryResult</code> interface.
+ */
+public class QueryResultImpl implements QueryResult {
+
+    /**
+     * The logger instance for this class
+     */
+    private static final Logger log = LoggerFactory.getLogger(QueryResultImpl.class);
+
+    /**
+     * The item manager of the session executing the query
+     */
+    private final ItemManager itemMgr;
+
+    /**
+     * The spi query result.
+     */
+    private final QueryInfo queryInfo;
+
+    /**
+     * The namespace resolver of the session executing the query
+     */
+    private final NamespaceResolver resolver;
+
+    /**
+     * Creates a new query result.
+     *
+     * @param itemMgr     the item manager of the session executing the query.
+     * @param queryInfo   the spi query result.
+     * @param resolver    the namespace resolver of the session executing the query.
+     */
+    public QueryResultImpl(ItemManager itemMgr,
+                           QueryInfo queryInfo,
+                           NamespaceResolver resolver) {
+        this.itemMgr = itemMgr;
+        this.queryInfo = queryInfo;
+        this.resolver = resolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] getColumnNames() throws RepositoryException {
+        try {
+            // DIFF JR: obtain names from QueryInfo
+            QName[] names = queryInfo.getColumnNames();
+            String[] propNames = new String[names.length];
+            for (int i = 0; i < names.length; i++) {
+                propNames[i] = resolver.getJCRName(names[i]);
+            }
+            return propNames;
+        } catch (NoPrefixDeclaredException npde) {
+            String msg = "encountered invalid property name";
+            log.debug(msg);
+            throw new RepositoryException(msg, npde);
+
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeIterator getNodes() throws RepositoryException {
+        return getNodeIterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public RowIterator getRows() throws RepositoryException {
+        return new RowIteratorImpl(getNodeIterator(), queryInfo.getColumnNames(), resolver);
+    }
+
+    /**
+     * Creates a node iterator over the result nodes.
+     * @return a node iterator over the result nodes.
+     */
+    private ScoreNodeIterator getNodeIterator() throws RepositoryException {
+        return new NodeIteratorImpl(itemMgr, resolver, queryInfo);
+    }
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/QueryResultImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/RowIteratorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/RowIteratorImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/RowIteratorImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/RowIteratorImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,291 @@
+/*
+ * 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.query;
+// DIFF JR: this class uses a different package than the jackrabbit original
+
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.value.LongValue;
+import org.apache.jackrabbit.value.PathValue;
+import org.apache.jackrabbit.value.StringValue;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.Node;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Implements the {@link javax.jcr.query.RowIterator} interface returned by
+ * a {@link javax.jcr.query.QueryResult}.
+ */
+class RowIteratorImpl implements RowIterator {
+
+    /**
+     * Iterator over nodes, that constitute the result set.
+     */
+    private final ScoreNodeIterator nodes;
+
+    /**
+     * Array of select property names
+     */
+    private final QName[] properties;
+
+    /**
+     * The <code>NamespaceResolver</code> of the user <code>Session</code>.
+     */
+    private final NamespaceResolver resolver;
+
+    /**
+     * Creates a new <code>RowIteratorImpl</code> that iterates over the result
+     * nodes.
+     *
+     * @param nodes      a <code>ScoreNodeIterator</code> that contains the nodes of
+     *                   the query result.
+     * @param properties <code>QName</code> of the select properties.
+     * @param resolver   <code>NamespaceResolver</code> of the user
+     *                   <code>Session</code>.
+     */
+    RowIteratorImpl(ScoreNodeIterator nodes, QName[] properties, NamespaceResolver resolver) {
+        this.nodes = nodes;
+        this.properties = properties;
+        this.resolver = resolver;
+    }
+
+    /**
+     * Returns the next <code>Row</code> in the iteration.
+     *
+     * @return the next <code>Row</code> in the iteration.
+     * @throws NoSuchElementException if iteration has no more
+     *                                <code>Row</code>s.
+     */
+    public Row nextRow() throws NoSuchElementException {
+        return new RowImpl(nodes.getScore(), nodes.nextNode());
+    }
+
+    /**
+     * Skip a number of <code>Row</code>s in this iterator.
+     *
+     * @param skipNum the non-negative number of <code>Row</code>s to skip
+     * @throws NoSuchElementException if skipped past the last
+     *                                <code>Row</code> in this iterator.
+     */
+    public void skip(long skipNum) throws NoSuchElementException {
+        nodes.skip(skipNum);
+    }
+
+    /**
+     * Returns the number of <code>Row</code>s in this iterator.
+     *
+     * @return the number of <code>Row</code>s in this iterator.
+     */
+    public long getSize() {
+        return nodes.getSize();
+    }
+
+    /**
+     * Returns the current position within this iterator. The number
+     * returned is the 0-based index of the next <code>Row</code> in the iterator,
+     * i.e. the one that will be returned on the subsequent <code>next</code> call.
+     * <p/>
+     * Note that this method does not check if there is a next element,
+     * i.e. an empty iterator will always return 0.
+     *
+     * @return the current position withing this iterator.
+     */
+    public long getPosition() {
+        return nodes.getPosition();
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("remove");
+    }
+
+    /**
+     * Returns <code>true</code> if the iteration has more <code>Row</code>s.
+     * (In other words, returns <code>true</code> if <code>next</code> would
+     * return an <code>Row</code> rather than throwing an exception.)
+     *
+     * @return <code>true</code> if the iterator has more elements.
+     */
+    public boolean hasNext() {
+        return nodes.hasNext();
+    }
+
+    /**
+     * Returns the next <code>Row</code> in the iteration.
+     *
+     * @return the next <code>Row</code> in the iteration.
+     * @throws NoSuchElementException if iteration has no more <code>Row</code>s.
+     */
+    public Object next() throws NoSuchElementException {
+        return nextRow();
+    }
+
+    //---------------------< class RowImpl >------------------------------------
+
+    /**
+     * Implements the {@link javax.jcr.query.Row} interface, which represents
+     * a row in the query result.
+     */
+    class RowImpl implements Row {
+
+        /**
+         * The score for this result row
+         */
+        private final float score;
+
+        /**
+         * The underlying <code>Node</code> of this result row.
+         */
+        // DIFF JR: use Node instead of NodeImpl
+        private final Node node;
+
+        /**
+         * Cached value array for returned by {@link #getValues()}.
+         */
+        private Value[] values;
+
+        /**
+         * Set of select property <code>QName</code>s.
+         */
+        private Set propertySet;
+
+        /**
+         * Creates a new <code>RowImpl</code> instance based on <code>node</code>.
+         *
+         * @param score the score value for this result row
+         * @param node  the underlying <code>Node</code> for this <code>Row</code>.
+         */
+        // DIFF JR: use Node instead of NodeImpl
+        RowImpl(float score, Node node) {
+            this.score = score;
+            this.node = node;
+        }
+
+        /**
+         * Returns an array of all the values in the same order as the property
+         * names (column names) returned by
+         * {@link javax.jcr.query.QueryResult#getColumnNames()}.
+         *
+         * @return a <code>Value</code> array.
+         * @throws RepositoryException if an error occurs while retrieving the
+         *                             values from the <code>Node</code>.
+         */
+        public Value[] getValues() throws RepositoryException {
+            if (values == null) {
+                Value[] tmp = new Value[properties.length];
+                for (int i = 0; i < properties.length; i++) {
+                    String propName;
+                    try {
+                        propName = resolver.getJCRName(properties[i]);
+                    } catch (NoPrefixDeclaredException e) {
+                        throw new RepositoryException(e.getMessage(), e);
+                    }
+                    if (node.hasProperty(propName)) {
+                        Property prop = node.getProperty(propName);
+                        if (!prop.getDefinition().isMultiple()) {
+                            if (prop.getDefinition().getRequiredType() == PropertyType.UNDEFINED) {
+                                tmp[i] = new StringValue(prop.getString());
+                            } else {
+                                tmp[i] = prop.getValue();
+                            }
+                        } else {
+                            // mvp values cannot be returned
+                            tmp[i] = null;
+                        }
+                    } else {
+                        // property not set or jcr:path / jcr:score
+                        if (QName.JCR_PATH.equals(properties[i])) {
+                            tmp[i] = PathValue.valueOf(node.getPath());
+                        } else if (QName.JCR_SCORE.equals(properties[i])) {
+                            tmp[i] = new LongValue((int) (score * 1000f));
+                        } else {
+                            tmp[i] = null;
+                        }
+                    }
+                }
+                values = tmp;
+            }
+            // return a copy of the array
+            Value[] ret = new Value[values.length];
+            System.arraycopy(values, 0, ret, 0, values.length);
+            return ret;
+        }
+
+        /**
+         * Returns the value of the indicated  property in this <code>Row</code>.
+         * <p/>
+         * If <code>propertyName</code> is not among the column names of the
+         * query result table, an <code>ItemNotFoundException</code> is thrown.
+         *
+         * @return a <code>Value</code>
+         * @throws ItemNotFoundException if <code>propertyName</code> is not
+         *                               among the column names of the query result table.
+         * @throws RepositoryException   if <code>propertyName</code> is not a
+         *                               valid property name.
+         */
+        public Value getValue(String propertyName) throws ItemNotFoundException, RepositoryException {
+            if (propertySet == null) {
+                // create the set first
+                Set tmp = new HashSet();
+                tmp.addAll(Arrays.asList(properties));
+                propertySet = tmp;
+            }
+            try {
+                QName prop = resolver.getQName(propertyName);
+                if (!propertySet.contains(prop)) {
+                    throw new ItemNotFoundException(propertyName);
+                }
+                if (node.hasProperty(propertyName)) {
+                    Property p = node.getProperty(propertyName);
+                    if (p.getDefinition().getRequiredType() == PropertyType.UNDEFINED) {
+                        return new StringValue(p.getString());
+                    } else {
+                        return p.getValue();
+                    }
+                } else {
+                    // either jcr:score, jcr:path or not set
+                    if (QName.JCR_PATH.equals(prop)) {
+                        return PathValue.valueOf(node.getPath());
+                    } else if (QName.JCR_SCORE.equals(prop)) {
+                        return new LongValue((int) (score * 1000f));
+                    } else {
+                        return null;
+                    }
+                }
+            } catch (IllegalNameException e) {
+                throw new RepositoryException(e.getMessage(), e);
+            } catch (UnknownPrefixException e) {
+                throw new RepositoryException(e.getMessage(), e);
+            }
+        }
+    }
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/RowIteratorImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/ScoreNodeIterator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/ScoreNodeIterator.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/ScoreNodeIterator.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/ScoreNodeIterator.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,38 @@
+/*
+ * 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.query;
+// DIFF JR: this class uses a different package than the jackrabbit original
+
+import javax.jcr.NodeIterator;
+
+/**
+ * Extends the {@link javax.jcr.NodeIterator} interface by adding a {@link
+ * #getScore()} method that returns the score for the node that is returned by
+ * {@link javax.jcr.NodeIterator#nextNode()}.
+ */
+public interface ScoreNodeIterator extends NodeIterator {
+
+    /**
+     * Returns the score of the node returned by {@link #nextNode()}. In other
+     * words, this method returns the score value of the next
+     * <code>Node</code>.
+     *
+     * @return the score of the node returned by {@link #nextNode()}.
+     * @throws java.util.NoSuchElementException if there is no next node.
+     */
+    public float getScore();
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/query/ScoreNodeIterator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/AccessManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/AccessManager.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/AccessManager.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/AccessManager.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,107 @@
+/*
+ * 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.security;
+
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.name.Path;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.RepositoryException;
+
+/**
+ * The <code>AccessManager</code> can be queried to determines whether permission
+ * is granted to perform a specific action on a specific item.
+ */
+public interface AccessManager {
+
+    /**
+     * predefined action constants
+     */
+    public String READ_ACTION = "read";
+    public String REMOVE_ACTION = "remove";
+    public String ADD_NODE_ACTION = "add_node";
+    public String SET_PROPERTY_ACTION = "set_property";
+
+    public String[] READ = new String[] {READ_ACTION};
+    public String[] REMOVE = new String[] {REMOVE_ACTION};
+    // TODO public String[] WRITE = new String[0];
+
+    /**
+     * Determines whether the specified <code>permissions</code> are granted
+     * on the item with the specified path.
+     *
+     * @param parentId The node id the existing ancestor.
+     * @param relPath The relative path pointing to the non-existing target item.
+     * @param actions An array of actions that need to be checked.
+     * @return <code>true</code> if the actions are granted; otherwise <code>false</code>
+     * @throws ItemNotFoundException if the target item does not exist
+     * @throws RepositoryException if another error occurs
+     */
+    // TODO: method can be removed, if jcr2spi uses spi-ids as well
+    boolean isGranted(NodeId parentId, Path relPath, String[] actions) throws ItemNotFoundException, RepositoryException;
+
+    /**
+      * Determines whether the specified <code>permissions</code> are granted
+      * on the item with the specified path.
+      *
+      * @param itemId The id of an existing target item.
+      * @param actions An array of actions that need to be checked.
+      * @return <code>true</code> if the actions are granted; otherwise <code>false</code>
+      * @throws ItemNotFoundException if the target item does not exist
+      * @throws RepositoryException if another error occurs
+      */
+     boolean isGranted(ItemId itemId, String[] actions) throws ItemNotFoundException, RepositoryException;
+
+
+    /**
+     * Returns true if the existing item with the given <code>ItemId</code> can
+     * be read.
+     *
+     * @param id The id of an existing target item.
+     * @return
+     * @throws ItemNotFoundException
+     * @throws RepositoryException
+     */
+    boolean canRead(ItemId id) throws ItemNotFoundException, RepositoryException;
+
+    // TODO need for canWrite(ItemId ?)
+
+    /**
+     * Returns true if the existing item with the given <code>ItemId</code> can
+     * be removed.
+     *
+     * @param id The id of an existing target item.
+     * @return
+     * @throws ItemNotFoundException
+     * @throws RepositoryException
+     */
+    boolean canRemove(ItemId id) throws ItemNotFoundException, RepositoryException;
+
+    /**
+     * Determines whether the subject of the current context is granted access
+     * to the given workspace.
+     *
+     * @param workspaceName name of workspace
+     * @return <code>true</code> if the subject of the current context is
+     * granted access to the given workspace; otherwise <code>false</code>.
+     * @throws NoSuchWorkspaceException if a workspace with the given name does not exist.
+     * @throws RepositoryException if another error occurs
+     */
+    boolean canAccess(String workspaceName) throws NoSuchWorkspaceException, RepositoryException;
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/AccessManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/SecurityConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/SecurityConstants.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/SecurityConstants.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/SecurityConstants.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,33 @@
+/*
+ * 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.security;
+
+/**
+ * This interface defines miscellaneous security related constants.
+ */
+public interface SecurityConstants {
+
+    /**
+     * Name of the internal <code>SimpleCredentials</code> attribute where
+     * the <code>Subject</code> of the <i>impersonating</i> <code>Session</code>
+     * is stored.
+     *
+     * @see javax.jcr.Session#impersonate(javax.jcr.Credentials)
+     */
+    String IMPERSONATOR_ATTRIBUTE =
+            "org.apache.jackrabbit.jcr2spi.security.impersonator";
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/security/SecurityConstants.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: 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=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,375 @@
+/*
+ * 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.util.PathMap;
+import org.apache.jackrabbit.name.Path;
+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.jcr2spi.observation.InternalEventListener;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Iterator;
+
+/**
+ * <code>CachingItemStateManager</code> implements an {@link ItemStateManager}
+ * and decorates it with a caching facility.
+ */
+public class CachingItemStateManager implements ItemStateManager, InternalEventListener {
+
+    /**
+     * The underlying item state manager.
+     */
+    private final ItemStateManager ism;
+
+    /**
+     * Maps a String uuid to a {@link NodeState}.
+     */
+    private final Map uuid2PathElement;
+
+    /**
+     * Maps a path to an {@link ItemState}.
+     */
+    private final PathMap path2State;
+
+    public CachingItemStateManager(ItemStateManager ism) {
+        this.ism = ism;
+        this.uuid2PathElement = new HashMap(); // TODO: must use weak references
+        path2State = new PathMap();      // TODO: must use weak references
+    }
+
+    //---------------------------------------------------< ItemStateManager >---
+    /**
+     * @inheritDoc
+     * @see ItemStateManager#getItemState(ItemId)
+     */
+    public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException {
+        return (ItemState) getPathElement(id).get();
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateManager#hasItemState(ItemId)
+     */
+    public boolean hasItemState(ItemId id) {
+        try {
+            getPathElement(id);
+        } catch (ItemStateException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateManager#getNodeReferences(NodeId)
+     */
+    public NodeReferences getNodeReferences(NodeId id)
+            throws NoSuchItemStateException, ItemStateException {
+        // TODO: caching missing
+        return ism.getNodeReferences(id);
+    }
+
+    /**
+     * @inheritDoc
+     * @see ItemStateManager#hasNodeReferences(NodeId)
+     */
+    public boolean hasNodeReferences(NodeId id) {
+        // TODO: caching missing
+        return ism.hasNodeReferences(id);
+    }
+
+    //-------------------------------< InternalEventListener >------------------
+
+    /**
+     * Processes <code>events</code> and invalidates cached <code>ItemState</code>s
+     * accordingly.
+     * @param events 
+     * @param isLocal
+     */
+    public void onEvent(EventIterator events, boolean isLocal) {
+        // if events origin from local changes then
+        // cache does not need invalidation
+        if (isLocal) {
+            return;
+        }
+
+        // collect set of removed node ids
+        Set removedNodeIds = new HashSet();
+        List eventList = new ArrayList();
+        while (events.hasNext()) {
+            Event e = events.nextEvent();
+            eventList.add(e);
+        }
+
+        for (Iterator it = eventList.iterator(); it.hasNext(); ) {
+            Event e = (Event) it.next();
+            ItemId itemId = e.getItemId();
+            NodeId parentId = e.getParentId();
+            ItemState state;
+            NodeState parent;
+            switch (e.getType()) {
+                case Event.NODE_ADDED:
+                case Event.PROPERTY_ADDED:
+                    PathMap.Element elem = lookup(itemId);
+                    if (elem != 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();
+                        }
+                    }
+                    elem = lookup(parentId);
+                    if (elem != null) {
+                        // discard and let wsp manager reload state when accessed next time
+                        parent = (NodeState) elem.get();
+                        if (parent != null) {
+                            parent.discard();
+                            elem.set(null);
+                        }
+                    }
+                    break;
+                case Event.NODE_REMOVED:
+                case Event.PROPERTY_REMOVED:
+                    elem = lookup(itemId);
+                    if (elem != null) {
+                        state = (ItemState) elem.get();
+                        if (itemId.denotesNode()) {
+                            if (itemId.getRelativePath() == null) {
+                                // also remove mapping from uuid
+                                uuid2PathElement.remove(itemId.getUUID());
+                            }
+                        }
+
+                        elem.remove();
+
+                        if (state != null) {
+                            state.notifyStateDestroyed();
+                        }
+                    }
+                    elem = lookup(parentId);
+                    if (elem != null && elem.get() != null) {
+                        parent = (NodeState) elem.get();
+                        // 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
+                            parent.discard();
+                            elem.set(null);
+                        }
+                    }
+                    break;
+                case Event.PROPERTY_CHANGED:
+                    elem = lookup(itemId);
+                    // discard and let wsp manager reload state when accessed next time
+                    if (elem != null) {
+                        state = (ItemState) elem.get();
+                        state.discard();
+                        elem.remove();
+                    }
+            }
+        }
+    }
+
+    //------------------------------< 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()};
+     *
+     * @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.
+     */
+    private PathMap.Element getPathElement(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+        String uuid = id.getUUID();
+        Path relPath = id.getRelativePath();
+        PathMap.Element elem = null;
+
+        // first get PathElement of uuid part
+        if (uuid != null) {
+            elem = (PathMap.Element) uuid2PathElement.get(uuid);
+            if (elem == null || elem.get() == null) {
+                // state identified by the uuid is not yet cached -> get from ISM
+                ItemId refId = (relPath == null) ? id : new NodeIdImpl(uuid);
+                ItemState state = ism.getItemState(refId);
+
+                // 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) {
+                        // TODO put element (Marcel, here goes your extra method)
+                        // i removed it, since its not present in JR commons (anchela)
+                    }
+                    elem.set(state);
+                    // now put current state to cache
+                    uuid2PathElement.put(uuid, elem);
+                }
+            }
+        }
+
+        // at this point we are guaranteed to have an element
+        // now resolve relative path part of id if there is one
+        if (relPath != null) {
+            // TODO map element (Marcel, here goes your extra method)
+            // i removed it, since its not present in JR commons (anchela)
+            PathMap.Element tmp = null;
+            if (tmp == null) {
+                // not yet cached, load from ism
+                ItemState state = ism.getItemState(id);
+                // put to cache
+                // TODO put element (Marcel, here goes your extra method)
+                // i removed it, since its not present in JR commons (anchela)
+                tmp = null;
+                tmp.set(state);
+            }
+            elem = tmp;
+        }
+        return elem;
+    }
+
+    /**
+     * Looks up the <code>Element</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
+     *         present in the cache.
+     */
+    private PathMap.Element lookup(ItemId id) {
+        PathMap.Element elem = null;
+        // resolve UUID
+        if (id.getUUID() != null) {
+            elem = (PathMap.Element) uuid2PathElement.get(id.getUUID());
+            if (elem == null) {
+                // not cached
+                return null;
+            }
+        }
+
+        // resolve relative path
+        if (id.getRelativePath() != null) {
+            // TODO map element (Marcel, here goes your extra method)
+            // i removed it, since its not present in JR commons (anchela)
+            elem = null;
+        }
+
+        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()) {
+            NodeState.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();
+            }
+        } else {
+            return state.getId().getRelativePath().getNameElement();
+        }
+    }
+
+    //--------------------------------------------------------< Inner class >---
+    /**
+     * Simple implementation of the NodeId interface that always wraps around a
+     * UUID String only and never takes a relative path.
+     * Since the uuid is retrieved from an existing <code>ItemId</code> there is
+     * no need to pass the IdFactory and using this simple implementation instead.
+     */
+    private static final class NodeIdImpl implements NodeId {
+
+        private final String uuid;
+
+        public NodeIdImpl(String uuid) {
+            if (uuid == null) {
+                throw new IllegalArgumentException("Expected non-null uuid.");
+            }
+            this.uuid = uuid;
+        }
+
+        /**
+         * Always return <code>true</code>.
+         *
+         * @return true
+         */
+        public boolean denotesNode() {
+            return true;
+        }
+
+        /**
+         * Always returns a non-null string.
+         *
+         * @return uuid passed to the constructor, which is never <code>null</code>.
+         */
+        public String getUUID() {
+            return uuid;
+        }
+
+        /**
+         * Always return <code>null</code>.
+         *
+         * @return <code>null</code>
+         */
+        public Path getRelativePath() {
+            return null;
+        }
+    }
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,379 @@
+/*
+ * 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.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.IdKeyMap;
+import org.apache.jackrabbit.jcr2spi.DefaultIdKeyMap;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.NodeId;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Registers changes made to states and references and consolidates
+ * empty changes.
+ */
+// DIFF JR: implements ItemStateManager instead of separate 'get' 'has'
+public class ChangeLog implements ItemStateManager {
+
+    /**
+     * Added states
+     */
+    // TODO: TO-BE-FIXED. With SPI_ItemId simple map cannot be used any more
+    protected final IdKeyMap addedStates = new DefaultIdKeyMap(); // JR: new LinkedMap();
+
+    /**
+     * Modified states
+     */
+    // TODO: TO-BE-FIXED. With SPI_ItemId simple map cannot be used any more
+    protected final IdKeyMap modifiedStates = new DefaultIdKeyMap(); // JR: new LinkedMap();
+
+    /**
+     * Deleted states
+     */
+    // TODO: TO-BE-FIXED. With SPI_ItemId simple map cannot be used any more
+    protected final IdKeyMap deletedStates = new DefaultIdKeyMap(); // JR: new LinkedMap();
+
+    /**
+     * Modified references
+     */
+    // TODO: TO-BE-FIXED. With SPI_ItemId simple map cannot be used any more
+    protected final IdKeyMap modifiedRefs = new DefaultIdKeyMap(); // JR: new LinkedMap();
+
+    /**
+     * Type of operation this changelog is collection state modifications for.
+     */  
+    private List operations = new ArrayList();
+
+    //-----------------------------------------------< Inform the ChangeLog >---
+    // DIFF JR: method added
+    /**
+     * Add the given operation to the list of operations to be recorded within
+     * the current update cycle of this ChangeLog.
+     *
+     * @param operation
+     */
+    public void addOperation(Operation operation) {
+        operations.add(operation);
+    }
+
+    /**
+     * A state has been added
+     *
+     * @param state state that has been added
+     */
+    public void added(ItemState state) {
+        addedStates.put(state.getId(), state);
+    }
+
+    /**
+     * A state has been modified. If the state is not a new state
+     * (not in the collection of added ones), then disconnect
+     * the local state from its underlying shared state and add
+     * it to the modified states collection.
+     *
+     * @param state state that has been modified
+     */
+    public void modified(ItemState state) {
+        if (!addedStates.containsKey(state.getId())) {
+            state.disconnect();
+            modifiedStates.put(state.getId(), state);
+        }
+    }
+
+    /**
+     * A state has been deleted. If the state is not a new state
+     * (not in the collection of added ones), then disconnect
+     * the local state from its underlying shared state, remove
+     * it from the modified states collection and add it to the
+     * deleted states collection.
+     *
+     * @param state state that has been deleted
+     */
+    public void deleted(ItemState state) {
+        if (addedStates.remove(state.getId()) == null) {
+            state.disconnect();
+            modifiedStates.remove(state.getId());
+            deletedStates.put(state.getId(), state);
+        }
+    }
+
+    /**
+     * A references has been modified
+     *
+     * @param refs refs that has been modified
+     */
+    public void modified(NodeReferences refs) {
+        modifiedRefs.put(refs.getId(), refs);
+    }
+
+    //----------------------< Retrieve information present in the ChangeLog >---
+    // DIFF JR: method added
+    public boolean isEmpty() {
+        return operations.isEmpty();
+    }
+
+    // DIFF JR: method added
+    /**
+     *
+     * @return
+     */
+    public Iterator getOperations() {
+        return operations.iterator();
+    }
+
+    /**
+     * Return an iterator over all added states.
+     *
+     * @return iterator over all added states.
+     */
+    public Iterator addedStates() {
+        return addedStates.values().iterator();
+    }
+
+    /**
+     * Return an iterator over all modified states.
+     *
+     * @return iterator over all modified states.
+     */
+    public Iterator modifiedStates() {
+        return modifiedStates.values().iterator();
+    }
+
+    /**
+     * Return an iterator over all deleted states.
+     *
+     * @return iterator over all deleted states.
+     */
+    public Iterator deletedStates() {
+        return deletedStates.values().iterator();
+    }
+
+    /**
+     * Return an iterator over all modified references.
+     *
+     * @return iterator over all modified references.
+     */
+    public Iterator modifiedRefs() {
+        return modifiedRefs.values().iterator();
+    }
+
+    //-----------------------------< Inform ChangeLog about Success/Failure >---
+    /**
+     * Push all states contained in the various maps of
+     * items we have.
+     */
+    public void push() {
+        Iterator iter = modifiedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).push();
+        }
+        iter = deletedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).push();
+        }
+        iter = addedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).push();
+        }
+    }
+
+    /**
+     * After the states have actually been persisted, update their
+     * internal states and notify listeners.
+     */
+    public void persisted() {
+        Iterator iter = modifiedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            state.setStatus(ItemState.STATUS_EXISTING);
+            state.notifyStateUpdated();
+        }
+        iter = deletedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            state.setStatus(ItemState.STATUS_EXISTING_REMOVED);
+            state.notifyStateDestroyed();
+            state.discard();
+        }
+        iter = addedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            state.setStatus(ItemState.STATUS_EXISTING);
+            state.notifyStateCreated();
+        }
+    }
+
+    /**
+     * Reset this change log, removing all members inside the
+     * maps we built.
+     */
+    public void reset() {
+        addedStates.clear();
+        modifiedStates.clear();
+        deletedStates.clear();
+        modifiedRefs.clear();
+        // also clear all operations
+        operations.clear();
+    }
+
+    /**
+     * Disconnect all states in the change log from their overlaid
+     * states.
+     */
+    public void disconnect() {
+        Iterator iter = modifiedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).disconnect();
+        }
+        iter = deletedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).disconnect();
+        }
+        iter = addedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).disconnect();
+        }
+    }
+
+    /**
+     * Undo changes made to items in the change log. Discards
+     * added items, refreshes modified and resurrects deleted
+     * items.
+     *
+     * @param parent parent manager that will hold current data
+     */
+    public void undo(ItemStateManager parent) {
+        Iterator iter = modifiedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            try {
+                state.connect(parent.getItemState(state.getId()));
+                state.pull();
+            } catch (ItemStateException e) {
+                state.discard();
+            }
+        }
+        iter = deletedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            try {
+                state.connect(parent.getItemState(state.getId()));
+                state.pull();
+            } catch (ItemStateException e) {
+                state.discard();
+            }
+        }
+        iter = addedStates();
+        while (iter.hasNext()) {
+            ((ItemState) iter.next()).discard();
+        }
+        reset();
+    }
+
+    //---------------------------------------------------< ItemStateManager >---
+    /**
+     * Return an item state given its id. Returns <code>null</code>
+     * if the item state is neither in the added nor in the modified
+     * section. Throws a <code>NoSuchItemStateException</code> if
+     * the item state is in the deleted section.
+     *
+     * @return item state or <code>null</code>
+     * @throws NoSuchItemStateException if the item has been deleted
+     * @see ItemStateManager#getItemState(ItemId)
+     */
+    public ItemState getItemState(ItemId id) throws NoSuchItemStateException, ItemStateException {
+        ItemState state = (ItemState) addedStates.get(id);
+        if (state == null) {
+            state = (ItemState) modifiedStates.get(id);
+            if (state == null) {
+                if (deletedStates.containsKey(id)) {
+                    throw new NoSuchItemStateException("State has been marked destroyed: " + id);
+                }
+            }
+        }
+        return state;
+    }
+
+    /**
+     * Return a flag indicating whether a given item state exists.
+     *
+     * @return <code>true</code> if item state exists within this
+     *         log; <code>false</code> otherwise
+     * @see ItemStateManager#hasItemState(ItemId)
+     */
+    public boolean hasItemState(ItemId id) {
+        return addedStates.containsKey(id) || modifiedStates.containsKey(id);
+    }
+
+    /**
+     * Return a node references object given its id. Returns
+     * <code>null</code> if the node reference is not in the modified
+     * section.
+     *
+     * @return node references or <code>null</code>
+     * @see ItemStateManager#getNodeReferences(NodeId)
+     */
+    public NodeReferences getNodeReferences(NodeId id) throws NoSuchItemStateException, ItemStateException {
+        return (NodeReferences) modifiedRefs.get(id);
+    }
+
+    /**
+     * Returns <code>false</code> if the node reference is not in the modified
+     * section.
+     *
+     * @return false if no references are present in this changelog for the
+     * given id.
+     * @see ItemStateManager#hasNodeReferences(NodeId)
+     */
+    public boolean hasNodeReferences(NodeId id) {
+        return modifiedRefs.get(id) != null;
+    }
+
+    //-------------------------------------------------------------< Object >---
+    /**
+     * Returns a string representation of this change log for diagnostic
+     * purposes.
+     *
+     * @return a string representation of this change log
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("{");
+        buf.append("#addedStates=").append(addedStates.size());
+        buf.append(", #modifiedStates=").append(modifiedStates.size());
+        buf.append(", #deletedStates=").append(deletedStates.size());
+        buf.append(", #modifiedRefs=").append(modifiedRefs.size());
+        buf.append("}");
+        return buf.toString();
+    }
+
+    //----------------------------------< for derived classes >-----------------
+
+    /**
+     * Removes the <code>operation</code> from the list of operations.
+     * @param operation the Operation to remove.
+     * @return <code>true</code> if the operation was removed.
+     */
+    protected boolean removeOperation(Operation operation) {
+        // @todo optimize
+        return operations.remove(operation);
+    }
+}

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

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,528 @@
+/*
+ * 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.util.WeakIdentityCollection;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.NodeId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+
+/**
+ * <code>ItemState</code> represents the state of an <code>Item</code>.
+ */
+public abstract class ItemState implements ItemStateListener {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = LoggerFactory.getLogger(ItemState.class);
+
+     //----------------< flags defining the current status of this instance >---
+    /**
+     * the status is undefined
+     */
+    public static final int STATUS_UNDEFINED = 0;
+    /**
+     * 'existing', i.e. persistent state
+     */
+    public static final int STATUS_EXISTING = 1;
+    /**
+     * 'existing', i.e. persistent state that has been transiently modified (copy-on-write)
+     */
+    public static final int STATUS_EXISTING_MODIFIED = 2;
+    /**
+     * 'existing', i.e. persistent state that has been transiently removed (copy-on-write)
+     */
+    public static final int STATUS_EXISTING_REMOVED = 3;
+    /**
+     * 'new' state
+     */
+    public static final int STATUS_NEW = 4;
+    /**
+     * 'existing', i.e. persistent state that has been persistently modified by somebody else
+     */
+    public static final int STATUS_STALE_MODIFIED = 5;
+    /**
+     * 'existing', i.e. persistent state that has been destroyed by somebody else
+     */
+    public static final int STATUS_STALE_DESTROYED = 6;
+
+    /**
+     * the internal status of this item state
+     */
+    protected int status = STATUS_UNDEFINED;
+
+    /**
+     * a modification counter used to prevent concurrent modifications
+     */
+    private short modCount;
+
+    /**
+     * Flag indicating whether this state is transient
+     */
+    private final boolean isTransient;
+
+    /**
+     * Listeners (weak references)
+     */
+    private final transient Collection listeners = new WeakIdentityCollection(5);
+
+    /**
+     * the backing persistent item state (may be null)
+     */
+    protected transient ItemState overlayedState;
+
+    /**
+     * Constructs a new unconnected item state
+     *
+     * @param initialStatus the initial status of the item state object
+     * @param isTransient   flag indicating whether this state is transient or not
+     */
+    protected ItemState(int initialStatus, boolean isTransient) {
+        switch (initialStatus) {
+            case STATUS_EXISTING:
+            case STATUS_NEW:
+                status = initialStatus;
+                break;
+            default:
+                String msg = "illegal status: " + initialStatus;
+                log.debug(msg);
+                throw new IllegalArgumentException(msg);
+        }
+        modCount = 0;
+        overlayedState = null;
+        this.isTransient = isTransient;
+    }
+
+    /**
+     * Constructs a new item state that is initially connected to an overlayed
+     * state.
+     *
+     * @param overlayedState the backing item state being overlayed
+     * @param initialStatus the initial status of the new <code>ItemState</code> instance
+     * @param isTransient   flag indicating whether this state is transient or not
+     */
+    protected ItemState(ItemState overlayedState, int initialStatus, boolean isTransient) {
+        switch (initialStatus) {
+            case STATUS_EXISTING:
+            case STATUS_EXISTING_MODIFIED:
+            case STATUS_EXISTING_REMOVED:
+                status = initialStatus;
+                break;
+            default:
+                String msg = "illegal status: " + initialStatus;
+                log.debug(msg);
+                throw new IllegalArgumentException(msg);
+        }
+        this.isTransient = isTransient;
+        connect(overlayedState);
+    }
+    
+    /**
+     * Copy state information from another state into this state
+     * @param state source state information
+     */
+    protected abstract void copy(ItemState state);
+
+    /**
+     * Pull state information from overlayed state.
+     */
+    void pull() {
+        if (overlayedState != null) {
+            copy(overlayedState);
+            // sync modification count
+            modCount = overlayedState.getModCount();
+        }
+    }
+
+    /**
+     * Push state information into overlayed state.
+     */
+    void push() {
+        if (overlayedState != null) {
+            overlayedState.copy(this);
+        }
+    }
+
+    /**
+     * Called by <code>TransientItemStateManager</code> and
+     * <code>LocalItemStateManager</code> when this item state has been disposed.
+     */
+    void onDisposed() {
+        // prepare this instance so it can be gc'ed
+        synchronized (listeners) {
+            listeners.clear();
+        }
+        disconnect();
+        overlayedState = null;
+        status = STATUS_UNDEFINED;
+    }
+
+    /**
+     * Connect this state to some underlying overlayed state.
+     */
+    protected void connect(ItemState overlayedState) {
+        if (this.overlayedState != null) {
+            if (this.overlayedState != overlayedState) {
+                throw new IllegalStateException("Item state already connected to another underlying state: " + this);
+            }
+        }
+        this.overlayedState = overlayedState;
+        this.overlayedState.addListener(this);
+    }
+
+    /**
+     * Reconnect this state to the overlayed state that it has been
+     * disconnected from earlier.
+     */
+    protected void reconnect() {
+        if (this.overlayedState == null) {
+            throw new IllegalStateException("Item state cannot be reconnected because there's no underlying state to reconnect to: " + this);
+        }
+        this.overlayedState.addListener(this);
+    }
+
+    /**
+     * Disconnect this state from the underlying overlayed state.
+     */
+    protected void disconnect() {
+        if (overlayedState != null) {
+            // de-register listener on overlayed state...
+            overlayedState.removeListener(this);
+            overlayedState = null;
+        }
+    }
+
+    /**
+     * Notify the listeners that the persistent state this object is
+     * representing has been discarded.
+     */
+    protected void notifyStateDiscarded() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].stateDiscarded(this);
+            }
+        }
+    }
+
+    /**
+     * Notify the listeners that the persistent state this object is
+     * representing has been created.
+     */
+    protected void notifyStateCreated() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].stateCreated(this);
+            }
+        }
+    }
+
+    /**
+     * Notify the listeners that the persistent state this object is
+     * representing has been updated.
+     */
+    public void notifyStateUpdated() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].stateModified(this);
+            }
+        }
+    }
+
+    /**
+     * Notify the listeners that the persistent state this object is
+     * representing has been destroyed.
+     */
+    public void notifyStateDestroyed() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].stateDestroyed(this);
+            }
+        }
+    }
+
+    /**
+     * Notify the transient state listeners that this state has overlayed
+     * another state.
+     *
+     * @param overlayer the state that now overlays the state that the listener
+     *                  is registered to.
+     */
+    protected void notifyStateOverlaid(ItemState overlayer) {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] instanceof TransientItemStateListener) {
+                ((TransientItemStateListener) la[i]).stateOverlaid(overlayer);
+            }
+        }
+    }
+
+    /**
+     * Notify the transient state listeners that this state no longer overlays
+     * another state.
+     */
+    protected void notifyStateUncovering() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        ItemStateListener[] la;
+        synchronized (listeners) {
+            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] instanceof TransientItemStateListener) {
+                ((TransientItemStateListener) la[i]).stateUncovering(this);
+            }
+        }
+    }
+
+    //-------------------------------------------------------< public methods >
+    /**
+     * Determines if this item state represents a node.
+     *
+     * @return true if this item state represents a node, otherwise false.
+     */
+    public abstract boolean isNode();
+
+    /**
+     * Returns the identifier of this item state.
+     *
+     * @return the identifier of this item state..
+     */
+    public abstract ItemId getId();
+
+    /**
+     * Returns <code>true</code> if this item state represents new or modified
+     * state (i.e. the result of copy-on-write) or <code>false</code> if it
+     * represents existing, unmodified state.
+     *
+     * @return <code>true</code> if this item state is modified or new,
+     *         otherwise <code>false</code>
+     */
+    public boolean isTransient() {
+        return isTransient;
+    }
+
+    /**
+     * Determines whether this item state has become stale.
+     * @return true if this item state has become stale, false otherwise.
+     */
+    public boolean isStale() {
+        if (isTransient) {
+            return status == STATUS_STALE_MODIFIED
+                || status == STATUS_STALE_DESTROYED;
+        } else {
+            return overlayedState != null
+                && modCount != overlayedState.getModCount();
+        }
+    }
+
+    /**
+     * Returns the NodeId of the parent <code>NodeState</code> or <code>null</code>
+     * if either this item state represents the root node or this item state is
+     * 'free floating', i.e. not attached to the repository's hierarchy.
+     *
+     * @return the parent <code>NodeState</code>'s Id
+     */
+    public abstract NodeId getParentId();
+
+    /**
+     * Returns the status of this item.
+     *
+     * @return the status of this item.
+     */
+    public int getStatus() {
+        return status;
+    }
+
+    /**
+     * Sets the new status of this item.
+     *
+     * @param newStatus the new status
+     */
+    public void setStatus(int newStatus) {
+        switch (newStatus) {
+            case STATUS_NEW:
+            case STATUS_EXISTING:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_EXISTING_MODIFIED:
+            case STATUS_STALE_MODIFIED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_UNDEFINED:
+                status = newStatus;
+                return;
+            default:
+                String msg = "illegal status: " + newStatus;
+                log.debug(msg);
+                throw new IllegalArgumentException(msg);
+        }
+    }
+
+    /**
+     * Returns the modification count.
+     *
+     * @return the modification count.
+     */
+    public short getModCount() {
+        return modCount;
+    }
+
+    /**
+     * Sets the modification count.
+     *
+     * @param modCount the modification count of this item
+     */
+    public void setModCount(short modCount) {
+        this.modCount = modCount;
+    }
+
+    /**
+     * Updates the modification count.
+     */
+    synchronized void touch() {
+        modCount++;
+    }
+
+    /**
+     * Discards this instance, i.e. renders it 'invalid'.
+     */
+    public void discard() {
+        if (status != STATUS_UNDEFINED) {
+            // notify listeners
+            notifyStateDiscarded();
+            // reset status
+            status = STATUS_UNDEFINED;
+        }
+    }
+
+    /**
+     * Determines if this item state is overlying persistent state.
+     *
+     * @return <code>true</code> if this item state is overlying persistent
+     *         state, otherwise <code>false</code>.
+     */
+    public boolean hasOverlayedState() {
+        return overlayedState != null;
+    }
+
+    /**
+     * Returns the persistent state backing <i>this</i> transient state or
+     * <code>null</code> if there is no persistent state (i.e.. <i>this</i>
+     * state is purely transient).
+     *
+     * @return the persistent item state or <code>null</code> if there is
+     *         no persistent state.
+     */
+    public ItemState getOverlayedState() {
+        return overlayedState;
+    }
+
+    /**
+     * Add an <code>ItemStateListener</code>
+     *
+     * @param listener the new listener to be informed on modifications
+     */
+    public void addListener(ItemStateListener listener) {
+        synchronized (listeners) {
+            assert (!listeners.contains(listener));
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove an <code>ItemStateListener</code>
+     *
+     * @param listener an existing listener
+     */
+    public void removeListener(ItemStateListener listener) {
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+    //--------------------------------------------------< ItemStateListener >---
+    /**
+     * {@inheritDoc}
+     */
+    public void stateCreated(ItemState created) {
+        // underlying state has been permanently created
+        status = STATUS_EXISTING;
+        pull();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void stateDestroyed(ItemState destroyed) {
+        // underlying state has been permanently destroyed
+        if (isTransient) {
+            status = STATUS_STALE_DESTROYED;
+        } else {
+            status = STATUS_EXISTING_REMOVED;
+            notifyStateDestroyed();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void stateModified(ItemState modified) {
+        // underlying state has been modified
+        if (isTransient) {
+            status = STATUS_STALE_MODIFIED;
+        } else {
+            synchronized (this) {
+                // this instance represents existing state, update it
+                pull();
+                notifyStateUpdated();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void stateDiscarded(ItemState discarded) {
+        // underlying persistent state has been discarded, discard this instance too
+        discard();
+    }
+}



Mime
View raw message