/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.wc;

import java.io.File;
import java.io.OutputStream;
import java.util.Collection;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.admin.ISVNEntryHandler;
import org.tmatesoft.svn.core.internal.wc16.SVNWCClient16;
import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc2.*;

/**
 * The <b>SVNWCClient</b> class combines a number of version control operations
 * mainly intended for local work with Working Copy items. This class includes
 * those operations that are destined only for local work on a Working Copy as
 * well as those that are moreover able to access a repository.
 * <p/>
 * <p/>
 * Here's a list of the <b>SVNWCClient</b>'s methods matched against
 * corresponing commands of the SVN command line client:
 * <p/>
 * <table cellpadding="3" cellspacing="1" border="0" width="70%" bgcolor="#999933">
 * <tr bgcolor="#ADB8D9" align="left">
 * <td><b>SVNKit</b></td>
 * <td><b>Subversion</b></td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doAdd()</td>
 * <td>'svn add'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doGetFileContents()</td>
 * <td>'svn cat'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doDelete()</td>
 * <td>'svn delete'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doCleanup()</td>
 * <td>'svn cleanup'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doInfo()</td>
 * <td>'svn info'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doLock()</td>
 * <td>'svn lock'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doUnlock()</td>
 * <td>'svn unlock'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>
 * doSetProperty()</td>
 * <td>
 *'svn propset PROPNAME PROPVAL PATH'<br />
 *'svn propdel PROPNAME PATH'<br />
 *'svn propedit PROPNAME PATH'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doSetRevisionProperty()</td>
 * <td>
 *'svn propset PROPNAME --revprop -r REV PROPVAL [URL]'<br />
 *'svn propdel PROPNAME --revprop -r REV [URL]'<br />
 *'svn propedit PROPNAME --revprop -r REV [URL]'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>
 * doGetProperty()</td>
 * <td>
 *'svn propget PROPNAME PATH'<br />
 *'svn proplist PATH'</td>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doGetRevisionProperty()</td>
 * <td>
 *'svn propget PROPNAME --revprop -r REV [URL]'<br />
 *'svn proplist --revprop -r REV [URL]'</td>
 * </tr>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doResolve()</td>
 * <td>'svn resolved'</td>
 * </tr>
 * <tr bgcolor="#EAEAEA" align="left">
 * <td>doRevert()</td>
 * <td>'svn revert'</td>
 * </tr>
 * </table>
 * 
 * @version 1.3
 * @author TMate Software Ltd.
 * @since 1.2
 * @see <a target="_top" href="http://svnkit.com/kb/examples/">Examples</a>
 */
public class SVNWCClient extends SVNBasicClient {

    private ISVNAddParameters addParameters;

    private ISVNCommitHandler commitHandler;

    private boolean revertMissingDirectories;

    /**
     * Default implementation of {@link ISVNAddParameters} which
     * <code>onInconsistentEOLs(File file)</code> always returns the
     * {@link ISVNAddParameters#REPORT_ERROR} action.
     * 
     * @since 1.2
     */
    public static ISVNAddParameters DEFAULT_ADD_PARAMETERS = new ISVNAddParameters() {

        public Action onInconsistentEOLs(File file) {
            return ISVNAddParameters.REPORT_ERROR;
        }
    };

    /**
     * Constructs and initializes an <b>SVNWCClient</b> object with the
     * specified run-time configuration and authentication drivers.
     * <p/>
     * <p/>
     * If <code>options</code> is <span class="javakeyword">null</span>, then
     * this <b>SVNWCClient</b> will be using a default run-time configuration
     * driver which takes client-side settings from the default SVN's run-time
     * configuration area but is not able to change those settings (read more on
     * {@link ISVNOptions} and {@link SVNWCUtil}).
     * <p/>
     * <p/>
     * If <code>authManager</code> is <span class="javakeyword">null</span>,
     * then this <b>SVNWCClient</b> will be using a default authentication and
     * network layers driver (see
     * {@link SVNWCUtil#createDefaultAuthenticationManager()}) which uses
     * server-side settings and auth storage from the default SVN's run-time
     * configuration area (or system properties if that area is not found).
     * 
     * @param authManager
     *            an authentication and network layers driver
     * @param options
     *            a run-time configuration options driver
     */
    public SVNWCClient(ISVNAuthenticationManager authManager, ISVNOptions options) {
        super(authManager, options);
    }

    /**
     * Constructs and initializes an <b>SVNWCClient</b> object with the
     * specified run-time configuration and repository pool object.
     * <p/>
     * <p/>
     * If <code>options</code> is <span class="javakeyword">null</span>, then
     * this <b>SVNWCClient</b> will be using a default run-time configuration
     * driver which takes client-side settings from the default SVN's run-time
     * configuration area but is not able to change those settings (read more on
     * {@link ISVNOptions} and {@link SVNWCUtil}).
     * <p/>
     * <p/>
     * If <code>repositoryPool</code> is <span class="javakeyword">null</span>,
     * then {@link org.tmatesoft.svn.core.io.SVNRepositoryFactory} will be used
     * to create {@link SVNRepository repository access objects}.
     * 
     * @param repositoryPool
     *            a repository pool object
     * @param options
     *            a run-time configuration options driver
     */
    public SVNWCClient(ISVNRepositoryPool repositoryPool, ISVNOptions options) {
        super(repositoryPool, options);
    }

    public SVNWCClient(SvnOperationFactory of) {
        super(of);
    }

    protected void initDefaults() {
        setCommitHandler(null);
        setAddParameters(null);
    }

    /**
     * Sets custom add parameters to this client object.
     * 
     * @param addParameters
     *            extra parameters for add operations
     * @since 1.2
     */
    public void setAddParameters(ISVNAddParameters addParameters) {
        if (addParameters == null) {
            addParameters = DEFAULT_ADD_PARAMETERS;
        }
        this.addParameters = addParameters;
    }

    /**
     * Returns the specified commit handler (if set) being in use or a default
     * one (<b>DefaultSVNCommitHandler</b>) if no special implementations of
     * <b>ISVNCommitHandler</b> were previousely provided.
     * 
     * @return the commit handler being in use or a default one
     * @see #setCommitHandler(ISVNCommitHandler)
     * @see DefaultSVNCommitHandler
     */
    public ISVNCommitHandler getCommitHandler() {
        return this.commitHandler;
    }

    /**
     * Sets an implementation of <b>ISVNCommitHandler</b> to the commit handler
     * that will be used during commit operations to handle commit log messages.
     * The handler will receive a clien's log message and items (represented as
     * <b>SVNCommitItem</b> objects) that will be committed. Depending on
     * implementor's aims the initial log message can be modified (or something
     * else) and returned back.
     * <p/>
     * <p/>
     * If using <b>SVNWCClient</b> without specifying any commit handler then a
     * default one will be used - {@link DefaultSVNCommitHandler}.
     * 
     * @param handler
     *            an implementor's handler that will be used to handle commit
     *            log messages
     * @see #getCommitHandler()
     * @see ISVNCommitHandler
     */
    public void setCommitHandler(ISVNCommitHandler handler) {
        if (handler == null) {
            handler = new DefaultSVNCommitHandler();
        }
        this.commitHandler = handler;
    }

    public void setRevertMissingDirectories(boolean revertMissing) {
        this.revertMissingDirectories = revertMissing;
    }

    public boolean isRevertMissingDirectories() {
        return revertMissingDirectories;
    }

    /**
     * Outputs the content of file identified by <code>path</code> and
     * <code>revision</code> to the stream <code>dst</code>. The actual node
     * revision selected is determined by the path as it exists in
     * <code>pegRevision</code>. If <code>pegRevision</code> is
     * {@link SVNRevision#UNDEFINED}, then it defaults to
     * {@link SVNRevision#WORKING}.
     * 
     * <p/>
     * If <vode>revision</code> is one of:
     * <ul>
     * <li>{@link SVNRevision#BASE}
     * <li>{@link SVNRevision#WORKING}
     * <li>{@link SVNRevision#COMMITTED}
     * </ul>
     * then the file contents are taken from the working copy file item (no
     * network connection is needed). Otherwise the file item's contents are
     * taken from the repository at a particular revision.
     * 
     * @param path
     *            working copy path
     * @param pegRevision
     *            revision in which the file item is first looked up
     * @param revision
     *            target revision
     * @param expandKeywords
     *            if <span class="javakeyword">true</span> then all keywords
     *            presenting in the file and listed in the file's
     *            {@link org.tmatesoft.svn.core.SVNProperty#KEYWORDS} property
     *            (if set) will be substituted, otherwise not
     * @param dst
     *            the destination where the file contents will be written to
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> refers to a directory <li><code>path
     *             </code> does not exist <li><code>path</code> is not under
     *             version control
     *             </ul>
     * @see #doGetFileContents(SVNURL,SVNRevision,SVNRevision,boolean,OutputStream)
     */
    public void doGetFileContents(File path, SVNRevision pegRevision, SVNRevision revision, boolean expandKeywords, OutputStream dst) throws SVNException {
        SvnCat cat = getOperationsFactory().createCat();
        if (revision == SVNRevision.UNDEFINED) {
            if (pegRevision != null && pegRevision.isValid()) {
                revision = pegRevision;
            } else {
                revision = SVNRevision.BASE;
            }
        }
        cat.setSingleTarget(SvnTarget.fromFile(path, pegRevision));
        cat.setRevision(revision);
        cat.setExpandKeywords(expandKeywords);
        cat.setOutput(dst);
        cat.run();
    }

    /**
     * Outputs the content of file identified by <code>url</code> and
     * <code>revision</code> to the stream <code>dst</code>. The actual node
     * revision selected is determined by the path as it exists in
     * <code>pegRevision</code>. If <code>pegRevision</code> is
     * {@link SVNRevision#UNDEFINED}, then it defaults to
     * {@link SVNRevision#HEAD}.
     * 
     * @param url
     *            a file item's repository location
     * @param pegRevision
     *            a revision in which the file item is first looked up
     * @param revision
     *            a target revision
     * @param expandKeywords
     *            if <span class="javakeyword">true</span> then all keywords
     *            presenting in the file and listed in the file's
     *            {@link org.tmatesoft.svn.core.SVNProperty#KEYWORDS} property
     *            (if set) will be substituted, otherwise not
     * @param dst
     *            the destination where the file contents will be written to
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>url</code> refers to a directory
     *             <li>it's impossible to create temporary files (
     *             {@link java.io.File#createTempFile(java.lang.String,java.lang.String)
     *             createTempFile()} fails) necessary for file translating
     *             </ul>
     * @see #doGetFileContents(File,SVNRevision,SVNRevision,boolean,OutputStream)
     */
    public void doGetFileContents(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean expandKeywords, OutputStream dst) throws SVNException {
        SvnCat cat = getOperationsFactory().createCat();
        if (revision == SVNRevision.UNDEFINED) {
            if (pegRevision != null && pegRevision.isValid()) {
                revision = pegRevision;
            } else {
                revision = SVNRevision.HEAD;
            }
        }
        cat.setSingleTarget(SvnTarget.fromURL(url, pegRevision));
        cat.setRevision(revision);
        cat.setExpandKeywords(expandKeywords);
        cat.setOutput(dst);
        cat.run();
    }

    /**
     * Cleans up a working copy. This method is equivalent to a call to
     * <code>doCleanup(path, false)</code>.
     * 
     * @param path
     *            a WC path to start a cleanup from
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> does not exist
     *             <li><code>path</code>'s parent directory is not under version
     *             control
     *             </ul>
     * @see #doCleanup(File, boolean)
     */
    public void doCleanup(File path) throws SVNException {
    	SvnCleanup cleanup = getOperationsFactory().createCleanup();
    	cleanup.setSingleTarget(SvnTarget.fromFile(path));
    	cleanup.run();
    }

    /**
     * Recursively cleans up the working copy, removing locks and resuming
     * unfinished operations.
     * 
     * <p/>
     * If you ever get a "working copy locked" error, use this method to remove
     * stale locks and get your working copy into a usable state again.
     * 
     * <p>
     * This method operates only on working copies and does not open any network
     * connection.
     * 
     * @param path
     *            a WC path to start a cleanup from
     * @param deleteWCProperties
     *            if <span class="javakeyword">true</span>, removes DAV specific
     *            <span class="javastring">"svn:wc:"</span> properties from the
     *            working copy
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> does not exist <li><code>path</code>'s
     *             parent directory is not under version control
     *             </ul>
     */
    public void doCleanup(File path, boolean deleteWCProperties) throws SVNException {
    	SvnCleanup cleanup = getOperationsFactory().createCleanup();
    	cleanup.setSingleTarget(SvnTarget.fromFile(path));
    	cleanup.setDeleteWCProperties(deleteWCProperties);
    	cleanup.run();
    }

    public void doCleanup(File path, boolean deleteWCProperties, boolean breakLocks, boolean vacuumPristines, boolean removeUnversionedItems, boolean removeIgnoredItems, boolean includeExternals) throws SVNException {
    	SvnCleanup cleanup = getOperationsFactory().createCleanup();
    	cleanup.setSingleTarget(SvnTarget.fromFile(path));
    	cleanup.setDeleteWCProperties(deleteWCProperties);
        cleanup.setBreakLocks(breakLocks);
        cleanup.setVacuumPristines(vacuumPristines);
        cleanup.setRemoveUnversionedItems(removeUnversionedItems);
        cleanup.setRemoveIgnoredItems(removeIgnoredItems);
        cleanup.setIncludeExternals(includeExternals);
    	cleanup.run();
    }

    /**
     * Sets <code>propName</code> to <code>propValue</code> on <code>path</code>
     * . A <code>propValue</code> of <span class="javakeyword">null</span> will
     * delete the property.
     * 
     * <p/>
     * If <code>depth</code> is {@link org.tmatesoft.svn.core.SVNDepth#EMPTY},
     * set the property on <code>path</code> only; if {@link SVNDepth#FILES},
     * set it on <code>path</code> and its file children (if any); if
     * {@link SVNDepth#IMMEDIATES}, on <code>path</code> and all of its
     * immediate children (both files and directories); if
     * {@link SVNDepth#INFINITY}, on <code>path</code> and everything beneath
     * it.
     * 
     * <p/>
     * If <code>propName</code> is an svn-controlled property (i.e. prefixed
     * with <span class="javastring">"svn:"</span>), then the caller is
     * responsible for ensuring that the value uses LF line-endings.
     * 
     * <p/>
     * If <code>skipChecks</code> is <span class="javakeyword">true</span>, this
     * method does no validity checking. But if <code>skipChecks</code> is <span
     * class="javakeyword">false</span>, and <code>propName</code> is not a
     * valid property for <code>path</code>, it throws an exception, either with
     * an error code {@link org.tmatesoft.svn.core.SVNErrorCode#ILLEGAL_TARGET}
     * (if the property is not appropriate for <code>path</code>), or with
     * {@link org.tmatesoft.svn.core.SVNErrorCode#BAD_MIME_TYPE} (if
     * <code>propName</code> is <span class="javastring">"svn:mime-type"</span>,
     * but <code>propVal</code> is not a valid mime-type).
     * 
     * <p/>
     * <code>changeLists</code> is a collection of <code>String</code>
     * changelist names, used as a restrictive filter on items whose properties
     * are set; that is, don't set properties on any item unless it's a member
     * of one of those changelists. If <code>changelists</code> is empty (or
     * <span class="javakeyword">null</span>), no changelist filtering occurs.
     * 
     * <p>
     * This method operates only on working copies and does not open any network
     * connection.
     * 
     * @param path
     *            working copy path
     * @param propName
     *            property name
     * @param propValue
     *            property value
     * @param skipChecks
     *            <span class="javakeyword">true</span> to force the operation
     *            to run without validity checking
     * @param depth
     *            working copy tree depth to process
     * @param handler
     *            a caller's property handler
     * @param changeLists
     *            changelist names
     * @throws SVNException
     *             <ul>
     *             <li><code>path</code> does not exist <li>exception with
     *             {@link SVNErrorCode#CLIENT_PROPERTY_NAME} error code - if
     *             <code>propName</code> is a revision property name or not a
     *             valid property name or not a regular property name (one
     *             starting with a <span class="javastring">"svn:entry"</span>
     *             or <span class="javastring">"svn:wc"</span> prefix)
     *             </ul>
     * @see #doSetProperty(SVNURL, String, SVNPropertyValue, SVNRevision,
     *      String, SVNProperties, boolean, ISVNPropertyHandler)
     * @since 1.2, SVN 1.5
     */
    public void doSetProperty(File path, String propName, SVNPropertyValue propValue, boolean skipChecks, SVNDepth depth, final ISVNPropertyHandler handler, Collection<String> changeLists) throws SVNException {
        SvnSetProperty ps = getOperationsFactory().createSetProperty();
        ps.setPropertyName(propName);
        ps.setPropertyValue(propValue);
        ps.setForce(skipChecks);
        ps.setDepth(depth);
        ps.setSingleTarget(SvnTarget.fromFile(path));
        ps.setApplicalbeChangelists(changeLists);
        ps.setReceiver(SvnCodec.propertyReceiver(handler));
        
        ps.run();
    }

    /**
     * Crawls the working copy at <code>path</code> and calls
     * {@link ISVNPropertyValueProvider#providePropertyValues(java.io.File, org.tmatesoft.svn.core.SVNProperties)}
     * to get properties to be change on each path being traversed
     * 
     * <p/>
     * If <code>depth</code> is {@link org.tmatesoft.svn.core.SVNDepth#EMPTY},
     * change the properties on <code>path</code> only; if
     * {@link SVNDepth#FILES}, change the properties on <code>path</code> and
     * its file children (if any); if {@link SVNDepth#IMMEDIATES}, on
     * <code>path</code> and all of its immediate children (both files and
     * directories); if {@link SVNDepth#INFINITY}, on <code>path</code> and
     * everything beneath it.
     * 
     * <p/>
     * If <code>skipChecks</code> is <span class="javakeyword">true</span>, this
     * method does no validity checking of changed properties. But if
     * <code>skipChecks</code> is <span class="javakeyword">false</span>, and
     * changed property name is not a valid property for <code>path</code>, it
     * throws an exception, either with an error code
     * {@link org.tmatesoft.svn.core.SVNErrorCode#ILLEGAL_TARGET} (if the
     * property is not appropriate for <code>path</code>), or with
     * {@link org.tmatesoft.svn.core.SVNErrorCode#BAD_MIME_TYPE} (if changed
     * propery name is <span class="javastring">"svn:mime-type"</span>, but
     * changed property value is not a valid mime-type).
     * 
     * <p/>
     * <code>changeLists</code> is a collection of <code>String</code>
     * changelist names, used as a restrictive filter on items whose properties
     * are set; that is, don't set properties on any item unless it's a member
     * of one of those changelists. If <code>changelists</code> is empty (or
     * <span class="javakeyword">null</span>), no changelist filtering occurs.
     * 
     * <p>
     * This method operates only on working copies and does not open any network
     * connection.
     * 
     * @param path
     *            working copy path
     * @param propertyValueProvider
     *            changed properties provider
     * @param skipChecks
     *            <span class="javakeyword">true</span> to force the operation
     *            to run without validity checking
     * @param depth
     *            working copy tree depth to process
     * @param handler
     *            a caller's property handler
     * @param changeLists
     *            changelist names
     * @throws SVNException
     *             <ul>
     *             <li><code>path</code> does not exist <li>exception with
     *             {@link SVNErrorCode#CLIENT_PROPERTY_NAME} error code - if
     *             changed property name is a revision property name or not a
     *             valid property name or not a regular property name (one
     *             starting with a <span class="javastring">"svn:entry"</span>
     *             or <span class="javastring">"svn:wc"</span> prefix)
     *             </ul>
     * @see #doSetProperty(java.io.File, String,
     *      org.tmatesoft.svn.core.SVNPropertyValue, boolean,
     *      org.tmatesoft.svn.core.SVNDepth, ISVNPropertyHandler,
     *      java.util.Collection)
     * @since 1.2, SVN 1.5
     */
    public void doSetProperty(File path, ISVNPropertyValueProvider propertyValueProvider, boolean skipChecks, SVNDepth depth, ISVNPropertyHandler handler, Collection<String> changeLists) throws SVNException {
        SvnSetProperty ps = getOperationsFactory().createSetProperty();
        ps.setPropertyValueProvider(SvnCodec.propertyValueProvider(propertyValueProvider));
        ps.setForce(skipChecks);
        ps.setDepth(depth);
        ps.setSingleTarget(SvnTarget.fromFile(path));
        ps.setApplicalbeChangelists(changeLists);
        ps.setReceiver(SvnCodec.propertyReceiver(handler));
        
        ps.run();
    }

    /**
     * Sets <code>propName</code> to <code>propValue</code> on <code>path</code>
     * . A <code>propValue</code> of <span class="javakeyword">null</span> will
     * delete the property.
     * 
     * <p/>
     * <code>baseRevision</code> must not be null; in this case, the property
     * will only be set if it has not changed since <code>baseRevision</code>.
     * 
     * <p/>
     * The {@link ISVNAuthenticationManager authentication manager} and
     * {@link ISVNCommitHandler commit handler}, either provided by a caller or
     * default ones, will be used to immediately attempt to commit the property
     * change in the repository.
     * 
     * <p/>
     * If <code>propName</code> is an svn-controlled property (i.e. prefixed
     * with <span class="javastring">"svn:"</span>), then the caller is
     * responsible for ensuring that the value uses LF line-endings.
     * 
     * <p/>
     * If <code>skipChecks</code> is <span class="javakeyword">true</span>, this
     * method does no validity checking. But if <code>skipChecks</code> is <span
     * class="javakeyword">false</span>, and <code>propName</code> is not a
     * valid property for <code>path</code>, it throws an exception, either with
     * an error code {@link org.tmatesoft.svn.core.SVNErrorCode#ILLEGAL_TARGET}
     * (if the property is not appropriate for <code>path</code>), or with
     * {@link org.tmatesoft.svn.core.SVNErrorCode#BAD_MIME_TYPE} (if
     * <code>propName</code> is <span class="javastring">"svn:mime-type"</span>,
     * but <code>propVal</code> is not a valid mime-type).
     * 
     * <p/>
     * If non-<span class="javakeyword">null</span>,
     * <code>revisionProperties</code> is an {@link SVNProperties} object
     * holding additional, custom revision properties (<code>String</code> names
     * mapped to <code>String</code> values) to be set on the new revision in
     * the event that this is a committing operation. This table cannot contain
     * any standard Subversion properties.
     * 
     * @param url
     *            versioned item url
     * @param propName
     *            property name
     * @param propValue
     *            property value
     * @param baseRevision
     *            revision to change properties against
     * @param commitMessage
     *            commit log message
     * @param revisionProperties
     *            custom revision properties to set
     * @param skipChecks
     *            <span class="javakeyword">true</span> to force the operation
     *            to run without validity checking
     * @param handler
     *            a caller's property handler
     * @return commit information if the commit succeeds
     * @throws SVNException
     *             <ul>
     *             <li><code>url</code> does not exist in <code>baseRevision
     *             </code> <li>exception with
     *             {@link SVNErrorCode#CLIENT_PROPERTY_NAME} error code - if
     *             <code>propName</code> is a revision property name or not a
     *             valid property name or not a regular property name (one
     *             starting with an <span class="javastring">"svn:entry"</span>
     *             or <span class="javastring">"svn:wc"</span> prefix) <li>
     *             exception with {@link SVNErrorCode#UNSUPPORTED_FEATURE} error
     *             code - if <code>propName</code> is either equal to
     *             {@link SVNProperty#EOL_STYLE} or {@link SVNProperty#KEYWORDS}
     *             or {@link SVNProperty#CHARSET}
     *             </ul>
     * @see #doSetProperty(File, String, SVNPropertyValue, boolean, SVNDepth,
     *      ISVNPropertyHandler, Collection)
     * @since 1.2, SVN 1.5
     */
    public SVNCommitInfo doSetProperty(SVNURL url, String propName, SVNPropertyValue propValue, SVNRevision baseRevision, String commitMessage, SVNProperties revisionProperties, boolean skipChecks, final ISVNPropertyHandler handler) throws SVNException {
        SvnRemoteSetProperty ps = getOperationsFactory().createRemoteSetProperty();
        ps.setSingleTarget(SvnTarget.fromURL(url));
        ps.setPropertyName(propName);
        ps.setPropertyValue(propValue);
        ps.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
        ps.setRevisionProperties(revisionProperties);
        ps.setCommitMessage(commitMessage);
        ps.setPropertyReceiver(new ISvnObjectReceiver<SVNPropertyData>() {

            public void receive(SvnTarget target, SVNPropertyData object) throws SVNException {
                if (handler != null) {
                    handler.handleProperty(target.getURL(), object);
                }
            }
        });
        return ps.run();
    }

    /**
     * Set <code>propName</code> to <code>propValue</code> on revision
     * <code>revision</code> in the repository represented by <code>path</code>.
     * 
     * <p/>
     * This method simply obtains a url given a working path and calls
     * {@link #doSetRevisionProperty(SVNURL, SVNRevision, String, SVNPropertyValue, boolean, ISVNPropertyHandler)}
     * passing this url and the rest parameters.
     * 
     * @param path
     *            working copy path
     * @param revision
     *            revision which properties are to be modified
     * @param propName
     *            property name
     * @param propValue
     *            property value
     * @param force
     *            if <span class="javakeyword">true</span> allows newlines in
     *            the author property
     * @param handler
     *            caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li>exception with {@link SVNErrorCode#CLIENT_PROPERTY_NAME}
     *             error code - if <code>propName</code> is invalid <li>
     *             exceptions thrown by
     *             {@link #doSetRevisionProperty(SVNURL, SVNRevision, String, SVNPropertyValue, boolean, ISVNPropertyHandler)}
     *             </ul>
     */
    public void doSetRevisionProperty(File path, SVNRevision revision, String propName, SVNPropertyValue propValue, boolean force, ISVNPropertyHandler handler) throws SVNException {
        SvnSetProperty ps = getOperationsFactory().createSetProperty();
        ps.setRevisionProperty(true);
        ps.setSingleTarget(SvnTarget.fromFile(path));
        ps.setRevision(revision);
        ps.setPropertyName(propName);
        ps.setPropertyValue(propValue);
        ps.setForce(force);
        
        SVNPropertyData propertyData = ps.run();
        if (handler != null) {
            handler.handleProperty(revision != null ? revision.getNumber() : -1, propertyData);
        }
    }

    /**
     * Set <code>propName</code> to <code>propValue</code> on revision
     * <code>revision</code> in the repository represented by <code>path</code>.
     * 
     * A <code>propValue</code> of <span class="javakeyword">null</span> will
     * delete the property. The {@link ISVNAuthenticationManager authentication
     * manager}, either provided by a caller or a default one, will be used for
     * authentication.
     * 
     * <p/>
     * If <code>propName</code> is an svn-controlled property (i.e. prefixed
     * with <span class="javastring">"svn:"</span>), then the caller is
     * responsible for ensuring that the value is UTF8-encoded and uses LF
     * line-endings.
     * 
     * <p/>
     * Although this routine accepts a working copy path it doesn't affect the
     * working copy at all; it's a pure network operation that changes an
     * *unversioned* property attached to a revision. This can be used to tweak
     * log messages, dates, authors, and the like. Be careful: it's a lossy
     * operation.
     * 
     * <p>
     * Also note that unless the administrator creates a pre-revprop-change hook
     * in the repository, this feature will fail.
     * 
     * @param url
     *            repository URL
     * @param revision
     *            revision which properties are to be modified
     * @param propName
     *            property name
     * @param propValue
     *            property value
     * @param force
     *            if <span class="javakeyword">true</span> allows newlines in
     *            the author property
     * @param handler
     *            caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li>the operation can not be performed without forcing <li>
     *             <code>propName</code> is either invalid or not a regular
     *             property name (one starting with an <span
     *             class="javastring">"svn:entry"</span> or <span
     *             class="javastring">"svn:wc"</span> prefix)
     *             </ul>
     * @see #doSetRevisionProperty(File, SVNRevision, String, SVNPropertyValue,
     *      boolean, ISVNPropertyHandler)
     */
    public void doSetRevisionProperty(SVNURL url, SVNRevision revision, String propName, SVNPropertyValue propValue, boolean force, ISVNPropertyHandler handler) throws SVNException {
        SvnSetProperty ps = getOperationsFactory().createSetProperty();
        ps.setRevisionProperty(true);
        ps.setSingleTarget(SvnTarget.fromURL(url));
        ps.setRevision(revision);
        ps.setPropertyName(propName);
        ps.setPropertyValue(propValue);
        ps.setForce(force);
        
        SVNPropertyData propertyData = ps.run();
        if (handler != null) {
            handler.handleProperty(revision != null ? revision.getNumber() : -1, propertyData);
        }
    }

    /**
     * Gets the value of the property <code>propName</code> for
     * <code>path</code>. This method simply creates an implementation of
     * {@link ISVNPropertyHandler} which stores the value only for
     * <code>path</code> which is then used in the following call to
     * 
     * <code>doGetProperty(path, propName, pegRevision, revision, SVNDepth.EMPTY, handler, null)</code>
     * .
     * 
     * @param path
     *            a WC item's path
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved but only the first of them returned
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision;
     * @return the item's property
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             <li><code>path</code> is not under version control
     *             </ul>
     * @see #doGetProperty(File, String, SVNRevision, SVNRevision, SVNDepth,
     *      ISVNPropertyHandler, Collection)
     */
    public SVNPropertyData doGetProperty(final File path, final String propName, SVNRevision pegRevision, SVNRevision revision) throws SVNException {
        final SVNPropertyData[] data = new SVNPropertyData[1];
        SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromFile(path, pegRevision));
        getProperties.setRevision(revision);
        getProperties.setDepth(SVNDepth.EMPTY);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName != null && object.containsName(propName)) {
                    data[0] = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                } 
            }
        });
        getProperties.run();
        return data[0];
    }

    /**
     * Gets the value of the property <code>propName</code> for <code>url</code>
     * . This method simply creates an implementation of
     * {@link ISVNPropertyHandler} which stores the value only for
     * <code>path</code> which is then used in the following call to
     * <code>doGetProperty(url, propName, pegRevision, revision, SVNDepth.EMPTY, handler)</code>
     * .
     * 
     * @param url
     *            an item's repository location
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved but only the first of them returned
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision;
     * @return the item's property
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             <li><code>path</code> is not under version control
     *             </ul>
     * @see #doGetProperty(SVNURL, String, SVNRevision, SVNRevision, SVNDepth,
     *      ISVNPropertyHandler)
     */
    public SVNPropertyData doGetProperty(final SVNURL url, final String propName, SVNRevision pegRevision, SVNRevision revision) throws SVNException {
        final SVNPropertyData[] data = new SVNPropertyData[1];
        SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromURL(url, pegRevision));
        getProperties.setRevision(revision);
        getProperties.setDepth(SVNDepth.EMPTY);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName != null && object.containsName(propName)) {
                    data[0] = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                } 
            }
        });
        getProperties.run();
        return data[0];
    }

    /**
     * Gets an item's versioned property and passes it to a provided property
     * handler. It's possible to get either a local property (from a Working
     * Copy) or a remote one (located in a repository). If <vode>revision</code>
     * is one of:
     * <ul>
     * <li>{@link SVNRevision#BASE BASE}
     * <li>{@link SVNRevision#WORKING WORKING}
     * <li>{@link SVNRevision#COMMITTED COMMITTED}
     * </ul>
     * then the result is a WC item's property. Otherwise the property is taken
     * from a repository (using the item's URL).
     * 
     * @param path
     *            a WC item's path
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved and passed to <code>handler</code> for
     *            processing
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision;
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     * @param handler
     *            a caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             <li><code>path</code> is not under version control
     *             </ul>
     * @deprecated use
     *             {@link #doGetProperty(File, String, SVNRevision, SVNRevision, SVNDepth, ISVNPropertyHandler, Collection)}
     *             instead
     */
    public void doGetProperty(File path, String propName, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNPropertyHandler handler) throws SVNException {
        doGetProperty(path, propName, pegRevision, revision, SVNDepth.getInfinityOrEmptyDepth(recursive), handler, null);
    }

    /**
     * Invokes <code>handler</code> on paths covered by <code>depth</code>
     * starting with the specified <code>path</code>.
     * 
     * <p/>
     * If both <vode>revision</code> and <code>pegRevision</code> are ones of:
     * <ul>
     * <li>{@link SVNRevision#BASE BASE}
     * <li>{@link SVNRevision#WORKING WORKING}
     * <li>{@link SVNRevision#COMMITTED COMMITTED}
     * <li>{@link SVNRevision#UNDEFINED}
     * </ul>
     * then this method gets properties from the working copy without connecting
     * to the repository. Otherwise properties are taken from the repository
     * (using the item's URL).
     * 
     * <p/>
     * The actual node revision selected is determined by the path as it exists
     * in <code>pegRevision</code>. If <code>pegRevision</code> is
     * {@link SVNRevision#UNDEFINED}, then it defaults to
     * {@link SVNRevision#WORKING}.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, fetch the property from
     * <code>path</code> only; if {@link SVNDepth#FILES}, fetch from
     * <code>path</code> and its file children (if any); if
     * {@link SVNDepth#IMMEDIATES}, from <code>path</code> and all of its
     * immediate children (both files and directories); if
     * {@link SVNDepth#INFINITY}, from <code>path</code> and everything beneath
     * it.
     * 
     * <p/>
     * <code>changeLists</code> is a collection of <code>String</tt> changelist
     * names, used as a restrictive filter on items whose properties are
     * set; that is, don't set properties on any item unless it's a member
     * of one of those changelists.  If <code>changeLists</code> is empty (or <span class="javakeyword">null</span>), no
     * changelist filtering occurs.
     * 
     * @param path
     *            a WC item's path
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved and passed to <code>handler</code> for
     *            processing
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision
     * @param depth
     *            tree depth
     * @param handler
     *            a caller's property handler
     * @param changeLists
     *            collection of changelist names
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX}
     *             prefix <li><code>path</code> is not under version control
     *             </ul>
     * @since 1.2, SVN 1.5
     */
    public void doGetProperty(File path, final String propName, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, final ISVNPropertyHandler handler, Collection<String> changeLists) throws SVNException {
        SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromFile(path, pegRevision));
        getProperties.setRevision(revision);
        getProperties.setDepth(depth);
        getProperties.setApplicalbeChangelists(changeLists);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName != null && object.containsName(propName)) {
                    SVNPropertyData propertyData = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                    if (target.isFile()) {
                        handler.handleProperty(target.getFile(), propertyData);
                    } else {
                        handler.handleProperty(target.getURL(), propertyData);
                    }
                }  else if (propName == null) {
                    for (Object propertyName : object.nameSet()) {
                        String name = propertyName.toString();
                        SVNPropertyData propertyData = new SVNPropertyData(name, object.getSVNPropertyValue(name), getOptions());
                        if (target.isFile()) {
                            handler.handleProperty(target.getFile(), propertyData);
                        } else {
                            handler.handleProperty(target.getURL(), propertyData);
                        }
                    }
                }
            }
        });
        getProperties.run();
    }

    /**
     * Gets an item's versioned property from a repository and passes it to a
     * provided property handler. This method is useful when having no Working
     * Copy at all.
     * 
     * @param url
     *            an item's repository location
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved and passed to <code>handler</code> for
     *            processing
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     * @param handler
     *            a caller's property handler
     * @throws SVNException
     *             if <code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     * @deprecated use
     *             {@link #doGetProperty(SVNURL, String, SVNRevision, SVNRevision, SVNDepth, ISVNPropertyHandler)}
     *             instead
     */
    public void doGetProperty(SVNURL url, String propName, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNPropertyHandler handler) throws SVNException {
        doGetProperty(url, propName, pegRevision, revision, SVNDepth.getInfinityOrEmptyDepth(recursive), handler);
    }

    /**
     * Invokes <code>handler</code> on paths covered by <code>depth</code>
     * starting with the specified <code>path</code>.
     * 
     * <p/>
     * If <code></code> is {@link SVNRevision#UNDEFINED} then get properties
     * from the repository head. Else get the properties as of
     * <code>revision</code>. The actual node revision selected is determined by
     * the path as it exists in <code>pegRevision</code>. If
     * <code>pegRevision</code> is {@link SVNRevision#UNDEFINED}, then it
     * defaults to {@link SVNRevision#HEAD}.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, fetch the property from
     * <code>path</code> only; if {@link SVNDepth#FILES}, fetch from
     * <code>path</code> and its file children (if any); if
     * {@link SVNDepth#IMMEDIATES}, from <code>path</code> and all of its
     * immediate children (both files and directories); if
     * {@link SVNDepth#INFINITY}, from <code>path</code> and everything beneath
     * it.
     * 
     * @param url
     *            versioned item url
     * @param propName
     *            an item's property name; if it's <span
     *            class="javakeyword">null</span> then all the item's properties
     *            will be retrieved and passed to <code>handler</code> for
     *            processing
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision
     * @param depth
     *            tree depth
     * @param handler
     *            a caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX}
     *             prefix <li><code>path</code> is not under version control
     *             </ul>
     * @since 1.2, SVN 1.5
     */
    public void doGetProperty(SVNURL url, final String propName, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, final ISVNPropertyHandler handler) throws SVNException {
        SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromURL(url, pegRevision));
        getProperties.setRevision(revision);
        getProperties.setDepth(depth);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName != null && object.containsName(propName)) {
                    SVNPropertyData propertyData = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                    handler.handleProperty(target.getURL(), propertyData);
                }  else if (propName == null) {
                    for (Object propertyName : object.nameSet()) {
                        String name = propertyName.toString();
                        SVNPropertyData propertyData = new SVNPropertyData(name, object.getSVNPropertyValue(name), getOptions());
                        handler.handleProperty(target.getURL(), propertyData);
                    }
                }
            }
        });
        getProperties.run();
    }

    /**
     * Gets an unversioned revision property from a repository (getting a
     * repository URL from a Working Copy) and passes it to a provided property
     * handler.
     * 
     * @param path
     *            a local Working Copy item which repository location is used to
     *            connect to a repository
     * @param propName
     *            a revision property name; if this parameter is <span
     *            class="javakeyword">null</span> then all the revision
     *            properties will be retrieved and passed to
     *            <code>handler</code> for processing
     * @param revision
     *            a revision which property is to be retrieved
     * @param handler
     *            a caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>revision</code> is invalid
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             </ul>
     * @see #doGetRevisionProperty(SVNURL,String,SVNRevision,ISVNPropertyHandler)
     */
    public long doGetRevisionProperty(File path, final String propName, final SVNRevision revision, final ISVNPropertyHandler handler) throws SVNException {
        final SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromFile(path));
        getProperties.setRevisionProperties(true);
        getProperties.setRevision(revision);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName == null) {
                    for (String name : object.nameSet()) {
                        SVNPropertyData pdata = new SVNPropertyData(name, object.getSVNPropertyValue(name), getOptions());
                        if (handler != null) {
                            handler.handleProperty(getProperties.getRevisionNumber(), pdata);
                        }
                    }
                } else if (propName != null && object.containsName(propName)) {
                    SVNPropertyData pdata = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                    if (handler != null) {
                        handler.handleProperty(getProperties.getRevisionNumber(), pdata);
                    }
                } 
            }
        });
        getProperties.run();
        
        return getProperties.getRevisionNumber();
    }

    /**
     * Gets an unversioned revision property from a repository and passes it to
     * a provided property handler.
     * 
     * @param url
     *            a URL pointing to a repository location which revision
     *            property is to be got
     * @param propName
     *            a revision property name; if this parameter is <span
     *            class="javakeyword">null</span> then all the revision
     *            properties will be retrieved and passed to
     *            <code>handler</code> for processing
     * @param revision
     *            a revision which property is to be retrieved
     * @param handler
     *            a caller's property handler
     * @return actual revision number to which <code>revision</code> is resolved
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>revision</code> is invalid
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             </ul>
     * @see #doGetRevisionProperty(File,String,SVNRevision,ISVNPropertyHandler)
     */
    public long doGetRevisionProperty(SVNURL url, final String propName, final SVNRevision revision, final ISVNPropertyHandler handler) throws SVNException {
        final SvnGetProperties getProperties = getOperationsFactory().createGetProperties();
        getProperties.setSingleTarget(SvnTarget.fromURL(url));
        getProperties.setRevisionProperties(true);
        getProperties.setRevision(revision);
        getProperties.setReceiver(new ISvnObjectReceiver<SVNProperties>() {
            public void receive(SvnTarget target, SVNProperties object) throws SVNException {
                if (propName == null) {
                    for (String name : object.nameSet()) {
                        SVNPropertyData pdata = new SVNPropertyData(name, object.getSVNPropertyValue(name), getOptions());
                        if (handler != null) {
                            handler.handleProperty(getProperties.getRevisionNumber(), pdata);
                        }
                    }
                } else if (propName != null && object.containsName(propName)) {
                    SVNPropertyData pdata = new SVNPropertyData(propName, object.getSVNPropertyValue(propName), getOptions());
                    if (handler != null) {
                        handler.handleProperty(getProperties.getRevisionNumber(), pdata);
                    }
                } 
            }
        });
        getProperties.run();
        
        return getProperties.getRevisionNumber();
    }

    /**
     * Schedules a Working Copy item for deletion. This method is equivalent to
     * <code>doDelete(path, force, true, dryRun)</code>.
     * 
     * @param path
     *            a WC item to be deleted
     * @param force
     *            <span class="javakeyword">true</span> to force the operation
     *            to run
     * @param dryRun
     *            <span class="javakeyword">true</span> only to try the delete
     *            operation without actual deleting
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control
     *             <li>can not delete <code>path</code> without forcing
     *             </ul>
     * @see #doDelete(File,boolean,boolean,boolean)
     */
    public void doDelete(File path, boolean force, boolean dryRun) throws SVNException {
        doDelete(path, force, true, dryRun);
    }

    /**
     * Schedules a Working Copy item for deletion. This method allows to choose
     * - whether file item(s) are to be deleted from the filesystem or not.
     * Another version of the {@link #doDelete(File,boolean,boolean) doDelete()}
     * method is similar to the corresponding SVN client's command - <code>'svn delete'</code> as
     * it always deletes files from the filesystem.
     * 
     * <p/>
     * This method deletes only local working copy paths without connecting to
     * the repository.
     * 
     * @param path
     *            a WC item to be deleted
     * @param force
     *            <span class="javakeyword">true</span> to force the operation
     *            to run
     * @param deleteFiles
     *            if <span class="javakeyword">true</span> then files will be
     *            scheduled for deletion as well as deleted from the filesystem,
     *            otherwise files will be only scheduled for deletion and still
     *            be present in the filesystem
     * @param dryRun
     *            <span class="javakeyword">true</span> only to try the delete
     *            operation without actual deleting
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control <li>can
     *             not delete <code>path</code> without forcing
     *             </ul>
     */
    public void doDelete(File path, boolean force, boolean deleteFiles, boolean dryRun) throws SVNException {
        SvnScheduleForRemoval remove = getOperationsFactory().createScheduleForRemoval();
        remove.setSingleTarget(SvnTarget.fromFile(path));
        remove.setForce(force);
        remove.setDeleteFiles(deleteFiles);
        remove.setDryRun(dryRun);
        
        remove.run();
    }

    /**
     * Schedules an unversioned item for addition to a repository thus putting
     * it under version control.
     * <p/>
     * <p/>
     * To create and add to version control a new directory, set
     * <code>mkdir</code> to <span class="javakeyword">true</span>.
     * <p/>
     * <p/>
     * Calling this method is equivalent to
     * <code>doAdd(path, force, mkdir, climbUnversionedParents, recursive, false)</code>.
     * 
     * @param path
     *            a path to be put under version control (will be added to a
     *            repository in next commit)
     * @param force
     *            when <span class="javakeyword">true</span> forces the
     *            operation to run on already versioned files or directories
     *            without reporting error. When ran recursively, all unversioned
     *            files and directories in a tree will be scheduled for
     *            addition.
     * @param mkdir
     *            if <span class="javakeyword">true</span> - creates a new
     *            directory and schedules it for addition
     * @param climbUnversionedParents
     *            if <span class="javakeyword">true</span> and <code>path</code>
     *            is located in an unversioned parent directory then the parent
     *            will be automatically scheduled for addition, too
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> doesn't belong to a Working Copy <li>
     *             <code>path</code> doesn't exist and <code>mkdir</code> is
     *             <span class="javakeyword">false</span> <li><code>path</code>
     *             is the root directory of the Working Copy
     * @deprecated use
     *             {@link #doAdd(File, boolean, boolean, boolean, SVNDepth, boolean, boolean)}
     *             instead
     */
    public void doAdd(File path, boolean force, boolean mkdir, boolean climbUnversionedParents, boolean recursive) throws SVNException {
        File[] paths = new File[] {path};
        doAdd(paths, force, mkdir, climbUnversionedParents, SVNDepth.fromRecurse(recursive), false, false, climbUnversionedParents);
    }

    /**
     * Schedules an unversioned item for addition to a repository thus putting
     * it under version control.
     * <p/>
     * <p/>
     * To create and add to version control a new directory, set
     * <code>mkdir</code> to <span class="javakeyword">true</span>.
     * 
     * @param path
     *            a path to be put under version control (will be added to a
     *            repository in next commit)
     * @param force
     *            when <span class="javakeyword">true</span> forces the
     *            operation to run on already versioned files or directories
     *            without reporting error. When ran recursively, all unversioned
     *            files and directories in a tree will be scheduled for
     *            addition.
     * @param mkdir
     *            if <span class="javakeyword">true</span> - creates a new
     *            directory and schedules it for addition
     * @param climbUnversionedParents
     *            if <span class="javakeyword">true</span> and <code>path</code>
     *            is located in an unversioned parent directory then the parent
     *            will be automatically scheduled for addition, too
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @param includeIgnored
     *            controls whether ignored items must be also added
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> doesn't belong to a Working Copy <li>
     *             <code>path</code> doesn't exist and <code>mkdir</code> is
     *             <span class="javakeyword">false</span> <li><code>path</code>
     *             is the root directory of the Working Copy
     *             </ul>
     * @since 1.1
     * @deprecated use
     *             {@link #doAdd(File, boolean, boolean, boolean, SVNDepth, boolean, boolean)}
     *             instead
     */
    public void doAdd(File path, boolean force, boolean mkdir, boolean climbUnversionedParents, boolean recursive, boolean includeIgnored) throws SVNException {
        File[] paths = new File[] {path};
        doAdd(paths, force, mkdir, climbUnversionedParents, SVNDepth.fromRecurse(recursive), false, includeIgnored, climbUnversionedParents);
    }

    /**
     * Schedules a working copy <code>path</code> for addition to the
     * repository.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, adds just
     * <code>path</code> and nothing below it. If {@link SVNDepth#FILES}, adds
     * <code>path</code> and any file children of <code>path</code>. If
     * {@link SVNDepth#IMMEDIATES}, adds <code>path</code>, any file children,
     * and any immediate subdirectories (but nothing underneath those
     * subdirectories). If {@link SVNDepth#INFINITY}, adds <code>path</code> and
     * everything under it fully recursively.
     * 
     * <p/>
     * <code>path</code>'s parent must be under revision control already (unless
     * <code>makeParents</code> is <span class="javakeyword">true</span>), but
     * <code>path</code> is not.
     * 
     * <p/>
     * If <code>force</code> is set, <code>path</code> is a directory,
     * <code>depth</code> is {@link SVNDepth#INFINITY}, then schedules for
     * addition unversioned files and directories scattered deep within a
     * versioned tree.
     * 
     * <p/>
     * If <code>includeIgnored</code> is <span class="javakeyword">false</span>,
     * doesn't add files or directories that match ignore patterns.
     * 
     * <p/>
     * If <code>makeParents</code> is <span class="javakeyword">true</span>,
     * recurse up <code>path</code>'s directory and look for a versioned
     * directory. If found, add all intermediate paths between it and
     * <code>path</code>.
     * 
     * <p/>
     * Important: this is a *scheduling* operation. No changes will happen to
     * the repository until a commit occurs. This scheduling can be removed with
     * a call to {@link #doRevert(File[], SVNDepth, Collection)}.
     * 
     * @param path
     *            working copy path
     * @param force
     *            if <span class="javakeyword">true</span>, this method does not
     *            throw exceptions on already-versioned items
     * @param mkdir
     *            if <span class="javakeyword">true</span>, create a directory
     *            also at <code>path</code>
     * @param climbUnversionedParents
     *            not used; make use of <code>makeParents</code> instead
     * @param depth
     *            tree depth
     * @param includeIgnored
     *            if <span class="javakeyword">true</span>, does not apply
     *            ignore patterns to paths being added
     * @param makeParents
     *            if <span class="javakeyword">true</span>, climb upper and
     *            schedule also all unversioned paths in the way
     * @throws SVNException
     *             <ul>
     *             <li/>exception with {@link SVNErrorCode#ENTRY_EXISTS} error
     *             code - if <code>force</code> is not set and <code>path</code>
     *             is already under version <li/>exception with
     *             {@link SVNErrorCode#CLIENT_NO_VERSIONED_PARENT} error code -
     *             if <code>makeParents</code> is <span
     *             class="javakeyword">true</span> but no unversioned paths
     *             stepping upper from <code>path</code> are found
     * @since 1.2, SVN 1.5
     */
    public void doAdd(File path, boolean force, boolean mkdir, boolean climbUnversionedParents, SVNDepth depth, boolean includeIgnored, boolean makeParents) throws SVNException {
        File[] paths = new File[] {path};
        doAdd(paths, force, mkdir, climbUnversionedParents, depth, false, includeIgnored, makeParents);
    }

    /**
     * Schedules working copy <code>paths</code> for addition to the repository.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, adds just
     * <code>paths</code> and nothing below it. If {@link SVNDepth#FILES}, adds
     * <code>paths</code> and any file children of <code>paths</code>. If
     * {@link SVNDepth#IMMEDIATES}, adds <code>paths</code>, any file children,
     * and any immediate subdirectories (but nothing underneath those
     * subdirectories). If {@link SVNDepth#INFINITY}, adds <code>paths</code>
     * and everything under it fully recursively.
     * 
     * <p/>
     * <code>paths</code>' parent must be under revision control already (unless
     * <code>makeParents</code> is <span class="javakeyword">true</span>), but
     * <code>paths</code> are not.
     * 
     * <p/>
     * If <code>force</code> is set, path is a directory, <code>depth</code> is
     * {@link SVNDepth#INFINITY}, then schedules for addition unversioned files
     * and directories scattered deep within a versioned tree.
     * 
     * <p/>
     * If <code>includeIgnored</code> is <span class="javakeyword">false</span>,
     * doesn't add files or directories that match ignore patterns.
     * 
     * <p/>
     * If <code>makeParents</code> is <span class="javakeyword">true</span>,
     * recurse up path's directory and look for a versioned directory. If found,
     * add all intermediate paths between it and the path.
     * 
     * <p/>
     * Important: this is a *scheduling* operation. No changes will happen to
     * the repository until a commit occurs. This scheduling can be removed with
     * a call to {@link #doRevert(File[], SVNDepth, Collection)}.
     * 
     * @param paths
     *            working copy paths to add
     * @param force
     *            if <span class="javakeyword">true</span>, this method does not
     *            throw exceptions on already-versioned items
     * @param mkdir
     *            if <span class="javakeyword">true</span>, create a directory
     *            also at <code>path</code>
     * @param climbUnversionedParents
     *            not used; make use of <code>makeParents</code> instead
     * @param depth
     *            tree depth
     * @param depthIsSticky
     *            if depth should be recorded to the working copy
     * @param includeIgnored
     *            if <span class="javakeyword">true</span>, does not apply
     *            ignore patterns to paths being added
     * @param makeParents
     *            if <span class="javakeyword">true</span>, climb upper and
     *            schedule also all unversioned paths in the way
     * @throws SVNException
     *             <ul>
     *             <li/>exception with {@link SVNErrorCode#ENTRY_EXISTS} error
     *             code - if <code>force</code> is not set and a path is already
     *             under version <li/>exception with
     *             {@link SVNErrorCode#CLIENT_NO_VERSIONED_PARENT} error code -
     *             if <code>makeParents</code> is <span
     *             class="javakeyword">true</span> but no unversioned paths
     *             stepping upper from a path are found
     * @since 1.3
     */
    public void doAdd(File[] paths, boolean force, boolean mkdir, boolean climbUnversionedParents, SVNDepth depth, boolean depthIsSticky, boolean includeIgnored, boolean makeParents) throws SVNException {
        doAdd(paths, force, mkdir, climbUnversionedParents, depth, depthIsSticky, includeIgnored, makeParents, true);
    }

    /**
     * Schedules working copy <code>paths</code> for addition to the repository.
     *
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, adds just
     * <code>paths</code> and nothing below it. If {@link SVNDepth#FILES}, adds
     * <code>paths</code> and any file children of <code>paths</code>. If
     * {@link SVNDepth#IMMEDIATES}, adds <code>paths</code>, any file children,
     * and any immediate subdirectories (but nothing underneath those
     * subdirectories). If {@link SVNDepth#INFINITY}, adds <code>paths</code>
     * and everything under it fully recursively.
     *
     * <p/>
     * <code>paths</code>' parent must be under revision control already (unless
     * <code>makeParents</code> is <span class="javakeyword">true</span>), but
     * <code>paths</code> are not.
     *
     * <p/>
     * If <code>force</code> is set, path is a directory, <code>depth</code> is
     * {@link SVNDepth#INFINITY}, then schedules for addition unversioned files
     * and directories scattered deep within a versioned tree.
     *
     * <p/>
     * If <code>includeIgnored</code> is <span class="javakeyword">false</span>,
     * doesn't add files or directories that match ignore patterns.
     *
     * <p/>
     * If <code>makeParents</code> is <span class="javakeyword">true</span>,
     * recurse up path's directory and look for a versioned directory. If found,
     * add all intermediate paths between it and the path.
     *
     * <p/>
     * Important: this is a *scheduling* operation. No changes will happen to
     * the repository until a commit occurs. This scheduling can be removed with
     * a call to {@link #doRevert(File[], SVNDepth, Collection)}.
     *
     * @param paths
     *            working copy paths to add
     * @param force
     *            if <span class="javakeyword">true</span>, this method does not
     *            throw exceptions on already-versioned items
     * @param mkdir
     *            if <span class="javakeyword">true</span>, create a directory
     *            also at <code>path</code>
     * @param climbUnversionedParents
     *            not used; make use of <code>makeParents</code> instead
     * @param depth
     *            tree depth
     * @param depthIsSticky
     *            if depth should be recorded to the working copy
     * @param includeIgnored
     *            if <span class="javakeyword">true</span>, does not apply
     *            ignore patterns to paths being added
     * @param makeParents
     *            if <span class="javakeyword">true</span>, climb upper and
     *            schedule also all unversioned paths in the way
     * @param applyAutoProperties
     *            if <span class="javakeyword">true</span>, applies auto-properties on file addition
     *            otherwise auto-properties application is disabled
     * @throws SVNException
     *             <ul>
     *             <li/>exception with {@link SVNErrorCode#ENTRY_EXISTS} error
     *             code - if <code>force</code> is not set and a path is already
     *             under version <li/>exception with
     *             {@link SVNErrorCode#CLIENT_NO_VERSIONED_PARENT} error code -
     *             if <code>makeParents</code> is <span
     *             class="javakeyword">true</span> but no unversioned paths
     *             stepping upper from a path are found
     * @since 1.8
     */
    public void doAdd(File[] paths, boolean force, boolean mkdir, boolean climbUnversionedParents, SVNDepth depth, boolean depthIsSticky, boolean includeIgnored, boolean makeParents, boolean applyAutoProperties) throws SVNException {
        SvnScheduleForAddition add = getOperationsFactory().createScheduleForAddition();
        for (int i = 0; i < paths.length; i++) {
            add.addTarget(SvnTarget.fromFile(paths[i]));            
        }
        add.setMkDir(mkdir);
        add.setForce(force);
        add.setDepth(depth);
        add.setDepth(depth);
        add.setIncludeIgnored(includeIgnored);
        add.setAddParents(makeParents);
        add.setAddParameters(SvnCodec.addParameters(addParameters));
        add.setApplyAutoProperties(applyAutoProperties);
        
        add.run();
    }

    /**
     * Schedules a working copy <code>path</code> for addition to the
     * repository.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, adds just
     * <code>path</code> and nothing below it. If {@link SVNDepth#FILES}, adds
     * <code>path</code> and any file children of <code>path</code>. If
     * {@link SVNDepth#IMMEDIATES}, adds <code>path</code>, any file children,
     * and any immediate subdirectories (but nothing underneath those
     * subdirectories). If {@link SVNDepth#INFINITY}, adds <code>path</code> and
     * everything under it fully recursively.
     * 
     * <p/>
     * <code>path</code>'s parent must be under revision control already (unless
     * <code>makeParents</code> is <span class="javakeyword">true</span>), but
     * <code>path</code> is not.
     * 
     * <p/>
     * If <code>force</code> is set, <code>path</code> is a directory,
     * <code>depth</code> is {@link SVNDepth#INFINITY}, then schedules for
     * addition unversioned files and directories scattered deep within a
     * versioned tree.
     * 
     * <p/>
     * If <code>includeIgnored</code> is <span class="javakeyword">false</span>,
     * doesn't add files or directories that match ignore patterns.
     * 
     * <p/>
     * If <code>makeParents</code> is <span class="javakeyword">true</span>,
     * recurse up <code>path</code>'s directory and look for a versioned
     * directory. If found, add all intermediate paths between it and
     * <code>path</code>.
     * 
     * <p/>
     * Important: this is a *scheduling* operation. No changes will happen to
     * the repository until a commit occurs. This scheduling can be removed with
     * a call to {@link #doRevert(File[], SVNDepth, Collection)}.
     * 
     * @param path
     *            working copy path
     * @param force
     *            if <span class="javakeyword">true</span>, this method does not
     *            throw exceptions on already-versioned items
     * @param mkdir
     *            if <span class="javakeyword">true</span>, create a directory
     *            also at <code>path</code>
     * @param climbUnversionedParents
     *            not used; make use of <code>makeParents</code> instead
     * @param depth
     *            tree depth
     * @param depthIsSticky
     *            if depth should be recorded to the working copy
     * @param includeIgnored
     *            if <span class="javakeyword">true</span>, does not apply
     *            ignore patterns to paths being added
     * @param makeParents
     *            if <span class="javakeyword">true</span>, climb upper and
     *            schedule also all unversioned paths in the way
     * @throws SVNException
     *             <ul>
     *             <li/>exception with {@link SVNErrorCode#ENTRY_EXISTS} error
     *             code - if <code>force</code> is not set and <code>path</code>
     *             is already under version <li/>exception with
     *             {@link SVNErrorCode#CLIENT_NO_VERSIONED_PARENT} error code -
     *             if <code>makeParents</code> is <span
     *             class="javakeyword">true</span> but no unversioned paths
     *             stepping upper from <code>path</code> are found
     * @since 1.3
     */
    public void doAdd(File path, boolean force, boolean mkdir, boolean climbUnversionedParents, SVNDepth depth, boolean depthIsSticky, boolean includeIgnored, boolean makeParents) throws SVNException {
        File[] paths = new File[] {path};
        doAdd(paths, force, mkdir, climbUnversionedParents, depth, depthIsSticky, includeIgnored, makeParents);
    }

    /**
     * Schedules <code>path</code> as being replaced. This method does not
     * perform any deletion\addition in the filesysem nor does it require a
     * connection to the repository. It just marks the current <code>path</code>
     * item as being replaced.
     * 
     * @param path
     *            working copy path to mark as
     * @throws SVNException
     * @since 1.2
     */
    public void doMarkReplaced(File path) throws SVNException {
        SvnMarkReplaced mr = getOperationsFactory().createMarkReplaced();
        mr.setSingleTarget(SvnTarget.fromFile(path));
        mr.run();
    }

    /**
     * Reverts all local changes made to a Working Copy item(s) thus bringing it
     * to a 'pristine' state.
     * 
     * @param path
     *            a WC path to perform a revert on
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control
     *             <li>when trying to revert an addition of a directory from
     *             within the directory itself
     *             </ul>
     * @see #doRevert(File[],boolean)
     * @deprecated use {@link #doRevert(File[], SVNDepth, Collection)}
     */
    public void doRevert(File path, boolean recursive) throws SVNException {
        doRevert(new File[] {path}, SVNDepth.fromRecurse(recursive), null);
    }

    /**
     * Reverts all local changes made to a Working Copy item(s) thus bringing it
     * to a 'pristine' state.
     * 
     * @param paths
     *            a WC paths to perform a revert on
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control
     *             <li>when trying to revert an addition of a directory from
     *             within the directory itself
     *             </ul>
     *             <p/>
     *             Exception will not be thrown if there are multiple paths
     *             passed. Instead caller should process events received by
     *             <code>ISVNEventHandler</code> instance to get information on
     *             whether certain path was reverted or not.
     * @deprecated use {@link #doRevert(File[], SVNDepth, Collection)} instead
     */
    public void doRevert(File[] paths, boolean recursive) throws SVNException {
        doRevert(paths, SVNDepth.fromRecurse(recursive), null);
    }

    /**
     * Restores the pristine version of working copy <code>paths</code>,
     * effectively undoing any local mods. For each path in <code>paths</code>,
     * reverts it if it is a file. Else if it is a directory, reverts according
     * to <code>depth</code>:
     * 
     * <p/>
     * If </code>depth</code> is {@link SVNDepth#EMPTY}, reverts just the
     * properties on the directory; else if {@link SVNDepth#FILES}, reverts the
     * properties and any files immediately under the directory; else if
     * {@link SVNDepth#IMMEDIATES}, reverts all of the preceding plus properties
     * on immediate subdirectories; else if {@link SVNDepth#INFINITY}, reverts
     * path and everything under it fully recursively.
     * 
     * <p/>
     * <code>changeLists</code> is a collection of <code>String</code>
     * changelist names, used as a restrictive filter on items reverted; that
     * is, doesn't revert any item unless it's a member of one of those
     * changelists. If <code>changeLists</code> is empty (or <span
     * class="javakeyword">null</span>), no changelist filtering occurs.
     * 
     * <p/>
     * If an item specified for reversion is not under version control, then
     * does not fail with an exception, just invokes {@link ISVNEventHandler}
     * using notification code {@link SVNEventAction#SKIP}.
     * 
     * @param paths
     *            working copy paths to revert
     * @param depth
     *            tree depth
     * @param changeLists
     *            collection with changelist names
     * @throws SVNException
     * @since 1.2, SVN 1.5
     */
    public void doRevert(File[] paths, SVNDepth depth, Collection<String> changeLists) throws SVNException {
        SvnRevert revert = getOperationsFactory().createRevert();
        for (int i = 0; i < paths.length; i++) {
            revert.addTarget(SvnTarget.fromFile(paths[i]));
        }
        revert.setDepth(depth);
        revert.setApplicalbeChangelists(changeLists);
        revert.setRevertMissingDirectories(revertMissingDirectories);
        
        revert.run();
    }

    /**
     * Resolves a 'conflicted' state on a Working Copy item.
     * 
     * @param path
     *            a WC item to be resolved
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories) - this will resolve the entire tree
     * @throws SVNException
     *             if <code>path</code> is not under version control
     * @deprecated use {@link #doResolve(File, SVNDepth, SVNConflictChoice)}
     *             instead
     */
    public void doResolve(File path, boolean recursive) throws SVNException {
        doResolve(path, SVNDepth.fromRecurse(recursive), SVNConflictChoice.MINE_FULL);
    }

    /**
     * Performs automatic conflict resolution on a working copy
     * <code>path</code>.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, acts only on
     * <code>path</code>; if {@link SVNDepth#FILES}, resolves <code>path</code>
     * and its conflicted file children (if any); if {@link SVNDepth#IMMEDIATES}
     * , resolves <code>path</code> and all its immediate conflicted children
     * (both files and directories, if any); if {@link SVNDepth#INFINITY},
     * resolves <code>path</code> and every conflicted file or directory
     * anywhere beneath it.
     * 
     * <p/>
     * If <code>conflictChoice</code> is {@link SVNConflictChoice#BASE},
     * resolves the conflict with the old file contents; if
     * {@link SVNConflictChoice#MINE_FULL}, uses the original working contents;
     * if {@link SVNConflictChoice#THEIRS_FULL}, the new contents; and if
     * {@link SVNConflictChoice#MERGED}, doesn't change the contents at all,
     * just removes the conflict status, which is the pre-1.2 (pre-SVN 1.5)
     * behavior.
     * 
     * <p/>
     * {@link SVNConflictChoice#THEIRS_CONFLICT} and
     * {@link SVNConflictChoice#MINE_CONFLICT} are not legal for binary files or
     * properties.
     * 
     * <p/>
     * If <code>path</code> is not in a state of conflict to begin with, does
     * nothing. If <code>path</code>'s conflict state is removed and caller's
     * {@link ISVNEntryHandler} is not <span class="javakeyword">null</span>,
     * then an {@link SVNEventAction#RESOLVED} event is dispatched to the
     * handler.
     * 
     * <p/>
     * This is equivalent to calling
     * <code>doResolve(path, depth, true, true, conflictChoice)</code>.
     * 
     * @param path
     *            working copy path
     * @param depth
     *            tree depth
     * @param conflictChoice
     *            choice object for making decision while resolving
     * @throws SVNException
     * @since 1.2, SVN 1.5
     */
    public void doResolve(File path, SVNDepth depth, SVNConflictChoice conflictChoice) throws SVNException {
    	SvnResolve resolve = getOperationsFactory().createResolve();
    	resolve.addTarget(SvnTarget.fromFile(path));
    	resolve.setDepth(depth);
    	resolve.setConflictChoice(conflictChoice);
    	resolve.run();
    }

    /**
     * Performs automatic conflict resolution on a working copy
     * <code>path</code>.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, acts only on
     * <code>path</code>; if {@link SVNDepth#FILES}, resolves <code>path</code>
     * and its conflicted file children (if any); if {@link SVNDepth#IMMEDIATES}
     * , resolves <code>path</code> and all its immediate conflicted children
     * (both files and directories, if any); if {@link SVNDepth#INFINITY},
     * resolves <code>path</code> and every conflicted file or directory
     * anywhere beneath it.
     * 
     * <p/>
     * If <code>conflictChoice</code> is {@link SVNConflictChoice#BASE},
     * resolves the conflict with the old file contents; if
     * {@link SVNConflictChoice#MINE_FULL}, uses the original working contents;
     * if {@link SVNConflictChoice#THEIRS_FULL}, the new contents; and if
     * {@link SVNConflictChoice#MERGED}, doesn't change the contents at all,
     * just removes the conflict status, which is the pre-1.2 (pre-SVN 1.5)
     * behavior.
     * 
     * <p/>
     * {@link SVNConflictChoice#THEIRS_CONFLICT} and
     * {@link SVNConflictChoice#MINE_CONFLICT} are not legal for binary files or
     * properties.
     * 
     * <p/>
     * If <code>path</code> is not in a state of conflict to begin with, does
     * nothing. If <code>path</code>'s conflict state is removed and caller's
     * {@link ISVNEntryHandler} is not <span class="javakeyword">null</span>,
     * then an {@link SVNEventAction#RESOLVED} event is dispatched to the
     * handler.
     * 
     * @param path
     *            working copy path
     * @param depth
     *            tree depth
     * @param resolveContents
     *            resolve content conflict
     * @param resolveProperties
     *            resolve property conflict
     * @param conflictChoice
     *            choice object for making decision while resolving
     * @throws SVNException
     * @since 1.2, SVN 1.5
     */
    public void doResolve(File path, SVNDepth depth, final boolean resolveContents, final boolean resolveProperties, SVNConflictChoice conflictChoice) throws SVNException {
    	SvnResolve resolve = getOperationsFactory().createResolve();
    	resolve.addTarget(SvnTarget.fromFile(path));
    	resolve.setDepth(depth);
    	resolve.setResolveContents(resolveContents);
    	resolve.setResolveProperties(resolveProperties);
    	resolve.setConflictChoice(conflictChoice);
    	resolve.run();
    }

    /**
     * Performs automatic conflict resolution on a working copy
     * <code>path</code>.
     * 
     * <p/>
     * If <code>depth</code> is {@link SVNDepth#EMPTY}, acts only on
     * <code>path</code>; if {@link SVNDepth#FILES}, resolves <code>path</code>
     * and its conflicted file children (if any); if {@link SVNDepth#IMMEDIATES}
     * , resolves <code>path</code> and all its immediate conflicted children
     * (both files and directories, if any); if {@link SVNDepth#INFINITY},
     * resolves <code>path</code> and every conflicted file or directory
     * anywhere beneath it.
     * 
     * <p/>
     * If <code>conflictChoice</code> is {@link SVNConflictChoice#BASE},
     * resolves the conflict with the old file contents; if
     * {@link SVNConflictChoice#MINE_FULL}, uses the original working contents;
     * if {@link SVNConflictChoice#THEIRS_FULL}, the new contents; and if
     * {@link SVNConflictChoice#MERGED}, doesn't change the contents at all,
     * just removes the conflict status, which is the pre-1.2 (pre-SVN 1.5)
     * behavior.
     * 
     * <p/>
     * {@link SVNConflictChoice#THEIRS_CONFLICT} and
     * {@link SVNConflictChoice#MINE_CONFLICT} are not legal for binary files or
     * properties.
     * 
     * <p/>
     * If <code>path</code> is not in a state of conflict to begin with, does
     * nothing. If <code>path</code>'s conflict state is removed and caller's
     * {@link ISVNEntryHandler} is not <span class="javakeyword">null</span>,
     * then an {@link SVNEventAction#RESOLVED} event is dispatched to the
     * handler.
     * 
     * @param path
     *            working copy path
     * @param depth
     *            tree depth
     * @param resolveContents
     *            resolve content conflict
     * @param resolveProperties
     *            resolve property conflict
     * @param resolveTree
     *            n resolve any tree conlicts
     * @param conflictChoice
     *            choice object for making decision while resolving
     * @throws SVNException
     * @since 1.3, SVN 1.6
     */
    public void doResolve(File path, SVNDepth depth, final boolean resolveContents, final boolean resolveProperties, final boolean resolveTree, SVNConflictChoice conflictChoice) throws SVNException {
    	SvnResolve resolve = getOperationsFactory().createResolve();
    	resolve.addTarget(SvnTarget.fromFile(path));
    	resolve.setDepth(depth);
    	resolve.setResolveContents(resolveContents);
    	resolve.setResolveProperties(resolveProperties);
    	resolve.setResolveTree(resolveTree);
    	resolve.setConflictChoice(conflictChoice);
    	resolve.run();
    }

    /**
     * Locks file items in a Working Copy as well as in a repository so that no
     * other user can commit changes to them.
     * 
     * @param paths
     *            an array of local WC file paths that should be locked
     * @param stealLock
     *            if <span class="javakeyword">true</span> then all existing
     *            locks on the specified <code>paths</code> will be "stolen"
     * @param lockMessage
     *            an optional lock comment
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li>a path to be locked is not under version control
     *             <li>can not obtain a URL of a local path to lock it in the
     *             repository - there's no such entry
     *             <li><code>paths</code> to be locked belong to different
     *             repositories
     *             </ul>
     * @see #doLock(SVNURL[],boolean,String)
     */
    public void doLock(File[] paths, boolean stealLock, String lockMessage) throws SVNException {
    	SvnSetLock lock = getOperationsFactory().createSetLock();
    	for (int i = 0; i < paths.length; i++) {
    		lock.addTarget(SvnTarget.fromFile(paths[i]));            
        }
    	lock.setStealLock(stealLock);
    	lock.setLockMessage(lockMessage);
    	
    	lock.run();
    }

    /**
     * Locks file items in a repository so that no other user can commit changes
     * to them.
     * 
     * @param urls
     *            an array of URLs to be locked
     * @param stealLock
     *            if <span class="javakeyword">true</span> then all existing
     *            locks on the specified <code>urls</code> will be "stolen"
     * @param lockMessage
     *            an optional lock comment
     * @throws SVNException
     * @see #doLock(File[],boolean,String)
     */
    public void doLock(SVNURL[] urls, boolean stealLock, String lockMessage) throws SVNException {
    	SvnSetLock lock = getOperationsFactory().createSetLock();
    	for (int i = 0; i < urls.length; i++) {
    		lock.addTarget(SvnTarget.fromURL(urls[i]));            
        }
    	lock.setStealLock(stealLock);
    	lock.setLockMessage(lockMessage);
    	
    	lock.run();
    	
        
    }

    /**
     * Unlocks file items in a Working Copy as well as in a repository.
     * 
     * @param paths
     *            an array of local WC file paths that should be unlocked
     * @param breakLock
     *            if <span class="javakeyword">true</span> and there are locks
     *            that belong to different users then those locks will be also
     *            unlocked - that is "broken"
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li>a path is not under version control
     *             <li>can not obtain a URL of a local path to unlock it in the
     *             repository - there's no such entry
     *             <li>if a path is not locked in the Working Copy and
     *             <code>breakLock</code> is <span
     *             class="javakeyword">false</span>
     *             <li><code>paths</code> to be unlocked belong to different
     *             repositories
     *             </ul>
     * @see #doUnlock(SVNURL[],boolean)
     */
    public void doUnlock(File[] paths, boolean breakLock) throws SVNException {
    	SvnUnlock unlock = getOperationsFactory().createUnlock();
    	for (int i = 0; i < paths.length; i++) {
    		unlock.addTarget(SvnTarget.fromFile(paths[i]));            
        }
    	unlock.setBreakLock(breakLock);
    	
    	unlock.run();
    }

    /**
     * Unlocks file items in a repository.
     * 
     * @param urls
     *            an array of URLs that should be unlocked
     * @param breakLock
     *            if <span class="javakeyword">true</span> and there are locks
     *            that belong to different users then those locks will be also
     *            unlocked - that is "broken"
     * @throws SVNException
     * @see #doUnlock(File[],boolean)
     */
    public void doUnlock(SVNURL[] urls, boolean breakLock) throws SVNException {
    	SvnUnlock unlock = getOperationsFactory().createUnlock();
    	for (int i = 0; i < urls.length; i++) {
    		unlock.addTarget(SvnTarget.fromURL(urls[i]));            
        }
    	unlock.setBreakLock(breakLock);
    	
    	unlock.run();
    }

    /**
     * Collects information about Working Copy item(s) and passes it to an info
     * handler.
     * <p/>
     * <p/>
     * If <code>revision</code> is valid and not local, then information will be
     * collected on remote items (that is taken from a repository). Otherwise
     * information is gathered on local items not accessing a repository.
     * 
     * @param path
     *            a WC item on which info should be obtained
     * @param revision
     *            a target revision
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @param handler
     *            a caller's info handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control <li>can
     *             not obtain a URL corresponding to <code>path</code> to get
     *             its information from the repository - there's no such entry
     *             <li>if a remote info: <code>path</code> is an item that does
     *             not exist in the specified <code>revision</code>
     *             </ul>
     * @deprecated use
     *             {@link #doInfo(File, SVNRevision, SVNRevision, SVNDepth, Collection, ISVNInfoHandler)}
     *             instead
     */
    public void doInfo(File path, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException {
        doInfo(path, SVNRevision.UNDEFINED, revision, SVNDepth.getInfinityOrEmptyDepth(recursive), null, handler);
    }

    /**
     * Collects information about Working Copy item(s) and passes it to an info
     * handler.
     * <p/>
     * <p/>
     * If <code>revision</code> & <code>pegRevision</code> are valid and not
     * local, then information will be collected on remote items (that is taken
     * from a repository). Otherwise information is gathered on local items not
     * accessing a repository.
     * 
     * @param path
     *            a WC item on which info should be obtained
     * @param pegRevision
     *            a revision in which <code>path</code> is first looked up
     * @param revision
     *            a target revision
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @param handler
     *            a caller's info handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control <li>can
     *             not obtain a URL corresponding to <code>path</code> to get
     *             its information from the repository - there's no such entry
     *             <li>if a remote info: <code>path</code> is an item that does
     *             not exist in the specified <code>revision</code>
     *             </ul>
     * @deprecated use
     *             {@link #doInfo(File, SVNRevision, SVNRevision, SVNDepth, Collection, ISVNInfoHandler)}
     *             instead
     */
    public void doInfo(File path, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException {
        doInfo(path, pegRevision, revision, SVNDepth.getInfinityOrEmptyDepth(recursive), null, handler);
    }

    /**
     * Invokes <code>handler</code> to return information about
     * <code>path</code> in <code>revision</code>. The information returned is
     * system-generated metadata, not the sort of "property" metadata created by
     * users. See {@link SVNInfo}.
     * 
     * <p/>
     * If both revision arguments are either <span
     * class="javakeyword">null</span> or {@link SVNRevision#isLocal() local},
     * or {@link SVNRevision#isValid() invalid}, then information will be pulled
     * solely from the working copy; no network connections will be made.
     * 
     * <p/>
     * Otherwise, information will be pulled from a repository. The actual node
     * revision selected is determined by the <code>path</code> as it exists in
     * <code>pegRevision</code>. If <code>pegRevision</code> is
     * {@link SVNRevision#UNDEFINED}, then it defaults to
     * {@link SVNRevision#WORKING}.
     * 
     * <p/>
     * If <code>path</code> is a file, just invokes <code>handler</code> on it.
     * If it is a directory, then descends according to <code>depth</code>. If
     * <code>depth</code> is {@link SVNDepth#EMPTY}, invokes
     * <code>handler</code> on <code>path</code> and nothing else; if
     * {@link SVNDepth#FILES}, on <code>path</code> and its immediate file
     * children; if {@link SVNDepth#IMMEDIATES}, the preceding plus on each
     * immediate subdirectory; if {@link SVNDepth#INFINITY}, then recurses
     * fully, invoking <code>handler</code> on <code>path</code> and everything
     * beneath it.
     * 
     * <p/>
     * <code>changeLists</code> is a collection of <code>String</code>
     * changelist names, used as a restrictive filter on items whose info is
     * reported; that is, doesn't report info about any item unless it's a
     * member of one of those changelists. If <code>changeLists</code> is empty
     * (or <span class="javakeyword">null</span>), no changelist filtering
     * occurs.
     * 
     * @param path
     *            a WC item on which info should be obtained
     * @param pegRevision
     *            a revision in which <code>path</code> is first looked up
     * @param revision
     *            a target revision
     * @param depth
     *            tree depth
     * @param changeLists
     *            collection changelist names
     * @param handler
     *            caller's info handler
     * @throws SVNException
     * @since 1.2, SVN 1.5
     */
    public void doInfo(File path, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, Collection<String> changeLists, final ISVNInfoHandler handler) throws SVNException {
        SvnGetInfo getInfo = getOperationsFactory().createGetInfo();
        getInfo.setSingleTarget(SvnTarget.fromFile(path, pegRevision));
        getInfo.setRevision(revision);
        getInfo.setDepth(depth);
        getInfo.setApplicalbeChangelists(changeLists);
        getInfo.setReceiver(new ISvnObjectReceiver<SvnInfo>() {
            public void receive(SvnTarget target, SvnInfo object) throws SVNException {
                handler.handleInfo(SvnCodec.info(object));
            }
        });
        getInfo.run();
    }

    /**
     * Collects information about item(s) in a repository and passes it to an
     * info handler.
     * 
     * @param url
     *            a URL of an item which information is to be obtained and
     *            processed
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     *            (relevant for directories)
     * @param handler
     *            a caller's info handler
     * @throws SVNException
     *             if <code>url</code> is an item that does not exist in the
     *             specified <code>revision</code>
     * @deprecated use
     *             {@link #doInfo(SVNURL, SVNRevision, SVNRevision, SVNDepth, ISVNInfoHandler)}
     *             instead
     */
    public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException {
        doInfo(url, pegRevision, revision, SVNDepth.getInfinityOrEmptyDepth(recursive), handler);
    }

    /**
     * Invokes <code>handler</code> to return information about <code>url</code>
     * in <code>revision</code>. The information returned is system-generated
     * metadata, not the sort of "property" metadata created by users. See
     * {@link SVNInfo}.
     * 
     * <p/>
     * If <code>revision</code> argument is either <span
     * class="javakeyword">null</span> or {@link SVNRevision#isValid() invalid},
     * it defaults to {@link SVNRevision#HEAD}. If <code>revision</code> is
     * {@link SVNRevision#PREVIOUS} (or some other kind that requires a local
     * path), an error will be returned, because the desired revision cannot be
     * determined. If <code>pegRevision</code> argument is either <span
     * class="javakeyword">null</span> or {@link SVNRevision#isValid() invalid},
     * it defaults to <code>revision</code>.
     * 
     * <p/>
     * Information will be pulled from the repository. The actual node revision
     * selected is determined by the <code>url</code> as it exists in
     * <code>pegRevision</code>. If <code>pegRevision</code> is
     * {@link SVNRevision#UNDEFINED}, then it defaults to
     * {@link SVNRevision#WORKING}.
     * 
     * <p/>
     * If <code>url</code> is a file, just invokes <code>handler</code> on it.
     * If it is a directory, then descends according to <code>depth</code>. If
     * <code>depth</code> is {@link SVNDepth#EMPTY}, invokes
     * <code>handler</code> on <code>url</code> and nothing else; if
     * {@link SVNDepth#FILES}, on <code>url</code> and its immediate file
     * children; if {@link SVNDepth#IMMEDIATES}, the preceding plus on each
     * immediate subdirectory; if {@link SVNDepth#INFINITY}, then recurses
     * fully, invoking <code>handler</code> on <code>url</code> and everything
     * beneath it.
     * 
     * @param url
     *            versioned item url
     * @param pegRevision
     *            revision in which <code>path</code> is first looked up
     * @param revision
     *            target revision
     * @param depth
     *            tree depth
     * @param handler
     *            caller's info handler
     * @throws SVNException
     * @since 1.2, SVN 1.5
     */
    public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, final ISVNInfoHandler handler) throws SVNException {
        SvnGetInfo getInfo = getOperationsFactory().createGetInfo();
        getInfo.setSingleTarget(SvnTarget.fromURL(url, pegRevision));
        getInfo.setRevision(revision);
        getInfo.setDepth(depth);
        getInfo.setReceiver(new ISvnObjectReceiver<SvnInfo>() {
            public void receive(SvnTarget target, SvnInfo object) throws SVNException {
                handler.handleInfo(SvnCodec.info(object));
            }
        });
        getInfo.run();
    }

    /**
     * Returns the current Working Copy min- and max- revisions as well as
     * changes and switch status within a single string.
     * 
     * <p/>
     * This method is the same as
     * <code>doGetWorkingCopyID(path, trailURL, false)</code>.
     * 
     * @param path
     *            a local path
     * @param trailURL
     *            optional: if not <span class="javakeyword">null</span>
     *            specifies the name of the item that should be met in the URL
     *            corresponding to the repository location of the
     *            <code>path</code>; if that URL ends with something different
     *            than this optional parameter - the Working Copy will be
     *            considered "switched"
     * @return brief info on the Working Copy or the string "exported" if
     *         <code>path</code> is a clean directory
     * @throws SVNException
     *             if <code>path</code> is neither versioned nor even exported
     * @see #doGetWorkingCopyID(File, String, boolean)
     */
    public String doGetWorkingCopyID(final File path, String trailURL) throws SVNException {
        return doGetWorkingCopyID(path, trailURL, false);
    }

    /**
     * Returns the current Working Copy min- and max- revisions as well as
     * changes and switch status within a single string.
     * <p/>
     * <p/>
     * A return string has a form of <code>"minR[:maxR][M][S]"</code> where:
     * <ul>
     * <li><code>minR</code> - is the smallest revision number met in the
     * Working Copy
     * <li><code>maxR</code> - is the biggest revision number met in the Working
     * Copy; appears only if there are different revision in the Working Copy
     * <li><code>M</code> - appears only if there're local edits to the Working
     * Copy - that means 'Modified'
     * <li><code>S</code> - appears only if the Working Copy is switched against
     * a different URL
     * </ul>
     * If <code>path</code> is a directory - this method recursively descends
     * into the Working Copy, collects and processes local information.
     * 
     * <p/>
     * This method operates on local working copies only without accessing a
     * repository.
     * 
     * @param path
     *            a local path
     * @param trailURL
     *            optional: if not <span class="javakeyword">null</span>
     *            specifies the name of the item that should be met in the URL
     *            corresponding to the repository location of the
     *            <code>path</code>; if that URL ends with something different
     *            than this optional parameter - the Working Copy will be
     *            considered "switched"
     * @param committed
     *            if <span class="javakeyword">true</span> committed (last
     *            chaned) revisions instead of working copy ones are reported
     * @return brief info on the Working Copy or the string "exported" if
     *         <code>path</code> is a clean directory
     * @throws SVNException
     *             if <code>path</code> is neither versioned nor even exported
     * @since 1.2
     */
    public String doGetWorkingCopyID(final File path, String trailURL, final boolean committed) throws SVNException {
        SvnGetStatusSummary gs = getOperationsFactory().createGetStatusSummary();
        gs.addTarget(SvnTarget.fromFile(path));
        gs.setTrailUrl(trailURL);
        gs.setCommitted(committed);
        SvnStatusSummary summary;
        try {
            summary = gs.run();
        } catch (SVNException e) {
            if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND 
                    || e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_WORKING_COPY) {
                SVNFileType ft = SVNFileType.getType(path);
                if (ft == SVNFileType.SYMLINK) {
                    return "Unversioned symlink";
                } else if (ft == SVNFileType.DIRECTORY) {
                    return "Unversioned directory";
                } else if (ft == SVNFileType.FILE) {
                    return "Unversioned file";
                }
                return "'" + path.getPath() + "' doesn't exist";
            } else {
                throw e;
            }
            
        }
        if (summary.getMinRevision() < 0) {
            return "Uncommitted local addition, copy or move";
        }
        StringBuffer result = new StringBuffer();
        result.append(summary.getMinRevision());
        if (summary.getMaxRevision() != summary.getMinRevision()) {
            result.append(":");
            result.append(summary.getMaxRevision());
        }
        result.append(summary.isModified() ? "M" : "");
        result.append(summary.isSwitched() ? "S" : "");
        result.append(summary.isSparseCheckout() ? "P" : "");
        
        return result.toString();
    }

    /**
     * Collects and returns information on a single Working Copy item.
     * 
     * <p/>
     * This method is the same as
     * <code>doInfo(path, SVNRevision.UNDEFINED, revision, SVNDepth.EMPTY, null, handler)</code>
     * where <code>handler</code> just stores {@link SVNInfo} for the
     * <code>path</code> and then returns it to the caller.
     * 
     * @param path
     *            a WC item on which info should be obtained
     * @param revision
     *            a target revision
     * @return collected info
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>path</code> is not under version control <li>can
     *             not obtain a URL corresponding to <code>path</code> to get
     *             its information from the repository - there's no such entry
     *             <li>if a remote info: <code>path</code> is an item that does
     *             not exist in the specified <code>revision</code>
     *             </ul>
     * @see #doInfo(File, SVNRevision, SVNRevision, SVNDepth, Collection,
     *      ISVNInfoHandler)
     */
    public SVNInfo doInfo(File path, SVNRevision revision) throws SVNException {
        final SVNInfo[] info = new SVNInfo[1];
        SvnGetInfo getInfo = getOperationsFactory().createGetInfo();
        getInfo.setSingleTarget(SvnTarget.fromFile(path));
        getInfo.setRevision(revision);
        getInfo.setDepth(SVNDepth.EMPTY);
        getInfo.setReceiver(new ISvnObjectReceiver<SvnInfo>() {
            public void receive(SvnTarget target, SvnInfo object) throws SVNException {
                if (info[0] == null) {
                    info[0] = SvnCodec.info(object);
                }
            }
        });
        getInfo.run();
        return info[0];
    }

    /**
     * Collects and returns information on a single item in a repository.
     * 
     * <p/>
     * This method is the same as
     * <code>doInfo(url, pegRevision, revision, SVNDepth.EMPTY, handler)</code>
     * where <code>handler</code> just stores {@link SVNInfo} for the
     * <code>url</code>.
     * 
     * @param url
     *            a URL of an item which information is to be obtained
     * @param pegRevision
     *            a revision in which the item is first looked up
     * @param revision
     *            a target revision
     * @return collected info
     * @throws SVNException
     *             if <code>url</code> is an item that does not exist in the
     *             specified <code>revision</code>
     * @see #doInfo(SVNURL, SVNRevision, SVNRevision, SVNDepth, ISVNInfoHandler)
     */
    public SVNInfo doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision) throws SVNException {
        final SVNInfo[] info = new SVNInfo[1];
        SvnGetInfo getInfo = getOperationsFactory().createGetInfo();
        getInfo.setSingleTarget(SvnTarget.fromURL(url, pegRevision));
        getInfo.setRevision(revision);
        getInfo.setDepth(SVNDepth.EMPTY);
        getInfo.setReceiver(new ISvnObjectReceiver<SvnInfo>() {
            public void receive(SvnTarget target, SvnInfo object) throws SVNException {
                if (info[0] == null) {
                    info[0] = SvnCodec.info(object);
                }
            }
        });
        getInfo.run();
        return info[0];
    }

    /**
     * Recursively removes all DAV-specific <span
     * class="javakeyword">"svn:wc:"</span> properties from the
     * <code>directory</code> and beneath.
     * 
     * <p>
     * This method does not connect to a repository, it's a local operation
     * only. Nor does it change any user's versioned data. Changes are made only
     * in administrative version control files.
     * 
     * @param directory
     *            working copy path
     * @throws SVNException
     * @since 1.2
     */
    public void doCleanupWCProperties(File directory) throws SVNException {
        SVNWCClient16 oldClient = new SVNWCClient16(getOperationsFactory().getAuthenticationManager(), getOptions());
        oldClient.doCleanupWCProperties(directory);
    }

    /**
     * Changes working copy format. This method may be used to upgrade\downgrade
     * working copy formats.
     * 
     * <p>
     * If externals are not {@link SVNBasicClient#isIgnoreExternals() ignored}
     * then external working copies are also converted to the new working copy
     * <code>format</code>.
     * 
     * <p>
     * This method does not connect to a repository, it's a local operation
     * only. Nor does it change any user's versioned data. Changes are made only
     * in administrative version control files.
     * 
     * <p> 
     * Once upgraded to 1.7 format (12) working copy format could not be downgraded.
     * 
     * @param directory
     *            working copy directory
     * @param format
     *            format to set, supported formats are: 12 (SVN 1.7), 10 (SVN 1.6), 9 (SVN 1.5), 8 (SVN 1.4)
     *            and 4 (SVN 1.2)
     * @throws SVNException
     * @since 1.2
     */
    public void doSetWCFormat(File directory, int format) throws SVNException {
        if (format >= 12) {
            SvnUpgrade upgrade = getOperationsFactory().createUpgrade();
            upgrade.setSingleTarget(SvnTarget.fromFile(directory));
            upgrade.setDepth(SVNDepth.INFINITY);
            upgrade.run();
        } else {
            SVNWCClient16 oldClient = new SVNWCClient16(getOperationsFactory().getAuthenticationManager(), getOptions());
            oldClient.doSetWCFormat(directory, format);
        }
    }

    /**
     * This method is deprecated.
     * 
     * @param path
     *            a WC item which properties are to be modified
     * @param propName
     *            a property name
     * @param propValue
     *            a property value
     * @param force
     *            <span class="javakeyword">true</span> to force the operation
     *            to run
     * @param recursive
     *            <span class="javakeyword">true</span> to descend recursively
     * @param handler
     *            a caller's property handler
     * @throws SVNException
     *             if one of the following is true:
     *             <ul>
     *             <li><code>propName</code> is a revision property
     *             <li><code>propName</code> starts with the
     *             {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
     *             svn:wc:} prefix
     *             </ul>
     * @deprecated use
     *             {@link #doSetProperty(File, String, SVNPropertyValue, boolean, SVNDepth, ISVNPropertyHandler, Collection)}
     *             instead
     */
    public void doSetProperty(File path, String propName, SVNPropertyValue propValue, boolean force, boolean recursive, ISVNPropertyHandler handler) throws SVNException {
        doSetProperty(path, propName, propValue, force, SVNDepth.fromRecurse(recursive), handler, null);
    }
    
}
