package org.tmatesoft.svn.core.internal.wc16;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNRevisionProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNHashSet;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.internal.wc.IOExceptionWrapper;
import org.tmatesoft.svn.core.internal.wc.ISVNFileContentFetcher;
import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
import org.tmatesoft.svn.core.internal.wc.SVNCancellableOutputStream;
import org.tmatesoft.svn.core.internal.wc.SVNCommitUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNExternal;
import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.SVNPropertiesManager;
import org.tmatesoft.svn.core.internal.wc.SVNStatusEditor;
import org.tmatesoft.svn.core.internal.wc.SVNTreeConflictUtil;
import org.tmatesoft.svn.core.internal.wc.SVNWCManager;
import org.tmatesoft.svn.core.internal.wc.admin.ISVNEntryHandler;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaInfo;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNLog;
import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator;
import org.tmatesoft.svn.core.internal.wc.admin.SVNVersionedProperties;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.ISVNLockHandler;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.DefaultSVNCommitHandler;
import org.tmatesoft.svn.core.wc.ISVNAddParameters;
import org.tmatesoft.svn.core.wc.ISVNCommitHandler;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNInfoHandler;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.ISVNPropertyHandler;
import org.tmatesoft.svn.core.wc.ISVNPropertyValueProvider;
import org.tmatesoft.svn.core.wc.ISVNRepositoryPool;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNBasicClient;
import org.tmatesoft.svn.core.wc.SVNCommitItem;
import org.tmatesoft.svn.core.wc.SVNConflictChoice;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.util.SVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
/**
* 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 SVNWCClient16 extends SVNBasicDelegate {
private ISVNAddParameters myAddParameters;
private ISVNCommitHandler myCommitHandler;
private boolean myIsRevertMissingDirectories;
/**
* 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 SVNWCClient16(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 SVNWCClient16(ISVNRepositoryPool repositoryPool, ISVNOptions options) {
super(repositoryPool, options);
}
/**
* Sets custom add parameters to this client object.
*
* @param addParameters
* extra parameters for add operations
* @since 1.2
*/
public void setAddParameters(ISVNAddParameters addParameters) {
this.myAddParameters = 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.myCommitHandler;
}
/**
* 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) {
this.myCommitHandler = handler;
}
/**
* Returns the add parameters object used by this object. If no custom
* object was specified through a call to
* {@link #setAddParameters(ISVNAddParameters)} then
* {@link #DEFAULT_ADD_PARAMETERS} is returned.
*
* @return add parameters object
* @since 1.2
*/
protected ISVNAddParameters getAddParameters() {
return this.myAddParameters;
}
public void setRevertMissingDirectories(boolean revertMissing) {
myIsRevertMissingDirectories = revertMissing;
}
public boolean isRevertMissingDirectories() {
return myIsRevertMissingDirectories;
}
/**
* 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 <code>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 {
if (dst == null) {
return;
}
if (revision == null || !revision.isValid()) {
revision = SVNRevision.BASE;
} else if (revision == SVNRevision.COMMITTED) {
revision = SVNRevision.BASE;
}
if ((!pegRevision.isValid() || pegRevision == SVNRevision.BASE || pegRevision == SVNRevision.WORKING)
&& (!revision.isValid() || revision == SVNRevision.BASE || revision == SVNRevision.WORKING)) {
doGetLocalFileContents(path, dst, revision, expandKeywords);
} else {
SVNRepository repos = createRepository(null, path, null, pegRevision, revision, null);
checkCancelled();
long revNumber = getRevisionNumber(revision, repos, path);
SVNNodeKind kind = repos.checkPath("", revNumber);
if (kind == SVNNodeKind.DIR) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_IS_DIRECTORY, "URL ''{0}'' refers to a directory", repos.getLocation());
SVNErrorManager.error(err, SVNLogType.WC);
}
checkCancelled();
if (!expandKeywords) {
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(dst, this));
} else {
SVNProperties properties = new SVNProperties();
repos.getFile("", revNumber, properties, null);
checkCancelled();
String keywords = properties.getStringValue(SVNProperty.KEYWORDS);
String eol = properties.getStringValue(SVNProperty.EOL_STYLE);
String mimeType = properties.getStringValue(SVNProperty.MIME_TYPE);
String charset = SVNTranslator.getCharset(properties.getStringValue(SVNProperty.CHARSET), mimeType, path.getPath(), getOptions());
if (keywords != null || eol != null || charset != null) {
String cmtRev = properties.getStringValue(SVNProperty.COMMITTED_REVISION);
String cmtDate = properties.getStringValue(SVNProperty.COMMITTED_DATE);
String author = properties.getStringValue(SVNProperty.LAST_AUTHOR);
Map keywordsMap = SVNTranslator.computeKeywords(keywords, expandKeywords ? repos.getLocation().toString() : null, repos.getRepositoryRoot(true).toString(), author, cmtDate, cmtRev, getOptions());
OutputStream translatingStream = SVNTranslator.getTranslatingOutputStream(dst, charset, SVNTranslator.getEOL(eol, getOptions()), false, keywordsMap, expandKeywords);
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(translatingStream, getEventDispatcher()));
try {
translatingStream.flush();
} catch (IOExceptionWrapper ioew) {
throw ioew.getOriginalException();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage()), SVNLogType.WC);
}
} else {
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(dst, getEventDispatcher()));
}
}
try {
dst.flush();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage()), SVNLogType.WC);
}
}
}
/**
* 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 {
revision = revision == null || !revision.isValid() ? SVNRevision.HEAD : revision;
SVNRepository repos = createRepository(url, null, null, pegRevision, revision, null);
checkCancelled();
long revNumber = getRevisionNumber(revision, repos, null);
checkCancelled();
SVNNodeKind nodeKind = repos.checkPath("", revNumber);
checkCancelled();
if (nodeKind == SVNNodeKind.DIR) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_IS_DIRECTORY, "URL ''{0}'' refers to a directory", url);
SVNErrorManager.error(err, SVNLogType.WC);
}
checkCancelled();
if (!expandKeywords) {
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(dst, this));
} else {
SVNProperties properties = new SVNProperties();
repos.getFile("", revNumber, properties, null);
checkCancelled();
String mimeType = properties.getStringValue(SVNProperty.MIME_TYPE);
String charset = SVNTranslator.getCharset(properties.getStringValue(SVNProperty.CHARSET), mimeType, repos.getLocation().toString(), getOptions());
String keywords = properties.getStringValue(SVNProperty.KEYWORDS);
String eol = properties.getStringValue(SVNProperty.EOL_STYLE);
if (charset != null || keywords != null || eol != null) {
String cmtRev = properties.getStringValue(SVNProperty.COMMITTED_REVISION);
String cmtDate = properties.getStringValue(SVNProperty.COMMITTED_DATE);
String author = properties.getStringValue(SVNProperty.LAST_AUTHOR);
Map keywordsMap = SVNTranslator.computeKeywords(keywords, expandKeywords ? repos.getLocation().toString() : null, repos.getRepositoryRoot(true).toString(), author, cmtDate, cmtRev, getOptions());
OutputStream translatingStream = SVNTranslator.getTranslatingOutputStream(dst, charset, SVNTranslator.getEOL(eol, getOptions()), false, keywordsMap, expandKeywords);
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(translatingStream, getEventDispatcher()));
try {
translatingStream.flush();
} catch (IOExceptionWrapper ioew) {
throw ioew.getOriginalException();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage()), SVNLogType.WC);
}
} else {
repos.getFile("", revNumber, null, new SVNCancellableOutputStream(dst, getEventDispatcher()));
}
}
try {
dst.flush();
} catch (IOException e) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage()), SVNLogType.WC);
}
}
/**
* 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 {
doCleanup(path, false);
}
/**
* 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 {
SVNFileType fType = SVNFileType.getType(path);
if (fType == SVNFileType.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND, "''{0}'' does not exist", path);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (fType == SVNFileType.FILE) {
path = path.getParentFile();
} else if (fType == SVNFileType.SYMLINK) {
path = SVNFileUtil.resolveSymlink(path);
if (SVNFileType.getType(path) == SVNFileType.FILE) {
path = path.getParentFile();
}
}
SVNWCAccess wcAccess = createWCAccess();
try {
SVNAdminArea adminArea = wcAccess.open(path, true, true, 0);
adminArea.cleanup();
if (deleteWCProperties) {
SVNPropertiesManager.deleteWCProperties(adminArea, null, true);
}
} catch (SVNException e) {
if (e instanceof SVNCancelException) {
throw e;
} else if (!SVNAdminArea.isSafeCleanup()) {
throw e;
}
SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, "CLEANUP FAILED for " + path);
SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, e);
} finally {
wcAccess.close();
sleepForTimeStamp();
}
}
/**
* 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, ISVNPropertyHandler handler, Collection changeLists) throws SVNException {
depth = depth == null ? SVNDepth.UNKNOWN : depth;
int admLockLevel = SVNWCAccess.INFINITE_DEPTH;
if (depth == SVNDepth.EMPTY || depth == SVNDepth.FILES) {
admLockLevel = 0;
}
if (propValue != null && !SVNPropertiesManager.isValidPropertyName(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Bad property name ''{0}''", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNRevisionProperty.isRevisionProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Revision property ''{0}'' not allowed in this context", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNWCAccess wcAccess = createWCAccess();
try {
wcAccess.probeOpen(path, true, admLockLevel);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
if (SVNDepth.FILES.compareTo(depth) <= 0 && entry.isDirectory()) {
PropSetHandler entryHandler = new PropSetHandler(skipChecks, propName, propValue, handler, changeLists);
wcAccess.walkEntries(path, entryHandler, false, depth);
} else if (SVNWCAccess.matchesChangeList(changeLists, entry)) {
boolean modified = SVNPropertiesManager.setProperty(wcAccess, path, propName, propValue, skipChecks);
if (modified && handler != null) {
handler.handleProperty(path, new SVNPropertyData(propName, propValue, getOptions()));
}
}
} finally {
wcAccess.close();
}
}
/**
* 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 changeLists) throws SVNException {
depth = depth == null ? SVNDepth.UNKNOWN : depth;
int admLockLevel = SVNWCAccess.INFINITE_DEPTH;
if (depth == SVNDepth.EMPTY || depth == SVNDepth.FILES) {
admLockLevel = 0;
}
SVNWCAccess wcAccess = createWCAccess();
try {
wcAccess.probeOpen(path, true, admLockLevel);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
if (SVNDepth.FILES.compareTo(depth) <= 0 && entry.isDirectory()) {
PropSetHandlerExt entryHandler = new PropSetHandlerExt(skipChecks, propertyValueProvider, handler, changeLists);
wcAccess.walkEntries(path, entryHandler, false, depth);
} else if (SVNWCAccess.matchesChangeList(changeLists, entry)) {
SVNAdminArea adminArea = entry.getAdminArea();
setLocalProperties(path, entry, adminArea, skipChecks, propertyValueProvider, handler);
}
} finally {
wcAccess.close();
}
}
/**
* 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,
ISVNPropertyHandler handler) throws SVNException {
if (propValue != null && !SVNPropertiesManager.isValidPropertyName(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Bad property name ''{0}''", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNRevisionProperty.isRevisionProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Revision property ''{0}'' not allowed in this context", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
final SVNRepository repos = createRepository(url, null, null, true);
long revNumber = SVNRepository.INVALID_REVISION;
try {
revNumber = getRevisionNumber(baseRevision, repos, null);
} catch (SVNException svne) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Setting property on non-local target ''{0}'' needs a base revision", url);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.EOL_STYLE.equals(propName) || SVNProperty.KEYWORDS.equals(propName) || SVNProperty.CHARSET.equals(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Setting property ''{0}'' on non-local target ''{1}'' is not supported", new Object[] {
propName, url
});
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNNodeKind kind = repos.checkPath("", revNumber);
if (kind == SVNNodeKind.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' does not exist in revision {1}", new Object[] {
url.getPath(), new Long(revNumber)
});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (propValue != null && SVNProperty.isSVNProperty(propName)) {
final long baseRev = revNumber;
propValue = SVNPropertiesManager.validatePropertyValue(url, kind, propName, propValue, skipChecks, getOptions(), new ISVNFileContentFetcher() {
private SVNProperties myProperties = null;
public void fetchFileContent(OutputStream os) throws SVNException {
SVNProperties props = myProperties != null ? null : new SVNProperties();
repos.getFile("", baseRev, props, os);
if (props != null) {
myProperties = props;
}
}
public SVNPropertyValue getProperty(String propertyName) throws SVNException {
fetchFileProperties();
return myProperties.getSVNPropertyValue(propertyName);
}
private void fetchFileProperties() throws SVNException {
if (myProperties == null) {
myProperties = new SVNProperties();
repos.getFile("", baseRev, myProperties, null);
}
}
public boolean fileIsBinary() throws SVNException {
fetchFileProperties();
return Boolean.valueOf(SVNProperty.isBinaryMimeType(myProperties.getStringValue(SVNProperty.MIME_TYPE)));
}
});
}
Collection commitItems = new ArrayList(2);
SVNCommitItem commitItem = new SVNCommitItem(null, url, null, kind, SVNRevision.create(revNumber), SVNRevision.UNDEFINED, false, false, true, false, false, false);
commitItems.add(commitItem);
SVNCommitItem[] commitItemsArray = (SVNCommitItem[]) commitItems.toArray(new SVNCommitItem[commitItems.size()]);
commitMessage = getCommitHandler() != null ?
getCommitHandler().getCommitMessage(commitMessage, commitItemsArray) :
(commitMessage == null ? "" : commitMessage);
if (commitMessage == null) {
return SVNCommitInfo.NULL;
}
commitMessage = SVNCommitUtil.validateCommitMessage(commitMessage);
if (getCommitHandler() != null) {
revisionProperties = getCommitHandler().getRevisionProperties(commitMessage, commitItemsArray, revisionProperties);
}
SVNPropertiesManager.validateRevisionProperties(revisionProperties);
SVNCommitInfo commitInfo = null;
ISVNEditor commitEditor = repos.getCommitEditor(commitMessage, null, true, revisionProperties, null);
try {
commitEditor.openRoot(revNumber);
if (kind == SVNNodeKind.FILE) {
commitEditor.openFile("", revNumber);
commitEditor.changeFileProperty("", propName, propValue);
commitEditor.closeFile("", null);
} else {
commitEditor.changeDirProperty(propName, propValue);
}
commitEditor.closeDir();
commitInfo = commitEditor.closeEdit();
} catch (SVNException svne) {
commitEditor.abortEdit();
throw svne;
}
if (handler != null) {
handler.handleProperty(url, new SVNPropertyData(propName, propValue, getOptions()));
}
return commitInfo;
}
/**
* 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 {
if (propValue != null && !SVNPropertiesManager.isValidPropertyName(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Bad property name ''{0}''", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNURL url = getURL(path);
doSetRevisionProperty(url, revision, propName, propValue, force, handler);
}
/**
* 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 {
if (propValue != null && !SVNPropertiesManager.isValidPropertyName(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Bad property name ''{0}''", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!force && SVNRevisionProperty.AUTHOR.equals(propName) && propValue != null && propValue.isString() && propValue.getString().indexOf('\n') >= 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_REVISION_AUTHOR_CONTAINS_NEWLINE, "Value will not be set unless forced");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNRepository repos = createRepository(url, null, null, SVNRevision.UNDEFINED, revision, null);
long revNumber = getRevisionNumber(revision, repos, null);
repos.setRevisionPropertyValue(revNumber, propName, propValue);
if (handler != null) {
handler.handleProperty(revNumber, new SVNPropertyData(propName, propValue, getOptions()));
}
}
/**
* 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_PREFIXsvn
* :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, String propName, SVNRevision pegRevision, SVNRevision revision) throws SVNException {
final SVNPropertyData[] data = new SVNPropertyData[1];
doGetProperty(path, propName, pegRevision, revision, SVNDepth.EMPTY, new ISVNPropertyHandler() {
public void handleProperty(File file, SVNPropertyData property) {
if (data[0] == null && path.equals(file)) {
data[0] = property;
}
}
public void handleProperty(SVNURL url, SVNPropertyData property) {
}
public void handleProperty(long revision, SVNPropertyData property) {
}
}, null);
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_PREFIXsvn
* :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, String propName, SVNRevision pegRevision, SVNRevision revision) throws SVNException {
final SVNPropertyData[] data = new SVNPropertyData[1];
doGetProperty(url, propName, pegRevision, revision, SVNDepth.EMPTY, new ISVNPropertyHandler() {
public void handleProperty(File file, SVNPropertyData property) {
}
public void handleProperty(long revision, SVNPropertyData property) {
}
public void handleProperty(SVNURL location, SVNPropertyData property) throws SVNException {
if (data[0] == null && url.toString().equals(location.toString())) {
data[0] = property;
}
}
});
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_PREFIXsvn
* :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, String propName, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, ISVNPropertyHandler handler, Collection changeLists) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (depth == null || depth == SVNDepth.UNKNOWN) {
depth = SVNDepth.EMPTY;
}
if ((revision != SVNRevision.WORKING && revision != SVNRevision.BASE && revision != SVNRevision.COMMITTED && revision != SVNRevision.UNDEFINED)
|| (pegRevision != SVNRevision.WORKING && pegRevision != SVNRevision.BASE && pegRevision != SVNRevision.COMMITTED && pegRevision != SVNRevision.UNDEFINED)) {
long[] revNum = {
SVNRepository.INVALID_REVISION
};
SVNRepository repository = createRepository(null, path, null, pegRevision, revision, revNum);
revision = SVNRevision.create(revNum[0]);
doGetRemoteProperty(repository.getLocation(), "", repository, propName, revision, depth, handler);
} else {
SVNWCAccess wcAccess = createWCAccess();
try {
int admDepth = getLevelsToLockFromDepth(depth);
SVNAdminArea area = wcAccess.probeOpen(path, false, admDepth);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
boolean base = revision == SVNRevision.BASE || revision == SVNRevision.COMMITTED;
doGetLocalProperty(entry, area, propName, base, handler, depth, changeLists);
} finally {
wcAccess.close();
}
}
}
/**
* 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_PREFIXsvn
* :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, String propName, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, ISVNPropertyHandler handler) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
long[] pegRev = new long[] {
-1
};
SVNRepository repos = createRepository(url, null, null, pegRevision, revision, pegRev);
revision = pegRev[0] < 0 ? revision : SVNRevision.create(pegRev[0]);
doGetRemoteProperty(url, "", repos, propName, revision, depth, handler);
}
/**
* 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_PREFIXsvn
* :wc:} prefix
* </ul>
* @see #doGetRevisionProperty(SVNURL,String,SVNRevision,ISVNPropertyHandler)
*/
public void doGetRevisionProperty(File path, String propName, SVNRevision revision, ISVNPropertyHandler handler) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!revision.isValid()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Valid revision have to be specified to fetch revision property");
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
SVNRepository repository = createRepository(null, path, null, SVNRevision.UNDEFINED, revision, null);
long revisionNumber = getRevisionNumber(revision, repository, path);
doGetRevisionProperty(repository, propName, revisionNumber, handler);
}
/**
* 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_PREFIXsvn
* :wc:} prefix
* </ul>
* @see #doGetRevisionProperty(File,String,SVNRevision,ISVNPropertyHandler)
*/
public long doGetRevisionProperty(SVNURL url, String propName, SVNRevision revision, ISVNPropertyHandler handler) throws SVNException {
if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!revision.isValid()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Valid revision have to be specified to fetch revision property");
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
SVNRepository repos = createRepository(url, null, null, true);
long revNumber = getRevisionNumber(revision, repos, null);
doGetRevisionProperty(repos, propName, revNumber, handler);
return revNumber;
}
/**
* 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 addition 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 {
SVNWCAccess wcAccess = createWCAccess();
path = path.getAbsoluteFile();
try {
if (!force && deleteFiles) {
SVNWCManager.canDelete(path, getOptions(), this);
}
SVNAdminArea root = wcAccess.open(path.getParentFile(), true, 0);
if (!dryRun) {
SVNWCManager.delete(wcAccess, root, path, deleteFiles, true);
}
} finally {
wcAccess.close();
}
}
/**
* 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 {
SVNDepth depth = SVNDepth.getInfinityOrEmptyDepth(recursive);
doAdd(path, force, mkdir, climbUnversionedParents, depth, 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 {
SVNDepth depth = SVNDepth.getInfinityOrEmptyDepth(recursive);
doAdd(path, force, mkdir, climbUnversionedParents, depth, 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 {
doAdd(path, 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 {
setEventPathPrefix("");
try {
if (paths != null) {
for (int i = 0; i < paths.length; i++) {
doAdd(paths[i], force, mkdir, climbUnversionedParents, depth, depthIsSticky, includeIgnored, makeParents);
}
}
} finally {
setEventPathPrefix(null);
}
}
/**
* 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 {
depth = depth == null ? SVNDepth.UNKNOWN : depth;
path = path.getAbsoluteFile();
if (!mkdir && makeParents && path.getParentFile() != null) {
SVNWCAccess wcAccess = createWCAccess();
try {
addParentDirectories(wcAccess, path.getParentFile());
} finally {
wcAccess.close();
}
}
SVNFileType kind = SVNFileType.getType(path);
if (force && mkdir && kind == SVNFileType.DIRECTORY) {
doAdd(path, force, false, true, SVNDepth.EMPTY, depthIsSticky, true, makeParents);
return;
} else if (mkdir) {
File parent = path;
File firstCreated = path;
while (parent != null && SVNFileType.getType(parent) == SVNFileType.NONE) {
if (!parent.equals(path) && !makeParents) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create directoy ''{0}'' with non-existent parents", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
firstCreated = parent;
parent = parent.getParentFile();
}
boolean created = path.mkdirs();
if (!created) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create new directory ''{0}''", path);
while (parent == null ? path != null : !path.equals(parent)) {
SVNFileUtil.deleteAll(path, true);
path = path.getParentFile();
}
SVNErrorManager.error(err, SVNLogType.WC);
}
try {
doAdd(firstCreated, false, false, climbUnversionedParents, depth, depthIsSticky, true, makeParents);
} catch (SVNException e) {
SVNFileUtil.deleteAll(firstCreated, true);
throw e;
}
return;
}
SVNWCAccess wcAccess = createWCAccess();
try {
SVNAdminArea dir = null;
SVNFileType fileType = SVNFileType.getType(path);
if (fileType == SVNFileType.DIRECTORY) {
dir = wcAccess.open(SVNWCUtil.isVersionedDirectory(path.getParentFile()) ? path.getParentFile() : path, true, 0);
} else {
dir = wcAccess.open(path.getParentFile(), true, 0);
}
if (fileType == SVNFileType.DIRECTORY && depth.compareTo(SVNDepth.FILES) >= 0) {
File wcRoot = SVNWCUtil.getWorkingCopyRoot(dir.getRoot(), true);
addDirectory(wcRoot, path, dir, force, includeIgnored, depth, depthIsSticky);
} else if (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK) {
addFile(path, fileType, dir);
} else {
SVNWCManager.add(path, dir, null, SVNRevision.UNDEFINED, depthIsSticky ? depth : null);
}
} catch (SVNException e) {
if (!(force && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS)) {
throw e;
}
} finally {
wcAccess.close();
}
}
/**
* 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 {
SVNWCAccess wcAccess = createWCAccess();
path = path.getAbsoluteFile();
try {
SVNAdminAreaInfo areaInfo = wcAccess.openAnchor(path, true, SVNWCAccess.INFINITE_DEPTH);
SVNAdminArea anchor = areaInfo.getAnchor();
if (path.equals(anchor.getRoot().getAbsoluteFile())) {
SVNWCManager.markTree(anchor, SVNProperty.SCHEDULE_REPLACE, false, false, SVNWCManager.SCHEDULE);
} else {
SVNEntry entry = anchor.getEntry(path.getName(), false);
SVNWCManager.markEntry(anchor, entry, SVNProperty.SCHEDULE_REPLACE, false, false, SVNWCManager.SCHEDULE);
}
anchor.saveEntries(false);
} finally {
wcAccess.close();
}
}
/**
* 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.getInfinityOrEmptyDepth(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, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, 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 changeLists) throws SVNException {
boolean reverted = false;
try {
for (int i = 0; i < paths.length; i++) {
File path = paths[i];
path = path.getAbsoluteFile();
SVNWCAccess wcAccess = createWCAccess();
try {
int admLockLevel = getLevelsToLockFromDepth(depth);
SVNAdminAreaInfo info = wcAccess.openAnchor(path, true, admLockLevel);
SVNEntry entry = wcAccess.getEntry(path, false);
if (entry != null && entry.isDirectory() && entry.isScheduledForAddition()) {
if (depth != SVNDepth.INFINITY) {
depth = SVNDepth.INFINITY;
wcAccess.close();
info = wcAccess.openAnchor(path, true, SVNWCAccess.INFINITE_DEPTH);
}
}
boolean useCommitTimes = getOptions().isUseCommitTimes();
reverted |= doRevert(path, info.getAnchor(), depth, useCommitTimes, changeLists);
} catch (SVNException e) {
reverted |= true;
SVNErrorCode code = e.getErrorMessage().getErrorCode();
if (code == SVNErrorCode.ENTRY_NOT_FOUND || code == SVNErrorCode.UNVERSIONED_RESOURCE) {
SVNEvent event = SVNEventFactory.createSVNEvent(path, SVNNodeKind.UNKNOWN, null, SVNRepository.INVALID_REVISION, SVNEventAction.SKIP, SVNEventAction.REVERT, null, null);
dispatchEvent(event);
continue;
}
throw e;
} finally {
wcAccess.close();
}
}
} finally {
if (reverted) {
sleepForTimeStamp();
}
}
}
/**
* 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.MERGED);
}
/**
* 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 {
doResolve(path, depth, true, true, conflictChoice);
}
/**
* 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 {
doResolve(path, depth, resolveContents, resolveProperties, true, conflictChoice);
}
/**
* 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 {
final SVNConflictChoice choice = conflictChoice == null ? SVNConflictChoice.MERGED : conflictChoice;
path = path.getAbsoluteFile();
final SVNWCAccess wcAccess = createWCAccess();
int admLockLevel = SVNWCAccess.INFINITE_DEPTH;
if (depth == SVNDepth.EMPTY || depth == SVNDepth.FILES) {
admLockLevel = 0;
}
try {
wcAccess.probeOpen(path, true, admLockLevel);
if (!wcAccess.isWCRoot(path)) {
wcAccess.close();
if (admLockLevel >= 0) {
admLockLevel++;
}
wcAccess.probeOpen(path.getParentFile(), true, admLockLevel);
}
ISVNEntryHandler resolveEntryHandler = new ISVNEntryHandler() {
public void handleEntry(File path, SVNEntry entry) throws SVNException {
if (entry != null && entry.isDirectory() && !"".equals(entry.getName())) {
return;
}
SVNNodeKind kind = SVNNodeKind.UNKNOWN;
long revision = -1;
boolean wcRoot = false;
boolean resolved = false;
if (entry != null && entry.isDirectory()) {
wcRoot = wcAccess.isWCRoot(path);
}
if (resolveTree && !wcRoot) {
File parentDir = path.getParentFile();
SVNAdminArea parentArea = wcAccess.probeRetrieve(parentDir);
SVNTreeConflictDescription tc = parentArea.getTreeConflict(path.getName());
if (tc != null) {
if (choice != SVNConflictChoice.MERGED) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CONFLICT_RESOLVER_FAILURE,
"Tree conflicts can only be resolved to ''working'' state; ''{0}'' not resolved", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
parentArea.deleteTreeConflict(path.getName());
kind = tc.getNodeKind();
resolved = true;
}
}
if (entry != null && (resolveContents || resolveProperties)) {
kind = entry.getKind();
revision = entry.getRevision();
File conflictDir = entry.isDirectory() ? path : path.getParentFile();
SVNAdminArea conflictArea = wcAccess.retrieve(conflictDir);
resolved |= conflictArea.markResolved(entry.getName(), resolveContents, resolveProperties, choice);
}
if (resolved) {
SVNEvent event = SVNEventFactory.createSVNEvent(path, kind, null, revision, SVNEventAction.RESOLVED, null, null, null);
dispatchEvent(event);
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
};
if (depth == SVNDepth.EMPTY) {
SVNEntry entry = wcAccess.getEntry(path, false);
if (entry != null) {
resolveEntryHandler.handleEntry(path, entry);
} else {
SVNTreeConflictDescription tc = wcAccess.getTreeConflict(path);
if (tc != null) {
resolveEntryHandler.handleEntry(path, null);
} else {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
} else {
wcAccess.walkEntries(path, resolveEntryHandler, false, true, depth);
}
} finally {
wcAccess.close();
}
}
/**
* 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 {
if (paths == null || paths.length == 0) {
return;
}
final Map entriesMap = new SVNHashMap();
Map pathsRevisionsMap = new SVNHashMap();
final SVNWCAccess wcAccess = createWCAccess();
try {
final SVNURL topURL = collectLockInfo(wcAccess, paths, entriesMap, pathsRevisionsMap, true, stealLock);
SVNRepository repository = createRepository(topURL, paths[0], wcAccess, true);
final SVNURL rootURL = repository.getRepositoryRoot(true);
repository.lock(pathsRevisionsMap, lockMessage, stealLock, new ISVNLockHandler() {
public void handleLock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
SVNURL fullURL = rootURL.appendPath(path, false);
LockInfo lockInfo = (LockInfo) entriesMap.get(fullURL);
SVNAdminArea dir = wcAccess.probeRetrieve(lockInfo.myFile);
if (error == null) {
SVNEntry entry = wcAccess.getVersionedEntry(lockInfo.myFile, false);
entry.setLockToken(lock.getID());
entry.setLockComment(lock.getComment());
entry.setLockOwner(lock.getOwner());
entry.setLockCreationDate(SVNDate.formatDate(lock.getCreationDate()));
SVNVersionedProperties props = dir.getProperties(entry.getName());
if (props.getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
SVNFileUtil.setReadonly(dir.getFile(entry.getName()), false);
}
SVNFileUtil.setExecutable(dir.getFile(entry.getName()), props.getPropertyValue(SVNProperty.EXECUTABLE) != null);
dir.saveEntries(false);
handleEvent(SVNEventFactory.createLockEvent(dir.getFile(entry.getName()), SVNEventAction.LOCKED, lock, null), ISVNEventHandler.UNKNOWN);
} else {
handleEvent(SVNEventFactory.createLockEvent(dir.getFile(lockInfo.myFile.getName()), SVNEventAction.LOCK_FAILED, lock, error), ISVNEventHandler.UNKNOWN);
}
}
public void handleUnlock(String path, SVNLock lock, SVNErrorMessage error) {
}
});
} finally {
wcAccess.close();
}
}
/**
* 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 {
Collection paths = new SVNHashSet();
SVNURL topURL = SVNURLUtil.condenceURLs(urls, paths, false);
if (paths.isEmpty()) {
paths.add("");
}
Map pathsToRevisions = new SVNHashMap();
for (Iterator p = paths.iterator(); p.hasNext();) {
String path = (String) p.next();
path = SVNEncodingUtil.uriDecode(path);
pathsToRevisions.put(path, null);
}
checkCancelled();
SVNRepository repository = createRepository(topURL, null, null, true);
repository.lock(pathsToRevisions, lockMessage, stealLock, new ISVNLockHandler() {
public void handleLock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
if (error != null) {
handleEvent(SVNEventFactory.createLockEvent(new File(path), SVNEventAction.LOCK_FAILED, lock, error), ISVNEventHandler.UNKNOWN);
} else {
handleEvent(SVNEventFactory.createLockEvent(new File(path), SVNEventAction.LOCKED, lock, null), ISVNEventHandler.UNKNOWN);
}
}
public void handleUnlock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
}
});
}
/**
* 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 {
if (paths == null || paths.length == 0) {
return;
}
final Map entriesMap = new SVNHashMap();
Map pathsTokensMap = new SVNHashMap();
final SVNWCAccess wcAccess = createWCAccess();
try {
final SVNURL topURL = collectLockInfo(wcAccess, paths, entriesMap, pathsTokensMap, false, breakLock);
checkCancelled();
SVNRepository repository = createRepository(topURL, paths[0], wcAccess, true);
final SVNURL rootURL = repository.getRepositoryRoot(true);
repository.unlock(pathsTokensMap, breakLock, new ISVNLockHandler() {
public void handleLock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
}
public void handleUnlock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
SVNURL fullURL = rootURL.appendPath(path, false);
LockInfo lockInfo = (LockInfo) entriesMap.get(fullURL);
SVNEventAction action = null;
SVNAdminArea dir = wcAccess.probeRetrieve(lockInfo.myFile);
if (error == null || (error != null && error.getErrorCode() != SVNErrorCode.FS_LOCK_OWNER_MISMATCH)) {
SVNEntry entry = wcAccess.getVersionedEntry(lockInfo.myFile, false);
entry.setLockToken(null);
entry.setLockComment(null);
entry.setLockOwner(null);
entry.setLockCreationDate(null);
SVNVersionedProperties props = dir.getProperties(entry.getName());
if (props.getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
SVNFileUtil.setReadonly(dir.getFile(entry.getName()), true);
}
dir.saveEntries(false);
action = SVNEventAction.UNLOCKED;
}
if (error != null) {
action = SVNEventAction.UNLOCK_FAILED;
}
if (action != null) {
handleEvent(SVNEventFactory.createLockEvent(dir.getFile(lockInfo.myFile.getName()), action, lock, error), ISVNEventHandler.UNKNOWN);
}
}
});
} finally {
wcAccess.close();
}
}
/**
* 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 {
Collection paths = new SVNHashSet();
SVNURL topURL = SVNURLUtil.condenceURLs(urls, paths, false);
if (paths.isEmpty()) {
paths.add("");
}
Map pathsToTokens = new SVNHashMap();
for (Iterator p = paths.iterator(); p.hasNext();) {
String path = (String) p.next();
path = SVNEncodingUtil.uriDecode(path);
pathsToTokens.put(path, null);
}
checkCancelled();
SVNRepository repository = createRepository(topURL, null, null, true);
if (!breakLock) {
pathsToTokens = fetchLockTokens(repository, pathsToTokens);
}
repository.unlock(pathsToTokens, breakLock, new ISVNLockHandler() {
public void handleLock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
}
public void handleUnlock(String path, SVNLock lock, SVNErrorMessage error) throws SVNException {
if (error != null) {
handleEvent(SVNEventFactory.createLockEvent(new File(path), SVNEventAction.UNLOCK_FAILED, null, error), ISVNEventHandler.UNKNOWN);
} else {
handleEvent(SVNEventFactory.createLockEvent(new File(path), SVNEventAction.UNLOCKED, null, null), ISVNEventHandler.UNKNOWN);
}
}
});
}
/**
* 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 changeLists, ISVNInfoHandler handler) throws SVNException {
if (handler == null) {
return;
}
boolean local = (revision == null || !revision.isValid() || revision.isLocal()) && (pegRevision == null || !pegRevision.isValid() || pegRevision.isLocal());
if (!local) {
SVNWCAccess wcAccess = createWCAccess();
SVNRevision wcRevision = null;
SVNURL url = null;
try {
wcAccess.probeOpen(path, false, 0);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
url = entry.getSVNURL();
if (url == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' has no URL", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
wcRevision = SVNRevision.create(entry.getRevision());
} finally {
wcAccess.close();
}
doInfo(url, pegRevision == null || !pegRevision.isValid() || pegRevision.isLocal() ? wcRevision : pegRevision, revision, depth, handler);
return;
}
Collection changelistsSet = null;
if (changeLists != null) {
changelistsSet = new SVNHashSet();
for (Iterator changeListsIter = changeLists.iterator(); changeListsIter.hasNext();) {
String changeList = (String) changeListsIter.next();
changelistsSet.add(changeList);
}
}
crawlEntries(path, depth, changelistsSet, handler);
}
/**
* 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, ISVNInfoHandler handler) throws SVNException {
depth = depth == null ? SVNDepth.UNKNOWN : depth;
long[] revNum = {
SVNRepository.INVALID_REVISION
};
SVNRepository repos = createRepository(url, null, null, pegRevision, revision, revNum);
url = repos.getLocation();
SVNDirEntry rootEntry = null;
SVNURL reposRoot = repos.getRepositoryRoot(true);
String reposUUID = repos.getRepositoryUUID(true);
String baseName = SVNPathUtil.tail(url.getPath());
try {
rootEntry = repos.info("", revNum[0]);
} catch (SVNException e) {
if (e.getErrorMessage() != null && e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED) {
if (url.equals(reposRoot)) {
if (depth.compareTo(SVNDepth.EMPTY) > 0) {
SVNLock[] locks = null;
if (pegRevision == SVNRevision.HEAD) {
try {
locks = repos.getLocks("");
} catch (SVNException svne) {
SVNErrorCode code = svne.getErrorMessage().getErrorCode();
if (code == SVNErrorCode.RA_NOT_IMPLEMENTED || code == SVNErrorCode.UNSUPPORTED_FEATURE) {
locks = new SVNLock[0];
} else {
throw svne;
}
}
} else {
locks = new SVNLock[0];
}
locks = locks == null ? new SVNLock[0] : locks;
Map locksMap = new SVNHashMap();
for (int i = 0; i < locks.length; i++) {
SVNLock lock = locks[i];
locksMap.put(lock.getPath(), lock);
}
pushDirInfo(repos, SVNRevision.create(revNum[0]), "", repos.getRepositoryRoot(true), reposUUID, url, locksMap, depth, handler);
return;
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Server does not support retrieving information about the repository root");
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNNodeKind urlKind = repos.checkPath("", revNum[0]);
if (urlKind == SVNNodeKind.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_ILLEGAL_URL, "URL ''{0}'' non-existent in revision {1}", new Object[] {
url, new Long(revNum[0])
});
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNRepository parentRepos = createRepository(url.removePathTail(), null, null, false);
Collection dirEntries = parentRepos.getDir("", revNum[0], null, SVNDirEntry.DIRENT_KIND | SVNDirEntry.DIRENT_CREATED_REVISION | SVNDirEntry.DIRENT_TIME
| SVNDirEntry.DIRENT_LAST_AUTHOR, (Collection) null);
for (Iterator ents = dirEntries.iterator(); ents.hasNext();) {
SVNDirEntry dirEntry = (SVNDirEntry) ents.next();
if (baseName.equals(dirEntry.getName())) {
rootEntry = dirEntry;
break;
}
}
if (rootEntry == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_ILLEGAL_URL, "URL ''{0}'' non-existent in revision {1}", new Object[] {
url, new Long(revNum[0])
});
SVNErrorManager.error(err, SVNLogType.WC);
}
} else {
throw e;
}
}
if (rootEntry == null || rootEntry.getKind() == SVNNodeKind.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RA_ILLEGAL_URL, "URL ''{0}'' non-existent in revision ''{1}''", new Object[] {
url, new Long(revNum[0])
});
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNLock lock = null;
if (rootEntry.getKind() == SVNNodeKind.FILE) {
try {
SVNRepositoryLocation[] locations = getLocations(url, null, null, SVNRevision.create(revNum[0]), SVNRevision.HEAD, SVNRevision.UNDEFINED);
if (locations != null && locations.length > 0) {
SVNURL headURL = locations[0].getURL();
if (headURL.equals(url)) {
try {
lock = repos.getLock("");
} catch (SVNException e) {
if (!(e.getErrorMessage() != null && e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED)) {
throw e;
}
}
}
}
} catch (SVNException e) {
SVNErrorCode code = e.getErrorMessage().getErrorCode();
if (code != SVNErrorCode.FS_NOT_FOUND && code != SVNErrorCode.CLIENT_UNRELATED_RESOURCES) {
throw e;
}
}
}
SVNInfo info = SVNInfo.createInfo(baseName, reposRoot, reposUUID, url, SVNRevision.create(revNum[0]), rootEntry, lock);
handler.handleInfo(info);
if (depth.compareTo(SVNDepth.EMPTY) > 0 && rootEntry.getKind() == SVNNodeKind.DIR) {
SVNLock[] locks = null;
if (pegRevision == SVNRevision.HEAD) {
try {
locks = repos.getLocks("");
} catch (SVNException svne) {
SVNErrorCode code = svne.getErrorMessage().getErrorCode();
if (code == SVNErrorCode.RA_NOT_IMPLEMENTED || code == SVNErrorCode.UNSUPPORTED_FEATURE) {
locks = new SVNLock[0];
} else {
throw svne;
}
}
} else {
locks = new SVNLock[0];
}
locks = locks == null ? new SVNLock[0] : locks;
Map locksMap = new SVNHashMap();
for (int i = 0; i < locks.length; i++) {
lock = locks[i];
locksMap.put(lock.getPath(), lock);
}
pushDirInfo(repos, SVNRevision.create(revNum[0]), "", repos.getRepositoryRoot(true), reposUUID, url, locksMap, depth, handler);
}
}
/**
* 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 {
SVNWCAccess wcAccess = createWCAccess();
try {
wcAccess.open(path, false, 0);
} catch (SVNException e) {
SVNFileType pathType = SVNFileType.getType(path);
if (pathType == SVNFileType.DIRECTORY) {
return "exported";
} else if (pathType == SVNFileType.NONE) {
throw e;
}
return "'" + path + "' is not versioned and not exported";
} finally {
wcAccess.close();
}
SVNStatusClient16 statusClient = new SVNStatusClient16((ISVNAuthenticationManager) null, getOptions());
statusClient.setIgnoreExternals(true);
final long[] maxRevision = new long[1];
final long[] minRevision = new long[] {
-1
};
final boolean[] switched = new boolean[3];
final String[] wcURL = new String[1];
statusClient.doStatus(path, SVNRevision.WORKING, SVNDepth.INFINITY, false, true, false, false, new ISVNStatusHandler() {
public void handleStatus(SVNStatus status) {
if (status.getEntryProperties() == null || status.getEntryProperties().isEmpty()) {
return;
}
if (status.getContentsStatus() != SVNStatusType.STATUS_ADDED && !status.isFileExternal()) {
SVNRevision revision = committed ? status.getCommittedRevision() : status.getRevision();
if (revision != null) {
if (minRevision[0] < 0 || minRevision[0] > revision.getNumber()) {
minRevision[0] = revision.getNumber();
}
maxRevision[0] = Math.max(maxRevision[0], revision.getNumber());
}
}
switched[0] |= status.isSwitched();
switched[1] |= status.getContentsStatus() != SVNStatusType.STATUS_NORMAL;
switched[1] |= status.getPropertiesStatus() != SVNStatusType.STATUS_NORMAL && status.getPropertiesStatus() != SVNStatusType.STATUS_NONE;
switched[2] |= status.getEntry() != null && status.getEntry().getDepth() != SVNDepth.INFINITY;
if (wcURL[0] == null && status.getFile() != null && status.getFile().equals(path) && status.getURL() != null) {
wcURL[0] = status.getURL().toString();
}
}
}, null);
if (!switched[0] && trailURL != null) {
if (wcURL[0] == null) {
switched[0] = true;
} else {
switched[0] = !wcURL[0].endsWith(trailURL);
}
}
StringBuffer id = new StringBuffer();
id.append(minRevision[0]);
if (minRevision[0] != maxRevision[0]) {
id.append(":").append(maxRevision[0]);
}
if (switched[1]) {
id.append("M");
}
if (switched[0]) {
id.append("S");
}
if (switched[2]) {
id.append("P");
}
return id.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[] result = new SVNInfo[1];
doInfo(path, SVNRevision.UNDEFINED, revision, SVNDepth.EMPTY, null, new ISVNInfoHandler() {
public void handleInfo(SVNInfo info) {
if (result[0] == null) {
result[0] = info;
}
}
});
return result[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[] result = new SVNInfo[1];
doInfo(url, pegRevision, revision, SVNDepth.EMPTY, new ISVNInfoHandler() {
public void handleInfo(SVNInfo info) {
if (result[0] == null) {
result[0] = info;
}
}
});
return result[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 {
SVNWCAccess wcAccess = SVNWCAccess.newInstance(this);
try {
SVNAdminArea dir = wcAccess.open(directory, true, true, -1);
if (dir != null) {
SVNPropertiesManager.deleteWCProperties(dir, null, true);
}
} finally {
wcAccess.close();
}
}
/**
* 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.
*
* @param directory
* working copy directory
* @param format
* format to set, supported formats are: 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 {
SVNAdminAreaInfo info = null;
SVNWCAccess wcAccess = SVNWCAccess.newInstance(this);
try {
info = wcAccess.openAnchor(directory, false, -1);
setWCFormat(info, info.getTarget(), format);
} finally {
wcAccess.close();
}
if (!isIgnoreExternals() && info != null) {
Collection processedDirs = new SVNHashSet();
Map externals = info.getOldExternals();
for (Iterator paths = externals.keySet().iterator(); paths.hasNext();) {
String path = (String) paths.next();
String value = (String) externals.get(path);
if (value == null) {
continue;
}
SVNExternal[] externalDefs = SVNExternal.parseExternals("", value);
for (int i = 0; i < externalDefs.length; i++) {
String externalPath = externalDefs[i].getPath();
File externalDir = new File(info.getAnchor().getRoot(), SVNPathUtil.append(path, externalPath));
if (processedDirs.add(externalDir)) {
try {
wcAccess.open(externalDir, false, 0);
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
continue;
}
throw svne;
} finally {
wcAccess.close();
}
try {
doSetWCFormat(externalDir, format);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
continue;
}
throw e;
}
}
}
}
externals = info.getNewExternals();
for (Iterator paths = externals.keySet().iterator(); paths.hasNext();) {
String path = (String) paths.next();
String value = (String) externals.get(path);
if (value == null) {
continue;
}
SVNExternal[] externalDefs = SVNExternal.parseExternals("", value);
for (int i = 0; i < externalDefs.length; i++) {
String externalPath = externalDefs[i].getPath();
File externalDir = new File(info.getAnchor().getRoot(), SVNPathUtil.append(path, externalPath));
if (processedDirs.add(externalDir)) {
try {
wcAccess.open(externalDir, false, 0);
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
continue;
}
throw svne;
} finally {
wcAccess.close();
}
try {
doSetWCFormat(externalDir, format);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
continue;
}
throw e;
}
}
}
}
}
}
/**
* 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_PREFIXsvn
* :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.getInfinityOrEmptyDepth(recursive), handler, null);
}
private SVNAdminArea addParentDirectories(SVNWCAccess wcAccess, File path) throws SVNException {
try {
return wcAccess.open(path, true, 0);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
if (path.getParentFile() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_NO_VERSIONED_PARENT);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNFileUtil.getAdminDirectoryName().equals(path.getName())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.RESERVED_FILENAME_SPECIFIED, "''{0}'' ends in a reserved name", path);
SVNErrorManager.error(err, SVNLogType.WC);
} else {
File parentPath = path.getParentFile();
SVNAdminArea parentDir = addParentDirectories(wcAccess, parentPath);
SVNWCManager.add(path, parentDir, null, SVNRevision.UNDEFINED, SVNDepth.INFINITY);
return wcAccess.getAdminArea(path);
}
}
throw e;
}
}
private void doGetRevisionProperty(SVNRepository repos, String propName, long revNumber, ISVNPropertyHandler handler) throws SVNException {
if (propName != null) {
SVNPropertyValue value = repos.getRevisionPropertyValue(revNumber, propName);
if (value != null) {
handler.handleProperty(revNumber, new SVNPropertyData(propName, value, getOptions()));
}
} else {
SVNProperties props = new SVNProperties();
repos.getRevisionProperties(revNumber, props);
for (Iterator names = props.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
SVNPropertyValue value = props.getSVNPropertyValue(name);
handler.handleProperty(revNumber, new SVNPropertyData(name, value, getOptions()));
}
}
}
private void addDirectory(File wcRoot, File path, SVNAdminArea parentDir, boolean force, boolean noIgnore, SVNDepth depth, boolean setDepth) throws SVNException {
checkCancelled();
try {
SVNWCManager.add(path, parentDir, null, SVNRevision.UNDEFINED, setDepth ? SVNDepth.INFINITY : null);
} catch (SVNException e) {
if (!(force && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS)) {
throw e;
}
}
SVNWCAccess access = parentDir.getWCAccess();
SVNAdminArea dir = access.retrieve(path);
Collection ignores = Collections.EMPTY_SET;
if (!noIgnore) {
ignores = SVNStatusEditor.getIgnorePatterns(dir, SVNStatusEditor.getGlobalIgnores(getOptions()));
}
String relativePath = SVNPathUtil.getRelativePath(wcRoot.getAbsolutePath().replace(File.separatorChar, '/'), dir.getRoot().getAbsolutePath().replace(File.separatorChar, '/'));
relativePath = relativePath != null ? "/" + relativePath : null;
File[] children = SVNFileListUtil.listFiles(dir.getRoot());
for (int i = 0; children != null && i < children.length; i++) {
checkCancelled();
if (SVNFileUtil.getAdminDirectoryName().equals(children[i].getName())) {
continue;
}
if (!noIgnore) {
String rootRelativePath = relativePath != null ? SVNPathUtil.append(relativePath, children[i].getName()) : null;
if (SVNStatusEditor.isIgnored(ignores, children[i], rootRelativePath)) {
continue;
}
}
SVNFileType childType = SVNFileType.getType(children[i]);
if (childType == SVNFileType.DIRECTORY && depth.compareTo(SVNDepth.IMMEDIATES) >= 0) {
SVNDepth depthBelowHere = depth;
if (depth == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
addDirectory(wcRoot, children[i], dir, force, noIgnore, depthBelowHere, setDepth);
} else if (childType != SVNFileType.UNKNOWN && childType != SVNFileType.DIRECTORY && depth.compareTo(SVNDepth.FILES) >= 0) {
try {
addFile(children[i], childType, dir);
} catch (SVNException e) {
if (force && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS) {
continue;
}
throw e;
}
}
}
}
private void addFile(File path, SVNFileType type, SVNAdminArea dir) throws SVNException {
ISVNEventHandler handler = dir.getWCAccess().getEventHandler();
dir.getWCAccess().setEventHandler(null);
SVNWCManager.add(path, dir, null, SVNRevision.UNDEFINED, null);
dir.getWCAccess().setEventHandler(handler);
String mimeType = null;
if (type == SVNFileType.SYMLINK) {
SVNPropertiesManager.setProperty(dir.getWCAccess(), path, SVNProperty.SPECIAL, SVNProperty.getValueOfBooleanProperty(SVNProperty.SPECIAL), false);
} else {
Map props = SVNPropertiesManager.computeAutoProperties(getOptions(), path, null);
for (Iterator names = props.keySet().iterator(); names.hasNext();) {
String propName = (String) names.next();
String propValue = (String) props.get(propName);
try {
SVNPropertiesManager.setProperty(dir.getWCAccess(), path, propName, SVNPropertyValue.create(propValue), false);
} catch (SVNException e) {
if (SVNProperty.EOL_STYLE.equals(propName) && e.getErrorMessage().getErrorCode() == SVNErrorCode.ILLEGAL_TARGET && e.getErrorMessage().getMessage().indexOf("newlines") >= 0) {
ISVNAddParameters.Action action = getAddParameters().onInconsistentEOLs(path);
if (action == ISVNAddParameters.REPORT_ERROR) {
ISVNEventHandler eventHandler = getEventDispatcher();
try {
setEventHandler(null);
doRevert(path, dir, SVNDepth.EMPTY, false, null);
} catch (SVNException svne) {
} finally {
setEventHandler(eventHandler);
}
throw e;
} else if (action == ISVNAddParameters.ADD_AS_IS) {
SVNPropertiesManager.setProperty(dir.getWCAccess(), path, propName, null, false);
} else if (action == ISVNAddParameters.ADD_AS_BINARY) {
SVNPropertiesManager.setProperty(dir.getWCAccess(), path, propName, null, false);
mimeType = SVNFileUtil.BINARY_MIME_TYPE;
}
} else {
ISVNEventHandler eventHandler = getEventDispatcher();
try {
setEventHandler(null);
doRevert(path, dir, SVNDepth.EMPTY, false, null);
} catch (SVNException svne) {
} finally {
setEventHandler(eventHandler);
}
throw e;
}
}
}
if (mimeType != null) {
SVNPropertiesManager.setProperty(dir.getWCAccess(), path, SVNProperty.MIME_TYPE, SVNPropertyValue.create(mimeType), false);
} else {
mimeType = (String) props.get(SVNProperty.MIME_TYPE);
}
}
SVNEvent event = SVNEventFactory.createSVNEvent(dir.getFile(path.getName()), SVNNodeKind.FILE, mimeType, SVNRepository.INVALID_REVISION, SVNEventAction.ADD, null, null, null);
dispatchEvent(event);
}
private SVNURL collectLockInfo(SVNWCAccess wcAccess, File[] files, Map lockInfo, Map lockPaths, boolean lock, boolean stealLock) throws SVNException {
String[] paths = new String[files.length];
for (int i = 0; i < files.length; i++) {
paths[i] = files[i].getAbsolutePath();
paths[i] = paths[i].replace(File.separatorChar, '/');
}
Collection condencedPaths = new ArrayList();
String commonParentPath = SVNPathUtil.condencePaths(paths, condencedPaths, false);
if (condencedPaths.isEmpty()) {
condencedPaths.add(SVNPathUtil.tail(commonParentPath));
commonParentPath = SVNPathUtil.removeTail(commonParentPath);
}
if (commonParentPath == null || "".equals(commonParentPath)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "No common parent found, unable to operate on dijoint arguments");
SVNErrorManager.error(err, SVNLogType.WC);
}
paths = (String[]) condencedPaths.toArray(new String[condencedPaths.size()]);
int depth = 0;
for (int i = 0; i < paths.length; i++) {
int segments = SVNPathUtil.getSegmentsCount(paths[i]);
if (depth < segments) {
depth = segments;
}
}
wcAccess.probeOpen(new File(commonParentPath).getAbsoluteFile(), true, depth);
for (int i = 0; i < paths.length; i++) {
File file = new File(commonParentPath, paths[i]);
SVNEntry entry = wcAccess.getVersionedEntry(file, false);
if (entry.getURL() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' has no URL", file);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (lock) {
SVNRevision revision = stealLock ? SVNRevision.UNDEFINED : SVNRevision.create(entry.getRevision());
lockInfo.put(entry.getSVNURL(), new LockInfo(file, revision));
} else {
if (!stealLock && entry.getLockToken() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_MISSING_LOCK_TOKEN, "''{0}'' is not locked in this working copy", file);
SVNErrorManager.error(err, SVNLogType.WC);
}
lockInfo.put(entry.getSVNURL(), new LockInfo(file, stealLock ? null : entry.getLockToken()));
}
}
checkCancelled();
SVNURL[] urls = (SVNURL[]) lockInfo.keySet().toArray(new SVNURL[lockInfo.size()]);
Collection urlPaths = new SVNHashSet();
final SVNURL topURL = SVNURLUtil.condenceURLs(urls, urlPaths, false);
if (urlPaths.isEmpty()) {
urlPaths.add("");
}
if (topURL == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Unable to lock/unlock across multiple repositories");
SVNErrorManager.error(err, SVNLogType.WC);
}
for (Iterator encodedPaths = urlPaths.iterator(); encodedPaths.hasNext();) {
String encodedPath = (String) encodedPaths.next();
SVNURL fullURL = topURL.appendPath(encodedPath, true);
LockInfo info = (LockInfo) lockInfo.get(fullURL);
encodedPath = SVNEncodingUtil.uriDecode(encodedPath);
if (lock) {
if (info.myRevision == SVNRevision.UNDEFINED) {
lockPaths.put(encodedPath, null);
} else {
lockPaths.put(encodedPath, new Long(info.myRevision.getNumber()));
}
} else {
lockPaths.put(encodedPath, info.myToken);
}
}
return topURL;
}
private void crawlEntries(File path, SVNDepth depth, final Collection changeLists, final ISVNInfoHandler handler) throws SVNException {
final SVNWCAccess wcAccess = createWCAccess();
int admLockLevel = getLevelsToLockFromDepth(depth);
try {
wcAccess.probeOpen(path, false, admLockLevel);
wcAccess.walkEntries(path, new ISVNEntryHandler() {
public void handleEntry(File path, SVNEntry entry) throws SVNException {
if (entry.isDirectory() && !entry.isThisDir()) {
return;
}
if (SVNWCAccess.matchesChangeList(changeLists, entry)) {
reportEntry(path, entry, handler);
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
if (error != null && error.getErrorCode() == SVNErrorCode.UNVERSIONED_RESOURCE) {
SVNAdminArea dir = wcAccess.probeTry(path.getParentFile(), false, 0);
if (dir != null) {
SVNTreeConflictDescription tc = dir.getTreeConflict(path.getName());
if (tc != null) {
SVNInfo info = SVNInfo.createInfo(path, tc);
handler.handleInfo(info);
return;
}
}
}
SVNErrorManager.error(error, SVNLogType.WC);
}
}, false, depth);
} finally {
wcAccess.close();
}
}
private void reportEntry(File path, SVNEntry entry, ISVNInfoHandler handler) throws SVNException {
if (entry.isDirectory() && !"".equals(entry.getName())) {
return;
}
handler.handleInfo(SVNInfo.createInfo(path, entry));
}
private boolean doRevert(File path, SVNAdminArea parent, SVNDepth depth, boolean useCommitTimes, Collection changeLists) throws SVNException {
checkCancelled();
SVNWCAccess wcAccess = parent.getWCAccess();
SVNAdminArea dir = wcAccess.probeRetrieve(path);
SVNEntry entry = wcAccess.getEntry(path, false);
SVNTreeConflictDescription treeConflict = wcAccess.getTreeConflict(path);
if (entry == null && treeConflict == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNVERSIONED_RESOURCE, "Cannot revert unversioned item ''{0}''", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (entry != null && entry.getKind() == SVNNodeKind.DIR) {
SVNFileType fileType = SVNFileType.getType(path);
if (fileType != SVNFileType.DIRECTORY && !entry.isScheduledForAddition()) {
if (isRevertMissingDirectories() && entry.getSchedule() != null && !entry.isThisDir()) {
boolean reverted = revert(parent, entry.getName(), entry, useCommitTimes);
if (reverted) {
SVNEvent event = SVNEventFactory.createSVNEvent(dir.getFile(entry.getName()), entry.getKind(), null, entry.getRevision(), SVNEventAction.REVERT, null, null, null);
dispatchEvent(event);
}
return reverted;
}
SVNEvent event = SVNEventFactory.createSVNEvent(dir.getFile(entry.getName()), entry.getKind(), null, entry.getRevision(), SVNEventAction.FAILED_REVERT, null, null, null);
dispatchEvent(event);
return false;
}
}
if (entry != null && entry.getKind() != SVNNodeKind.DIR && entry.getKind() != SVNNodeKind.FILE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot revert ''{0}'': unsupported entry node kind", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNFileType fileType = SVNFileType.getType(path);
if (fileType == SVNFileType.UNKNOWN) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot revert ''{0}'': unsupported node kind in working copy", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
boolean reverted = false;
if (SVNWCAccess.matchesChangeList(changeLists, entry)) {
if (treeConflict != null) {
parent.deleteTreeConflict(path.getName());
reverted = true;
}
if (entry != null) {
if (entry.isScheduledForAddition()) {
boolean wasDeleted = false;
if (entry.getKind() == SVNNodeKind.FILE) {
wasDeleted = entry.isDeleted();
parent.removeFromRevisionControl(path.getName(), false, false);
} else if (entry.getKind() == SVNNodeKind.DIR) {
SVNEntry entryInParent = parent.getEntry(path.getName(), true);
if (entryInParent != null) {
wasDeleted = entryInParent.isDeleted();
}
if (fileType == SVNFileType.NONE || wcAccess.isMissing(path)) {
parent.deleteEntry(path.getName());
parent.saveEntries(false);
} else {
dir.removeFromRevisionControl("", false, false);
}
}
reverted = true;
depth = SVNDepth.EMPTY;
if (wasDeleted) {
Map attributes = new SVNHashMap();
attributes.put(SVNProperty.KIND, entry.getKind().toString());
attributes.put(SVNProperty.DELETED, Boolean.TRUE.toString());
parent.modifyEntry(path.getName(), attributes, true, false);
}
} else if (entry.getSchedule() == null || entry.isScheduledForDeletion() || entry.isScheduledForReplacement()) {
if (entry.getKind() == SVNNodeKind.FILE) {
reverted = revert(parent, entry.getName(), entry, useCommitTimes);
} else if (entry.getKind() == SVNNodeKind.DIR) {
reverted = revert(dir, dir.getThisDirName(), entry, useCommitTimes);
if (reverted && parent != dir) {
SVNEntry entryInParent = parent.getEntry(path.getName(), false);
revert(parent, path.getName(), entryInParent, useCommitTimes);
}
if (entry.isScheduledForReplacement()) {
depth = SVNDepth.INFINITY;
}
}
}
}
if (reverted) {
SVNEvent event = null;
if (entry != null) {
event = SVNEventFactory.createSVNEvent(dir.getFile(entry.getName()), entry.getKind(), null, entry.getRevision(), SVNEventAction.REVERT, null, null, null);
} else {
event = SVNEventFactory.createSVNEvent(path, SVNNodeKind.UNKNOWN, null, SVNRepository.INVALID_REVISION, SVNEventAction.REVERT, null, null, null);
}
dispatchEvent(event);
}
}
if (entry != null && entry.getKind() == SVNNodeKind.DIR && depth.compareTo(SVNDepth.EMPTY) > 0) {
SVNDepth depthBelowHere = depth;
if (depth == SVNDepth.FILES || depth == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
for (Iterator entries = dir.entries(false); entries.hasNext();) {
SVNEntry childEntry = (SVNEntry) entries.next();
if (dir.getThisDirName().equals(childEntry.getName())) {
continue;
}
if (depth == SVNDepth.FILES && !childEntry.isFile()) {
continue;
}
File childPath = new File(path, childEntry.getName());
reverted |= doRevert(childPath, dir, depthBelowHere, useCommitTimes, changeLists);
}
Map conflicts = SVNTreeConflictUtil.readTreeConflicts(path, entry.getTreeConflictData());
for (Iterator conflictsIter = conflicts.keySet().iterator(); conflictsIter.hasNext();) {
File conflictedPath = (File) conflictsIter.next();
if (dir.getEntry(conflictedPath.getName(), false) == null) {
reverted |= doRevert(conflictedPath, dir, SVNDepth.EMPTY, useCommitTimes, changeLists);
}
}
}
return reverted;
}
private boolean revert(SVNAdminArea dir, String name, SVNEntry entry, boolean useCommitTime) throws SVNException {
SVNLog log = dir.getLog();
boolean reverted = false;
SVNVersionedProperties baseProperties = null;
SVNProperties command = new SVNProperties();
boolean revertBase = false;
if (entry.isScheduledForReplacement()) {
revertBase = true;
baseProperties = dir.getRevertProperties(name);
String propRevertPath = SVNAdminUtil.getPropRevertPath(name, entry.getKind(), false);
command.put(SVNLog.NAME_ATTR, propRevertPath);
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
reverted = true;
}
boolean reinstallWorkingFile = false;
if (baseProperties == null) {
if (dir.hasPropModifications(name)) {
baseProperties = dir.getBaseProperties(name);
SVNVersionedProperties propDiff = dir.getProperties(name).compareTo(baseProperties);
Collection propNames = propDiff.getPropertyNames(null);
reinstallWorkingFile = propNames.contains(SVNProperty.EXECUTABLE) || propNames.contains(SVNProperty.KEYWORDS) || propNames.contains(SVNProperty.EOL_STYLE)
|| propNames.contains(SVNProperty.CHARSET) || propNames.contains(SVNProperty.SPECIAL) || propNames.contains(SVNProperty.NEEDS_LOCK);
}
}
if (baseProperties != null) {
SVNProperties newProperties = baseProperties.asMap();
SVNVersionedProperties originalBaseProperties = dir.getBaseProperties(name);
SVNVersionedProperties workProperties = dir.getProperties(name);
if (revertBase) {
originalBaseProperties.removeAll();
}
workProperties.removeAll();
for (Iterator names = newProperties.nameSet().iterator(); names.hasNext();) {
String propName = (String) names.next();
if (revertBase) {
originalBaseProperties.setPropertyValue(propName, newProperties.getSVNPropertyValue(propName));
}
workProperties.setPropertyValue(propName, newProperties.getSVNPropertyValue(propName));
}
dir.saveVersionedProperties(log, false);
reverted = true;
}
SVNProperties newEntryProperties = new SVNProperties();
if (entry.getKind() == SVNNodeKind.FILE) {
String basePath = SVNAdminUtil.getTextBasePath(name, false);
String revertBasePath = SVNAdminUtil.getTextRevertPath(name, false);
if (!reinstallWorkingFile) {
SVNFileType fileType = SVNFileType.getType(dir.getFile(name));
if (fileType == SVNFileType.NONE) {
reinstallWorkingFile = true;
}
}
if (dir.getFile(revertBasePath).isFile()) {
reinstallWorkingFile = true;
} else {
if (!dir.getFile(basePath).isFile()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Error restoring text for ''{0}''", dir.getFile(name));
SVNErrorManager.error(err, SVNLogType.WC);
}
revertBasePath = null;
}
if (revertBasePath != null) {
command.put(SVNLog.NAME_ATTR, revertBasePath);
command.put(SVNLog.DEST_ATTR, name);
log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, revertBasePath);
command.put(SVNLog.DEST_ATTR, basePath);
log.addCommand(SVNLog.MOVE, command, false);
reverted = true;
} else {
if (!reinstallWorkingFile) {
reinstallWorkingFile = dir.hasTextModifications(name, false, false, false);
}
if (reinstallWorkingFile) {
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getTextBasePath(name, false));
command.put(SVNLog.DEST_ATTR, name);
log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false);
command.clear();
if (useCommitTime && entry.getCommittedDate() != null) {
command.put(SVNLog.NAME_ATTR, name);
command.put(SVNLog.TIMESTAMP_ATTR, entry.getCommittedDate());
log.addCommand(SVNLog.SET_TIMESTAMP, command, false);
command.clear();
} else {
command.put(SVNLog.NAME_ATTR, name);
command.put(SVNLog.TIMESTAMP_ATTR, SVNDate.formatDate(new Date(System.currentTimeMillis())));
log.addCommand(SVNLog.SET_TIMESTAMP, command, false);
command.clear();
}
command.put(SVNLog.NAME_ATTR, name);
command.put(SVNProperty.shortPropertyName(SVNProperty.TEXT_TIME), SVNLog.WC_TIMESTAMP);
log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, name);
command.put(SVNProperty.shortPropertyName(SVNProperty.WORKING_SIZE), SVNLog.WC_WORKING_SIZE);
log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
command.clear();
}
reverted |= reinstallWorkingFile;
}
}
if (entry.getConflictNew() != null) {
command.put(SVNLog.NAME_ATTR, entry.getConflictNew());
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_NEW), (String) null);
if (!reverted) {
reverted |= dir.getFile(entry.getConflictNew()).exists();
}
}
if (entry.getConflictOld() != null) {
command.put(SVNLog.NAME_ATTR, entry.getConflictOld());
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_OLD), (String) null);
if (!reverted) {
reverted |= dir.getFile(entry.getConflictOld()).exists();
}
}
if (entry.getConflictWorking() != null) {
command.put(SVNLog.NAME_ATTR, entry.getConflictWorking());
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.CONFLICT_WRK), (String) null);
if (!reverted) {
reverted |= dir.getFile(entry.getConflictWorking()).exists();
}
}
if (entry.getPropRejectFile() != null) {
command.put(SVNLog.NAME_ATTR, entry.getPropRejectFile());
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.PROP_REJECT_FILE), (String) null);
if (!reverted) {
reverted |= dir.getFile(entry.getPropRejectFile()).exists();
}
}
if (entry.isScheduledForReplacement()) {
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.COPIED), SVNProperty.toString(false));
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_URL), (String) null);
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.COPYFROM_REVISION), SVNProperty.toString(SVNRepository.INVALID_REVISION));
if (entry.isFile() && entry.getCopyFromURL() != null) {
String basePath = SVNAdminUtil.getTextRevertPath(name, false);
File baseFile = dir.getFile(basePath);
String digest = SVNFileUtil.computeChecksum(baseFile);
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.CHECKSUM), digest);
}
}
if (entry.getSchedule() != null) {
newEntryProperties.put(SVNProperty.shortPropertyName(SVNProperty.SCHEDULE), (String) null);
reverted = true;
}
if (!newEntryProperties.isEmpty()) {
newEntryProperties.put(SVNLog.NAME_ATTR, name);
log.addCommand(SVNLog.MODIFY_ENTRY, newEntryProperties, false);
}
log.save();
dir.runLogs();
return reverted;
}
private void setWCFormat(SVNAdminAreaInfo info, SVNAdminArea area, int format) throws SVNException {
if (!isIgnoreExternals()) {
SVNVersionedProperties props = area.getProperties(area.getThisDirName());
SVNVersionedProperties baseProps = area.getBaseProperties(area.getThisDirName());
SVNPropertyValue property = props.getPropertyValue(SVNProperty.EXTERNALS);
SVNPropertyValue baseProperty = baseProps.getPropertyValue(SVNProperty.EXTERNALS);
if (property != null || baseProperty != null) {
String areaPath = area.getRelativePath(info.getAnchor());
info.addExternal(areaPath, property != null ? property.getString() : null, baseProperty != null ? baseProperty.getString() : null);
}
}
area.getWCAccess().closeAdminArea(area.getRoot());
area = area.getWCAccess().open(area.getRoot(), true, false, false, 0, Level.FINE);
SVNAdminArea newArea = SVNAdminAreaFactory.changeWCFormat(area, format);
for (Iterator entries = newArea.entries(false); entries.hasNext();) {
SVNEntry entry = (SVNEntry) entries.next();
if (entry.isThisDir() || entry.isFile()) {
continue;
}
File childDir = new File(newArea.getRoot(), entry.getName());
SVNAdminArea childArea = newArea.getWCAccess().getAdminArea(childDir);
if (childArea != null) {
setWCFormat(info, childArea, format);
}
}
}
private void pushDirInfo(SVNRepository repos, SVNRevision rev, String path, SVNURL root, String uuid, SVNURL url, Map locks, SVNDepth depth, ISVNInfoHandler handler) throws SVNException {
Collection children = repos.getDir(path, rev.getNumber(), null, SVNDirEntry.DIRENT_KIND | SVNDirEntry.DIRENT_CREATED_REVISION | SVNDirEntry.DIRENT_TIME | SVNDirEntry.DIRENT_LAST_AUTHOR,
new ArrayList());
for (Iterator ents = children.iterator(); ents.hasNext();) {
checkCancelled();
SVNDirEntry child = (SVNDirEntry) ents.next();
SVNURL childURL = url.appendPath(child.getName(), false);
String childPath = SVNPathUtil.append(path, child.getName());
String displayPath = repos.getFullPath(childPath);
displayPath = displayPath.substring(repos.getLocation().getPath().length());
if (displayPath.startsWith("/")) {
displayPath = displayPath.substring(1);
}
if ("".equals(displayPath)) {
displayPath = path;
}
SVNLock lock = (SVNLock) locks.get(path);
SVNInfo info = SVNInfo.createInfo(displayPath, root, uuid, childURL, rev, child, lock);
if (depth.compareTo(SVNDepth.IMMEDIATES) >= 0 || (depth == SVNDepth.FILES && child.getKind() == SVNNodeKind.FILE)) {
handler.handleInfo(info);
}
if (depth == SVNDepth.INFINITY && child.getKind() == SVNNodeKind.DIR) {
pushDirInfo(repos, rev, SVNPathUtil.append(path, child.getName()), root, uuid, childURL, locks, depth, handler);
}
}
}
private void doGetRemoteProperty(SVNURL url, String path, SVNRepository repos, String propName, SVNRevision rev, SVNDepth depth, ISVNPropertyHandler handler) throws SVNException {
checkCancelled();
long revNumber = getRevisionNumber(rev, repos, null);
SVNNodeKind kind = repos.checkPath(path, revNumber);
SVNProperties props = new SVNProperties();
if (kind == SVNNodeKind.DIR) {
Collection children = repos.getDir(path, revNumber, props, SVNDirEntry.DIRENT_KIND, SVNDepth.FILES.compareTo(depth) <= 0 ? new ArrayList() : null);
if (propName != null) {
SVNPropertyValue value = props.getSVNPropertyValue(propName);
if (value != null) {
handler.handleProperty(url, new SVNPropertyData(propName, value, getOptions()));
}
} else {
for (Iterator names = props.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
if (name.startsWith(SVNProperty.SVN_ENTRY_PREFIX) || name.startsWith(SVNProperty.SVN_WC_PREFIX)) {
continue;
}
SVNPropertyValue value = props.getSVNPropertyValue(name);
handler.handleProperty(url, new SVNPropertyData(name, value, getOptions()));
}
}
if (SVNDepth.FILES.compareTo(depth) <= 0) {
checkCancelled();
for (Iterator entries = children.iterator(); entries.hasNext();) {
SVNDirEntry child = (SVNDirEntry) entries.next();
SVNURL childURL = url.appendPath(child.getName(), false);
String childPath = "".equals(path) ? child.getName() : SVNPathUtil.append(path, child.getName());
SVNDepth depthBelowHere = depth;
if (child.getKind() == SVNNodeKind.DIR && depth == SVNDepth.FILES) {
continue;
}
if (depth == SVNDepth.FILES || depth == SVNDepth.IMMEDIATES) {
depthBelowHere = SVNDepth.EMPTY;
}
doGetRemoteProperty(childURL, childPath, repos, propName, rev, depthBelowHere, handler);
}
}
} else if (kind == SVNNodeKind.FILE) {
repos.getFile(path, revNumber, props, null);
if (propName != null) {
SVNPropertyValue value = props.getSVNPropertyValue(propName);
if (value != null) {
handler.handleProperty(url, new SVNPropertyData(propName, value, getOptions()));
}
} else {
for (Iterator names = props.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
if (name.startsWith(SVNProperty.SVN_ENTRY_PREFIX) || name.startsWith(SVNProperty.SVN_WC_PREFIX)) {
continue;
}
SVNPropertyValue value = props.getSVNPropertyValue(name);
handler.handleProperty(url, new SVNPropertyData(name, value, getOptions()));
}
}
} else if (kind == SVNNodeKind.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' does not exist in revision {1}", new Object[] {
path, String.valueOf(revNumber)
});
SVNErrorManager.error(err, SVNLogType.WC);
} else {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND, "Unknown node kind for ''{0}''", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
private void doGetLocalProperty(SVNEntry entry, SVNAdminArea area, String propName, boolean base, ISVNPropertyHandler handler, SVNDepth depth, Collection changeLists) throws SVNException {
if (depth == null || depth == SVNDepth.UNKNOWN) {
depth = SVNDepth.EMPTY;
}
File target = area.getFile(entry.getName());
SVNWCAccess wcAccess = area.getWCAccess();
ISVNEntryHandler propGetHandler = new PropFetchHandler(changeLists, propName, handler, base);
if (SVNDepth.FILES.compareTo(depth) <= 0 && entry.isDirectory()) {
wcAccess.walkEntries(target, propGetHandler, false, depth);
} else if (SVNWCAccess.matchesChangeList(changeLists, entry)) {
if (propName == null) {
SVNVersionedProperties properties = base ? area.getBaseProperties(entry.getName()) : area.getProperties(entry.getName());
SVNProperties allProps = properties.asMap();
for (Iterator names = allProps.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
SVNPropertyValue val = allProps.getSVNPropertyValue(name);
handler.handleProperty(area.getFile(entry.getName()), new SVNPropertyData(name, val, getOptions()));
}
} else {
propGetHandler.handleEntry(target, entry);
}
}
}
private void setLocalProperties(File path, SVNEntry entry, SVNAdminArea adminArea, boolean force, ISVNPropertyValueProvider propertyValueProvider, ISVNPropertyHandler handler) throws SVNException {
SVNVersionedProperties entryProperties = adminArea.getProperties(entry.getName());
SVNProperties properties = entryProperties.asMap();
SVNProperties unmodifiableProperties = SVNProperties.unmodifiableProperties(properties);
SVNProperties changedProperties = propertyValueProvider.providePropertyValues(path, unmodifiableProperties);
SVNProperties propDiff = properties.compareTo(changedProperties);
for (Iterator iterator = propDiff.nameSet().iterator(); iterator.hasNext();) {
String propName = (String) iterator.next();
SVNPropertyValue propValue = propDiff.getSVNPropertyValue(propName);
if (propValue != null && !SVNPropertiesManager.isValidPropertyName(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Bad property name ''{0}''", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNRevisionProperty.isRevisionProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Revision property ''{0}'' not allowed in this context", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isWorkingCopyProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "''{0}'' is a wcprop, thus not accessible to clients", propName);
SVNErrorManager.error(err, SVNLogType.WC);
} else if (SVNProperty.isEntryProperty(propName)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_PROPERTY_NAME, "Property ''{0}'' is an entry property", propName);
SVNErrorManager.error(err, SVNLogType.WC);
}
try {
boolean modified = SVNPropertiesManager.setProperty(adminArea.getWCAccess(), path, propName, propValue, force);
if (modified && handler != null) {
handler.handleProperty(path, new SVNPropertyData(propName, propValue, getOptions()));
}
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.ILLEGAL_TARGET) {
throw svne;
}
}
}
}
private Map fetchLockTokens(SVNRepository repository, Map pathsTokensMap) throws SVNException {
Map tokens = new SVNHashMap();
for (Iterator paths = pathsTokensMap.keySet().iterator(); paths.hasNext();) {
String path = (String) paths.next();
SVNLock lock = repository.getLock(path);
if (lock == null || lock.getID() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_MISSING_LOCK_TOKEN, "''{0}'' is not locked", path);
SVNErrorManager.error(err, SVNLogType.WC);
continue;
}
tokens.put(path, lock.getID());
}
return tokens;
}
private void doGetLocalFileContents(File path, OutputStream dst, SVNRevision revision, boolean expandKeywords) throws SVNException {
SVNWCAccess wcAccess = createWCAccess();
InputStream input = null;
boolean hasMods = false;
SVNVersionedProperties properties = null;
try {
SVNAdminArea area = wcAccess.open(path.getParentFile(), false, 0);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
if (entry.getKind() != SVNNodeKind.FILE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNVERSIONED_RESOURCE, "''{0}'' refers to a directory", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
String name = path.getName();
if (revision != SVNRevision.WORKING) {
input = area.getBaseFileForReading(name, false);
properties = area.getBaseProperties(name);
} else {
input = SVNFileUtil.openFileForReading(area.getFile(path.getName()), SVNLogType.WC);
hasMods = area.hasPropModifications(name) || area.hasTextModifications(name, true);
properties = area.getProperties(name);
}
String charsetProp = properties.getStringPropertyValue(SVNProperty.CHARSET);
String mimeType = properties.getStringPropertyValue(SVNProperty.MIME_TYPE);
String eolStyle = properties.getStringPropertyValue(SVNProperty.EOL_STYLE);
String keywords = properties.getStringPropertyValue(SVNProperty.KEYWORDS);
boolean special = properties.getPropertyValue(SVNProperty.SPECIAL) != null;
byte[] eols = null;
Map keywordsMap = null;
String time = null;
String charset = SVNTranslator.getCharset(charsetProp, mimeType, path.getPath(), getOptions());
eols = SVNTranslator.getEOL(eolStyle, getOptions());
if (hasMods && !special) {
time = SVNDate.formatDate(new Date(SVNFileUtil.getFileLastModified(path)));
} else {
time = entry.getCommittedDate();
}
if (keywords != null) {
String url = entry.getURL();
String repositoryRoot = entry.getRepositoryRoot();
String author = hasMods ? "(local)" : entry.getAuthor();
String rev = hasMods ? entry.getCommittedRevision() + "M" : entry.getCommittedRevision() + "";
keywordsMap = SVNTranslator.computeKeywords(keywords, expandKeywords ? url : null, repositoryRoot, author, time, rev, getOptions());
}
OutputStream translatingStream = charset != null || eols != null || keywordsMap != null ? SVNTranslator.getTranslatingOutputStream(dst, charset, eols, false, keywordsMap, expandKeywords)
: dst;
try {
SVNTranslator.copy(input, new SVNCancellableOutputStream(translatingStream, getEventDispatcher()));
dst.flush();
} catch (IOExceptionWrapper ioew) {
throw ioew.getOriginalException();
} catch (IOException e) {
if (e instanceof SVNCancellableOutputStream.IOCancelException) {
SVNErrorManager.cancel(e.getMessage(), SVNLogType.NETWORK);
}
SVNTranslator.translationError(path, e);
}
} finally {
SVNFileUtil.closeFile(input);
wcAccess.close();
}
}
private static class LockInfo {
public LockInfo(File file, SVNRevision rev) {
myFile = file;
myRevision = rev;
}
public LockInfo(File file, String token) {
myFile = file;
myToken = token;
}
private File myFile;
private SVNRevision myRevision;
private String myToken;
}
private class PropSetHandler implements ISVNEntryHandler {
private boolean myIsForce;
private String myPropName;
private SVNPropertyValue myPropValue;
private ISVNPropertyHandler myPropHandler;
private Collection myChangeLists;
public PropSetHandler(boolean isForce, String propName, SVNPropertyValue propValue, ISVNPropertyHandler handler, Collection changeLists) {
myIsForce = isForce;
myPropName = propName;
myPropValue = propValue;
myPropHandler = handler;
myChangeLists = changeLists;
}
public void handleEntry(File path, SVNEntry entry) throws SVNException {
SVNAdminArea adminArea = entry.getAdminArea();
if (entry.isDirectory() && !adminArea.getThisDirName().equals(entry.getName())) {
return;
}
if (entry.isScheduledForDeletion()) {
return;
}
if (!SVNWCAccess.matchesChangeList(myChangeLists, entry)) {
return;
}
try {
boolean modified = SVNPropertiesManager.setProperty(adminArea.getWCAccess(), path, myPropName, myPropValue, myIsForce);
if (modified && myPropHandler != null) {
myPropHandler.handleProperty(path, new SVNPropertyData(myPropName, myPropValue, getOptions()));
}
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.ILLEGAL_TARGET) {
throw svne;
}
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
}
private class PropSetHandlerExt implements ISVNEntryHandler {
private boolean myIsForce;
private ISVNPropertyValueProvider myPropValueProvider;
private ISVNPropertyHandler myPropHandler;
private Collection myChangeLists;
public PropSetHandlerExt(boolean isForce, ISVNPropertyValueProvider propertyValueProvider, ISVNPropertyHandler handler, Collection changeLists) {
myIsForce = isForce;
myPropValueProvider = propertyValueProvider;
myPropHandler = handler;
myChangeLists = changeLists;
}
public void handleEntry(File path, SVNEntry entry) throws SVNException {
SVNAdminArea adminArea = entry.getAdminArea();
if (entry.isDirectory() && !adminArea.getThisDirName().equals(entry.getName())) {
return;
}
if (entry.isScheduledForDeletion()) {
return;
}
if (!SVNWCAccess.matchesChangeList(myChangeLists, entry)) {
return;
}
setLocalProperties(path, entry, adminArea, myIsForce, myPropValueProvider, myPropHandler);
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
}
private class PropFetchHandler implements ISVNEntryHandler {
private Collection myChangeLists;
private boolean myIsPristine;
private String myPropName;
private ISVNPropertyHandler myPropHandler;
public PropFetchHandler(Collection changeLists, String propName, ISVNPropertyHandler handler, boolean pristine) {
myChangeLists = changeLists;
myIsPristine = pristine;
myPropName = propName;
myPropHandler = handler;
}
public void handleEntry(File path, SVNEntry entry) throws SVNException {
SVNAdminArea adminArea = entry.getAdminArea();
if (entry.isDirectory() && !entry.isThisDir()) {
return;
}
if ((myIsPristine && entry.isScheduledForAddition()) || (!myIsPristine && entry.isScheduledForDeletion())) {
return;
}
if (!SVNWCAccess.matchesChangeList(myChangeLists, entry)) {
return;
}
SVNVersionedProperties properties = myIsPristine ? adminArea.getBaseProperties(entry.getName()) : adminArea.getProperties(entry.getName());
if (myPropName != null) {
SVNPropertyValue propValue = properties.getPropertyValue(myPropName);
if (propValue != null) {
myPropHandler.handleProperty(path, new SVNPropertyData(myPropName, propValue, getOptions()));
}
} else {
SVNProperties allProps = properties.asMap();
for (Iterator names = allProps.nameSet().iterator(); names.hasNext();) {
String name = (String) names.next();
SVNPropertyValue val = allProps.getSVNPropertyValue(name);
myPropHandler.handleProperty(path, new SVNPropertyData(name, val, getOptions()));
}
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
}
}