/*
 * Copyright (C) 2005-2007 Alfresco Software Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * As a special exception to the terms and conditions of version 2.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * and Open Source Software ("FLOSS") applications as described in Alfresco's
 * FLOSS exception.  You should have recieved a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * http://www.alfresco.com/legal/licensing" */

package org.alfresco.service.cmr.avm;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;

import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;

/**
 * This is the service interface for the 
 * Alfresco Versioning Model (AVM).
 *
 * Because the AVM is a versioning repository, fully explicit 
 * references to the nodes within it consist of an absolute AVM path,
 * and a version ID.  Absolute AVM paths are of
 * the form:&nbsp;&nbsp;<em>&lt;store-name&gt;</em>:<em>&lt;store-relative-path&gt;</em>.  
 * <p>
 * For example:&nbsp;&nbsp;<code>mystore:/www/avm_webapps/ROOT/x/y/z.html</code>
 * <p>
 * Within AVMService, whenever an API takes a <em>path</em> 
 * and a <em>name</em>, the <em>path</em> is the parent 
 * directory in which the <em>name</em> appears.
 * Whenever just a <em>path</em> is needed by an API, it is an absolute 
 * path to a file or directory.
 * <p>
 * The special version ID <strong><code> -1 </code></strong> (negative one)
 * refers to the latest read/write version at the given absolute AVM path.  
 * Non-negative version ID values refer to read-only snapshots of a store. 
 * For this reason, the version ID <strong><code> -1 </code></strong> 
 * is implicit for all write operations.  Sometimes, 
 * <strong><code> -1 </code></strong> is referred to as the
 * <strong><code>HEAD</code></strong> version (a term that should be
 * already be familiar to users of other versioning systems like
 * <a href="http://en.wikipedia.org/wiki/Concurrent_Versions_System">CVS</a>
 * and <a href="http://en.wikipedia.org/wiki/Subversion_(software)">SVN</a>).
 * <p>
 * Snapshots can be created explicitly via a call to 
 * {@link #createSnapshot(String store, String tag, String description) createSnapshot},
 * or implicitly by various APIs in this interface and in
 * {@link org.alfresco.service.cmr.avmsync.AVMSyncService AVMSyncService}.
 * While a new snapshot of a store will have version a ID one higher 
 * than the previous snapshot in that store, the history of an AMV store
 * does not necessarily contain a continuous range of version ID values,
 * because {@link #purgeVersion(int version, String name) purgeVersion}
 * may have been called. However, version ID values are never recycled 
 * within a store.
 *
 * @author britt
 */
public interface AVMService
{
    /**
     * Get an InputStream for reading the contents of a 
     * file identified by its version ID and AVM path.
     * This method can be used for either plain or layered files.
     *
     * @param  version The version ID to look in.
     * @param  path    The absolute path to the file.
     * @return         An InputStream for the designated file.
     * @throws         AVMNotFoundException
     * @throws         AVMWrongTypeException
     */
    public InputStream getFileInputStream(int version, String path);
    
    /**
     * Get an InputStream for reading the contents of a 
     * file node identified by its descriptor.
     * This method can be used for either plain or layered files.
     *
     * @param desc The descriptor.
     * @return An InputStream.
     * @throws AVMNotFoundException
     */
    public InputStream getFileInputStream(AVMNodeDescriptor desc);
    

    /**
     * Get an output stream to write to a file 
     * identified by an AVM path.  This file must already exist.  
     * This method can be used for either plain or layered files.
     *
     * To create a plain file, see:
     * {@link #createFile(String path, String name) createFile}.
     * To create a layered file, see:
     * {@link #createLayeredFile(String targetPath, String parent, String name) createLayeredFile}.
     *
     * @param  path The absolute path to the file.
     * @throws      AVMNotFoundException    
     * @throws      AVMWrongTypeException 
     */
    public OutputStream getFileOutputStream(String path);
    

    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Fetch a content reader for a file node.
     * 
     * This function is similar to 
     * {@link getFileInputStream(int version, String path) getFileInputStream};
     * however, it can be used to fetch either InputStream or a 
     * random-access nio channel.
     *
     * @param  version The version ID of the file.
     * @param  path    The absolute AVM path to the file.
     * @return         A ContentReader.
     * @throws         AVMNotFoundException
     * @throws         AVMWrongTypeException
     */
    public ContentReader getContentReader(int version, String path);

    
    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Fetch a ContentWriter to a file node.
     *
     * @param path The path to the file.
     * @return A ContentWriter.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public ContentWriter getContentWriter(String path);   


    /**
     * Get a non-recursive listing of a directory
     * identified by its version ID and path.
     * If <code>path</code> does not refer to a directory
     * node, <code>AVMWrongTypeException</code> is thrown.
     * 
     * @param  version The version ID to look in.
     * @param  path    The absolute AVM path to the file.
     * @return         A Map of names to descriptors.
     * @throws         AVMNotFoundException
     * @throws         AVMWrongTypeException
     */
    public SortedMap<String, AVMNodeDescriptor> getDirectoryListing(int version, String path);


    /**
     * Get a non-recursive listing of a directory node identified by its
     * version ID and path; optionally, deleted notes can be included in
     * this listing.
     * <p>
     * If instead, you wish to obtain a list of <em>only</em> 
     * the deleted nodes within a directory, see:
     * {@link #getDeleted(int version, String path) getDeleted}.
     *
     * @param version        The version ID to look in.
     * @param path           The absolute AVM path to the file.
     * @param includeDeleted Whether to include deleted nodes
     * @return               A Map of names to descriptors.
     * @throws               AVMNotFoundException 
     * @throws               AVMWrongTypeException
     */
    public SortedMap<String, AVMNodeDescriptor> getDirectoryListing(int version, String path,
                                                                    boolean includeDeleted);
    
    /**
     * Get a non-recursive listing of nodes contained by a directory identified
     * by its version ID and path, but exclude all nodes that are only 
     * contained "indirectly" via layering.
     * <p>
     * If this function is called on a "plain" (non-layered) directory,
     * it is equivalent to 
     * {@link #getDirectoryListing(int version, String path) getDirectoryListing}.
     *
     * @param version The version to look up.
     * @param path The full path to get listing for.
     * @return A Map of names to descriptors.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException 
     */
    public SortedMap<String, AVMNodeDescriptor> 
        getDirectoryListingDirect(int version, String path);
    
    /**
     * Get a non-recursive listing of nodes contained directly,
     * but exclude all nodes that are only contained "indirectly"
     * (i.e.: via layering).  This function is identical to 
     * {@link #getDirectoryListingDirect(int version, String path) getDirectoryListingDirect}
     * but it has the option of including deleted nodes in the listing.
     * 
     * @param version        The version to look up.
     * @param path           The absolute AVM directory path to get a listing for.
     * @param includeDeleted Whether to include deleted nodes.
     * @return               A Map of names to descriptors.
     * @throws               AVMNotFoundException
     * @throws               AVMWrongTypeException 
     */
    public SortedMap<String, AVMNodeDescriptor>
        getDirectoryListingDirect(int version, String path, boolean includeDeleted);


    /**
     * A convenience method to get a directory listing 
     * as an Array of AVMNodeDescriptors; this function
     * is otherwise equivalent to 
     * {@link #getDirectoryListing(int version, String path, boolean includeDeleted) getDirectoryListing}
     *
     * @param version        The version to look under.
     * @param path           The path to the directory to be listed.
     * @param includeDeleted Whether to include deleted nodes
     * @return               An array of AVMNodeDescriptors.
     * @throws               AVMNotFoundException
     * @throws               AVMWrongTypeException
     */
    public AVMNodeDescriptor [] getDirectoryListingArray(int version, String path,
                                                         boolean includeDeleted);
    
    /**
     * Get a non-recursive listing of all the nodes contained by a directory
     * identified by an AVMNodeDescriptor, excluding those that are only 
     * present "indirectly" via layering; optionally, deleted nodes that
     * are directly contained can be included this listing.
     *
     * @param dir            The directory descriptor.
     * @param includeDeleted Whether to include directly contained deleted nodes
     * @return               A Map of Strings to descriptors.
     * @throws               AVMNotFoundException
     * @throws               AVMWrongTypeException
     */
    public SortedMap<String, AVMNodeDescriptor>
        getDirectoryListingDirect(AVMNodeDescriptor dir, boolean includeDeleted);
    

    /**
     * Get a non-recursive directory listing of a directory node
     * identified by a node descriptor.
     *
     * @param dir The directory node descriptor.
     * @return A Map of names to node descriptors.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public SortedMap<String, AVMNodeDescriptor> getDirectoryListing(AVMNodeDescriptor dir);
    
    /**
     * Get a non-recursive directory listing of a directory identified
     * by a node descriptor;  optionally, deleted nodes can be included 
     * in this listing.
     *
     * @param dir            The directory node descriptor.
     * @param includeDeleted Whether to include deleted nodes.
     * @return               A Map of names to node descriptors.
     * @throws               AVMNotFoundException If the descriptor is stale.
     * @throws               AVMWrongTypeException If the descriptor does not point at a directory.
     */
    public SortedMap<String, AVMNodeDescriptor> getDirectoryListing(AVMNodeDescriptor dir,
                                                                    boolean includeDeleted);
    
    /**
     * A convenience method to get a non-recursive directory listing 
     * as an Array from a directory identified by a node descriptor;  
     * optionally, deleted nodes can be included in this listing.  
     *
     * This function is identical to 
     * {@link getDirectoryListing(AVMNodeDescriptor dir,boolean includeDeleted)  getDirectoryListing}
     * except that it returns an array.
     *
     * @param dir            The descriptor pointing at the directory to list.
     * @param includeDeleted Whether include deleted nodes
     * @return               An array of AVMNodeDescriptors.
     * @throws               AVMNotFoundException 
     * @throws               AVMWrongTypeException
     */
    public AVMNodeDescriptor [] getDirectoryListingArray(AVMNodeDescriptor dir,
                                                         boolean includeDeleted);
    

    /**
     * Get the names of nodes that have been deleted in a directory
     * identified by a version ID and a path.
     *
     * @param version The version to look under.
     * @param path    The path of the directory.
     * @return        A List of names.
     * @throws        AVMNotFoundException 
     * @throws        AVMWrongTypeException 
     */
    public List<String> getDeleted(int version, String path);
    

    /**
     * Create a new "plain" (non-layered) file within a path. 
     * This function fails if the file already exists,
     * or if the directory identified by <code>path</code>
     * does not exist.
     *
     * @param path The path of the directory containing the created file.
     * @param name The name of the new file
     * @throws     AVMNotFound 
     * @throws     AVMExists
     * @throws     AVMWrongType
     */
    public OutputStream createFile(String path, String name);
    


    /**
     * Create a new "plain" (non-layered) file.  
     * Guarantees that the entire contents of the
     * input stream will be loaded atomically.
     * The directory identified by <code>path</code> must already exist.
     *
     * @param path The path of the directory containing the created file.
     * @param name The name of the new file
     * @param in   An input stream with data for the file.
     * @throws     AVMNotFound
     * @throws     AVMExists
     * @throws     AVMWrongType
     */
    public void createFile(String path, String name, InputStream in);
    

    /**
     * Create a new directory. 
     * If <code>path</code> is within a layer, the new directory will be a layered directory;
     * otherwise, the new directory will be a plain directory.
     *
     * @param path The simple absolute path to the parent.
     * @param name The name to give the directory.
     * @throws     AVMNotFound
     * @throws     AVMExists
     * @throws     AVMWrongType
     */  
    public void createDirectory(String path, String name);
    

    /**
     * Create a new layered file. 
     * <p>
     * Note: the target of the indirection does not need to exist at 
     * the time the layered file node is created.
     * 
     * @param targetPath The absolute path of the underlying file being pointed at
     * @param parent     The absolute path of the directory containing layered file to be created
     * @param name       The name of the layered file to be created
     * @throws           AVMNotFound
     * @throws           AVMExists
     * @throws           AVMWrongType
     */
    public void createLayeredFile(String targetPath, String parent, String name);
    

    /**
     * Create a new layered directory. In whatever context this is created, this
     * will be a layered directory that has a primary indirection.
     * <p>
     * Note: a "primary" indirection is one in which the target is explicitly set;
     * "non-primary" indirect nodes compute their effective target dynamically
     * on the basis of their relative position to the closest "primary"
     * indirect node that contains them. Therefore, changing the target of a 
     * "primary" layered directory node immediately alters the indirection 
     * targets computed by the "non-primary" layered nodes it contains.
     * <p>
     * Note: the target of the indirection does not need to exist at 
     * the time the layered directory node is created.
     *
     * @param targetPath The absolute path to the underlying directory that 
     *                   the layered directory being created will point at.
     * @param parent     The absolute path to directory containing the layered directory being created.
     * @param name       The name of the layered directory being created
     * @throws           AVMNotFound
     * @throws           AVMExists
     * @throws           AVMWrongType
     */
    public void createLayeredDirectory(String targetPath, String parent, String name);


    /**
     * Retarget a layered directory. 
     * Change the target pointed to by a layered directory node.
     * This has the side effect of making the layered directory 
     * a primary indirection if the layered directory's indirection
     * was "non-primary".
     *
     * @param path   Path to the layered directory.
     * @param target The new indirection target of the layered directory
     * @throws       AVMNotFoundException
     * @throws       AVMWrongTypeException
     */
    public void retargetLayeredDirectory(String path, String target);
    

    /**
     * Create a new AVMStore.  
     * All stores are top level objects within the AVM repository.
     * The AVM is a forest of versioned trees;  each versioned
     * tree is contained within a AVM store with a unique 
     * name.  If a store is removed via 
     * {@link purgeStore(String name) purgeStore}, the name of
     * the deleted store can be reused in a later call to 
     * @{link createStore(String name) createStore}.
     * <p>
     * The store name must be non-null, cannot be the empty string,
     * and must not contain characters that are illegal in 
     * normal file names.
     *
     * @param name The name of the new AVMStore.
     * @throws     AVMExistsException
     */
    public void createStore(String name);
    

    /**
     * Create a branch from a given version and path.  As a side effect,
     * an automatic snapshot is taken of the store that contains the 
     * node that is being branched from.
     *
     * @param version The version number from which to make the branch.
     * @param srcPath The path to the node to branch from.
     * @param dstPath The path to the directory to contain the new branch.
     * @param name    The name to give the new branch.
     * @throws        AVMNotFoundException
     * @throws        AVMExistsException
     * @throws        AVMWrongTypeException
     */
    public void createBranch(int version, String srcPath, String dstPath, String name);
    

    /**
     * Remove a file or directory from its parent directory.
     * In a layered context, the newly deleted node will hide 
     * a file of the same name in the corresponding layer below.
     * <p>
     * If instead you want to make the file in the lower layer visible
     * via transparency, see: 
     * {@link uncover(String dirPath, String name) uncover}.
     * If you want to perform a removal and an uncover
     * operation atomically, see:
     * {@link #makeTransparent(String dirPath, String name) makeTransparent}.
     * 
     *
     * <p>
     * Caution:  this removes directories even if they are not empty.
     *
     * <p>
     * Note: developers of records management systems must also
     * be aware that the AVMNode corresponding to the 
     * <code>parent</code> directory and <code>name</code>
     * provided might still be accessible via different
     * path lookups after this function has completed;
     * this because branching and versioning operations create
     * manifestations of nodes in a manner that is similar
     * to a UNIX hard link.  If you need to discover every 
     * possible path that could retrieve the associated AVMNode, see:
     * {@link #getPaths(AVMNodeDescriptor desc) getPaths},
     * {@link #getHeadPaths(AVMNodeDescriptor desc) getHeadPaths}, and
     * {@link #getPathsInStoreHead(AVMNodeDescriptor desc) getPathsInStoreHead}.
     *
     * @param parent The absolute path to the parent directory. 
     * @param name   The name of the child to remove.
     * @throws       AVMNotFoundException
     * @throws       AVMWrongTypeException
     */
    public void removeNode(String parent, String name);
    

    /**
     * A convenience method that removes a node specified by an AVM path.
     * It merely parses an absolute path into a parent directory and a child
     * name, then calls {@link #removeNode(parent, name) removeNode}.
     *
     * @param path The full path to the node to remove.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public void removeNode(String path);
    

    /**
     * Rename a file or directory.
     * There are a number of things to note about the
     * interaction of rename and layering:
     * <p>
     * <ul>
     *   <li> If you rename a layered directory into a non layered context, 
     *        the layered directory continues to point to the same place 
     *        it did before it was renamed, and automatically becomes a 
     *        primary indirection node.  
     *   </li>
     *
     *   <li> If a plain directory is renamed into a layered context it 
     *        remains a plain directory, thus acting as an opaque node 
     *        in its new layered home.
     *   </li>
     *  
     *   <li>  Renaming a layered node into a layered node leaves the renamed 
     *         node pointing to the same place it did before the rename and 
     *         makes it automatically a primary indirection node.
     *   </li>
     *
     *   <li>  After a layered node or directory is renamed, 
     *         the directory that contained it acquires a 
     *         "deleted" node in its place;  therefore, 
     *         if a layer beneath it contains a file or directory
     *         with the same name as the "deleted" node, it
     *         will not be visible via transparency
     *         (unless an  {@link uncover(String dirPath, String name) uncover}
     *         operation is performed afterwards).
     *   </li>
     * </ul>
     * <p>
     *
     * Note: if instead you want to rename an AVM store, see 
     * {@link #renameStore(String sourceName, String destName) renameStore}.
     *
     * @param srcParent The absolute path to the parent directory.
     * @param srcName   The name of the node in the src directory.
     * @param dstParent The absolute path to the destination directory.
     * @param dstName   The name that the node will have in the destination directory.
     * @throws          AVMNotFoundException 
     * @throws          AVMExistsException 
     */
    public void rename(String srcParent, String srcName, String dstParent, String dstName);
    

    /**
     * If a layered directory <code>dirPath</code> 
     * has a deleted entry of the given <code>name</code>, 
     * remove that name from the deleted list,
     * so that if a layer below it contains an entry
     * of this name, it can be seen via transparency
     * from <code>dirPath</code>.  
     * <p>
     * Note: if you are looking for an atomic operation
     * that first deletes an object, then performs
     * an "uncover" operation to make it transparent, see 
     * {@link #makeTransparent(String dirPath, String name) makeTransparent}.
     *
     * @param dirPath The path to the layered directory.
     * @param name    The name to uncover.
     * @throws        AVMNotFoundException
     * @throws        AVMWrongTypeException
     */
    public void uncover(String dirPath, String name);


    /**
     * Atomically delete <code>name</code> within <code>dirPath</code> 
     * and {@link uncover(String dirPath, String name) uncover}
     * it so whatever is underneath can be seen via transparency.
     * If <code>name</code> corresponds to a deletion already,
     * then the deletion step is skipped, and the "uncover"
     * operation is performed.
     *
     * @param dirPath The path to the layered directory.
     * @param name    The name of the item this method will 
     *             {@link org.alfresco.service.cmr.avmsync.AVMSyncService#flatten(String layerPath, String underlyingPath) flatten}
     *
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public void makeTransparent(String dirPath, String name);
    

    /**
     * Gets the ID that the next snapshotted version of a store 
     * will have.
     *
     *<p>
     *  Note: unless the operations that require this value
     *  to be valid are performed within a transaction,
     *  this value can become "stale".
     *
     * @param storeName The name of the AVMStore.
     * @return          The next version ID of the AVMStore.
     * @throws          AVMNotFoundException
     */
    public int getNextVersionID(String storeName);
    

    /**
     * Get the latest snapshot ID of a store. 
     * @param storeName The store name.
     * @return          The ID of the latest extant version of the store.
     * @throws          AVMNotFoundException
     */
    public int getLatestSnapshotID(String storeName);

    
    /**
     * Snapshot the given AVMStore.
     * @param store The name of the AVMStore to snapshot.
     * @param tag The short description.
     * @param description The thick description.
     * @throws AVMNotFoundException
     */
    public int createSnapshot(String store, String tag, String description);
    

    /**
     * Get the set of versions in an AVMStore. The version ID values
     * within this list will always be may appear out of order, 
     * and may contain missing values (due to the possibility that
     * {@link #purgeStore(String name) purgeStore} operations have
     * been performed).
     *
     * Because the number of versions that a store can contain 
     * may become large, this call can be a resource-intensive, 
     * and may even causing Out of Memory exceptions.
     *
     * @param name The name of the AVMStore.
     * @return     A Set of version descriptors.
     * @throws     AVMNotFoundException
     */
    public List<VersionDescriptor> getStoreVersions(String name);
    

    /**
     * Get AVMStore version descriptors by creation date. Either 
     * <code>from</code> or <code>to</code> can be null but not both.  
     * <p>
     * <ul>
     *     <li>
     *         If <code>from</code> is null, all versions earlier than 
     *         <code>to</code> will be returned.  
     *     </li>
     *
     *     <li>
     *         If <code>to</code> is null, all versions later than 
     *         <code>from</code> will be returned.  
     *     </li>
     * </ul>
     * <p>
     *
     * The order of the values returned is not guaranteed, nor are the version 
     * IDs necessarily free of "missing" values (due to the possibility that 
     * {@link #purgeStore(String name) purgeStore} operations have
     * been performed).
     *
     * <p>
     * Note: for portability, all dates are stored as 64-bit longs, with a
     * time resolution of one millisecond.  Therefore, aliasing/wrapping 
     * are not a concern unless you need to plan 292.4 million years ahead.
     * If so, please contact your system administrator.
     *
     * @param name The name of the AVMStore.
     * @param from Earliest date of version to include.
     * @param to   Latest date of version to include.
     * @return     The Set of version descriptors that match.
     * @throws     AVMNotFoundException
     */
    public List<VersionDescriptor> getStoreVersions(String name, Date from, Date to);
    
    /**
     * Get the descriptors of all AVMStores in the repository. 
     *
     * @return A List of all AVMStores.
     */
    public List<AVMStoreDescriptor> getStores();



    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp;
     * Retrieve the reserved "system" store.  
     * This method isn't currently used,
     * but may be in some future release.
     *
     * @return The descriptor.
     */
    public AVMStoreDescriptor getSystemStore();
    

    /**
     * Get a descriptor for an AVMStore.
     * @param name The AVMStore's name.
     * @return     A Descriptor, or null if not found.
     */
    public AVMStoreDescriptor getStore(String name);
    

    /**
     * A convenience method for getting the specified 
     * root directory of an AVMStore (e.g.:  <code>"mysite:/"</code>).
     *
     * @param version The version to look up.
     * @param name The name of the AVMStore.
     * @return A descriptor for the specified root.
     * @throws AVMNotFoundException
     */
    public AVMNodeDescriptor getStoreRoot(int version, String name);

        
    /**
     * Lookup a node identified by version ID and path.
     *
     * @param version The version ID to look under.
     * @param path The simple absolute path to the parent directory.
     * @return An AVMNodeDescriptor, or null if the node does not exist.
     */
    public AVMNodeDescriptor lookup(int version, String path);
    
    /**
     * Lookup a node identified by version ID and path; optionally,
     * if the node is deleted, its descriptor can still
     * be retrieved. 
     *
     * @param version        The version ID to look under.
     * @param path           The simple absolute path to the parent directory.
     * @param includeDeleted Whether to allow a deleted node to be retrieved
     * @return               An AVMNodeDescriptor, or null if the version does not exist.
     */
    public AVMNodeDescriptor lookup(int version, String path, boolean includeDeleted);
    
    /**
     * Lookup a node identified by the directory node that contains it, and its name.
     *
     * @param dir  The descriptor for the directory node.
     * @param name The name to lookup.
     * @return     The descriptor for the child.
     * @throws     AVMWrongTypeException If <code>dir</code> does not refer to a directory.
     */
    public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name);
    
    /**
     * Lookup a node identified by the directory that contains it, and its name;
     * optionally, the lookup can retrive the descriptor of a node even if 
     * it has been deleted from its containing directory.
     *
     * @param dir            The descriptor for the directory node.
     * @param name           The name to lookup.
     * @param includeDeleted Whether to allow a deleted node to be retrieved via the lookup
     * @return               The descriptor for the child, null if the child doesn't exist.
     * @throws               AVMNotFoundException
     * @throws               AVMWrongTypeException
     */
    public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name, boolean includeDeleted);
    

    /**
     * Get a list of all paths that a given node has.  
     * This can be an extremely expensive operation due to the large number
     * of paths to an AVMNodeDescriptor that can be generated
     * via branching and versioning.  For example, if an asset
     * is present in the initial version of a store, and
     * that store has been versioned 10,000 times, 
     * there are a minimum of 10,000 paths that lead to it.
     * The effect of branching is multiplicative.
     * <p>
     * Note:  paths that only access <code>desc</code>
     * via transparency are not returned by this function;
     * only "direct" containment relationships are considered.
     * <p>
     * For those concerned with records management applications,
     * it's worth noting that once every path to an asset has
     * been deleted, the system will purge it entirely in an
     * asynchronous manner.
     *
     * @param desc The node descriptor to get paths for.
     * @return     A List of version, path Pairs.
     * @throws     AVMNotFoundException
     */
    public List<Pair<Integer, String>> getPaths(AVMNodeDescriptor desc);
    

    /**
     * Get a single valid path to a given node.
     * @param desc The node descriptor to get a path for.
     * @return AVMNotFoundException
     */
    public Pair<Integer, String> getAPath(AVMNodeDescriptor desc);
    
    /**
     * Get all paths that a given node has that are in the 
     * <strong><code>HEAD</code></strong> version (<strong><code> -1 </code></strong>).
     * This can be an expensive operation but less so than getPaths().
     *
     * @param desc The node descriptor to get paths for.
     * @return     A List of version, path Pairs.
     * @throws     AVMNotFoundException
     */
    public List<Pair<Integer, String>> getHeadPaths(AVMNodeDescriptor desc);
    
    /**
     * Get all paths to a node starting at the 
     * <strong><code>HEAD</code></strong> version (<strong><code> -1 </code></strong>)
     * of a store.  This can be an expensive operation but less so than getHeadPaths().
     *
     * @param desc The node descriptor.
     * @param      store The store.
     * @return     A List of all paths meeting the criteria.
     * @throws     AVMNotFoundException
     */
    public List<Pair<Integer, String>> getPathsInStoreHead(AVMNodeDescriptor desc, String store);
    

    /**
     * Get the indirection path for a node in a layered context
     * whether that indirection path is primary or non-primary
     * (or seen via transparency).
     *
     * If you call getIndirectionPath on a layered node, 
     * you'll fetch its explicitly set target; if you call 
     * this function on a non-primary indirection node or a 
     * node seen via transparency, you'll get back the path 
     * to the corresponding node in the underlying target.
     * <p>
     * For example, if "mysite--alice:/www" is a layered
     * directory that targets "mysite:/www", and "mysite--alice"
     * contains no content directly, then if the path
     * path "mysite:/www/avm_webapps/ROOT/x/y/z" is valid,
     * calling <code>getIndirectionPath</code> on
     * "mysite--alice:/www/avm_webapps/ROOT/x/y/z" will yield
     * "mysite:/www/avm_webapps/ROOT/x/y/z".
     * 
     * @param version The version number to get.
     * @param path    The path to the node of interest.
     * @return        The indirection path, or null if the path is not in a layered context.
     * @throws        AVMNotFoundException
     * @throws        AVMWrongTypeException
     */
    public String getIndirectionPath(int version, String path);
    
    /**
     * Purge an AVMStore.  
     * This completely removes an AVMStore.
     * <p>
     * Note:  while the store being purged disappears from view 
     * immediately, any nodes that become unreachable as a result 
     * are deleted asynchronously.
     * 
     * @param  name The name of the AVMStore.
     * @throws      AVMNotFoundException 
     */
    public void purgeStore(String name);
    
    /**
     * Purge a version from an AVMStore.  
     * Deletes everything that lives in the given version only.
     *
     * @param version                The version to purge.
     * @param name                   The name of the AVMStore from which to purge it.
     * @throws AVMNotFoundException If <code>name</code> or <code>version</code>
     * do not exist.
     */
    public void purgeVersion(int version, String name);
    

    /**
     * Make a directory into a primary indirection node.
     * @param path The full path.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public void makePrimary(String path);
    

    /**
     * Get a list of up to count nodes in the history chain of a node.
     * The initial element of the list returned will be <code>desc</code>
     * (as long as the count is non-zero).
     *
     * @param desc  The descriptor for a node to find ancestors for.
     * @param count maximum number of ancestors to return in the list 
     *              (the value <strong><code> -1 </code></strong> means 
     *              "no limit -- return them all")
     * @return      A List of ancestors starting with the most recent.
     * @throws      AVMNotFoundException
     */
    public List<AVMNodeDescriptor> getHistory(AVMNodeDescriptor desc, int count);
    
    /**
     * Set the opacity of a layered directory.  
     * An opaque layered directory hides the contents of its indirection.
     *
     * @param path The path to the layered directory.
     * @throws     AVMNotFoundException
     * @throws     AVMWrongTypeException
     */
    public void setOpacity(String path, boolean opacity);
    
    /**
     * Get the common ancestor of two nodes if a common ancestor exists.
     * This function is useful for detecting and merging conflicts.
     *
     * @param left  The first node.
     * @param right The second node.
     * @return The common ancestor. There are four possible results. Null means
     * that there is no common ancestor.  Left returned means that left is strictly
     * an ancestor of right.  Right returned means that right is strictly an
     * ancestor of left.  Any other non null return is the common ancestor and
     * indicates that left and right are in conflict.
     * @throws AVMNotFoundException 
     */
    public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left,
                                               AVMNodeDescriptor right);
    
    /**
     * Get layering information about a path.
     * The LayeringDescriptor returned can be used to determine
     * whether a node is in the background (and if so, which 
     * AVM store contains it directly), or if it is directly
     * contained by the AVM store referenced by <code>path</code>.
     * 
     *
     * @param version The version to look under.
     * @param path    The absolute AVM path.
     * @return        A LayeringDescriptor.
     * @throws        AVMNotFoundException
     * @throws        AVMWrongTypeException
     */
    public LayeringDescriptor getLayeringInfo(int version, String path);
    
    /**
     * Set a property on a node.
     *
     * @param path  The path to the node to set the property on. 
     * @param name  The QName of the property.
     * @param value The property to set.
     * @throws      AVMNotFoundException
     * @throws      AVMWrongTypeException
     */
    public void setNodeProperty(String path, QName name, PropertyValue value);
    
    /**
     * Set a collection of properties on a node.
     *
     * @param path       The path to the node.
     * @param properties The Map of properties to set.
     * @throws           AVMNotFoundException
     * @throws           AVMWrongTypeException
     */
    public void setNodeProperties(String path, Map<QName, PropertyValue> properties);
    
    /**
     * Get a property of a node by QName.
     *
     * @param version The version to look under.
     * @param path    The path to the node.
     * @param name    The QName.
     * @return        The PropertyValue or null if it doesn't exist.
     * @throws        AVMNotFoundException
     * @throws        AVMWrongTypeException
     */
    public PropertyValue getNodeProperty(int version, String path, QName name);
    
    /**
     * Get all the properties associated with a node that is identified
     * by a version ID and a path.
     *
     * @param version The version to look under.
     * @param path    The path to the node.
     * @return        A Map of QNames to PropertyValues.
     * @throws        AVMNotFoundException
     */
    public Map<QName, PropertyValue> getNodeProperties(int version, String path);
    
    /**
     * Delete a property.
     *
     * @param path The path to the node.
     * @param name The QName of the property to delete.
     * @throws     AVMNotFoundException
     */
    public void deleteNodeProperty(String path, QName name);
    
    /**
     * Delete all the properties attached to an AVM node.
     *
     * @param path The path to the node.
     * @throws AVMNotFoundException
     */
    public void deleteNodeProperties(String path);
    
    /**
     * Set a property on a store. If the property exists it will be overwritten.
     *
     * @param store The store to set the property on.
     * @param name  The name of the property.
     * @param value The value of the property.
     * @throws      AVMNotFoundException
     */
    public void setStoreProperty(String store, QName name, PropertyValue value);
    
    /**
     * Set a group of properties on a store. Existing properties will be overwritten.
     *
     * @param store The name of the store.
     * @param props A Map of the properties to set.
     * @throws      AVMNotFoundException
     */
    public void setStoreProperties(String store, Map<QName, PropertyValue> props);
    
    /**
     * Get a property from a store.
     *
     * @param store The name of the store.
     * @param name  The name of the property.
     * @return      A PropertyValue or null if non-existent.
     * @throws      AVMNotFoundException
     */
    public PropertyValue getStoreProperty(String store, QName name);
    
    /**
     * Get all the properties associated with a store.
     *
     * @param store The name of the store.
     * @return      A Map of the stores properties.
     * @throws      AVMNotFoundException
     */
    public Map<QName, PropertyValue> getStoreProperties(String store);

    /**
     * Queries a given store for properties with keys that match a given pattern. 
     *
     * @param store      The name of the store.
     * @param keyPattern The sql 'like' pattern, inserted into a QName.
     * @return           A Map of the matching key value pairs.
     */
    public Map<QName, PropertyValue> queryStorePropertyKey(String store, QName keyPattern);
    
    /**
     * Queries all AVM stores for properties with keys that match a given pattern. 
     *
     * @param keyPattern The sql 'like' pattern, inserted into a QName.
     * @return           A Map of store names to Maps of property key value pairs that match
     * the pattern.
     */
    public Map<String, Map<QName, PropertyValue>>
        queryStoresPropertyKeys(QName keyPattern);
    
    /**
     * Delete a property on a store by name.
     *
     * @param store The name of the store.
     * @param name  The name of the property to delete.
     * @throws      AVMNotFoundException
     */
    public void deleteStoreProperty(String store, QName name);
    

    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Get the ContentData for 
     * a node in a read context.  Only applies to a file.
     *
     * @param version The version to look under.
     * @param path The path to the node.
     * @return The ContentData object.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public ContentData getContentDataForRead(int version, String path);
    

    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Get the ContentData for 
     * a node in a write context.
     *
     * @param path The path to the node.
     * @return The ContentData object.
     * @throws AVMNotFoundException
     * @throws AVMWrongTypeException
     */
    public ContentData getContentDataForWrite(String path);
    

    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Set the content data on a file. 
     *
     * @param path The path to the file.
     * @param data The ContentData to set.
     * @throws     AVMNotFoundException
     * @throws     AVMWrongTypeException
     */
    public void setContentData(String path, ContentData data);
    

    /**
     * Set all metadata on a node from another node. Aspects, properties, ACLs.
     *
     * @param path The path to the node to set.
     * @param from The descriptor for the node to get metadata from.
     * @throws     AVMNotFoundException
     */
    public void setMetaDataFrom(String path, AVMNodeDescriptor from);
    

    /**
     * Add an aspect to an AVM node.
     *
     * @param path       The path to the node.
     * @param aspectName The QName of the aspect.
     * @throws           AVMNotFoundException
     * @throws           AVMExistsException
     */
    public void addAspect(String path, QName aspectName);
    
    /**
     * Get all the aspects on an AVM node.
     *
     * @param version The version to look under.
     * @param path    The path to the node.
     * @return        A List of the QNames of the aspects.
     * @throws        AVMNotFoundException   
     */
    public List<QName> getAspects(int version, String path);
    
    /**
     * Remove an aspect and its properties from a node.
     *
     * @param path       The path to the node.
     * @param aspectName The name of the aspect.
     * @throws           AVMNotFoundException
     */
    public void removeAspect(String path, QName aspectName);
    
    /**
     * Determines whether a node has a particular aspect.
     *
     * @param version    The version to look under.
     * @param path       The path to the node.
     * @param aspectName The aspect name to check.
     * @return           Whether the given node has the given aspect.
     * @throws           AVMNotFoundException
     */
    public boolean hasAspect(int version, String path, QName aspectName);
    

    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp; Insert a node 
     * into a parent directly.  Caution: this is not something 
     * one ordinary applications should do, but it is used 
     * internally by the AVMSyncService.update() method. 
     * This function may disappear from the public interface.
     *
     * @param parentPath The path to the parent directory.
     * @param name       The name to give the node.
     * @param toLink     A descriptor for the node to insert.
     * @throws           AVMNotFoundException
     */
    public void link(String parentPath, String name, AVMNodeDescriptor toLink);
    
    /**
     * <em>Low-level internal function</em>:&nbsp;&nbsp;  Force a copy on write 
     * write event on the given node.  This function is not usually 
     * needed, and may be removed from the public interface.
     *
     * @param path The path to force.
     * @throws     AVMNotFoundException
     */
    public AVMNodeDescriptor forceCopy(String path);
    

    /**
     * Perform a non-virtual (heavy-weight), and potentially recursive
     * copy of the source into the destination directory. 
     * All node properties, access control lists (ACLs), 
     * and aspects are copied.
     *
     * @param srcVersion The version of the source.
     * @param srcPath    The path of the source directory.
     * @param dstPath    The directory containing the destination directory.
     * @param name       The name of the destination within <code>dstPath</code>.
     * @throws           AVMNotFoundException
     */
    public void copy(int srcVersion, String srcPath, String dstPath, String name);
    

    /**
     * Rename a store.
     *
     * @param sourceName The original name.
     * @param destName   The new name.
     * @throws           AVMNotFoundException
     * @throws           AVMExistsException
     */
    public void renameStore(String sourceName, String destName);
    
    /**
     * Revert a <strong><code>HEAD</code></strong> path to a given version. 
     * This works by cloning the version to revert to, and then linking 
     * that new version into <strong><code>HEAD</code></strong>.
     * The reverted version will have the previous 
     * <strong><code>HEAD</code></strong> version as ancestor.
     *
     * @param path       The path to the node to revert.
     * @param toRevertTo The descriptor of the version to revert to.
     * @throws            AVMNotFoundException
     */
    public void revert(String path, AVMNodeDescriptor toRevertTo);
}
