/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/wvcm/src/org/apache/wvcm/ControllableResourceImpl.java,v 1.14 2004/07/30 06:52:25 ozeigermann Exp $
 * $Revision: 1.14 $
 * $Date: 2004/07/30 06:52:25 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Slide", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */


package org.apache.wvcm;

import java.util.Iterator;
import java.util.List;
import javax.wvcm.Configuration;
import javax.wvcm.ControllableResource;
import javax.wvcm.Folder;
import javax.wvcm.Location;
import javax.wvcm.LockToken;
import javax.wvcm.PropertyNameList;
import javax.wvcm.PropertyNameList.PropertyName;
import javax.wvcm.Resource;
import javax.wvcm.Version;
import javax.wvcm.VersionHistory;
import javax.wvcm.Workspace;
import javax.wvcm.WvcmException;
import javax.wvcm.WvcmException.ReasonCode;
import org.apache.wvcm.store.ControllableResourceAccessor;

/**
 * Implementation of ControllableResource.
 *
 * @author <a href="mailto:peter.nevermann@softwareag.com">Peter Nevermann</a>
 * @version $Revision: 1.14 $
 */
public class ControllableResourceImpl extends ResourceImpl implements ControllableResource {
    
    /**
     * Constructor
     */
    public ControllableResourceImpl( Location location ) {
        super (location);
    }
    
    /**
     * Return the version-controlled configuration of the
     * baseline-controlled folder of which this {@link ControllableResource} is a member.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.VERSION_CONTROLLED_CONFIGURATION</code>
     * as a wanted property.
     */
    public Configuration getControlledConfiguration() throws WvcmException {
        return (Configuration)loadedProperties().get( PropertyName.VERSION_CONTROLLED_CONFIGURATION );
    }
    
    /**
     * Set the ActivityList property of this {@link ControllableResource}.
     * @param activityList A list of {@link Activity} objects that identify
     * the new value for the ActivityList property.
     */
    public void setActivityList(List activityList) {
        loadedProperties().set( PropertyName.ACTIVITY_LIST, activityList );
    }
    
    /**
     * Create a new persistent resource at the
     * location identified by this {@link ControllableResource}.  The request will
     * fail if a resource already exists at that location.
     */
    public void doCreateResource() throws WvcmException {
        try {
            ((ControllableResourceAccessor)accessor()).doCreateResource();
        }
        catch (WvcmException e) {
            if (e.getReasonCode() != ReasonCode.LOCKED) {
                throw e;
            }
            // retry with lock-tokens from parent
            Folder parentFolder =
                (Folder)location().parent().folder().doReadProperties(null);
            Iterator i = parentFolder.getLockTokens().iterator();
            while (i.hasNext()) {
                addLockToken((LockToken)i.next());
            }
            ((ControllableResourceAccessor)accessor()).doCreateResource();
        }
    }
    
    /**
     * Merge the version specified in <code>source</code> into the resource
     * identified by this {@link ControllableResource} (the "target").
     * If the target is checked-in, and the source version is a descendant
     * of the target version, the target is updated to the source version.
     * If the target is checked-in, and the source version is an ancestor
     * of the target version, the target is left unmodified.
     * If the target is checked-out, or if the target version is on a different
     * line of descent than the source version, the target is checked out
     * (if it is not already checked-out), and the source version is either
     * added to the MergeList or the AutoMergeList of the target.
     * If a source version is added to the AutoMergeList, the server must
     * have updated the content of the target to contain the results of merging
     * the content of the source version with the content of the target at the
     * time of the merge.
     *
     * @return A {@link ControllableResource} that contains the requested properties
     * of the resource modified by the merge.
     * @param source The version to be merged.
     * @param noAutoMerge The server is not allowed to auto-merge.
     * @param noCheckout The server is not allowed to checkout files for merging.
     * @param forkOK Passed into any checkout requests.
     * @param unreserved Passed into any checkout requests.
     * @param activityList Passed into any checkout requests.
     * @param newActivity Passed into any checkout requests.
     * @param wantedPropertyList The properties available in the returned proxy.
     */
    public ControllableResource doMerge(Version source, boolean noAutoMerge, boolean noCheckout, boolean forkOK, boolean unreserved, List activityList, boolean newActivity, PropertyNameList wantedPropertyList) throws WvcmException {
        // TODO: prio=m, effort=1.5, descr=(merge)
        return null;
    }
    
    /**
     * If this {@link ControllableResource} is checked-in,
     * return whether the version whose content
     * is identical to that of this {@link ControllableResource};
     * otherwise, return <code>null</code>.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.CHECKED_IN</code> as a wanted property.
     */
    public Version getCheckedIn() throws WvcmException {
        return (Version)loadedProperties().get( PropertyName.CHECKED_IN );
    }
    
    /**
     * Return a {@link Resource} containing the wanted properties
     * that are available on the client host
     * without communicating with the server.
     * @param wantedPropertyList The properties available in the returned proxy.
     * @see Resource#doReadProperties doReadProperties
     */
    public Resource readProperties(PropertyNameList wantedPropertyList) throws WvcmException {
        // TODO: prio=l, effort=5.0, descr=(property format to be invented)
        return null;
    }
    
    /**
     * If this {@link ControllableResource} is checked-out,
     * return the value of getCheckedIn
     * when the checkout was performed; otherwise,
     * return <code>null</code>.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.CHECKED_OUT</code> as a wanted property.
     */
    public Version getCheckedOut() throws WvcmException {
        return (Version)loadedProperties().get( PropertyName.CHECKED_OUT );
    }
    
    /**
     * If the resource content is available on the client host,
     * the resource content is written to <code>content</code>
     * and <code>content</code> is closed.
     * @see Resource#doReadContent doReadContent
     * @return A Resource containing the wanted properties
     * that are available on the client host
     * without communicating with the server.
     */
    public Resource readContent(PropertyNameList wantedPropertyList, java.io.OutputStream content) throws WvcmException {
        // TODO: prio=l, effort=2.0, descr=(infrastructure for FS to be provided)
        return null;
    }
    
    /**
     * Apply {@link Version#doAddLabel Version.doAddLabel} to the CheckedIn version of this {@link ControllableResource}.
     */
    public void doAddLabel(String label) throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doAddLabel( label );
    }
    
    /**
     * If this {@link ControllableResource} is checked-out,
     * return a list of {@link Version} objects that identify the versions that
     * will become the predecessors of the version created
     * when this {@link ControllableResource} is checked in;
     * otherwise, return <code>null</code>.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.PREDECESSOR_LIST</code> as a wanted property.
     */
    public List getPredecessorList() throws WvcmException {
        return (List)loadedProperties().get( PropertyName.PREDECESSOR_LIST );
    }
    
    /**
     * Specify the versions that will be the predecessors
     * of the version created when this {@link ControllableResource} is checked in.
     * @param versionList A list of {@link Version} objects that will
     * be the predecessors of the version created when this {@link ControllableResource}
     * is checked in.
     */
    public void setPredecessorList(List versionList) {
        loadedProperties().set( PropertyName.PREDECESSOR_LIST, versionList );
    }
    
    /**
     * Require that the resource be checked out to be modified
     * and checked in to commit the modifications.
     * If the server tracks versions, each checkin creates
     * a new version in the version history of the resource.
     */
    public void doControl() throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doControl();
    }
    
    /**
     * Apply {@link Version#doRemoveLabel Version.doRemoveLabel} to the CheckedIn version of this {@link ControllableResource}.
     */
    public void doRemoveLabel(String label) throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doRemoveLabel( label );
    }
    
    /**
     * Checkout this {@link ControllableResource} so that its content can be modified.
     * @param forkOK Indicates whether to do the checkout even if the
     * version already has a successor or a checkout.
     * @param activityList A list of {@link Activity} objects that identify
     * the activities to which the work on the
     * checked-out resource will contribute.
     * @param newActivity Indicates whether to create a new activity
     * for the checkout.
     * @param unreserved Indicates whether to do the checkout even
     * if there already is a checkout from the currently selected
     * version in one of the activities specified in the activityList.
     */
    public void doCheckout(boolean forkOK, List activityList, boolean newActivity, boolean unreserved) throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doCheckout( forkOK, activityList, newActivity, unreserved );
    }
    
    /**
     * Changes the state of the resource identified by this {@link ControllableResource}
     * to be checked-in, and synchronizes the persistent state
     * of the resource to be that of the persistent state
     * on the client.  If version history is being tracked for the
     * resource, the current content of the resource is captured
     * in a new version resource at a server-defined location.
     * @param keepCheckedOut indicates whether to checkout the
     * resource again immediately after checking it in.
     * If keepCheckedOut is <code>true</code>, the ActivityList
     * and Unreserved properties should not be changed by the checkout.
     * @param forkOK indicates whether to fork even if forking
     * is discouraged.
     */
    public void doCheckin(boolean keepCheckedOut, boolean forkOK) throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doCheckin( keepCheckedOut, forkOK );
    }
    
    /**
     * Apply {@link #doCheckin(boolean, boolean)
     * doCheckin(false, true)}
     */
    public void doCheckin() throws WvcmException {
        doCheckin( false, true );
    }
    
    /**
     * If this {@link ControllableResource} is checked-out,
     * return a list of {@link Activity} objects that identify the
     * activities that will become the activityList of the version created
     * when this {@link ControllableResource} is checked in; otherwise,
     * return <code>null</code>/
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.ACTIVITY_LIST</code> as a wanted property.
     */
    public List getActivityList() throws WvcmException {
        return (List)loadedProperties().get( PropertyName.ACTIVITY_LIST );
    }
    
    /**
     * Return a MergePreviewReport indicating how the resource
     * identified by this {@link ControllableResource}
     * would be modified by a {@link #doMerge doMerge}.
     * @param source The version to be merged.
     */
    public ControllableResource.MergePreviewReport doMergePreviewReport(Version source) throws WvcmException {
        // TODO: prio=m, effort=2.0, descr=(report class to be implemented)
        return null;
    }
    
    /**
     * Cancels the checkout of a version-controlled resource,
     * and restores its content to the state of its CheckedOut version.
     */
    public void doUncheckout() throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doUncheckout();
        loadedProperties().resetContainer();
    }
    
    /**
     * Return whether the resource identified by this {@link ControllableResource} is
     * checked-out.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.IS_CHECKED_OUT</code> as a wanted property.
     */
    public boolean getIsCheckedOut() throws WvcmException {
        Boolean co = (Boolean)loadedProperties().get( PropertyName.IS_CHECKED_OUT );
        return co.booleanValue();
    }
    
    /**
     * Set the MergeList of this {@link ControllableResource}.
     * In particular, a client is responsible for deleting versions from
     * the MergeList after the user has verified that the current
     * content represents a correct merge of that version.
     * @param versionList A list of {@link Version} objects that will be
     * the value of the MergeList property.
     */
    public void setMergeList(List versionList) {
        loadedProperties().set( PropertyName.MERGE_LIST, versionList );
    }
    
    /**
     * Update the state of this checked-in version-controlled
     * {@link ControllableResource} to be the same as the specified version from
     * the version history of this {@link ControllableResource}.
     * @return An iterator of {@link ControllableResource} objects that
     * have been modified as a result of the doUpdate request.
     * Each version-controlled resource contains a value for each
     * of the properties specified in the WantedPropertyList.
     * @param v The version specifying the new state of the resource
     * identified by this {@link ControllableResource}.
     * @param w The list of properties that will be available
     * on each {@link ControllableResource} in the result.
     */
    public Iterator doUpdate(Version v, PropertyNameList wantedPropertyList) throws WvcmException {
        Iterator result = ((ControllableResourceAccessor)accessor()).doUpdate( v, wantedPropertyList );
        loadedProperties().resetContainer();
        return result;
    }
    
    /**
     * Return whether this ControllableResource is checked-out,
     * and whether another checkout of the same version
     * can be made in the same activity as the
     * one in the ActivitySet of this {@link ControllableResource}.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.UNRESERVED</code> as a wanted property.
     */
    public boolean getUnreserved() throws WvcmException {
        Boolean ur = (Boolean)loadedProperties().get( PropertyName.UNRESERVED );
        return ur.booleanValue();
    }
    
    /**
     * If both the client and the server are maintaining the
     * persistent state of the resource identified by this {@link ControllableResource},
     * synchronizes the state on the client to be that of the server.
     * If this {@link ControllableResource} is a {@link Folder}, every resource in the configuration
     * rooted at that folder is refreshed.
     * @return An iterator of {@link ControllableResource} objects
     * that are dirty or stale
     * @param wantedPropertyList The properties available in the returned proxies.
     * @param ignoreDirty If true, the content should be downloaded even if
     * it has been changed on the client since the last download from the
     * server (thereby overwriting those changes).  If false, the content should
     * be downloaded only if it is unchanged since the last download.
     */
    public Iterator doRefresh(PropertyNameList wantedPropertyList, boolean ignoreDirty) throws WvcmException {
        // TODO: prio=l, effort=2.0, descr=(refresh)
        return null;
    }
    
    /**
     * Return the {@link ControllableResource} that identifies the resource
     * on the server that maintains the state for the client resource
     * identified by this {@link ControllableResource}.  If this Controllable
     * resource only has state on the server or only has state on the client,
     * <code>null</code> is returned.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.SERVER_STATE</code>
     * as a wanted property.
     * @see Workspace#setServerState
     */
    public ControllableResource getServerState() throws WvcmException {
        // TODO: prio=l, effort=0.5, descr=(get server state)
        return null;
    }
    
    /**
     * Set the value of the Unreserved property of this {@link ControllableResource}.
     * @param val The new value for the Unreserved property of this {@link ControllableResource}.
     */
    public void setUnreserved(boolean val) {
        loadedProperties().set( PropertyName.UNRESERVED, new Boolean(val) );
    }
    
    /**
     * If this {@link ControllableResource} is checked-out,
     * return a list of {@link Version} objects that identify the versions whose
     * content has been merged by the server into the content of
     * this {@link ControllableResource}; otherwise, return <code>null</code>.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.AUTO_MERGE_LIST</code> as a wanted property.
     */
    public List getAutoMergeList() throws WvcmException {
        return (List)loadedProperties().get( PropertyName.AUTO_MERGE_LIST );
    }
    
    /**
     * Return whether both the client and server are maintaining persistent
     * state for the content of this {@link ControllableResource}, and whether the server
     * content has changed since the client and server content
     * was last synchronized.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.IS_STALE_CONTENT</code> as a wanted property.
     */
    public boolean getIsStaleContent() throws WvcmException {
        // TODO: prio=l, effort=0.5, descr=(is stale content)
        return false;
    }
    
    /**
     * If this {@link ControllableResource} is checked-out, return
     * a list of {@link Version} objects that identify the versions whose
     * content must be merged by the client into the content of
     * this {@link ControllableResource}; otherwise, return <code>null</code>.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.MERGE_LIST</code> as a wanted property.
     */
    public List getMergeList() throws WvcmException {
        return (List)loadedProperties().get( PropertyName.MERGE_LIST );
    }
    
    /**
     * Return whether both the client and server are maintaining persistent
     * state for the content of this {@link ControllableResource}, and whether the client
     * content has changed since the client and server content
     * was last synchronized.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.IS_DIRTY_CONTENT</code> as a wanted property.
     */
    public boolean getIsDirtyContent() throws WvcmException {
        // TODO: prio=l, effort=0.5, descr=(is dirty content)
        return false;
    }
    
    /**
     * Apply {@link Version#doSetLabel Version.doSetLabel} to the CheckedIn version of this {@link ControllableResource}.
     */
    public void doSetLabel(String label) throws WvcmException {
        ((ControllableResourceAccessor)accessor()).doSetLabel( label );
    }
    
    /**
     * Apply {@link VersionHistory#doReadLabelledVersionProperties
     * Version.doReadLabelledVersionProperties}
     * to the version history of this {@link ControllableResource}.
     */
    public Version doReadLabelledVersionProperties(String label, PropertyNameList wantedPropertyList) throws WvcmException {
        return ((ControllableResourceAccessor)accessor()).doReadLabelledVersionProperties( label, wantedPropertyList );
    }
    
    /**
     * Return the list of property values whose persistent state
     * is being maintained on both the client and server,
     * and whose server-side state has changed
     * since the client and server state was last synchronized.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.STALE_PROPERTY_LIST</code> as a wanted property.
     */
    public PropertyNameList getStalePropertyList() throws WvcmException {
        // TODO: prio=l, effort=2.0, descr=(get stale property list)
        return null;
    }
    
    /**
     * Apply {@link #doCheckout(boolean, List, boolean, boolean)
     * doCheckout(true, null, false, false)}
     */
    public void doCheckout() throws WvcmException {
        doCheckout( true, null, false, false );
    }
    
    /**
     * Set the AutoMergeList of this {@link ControllableResource}.
     * A client is responsible for deleting entries from
     * the AutoMergeList after the user has verified that the automatic
     * merge was performed correctly by the server.
     * @param versionList A list of {@link Version} objects that identify
     * the new value for the AutoMergeList property.
     */
    public void setAutoMergeList(List versionList) {
        loadedProperties().set( PropertyName.AUTO_MERGE_LIST, versionList );
    }
    
    /**
     * Return the list of property values whose persistent state
     * is being maintained on both the client and server,
     * and whose client-side state has changed
     * since the client and server state was last synchronized.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.DIRTY_PROPERTY_LIST</code> as a wanted property.
     */
    public PropertyNameList getDirtyPropertyList() throws WvcmException {
        // TODO: prio=l, effort=2.0, descr=(get dirty property list)
        return null;
    }
    
    /**
     * Return whether version history will be tracked when the
     * resource is controlled.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.VERSION_CONTROLLABLE</code> as a wanted property.
     * @see #doControl doControl
     */
    public boolean getVersionControllable() throws WvcmException {
        Boolean vc = (Boolean)loadedProperties().get( PropertyName.VERSION_CONTROLLABLE );
        return vc.booleanValue();
    }
    
    /**
     * Create a new controlled resource
     * at the location identified by the proxy.  The resource
     * is associated with an existing version history,
     * and is initialized with a specified version from that
     * version history.  The request will
     * fail if a resource already exists at that location.
     * @param v The version used to initialize the controlled
     * resource.
     */
    public void doCreateVersionControlledResource(Version v) throws WvcmException {
        try {
            ((ControllableResourceAccessor)accessor()).doCreateVersionControlledResource(v);
        }
        catch (WvcmException e) {
            if (e.getReasonCode() != ReasonCode.LOCKED) {
                throw e;
            }
            // retry with lock-tokens from parent
            Folder parentFolder =
                (Folder)location().parent().folder().doReadProperties(null);
            Iterator i = parentFolder.getLockTokens().iterator();
            while (i.hasNext()) {
                addLockToken((LockToken)i.next());
            }
            ((ControllableResourceAccessor)accessor()).doCreateVersionControlledResource(v);
        }
    }
    
    /**
     * Return the version history of the CheckedIn or CheckedOut version.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.VERSION_HISTORY</code> as a wanted property.
     */
    public VersionHistory getVersionHistory() throws WvcmException {
        return (VersionHistory)loadedProperties().get( PropertyName.VERSION_HISTORY );
    }
    
    /**
     * Return the workspace of which this {@link ControllableResource} is a member.
     * By definition, the workspace property of a workspace
     * identifies itself, and the workspace property of any other
     * type of resource must identify the same workspace as its parent.
     * @throws WvcmException if this {@link ControllableResource} was not created with
     * <code>PropertyName.WORKSPACE</code> as a wanted property.
     */
    public Workspace getWorkspace() throws WvcmException {
        return (Workspace)loadedProperties().get( PropertyName.WORKSPACE );
    }
}

