0001: /*
0002: * ====================================================================
0003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
0004: *
0005: * This software is licensed as described in the file COPYING, which
0006: * you should have received as part of this distribution. The terms
0007: * are also available at http://svnkit.com/license.html
0008: * If newer versions of this license are posted there, you may use a
0009: * newer version instead, at your option.
0010: * ====================================================================
0011: */
0012: package org.tmatesoft.svn.core.wc;
0013:
0014: import java.io.File;
0015: import java.io.IOException;
0016: import java.io.InputStream;
0017: import java.io.OutputStream;
0018: import java.util.ArrayList;
0019: import java.util.Collection;
0020: import java.util.Collections;
0021: import java.util.Date;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.Map;
0026:
0027: import org.tmatesoft.svn.core.SVNCancelException;
0028: import org.tmatesoft.svn.core.SVNDirEntry;
0029: import org.tmatesoft.svn.core.SVNErrorCode;
0030: import org.tmatesoft.svn.core.SVNErrorMessage;
0031: import org.tmatesoft.svn.core.SVNException;
0032: import org.tmatesoft.svn.core.SVNLock;
0033: import org.tmatesoft.svn.core.SVNNodeKind;
0034: import org.tmatesoft.svn.core.SVNProperty;
0035: import org.tmatesoft.svn.core.SVNRevisionProperty;
0036: import org.tmatesoft.svn.core.SVNURL;
0037: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
0038: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
0039: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0040: import org.tmatesoft.svn.core.internal.util.SVNTimeUtil;
0041: import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
0042: import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
0043: import org.tmatesoft.svn.core.internal.wc.SVNCancellableOutputStream;
0044: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0045: import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
0046: import org.tmatesoft.svn.core.internal.wc.SVNExternalInfo;
0047: import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
0048: import org.tmatesoft.svn.core.internal.wc.SVNFileType;
0049: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0050: import org.tmatesoft.svn.core.internal.wc.SVNPropertiesManager;
0051: import org.tmatesoft.svn.core.internal.wc.SVNStatusEditor;
0052: import org.tmatesoft.svn.core.internal.wc.SVNWCManager;
0053: import org.tmatesoft.svn.core.internal.wc.admin.SVNLog;
0054: import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
0055: import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaInfo;
0056: import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
0057: import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator;
0058: import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslatorOutputStream;
0059: import org.tmatesoft.svn.core.internal.wc.admin.SVNVersionedProperties;
0060: import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
0061: import org.tmatesoft.svn.core.io.ISVNLockHandler;
0062: import org.tmatesoft.svn.core.io.SVNRepository;
0063: import org.tmatesoft.svn.util.SVNDebugLog;
0064:
0065: /**
0066: * The <b>SVNWCClient</b> class combines a number of version control
0067: * operations mainly intended for local work with Working Copy items. This class
0068: * includes those operations that are destined only for local work on a
0069: * Working Copy as well as those that are moreover able to access a repository.
0070: *
0071: * <p>
0072: * Here's a list of the <b>SVNWCClient</b>'s methods
0073: * matched against corresponing commands of the SVN command line
0074: * client:
0075: *
0076: * <table cellpadding="3" cellspacing="1" border="0" width="70%" bgcolor="#999933">
0077: * <tr bgcolor="#ADB8D9" align="left">
0078: * <td><b>SVNKit</b></td>
0079: * <td><b>Subversion</b></td>
0080: * </tr>
0081: * <tr bgcolor="#EAEAEA" align="left">
0082: * <td>doAdd()</td><td>'svn add'</td>
0083: * </tr>
0084: * <tr bgcolor="#EAEAEA" align="left">
0085: * <td>doDelete()</td><td>'svn delete'</td>
0086: * </tr>
0087: * <tr bgcolor="#EAEAEA" align="left">
0088: * <td>doCleanup()</td><td>'svn cleanup'</td>
0089: * </tr>
0090: * <tr bgcolor="#EAEAEA" align="left">
0091: * <td>doInfo()</td><td>'svn info'</td>
0092: * </tr>
0093: * <tr bgcolor="#EAEAEA" align="left">
0094: * <td>doLock()</td><td>'svn lock'</td>
0095: * </tr>
0096: * <tr bgcolor="#EAEAEA" align="left">
0097: * <td>doUnlock()</td><td>'svn unlock'</td>
0098: * </tr>
0099: * <tr bgcolor="#EAEAEA" align="left">
0100: * <td>
0101: * doSetProperty()
0102: * </td>
0103: * <td>
0104: * 'svn propset PROPNAME PROPVAL PATH'<br />
0105: * 'svn propdel PROPNAME PATH'<br />
0106: * 'svn propedit PROPNAME PATH'
0107: * </td>
0108: * </tr>
0109: * <tr bgcolor="#EAEAEA" align="left">
0110: * <td>doSetRevisionProperty()</td>
0111: * <td>
0112: * 'svn propset PROPNAME --revprop -r REV PROPVAL [URL]'<br />
0113: * 'svn propdel PROPNAME --revprop -r REV [URL]'<br />
0114: * 'svn propedit PROPNAME --revprop -r REV [URL]'
0115: * </td>
0116: * </tr>
0117: * <tr bgcolor="#EAEAEA" align="left">
0118: * <td>
0119: * doGetProperty()
0120: * </td>
0121: * <td>
0122: * 'svn propget PROPNAME PATH'<br />
0123: * 'svn proplist PATH'
0124: * </td>
0125: * <tr bgcolor="#EAEAEA" align="left">
0126: * <td>doGetRevisionProperty()</td>
0127: * <td>
0128: * 'svn propget PROPNAME --revprop -r REV [URL]'<br />
0129: * 'svn proplist --revprop -r REV [URL]'
0130: * </td>
0131: * </tr>
0132: * </tr>
0133: * <tr bgcolor="#EAEAEA" align="left">
0134: * <td>doResolve()</td><td>'svn resolved'</td>
0135: * </tr>
0136: * <tr bgcolor="#EAEAEA" align="left">
0137: * <td>doRevert()</td><td>'svn revert'</td>
0138: * </tr>
0139: * </table>
0140: *
0141: * @version 1.1.1
0142: * @author TMate Software Ltd.
0143: * @see <a target="_top" href="http://svnkit.com/kb/examples/">Examples</a>
0144: */
0145: public class SVNWCClient extends SVNBasicClient {
0146:
0147: public static ISVNAddParameters DEFAULT_ADD_PARAMETERS = new ISVNAddParameters() {
0148: public Action onInconsistentEOLs(File file) {
0149: return ISVNAddParameters.REPORT_ERROR;
0150: }
0151: };
0152:
0153: private ISVNAddParameters myAddParameters;
0154:
0155: /**
0156: * Constructs and initializes an <b>SVNWCClient</b> object
0157: * with the specified run-time configuration and authentication
0158: * drivers.
0159: *
0160: * <p>
0161: * If <code>options</code> is <span class="javakeyword">null</span>,
0162: * then this <b>SVNWCClient</b> will be using a default run-time
0163: * configuration driver which takes client-side settings from the
0164: * default SVN's run-time configuration area but is not able to
0165: * change those settings (read more on {@link ISVNOptions} and {@link SVNWCUtil}).
0166: *
0167: * <p>
0168: * If <code>authManager</code> is <span class="javakeyword">null</span>,
0169: * then this <b>SVNWCClient</b> will be using a default authentication
0170: * and network layers driver (see {@link SVNWCUtil#createDefaultAuthenticationManager()})
0171: * which uses server-side settings and auth storage from the
0172: * default SVN's run-time configuration area (or system properties
0173: * if that area is not found).
0174: *
0175: * @param authManager an authentication and network layers driver
0176: * @param options a run-time configuration options driver
0177: */
0178: public SVNWCClient(ISVNAuthenticationManager authManager,
0179: ISVNOptions options) {
0180: super (authManager, options);
0181: }
0182:
0183: public SVNWCClient(ISVNRepositoryPool repositoryPool,
0184: ISVNOptions options) {
0185: super (repositoryPool, options);
0186: }
0187:
0188: public void setAddParameters(ISVNAddParameters addParameters) {
0189: myAddParameters = addParameters;
0190: }
0191:
0192: protected ISVNAddParameters getAddParameters() {
0193: if (myAddParameters == null) {
0194: return DEFAULT_ADD_PARAMETERS;
0195: }
0196:
0197: return myAddParameters;
0198: }
0199:
0200: /**
0201: * Gets contents of a file.
0202: * If <vode>revision</code> is one of:
0203: * <ul>
0204: * <li>{@link SVNRevision#BASE BASE}
0205: * <li>{@link SVNRevision#WORKING WORKING}
0206: * <li>{@link SVNRevision#COMMITTED COMMITTED}
0207: * </ul>
0208: * then the file contents are taken from the Working Copy file item.
0209: * Otherwise the file item's contents are taken from the repository
0210: * at a particular revision.
0211: *
0212: * @param path a Working Copy file item
0213: * @param pegRevision a revision in which the file item is first looked up
0214: * @param revision a target revision
0215: * @param expandKeywords if <span class="javakeyword">true</span> then
0216: * all keywords presenting in the file and listed in
0217: * the file's {@link org.tmatesoft.svn.core.SVNProperty#KEYWORDS svn:keywords}
0218: * property (if set) will be substituted, otherwise not
0219: * @param dst the destination where the file contents will be written to
0220: * @throws SVNException if one of the following is true:
0221: * <ul>
0222: * <li><code>path</code> refers to a directory
0223: * <li><code>path</code> does not exist
0224: * <li><code>path</code> is not under version control
0225: * </ul>
0226: * @see #doGetFileContents(SVNURL, SVNRevision, SVNRevision, boolean, OutputStream)
0227: */
0228: public void doGetFileContents(File path, SVNRevision pegRevision,
0229: SVNRevision revision, boolean expandKeywords,
0230: OutputStream dst) throws SVNException {
0231: if (dst == null) {
0232: return;
0233: }
0234: if (revision == null || !revision.isValid()) {
0235: revision = SVNRevision.BASE;
0236: } else if (revision == SVNRevision.COMMITTED) {
0237: revision = SVNRevision.BASE;
0238: }
0239: if ((!pegRevision.isValid() || pegRevision == SVNRevision.BASE || pegRevision == SVNRevision.WORKING)
0240: && (!revision.isValid() || revision == SVNRevision.BASE || revision == SVNRevision.WORKING)) {
0241: if (pegRevision == null || !pegRevision.isValid()) {
0242: pegRevision = SVNRevision.BASE;
0243: } else if (pegRevision == SVNRevision.COMMITTED) {
0244: pegRevision = SVNRevision.BASE;
0245: }
0246: doGetLocalFileContents(path, dst, revision, expandKeywords);
0247: } else {
0248: SVNRepository repos = createRepository(null, path,
0249: pegRevision, revision);
0250: checkCancelled();
0251: long revNumber = getRevisionNumber(revision, repos, path);
0252: SVNNodeKind kind = repos.checkPath("", revNumber);
0253: if (kind == SVNNodeKind.DIR) {
0254: SVNErrorMessage err = SVNErrorMessage.create(
0255: SVNErrorCode.CLIENT_IS_DIRECTORY,
0256: "URL ''{0}'' refers to a directory", repos
0257: .getLocation());
0258: SVNErrorManager.error(err);
0259: }
0260: checkCancelled();
0261: if (!expandKeywords) {
0262: repos.getFile("", revNumber, null,
0263: new SVNCancellableOutputStream(dst, this ));
0264: } else {
0265: Map properties = new HashMap();
0266: repos.getFile("", revNumber, properties, null);
0267: checkCancelled();
0268:
0269: String keywords = (String) properties
0270: .get(SVNProperty.KEYWORDS);
0271: String eol = (String) properties
0272: .get(SVNProperty.EOL_STYLE);
0273: if (keywords != null || eol != null) {
0274: String cmtRev = (String) properties
0275: .get(SVNProperty.COMMITTED_REVISION);
0276: String cmtDate = (String) properties
0277: .get(SVNProperty.COMMITTED_DATE);
0278: String author = (String) properties
0279: .get(SVNProperty.LAST_AUTHOR);
0280: Map keywordsMap = SVNTranslator.computeKeywords(
0281: keywords, expandKeywords ? repos
0282: .getLocation().toString() : null,
0283: author, cmtDate, cmtRev, getOptions());
0284: OutputStream translatingStream = new SVNTranslatorOutputStream(
0285: dst, SVNTranslator.getEOL(eol), false,
0286: keywordsMap, expandKeywords);
0287: repos.getFile("", revNumber, null,
0288: new SVNCancellableOutputStream(
0289: translatingStream,
0290: getEventDispatcher()));
0291: try {
0292: translatingStream.close();
0293: } catch (IOException e) {
0294: SVNErrorManager.error(SVNErrorMessage.create(
0295: SVNErrorCode.IO_ERROR, e.getMessage()));
0296: }
0297: } else {
0298: repos.getFile("", revNumber, null,
0299: new SVNCancellableOutputStream(dst,
0300: getEventDispatcher()));
0301: }
0302: }
0303: try {
0304: dst.flush();
0305: } catch (IOException e) {
0306: SVNErrorManager.error(SVNErrorMessage.create(
0307: SVNErrorCode.IO_ERROR, e.getMessage()));
0308: }
0309: }
0310: }
0311:
0312: /**
0313: * Gets contents of a file of a particular revision from a repository.
0314: *
0315: * @param url a file item's repository location
0316: * @param pegRevision a revision in which the file item is first looked up
0317: * @param revision a target revision
0318: * @param expandKeywords if <span class="javakeyword">true</span> then
0319: * all keywords presenting in the file and listed in
0320: * the file's {@link org.tmatesoft.svn.core.SVNProperty#KEYWORDS svn:keywords}
0321: * property (if set) will be substituted, otherwise not
0322: * @param dst the destination where the file contents will be written to
0323: * @throws SVNException if one of the following is true:
0324: * <ul>
0325: * <li><code>url</code> refers to a directory
0326: * <li>it's impossible to create temporary files
0327: * ({@link java.io.File#createTempFile(java.lang.String, java.lang.String) createTempFile()}
0328: * fails) necessary for file translating
0329: * </ul>
0330: * @see #doGetFileContents(File, SVNRevision, SVNRevision, boolean, OutputStream)
0331: */
0332: public void doGetFileContents(SVNURL url, SVNRevision pegRevision,
0333: SVNRevision revision, boolean expandKeywords,
0334: OutputStream dst) throws SVNException {
0335: revision = revision == null || !revision.isValid() ? SVNRevision.HEAD
0336: : revision;
0337: // now get contents from URL.
0338: SVNRepository repos = createRepository(url, null, pegRevision,
0339: revision);
0340: checkCancelled();
0341: long revNumber = getRevisionNumber(revision, repos, null);
0342: checkCancelled();
0343: SVNNodeKind nodeKind = repos.checkPath("", revNumber);
0344: checkCancelled();
0345: if (nodeKind == SVNNodeKind.DIR) {
0346: SVNErrorMessage err = SVNErrorMessage.create(
0347: SVNErrorCode.CLIENT_IS_DIRECTORY,
0348: "URL ''{0}'' refers to a directory", url,
0349: SVNErrorMessage.TYPE_WARNING);
0350: SVNErrorManager.error(err);
0351: }
0352: checkCancelled();
0353: if (!expandKeywords) {
0354: repos.getFile("", revNumber, null,
0355: new SVNCancellableOutputStream(dst, this ));
0356: } else {
0357: Map properties = new HashMap();
0358: repos.getFile("", revNumber, properties, null);
0359: checkCancelled();
0360:
0361: String keywords = (String) properties
0362: .get(SVNProperty.KEYWORDS);
0363: String eol = (String) properties.get(SVNProperty.EOL_STYLE);
0364: if (keywords != null || eol != null) {
0365: String cmtRev = (String) properties
0366: .get(SVNProperty.COMMITTED_REVISION);
0367: String cmtDate = (String) properties
0368: .get(SVNProperty.COMMITTED_DATE);
0369: String author = (String) properties
0370: .get(SVNProperty.LAST_AUTHOR);
0371: Map keywordsMap = SVNTranslator.computeKeywords(
0372: keywords, expandKeywords ? repos.getLocation()
0373: .toString() : null, author, cmtDate,
0374: cmtRev, getOptions());
0375: OutputStream translatingStream = new SVNTranslatorOutputStream(
0376: dst, SVNTranslator.getEOL(eol), false,
0377: keywordsMap, expandKeywords);
0378: repos
0379: .getFile("", revNumber, null,
0380: new SVNCancellableOutputStream(
0381: translatingStream,
0382: getEventDispatcher()));
0383: try {
0384: translatingStream.close();
0385: } catch (IOException e) {
0386: SVNErrorManager.error(SVNErrorMessage.create(
0387: SVNErrorCode.IO_ERROR, e.getMessage()));
0388: }
0389: } else {
0390: repos.getFile("", revNumber, null,
0391: new SVNCancellableOutputStream(dst,
0392: getEventDispatcher()));
0393: }
0394: }
0395: try {
0396: dst.flush();
0397: } catch (IOException e) {
0398: SVNErrorManager.error(SVNErrorMessage.create(
0399: SVNErrorCode.IO_ERROR, e.getMessage()));
0400: }
0401: }
0402:
0403: /**
0404: * Recursively cleans up the working copy, removing locks and resuming
0405: * unfinished operations.
0406: *
0407: * <p>
0408: * If you ever get a "working copy locked" error, use this method
0409: * to remove stale locks and get your working copy into a usable
0410: * state again.
0411: *
0412: * @param path a WC path to start a cleanup from
0413: * @throws SVNException if one of the following is true:
0414: * <ul>
0415: * <li><code>path</code> does not exist
0416: * <li><code>path</code>'s parent directory
0417: * is not under version control
0418: * </ul>
0419: */
0420: public void doCleanup(File path) throws SVNException {
0421: doCleanup(path, false);
0422: }
0423:
0424: public void doCleanup(File path, boolean deleteWCProperties)
0425: throws SVNException {
0426: SVNFileType fType = SVNFileType.getType(path);
0427: if (fType == SVNFileType.NONE) {
0428: SVNErrorMessage err = SVNErrorMessage.create(
0429: SVNErrorCode.WC_PATH_NOT_FOUND,
0430: "''{0}'' does not exist", path);
0431: SVNErrorManager.error(err);
0432: } else if (fType == SVNFileType.FILE
0433: || fType == SVNFileType.SYMLINK) {
0434: path = path.getParentFile();
0435: }
0436: SVNWCAccess wcAccess = createWCAccess();
0437: try {
0438: SVNAdminArea adminArea = wcAccess.open(path, true, true, 0);
0439: adminArea.cleanup();
0440: if (deleteWCProperties) {
0441: SVNPropertiesManager.deleteWCProperties(adminArea,
0442: null, true);
0443: }
0444: } catch (SVNException e) {
0445: if (e instanceof SVNCancelException) {
0446: throw e;
0447: } else if (!SVNAdminArea.isSafeCleanup()) {
0448: throw e;
0449: }
0450: SVNDebugLog.getDefaultLog().info(
0451: "CLEANUP FAILED for " + path);
0452: SVNDebugLog.getDefaultLog().info(e);
0453: } finally {
0454: wcAccess.close();
0455: sleepForTimeStamp();
0456: }
0457: }
0458:
0459: /**
0460: * Sets, edits or deletes a property on a file or directory item(s).
0461: *
0462: * <p>
0463: * To set or edit a property simply provide a <code>propName</code>
0464: * and a <code>propValue</code>. To delete a property set
0465: * <code>propValue</code> to <span class="javakeyword">null</span>
0466: * and the property <code>propName</code> will be deleted.
0467: *
0468: * @param path a WC item which properties are to be
0469: * modified
0470: * @param propName a property name
0471: * @param propValue a property value
0472: * @param force <span class="javakeyword">true</span> to
0473: * force the operation to run
0474: * @param recursive <span class="javakeyword">true</span> to
0475: * descend recursively
0476: * @param handler a caller's property handler
0477: * @throws SVNException if one of the following is true:
0478: * <ul>
0479: * <li><code>propName</code> is a revision
0480: * property
0481: * <li><code>propName</code> starts
0482: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0483: * svn:wc:} prefix
0484: * </ul>
0485: * @see #doSetRevisionProperty(File, SVNRevision, String, String, boolean, ISVNPropertyHandler)
0486: * @see #doGetProperty(File, String, SVNRevision, SVNRevision, boolean)
0487: * @see #doGetRevisionProperty(File, String, SVNRevision, ISVNPropertyHandler)
0488: */
0489: public void doSetProperty(File path, String propName,
0490: String propValue, boolean force, boolean recursive,
0491: ISVNPropertyHandler handler) throws SVNException {
0492: propName = validatePropertyName(propName);
0493: if (SVNRevisionProperty.isRevisionProperty(propName)) {
0494: SVNErrorMessage err = SVNErrorMessage
0495: .create(
0496: SVNErrorCode.CLIENT_PROPERTY_NAME,
0497: "Revision property ''{0}'' not allowed in this context",
0498: propName);
0499: SVNErrorManager.error(err);
0500: } else if (SVNProperty.isWorkingCopyProperty(propName)) {
0501: SVNErrorMessage err = SVNErrorMessage
0502: .create(
0503: SVNErrorCode.CLIENT_PROPERTY_NAME,
0504: "''{0}'' is a wcprop, thus not accessible to clients",
0505: propName);
0506: SVNErrorManager.error(err);
0507: } else if (SVNProperty.isEntryProperty(propName)) {
0508: SVNErrorMessage err = SVNErrorMessage
0509: .create(
0510: SVNErrorCode.CLIENT_PROPERTY_NAME,
0511: "''{0}'' is an entry property, thus not accessible to clients",
0512: propName);
0513: SVNErrorManager.error(err);
0514: }
0515: propValue = validatePropertyValue(propName, propValue, force);
0516: SVNWCAccess wcAccess = createWCAccess();
0517: try {
0518: SVNAdminArea area = wcAccess.probeOpen(path, true,
0519: recursive ? SVNWCAccess.INFINITE_DEPTH : 1);//wcAccess.open(path, true, recursive ? SVNWCAccess2.INFINITE_DEPTH : 1);
0520: SVNEntry entry = wcAccess.getEntry(path, false);
0521: if (entry == null) {
0522: SVNErrorMessage err = SVNErrorMessage.create(
0523: SVNErrorCode.UNVERSIONED_RESOURCE,
0524: "''{0}'' is not under version control", path);
0525: SVNErrorManager.error(err);
0526: }
0527: doSetLocalProperty(area, entry.isDirectory() ? area
0528: .getThisDirName() : entry.getName(), propName,
0529: propValue, force, recursive, true, handler);
0530: } finally {
0531: wcAccess.close();
0532: }
0533: }
0534:
0535: /**
0536: * Sets, edits or deletes an unversioned revision property.
0537: * This method uses a Working Copy item to obtain the URL of
0538: * the repository which revision properties are to be changed.
0539: *
0540: * <p>
0541: * To set or edit a property simply provide a <code>propName</code>
0542: * and a <code>propValue</code>. To delete a revision property set
0543: * <code>propValue</code> to <span class="javakeyword">null</span>
0544: * and the property <code>propName</code> will be deleted.
0545: *
0546: * @param path a Working Copy item
0547: * @param revision a revision which properties are to be
0548: * modified
0549: * @param propName a property name
0550: * @param propValue a property value
0551: * @param force <span class="javakeyword">true</span> to
0552: * force the operation to run
0553: * @param handler a caller's property handler
0554: * @throws SVNException if one of the following is true:
0555: * <ul>
0556: * <li>the operation can not be performed
0557: * without forcing
0558: * <li><code>propName</code> starts
0559: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0560: * svn:wc:} prefix
0561: * </ul>
0562: * @see #doSetRevisionProperty(SVNURL, SVNRevision, String, String, boolean, ISVNPropertyHandler)
0563: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0564: * @see #doGetProperty(File, String, SVNRevision, SVNRevision, boolean)
0565: * @see #doGetRevisionProperty(File, String, SVNRevision, ISVNPropertyHandler)
0566: */
0567: public void doSetRevisionProperty(File path, SVNRevision revision,
0568: String propName, String propValue, boolean force,
0569: ISVNPropertyHandler handler) throws SVNException {
0570: propName = validatePropertyName(propName);
0571: propValue = validatePropertyValue(propName, propValue, force);
0572: SVNURL url = getURL(path);
0573: doSetRevisionProperty(url, revision, propName, propValue,
0574: force, handler);
0575: }
0576:
0577: /**
0578: * Sets, edits or deletes an unversioned revision property.
0579: * This method uses a URL pointing to a repository which revision
0580: * properties are to be changed.
0581: *
0582: * <p>
0583: * To set or edit a property simply provide a <code>propName</code>
0584: * and a <code>propValue</code>. To delete a revision property set
0585: * <code>propValue</code> to <span class="javakeyword">null</span>
0586: * and the property <code>propName</code> will be deleted.
0587: *
0588: * @param url a URL pointing to a repository location
0589: * @param revision a revision which properties are to be
0590: * modified
0591: * @param propName a property name
0592: * @param propValue a property value
0593: * @param force <span class="javakeyword">true</span> to
0594: * force the operation to run
0595: * @param handler a caller's property handler
0596: * @throws SVNException if one of the following is true:
0597: * <ul>
0598: * <li>the operation can not be performed
0599: * without forcing
0600: * <li><code>propName</code> starts
0601: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0602: * svn:wc:} prefix
0603: * </ul>
0604: * @see #doSetRevisionProperty(File, SVNRevision, String, String, boolean, ISVNPropertyHandler)
0605: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0606: * @see #doGetProperty(File, String, SVNRevision, SVNRevision, boolean)
0607: * @see #doGetRevisionProperty(File, String, SVNRevision, ISVNPropertyHandler)
0608: */
0609: public void doSetRevisionProperty(SVNURL url, SVNRevision revision,
0610: String propName, String propValue, boolean force,
0611: ISVNPropertyHandler handler) throws SVNException {
0612: propName = validatePropertyName(propName);
0613: propValue = validatePropertyValue(propName, propValue, force);
0614: if (!force && SVNRevisionProperty.AUTHOR.equals(propName)
0615: && propValue != null && propValue.indexOf('\n') >= 0) {
0616: SVNErrorMessage err = SVNErrorMessage
0617: .create(
0618: SVNErrorCode.CLIENT_REVISION_AUTHOR_CONTAINS_NEWLINE,
0619: "Value will not be set unless forced");
0620: SVNErrorManager.error(err);
0621: }
0622: if (propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
0623: SVNErrorMessage err = SVNErrorMessage
0624: .create(
0625: SVNErrorCode.CLIENT_PROPERTY_NAME,
0626: "''{0}'' is a wcprop , thus not accessible to clients",
0627: propName);
0628: SVNErrorManager.error(err);
0629: }
0630: SVNRepository repos = createRepository(url, null,
0631: SVNRevision.UNDEFINED, revision);
0632: long revNumber = getRevisionNumber(revision, repos, null);
0633: repos.setRevisionPropertyValue(revNumber, propName, propValue);
0634: if (handler != null) {
0635: handler.handleProperty(revNumber, new SVNPropertyData(
0636: propName, propValue));
0637: }
0638: }
0639:
0640: /**
0641: * Gets an item's versioned property. It's possible to get either a local
0642: * property (from a Working Copy) or a remote one (located in a repository).
0643: * If <vode>revision</code> is one of:
0644: * <ul>
0645: * <li>{@link SVNRevision#BASE BASE}
0646: * <li>{@link SVNRevision#WORKING WORKING}
0647: * <li>{@link SVNRevision#COMMITTED COMMITTED}
0648: * </ul>
0649: * then the result is a WC item's property. Otherwise the
0650: * property is taken from a repository (using the item's URL).
0651: *
0652: * @param path a WC item's path
0653: * @param propName an item's property name; if it's
0654: * <span class="javakeyword">null</span> then
0655: * all the item's properties will be retrieved
0656: * but only the first of them returned
0657: *
0658: * @param pegRevision a revision in which the item is first looked up
0659: * @param revision a target revision;
0660: *
0661: * @param recursive <span class="javakeyword">true</span> to
0662: * descend recursively
0663: * @return the item's property
0664: * @throws SVNException if one of the following is true:
0665: * <ul>
0666: * <li><code>propName</code> starts
0667: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0668: * svn:wc:} prefix
0669: * <li><code>path</code> is not under version control
0670: * </ul>
0671: * @see #doGetProperty(File, String, SVNRevision, SVNRevision, boolean, ISVNPropertyHandler)
0672: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0673: */
0674: public SVNPropertyData doGetProperty(final File path,
0675: String propName, SVNRevision pegRevision,
0676: SVNRevision revision, boolean recursive)
0677: throws SVNException {
0678: final SVNPropertyData[] data = new SVNPropertyData[1];
0679: doGetProperty(path, propName, pegRevision, revision, recursive,
0680: new ISVNPropertyHandler() {
0681: public void handleProperty(File file,
0682: SVNPropertyData property) {
0683: if (data[0] == null && path.equals(file)) {
0684: data[0] = property;
0685: }
0686: }
0687:
0688: public void handleProperty(SVNURL url,
0689: SVNPropertyData property) {
0690: }
0691:
0692: public void handleProperty(long revision,
0693: SVNPropertyData property) {
0694: }
0695: });
0696: return data[0];
0697: }
0698:
0699: /**
0700: * Gets an item's versioned property from a repository.
0701: * This method is useful when having no Working Copy at all.
0702: *
0703: * @param url an item's repository location
0704: * @param propName an item's property name; if it's
0705: * <span class="javakeyword">null</span> then
0706: * all the item's properties will be retrieved
0707: * but only the first of them returned
0708: * @param pegRevision a revision in which the item is first looked up
0709: * @param revision a target revision
0710: * @param recursive <span class="javakeyword">true</span> to
0711: * descend recursively
0712: * @return the item's property
0713: * @throws SVNException if <code>propName</code> starts
0714: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0715: * svn:wc:} prefix
0716: * @see #doGetProperty(SVNURL, String, SVNRevision, SVNRevision, boolean, ISVNPropertyHandler)
0717: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0718: */
0719: public SVNPropertyData doGetProperty(final SVNURL url,
0720: String propName, SVNRevision pegRevision,
0721: SVNRevision revision, boolean recursive)
0722: throws SVNException {
0723: final SVNPropertyData[] data = new SVNPropertyData[1];
0724: doGetProperty(url, propName, pegRevision, revision, recursive,
0725: new ISVNPropertyHandler() {
0726: public void handleProperty(File file,
0727: SVNPropertyData property) {
0728: }
0729:
0730: public void handleProperty(long revision,
0731: SVNPropertyData property) {
0732: }
0733:
0734: public void handleProperty(SVNURL location,
0735: SVNPropertyData property)
0736: throws SVNException {
0737: if (data[0] == null
0738: && url.toString().equals(
0739: location.toString())) {
0740: data[0] = property;
0741: }
0742: }
0743: });
0744: return data[0];
0745: }
0746:
0747: /**
0748: * Gets an item's versioned property and passes it to a provided property
0749: * handler. It's possible to get either a local property (from a Working
0750: * Copy) or a remote one (located in a repository).
0751: * If <vode>revision</code> is one of:
0752: * <ul>
0753: * <li>{@link SVNRevision#BASE BASE}
0754: * <li>{@link SVNRevision#WORKING WORKING}
0755: * <li>{@link SVNRevision#COMMITTED COMMITTED}
0756: * </ul>
0757: * then the result is a WC item's property. Otherwise the
0758: * property is taken from a repository (using the item's URL).
0759: *
0760: * @param path a WC item's path
0761: * @param propName an item's property name; if it's
0762: * <span class="javakeyword">null</span> then
0763: * all the item's properties will be retrieved
0764: * and passed to <code>handler</code> for
0765: * processing
0766: * @param pegRevision a revision in which the item is first looked up
0767: * @param revision a target revision;
0768: *
0769: * @param recursive <span class="javakeyword">true</span> to
0770: * descend recursively
0771: * @param handler a caller's property handler
0772: * @throws SVNException if one of the following is true:
0773: * <ul>
0774: * <li><code>propName</code> starts
0775: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0776: * svn:wc:} prefix
0777: * <li><code>path</code> is not under version control
0778: * </ul>
0779: * @see #doGetProperty(File, String, SVNRevision, SVNRevision, boolean)
0780: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0781: */
0782: public void doGetProperty(File path, String propName,
0783: SVNRevision pegRevision, SVNRevision revision,
0784: boolean recursive, ISVNPropertyHandler handler)
0785: throws SVNException {
0786: if (propName != null
0787: && propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
0788: SVNErrorMessage err = SVNErrorMessage
0789: .create(
0790: SVNErrorCode.CLIENT_PROPERTY_NAME,
0791: "''{0}'' is a wcprop , thus not accessible to clients",
0792: propName);
0793: SVNErrorManager.error(err);
0794: }
0795: if (revision == null || !revision.isValid()) {
0796: revision = SVNRevision.WORKING;
0797: }
0798: SVNWCAccess wcAccess = createWCAccess();
0799:
0800: try {
0801: SVNAdminArea area = wcAccess.probeOpen(path, false,
0802: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
0803: SVNEntry entry = wcAccess.getEntry(path, false);
0804: if (entry == null) {
0805: SVNErrorMessage err = SVNErrorMessage.create(
0806: SVNErrorCode.ENTRY_NOT_FOUND,
0807: "''{0}'' is not under version control", path);
0808: SVNErrorManager.error(err);
0809: }
0810: if (revision != SVNRevision.WORKING
0811: && revision != SVNRevision.BASE
0812: && revision != SVNRevision.COMMITTED) {
0813: SVNURL url = entry.getSVNURL();
0814: SVNRepository repository = createRepository(null, path,
0815: pegRevision, revision);
0816: long revisionNumber = getRevisionNumber(revision,
0817: repository, path);
0818: revision = SVNRevision.create(revisionNumber);
0819: doGetRemoteProperty(url, "", repository, propName,
0820: revision, recursive, handler);
0821: } else {
0822: boolean base = revision == SVNRevision.BASE;
0823: if (entry.getKind() == SVNNodeKind.DIR && recursive) {
0824: // area is path itself.
0825: doGetLocalProperty(area, propName, base, handler);
0826: } else {
0827: // area could only be path itself or child file in it.
0828: if ((base && entry.isScheduledForAddition())
0829: || (!base && entry.isScheduledForDeletion())) {
0830: return;
0831: }
0832: SVNVersionedProperties properties = base ? area
0833: .getBaseProperties(entry.getName()) : area
0834: .getProperties(entry.getName());
0835: if (propName != null) {
0836: String propValue = properties
0837: .getPropertyValue(propName);
0838: if (propValue != null) {
0839: handler.handleProperty(path,
0840: new SVNPropertyData(propName,
0841: propValue));
0842: }
0843: } else {
0844: Map allProps = properties.asMap();
0845: for (Iterator names = allProps.keySet()
0846: .iterator(); names.hasNext();) {
0847: String name = (String) names.next();
0848: String val = (String) allProps.get(name);
0849: handler.handleProperty(area.getFile(entry
0850: .getName()), new SVNPropertyData(
0851: name, val));
0852: }
0853: }
0854: }
0855: }
0856: } finally {
0857: wcAccess.close();
0858: }
0859: }
0860:
0861: /**
0862: * Gets an item's versioned property from a repository and passes it to
0863: * a provided property handler. This method is useful when having no
0864: * Working Copy at all.
0865: *
0866: * @param url an item's repository location
0867: * @param propName an item's property name; if it's
0868: * <span class="javakeyword">null</span> then
0869: * all the item's properties will be retrieved
0870: * and passed to <code>handler</code> for
0871: * processing
0872: * @param pegRevision a revision in which the item is first looked up
0873: * @param revision a target revision
0874: * @param recursive <span class="javakeyword">true</span> to
0875: * descend recursively
0876: * @param handler a caller's property handler
0877: * @throws SVNException if <code>propName</code> starts
0878: * with the {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0879: * svn:wc:} prefix
0880: * @see #doGetProperty(SVNURL, String, SVNRevision, SVNRevision, boolean)
0881: * @see #doSetProperty(File, String, String, boolean, boolean, ISVNPropertyHandler)
0882: */
0883: public void doGetProperty(SVNURL url, String propName,
0884: SVNRevision pegRevision, SVNRevision revision,
0885: boolean recursive, ISVNPropertyHandler handler)
0886: throws SVNException {
0887: if (propName != null
0888: && propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
0889: SVNErrorMessage err = SVNErrorMessage
0890: .create(
0891: SVNErrorCode.CLIENT_PROPERTY_NAME,
0892: "''{0}'' is a wcprop , thus not accessible to clients",
0893: propName);
0894: SVNErrorManager.error(err);
0895: }
0896: if (revision == null || !revision.isValid()) {
0897: revision = SVNRevision.HEAD;
0898: }
0899: SVNRepository repos = createRepository(url, null, pegRevision,
0900: revision);
0901: doGetRemoteProperty(url, "", repos, propName, revision,
0902: recursive, handler);
0903: }
0904:
0905: /**
0906: * Gets an unversioned revision property from a repository (getting
0907: * a repository URL from a Working Copy) and passes it to a provided
0908: * property handler.
0909: *
0910: * @param path a local Working Copy item which repository
0911: * location is used to connect to a repository
0912: * @param propName a revision property name; if this parameter
0913: * is <span class="javakeyword">null</span> then
0914: * all the revision properties will be retrieved
0915: * and passed to <code>handler</code> for
0916: * processing
0917: * @param revision a revision which property is to be retrieved
0918: * @param handler a caller's property handler
0919: * @throws SVNException if one of the following is true:
0920: * <ul>
0921: * <li><code>revision</code> is invalid
0922: * <li><code>propName</code> starts with the
0923: * {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0924: * svn:wc:} prefix
0925: * </ul>
0926: * @see #doGetRevisionProperty(SVNURL, String, SVNRevision, ISVNPropertyHandler)
0927: * @see #doSetRevisionProperty(File, SVNRevision, String, String, boolean, ISVNPropertyHandler)
0928: */
0929: public void doGetRevisionProperty(File path, String propName,
0930: SVNRevision revision, ISVNPropertyHandler handler)
0931: throws SVNException {
0932: if (propName != null
0933: && propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
0934: SVNErrorMessage err = SVNErrorMessage
0935: .create(
0936: SVNErrorCode.CLIENT_PROPERTY_NAME,
0937: "''{0}'' is a wcprop , thus not accessible to clients",
0938: propName);
0939: SVNErrorManager.error(err);
0940: }
0941: if (!revision.isValid()) {
0942: SVNErrorMessage err = SVNErrorMessage
0943: .create(SVNErrorCode.CLIENT_BAD_REVISION,
0944: "Valid revision have to be specified to fetch revision property");
0945: SVNErrorManager.error(err);
0946: }
0947: SVNRepository repository = createRepository(null, path,
0948: SVNRevision.UNDEFINED, revision);
0949: long revisionNumber = getRevisionNumber(revision, repository,
0950: path);
0951: doGetRevisionProperty(repository, propName, revisionNumber,
0952: handler);
0953: }
0954:
0955: /**
0956: * Gets an unversioned revision property from a repository and passes
0957: * it to a provided property handler.
0958: *
0959: * @param url a URL pointing to a repository location
0960: * which revision property is to be got
0961: * @param propName a revision property name; if this parameter
0962: * is <span class="javakeyword">null</span> then
0963: * all the revision properties will be retrieved
0964: * and passed to <code>handler</code> for
0965: * processing
0966: * @param revision a revision which property is to be retrieved
0967: * @param handler a caller's property handler
0968: * @throws SVNException if one of the following is true:
0969: * <ul>
0970: * <li><code>revision</code> is invalid
0971: * <li><code>propName</code> starts with the
0972: * {@link org.tmatesoft.svn.core.SVNProperty#SVN_WC_PREFIX
0973: * svn:wc:} prefix
0974: * </ul>
0975: * @see #doGetRevisionProperty(File, String, SVNRevision, ISVNPropertyHandler)
0976: * @see #doSetRevisionProperty(SVNURL, SVNRevision, String, String, boolean, ISVNPropertyHandler)
0977: */
0978: public void doGetRevisionProperty(SVNURL url, String propName,
0979: SVNRevision revision, ISVNPropertyHandler handler)
0980: throws SVNException {
0981: if (propName != null
0982: && propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
0983: SVNErrorMessage err = SVNErrorMessage
0984: .create(
0985: SVNErrorCode.CLIENT_PROPERTY_NAME,
0986: "''{0}'' is a wcprop , thus not accessible to clients",
0987: propName);
0988: SVNErrorManager.error(err);
0989: }
0990: if (!revision.isValid()) {
0991: SVNErrorMessage err = SVNErrorMessage
0992: .create(SVNErrorCode.CLIENT_BAD_REVISION,
0993: "Valid revision have to be specified to fetch revision property");
0994: SVNErrorManager.error(err);
0995: }
0996: SVNRepository repos = createRepository(url, true);
0997: long revNumber = getRevisionNumber(revision, repos, null);
0998: doGetRevisionProperty(repos, propName, revNumber, handler);
0999: }
1000:
1001: private void doGetRevisionProperty(SVNRepository repos,
1002: String propName, long revNumber, ISVNPropertyHandler handler)
1003: throws SVNException {
1004: if (propName != null) {
1005: String value = repos.getRevisionPropertyValue(revNumber,
1006: propName);
1007: if (value != null) {
1008: handler.handleProperty(revNumber, new SVNPropertyData(
1009: propName, value));
1010: }
1011: } else {
1012: Map props = new HashMap();
1013: repos.getRevisionProperties(revNumber, props);
1014: for (Iterator names = props.keySet().iterator(); names
1015: .hasNext();) {
1016: String name = (String) names.next();
1017: String value = (String) props.get(name);
1018: handler.handleProperty(revNumber, new SVNPropertyData(
1019: name, value));
1020: }
1021: }
1022: }
1023:
1024: /**
1025: * Schedules a Working Copy item for deletion.
1026: *
1027: * @param path a WC item to be deleted
1028: * @param force <span class="javakeyword">true</span> to
1029: * force the operation to run
1030: * @param dryRun <span class="javakeyword">true</span> only to
1031: * try the delete operation without actual deleting
1032: * @throws SVNException if one of the following is true:
1033: * <ul>
1034: * <li><code>path</code> is not under version control
1035: * <li>can not delete <code>path</code> without forcing
1036: * </ul>
1037: * @see #doDelete(File, boolean, boolean, boolean)
1038: */
1039: public void doDelete(File path, boolean force, boolean dryRun)
1040: throws SVNException {
1041: doDelete(path, force, true, dryRun);
1042: }
1043:
1044: /**
1045: * Schedules a Working Copy item for deletion. This method allows to
1046: * choose - whether file item(s) are to be deleted from the filesystem or
1047: * not. Another version of the {@link #doDelete(File, boolean, boolean) doDelete()}
1048: * method is similar to the corresponding SVN client's command - <code>'svn delete'</code>
1049: * as it always deletes files from the filesystem.
1050: *
1051: *
1052: * @param path a WC item to be deleted
1053: * @param force <span class="javakeyword">true</span> to
1054: * force the operation to run
1055: * @param deleteFiles if <span class="javakeyword">true</span> then
1056: * files will be scheduled for deletion as well as
1057: * deleted from the filesystem, otherwise files will
1058: * be only scheduled for addition and still be present
1059: * in the filesystem
1060: * @param dryRun <span class="javakeyword">true</span> only to
1061: * try the delete operation without actual deleting
1062: * @throws SVNException if one of the following is true:
1063: * <ul>
1064: * <li><code>path</code> is not under version control
1065: * <li>can not delete <code>path</code> without forcing
1066: * </ul>
1067: */
1068: public void doDelete(File path, boolean force, boolean deleteFiles,
1069: boolean dryRun) throws SVNException {
1070: SVNWCAccess wcAccess = createWCAccess();
1071: path = new File(SVNPathUtil.validateFilePath(path
1072: .getAbsolutePath())).getAbsoluteFile();
1073: try {
1074: if (!force) {
1075: SVNWCManager.canDelete(path, getOptions(), this );
1076: }
1077: SVNAdminArea root = wcAccess.open(path.getParentFile(),
1078: true, 0);
1079: if (!dryRun) {
1080: SVNWCManager.delete(wcAccess, root, path, deleteFiles,
1081: true);
1082: }
1083: } finally {
1084: wcAccess.close();
1085: }
1086: }
1087:
1088: /**
1089: * Schedules an unversioned item for addition to a repository thus
1090: * putting it under version control.
1091: *
1092: * <p>
1093: * To create and add to version control a new directory, set <code>mkdir</code>
1094: * to <span class="javakeyword">true</span>.
1095: *
1096: * <p>
1097: * Calling this method is equivalent to
1098: * <code>doAdd(path, force, mkdir, climbUnversionedParents, recursive, false)</code>.
1099: *
1100: * @param path a path to be put under version
1101: * control (will be added to a repository
1102: * in next commit)
1103: * @param force when <span class="javakeyword">true</span> forces the operation
1104: * to run on already versioned files or directories without reporting
1105: * error. When ran recursively, all unversioned files and directories
1106: * in a tree will be scheduled for addition.
1107: * @param mkdir if <span class="javakeyword">true</span> -
1108: * creates a new directory and schedules it for
1109: * addition
1110: * @param climbUnversionedParents if <span class="javakeyword">true</span> and
1111: * <code>path</code> is located in an unversioned
1112: * parent directory then the parent will be automatically
1113: * scheduled for addition, too
1114: * @param recursive <span class="javakeyword">true</span> to
1115: * descend recursively (relevant for directories)
1116: * @throws SVNException if one of the following is true:
1117: * <ul>
1118: * <li><code>path</code> doesn't belong
1119: * to a Working Copy
1120: * <li><code>path</code> doesn't exist and
1121: * <code>mkdir</code> is <span class="javakeyword">false</span>
1122: * <li><code>path</code> is the root directory of the Working Copy
1123: */
1124: public void doAdd(File path, boolean force, boolean mkdir,
1125: boolean climbUnversionedParents, boolean recursive)
1126: throws SVNException {
1127: doAdd(path, force, mkdir, climbUnversionedParents, recursive,
1128: false);
1129: }
1130:
1131: /**
1132: * Schedules an unversioned item for addition to a repository thus
1133: * putting it under version control.
1134: *
1135: * <p>
1136: * To create and add to version control a new directory, set <code>mkdir</code>
1137: * to <span class="javakeyword">true</span>.
1138: *
1139: * @param path a path to be put under version
1140: * control (will be added to a repository
1141: * in next commit)
1142: * @param force when <span class="javakeyword">true</span> forces the operation
1143: * to run on already versioned files or directories without reporting
1144: * error. When ran recursively, all unversioned files and directories
1145: * in a tree will be scheduled for addition.
1146: * @param mkdir if <span class="javakeyword">true</span> -
1147: * creates a new directory and schedules it for
1148: * addition
1149: * @param climbUnversionedParents if <span class="javakeyword">true</span> and
1150: * <code>path</code> is located in an unversioned
1151: * parent directory then the parent will be automatically
1152: * scheduled for addition, too
1153: * @param recursive <span class="javakeyword">true</span> to
1154: * descend recursively (relevant for directories)
1155: * @param includeIgnored controls whether ignored items must be also added
1156: * @throws SVNException if one of the following is true:
1157: * <ul>
1158: * <li><code>path</code> doesn't belong
1159: * to a Working Copy
1160: * <li><code>path</code> doesn't exist and
1161: * <code>mkdir</code> is <span class="javakeyword">false</span>
1162: * <li><code>path</code> is the root directory of the Working Copy
1163: * </ul>
1164: * @since 1.1
1165: */
1166: public void doAdd(File path, boolean force, boolean mkdir,
1167: boolean climbUnversionedParents, boolean recursive,
1168: boolean includeIgnored) throws SVNException {
1169: path = new File(SVNPathUtil.validateFilePath(path
1170: .getAbsolutePath()));
1171: if (!mkdir && climbUnversionedParents
1172: && path.getParentFile() != null) {
1173: // check if parent is versioned. if not, add it.
1174: SVNWCAccess wcAccess = createWCAccess();
1175: try {
1176: wcAccess.open(path.getParentFile(), false, 0);
1177: } catch (SVNException e) {
1178: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
1179: doAdd(path.getParentFile(), false, false,
1180: climbUnversionedParents, false);
1181: } else {
1182: throw e;
1183: }
1184: } finally {
1185: wcAccess.close();
1186: }
1187: }
1188: if (force && mkdir
1189: && SVNFileType.getType(path) == SVNFileType.DIRECTORY) {
1190: // directory is already there.
1191: doAdd(path, force, false, true, false, true);
1192: return;
1193: } else if (mkdir) {
1194: // attempt to create dir
1195: File parent = path;
1196: File firstCreated = path;
1197: while (parent != null
1198: && SVNFileType.getType(parent) == SVNFileType.NONE) {
1199: firstCreated = parent;
1200: parent = parent.getParentFile();
1201: }
1202: boolean created = path.mkdirs();
1203: if (!created) {
1204: // delete created dirs.
1205: SVNErrorMessage err = SVNErrorMessage.create(
1206: SVNErrorCode.IO_ERROR,
1207: "Cannot create new directory ''{0}''", path);
1208: while (parent == null ? path != null : !path
1209: .equals(parent)) {
1210: SVNFileUtil.deleteAll(path, true);
1211: path = path.getParentFile();
1212: }
1213: SVNErrorManager.error(err);
1214: }
1215: try {
1216: doAdd(firstCreated, false, false,
1217: climbUnversionedParents, true, true);
1218: } catch (SVNException e) {
1219: SVNFileUtil.deleteAll(firstCreated, true);
1220: throw e;
1221: }
1222: return;
1223: }
1224: SVNWCAccess wcAccess = createWCAccess();
1225: try {
1226: SVNAdminArea dir = null;
1227: if (path.isDirectory()) {
1228: dir = wcAccess.open(
1229: SVNWCUtil.isVersionedDirectory(path
1230: .getParentFile()) ? path
1231: .getParentFile() : path, true, 0);
1232: } else {
1233: dir = wcAccess.open(path.getParentFile(), true, 0);
1234: }
1235: SVNFileType fileType = SVNFileType.getType(path);
1236: if (fileType == SVNFileType.DIRECTORY && recursive) {
1237: addDirectory(path, dir, force, includeIgnored);
1238: } else if (fileType == SVNFileType.FILE
1239: || fileType == SVNFileType.SYMLINK) {
1240: addFile(path, fileType, dir);
1241: } else {
1242: SVNWCManager
1243: .add(path, dir, null, SVNRevision.UNDEFINED);
1244: }
1245: } catch (SVNException e) {
1246: if (!(force && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS)) {
1247: throw e;
1248: }
1249: } finally {
1250: wcAccess.close();
1251: }
1252: }
1253:
1254: private void addDirectory(File path, SVNAdminArea parentDir,
1255: boolean force, boolean noIgnore) throws SVNException {
1256: checkCancelled();
1257: try {
1258: SVNWCManager.add(path, parentDir, null,
1259: SVNRevision.UNDEFINED);
1260: } catch (SVNException e) {
1261: if (!(force && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS)) {
1262: throw e;
1263: }
1264: }
1265: SVNWCAccess access = parentDir.getWCAccess();
1266: SVNAdminArea dir = access.retrieve(path);
1267: Collection ignores = Collections.EMPTY_SET;
1268: if (!noIgnore) {
1269: ignores = SVNStatusEditor.getIgnorePatterns(dir,
1270: SVNStatusEditor.getGlobalIgnores(getOptions()));
1271: }
1272: File[] children = SVNFileListUtil.listFiles(dir.getRoot());
1273: for (int i = 0; children != null && i < children.length; i++) {
1274: checkCancelled();
1275: if (SVNFileUtil.getAdminDirectoryName().equals(
1276: children[i].getName())) {
1277: continue;
1278: }
1279: if (!noIgnore
1280: && SVNStatusEditor.isIgnored(ignores, children[i]
1281: .getName())) {
1282: continue;
1283: }
1284: SVNFileType childType = SVNFileType.getType(children[i]);
1285: if (childType == SVNFileType.DIRECTORY) {
1286: addDirectory(children[i], dir, force, noIgnore);
1287: } else if (childType != SVNFileType.UNKNOWN) {
1288: try {
1289: addFile(children[i], childType, dir);
1290: } catch (SVNException e) {
1291: if (force
1292: && e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_EXISTS) {
1293: continue;
1294: }
1295: throw e;
1296: }
1297: }
1298: }
1299:
1300: }
1301:
1302: private void addFile(File path, SVNFileType type, SVNAdminArea dir)
1303: throws SVNException {
1304: ISVNEventHandler handler = dir.getWCAccess().getEventHandler();
1305: dir.getWCAccess().setEventHandler(null);
1306: SVNWCManager.add(path, dir, null, SVNRevision.UNDEFINED);
1307: dir.getWCAccess().setEventHandler(handler);
1308:
1309: String mimeType = null;
1310: if (type == SVNFileType.SYMLINK) {
1311: SVNPropertiesManager
1312: .setProperty(
1313: dir.getWCAccess(),
1314: path,
1315: SVNProperty.SPECIAL,
1316: SVNProperty
1317: .getValueOfBooleanProperty(SVNProperty.SPECIAL),
1318: false);
1319: } else {
1320: Map props = SVNPropertiesManager.computeAutoProperties(
1321: getOptions(), path);
1322: for (Iterator names = props.keySet().iterator(); names
1323: .hasNext();) {
1324: String propName = (String) names.next();
1325: String propValue = (String) props.get(propName);
1326: try {
1327: SVNPropertiesManager.setProperty(dir.getWCAccess(),
1328: path, propName, propValue, false);
1329: } catch (SVNException e) {
1330: if (SVNProperty.EOL_STYLE.equals(propName)
1331: && e.getErrorMessage().getErrorCode() == SVNErrorCode.ILLEGAL_TARGET
1332: && e.getErrorMessage().getMessage()
1333: .indexOf("newlines") >= 0) {
1334: ISVNAddParameters.Action action = getAddParameters()
1335: .onInconsistentEOLs(path);
1336: if (action == ISVNAddParameters.REPORT_ERROR) {
1337: throw e;
1338: } else if (action == ISVNAddParameters.ADD_AS_IS) {
1339: SVNPropertiesManager.setProperty(dir
1340: .getWCAccess(), path, propName,
1341: null, false);
1342: } else if (action == ISVNAddParameters.ADD_AS_BINARY) {
1343: SVNPropertiesManager.setProperty(dir
1344: .getWCAccess(), path, propName,
1345: null, false);
1346: mimeType = SVNFileUtil.BINARY_MIME_TYPE;
1347: }
1348: } else {
1349: throw e;
1350: }
1351: }
1352: }
1353: if (mimeType != null) {
1354: SVNPropertiesManager.setProperty(dir.getWCAccess(),
1355: path, SVNProperty.MIME_TYPE, mimeType, false);
1356: } else {
1357: mimeType = (String) props.get(SVNProperty.MIME_TYPE);
1358: }
1359: }
1360: SVNEvent event = SVNEventFactory.createAddedEvent(dir, path
1361: .getName(), SVNNodeKind.FILE, mimeType);
1362: dispatchEvent(event);
1363: }
1364:
1365: /**
1366: * Reverts all local changes made to a Working Copy item(s) thus
1367: * bringing it to a 'pristine' state.
1368: *
1369: * @param path a WC path to perform a revert on
1370: * @param recursive <span class="javakeyword">true</span> to
1371: * descend recursively (relevant for directories)
1372: * @throws SVNException if one of the following is true:
1373: * <ul>
1374: * <li><code>path</code> is not under version control
1375: * <li>when trying to revert an addition of a directory
1376: * from within the directory itself
1377: * </ul>
1378: * @see #doRevert(File[], boolean)
1379: */
1380: public void doRevert(File path, boolean recursive)
1381: throws SVNException {
1382: doRevert(new File[] { path }, recursive);
1383: }
1384:
1385: /**
1386: * Reverts all local changes made to a Working Copy item(s) thus
1387: * bringing it to a 'pristine' state.
1388: *
1389: * @param paths a WC paths to perform a revert on
1390: * @param recursive <span class="javakeyword">true</span> to
1391: * descend recursively (relevant for directories)
1392: * @throws SVNException if one of the following is true:
1393: * <ul>
1394: * <li><code>path</code> is not under version control
1395: * <li>when trying to revert an addition of a directory
1396: * from within the directory itself
1397: * </ul>
1398: *
1399: * Exception will not be thrown if there are multiple paths passed.
1400: * Instead caller should process events received by <code>ISVNEventHandler</code>
1401: * instance to get information on whether certain path was reverted or not.
1402: */
1403: public void doRevert(File[] paths, boolean recursive)
1404: throws SVNException {
1405: boolean reverted = false;
1406: try {
1407: for (int i = 0; i < paths.length; i++) {
1408: File path = paths[i];
1409: path = new File(SVNPathUtil.validateFilePath(path
1410: .getAbsolutePath()));
1411: SVNWCAccess wcAccess = createWCAccess();
1412: try {
1413: SVNAdminAreaInfo info = wcAccess.openAnchor(path,
1414: true,
1415: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
1416: SVNEntry entry = wcAccess.getEntry(path, false);
1417: if (entry != null && entry.isDirectory()
1418: && entry.isScheduledForAddition()) {
1419: if (!recursive) {
1420: getDebugLog().info(
1421: "Forcing revert on path '" + path
1422: + "' to recurse");
1423: recursive = true;
1424: wcAccess.close();
1425: info = wcAccess.openAnchor(path, true,
1426: SVNWCAccess.INFINITE_DEPTH);
1427: }
1428: }
1429:
1430: boolean useCommitTimes = getOptions()
1431: .isUseCommitTimes();
1432: reverted |= doRevert(path, info.getAnchor(),
1433: recursive, useCommitTimes);
1434: } catch (SVNException e) {
1435: reverted |= true;
1436: SVNErrorCode code = e.getErrorMessage()
1437: .getErrorCode();
1438: if (code == SVNErrorCode.ENTRY_NOT_FOUND
1439: || code == SVNErrorCode.UNVERSIONED_RESOURCE) {
1440: SVNEvent event = SVNEventFactory
1441: .createSkipEvent(path.getParentFile(),
1442: path, SVNEventAction.SKIP,
1443: SVNEventAction.REVERT, null);
1444: dispatchEvent(event);
1445: }
1446: if (paths.length == 1) {
1447: throw e;
1448: }
1449: } finally {
1450: wcAccess.close();
1451: }
1452: }
1453: } finally {
1454: if (reverted) {
1455: sleepForTimeStamp();
1456: }
1457: }
1458: }
1459:
1460: private boolean doRevert(File path, SVNAdminArea parent,
1461: boolean recursive, boolean useCommitTimes)
1462: throws SVNException {
1463: checkCancelled();
1464: SVNAdminArea dir = parent.getWCAccess().probeRetrieve(path);
1465: SVNEntry entry = dir.getWCAccess().getEntry(path, false);
1466: if (entry == null) {
1467: SVNErrorMessage err = SVNErrorMessage
1468: .create(
1469: SVNErrorCode.UNVERSIONED_RESOURCE,
1470: "Cannot revert: ''{0}'' is not under version control",
1471: path);
1472: SVNErrorManager.error(err);
1473: }
1474: if (entry.getKind() == SVNNodeKind.DIR) {
1475: SVNFileType fileType = SVNFileType.getType(path);
1476: if (fileType != SVNFileType.DIRECTORY
1477: && !entry.isScheduledForAddition()) {
1478: SVNEvent event = SVNEventFactory
1479: .createNotRevertedEvent(dir, entry);
1480: dispatchEvent(event);
1481: return false;
1482: }
1483: }
1484: if (entry.getKind() != SVNNodeKind.DIR
1485: && entry.getKind() != SVNNodeKind.FILE) {
1486: SVNErrorMessage err = SVNErrorMessage
1487: .create(
1488: SVNErrorCode.UNSUPPORTED_FEATURE,
1489: "Cannot revert ''{0}'': unsupported entry node kind",
1490: path);
1491: SVNErrorManager.error(err);
1492: }
1493: SVNFileType fileType = SVNFileType.getType(path);
1494: if (fileType == SVNFileType.UNKNOWN) {
1495: SVNErrorMessage err = SVNErrorMessage
1496: .create(
1497: SVNErrorCode.UNSUPPORTED_FEATURE,
1498: "Cannot revert ''{0}'': unsupported node kind in working copy",
1499: path);
1500: SVNErrorManager.error(err);
1501: }
1502: boolean reverted = false;
1503: if (entry.isScheduledForAddition()) {
1504: boolean wasDeleted = false;
1505: if (entry.getKind() == SVNNodeKind.FILE) {
1506: wasDeleted = entry.isDeleted();
1507: parent.removeFromRevisionControl(path.getName(), false,
1508: false);
1509: } else if (entry.getKind() == SVNNodeKind.DIR) {
1510: SVNEntry entryInParent = parent.getEntry(
1511: path.getName(), true);
1512: if (entryInParent != null) {
1513: wasDeleted = entryInParent.isDeleted();
1514: }
1515: if (fileType == SVNFileType.NONE) {
1516: parent.deleteEntry(path.getName());
1517: parent.saveEntries(false);
1518: } else {
1519: dir.removeFromRevisionControl("", false, false);
1520: }
1521: }
1522: reverted = true;
1523: recursive = false;
1524: if (wasDeleted) {
1525: SVNEntry entryInParent = parent.getEntry(
1526: path.getName(), true);
1527: if (entryInParent == null) {
1528: entryInParent = parent.addEntry(path.getName());
1529: }
1530: entryInParent.setKind(entry.getKind());
1531: entryInParent.setDeleted(true);
1532: parent.saveEntries(false);
1533: }
1534: } else if (entry.getSchedule() == null
1535: || entry.isScheduledForDeletion()
1536: || entry.isScheduledForReplacement()) {
1537: if (entry.getKind() == SVNNodeKind.FILE) {
1538: reverted = revert(parent, entry.getName(), entry,
1539: useCommitTimes);
1540: } else if (entry.getKind() == SVNNodeKind.DIR) {
1541: boolean notScheduled = entry.getSchedule() == null;
1542: reverted = revert(dir, dir.getThisDirName(), entry,
1543: useCommitTimes);
1544: if (entry.isScheduledForReplacement()) {
1545: recursive = true;
1546: }
1547: // check parent entry for schedule.
1548: if (reverted && parent != dir) {
1549: SVNEntry entryInParent = parent.getEntry(path
1550: .getName(), false);
1551: revert(parent, path.getName(), entryInParent,
1552: useCommitTimes);
1553: parent.saveEntries(false);
1554: } else if (notScheduled && parent != dir) {
1555: SVNEntry entryInParent = parent.getEntry(path
1556: .getName(), false);
1557: if (entryInParent.getSchedule() != null) {
1558: entryInParent.setSchedule(null);
1559: parent.saveEntries(false);
1560: }
1561: }
1562: }
1563: }
1564: if (reverted) {
1565: SVNEvent event = SVNEventFactory.createRevertedEvent(dir,
1566: entry);
1567: dispatchEvent(event);
1568: }
1569: if (recursive && entry.getKind() == SVNNodeKind.DIR) {
1570: for (Iterator entries = dir.entries(false); entries
1571: .hasNext();) {
1572: SVNEntry childEntry = (SVNEntry) entries.next();
1573: if (dir.getThisDirName().equals(childEntry.getName())) {
1574: continue;
1575: }
1576: File childPath = new File(path, childEntry.getName());
1577: reverted |= doRevert(childPath, dir, true,
1578: useCommitTimes);
1579: }
1580: }
1581: return reverted;
1582: }
1583:
1584: private boolean revert(SVNAdminArea dir, String name,
1585: SVNEntry entry, boolean useCommitTime) throws SVNException {
1586: SVNLog log = dir.getLog();
1587: boolean reverted = false;
1588: boolean revertBase = false;
1589: SVNVersionedProperties baseProperties = null;
1590: Map command = new HashMap();
1591:
1592: if (entry.isScheduledForReplacement()) {
1593: String propRevertPath = SVNAdminUtil.getPropRevertPath(
1594: name, entry.getKind(), false);
1595: File propRevertFile = dir.getFile(propRevertPath);
1596: revertBase = true;
1597: if (!propRevertFile.isFile()) {
1598: propRevertPath = SVNAdminUtil.getPropBasePath(name,
1599: entry.getKind(), false);
1600: revertBase = false;
1601: }
1602: if (dir.getFile(propRevertPath).isFile()) {
1603: baseProperties = revertBase ? dir
1604: .getRevertProperties(name) : dir
1605: .getBaseProperties(name);
1606: if (revertBase) {
1607: command.put(SVNLog.NAME_ATTR, propRevertPath);
1608: log.addCommand(SVNLog.DELETE, command, false);
1609: command.clear();
1610: }
1611: reverted = true;
1612: }
1613: }
1614: boolean reinstallWorkingFile = false;
1615: if (baseProperties == null) {
1616: if (dir.hasPropModifications(name)) {
1617: baseProperties = dir.getBaseProperties(name);
1618: SVNVersionedProperties propDiff = dir.getProperties(
1619: name).compareTo(baseProperties);
1620: Collection propNames = propDiff.getPropertyNames(null);
1621: reinstallWorkingFile = propNames
1622: .contains(SVNProperty.EXECUTABLE)
1623: || propNames.contains(SVNProperty.KEYWORDS)
1624: || propNames.contains(SVNProperty.EOL_STYLE)
1625: || propNames.contains(SVNProperty.SPECIAL)
1626: || propNames.contains(SVNProperty.NEEDS_LOCK);
1627: }
1628: }
1629: if (baseProperties != null) {
1630: // save base props both to base and working.
1631: Map newProperties = baseProperties.asMap();
1632: SVNVersionedProperties originalBaseProperties = dir
1633: .getBaseProperties(name);
1634: SVNVersionedProperties workProperties = dir
1635: .getProperties(name);
1636: if (revertBase) {
1637: originalBaseProperties.removeAll();
1638: }
1639: workProperties.removeAll();
1640: for (Iterator names = newProperties.keySet().iterator(); names
1641: .hasNext();) {
1642: String propName = (String) names.next();
1643: if (revertBase) {
1644: originalBaseProperties.setPropertyValue(propName,
1645: (String) newProperties.get(propName));
1646: }
1647: workProperties.setPropertyValue(propName,
1648: (String) newProperties.get(propName));
1649: }
1650: dir.saveVersionedProperties(log, false);
1651: reverted = true;
1652: }
1653: Map newEntryProperties = new HashMap();
1654: if (entry.isScheduledForReplacement() && entry.isCopied()) {
1655: newEntryProperties.put(SVNProperty
1656: .shortPropertyName(SVNProperty.COPIED), null);
1657: reverted = true;
1658: }
1659: if (entry.getKind() == SVNNodeKind.FILE) {
1660: if (!reinstallWorkingFile) {
1661: SVNFileType fileType = SVNFileType.getType(dir
1662: .getFile(name));
1663: if (fileType == SVNFileType.NONE) {
1664: reinstallWorkingFile = true;
1665: }
1666: }
1667: String basePath = SVNAdminUtil.getTextBasePath(name, false);
1668: if (!dir.getFile(basePath).isFile()) {
1669: SVNErrorMessage err = SVNErrorMessage.create(
1670: SVNErrorCode.IO_ERROR,
1671: "Error restoring text for ''{0}''", dir
1672: .getFile(name));
1673: SVNErrorManager.error(err);
1674: }
1675: File revertFile = dir.getFile(SVNAdminUtil
1676: .getTextRevertPath(name, false));
1677: if (revertFile.isFile()) {
1678: command.put(SVNLog.NAME_ATTR, SVNAdminUtil
1679: .getTextRevertPath(name, false));
1680: command.put(SVNLog.DEST_ATTR, SVNAdminUtil
1681: .getTextBasePath(name, false));
1682: log.addCommand(SVNLog.MOVE, command, false);
1683: command.clear();
1684: reinstallWorkingFile = true;
1685: }
1686: if (!reinstallWorkingFile) {
1687: reinstallWorkingFile = dir.hasTextModifications(name,
1688: false, false, false);
1689: }
1690: if (reinstallWorkingFile) {
1691: command.put(SVNLog.NAME_ATTR, SVNAdminUtil
1692: .getTextBasePath(name, false));
1693: command.put(SVNLog.DEST_ATTR, name);
1694: log.addCommand(SVNLog.COPY_AND_TRANSLATE, command,
1695: false);
1696: command.clear();
1697: if (useCommitTime && entry.getCommittedDate() != null) {
1698: command.put(SVNLog.NAME_ATTR, name);
1699: command.put(SVNLog.TIMESTAMP_ATTR, entry
1700: .getCommittedDate());
1701: log
1702: .addCommand(SVNLog.SET_TIMESTAMP, command,
1703: false);
1704: command.clear();
1705: } else {
1706: command.put(SVNLog.NAME_ATTR, name);
1707: command.put(SVNLog.TIMESTAMP_ATTR, SVNTimeUtil
1708: .formatDate(new Date(System
1709: .currentTimeMillis())));
1710: log
1711: .addCommand(SVNLog.SET_TIMESTAMP, command,
1712: false);
1713: command.clear();
1714: }
1715: command.put(SVNLog.NAME_ATTR, name);
1716: command.put(SVNProperty
1717: .shortPropertyName(SVNProperty.TEXT_TIME),
1718: SVNLog.WC_TIMESTAMP);
1719: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
1720: command.clear();
1721: }
1722: reverted |= reinstallWorkingFile;
1723: }
1724: if (entry.getConflictNew() != null) {
1725: command.put(SVNLog.NAME_ATTR, entry.getConflictNew());
1726: log.addCommand(SVNLog.DELETE, command, false);
1727: command.clear();
1728: newEntryProperties.put(SVNProperty
1729: .shortPropertyName(SVNProperty.CONFLICT_NEW), null);
1730: if (!reverted) {
1731: reverted |= dir.getFile(entry.getConflictNew())
1732: .exists();
1733: }
1734: }
1735: if (entry.getConflictOld() != null) {
1736: command.put(SVNLog.NAME_ATTR, entry.getConflictOld());
1737: log.addCommand(SVNLog.DELETE, command, false);
1738: command.clear();
1739: newEntryProperties.put(SVNProperty
1740: .shortPropertyName(SVNProperty.CONFLICT_OLD), null);
1741: if (!reverted) {
1742: reverted |= dir.getFile(entry.getConflictOld())
1743: .exists();
1744: }
1745: }
1746: if (entry.getConflictWorking() != null) {
1747: command.put(SVNLog.NAME_ATTR, entry.getConflictWorking());
1748: log.addCommand(SVNLog.DELETE, command, false);
1749: command.clear();
1750: newEntryProperties.put(SVNProperty
1751: .shortPropertyName(SVNProperty.CONFLICT_WRK), null);
1752: if (!reverted) {
1753: reverted |= dir.getFile(entry.getConflictWorking())
1754: .exists();
1755: }
1756: }
1757: if (entry.getPropRejectFile() != null) {
1758: command.put(SVNLog.NAME_ATTR, entry.getPropRejectFile());
1759: log.addCommand(SVNLog.DELETE, command, false);
1760: command.clear();
1761: newEntryProperties.put(SVNProperty
1762: .shortPropertyName(SVNProperty.PROP_REJECT_FILE),
1763: null);
1764: if (!reverted) {
1765: reverted |= dir.getFile(entry.getPropRejectFile())
1766: .exists();
1767: }
1768: }
1769: if (entry.getSchedule() != null) {
1770: newEntryProperties.put(SVNProperty
1771: .shortPropertyName(SVNProperty.SCHEDULE), null);
1772: reverted = true;
1773: }
1774: if (!newEntryProperties.isEmpty()) {
1775: newEntryProperties.put(SVNLog.NAME_ATTR, name);
1776: log.addCommand(SVNLog.MODIFY_ENTRY, newEntryProperties,
1777: false);
1778: }
1779: log.save();
1780: dir.runLogs();
1781: return reverted;
1782: }
1783:
1784: /**
1785: * Resolves a 'conflicted' state on a Working Copy item.
1786: *
1787: * @param path a WC item to be resolved
1788: * @param recursive <span class="javakeyword">true</span> to
1789: * descend recursively (relevant for directories) - this
1790: * will resolve the entire tree
1791: * @throws SVNException if <code>path</code> is not under version control
1792: */
1793: public void doResolve(File path, boolean recursive)
1794: throws SVNException {
1795: path = path.getAbsoluteFile();
1796: SVNWCAccess wcAccess = createWCAccess();
1797: try {
1798: wcAccess.probeOpen(path, true,
1799: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
1800: if (!recursive) {
1801: SVNEntry entry = wcAccess.getEntry(path, false);
1802: if (entry == null) {
1803: SVNErrorMessage err = SVNErrorMessage.create(
1804: SVNErrorCode.ENTRY_NOT_FOUND,
1805: "''{0}'' is not under version control",
1806: path);
1807: SVNErrorManager.error(err);
1808: }
1809: resolveEntry(wcAccess, path, entry);
1810: } else {
1811: resolveAll(wcAccess, path);
1812: }
1813: } finally {
1814: wcAccess.close();
1815: }
1816: }
1817:
1818: private void resolveEntry(SVNWCAccess wcAccess, File path,
1819: SVNEntry entry) throws SVNException {
1820: if (entry.getKind() == SVNNodeKind.DIR
1821: && !"".equals(entry.getName())) {
1822: return;
1823: }
1824: File dirPath = path;
1825: if (entry.getKind() == SVNNodeKind.FILE) {
1826: dirPath = path.getParentFile();
1827: }
1828: SVNAdminArea dir = wcAccess.retrieve(dirPath);
1829: if (dir.markResolved(entry.getName(), true, true)) {
1830: SVNEvent event = SVNEventFactory.createResolvedEvent(null,
1831: dir, entry);
1832: dispatchEvent(event);
1833: }
1834: }
1835:
1836: private void resolveAll(SVNWCAccess access, File path)
1837: throws SVNException {
1838: checkCancelled();
1839: SVNEntry entry = access.getEntry(path, false);
1840: resolveEntry(access, path, entry);
1841: if (entry.isDirectory()) {
1842: SVNAdminArea dir = access.retrieve(path);
1843: for (Iterator ents = dir.entries(false); ents.hasNext();) {
1844: SVNEntry childEntry = (SVNEntry) ents.next();
1845: if (dir.getThisDirName().equals(childEntry.getName())) {
1846: continue;
1847: }
1848: resolveAll(access, dir.getFile(childEntry.getName()));
1849: }
1850: }
1851: }
1852:
1853: /**
1854: * Locks file items in a Working Copy as well as in a repository so that
1855: * no other user can commit changes to them.
1856: *
1857: * @param paths an array of local WC file paths that should be locked
1858: * @param stealLock if <span class="javakeyword">true</span> then all existing
1859: * locks on the specified <code>paths</code> will be "stolen"
1860: * @param lockMessage an optional lock comment
1861: * @throws SVNException if one of the following is true:
1862: * <ul>
1863: * <li>a path to be locked is not under version control
1864: * <li>can not obtain a URL of a local path to lock it in
1865: * the repository - there's no such entry
1866: * <li><code>paths</code> to be locked belong to different repositories
1867: * </ul>
1868: * @see #doLock(SVNURL[], boolean, String)
1869: */
1870: public void doLock(File[] paths, boolean stealLock,
1871: String lockMessage) throws SVNException {
1872: final Map entriesMap = new HashMap();
1873: Map pathsRevisionsMap = new HashMap();
1874: final SVNWCAccess wcAccess = createWCAccess();
1875: try {
1876: final SVNURL topURL = collectLockInfo(wcAccess, paths,
1877: entriesMap, pathsRevisionsMap, true, stealLock);
1878: SVNRepository repository = createRepository(topURL, true);
1879: final SVNURL rootURL = repository.getRepositoryRoot(true);
1880:
1881: repository.lock(pathsRevisionsMap, lockMessage, stealLock,
1882: new ISVNLockHandler() {
1883: public void handleLock(String path,
1884: SVNLock lock, SVNErrorMessage error)
1885: throws SVNException {
1886: SVNURL fullURL = rootURL.appendPath(path,
1887: false);
1888: LockInfo lockInfo = (LockInfo) entriesMap
1889: .get(fullURL);
1890: SVNAdminArea dir = wcAccess
1891: .probeRetrieve(lockInfo.myFile);
1892: if (error == null) {
1893: SVNEntry entry = wcAccess.getEntry(
1894: lockInfo.myFile, false);
1895: if (entry == null) {
1896: SVNErrorMessage err = SVNErrorMessage
1897: .create(
1898: SVNErrorCode.UNVERSIONED_RESOURCE,
1899: "''{0}'' is not under version control",
1900: lockInfo.myFile);
1901: SVNErrorManager.error(err);
1902: }
1903: entry.setLockToken(lock.getID());
1904: entry.setLockComment(lock.getComment());
1905: entry.setLockOwner(lock.getOwner());
1906: entry.setLockCreationDate(SVNTimeUtil
1907: .formatDate(lock
1908: .getCreationDate()));
1909: // get properties and values.
1910: SVNVersionedProperties props = dir
1911: .getProperties(entry.getName());
1912:
1913: if (props
1914: .getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
1915: SVNFileUtil.setReadonly(dir
1916: .getFile(entry.getName()),
1917: false);
1918: }
1919: SVNFileUtil
1920: .setExecutable(
1921: dir.getFile(entry
1922: .getName()),
1923: props
1924: .getPropertyValue(SVNProperty.EXECUTABLE) != null);
1925: dir.saveEntries(false);
1926: handleEvent(SVNEventFactory
1927: .createLockEvent(dir, entry
1928: .getName(),
1929: SVNEventAction.LOCKED,
1930: lock, null),
1931: ISVNEventHandler.UNKNOWN);
1932: } else {
1933: handleEvent(
1934: SVNEventFactory
1935: .createLockEvent(
1936: dir,
1937: lockInfo.myFile
1938: .getName(),
1939: SVNEventAction.LOCK_FAILED,
1940: lock, error),
1941: ISVNEventHandler.UNKNOWN);
1942: }
1943: }
1944:
1945: public void handleUnlock(String path,
1946: SVNLock lock, SVNErrorMessage error) {
1947: }
1948: });
1949: } finally {
1950: wcAccess.close();
1951: }
1952: }
1953:
1954: /**
1955: * Locks file items in a repository so that no other user can commit
1956: * changes to them.
1957: *
1958: * @param urls an array of URLs to be locked
1959: * @param stealLock if <span class="javakeyword">true</span> then all existing
1960: * locks on the specified <code>urls</code> will be "stolen"
1961: * @param lockMessage an optional lock comment
1962: * @throws SVNException
1963: * @see #doLock(File[], boolean, String)
1964: */
1965: public void doLock(SVNURL[] urls, boolean stealLock,
1966: String lockMessage) throws SVNException {
1967: Collection paths = new HashSet();
1968: SVNURL topURL = SVNURLUtil.condenceURLs(urls, paths, false);
1969: if (paths.isEmpty()) {
1970: paths.add("");
1971: }
1972: Map pathsToRevisions = new HashMap();
1973: for (Iterator p = paths.iterator(); p.hasNext();) {
1974: String path = (String) p.next();
1975: path = SVNEncodingUtil.uriDecode(path);
1976: pathsToRevisions.put(path, null);
1977: }
1978: checkCancelled();
1979: SVNRepository repository = createRepository(topURL, true);
1980: repository.lock(pathsToRevisions, lockMessage, stealLock,
1981: new ISVNLockHandler() {
1982: public void handleLock(String path, SVNLock lock,
1983: SVNErrorMessage error) throws SVNException {
1984: if (error != null) {
1985: handleEvent(SVNEventFactory
1986: .createLockEvent(null, path,
1987: SVNEventAction.LOCK_FAILED,
1988: lock, error),
1989: ISVNEventHandler.UNKNOWN);
1990: } else {
1991: handleEvent(SVNEventFactory
1992: .createLockEvent(null, path,
1993: SVNEventAction.LOCKED,
1994: lock, null),
1995: ISVNEventHandler.UNKNOWN);
1996: }
1997: }
1998:
1999: public void handleUnlock(String path, SVNLock lock,
2000: SVNErrorMessage error) throws SVNException {
2001: }
2002:
2003: });
2004: }
2005:
2006: /**
2007: * Unlocks file items in a Working Copy as well as in a repository.
2008: *
2009: * @param paths an array of local WC file paths that should be unlocked
2010: * @param breakLock if <span class="javakeyword">true</span> and there are locks
2011: * that belong to different users then those locks will be also
2012: * unlocked - that is "broken"
2013: * @throws SVNException if one of the following is true:
2014: * <ul>
2015: * <li>a path is not under version control
2016: * <li>can not obtain a URL of a local path to unlock it in
2017: * the repository - there's no such entry
2018: * <li>if a path is not locked in the Working Copy
2019: * and <code>breakLock</code> is <span class="javakeyword">false</span>
2020: * <li><code>paths</code> to be unlocked belong to different repositories
2021: * </ul>
2022: * @see #doUnlock(SVNURL[], boolean)
2023: */
2024: public void doUnlock(File[] paths, boolean breakLock)
2025: throws SVNException {
2026: final Map entriesMap = new HashMap();
2027: Map pathsTokensMap = new HashMap();
2028: final SVNWCAccess wcAccess = createWCAccess();
2029: try {
2030: final SVNURL topURL = collectLockInfo(wcAccess, paths,
2031: entriesMap, pathsTokensMap, false, breakLock);
2032: checkCancelled();
2033: SVNRepository repository = createRepository(topURL, true);
2034: final SVNURL rootURL = repository.getRepositoryRoot(true);
2035: repository.unlock(pathsTokensMap, breakLock,
2036: new ISVNLockHandler() {
2037: public void handleLock(String path,
2038: SVNLock lock, SVNErrorMessage error)
2039: throws SVNException {
2040: }
2041:
2042: public void handleUnlock(String path,
2043: SVNLock lock, SVNErrorMessage error)
2044: throws SVNException {
2045: SVNURL fullURL = rootURL.appendPath(path,
2046: false);
2047: LockInfo lockInfo = (LockInfo) entriesMap
2048: .get(fullURL);
2049: SVNEventAction action = null;
2050: SVNAdminArea dir = wcAccess
2051: .probeRetrieve(lockInfo.myFile);
2052: if (error == null
2053: || (error != null && error
2054: .getErrorCode() != SVNErrorCode.FS_LOCK_OWNER_MISMATCH)) {
2055: SVNEntry entry = wcAccess.getEntry(
2056: lockInfo.myFile, false);
2057: if (entry == null) {
2058: SVNErrorMessage err = SVNErrorMessage
2059: .create(
2060: SVNErrorCode.UNVERSIONED_RESOURCE,
2061: "''{0}'' is not under version control",
2062: lockInfo.myFile);
2063: SVNErrorManager.error(err);
2064: }
2065: entry.setLockToken(null);
2066: entry.setLockComment(null);
2067: entry.setLockOwner(null);
2068: entry.setLockCreationDate(null);
2069:
2070: SVNVersionedProperties props = dir
2071: .getProperties(entry.getName());
2072:
2073: if (props
2074: .getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
2075: SVNFileUtil.setReadonly(dir
2076: .getFile(entry.getName()),
2077: true);
2078: }
2079: dir.saveEntries(false);
2080: action = SVNEventAction.UNLOCKED;
2081: }
2082: if (error != null) {
2083: action = SVNEventAction.UNLOCK_FAILED;
2084: }
2085: if (action != null) {
2086: handleEvent(SVNEventFactory
2087: .createLockEvent(dir,
2088: lockInfo.myFile
2089: .getName(),
2090: action, lock, error),
2091: ISVNEventHandler.UNKNOWN);
2092: }
2093: }
2094: });
2095: } finally {
2096: wcAccess.close();
2097: }
2098: }
2099:
2100: /**
2101: * Unlocks file items in a repository.
2102: *
2103: * @param urls an array of URLs that should be unlocked
2104: * @param breakLock if <span class="javakeyword">true</span> and there are locks
2105: * that belong to different users then those locks will be also
2106: * unlocked - that is "broken"
2107: * @throws SVNException
2108: * @see #doUnlock(File[], boolean)
2109: */
2110: public void doUnlock(SVNURL[] urls, boolean breakLock)
2111: throws SVNException {
2112: Collection paths = new HashSet();
2113: SVNURL topURL = SVNURLUtil.condenceURLs(urls, paths, false);
2114: if (paths.isEmpty()) {
2115: paths.add("");
2116: }
2117: Map pathsToTokens = new HashMap();
2118: for (Iterator p = paths.iterator(); p.hasNext();) {
2119: String path = (String) p.next();
2120: path = SVNEncodingUtil.uriDecode(path);
2121: pathsToTokens.put(path, null);
2122: }
2123:
2124: checkCancelled();
2125: SVNRepository repository = createRepository(topURL, true);
2126: if (!breakLock) {
2127: pathsToTokens = fetchLockTokens(repository, pathsToTokens);
2128: }
2129: repository.unlock(pathsToTokens, breakLock,
2130: new ISVNLockHandler() {
2131: public void handleLock(String path, SVNLock lock,
2132: SVNErrorMessage error) throws SVNException {
2133: }
2134:
2135: public void handleUnlock(String path, SVNLock lock,
2136: SVNErrorMessage error) throws SVNException {
2137: if (error != null) {
2138: handleEvent(
2139: SVNEventFactory
2140: .createLockEvent(
2141: null,
2142: path,
2143: SVNEventAction.UNLOCK_FAILED,
2144: null, error),
2145: ISVNEventHandler.UNKNOWN);
2146: } else {
2147: handleEvent(SVNEventFactory
2148: .createLockEvent(null, path,
2149: SVNEventAction.UNLOCKED,
2150: null, null),
2151: ISVNEventHandler.UNKNOWN);
2152: }
2153: }
2154: });
2155: }
2156:
2157: private SVNURL collectLockInfo(SVNWCAccess wcAccess, File[] files,
2158: Map lockInfo, Map lockPaths, boolean lock, boolean stealLock)
2159: throws SVNException {
2160: String[] paths = new String[files.length];
2161: for (int i = 0; i < files.length; i++) {
2162: paths[i] = files[i].getAbsolutePath();
2163: paths[i] = paths[i].replace(File.separatorChar, '/');
2164: }
2165: Collection condencedPaths = new ArrayList();
2166: String commonParentPath = SVNPathUtil.condencePaths(paths,
2167: condencedPaths, false);
2168: if (condencedPaths.isEmpty()) {
2169: condencedPaths.add(SVNPathUtil.tail(commonParentPath));
2170: commonParentPath = SVNPathUtil.removeTail(commonParentPath);
2171: }
2172: if (commonParentPath == null || "".equals(commonParentPath)) {
2173: SVNErrorMessage err = SVNErrorMessage
2174: .create(SVNErrorCode.UNSUPPORTED_FEATURE,
2175: "No common parent found, unable to operate on dijoint arguments");
2176: SVNErrorManager.error(err);
2177: }
2178: paths = (String[]) condencedPaths
2179: .toArray(new String[condencedPaths.size()]);
2180: int depth = 0;
2181: for (int i = 0; i < paths.length; i++) {
2182: int segments = SVNPathUtil.getSegmentsCount(paths[i]);
2183: if (depth < segments) {
2184: depth = segments;
2185: }
2186: }
2187: wcAccess.probeOpen(
2188: new File(commonParentPath).getAbsoluteFile(), true,
2189: depth);
2190: for (int i = 0; i < paths.length; i++) {
2191: File file = new File(commonParentPath, paths[i]);
2192: SVNEntry entry = wcAccess.getEntry(file, false);
2193: if (entry == null) {
2194: SVNErrorMessage err = SVNErrorMessage.create(
2195: SVNErrorCode.UNVERSIONED_RESOURCE,
2196: "''{0}'' is not under version control", file
2197: .getName());
2198: SVNErrorManager.error(err);
2199: }
2200: if (entry.getURL() == null) {
2201: SVNErrorMessage err = SVNErrorMessage.create(
2202: SVNErrorCode.ENTRY_MISSING_URL,
2203: "''{0}'' has no URL", file);
2204: SVNErrorManager.error(err);
2205: }
2206: if (lock) {
2207: SVNRevision revision = stealLock ? SVNRevision.UNDEFINED
2208: : SVNRevision.create(entry.getRevision());
2209: lockInfo.put(entry.getSVNURL(), new LockInfo(file,
2210: revision));
2211: } else {
2212: if (!stealLock && entry.getLockToken() == null) {
2213: SVNErrorMessage err = SVNErrorMessage
2214: .create(
2215: SVNErrorCode.CLIENT_MISSING_LOCK_TOKEN,
2216: "''{0}'' is not locked in this working copy",
2217: file);
2218: SVNErrorManager.error(err);
2219: }
2220: lockInfo.put(entry.getSVNURL(), new LockInfo(file,
2221: entry.getLockToken()));
2222: }
2223: }
2224: checkCancelled();
2225: SVNURL[] urls = (SVNURL[]) lockInfo.keySet().toArray(
2226: new SVNURL[lockInfo.size()]);
2227: Collection urlPaths = new HashSet();
2228: final SVNURL topURL = SVNURLUtil.condenceURLs(urls, urlPaths,
2229: false);
2230: if (urlPaths.isEmpty()) {
2231: urlPaths.add("");
2232: }
2233: if (topURL == null) {
2234: SVNErrorMessage err = SVNErrorMessage
2235: .create(SVNErrorCode.UNSUPPORTED_FEATURE,
2236: "Unable to lock/unlock across multiple repositories");
2237: SVNErrorManager.error(err);
2238: }
2239: // prepare Map for SVNRepository (decoded path : revision/lock token).
2240: for (Iterator encodedPaths = urlPaths.iterator(); encodedPaths
2241: .hasNext();) {
2242: String encodedPath = (String) encodedPaths.next();
2243: // get LockInfo for it.
2244: SVNURL fullURL = topURL.appendPath(encodedPath, true);
2245: LockInfo info = (LockInfo) lockInfo.get(fullURL);
2246: encodedPath = SVNEncodingUtil.uriDecode(encodedPath);
2247: if (lock) {
2248: if (info.myRevision == SVNRevision.UNDEFINED) {
2249: lockPaths.put(encodedPath, null);
2250: } else {
2251: lockPaths.put(encodedPath, new Long(info.myRevision
2252: .getNumber()));
2253: }
2254: } else {
2255: lockPaths.put(encodedPath, info.myToken);
2256: }
2257: }
2258: return topURL;
2259: }
2260:
2261: /**
2262: * Collects information about Working Copy item(s) and passes it to an
2263: * info handler.
2264: *
2265: * <p>
2266: * If <code>revision</code> is valid and not local,
2267: * then information will be collected on remote items (that is taken from
2268: * a repository). Otherwise information is gathered on local items not
2269: * accessing a repository.
2270: *
2271: * @param path a WC item on which info should be obtained
2272: * @param revision a target revision
2273: * @param recursive <span class="javakeyword">true</span> to
2274: * descend recursively (relevant for directories)
2275: * @param handler a caller's info handler
2276: * @throws SVNException if one of the following is true:
2277: * <ul>
2278: * <li><code>path</code> is not under version control
2279: * <li>can not obtain a URL corresponding to <code>path</code> to
2280: * get its information from the repository - there's no such entry
2281: * <li>if a remote info: <code>path</code> is an item that does not exist in
2282: * the specified <code>revision</code>
2283: * </ul>
2284: * @see #doInfo(File, SVNRevision)
2285: * @see #doInfo(SVNURL, SVNRevision, SVNRevision, boolean, ISVNInfoHandler)
2286: */
2287: public void doInfo(File path, SVNRevision revision,
2288: boolean recursive, ISVNInfoHandler handler)
2289: throws SVNException {
2290: doInfo(path, SVNRevision.UNDEFINED, revision, recursive,
2291: handler);
2292: }
2293:
2294: /**
2295: * Collects information about Working Copy item(s) and passes it to an
2296: * info handler.
2297: *
2298: * <p>
2299: * If <code>revision</code> & <code>pegRevision</code> are valid and not
2300: * local, then information will be collected
2301: * on remote items (that is taken from a repository). Otherwise information
2302: * is gathered on local items not accessing a repository.
2303: *
2304: * @param path a WC item on which info should be obtained
2305: * @param pegRevision a revision in which <code>path</code> is first
2306: * looked up
2307: * @param revision a target revision
2308: * @param recursive <span class="javakeyword">true</span> to
2309: * descend recursively (relevant for directories)
2310: * @param handler a caller's info handler
2311: * @throws SVNException if one of the following is true:
2312: * <ul>
2313: * <li><code>path</code> is not under version control
2314: * <li>can not obtain a URL corresponding to <code>path</code> to
2315: * get its information from the repository - there's no such entry
2316: * <li>if a remote info: <code>path</code> is an item that does not exist in
2317: * the specified <code>revision</code>
2318: * </ul>
2319: * @see #doInfo(File, SVNRevision)
2320: * @see #doInfo(File, SVNRevision, boolean, ISVNInfoHandler)
2321: */
2322: public void doInfo(File path, SVNRevision pegRevision,
2323: SVNRevision revision, boolean recursive,
2324: ISVNInfoHandler handler) throws SVNException {
2325: if (handler == null) {
2326: return;
2327: }
2328: boolean local = (revision == null || !revision.isValid() || revision
2329: .isLocal())
2330: && (pegRevision == null || !pegRevision.isValid() || pegRevision
2331: .isLocal());
2332:
2333: if (!local) {
2334: SVNWCAccess wcAccess = createWCAccess();
2335: SVNRevision wcRevision = null;
2336: SVNURL url = null;
2337: try {
2338: wcAccess.probeOpen(path, false, 0);
2339: SVNEntry entry = wcAccess.getEntry(path, false);
2340: if (entry == null) {
2341: SVNErrorMessage err = SVNErrorMessage.create(
2342: SVNErrorCode.UNVERSIONED_RESOURCE,
2343: "''{0}'' is not under version control",
2344: path);
2345: SVNErrorManager.error(err);
2346: }
2347: url = entry.getSVNURL();
2348: if (url == null) {
2349: SVNErrorMessage err = SVNErrorMessage.create(
2350: SVNErrorCode.ENTRY_MISSING_URL,
2351: "''{0}'' has no URL", path);
2352: SVNErrorManager.error(err);
2353: }
2354: wcRevision = SVNRevision.create(entry.getRevision());
2355: } finally {
2356: wcAccess.close();
2357: }
2358: doInfo(url,
2359: pegRevision == null || !pegRevision.isValid()
2360: || pegRevision.isLocal() ? wcRevision
2361: : pegRevision, revision, recursive, handler);
2362: return;
2363: }
2364: SVNWCAccess wcAccess = createWCAccess();
2365: try {
2366: wcAccess.probeOpen(path, false,
2367: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
2368: SVNEntry entry = wcAccess.getEntry(path, false);
2369: if (entry == null) {
2370: SVNErrorMessage err = SVNErrorMessage.create(
2371: SVNErrorCode.UNVERSIONED_RESOURCE,
2372: "Cannot read entry for ''{0}''", path);
2373: SVNErrorManager.error(err);
2374: }
2375: if (entry.isFile()) {
2376: reportEntry(path, entry, handler);
2377: } else if (entry.isDirectory()) {
2378: if (recursive) {
2379: reportAllEntries(wcAccess, path, handler);
2380: } else {
2381: reportEntry(path, entry, handler);
2382: }
2383: }
2384: } finally {
2385: wcAccess.close();
2386: }
2387: }
2388:
2389: private void reportEntry(File path, SVNEntry entry,
2390: ISVNInfoHandler handler) throws SVNException {
2391: if (entry.isDirectory() && !"".equals(entry.getName())) {
2392: return;
2393: }
2394: handler.handleInfo(SVNInfo.createInfo(path, entry));
2395: }
2396:
2397: private void reportAllEntries(SVNWCAccess wcAccess, File path,
2398: ISVNInfoHandler handler) throws SVNException {
2399: SVNEntry entry = wcAccess.getEntry(path, false);
2400: reportEntry(path, entry, handler);
2401: if (entry.isDirectory()) {
2402: SVNAdminArea dir = wcAccess.retrieve(path);
2403: for (Iterator entries = dir.entries(false); entries
2404: .hasNext();) {
2405: SVNEntry childEntry = (SVNEntry) entries.next();
2406: if (dir.getThisDirName().equals(childEntry.getName())) {
2407: continue;
2408: }
2409: File childPath = dir.getFile(childEntry.getName());
2410: if (childEntry.isDirectory()) {
2411: reportAllEntries(wcAccess, childPath, handler);
2412: }
2413: reportEntry(childPath, childEntry, handler);
2414: }
2415: }
2416: }
2417:
2418: /**
2419: * Collects information about item(s) in a repository and passes it to
2420: * an info handler.
2421: *
2422: *
2423: * @param url a URL of an item which information is to be
2424: * obtained and processed
2425: * @param pegRevision a revision in which the item is first looked up
2426: * @param revision a target revision
2427: * @param recursive <span class="javakeyword">true</span> to
2428: * descend recursively (relevant for directories)
2429: * @param handler a caller's info handler
2430: * @throws SVNException if <code>url</code> is an item that does not exist in
2431: * the specified <code>revision</code>
2432: * @see #doInfo(SVNURL, SVNRevision, SVNRevision)
2433: * @see #doInfo(File, SVNRevision, boolean, ISVNInfoHandler)
2434: */
2435: public void doInfo(SVNURL url, SVNRevision pegRevision,
2436: SVNRevision revision, boolean recursive,
2437: ISVNInfoHandler handler) throws SVNException {
2438: if (revision == null || !revision.isValid()) {
2439: revision = SVNRevision.HEAD;
2440: }
2441: if (pegRevision == null || !pegRevision.isValid()) {
2442: pegRevision = revision;
2443: }
2444:
2445: SVNRepository repos = createRepository(url, null, pegRevision,
2446: revision);
2447: ;
2448: url = repos.getLocation();
2449: long revNum = getRevisionNumber(revision, repos, null);
2450: SVNDirEntry rootEntry = null;
2451: try {
2452: rootEntry = repos.info("", revNum);
2453: } catch (SVNException e) {
2454: if (e.getErrorMessage() != null
2455: && e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED) {
2456: // for svnserve older then 1.2.0
2457: if (repos.getLocation().equals(
2458: repos.getRepositoryRoot(true))) {
2459: rootEntry = new SVNDirEntry(url, "",
2460: SVNNodeKind.DIR, -1, false, -1, null, null);
2461: } else {
2462: String name = SVNPathUtil.tail(url.getPath());
2463: SVNURL location = repos.getLocation();
2464: repos.setLocation(location.removePathTail(), false);
2465: Collection dirEntries = repos.getDir("", revNum,
2466: null, (Collection) null);
2467: for (Iterator ents = dirEntries.iterator(); ents
2468: .hasNext();) {
2469: SVNDirEntry dirEntry = (SVNDirEntry) ents
2470: .next();
2471: // dir entry name may differ from 'name', due to renames...
2472: if (name.equals(dirEntry.getName())) {
2473: rootEntry = dirEntry;
2474: break;
2475: }
2476: }
2477: repos.setLocation(location, false);
2478: }
2479: } else {
2480: throw e;
2481: }
2482: }
2483: if (rootEntry == null
2484: || rootEntry.getKind() == SVNNodeKind.NONE) {
2485: SVNErrorMessage err = SVNErrorMessage.create(
2486: SVNErrorCode.RA_ILLEGAL_URL,
2487: "URL ''{0}'' non-existent in revision ''{1}''",
2488: new Object[] { url, new Long(revNum) });
2489: SVNErrorManager.error(err);
2490: }
2491: SVNURL reposRoot = repos.getRepositoryRoot(true);
2492: String reposUUID = repos.getRepositoryUUID(true);
2493: // 1. get locks for this dir and below (only for dir).
2494: // and only when pegRev is HEAD.
2495: SVNLock[] locks = null;
2496: if (pegRevision == SVNRevision.HEAD
2497: && rootEntry.getKind() == SVNNodeKind.DIR) {
2498: try {
2499: locks = repos.getLocks("");
2500: } catch (SVNException e) {
2501: // may be not supported.
2502: if (e.getErrorMessage() != null
2503: && e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED) {
2504: locks = new SVNLock[0];
2505: } else {
2506: throw e;
2507: }
2508: }
2509: }
2510: locks = locks == null ? new SVNLock[0] : locks;
2511: Map locksMap = new HashMap();
2512: for (int i = 0; i < locks.length; i++) {
2513: SVNLock lock = locks[i];
2514: locksMap.put(lock.getPath(), lock);
2515: }
2516: // 2. add lock for this entry, only when it is 'related' to head (and is a file).
2517: if (rootEntry.getKind() == SVNNodeKind.FILE) {
2518: try {
2519: SVNRepositoryLocation[] locations = getLocations(url,
2520: null, null, revision, SVNRevision.HEAD,
2521: SVNRevision.UNDEFINED);
2522: if (locations != null && locations.length > 0) {
2523: SVNURL headURL = locations[0].getURL();
2524: if (headURL.equals(url)) {
2525: // get lock for this item (@headURL).
2526: try {
2527: SVNLock lock = repos.getLock("");
2528: if (lock != null) {
2529: locksMap.put(lock.getPath(), lock);
2530: }
2531: } catch (SVNException e) {
2532: if (!(e.getErrorMessage() != null && e
2533: .getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED)) {
2534: throw e;
2535: }
2536: }
2537: }
2538: }
2539: } catch (SVNException e) {
2540: SVNErrorCode code = e.getErrorMessage().getErrorCode();
2541: if (code != SVNErrorCode.FS_NOT_FOUND
2542: && code != SVNErrorCode.CLIENT_UNRELATED_RESOURCES) {
2543: throw e;
2544: }
2545: }
2546: }
2547:
2548: String fullPath = url.getPath();
2549: String rootPath = fullPath.substring(reposRoot.getPath()
2550: .length());
2551: if (!rootPath.startsWith("/")) {
2552: rootPath = "/" + rootPath;
2553: }
2554: collectInfo(repos, rootEntry, SVNRevision.create(revNum),
2555: rootPath, reposRoot, reposUUID, url, locksMap,
2556: recursive, handler);
2557: }
2558:
2559: /**
2560: * Returns the current Working Copy min- and max- revisions as well as
2561: * changes and switch status within a single string.
2562: *
2563: * <p>
2564: * A return string has a form of <code>"minR[:maxR][M][S]"</code> where:
2565: * <ul>
2566: * <li><code>minR</code> - is the smallest revision number met in the
2567: * Working Copy
2568: * <li><code>maxR</code> - is the biggest revision number met in the
2569: * Working Copy; appears only if there are different revision in the
2570: * Working Copy
2571: * <li><code>M</code> - appears only if there're local edits to the
2572: * Working Copy - that means 'Modified'
2573: * <li><code>S</code> - appears only if the Working Copy is switched
2574: * against a different URL
2575: * </ul>
2576: * If <code>path</code> is a directory - this method recursively descends
2577: * into the Working Copy, collects and processes local information.
2578: *
2579: * @param path a local path
2580: * @param trailURL optional: if not <span class="javakeyword">null</span>
2581: * specifies the name of the item that should be met
2582: * in the URL corresponding to the repository location
2583: * of the <code>path</code>; if that URL ends with something
2584: * different than this optional parameter - the Working
2585: * Copy will be considered "switched"
2586: * @return brief info on the Working Copy or the string
2587: * "exported" if <code>path</code> is a clean directory
2588: * @throws SVNException if <code>path</code> is neither versioned nor
2589: * even exported
2590: */
2591: public String doGetWorkingCopyID(final File path, String trailURL)
2592: throws SVNException {
2593: return doGetWorkingCopyID(path, trailURL, false);
2594: }
2595:
2596: public String doGetWorkingCopyID(final File path, String trailURL,
2597: final boolean committed) throws SVNException {
2598: SVNWCAccess wcAccess = createWCAccess();
2599: try {
2600: wcAccess.open(path, false, 0);
2601: } catch (SVNException e) {
2602: SVNFileType pathType = SVNFileType.getType(path);
2603: if (pathType == SVNFileType.DIRECTORY) {
2604: return "exported";
2605: } else if (pathType == SVNFileType.NONE) {
2606: return null;
2607: }
2608: return "'" + path + "' is not versioned and not exported";
2609: } finally {
2610: wcAccess.close();
2611: }
2612: SVNStatusClient statusClient = new SVNStatusClient(
2613: (ISVNAuthenticationManager) null, getOptions());
2614: statusClient.setIgnoreExternals(true);
2615: final long[] maxRevision = new long[1];
2616: final long[] minRevision = new long[] { -1 };
2617: final boolean[] switched = new boolean[2];
2618: final String[] wcURL = new String[1];
2619: statusClient.doStatus(path, true, false, true, false, false,
2620: new ISVNStatusHandler() {
2621: public void handleStatus(SVNStatus status) {
2622: if (status.getEntryProperties() == null
2623: || status.getEntryProperties()
2624: .isEmpty()) {
2625: return;
2626: }
2627: if (status.getContentsStatus() != SVNStatusType.STATUS_ADDED) {
2628: SVNRevision revision = committed ? status
2629: .getCommittedRevision() : status
2630: .getRevision();
2631: if (revision != null) {
2632: if (minRevision[0] < 0
2633: || minRevision[0] > revision
2634: .getNumber()) {
2635: minRevision[0] = revision
2636: .getNumber();
2637: }
2638: maxRevision[0] = Math.max(
2639: maxRevision[0], revision
2640: .getNumber());
2641: }
2642: }
2643: switched[0] |= status.isSwitched();
2644: switched[1] |= status.getContentsStatus() != SVNStatusType.STATUS_NORMAL;
2645: switched[1] |= status.getPropertiesStatus() != SVNStatusType.STATUS_NORMAL
2646: && status.getPropertiesStatus() != SVNStatusType.STATUS_NONE;
2647: if (wcURL[0] == null
2648: && status.getFile() != null
2649: && status.getFile().equals(path)
2650: && status.getURL() != null) {
2651: wcURL[0] = status.getURL().toString();
2652: }
2653: }
2654: });
2655: if (!switched[0] && trailURL != null) {
2656: if (wcURL[0] == null) {
2657: switched[0] = true;
2658: } else {
2659: switched[0] = !wcURL[0].endsWith(trailURL);
2660: }
2661: }
2662: StringBuffer id = new StringBuffer();
2663: id.append(minRevision[0]);
2664: if (minRevision[0] != maxRevision[0]) {
2665: id.append(":").append(maxRevision[0]);
2666: }
2667: if (switched[1]) {
2668: id.append("M");
2669: }
2670: if (switched[0]) {
2671: id.append("S");
2672: }
2673: return id.toString();
2674: }
2675:
2676: /**
2677: * Collects and returns information on a single Working Copy item.
2678: *
2679: * <p>
2680: * If <code>revision</code> is valid and not {@link SVNRevision#WORKING WORKING}
2681: * then information will be collected on remote items (that is taken from
2682: * a repository). Otherwise information is gathered on local items not
2683: * accessing a repository.
2684: *
2685: * @param path a WC item on which info should be obtained
2686: * @param revision a target revision
2687: * @return collected info
2688: * @throws SVNException if one of the following is true:
2689: * <ul>
2690: * <li><code>path</code> is not under version control
2691: * <li>can not obtain a URL corresponding to <code>path</code> to
2692: * get its information from the repository - there's no such entry
2693: * <li>if a remote info: <code>path</code> is an item that does not exist in
2694: * the specified <code>revision</code>
2695: * </ul>
2696: * @see #doInfo(File, SVNRevision, boolean, ISVNInfoHandler)
2697: * @see #doInfo(SVNURL, SVNRevision, SVNRevision)
2698: */
2699: public SVNInfo doInfo(File path, SVNRevision revision)
2700: throws SVNException {
2701: final SVNInfo[] result = new SVNInfo[1];
2702: doInfo(path, revision, false, new ISVNInfoHandler() {
2703: public void handleInfo(SVNInfo info) {
2704: if (result[0] == null) {
2705: result[0] = info;
2706: }
2707: }
2708: });
2709: return result[0];
2710: }
2711:
2712: /**
2713: * Collects and returns information on a single item in a repository.
2714: *
2715: * @param url a URL of an item which information is to be
2716: * obtained
2717: * @param pegRevision a revision in which the item is first looked up
2718: * @param revision a target revision
2719: * @return collected info
2720: * @throws SVNException if <code>url</code> is an item that does not exist in
2721: * the specified <code>revision</code>
2722: * @see #doInfo(SVNURL, SVNRevision, SVNRevision, boolean, ISVNInfoHandler)
2723: * @see #doInfo(File, SVNRevision)
2724: */
2725: public SVNInfo doInfo(SVNURL url, SVNRevision pegRevision,
2726: SVNRevision revision) throws SVNException {
2727: final SVNInfo[] result = new SVNInfo[1];
2728: doInfo(url, pegRevision, revision, false,
2729: new ISVNInfoHandler() {
2730: public void handleInfo(SVNInfo info) {
2731: if (result[0] == null) {
2732: result[0] = info;
2733: }
2734: }
2735: });
2736: return result[0];
2737: }
2738:
2739: public void doCleanupWCProperties(File directory)
2740: throws SVNException {
2741: SVNWCAccess wcAccess = SVNWCAccess.newInstance(this );
2742: try {
2743: SVNAdminArea dir = wcAccess.open(directory, true, true, -1);
2744: if (dir != null) {
2745: SVNPropertiesManager
2746: .deleteWCProperties(dir, null, true);
2747: }
2748: } finally {
2749: wcAccess.close();
2750: }
2751: }
2752:
2753: private void collectInfo(SVNRepository repos, SVNDirEntry entry,
2754: SVNRevision rev, String path, SVNURL root, String uuid,
2755: SVNURL url, Map locks, boolean recursive,
2756: ISVNInfoHandler handler) throws SVNException {
2757: checkCancelled();
2758: String displayPath = repos.getFullPath(path);
2759: displayPath = displayPath.substring(repos.getLocation()
2760: .getPath().length());
2761: if ("".equals(displayPath) || "/".equals(displayPath)) {
2762: displayPath = path;
2763: }
2764: handler.handleInfo(SVNInfo.createInfo(displayPath, root, uuid,
2765: url, rev, entry, (SVNLock) locks.get(path)));
2766: if (entry.getKind() == SVNNodeKind.DIR && recursive) {
2767: Collection children = repos.getDir(path, rev.getNumber(),
2768: null, new ArrayList());
2769: for (Iterator ents = children.iterator(); ents.hasNext();) {
2770: SVNDirEntry child = (SVNDirEntry) ents.next();
2771: SVNURL childURL = url
2772: .appendPath(child.getName(), false);
2773: collectInfo(repos, child, rev, SVNPathUtil.append(path,
2774: child.getName()), root, uuid, childURL, locks,
2775: recursive, handler);
2776: }
2777: }
2778: }
2779:
2780: private void doGetRemoteProperty(SVNURL url, String path,
2781: SVNRepository repos, String propName, SVNRevision rev,
2782: boolean recursive, ISVNPropertyHandler handler)
2783: throws SVNException {
2784: checkCancelled();
2785: long revNumber = getRevisionNumber(rev, repos, null);
2786: SVNNodeKind kind = repos.checkPath(path, revNumber);
2787: Map props = new HashMap();
2788: if (kind == SVNNodeKind.DIR) {
2789: Collection children = repos.getDir(path, revNumber, props,
2790: recursive ? new ArrayList() : null);
2791: if (propName != null) {
2792: String value = (String) props.get(propName);
2793: if (value != null) {
2794: handler.handleProperty(url, new SVNPropertyData(
2795: propName, value));
2796: }
2797: } else {
2798: for (Iterator names = props.keySet().iterator(); names
2799: .hasNext();) {
2800: String name = (String) names.next();
2801: if (name.startsWith(SVNProperty.SVN_ENTRY_PREFIX)
2802: || name
2803: .startsWith(SVNProperty.SVN_WC_PREFIX)) {
2804: continue;
2805: }
2806: String value = (String) props.get(name);
2807: handler.handleProperty(url, new SVNPropertyData(
2808: name, value));
2809: }
2810: }
2811: if (recursive) {
2812: checkCancelled();
2813: for (Iterator entries = children.iterator(); entries
2814: .hasNext();) {
2815: SVNDirEntry child = (SVNDirEntry) entries.next();
2816: SVNURL childURL = url.appendPath(child.getName(),
2817: false);
2818: String childPath = "".equals(path) ? child
2819: .getName() : SVNPathUtil.append(path, child
2820: .getName());
2821: doGetRemoteProperty(childURL, childPath, repos,
2822: propName, rev, recursive, handler);
2823: }
2824: }
2825: } else if (kind == SVNNodeKind.FILE) {
2826: repos.getFile(path, revNumber, props, null);
2827: if (propName != null) {
2828: String value = (String) props.get(propName);
2829: if (value != null) {
2830: handler.handleProperty(url, new SVNPropertyData(
2831: propName, value));
2832: }
2833: } else {
2834: for (Iterator names = props.keySet().iterator(); names
2835: .hasNext();) {
2836: String name = (String) names.next();
2837: if (name.startsWith(SVNProperty.SVN_ENTRY_PREFIX)
2838: || name
2839: .startsWith(SVNProperty.SVN_WC_PREFIX)) {
2840: continue;
2841: }
2842: String value = (String) props.get(name);
2843: handler.handleProperty(url, new SVNPropertyData(
2844: name, value));
2845: }
2846: }
2847: }
2848: }
2849:
2850: private void doGetLocalProperty(SVNAdminArea area, String propName,
2851: boolean base, ISVNPropertyHandler handler)
2852: throws SVNException {
2853: checkCancelled();
2854: for (Iterator entries = area.entries(false); entries.hasNext();) {
2855: SVNEntry entry = (SVNEntry) entries.next();
2856: if (entry.getKind() == SVNNodeKind.DIR
2857: && !"".equals(entry.getName())) {
2858: continue;
2859: }
2860: if ((base && entry.isScheduledForAddition())
2861: || (!base && entry.isScheduledForDeletion())) {
2862: continue;
2863: }
2864: SVNVersionedProperties properties = base ? area
2865: .getBaseProperties(entry.getName()) : area
2866: .getProperties(entry.getName());
2867: if (propName != null) {
2868: String propVal = properties.getPropertyValue(propName);
2869: if (propVal != null) {
2870: handler.handleProperty(area
2871: .getFile(entry.getName()),
2872: new SVNPropertyData(propName, propVal));
2873: }
2874: } else {
2875: Map allProps = properties.asMap();
2876: for (Iterator names = allProps.keySet().iterator(); names
2877: .hasNext();) {
2878: String name = (String) names.next();
2879: String val = (String) allProps.get(name);
2880: handler.handleProperty(area
2881: .getFile(entry.getName()),
2882: new SVNPropertyData(name, val));
2883: }
2884: }
2885: }
2886:
2887: for (Iterator entries = area.entries(false); entries.hasNext();) {
2888: SVNEntry entry = (SVNEntry) entries.next();
2889: if (entry.getKind() == SVNNodeKind.DIR
2890: && !"".equals(entry.getName())) {
2891: SVNAdminArea childArea = null;
2892: try {
2893: childArea = area.getWCAccess().retrieve(
2894: area.getFile(entry.getName()));
2895: } catch (SVNException e) {
2896: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
2897: continue;
2898: }
2899: throw e;
2900: }
2901: if (childArea != null) {
2902: doGetLocalProperty(childArea, propName, base,
2903: handler);
2904: }
2905: }
2906: }
2907: }
2908:
2909: private void doSetLocalProperty(SVNAdminArea anchor, String name,
2910: String propName, String propValue, boolean force,
2911: boolean recursive, boolean cancel,
2912: ISVNPropertyHandler handler) throws SVNException {
2913: if (cancel) {
2914: checkCancelled();
2915: }
2916: if (!"".equals(name)) {
2917: SVNEntry entry = anchor.getEntry(name, true);
2918: if (entry == null || (recursive && entry.isDeleted())) {
2919: return;
2920: }
2921: if (entry.getKind() == SVNNodeKind.DIR) {
2922: File path = anchor.getFile(name);
2923: SVNAdminArea dir = anchor.getWCAccess().retrieve(path);
2924: if (dir != null) {
2925: doSetLocalProperty(dir, "", propName, propValue,
2926: force, recursive, cancel, handler);
2927: }
2928: } else if (entry.getKind() == SVNNodeKind.FILE) {
2929: File wcFile = anchor.getFile(name);
2930: if ((SVNProperty.IGNORE.equals(propName) || SVNProperty.EXTERNALS
2931: .equals(propName))
2932: && propValue != null) {
2933: if (!recursive) {
2934: SVNErrorMessage err = SVNErrorMessage
2935: .create(
2936: SVNErrorCode.ILLEGAL_TARGET,
2937: "Cannot set ''{0}'' on a file (''{1}'')",
2938: new Object[] { propName, wcFile });
2939: SVNErrorManager.error(err);
2940: }
2941: return;
2942: }
2943: SVNVersionedProperties props = anchor
2944: .getProperties(name);
2945: if (SVNProperty.EXECUTABLE.equals(propName)) {
2946: SVNFileUtil
2947: .setExecutable(wcFile, propValue != null);
2948: }
2949: if (!force && SVNProperty.EOL_STYLE.equals(propName)
2950: && propValue != null) {
2951: if (SVNProperty.isBinaryMimeType(props
2952: .getPropertyValue(SVNProperty.MIME_TYPE))) {
2953: if (!recursive) {
2954: SVNErrorMessage err = SVNErrorMessage
2955: .create(
2956: SVNErrorCode.ILLEGAL_TARGET,
2957: "File ''{0}'' has binary mime type property",
2958: wcFile);
2959: SVNErrorManager.error(err);
2960: }
2961: return;
2962: }
2963: if (!SVNTranslator.checkNewLines(wcFile)) {
2964: SVNErrorMessage err = SVNErrorMessage
2965: .create(
2966: SVNErrorCode.ILLEGAL_TARGET,
2967: "File ''{0}'' has incosistent newlines",
2968: wcFile);
2969: SVNErrorManager.error(err);
2970: }
2971: }
2972: String oldValue = props.getPropertyValue(propName);
2973: boolean modified = oldValue == null ? propValue != null
2974: : !oldValue.equals(propValue);
2975: props.setPropertyValue(propName, propValue);
2976:
2977: if (SVNProperty.EOL_STYLE.equals(propName)
2978: || SVNProperty.KEYWORDS.equals(propName)) {
2979: entry.setTextTime(null);
2980: } else if (SVNProperty.NEEDS_LOCK.equals(propName)
2981: && propValue == null) {
2982: SVNFileUtil.setReadonly(wcFile, false);
2983: }
2984: if (modified && handler != null) {
2985: handler.handleProperty(anchor.getFile(name),
2986: new SVNPropertyData(propName, propValue));
2987: }
2988: }
2989: SVNLog log = anchor.getLog();
2990: anchor.saveVersionedProperties(log, true);
2991: anchor.saveEntries(false);
2992: log.save();
2993: anchor.runLogs();
2994: return;
2995: }
2996: SVNVersionedProperties props = anchor.getProperties(name);
2997: if ((SVNProperty.KEYWORDS.equals(propName)
2998: || SVNProperty.EOL_STYLE.equals(propName)
2999: || SVNProperty.MIME_TYPE.equals(propName) || SVNProperty.EXECUTABLE
3000: .equals(propName))
3001: && propValue != null) {
3002: if (!recursive) {
3003: SVNErrorMessage err = SVNErrorMessage.create(
3004: SVNErrorCode.ILLEGAL_TARGET,
3005: "Cannot set ''{0}'' on a directory (''{1}'')",
3006: new Object[] { propName, anchor.getRoot() });
3007: SVNErrorManager.error(err);
3008: }
3009: } else {
3010: String oldValue = props.getPropertyValue(propName);
3011: boolean modified = oldValue == null ? propValue != null
3012: : !oldValue.equals(propValue);
3013: props.setPropertyValue(propName, propValue);
3014: SVNLog log = anchor.getLog();
3015: anchor.saveVersionedProperties(log, true);
3016: log.save();
3017: anchor.runLogs();
3018: if (modified && handler != null) {
3019: handler.handleProperty(anchor.getFile(name),
3020: new SVNPropertyData(propName, propValue));
3021: }
3022: }
3023: if (!recursive) {
3024: return;
3025: }
3026: for (Iterator ents = anchor.entries(true); ents.hasNext();) {
3027: SVNEntry entry = (SVNEntry) ents.next();
3028: if ("".equals(entry.getName())) {
3029: continue;
3030: }
3031: doSetLocalProperty(anchor, entry.getName(), propName,
3032: propValue, force, recursive, cancel, handler);
3033: }
3034: }
3035:
3036: private static String validatePropertyName(String name)
3037: throws SVNException {
3038: if (name == null || name.trim().length() == 0) {
3039: SVNErrorMessage err = SVNErrorMessage.create(
3040: SVNErrorCode.CLIENT_PROPERTY_NAME,
3041: "Property name is empty");
3042: SVNErrorManager.error(err);
3043: return name;
3044: }
3045: name = name.trim();
3046: if (!(Character.isLetter(name.charAt(0))
3047: || name.charAt(0) == ':' || name.charAt(0) == '_')) {
3048: SVNErrorMessage err = SVNErrorMessage.create(
3049: SVNErrorCode.CLIENT_PROPERTY_NAME,
3050: "Bad property name ''{0}''", name);
3051: SVNErrorManager.error(err);
3052: }
3053: for (int i = 1; i < name.length(); i++) {
3054: if (!(Character.isLetterOrDigit(name.charAt(i))
3055: || name.charAt(i) == '-' || name.charAt(i) == '.'
3056: || name.charAt(i) == ':' || name.charAt(i) == '_')) {
3057: SVNErrorMessage err = SVNErrorMessage.create(
3058: SVNErrorCode.CLIENT_PROPERTY_NAME,
3059: "Bad property name ''{0}''", name);
3060: SVNErrorManager.error(err);
3061: }
3062: }
3063: return name;
3064: }
3065:
3066: private static String validatePropertyValue(String name,
3067: String value, boolean force) throws SVNException {
3068: if (value == null) {
3069: return value;
3070: }
3071: if (SVNProperty.isSVNProperty(name)) {
3072: value = value.replaceAll("\r\n", "\n");
3073: value = value.replace('\r', '\n');
3074: }
3075: if (!force && SVNProperty.EOL_STYLE.equals(name)) {
3076: value = value.trim();
3077: if (SVNTranslator.getEOL(value) == null) {
3078: SVNErrorMessage err = SVNErrorMessage
3079: .create(
3080: SVNErrorCode.IO_UNKNOWN_EOL,
3081: "Unrecognized line ending style ''{0}''",
3082: value);
3083: SVNErrorManager.error(err);
3084: }
3085: } else if (!force && SVNProperty.MIME_TYPE.equals(name)) {
3086: value = value.trim();
3087: } else if (SVNProperty.IGNORE.equals(name)
3088: || SVNProperty.EXTERNALS.equals(name)) {
3089: if (!value.endsWith("\n")) {
3090: value += "\n";
3091: }
3092: if (SVNProperty.EXTERNALS.equals(name)) {
3093: SVNExternalInfo[] externalInfos = SVNWCAccess
3094: .parseExternals("", value);
3095: for (int i = 0; externalInfos != null
3096: && i < externalInfos.length; i++) {
3097: String path = externalInfos[i].getPath();
3098: SVNExternalInfo.checkPath(path);
3099: }
3100: }
3101: } else if (SVNProperty.KEYWORDS.equals(name)) {
3102: value = value.trim();
3103: } else if (SVNProperty.EXECUTABLE.equals(name)
3104: || SVNProperty.SPECIAL.equals(name)
3105: || SVNProperty.NEEDS_LOCK.equals(name)) {
3106: value = "*";
3107: }
3108: return value;
3109: }
3110:
3111: private Map fetchLockTokens(SVNRepository repository,
3112: Map pathsTokensMap) throws SVNException {
3113: Map tokens = new HashMap();
3114: for (Iterator paths = pathsTokensMap.keySet().iterator(); paths
3115: .hasNext();) {
3116: String path = (String) paths.next();
3117: SVNLock lock = repository.getLock(path);
3118: if (lock == null || lock.getID() == null) {
3119: SVNErrorMessage err = SVNErrorMessage.create(
3120: SVNErrorCode.CLIENT_MISSING_LOCK_TOKEN,
3121: "''{0}'' is not locked", path);
3122: SVNErrorManager.error(err);
3123: continue;
3124: }
3125: tokens.put(path, lock.getID());
3126: }
3127: return tokens;
3128: }
3129:
3130: private void doGetLocalFileContents(File path, OutputStream dst,
3131: SVNRevision revision, boolean expandKeywords)
3132: throws SVNException {
3133: SVNWCAccess wcAccess = createWCAccess();
3134: InputStream input = null;
3135: boolean hasMods = false;
3136: SVNVersionedProperties properties = null;
3137:
3138: try {
3139: SVNAdminArea area = wcAccess.open(path.getParentFile(),
3140: false, 0);
3141: SVNEntry entry = wcAccess.getEntry(path, false);
3142: if (entry == null) {
3143: SVNErrorMessage err = SVNErrorMessage
3144: .create(
3145: SVNErrorCode.UNVERSIONED_RESOURCE,
3146: "''{0}'' is not under version control or doesn''t exist",
3147: path, SVNErrorMessage.TYPE_WARNING);
3148: SVNErrorManager.error(err);
3149: } else if (entry.getKind() != SVNNodeKind.FILE) {
3150: SVNErrorMessage err = SVNErrorMessage.create(
3151: SVNErrorCode.UNVERSIONED_RESOURCE,
3152: "''{0}'' refers to a directory", path,
3153: SVNErrorMessage.TYPE_WARNING);
3154: SVNErrorManager.error(err);
3155: }
3156: String name = path.getName();
3157: if (revision != SVNRevision.WORKING) {
3158: // get base version and base props.
3159: input = area.getBaseFileForReading(name, false);
3160: properties = area.getBaseProperties(name);
3161: } else {
3162: // get working version and working props.
3163: input = SVNFileUtil.openFileForReading(area
3164: .getFile(path.getName()));
3165: hasMods = area.hasPropModifications(name)
3166: || area.hasTextModifications(name, true);
3167: properties = area.getProperties(name);
3168: }
3169: String eolStyle = properties
3170: .getPropertyValue(SVNProperty.EOL_STYLE);
3171: String keywords = properties
3172: .getPropertyValue(SVNProperty.KEYWORDS);
3173: boolean special = properties
3174: .getPropertyValue(SVNProperty.SPECIAL) != null;
3175: byte[] eols = null;
3176: Map keywordsMap = null;
3177: String time = null;
3178:
3179: if (eolStyle != null) {
3180: eols = SVNTranslator.getEOL(eolStyle);
3181: }
3182: if (hasMods && !special) {
3183: time = SVNTimeUtil.formatDate(new Date(path
3184: .lastModified()));
3185: } else {
3186: time = entry.getCommittedDate();
3187: }
3188: if (keywords != null) {
3189: String url = entry.getURL();
3190: String author = hasMods ? "(local)" : entry.getAuthor();
3191: String rev = hasMods ? entry.getCommittedRevision()
3192: + "M" : entry.getCommittedRevision() + "";
3193: keywordsMap = SVNTranslator.computeKeywords(keywords,
3194: expandKeywords ? url : null, author, time, rev,
3195: getOptions());
3196: }
3197: OutputStream translatingStream = eols != null
3198: || keywordsMap != null ? new SVNTranslatorOutputStream(
3199: dst, eols, false, keywordsMap, expandKeywords)
3200: : dst;
3201: try {
3202: SVNTranslator
3203: .copy(input,
3204: new SVNCancellableOutputStream(
3205: translatingStream,
3206: getEventDispatcher()));
3207: if (translatingStream != dst) {
3208: SVNFileUtil.closeFile(translatingStream);
3209: }
3210: dst.flush();
3211: } catch (IOException e) {
3212: if (e instanceof SVNCancellableOutputStream.IOCancelException) {
3213: SVNErrorManager.cancel(e.getMessage());
3214: }
3215: SVNErrorManager.error(SVNErrorMessage.create(
3216: SVNErrorCode.IO_ERROR, e.getMessage()));
3217: }
3218: } finally {
3219: SVNFileUtil.closeFile(input);
3220: wcAccess.close();
3221: }
3222: }
3223:
3224: private static class LockInfo {
3225:
3226: public LockInfo(File file, SVNRevision rev) {
3227: myFile = file;
3228: myRevision = rev;
3229: }
3230:
3231: public LockInfo(File file, String token) {
3232: myFile = file;
3233: myToken = token;
3234: }
3235:
3236: private File myFile;
3237: private SVNRevision myRevision;
3238: private String myToken;
3239: }
3240:
3241: }
|