/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/store/XMemoryStore.java,v 1.3 2004/07/30 06:52:03 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:03 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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.slide.store.tamino.store;
import com.softwareag.common.instrumentation.logging.Level;
import com.softwareag.common.instrumentation.logging.Logger;
import com.softwareag.common.instrumentation.logging.LoggerFactory;
import com.softwareag.common.instrumentation.logging.LoggerUtil;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import org.apache.slide.common.AbstractService;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.Scope;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceDisconnectionFailedException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.ServiceResetFailedException;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionAlreadyExistException;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.RevisionNotFoundException;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.security.NodePermission;
import org.apache.slide.store.ContentStore;
import org.apache.slide.store.LockStore;
import org.apache.slide.store.NodeStore;
import org.apache.slide.store.RevisionDescriptorStore;
import org.apache.slide.store.RevisionDescriptorsStore;
import org.apache.slide.store.SecurityStore;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.ClassName;


/**
 ** Memory store. This non-persistent store serves as base for several store
 ** implementations of te administration scope.
 **
 ** @author    peter.nevermann@softwareag.com
 ** @version   $Revision: 1.3 $
 **
 **/
public class XMemoryStore extends AbstractService implements ContentStore, LockStore,
NodeStore, RevisionDescriptorsStore, RevisionDescriptorStore, SecurityStore {

    private static final String LOGNAME = LoggerUtil.getThisClassName();
    private static final String CLASSNAME = new ClassName(LOGNAME).getPlainName();
    private static Logger logger = LoggerFactory.getLogger(LOGNAME);

    /** The parameters (from Domain.xml).*/
    protected Hashtable parameters = null;


    /** True, if this store is initialized. */
    protected boolean initialized = false;

    /** The namespace access token */
    protected NamespaceAccessToken nsaToken = null;

    /** Node cache */
    protected Map objects = null;

    /** Descriptors cache */
    protected Map descriptors = null;

    /** Descriptor cache */
    protected Map descriptor = null;

    /** Content cache */
    protected Map content = null;

    /** Security cache */
    protected Map permissions = null;

    /** Lock cache */
    protected Map locks = null;



    /**
     ** Default constructor.
     **/
    public XMemoryStore() {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "<init>" );

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "<init>" );
    }

    /**
     * Return true, if this store is initialized.
     * @return true, if this store is initialized
     */
    public boolean isInitialized() {
        return initialized;
    }

    /**
     ** Get the associated namespace.
     **
     ** @return     return the namespace
     **/
    public Namespace getNamespace() {
        return namespace;
    }

    /**
     ** Get the associated scope.
     **
     ** @return     return the scope
     **/
    public Scope getScope() {
        return scope;
    }

    /**
     ** Initialize the caches.
     **/
    private void init() {
        objects = new HashMap();
        descriptors = new HashMap();
        descriptor = new HashMap();
        content = new HashMap();
        permissions = new HashMap();
        locks = new HashMap();
    }

    /**
     * Initializes this child store with a set of parameters.
     * These are:
     * <li>not yet defined...
     *
     * @param parameters Hashtable containing the parameters' name
     * and associated value
     *
     * @exception   ServiceParameterErrorException
     * @exception   ServiceParameterMissingException
     */
    public void setParameters(Hashtable parameters)
    throws ServiceParameterErrorException, ServiceParameterMissingException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "setParameters", new Object[] {parameters} );

        this.parameters = parameters;

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "setParameters" );
    }

    /**
     * Connects to this child store.
     *
     * @exception ServiceConnectionFailedException
     */
    public synchronized void connect() throws ServiceConnectionFailedException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "connect" );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "connect" );
    }

    /**
     * Disconnects from this child store.
     *
     * @exception ServiceDisconnectionFailedException
     */
    public void disconnect() throws ServiceDisconnectionFailedException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "disconnect" );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "disconnect" );
    }

    /**
     * Initializes this child store.
     * @param token token
     * @exception ServiceInitializationFailedException Throws an exception
     * if this child store has already been initialized before
     */
    public synchronized void initialize( NamespaceAccessToken token )
    throws ServiceInitializationFailedException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "initialize",
            new Object[] {(token!=null ? token.getName() : null)} );

        if( initialized )
            throw new ServiceInitializationFailedException( this, "Already initialized" );

        this.initialized = true;
        this.nsaToken = token;
        init();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "initialize" );
    }

    /**
     * Deletes content store.
     *
     * @exception ServiceResetFailedException
     */
    public void reset() throws ServiceResetFailedException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "reset" );

        init();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "reset" );
    }

    /**
     * This function tells whether or not this child store is connected.
     *
     * @return boolean true if we are connected
     * @exception ServiceAccessException Service access error
     */
    public boolean isConnected() throws ServiceAccessException {
        boolean result = true;
        return result;
    }

    /**
     * Indicates whether or not the objects managed by this service should be
     * cached. Caching is enabled by default.
     *
     * @return boolean True if results should be cached
     */
    public boolean cacheResults() {
        boolean result = false;
        return result;
    }

    /**
     * Commit the global transaction specified by xid.
     * @param xid  transaction id
     * @param onePhase handle in one phase
     * @exception XAException transaction error
     */
    public void commit( Xid xid, boolean onePhase ) throws XAException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "commit",
            new Object[] {xid, new Boolean(onePhase)}  );

        super.commit( xid, onePhase );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "commit" );
    }

    /**
     * Inform the resource manager to roll back work done on behalf of a
     * transaction branch.
     * @param xid transaction id
     * @exception XAException transaction error
     */
    public void rollback( Xid xid ) throws XAException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "rollback",
            new Object[] {xid}  );

        super.rollback( xid );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "rollback" );
    }

    // ------------------------------------------------------------------------
    // ContentStore interface
    // ------------------------------------------------------------------------

    /**
     * Retrieve revision content.
     *
     * @param uri Uri
     * @param revisionDescriptor revision descriptor
     * @return a node revision content
     * @exception ServiceAccessException Error accessing the revision descriptor
     * @exception RevisionNotFoundException Error accessing the revision descriptor
     */
    public NodeRevisionContent retrieveRevisionContent(
    Uri uri, NodeRevisionDescriptor revisionDescriptor)
    throws ServiceAccessException, RevisionNotFoundException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "retrieveRevisionContent", new Object[] {uri,
            (revisionDescriptor!=null
               ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
               : null) } );

        String uriStr = uri.toString();
        NodeRevisionContent rc = (NodeRevisionContent) content.get(uriStr);

        if( rc == null ) {
            throw new RevisionNotFoundException(
                String.valueOf(uri), revisionDescriptor.getRevisionNumber() );
        }

        NodeRevisionContent result = rc;

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "retrieveRevisionContent", result );
        return result;
    }

    /**
     * Create a new revision
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @param revisionContent Node revision content
     * @exception ServiceAccessException Error accessing the revision descriptor
     * @exception RevisionAlreadyExistException Error accessing the revision descriptor
     */
    public void createRevisionContent( Uri uri, NodeRevisionDescriptor revisionDescriptor,
    NodeRevisionContent revisionContent )
    throws ServiceAccessException, RevisionAlreadyExistException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "createRevisionContent", new Object[] {uri,
            (revisionDescriptor!=null
                 ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
                 : null), revisionContent} );

        String uriStr = uri.toString();
        if( content.containsKey(uriStr) ) {
            throw new RevisionAlreadyExistException(
                String.valueOf(uri), revisionDescriptor.getRevisionNumber() );
        }

        // clone the content, for the input stream may point to a (closed)
        // http input stream.
        NodeRevisionContent clonedContent = new NodeRevisionContent();
        clonedContent.setContent(revisionContent.getContentBytes());
        content.put( uriStr, clonedContent );

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "createRevisionContent" );
    }

    /**
     * Modify the latest revision of an object.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @param revisionContent Node revision content
     * @exception ServiceAccessException Error accessing the revision descriptor
     * @exception RevisionNotFoundException Error accessing the revision descriptor
     */
    public void storeRevisionContent( Uri uri, NodeRevisionDescriptor revisionDescriptor,
    NodeRevisionContent revisionContent)
    throws ServiceAccessException, RevisionNotFoundException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "storeRevisionContent", new Object[] {uri,
            (revisionDescriptor!=null
                 ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
                 : null), revisionContent} );

        String uriStr = uri.toString();
        NodeRevisionContent rc = (NodeRevisionContent) content.get(uriStr);

        if( rc == null ) {
            throw new RevisionNotFoundException(
                String.valueOf(uri), revisionDescriptor.getRevisionNumber() );
        }

        content.put( uriStr, revisionContent );

        if (logger.isLoggable(Level.FINE))
            logger.exiting( CLASSNAME, "storeRevisionContent" );
    }

    /**
     * Remove revision.
     *
     * @param uri Uri
     * @param revisionDescriptor revision descriptor
     * @exception ServiceAccessException Error accessing the revision descriptor
     *
     */
    public void removeRevisionContent( Uri uri, NodeRevisionDescriptor revisionDescriptor )
    throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "removeRevisionContent", new Object[] {uri,
            (revisionDescriptor!=null
                 ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
                 : null)} );

        content.remove( uri.toString() );

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "removeRevisionContent" );
    }

    // ------------------------------------------------------------------------
    // NodeStore interface
    // ------------------------------------------------------------------------

    /**
     * Retrieve an object from the Descriptors Store.
     *
     * @param uri Uri of the object we want to retrieve
     * @return an object node
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to retrieve was not found
     */
    public ObjectNode retrieveObject(Uri uri)
    throws ServiceAccessException, ObjectNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "retrieveObject",
            new Object[] {uri} );

        String uriStr = uri.toString();
        Object tempObject = objects.get(uriStr);
        if (tempObject == null) {
            throw new ObjectNotFoundException(uri);
        }
        ObjectNode result = ((ObjectNode) tempObject).cloneObject();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "retrieveObject",
            (result!=null ? result.getUri() : null) );
        return result;
    }

    /**
     * Store an object in the Descriptors Store.
     *
     * @param uri Uri of the object we want to update
     * @param object Object to update
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to update was not found
     */
    public void storeObject(Uri uri, ObjectNode object)
    throws ServiceAccessException, ObjectNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "storeObject",
            new Object[] {uri, (object!=null ? object.getUri() : null)} );

        String uriStr = uri.toString();
        if( !objects.containsKey(uriStr) )
            throw new ObjectNotFoundException(uri);

        objects.put( uriStr, object.cloneObject() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "storeObject" );
    }

    /**
     * Create a new object in the Descriptors Store.
     *
     * @param uri Uri of the object we want to create
     * @param object SlideObject
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectAlreadyExistsException An object already exists
     * at this Uri
     */
    public void createObject(Uri uri, ObjectNode object)
    throws ServiceAccessException, ObjectAlreadyExistsException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "createObject",
            new Object[] {uri, (object!=null ? object.getUri() : null)} );

        String uriStr = uri.toString();
        if( objects.containsKey(uriStr) )
            throw new ObjectAlreadyExistsException(uriStr);

        objects.put( uriStr, object.cloneObject() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "createObject" );
    }

    /**
     * Remove an object from the Descriptors Store.
     *
     * @param uri Uri of the object we want to remove
     * @param object Object to remove **IGNORED**
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to remove was not found
     */
    public void removeObject(Uri uri, ObjectNode object)
    throws ServiceAccessException, ObjectNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "removeObject",
            new Object[] {uri, (object!=null ? object.getUri() : null)} );

        String uriStr = uri.toString();
        if (!objects.containsKey(uriStr)) {
            throw new ObjectNotFoundException(uri);
        }
        objects.remove(uriStr);


        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "removeObject" );
    }

    // ------------------------------------------------------------------------
    // SecurityStore interface
    // ------------------------------------------------------------------------

    /**
     * Store an object permissions in the Descriptors Store.
     *
     * @param uri Uri of the resource
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void grantPermission(Uri uri, NodePermission permission)
    throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "grantPermission",
            new Object[] {uri, permission} );

        String uriStr = uri.toString();
        Object value = permissions.get(uriStr);
        Vector permissionsVector = null;
        if (value == null) {
            permissionsVector = new Vector();
            permissions.put(uriStr, permissionsVector);
        } else {
            permissionsVector = (Vector) value;
        }
        permissionsVector.addElement(permission.cloneObject());

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "grantPermission" );
    }

    /**
     * Revoke an object permissions in the Descriptors Store.
     *
     * @param uri Uri of the resource
     * @param permission Permission we want to create
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void revokePermission(Uri uri, NodePermission permission)
    throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "revokePermission", new Object[] {uri, permission} );

        Object value = permissions.get(uri.toString());
        Vector permissionsVector = null;
        if (value != null) {
            permissionsVector = (Vector) value;
            permissionsVector.removeElement(permission);
        }

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "revokePermission" );
    }

    /**
     * Revoke all the permissions on the object.
     *
     * @param uri Uri of the resource
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public void revokePermissions(Uri uri) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "revokePermissions", new Object[] {uri} );

        Object value = permissions.get(uri.toString());
        Vector permissionsVector = null;
        if (value != null) {
            permissionsVector = (Vector) value;
            permissionsVector.removeAllElements();
        }

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "revokePermissions" );
    }

    /**
     * Enumerate permissions on the resource given by uri.
     *
     * @param uri Uri of the resource
     * @return the permissions as enumeration
     * @exception ServiceAccessException Error accessing the Descriptors Store
     */
    public Enumeration enumeratePermissions(Uri uri) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "enumeratePermissions", new Object[] {uri} );

        Object value = permissions.get(uri.toString());
        Vector permissionsVector = null;
        if (value != null) {
            permissionsVector = (Vector) value;
        } else {
            permissionsVector = new Vector();
        }
        Enumeration result = permissionsVector.elements();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "enumeratePermissions", result );
        return result;
    }

    // ------------------------------------------------------------------------
    // LockStore interface
    // ------------------------------------------------------------------------

    /**
     * Puts a lock on a subject.
     *
     * @param uri Uri of the resource
     * @param lock Lock token
     * @exception ServiceAccessException Service access error
     */
    public void putLock(Uri uri, NodeLock lock) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "putLock",
            new Object[] {uri, (lock!=null ? lock.getLockId() : null)} );

        Object value = locks.get(uri.toString());
        Vector locksVector = null;
        if (value == null) {
            locksVector = new Vector();
            locks.put(uri.toString(), locksVector);
        } else {
            locksVector = (Vector) value;
        }
        locksVector.addElement(lock.cloneObject());

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "putLock" );
    }

    /**
     * Renews a lock.
     *
     * @param uri Uri of the resource
     * @param lock Token to renew
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void renewLock(Uri uri, NodeLock lock)
    throws ServiceAccessException, LockTokenNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "renewLock",
            new Object[] {uri, (lock!=null ? lock.getLockId() : null)} );

        Object value = locks.get(uri.toString());
        Vector locksVector = null;
        if (value == null) {
            throw new LockTokenNotFoundException(lock);
        } else {
            locksVector = (Vector) value;
            boolean wasPresent = locksVector.removeElement(lock);
            if (!wasPresent) {
                throw new LockTokenNotFoundException(lock);
            }
            locksVector.addElement(lock.cloneObject());
        }

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "renewLock" );
    }

    /**
     * Removes (cancels) a lock.
     *
     * @param uri Uri of the resource
     * @param lock Token to remove
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void removeLock(Uri uri, NodeLock lock)
    throws ServiceAccessException, LockTokenNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "removeLock",
            new Object[] {uri, (lock!=null ? lock.getLockId() : null)} );

        Object value = locks.get(uri.toString());
        Vector locksVector = null;
        if (value == null) {
            throw new LockTokenNotFoundException(lock);
        } else {
            locksVector = (Vector) value;
            boolean wasPresent = locksVector.removeElement(lock);
            if (!wasPresent) {
                throw new LockTokenNotFoundException(lock);
            }
        }

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "removeLock" );
    }

    /**
     * Kills a lock.
     *
     * @param uri Uri of the resource
     * @param lock Token to remove
     * @exception ServiceAccessException Service access error
     * @exception LockTokenNotFoundException Lock token was not found
     */
    public void killLock(Uri uri, NodeLock lock)
    throws ServiceAccessException, LockTokenNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "killLock",
            new Object[] {uri, (lock!=null ? lock.getLockId() : null)} );

        // What's the difference between killLock and removeLock??
        removeLock(uri, lock);

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "killLock" );
    }

    /**
     * Returns the list of locks put on a subject.
     *
     * @param uri Uri of the resource
     * @return Enumeration List of locks which have been put on the subject
     * @exception ServiceAccessException Service access error
     */
    public Enumeration enumerateLocks(Uri uri) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "enumerateLocks",
            new Object[] {uri} );

        Object value = locks.get(uri.toString());
        Vector locksVector = null;
        if (value == null) {
            locksVector = new Vector();
        } else {
            locksVector = (Vector) value;
        }
        Enumeration result = locksVector.elements();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "enumerateLocks",
            result );
        return result;
    }

    // ------------------------------------------------------------------------
    // RevisionDescriptorsStore interface
    // ------------------------------------------------------------------------

    /**
     * Retrieve a revision descriptors.
     *
     * @param uri Uri
     * @return node revision descriptors
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public NodeRevisionDescriptors retrieveRevisionDescriptors(Uri uri)
    throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "retrieveRevisionDescriptors", new Object[] {uri} );

        String uriStr = uri.toString();
        NodeRevisionDescriptors rds = (NodeRevisionDescriptors) descriptors.get(uriStr);

        if( rds == null ) {
            throw new RevisionDescriptorNotFoundException( uriStr );
        }

        NodeRevisionDescriptors result = rds.cloneObject();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "retrieveRevisionDescriptors", (result!=null ? result.getUri() : null) );
        return result;
    }

    /**
     * Create new revision descriptors.
     *
     * @param uri Uri
     * @param revisionDescriptors Node revision descriptors
     * @exception ServiceAccessException Service access error
     */
    public void createRevisionDescriptors
    (Uri uri, NodeRevisionDescriptors revisionDescriptors)
    throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "createRevisionDescriptors", new Object[] {uri,
            (revisionDescriptors!=null ? revisionDescriptors.getUri() : null)} );

        descriptors.put( uri.toString(), revisionDescriptors.cloneObject() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "createRevisionDescriptors" );
    }

    /**
     * Update revision descriptors.
     *
     * @param uri Uri
     * @param revisionDescriptors Node revision descriptors
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public void storeRevisionDescriptors
    (Uri uri, NodeRevisionDescriptors revisionDescriptors)
    throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "storeRevisionDescriptors", new Object[] {uri,
            (revisionDescriptors!=null ? revisionDescriptors.getUri() : null)} );

        String uriStr = uri.toString();
        if( !descriptors.containsKey(uriStr) )
            throw new RevisionDescriptorNotFoundException(uriStr);

        descriptors.put( uriStr, revisionDescriptors.cloneObject() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "storeRevisionDescriptors" );
    }

    /**
     * Remove revision descriptors.
     *
     * @param uri uri
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptors(Uri uri) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "removeRevisionDescriptors", new Object[] {uri} );

        descriptors.remove( uri.toString() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "removeRevisionDescriptors" );
    }

    // ------------------------------------------------------------------------
    // RevisionDescriptorStore interface
    // ------------------------------------------------------------------------

    /**
     * Retrieve revision descriptor.
     *
     * @param uri uri
     * @param revisionNumber Node revision number
     * @return a node revision descriptor
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException revision descriptor not found
     */
    public NodeRevisionDescriptor retrieveRevisionDescriptor
    (Uri uri, NodeRevisionNumber revisionNumber)
    throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "retrieveRevisionDescriptor", new Object[] {uri, revisionNumber} );

        String uriStr = uri.toString();
        NodeRevisionDescriptor result = null;
        NodeRevisionDescriptor rd =
            (NodeRevisionDescriptor) descriptor.get( uriStr+"-"+revisionNumber );
        if( rd == null ) {
            throw new RevisionDescriptorNotFoundException(uriStr);
        }
        result = rd.cloneObject();

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "retrieveRevisionDescriptor", (result!=null
            ? result.getRevisionNumber()+"@"+result.getBranchName()
            : null) );
        return result;
    }

    /**
     * Create new revision descriptor.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @exception ServiceAccessException Service access error
     */
    public void createRevisionDescriptor
    (Uri uri, NodeRevisionDescriptor revisionDescriptor) throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "createRevisionDescriptor", new Object[] {uri, (revisionDescriptor!=null
            ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
            : null)} );

        String uriStr = uri.toString();
        NodeRevisionDescriptor rd = revisionDescriptor.cloneObject();
        descriptor.put( uriStr+"-"+rd.getRevisionNumber(), rd );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "createRevisionDescriptor" );
    }

    /**
     * Update revision descriptor.
     *
     * @param uri Uri
     * @param revisionDescriptor Node revision descriptor
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException Revision descriptor
     * was not found
     */
    public void storeRevisionDescriptor
    (Uri uri, NodeRevisionDescriptor revisionDescriptor)
    throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "storeRevisionDescriptor", new Object[] {uri, (revisionDescriptor!=null
            ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
            : null)} );

        String uriStr = uri.toString();
        String key = uriStr+"-"+revisionDescriptor.getRevisionNumber();

        if( !descriptor.containsKey(key) )
            throw new RevisionDescriptorNotFoundException(uriStr);

        descriptor.put( key, revisionDescriptor.cloneObject() );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "storeRevisionDescriptor" );
    }

    /**
     * Remove revision descriptor.
     *
     * @param uri Uri
     * @param number Revision number
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptor(Uri uri, NodeRevisionNumber number)
    throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
            "removeRevisionDescriptor", new Object[] {uri, number} );

        String uriStr = uri.toString();
        descriptor.remove( uriStr+"-"+number );

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
            "removeRevisionDescriptor" );
    }



    /**
     * Return true if the given node revision descriptor is a collection.
     * @param nrd node revision descriptor
     * @return true if the given node revision descriptor is a collection
     */
    protected boolean isCollection( NodeRevisionDescriptor nrd ) {
        boolean result = false;
        if (nrd.propertyValueContains(NodeRevisionDescriptor.RESOURCE_TYPE ,"collection")) {
            result = true;
        }
        return result;
    }
}

