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.internal.wc.admin;
0013:
0014: import java.io.File;
0015: import java.io.IOException;
0016: import java.io.InputStream;
0017: import java.io.OutputStream;
0018: import java.io.Writer;
0019: import java.text.MessageFormat;
0020: import java.util.ArrayList;
0021: import java.util.Collection;
0022: import java.util.Collections;
0023: import java.util.Date;
0024: import java.util.HashMap;
0025: import java.util.Iterator;
0026: import java.util.LinkedList;
0027: import java.util.List;
0028: import java.util.Map;
0029:
0030: import org.tmatesoft.svn.core.SVNCancelException;
0031: import org.tmatesoft.svn.core.SVNCommitInfo;
0032: import org.tmatesoft.svn.core.SVNErrorCode;
0033: import org.tmatesoft.svn.core.SVNErrorMessage;
0034: import org.tmatesoft.svn.core.SVNException;
0035: import org.tmatesoft.svn.core.SVNNodeKind;
0036: import org.tmatesoft.svn.core.SVNProperty;
0037: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
0038: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0039: import org.tmatesoft.svn.core.internal.util.SVNTimeUtil;
0040: import org.tmatesoft.svn.core.internal.wc.DefaultSVNMerger;
0041: import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
0042: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0043: import org.tmatesoft.svn.core.internal.wc.SVNFileType;
0044: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0045: import org.tmatesoft.svn.core.internal.wc.SVNPropertiesManager;
0046: import org.tmatesoft.svn.core.wc.ISVNCommitParameters;
0047: import org.tmatesoft.svn.core.wc.ISVNMerger;
0048: import org.tmatesoft.svn.core.wc.ISVNMergerFactory;
0049: import org.tmatesoft.svn.core.wc.SVNDiffOptions;
0050: import org.tmatesoft.svn.core.wc.SVNRevision;
0051: import org.tmatesoft.svn.core.wc.SVNStatusType;
0052: import org.tmatesoft.svn.util.SVNDebugLog;
0053:
0054: /**
0055: * @version 1.1.1
0056: * @author TMate Software Ltd.
0057: */
0058: public abstract class SVNAdminArea {
0059:
0060: private static volatile boolean ourIsCleanupSafe;
0061:
0062: private File myDirectory;
0063: private SVNWCAccess myWCAccess;
0064: private File myAdminRoot;
0065: protected Map myBaseProperties;
0066: protected Map myProperties;
0067: protected Map myWCProperties;
0068: protected Map myEntries;
0069: private Map myRevertProperties;
0070:
0071: protected boolean myWasLocked;
0072: private ISVNCommitParameters myCommitParameters;
0073:
0074: public static synchronized void setSafeCleanup(boolean safe) {
0075: ourIsCleanupSafe = safe;
0076: }
0077:
0078: public static synchronized boolean isSafeCleanup() {
0079: return ourIsCleanupSafe;
0080: }
0081:
0082: public abstract boolean isLocked() throws SVNException;
0083:
0084: public abstract boolean isVersioned();
0085:
0086: protected abstract boolean isEntryPropertyApplicable(String name);
0087:
0088: public abstract boolean lock(boolean stealLock) throws SVNException;
0089:
0090: public abstract boolean unlock() throws SVNException;
0091:
0092: public abstract SVNVersionedProperties getBaseProperties(String name)
0093: throws SVNException;
0094:
0095: public abstract SVNVersionedProperties getRevertProperties(
0096: String name) throws SVNException;
0097:
0098: public abstract SVNVersionedProperties getWCProperties(String name)
0099: throws SVNException;
0100:
0101: public abstract SVNVersionedProperties getProperties(String name)
0102: throws SVNException;
0103:
0104: public abstract void saveVersionedProperties(SVNLog log,
0105: boolean close) throws SVNException;
0106:
0107: public abstract void saveWCProperties(boolean close)
0108: throws SVNException;
0109:
0110: public abstract void saveEntries(boolean close) throws SVNException;
0111:
0112: public abstract String getThisDirName();
0113:
0114: public abstract boolean hasPropModifications(String entryName)
0115: throws SVNException;
0116:
0117: public abstract boolean hasProperties(String entryName)
0118: throws SVNException;
0119:
0120: public abstract SVNAdminArea createVersionedDirectory(File dir,
0121: String url, String rootURL, String uuid, long revNumber,
0122: boolean createMyself) throws SVNException;
0123:
0124: public abstract SVNAdminArea upgradeFormat(SVNAdminArea adminArea)
0125: throws SVNException;
0126:
0127: public abstract void postUpgradeFormat(int format)
0128: throws SVNException;
0129:
0130: public abstract void postCommit(String fileName,
0131: long revisionNumber, boolean implicit,
0132: SVNErrorCode errorCode) throws SVNException;
0133:
0134: public void updateURL(String rootURL, boolean recursive)
0135: throws SVNException {
0136: SVNWCAccess wcAccess = getWCAccess();
0137: for (Iterator ents = entries(false); ents.hasNext();) {
0138: SVNEntry entry = (SVNEntry) ents.next();
0139: if (!getThisDirName().equals(entry.getName())
0140: && entry.isDirectory() && recursive) {
0141: SVNAdminArea childDir = wcAccess.retrieve(getFile(entry
0142: .getName()));
0143: if (childDir != null) {
0144: String childURL = SVNPathUtil.append(rootURL,
0145: SVNEncodingUtil.uriEncode(entry.getName()));
0146: childDir.updateURL(childURL, recursive);
0147: }
0148: continue;
0149: }
0150: entry
0151: .setURL(getThisDirName().equals(entry.getName()) ? rootURL
0152: : SVNPathUtil.append(rootURL,
0153: SVNEncodingUtil.uriEncode(entry
0154: .getName())));
0155: }
0156: saveEntries(false);
0157: }
0158:
0159: public boolean hasTextModifications(String name,
0160: boolean forceComparision) throws SVNException {
0161: return hasTextModifications(name, forceComparision, true, false);
0162: }
0163:
0164: public boolean hasTextModifications(String name,
0165: boolean forceComparison, boolean compareTextBase,
0166: boolean compareChecksum) throws SVNException {
0167: SVNEntry entry = getEntry(name, false);
0168: SVNFileType fType = SVNFileType.getType(getFile(name));
0169: if (!forceComparison && fType != SVNFileType.SYMLINK) {
0170: if (entry == null || entry.isDirectory()) {
0171: return false;
0172: }
0173:
0174: String textTime = entry.getTextTime();
0175: if (textTime != null) {
0176: long textTimeAsLong = SVNFileUtil
0177: .roundTimeStamp(SVNTimeUtil
0178: .parseDateAsLong(textTime));
0179: long tstamp = SVNFileUtil.roundTimeStamp(getFile(name)
0180: .lastModified());
0181: if (textTimeAsLong == tstamp) {
0182: return false;
0183: }
0184: }
0185: }
0186: if (fType != SVNFileType.FILE && fType != SVNFileType.SYMLINK) {
0187: return false;
0188: }
0189: File textFile = getFile(name);
0190: File baseFile = getBaseFile(name, false);
0191: if (!baseFile.isFile()) {
0192: return true;
0193: }
0194: boolean differs = compareAndVerify(textFile, baseFile,
0195: compareTextBase, compareChecksum);
0196: if (!differs && isLocked()) {
0197: entry.setTextTime(SVNTimeUtil.formatDate(new Date(textFile
0198: .lastModified())));
0199: saveEntries(false);
0200: }
0201: return differs;
0202: }
0203:
0204: private boolean compareAndVerify(File text, File baseFile,
0205: boolean compareTextBase, boolean checksum)
0206: throws SVNException {
0207: String eolStyle = getProperties(text.getName())
0208: .getPropertyValue(SVNProperty.EOL_STYLE);
0209: String keywords = getProperties(text.getName())
0210: .getPropertyValue(SVNProperty.KEYWORDS);
0211: boolean special = getProperties(text.getName())
0212: .getPropertyValue(SVNProperty.SPECIAL) != null;
0213:
0214: if (special) {
0215: compareTextBase = true;
0216: }
0217:
0218: boolean needsTranslation = eolStyle != null || keywords != null
0219: || special;
0220: SVNChecksumInputStream checksumStream = null;
0221: SVNEntry entry = null;
0222:
0223: if (checksum || needsTranslation) {
0224: InputStream baseStream = null;
0225: InputStream textStream = null;
0226: entry = getEntry(text.getName(), true);
0227: if (entry == null) {
0228: SVNErrorMessage err = SVNErrorMessage.create(
0229: SVNErrorCode.UNVERSIONED_RESOURCE,
0230: "''{0}'' is not under version control", text);
0231: SVNErrorManager.error(err);
0232: }
0233: File tmpFile = null;
0234: try {
0235: baseStream = SVNFileUtil.openFileForReading(baseFile);
0236: textStream = special ? null : SVNFileUtil
0237: .openFileForReading(text);
0238: if (checksum) {
0239: if (entry.getChecksum() != null) {
0240: checksumStream = new SVNChecksumInputStream(
0241: baseStream);
0242: baseStream = checksumStream;
0243: }
0244: }
0245: if (compareTextBase && needsTranslation) {
0246: if (!special) {
0247: Map keywordsMap = SVNTranslator
0248: .computeKeywords(keywords, null, entry
0249: .getAuthor(), entry
0250: .getCommittedDate(), entry
0251: .getRevision()
0252: + "", getWCAccess()
0253: .getOptions());
0254: byte[] eols = SVNTranslator
0255: .getBaseEOL(eolStyle);
0256: textStream = new SVNTranslatorInputStream(
0257: textStream, eols, false, keywordsMap,
0258: false);
0259: } else {
0260: tmpFile = SVNFileUtil.createUniqueFile(
0261: getAdminFile("tmp/text-base"), text
0262: .getName(), ".tmp");
0263: String tmpPath = SVNFileUtil
0264: .getBasePath(tmpFile);
0265: SVNTranslator.translate(this , text.getName(),
0266: text.getName(), tmpPath, false);
0267: textStream = SVNFileUtil
0268: .openFileForReading(getFile(tmpPath));
0269: }
0270: } else if (needsTranslation) {
0271: Map keywordsMap = SVNTranslator.computeKeywords(
0272: keywords, entry.getURL(),
0273: entry.getAuthor(),
0274: entry.getCommittedDate(), entry
0275: .getRevision()
0276: + "", getWCAccess().getOptions());
0277: byte[] eols = SVNTranslator.getWorkingEOL(eolStyle);
0278: baseStream = new SVNTranslatorInputStream(
0279: baseStream, eols, false, keywordsMap, true);
0280: }
0281: byte[] buffer1 = new byte[8192];
0282: byte[] buffer2 = new byte[8192];
0283: try {
0284: while (true) {
0285: int r1 = baseStream.read(buffer1);
0286: int r2 = textStream.read(buffer2);
0287: r1 = r1 == -1 ? 0 : r1;
0288: r2 = r2 == -1 ? 0 : r2;
0289: if (r1 != r2) {
0290: return true;
0291: } else if (r1 == 0) {
0292: return false;
0293: }
0294: for (int i = 0; i < r1; i++) {
0295: if (buffer1[i] != buffer2[i]) {
0296: return true;
0297: }
0298: }
0299: }
0300: } catch (IOException e) {
0301: SVNErrorMessage err = SVNErrorMessage.create(
0302: SVNErrorCode.IO_ERROR, e.getMessage());
0303: SVNErrorManager.error(err);
0304: }
0305: } finally {
0306: SVNFileUtil.closeFile(baseStream);
0307: SVNFileUtil.closeFile(textStream);
0308: SVNFileUtil.deleteFile(tmpFile);
0309: }
0310: } else {
0311: return !SVNFileUtil.compareFiles(text, baseFile, null);
0312: }
0313: if (entry != null && checksumStream != null) {
0314: if (!entry.getChecksum().equals(checksumStream.getDigest())) {
0315: SVNErrorMessage err = SVNErrorMessage.create(
0316: SVNErrorCode.WC_CORRUPT_TEXT_BASE,
0317: "Checksum mismatch indicates corrupt text base: ''{0}''\n"
0318: + " expected: {1}\n"
0319: + " actual: {2}\n", new Object[] {
0320: baseFile, entry.getChecksum(),
0321: checksumStream.getDigest() });
0322: SVNErrorManager.error(err);
0323: }
0324: }
0325: return false;
0326: }
0327:
0328: public String getRelativePath(SVNAdminArea anchor) {
0329: String absoluteAnchor = anchor.getRoot().getAbsolutePath();
0330: String ownAbsolutePath = getRoot().getAbsolutePath();
0331: String relativePath = ownAbsolutePath.substring(absoluteAnchor
0332: .length());
0333:
0334: relativePath = relativePath.replace(File.separatorChar, '/');
0335: if (relativePath.startsWith("/")) {
0336: relativePath = relativePath.substring(1);
0337: }
0338: if (relativePath.endsWith("/")) {
0339: relativePath = relativePath.substring(0, relativePath
0340: .length() - 1);
0341: }
0342: return relativePath;
0343: }
0344:
0345: public boolean tweakEntry(String name, String newURL,
0346: String reposRoot, long newRevision, boolean remove)
0347: throws SVNException {
0348: boolean rewrite = false;
0349: SVNEntry entry = getEntry(name, true);
0350: if (entry == null) {
0351: SVNErrorMessage err = SVNErrorMessage.create(
0352: SVNErrorCode.ENTRY_NOT_FOUND,
0353: "No such entry: ''{0}''", name);
0354: SVNErrorManager.error(err);
0355: }
0356:
0357: if (newURL != null
0358: && (entry.getURL() == null || !newURL.equals(entry
0359: .getURL()))) {
0360: rewrite = true;
0361: entry.setURL(newURL);
0362: }
0363:
0364: if (reposRoot != null
0365: && (entry.getRepositoryRootURL() == null || !reposRoot
0366: .equals(entry.getRepositoryRoot()))
0367: && entry.getURL() != null
0368: && SVNPathUtil.isAncestor(reposRoot, entry.getURL())) {
0369: boolean setReposRoot = true;
0370: if (getThisDirName().equals(entry.getName())) {
0371: for (Iterator entries = entries(true); entries
0372: .hasNext();) {
0373: SVNEntry childEntry = (SVNEntry) entries.next();
0374: if (childEntry.getRepositoryRoot() == null
0375: && childEntry.getURL() != null
0376: && !SVNPathUtil.isAncestor(reposRoot, entry
0377: .getURL())) {
0378: setReposRoot = false;
0379: break;
0380: }
0381: }
0382: }
0383: if (setReposRoot) {
0384: rewrite = true;
0385: entry.setRepositoryRoot(reposRoot);
0386: }
0387: }
0388:
0389: if (newRevision >= 0 && !entry.isScheduledForAddition()
0390: && !entry.isScheduledForReplacement()
0391: && entry.getRevision() != newRevision) {
0392: rewrite = true;
0393: entry.setRevision(newRevision);
0394: }
0395:
0396: if (remove
0397: && (entry.isDeleted() || (entry.isAbsent() && entry
0398: .getRevision() != newRevision))) {
0399: deleteEntry(name);
0400: rewrite = true;
0401: }
0402: return rewrite;
0403: }
0404:
0405: public boolean isKillMe() {
0406: return getAdminFile("KILLME").isFile();
0407: }
0408:
0409: public boolean markResolved(String name, boolean text, boolean props)
0410: throws SVNException {
0411: if (!text && !props) {
0412: return false;
0413: }
0414: SVNEntry entry = getEntry(name, true);
0415: if (entry == null) {
0416: return false;
0417: }
0418: boolean filesDeleted = false;
0419: boolean updateEntry = false;
0420: if (text && entry.getConflictOld() != null) {
0421: File file = getFile(entry.getConflictOld());
0422: filesDeleted |= file.isFile();
0423: updateEntry = true;
0424: SVNFileUtil.deleteFile(file);
0425: }
0426: if (text && entry.getConflictNew() != null) {
0427: File file = getFile(entry.getConflictNew());
0428: filesDeleted |= file.isFile();
0429: updateEntry = true;
0430: SVNFileUtil.deleteFile(file);
0431: }
0432: if (text && entry.getConflictWorking() != null) {
0433: File file = getFile(entry.getConflictWorking());
0434: filesDeleted |= file.isFile();
0435: updateEntry = true;
0436: SVNFileUtil.deleteFile(file);
0437: }
0438: if (props && entry.getPropRejectFile() != null) {
0439: File file = getFile(entry.getPropRejectFile());
0440: filesDeleted |= file.isFile();
0441: updateEntry = true;
0442: SVNFileUtil.deleteFile(file);
0443: }
0444: if (updateEntry) {
0445: if (text) {
0446: entry.setConflictOld(null);
0447: entry.setConflictNew(null);
0448: entry.setConflictWorking(null);
0449: }
0450: if (props) {
0451: entry.setPropRejectFile(null);
0452: }
0453: saveEntries(false);
0454: }
0455: return filesDeleted;
0456: }
0457:
0458: public void restoreFile(String name) throws SVNException {
0459: SVNVersionedProperties props = getProperties(name);
0460: SVNEntry entry = getEntry(name, true);
0461: boolean special = props.getPropertyValue(SVNProperty.SPECIAL) != null;
0462:
0463: File src = getBaseFile(name, false);
0464: File dst = getFile(name);
0465: SVNTranslator.translate(this , name, SVNFileUtil
0466: .getBasePath(src), SVNFileUtil.getBasePath(dst), true);
0467:
0468: boolean executable = props
0469: .getPropertyValue(SVNProperty.EXECUTABLE) != null;
0470: boolean needsLock = props
0471: .getPropertyValue(SVNProperty.NEEDS_LOCK) != null;
0472: if (needsLock) {
0473: SVNFileUtil.setReadonly(dst, entry.getLockToken() == null);
0474: }
0475: if (executable) {
0476: SVNFileUtil.setExecutable(dst, true);
0477: }
0478:
0479: markResolved(name, true, false);
0480:
0481: long tstamp;
0482: if (myWCAccess.getOptions().isUseCommitTimes() && !special) {
0483: entry.setTextTime(entry.getCommittedDate());
0484: tstamp = SVNTimeUtil.parseDate(entry.getCommittedDate())
0485: .getTime();
0486: dst.setLastModified(tstamp);
0487: } else {
0488: tstamp = System.currentTimeMillis();
0489: dst.setLastModified(tstamp);
0490: entry.setTextTime(SVNTimeUtil.formatDate(new Date(tstamp)));
0491: }
0492: saveEntries(false);
0493: }
0494:
0495: public SVNStatusType mergeProperties(String name,
0496: Map serverBaseProps, Map propDiff, boolean baseMerge,
0497: boolean dryRun, SVNLog log) throws SVNException {
0498: serverBaseProps = serverBaseProps == null ? Collections.EMPTY_MAP
0499: : serverBaseProps;
0500: propDiff = propDiff == null ? Collections.EMPTY_MAP : propDiff;
0501:
0502: SVNVersionedProperties working = getProperties(name);
0503: Map workingProps = working.asMap();
0504: SVNVersionedProperties base = getBaseProperties(name);
0505:
0506: Collection conflicts = new ArrayList();
0507: SVNStatusType result = propDiff.isEmpty() ? SVNStatusType.UNCHANGED
0508: : SVNStatusType.CHANGED;
0509:
0510: for (Iterator propEntries = propDiff.entrySet().iterator(); propEntries
0511: .hasNext();) {
0512: Map.Entry incomingEntry = (Map.Entry) propEntries.next();
0513: String propName = (String) incomingEntry.getKey();
0514: String toValue = (String) incomingEntry.getValue();
0515: String fromValue = (String) serverBaseProps.get(propName);
0516: String workingValue = (String) workingProps.get(propName);
0517: boolean isNormal = SVNProperty.isRegularProperty(propName);
0518: if (baseMerge) {
0519: base.setPropertyValue(propName, toValue);
0520: }
0521:
0522: result = isNormal ? SVNStatusType.CHANGED : result;
0523: if (fromValue == null) {
0524: if (workingValue != null) {
0525: if (workingValue.equals(toValue)) {
0526: result = result != SVNStatusType.CONFLICTED
0527: && isNormal ? SVNStatusType.MERGED
0528: : result;
0529: } else {
0530: result = isNormal ? SVNStatusType.CONFLICTED
0531: : result;
0532: conflicts
0533: .add(MessageFormat
0534: .format(
0535: "Trying to add new property ''{0}'' with value ''{1}'',\n"
0536: + "but property already exists with value ''{2}''.",
0537: new Object[] {
0538: propName,
0539: toValue,
0540: workingValue }));
0541: }
0542: } else {
0543: working.setPropertyValue(propName, toValue);
0544: }
0545: } else {
0546: if (workingValue == null) {
0547: if (toValue != null) {
0548: result = isNormal ? SVNStatusType.CONFLICTED
0549: : result;
0550: conflicts
0551: .add(MessageFormat
0552: .format(
0553: "Trying to change property ''{0}'' from ''{1}'' to ''{2}'',\n"
0554: + "but the property does not exist.",
0555: new Object[] {
0556: propName,
0557: fromValue,
0558: toValue }));
0559: } else {
0560: result = result != SVNStatusType.CONFLICTED
0561: && isNormal ? SVNStatusType.MERGED
0562: : result;
0563: }
0564: } else {
0565: if (workingValue.equals(fromValue)) {
0566: working.setPropertyValue(propName, toValue);
0567: } else if (toValue == null
0568: && !workingValue.equals(fromValue)) {
0569: result = isNormal ? SVNStatusType.CONFLICTED
0570: : result;
0571: conflicts
0572: .add(MessageFormat
0573: .format(
0574: "Trying to delete property ''{0}'' but value has been modified from ''{1}'' to ''{2}''.",
0575: new Object[] {
0576: propName,
0577: fromValue,
0578: workingValue }));
0579: } else if (workingValue.equals(toValue)) {
0580: result = result != SVNStatusType.CONFLICTED
0581: && isNormal ? SVNStatusType.MERGED
0582: : result;
0583: } else {
0584: result = isNormal ? SVNStatusType.CONFLICTED
0585: : result;
0586:
0587: conflicts
0588: .add(MessageFormat
0589: .format(
0590: "Trying to change property ''{0}'' from ''{1}'' to ''{2}'',\n"
0591: + "but property already exists with value ''{3}''.",
0592: new Object[] {
0593: propName,
0594: fromValue,
0595: toValue,
0596: workingValue }));
0597: }
0598: }
0599: }
0600: }
0601:
0602: Map command = new HashMap();
0603: if (dryRun) {
0604: return result;
0605: }
0606: log = log == null ? getLog() : log;
0607: saveVersionedProperties(log, true);
0608:
0609: if (!conflicts.isEmpty()) {
0610: String prejTmpPath = getThisDirName().equals(name) ? "tmp/dir_conflicts"
0611: : "tmp/props/" + name;
0612: File prejTmpFile = SVNFileUtil.createUniqueFile(
0613: getAdminDirectory(), prejTmpPath, ".prej");
0614:
0615: prejTmpPath = SVNFileUtil.getBasePath(prejTmpFile);
0616:
0617: SVNEntry entry = getEntry(name, false);
0618: if (entry == null) {
0619: SVNErrorMessage err = SVNErrorMessage.create(
0620: SVNErrorCode.ENTRY_NOT_FOUND,
0621: "Can''t find entry ''{0}'' in ''{1}''",
0622: new Object[] { name, getRoot() });
0623: SVNErrorManager.error(err);
0624: }
0625: String prejPath = entry.getPropRejectFile();
0626: closeEntries();
0627:
0628: if (prejPath == null) {
0629: prejPath = getThisDirName().equals(name) ? "dir_conflicts"
0630: : name;
0631: File prejFile = SVNFileUtil.createUniqueFile(getRoot(),
0632: prejPath, ".prej");
0633: prejPath = SVNFileUtil.getBasePath(prejFile);
0634: }
0635: File file = getFile(prejTmpPath);
0636:
0637: OutputStream os = SVNFileUtil.openFileForWriting(file);
0638: try {
0639: for (Iterator lines = conflicts.iterator(); lines
0640: .hasNext();) {
0641: String line = (String) lines.next();
0642: os.write(SVNEncodingUtil.fuzzyEscape(line)
0643: .getBytes("UTF-8"));
0644: // os.write(line.getBytes("UTF-8"));
0645: }
0646: } catch (IOException e) {
0647: SVNErrorMessage err = SVNErrorMessage.create(
0648: SVNErrorCode.IO_ERROR,
0649: "Cannot write properties conflict file: {1}", e
0650: .getLocalizedMessage());
0651: SVNErrorManager.error(err, e);
0652: } finally {
0653: SVNFileUtil.closeFile(os);
0654: }
0655:
0656: command.put(SVNLog.NAME_ATTR, prejTmpPath);
0657: command.put(SVNLog.DEST_ATTR, prejPath);
0658: log.addCommand(SVNLog.APPEND, command, false);
0659: command.clear();
0660:
0661: command.put(SVNLog.NAME_ATTR, prejTmpPath);
0662: log.addCommand(SVNLog.DELETE, command, false);
0663: command.clear();
0664:
0665: command.put(SVNLog.NAME_ATTR, name);
0666: command.put(SVNProperty
0667: .shortPropertyName(SVNProperty.PROP_REJECT_FILE),
0668: prejPath);
0669: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
0670: }
0671: return result;
0672: }
0673:
0674: public SVNStatusType mergeText(String localPath, File base,
0675: File latest, String localLabel, String baseLabel,
0676: String latestLabel, boolean leaveConflict, boolean dryRun)
0677: throws SVNException {
0678: return mergeText(localPath, base, latest, localLabel,
0679: baseLabel, latestLabel, leaveConflict, dryRun, null);
0680: }
0681:
0682: public SVNStatusType mergeText(String localPath, File base,
0683: File latest, String localLabel, String baseLabel,
0684: String latestLabel, boolean leaveConflict, boolean dryRun,
0685: SVNDiffOptions options) throws SVNException {
0686: SVNEntry entry = getEntry(localPath, false);
0687: if (entry == null) {
0688: return SVNStatusType.UNCHANGED;
0689: }
0690:
0691: SVNVersionedProperties props = getProperties(localPath);
0692: String mimeType = props.getPropertyValue(SVNProperty.MIME_TYPE);
0693: SVNStatusType status = SVNStatusType.UNCHANGED;
0694:
0695: byte[] conflictStart = ("<<<<<<< " + localLabel).getBytes();
0696: byte[] conflictEnd = (">>>>>>> " + latestLabel).getBytes();
0697: byte[] separator = ("=======").getBytes();
0698: ISVNMergerFactory factory = myWCAccess.getOptions()
0699: .getMergerFactory();
0700: ISVNMerger merger = factory.createMerger(conflictStart,
0701: separator, conflictEnd);
0702: boolean customMerger = merger.getClass() != DefaultSVNMerger.class;
0703:
0704: if (SVNProperty.isBinaryMimeType(mimeType) && !customMerger) {
0705: // binary
0706: if (!dryRun) {
0707: File oldFile = SVNFileUtil.createUniqueFile(getRoot(),
0708: localPath, baseLabel);
0709: File newFile = SVNFileUtil.createUniqueFile(getRoot(),
0710: localPath, latestLabel);
0711: SVNFileUtil.copyFile(base, oldFile, false);
0712: SVNFileUtil.copyFile(latest, newFile, false);
0713: // update entry props
0714: entry.setConflictNew(SVNFileUtil.getBasePath(newFile));
0715: entry.setConflictOld(SVNFileUtil.getBasePath(oldFile));
0716: entry.setConflictWorking(null);
0717: saveEntries(false);
0718: }
0719: status = SVNStatusType.CONFLICTED;
0720: } else {
0721: // text
0722: // 1. destranslate local
0723: File localTmpFile = SVNFileUtil.createUniqueFile(getRoot(),
0724: localPath, ".tmp");
0725: SVNTranslator.translate(this , localPath, localPath,
0726: SVNFileUtil.getBasePath(localTmpFile), false);
0727: // 2. run merge between all files we have :)
0728: OutputStream result = null;
0729: File resultFile = dryRun ? null : SVNFileUtil
0730: .createUniqueFile(getRoot(), localPath, ".result");
0731:
0732: result = resultFile == null ? SVNFileUtil.DUMMY_OUT
0733: : SVNFileUtil.openFileForWriting(resultFile);
0734: try {
0735: status = SVNProperty.isBinaryMimeType(mimeType) ? merger
0736: .mergeBinary(base, localTmpFile, latest,
0737: dryRun, result)
0738: : merger.mergeText(base, localTmpFile, latest,
0739: dryRun, options, result);
0740: } finally {
0741: SVNFileUtil.closeFile(result);
0742: }
0743: if (dryRun) {
0744: localTmpFile.delete();
0745: if (leaveConflict && status == SVNStatusType.CONFLICTED) {
0746: status = SVNStatusType.CONFLICTED_UNRESOLVED;
0747: }
0748: } else {
0749: if (status != SVNStatusType.CONFLICTED) {
0750: SVNTranslator.translate(this , localPath,
0751: SVNFileUtil.getBasePath(resultFile),
0752: localPath, true);
0753: } else {
0754: // copy all to wc.
0755: File mineFile = SVNFileUtil.createUniqueFile(
0756: getRoot(), localPath, localLabel);
0757: String minePath = SVNFileUtil.getBasePath(mineFile);
0758: SVNFileUtil.copyFile(getFile(localPath), mineFile,
0759: false);
0760: File oldFile = SVNFileUtil.createUniqueFile(
0761: getRoot(), localPath, baseLabel);
0762: String oldPath = SVNFileUtil.getBasePath(oldFile);
0763: File newFile = SVNFileUtil.createUniqueFile(
0764: getRoot(), localPath, latestLabel);
0765: String newPath = SVNFileUtil.getBasePath(newFile);
0766:
0767: SVNTranslator.translate(this , localPath, base,
0768: oldFile, true);
0769: SVNTranslator.translate(this , localPath, latest,
0770: newFile, true);
0771: // translate result to local
0772: if (!leaveConflict) {
0773: SVNTranslator.translate(this , localPath,
0774: SVNFileUtil.getBasePath(resultFile),
0775: localPath, true);
0776: }
0777:
0778: entry.setConflictNew(newPath);
0779: entry.setConflictOld(oldPath);
0780: entry.setConflictWorking(minePath);
0781: saveEntries(false);
0782: }
0783: }
0784: localTmpFile.delete();
0785: if (resultFile != null) {
0786: resultFile.delete();
0787: }
0788: if (status == SVNStatusType.CONFLICTED && leaveConflict) {
0789: status = SVNStatusType.CONFLICTED_UNRESOLVED;
0790: }
0791: }
0792:
0793: if (!dryRun) {
0794: boolean executable = SVNFileUtil.isWindows ? false : props
0795: .getPropertyValue(SVNProperty.EXECUTABLE) != null;
0796:
0797: if (executable) {
0798: SVNFileUtil.setExecutable(getFile(localPath), true);
0799: }
0800: if (entry.getLockToken() == null
0801: && props.getPropertyValue(SVNProperty.NEEDS_LOCK) != null) {
0802: SVNFileUtil.setReadonly(getFile(localPath), true);
0803: }
0804: }
0805: return status;
0806: }
0807:
0808: public InputStream getBaseFileForReading(String name, boolean tmp)
0809: throws SVNException {
0810: String path = tmp ? "tmp/" : "";
0811: path += "text-base/" + name + ".svn-base";
0812: File baseFile = getAdminFile(path);
0813: return SVNFileUtil.openFileForReading(baseFile);
0814: }
0815:
0816: public OutputStream getBaseFileForWriting(String name)
0817: throws SVNException {
0818: final String fileName = name;
0819: final File tmpFile = getBaseFile(name, true);
0820: try {
0821: final OutputStream os = SVNFileUtil
0822: .openFileForWriting(tmpFile);
0823: return new OutputStream() {
0824: private String myName = fileName;
0825: private File myTmpFile = tmpFile;
0826:
0827: public void write(int b) throws IOException {
0828: os.write(b);
0829: }
0830:
0831: public void write(byte[] b) throws IOException {
0832: os.write(b);
0833: }
0834:
0835: public void write(byte[] b, int off, int len)
0836: throws IOException {
0837: os.write(b, off, len);
0838: }
0839:
0840: public void close() throws IOException {
0841: os.close();
0842: File baseFile = getBaseFile(myName, false);
0843: try {
0844: SVNFileUtil.rename(myTmpFile, baseFile);
0845: } catch (SVNException e) {
0846: throw new IOException(e.getMessage());
0847: }
0848: SVNFileUtil.setReadonly(baseFile, true);
0849: }
0850: };
0851: } catch (SVNException svne) {
0852: SVNErrorMessage err = svne
0853: .getErrorMessage()
0854: .wrap(
0855: "Your .svn/tmp directory may be missing or corrupt; run 'svn cleanup' and try again");
0856: SVNErrorManager.error(err);
0857: }
0858: return null;
0859: }
0860:
0861: public String getPropertyTime(String name) {
0862: String path = getThisDirName().equals(name) ? "dir-props"
0863: : "props/" + name + ".svn-work";
0864: File file = getAdminFile(path);
0865: return SVNTimeUtil.formatDate(new Date(file.lastModified()));
0866: }
0867:
0868: public SVNLog getLog() {
0869: int index = 0;
0870: File logFile = null;
0871: File tmpFile = null;
0872: while (true) {
0873: logFile = getAdminFile("log"
0874: + (index == 0 ? "" : "." + index));
0875: if (logFile.exists()) {
0876: index++;
0877: continue;
0878: }
0879: tmpFile = getAdminFile("tmp/log"
0880: + (index == 0 ? "" : "." + index));
0881: return new SVNLogImpl(logFile, tmpFile, this );
0882: }
0883: }
0884:
0885: public void runLogs() throws SVNException {
0886: SVNLogRunner runner = new SVNLogRunner();
0887: int index = 0;
0888: SVNLog log = null;
0889: try {
0890: File logFile = null;
0891: while (true) {
0892: if (getWCAccess() != null) {
0893: getWCAccess().checkCancelled();
0894: }
0895: logFile = getAdminFile("log"
0896: + (index == 0 ? "" : "." + index));
0897: log = new SVNLogImpl(logFile, null, this );
0898: if (log.exists()) {
0899: log.run(runner);
0900: markLogProcessed(logFile);
0901: index++;
0902: continue;
0903: }
0904: break;
0905: }
0906: } catch (Throwable e) {
0907: runner.logFailed(this );
0908: if (e instanceof SVNException) {
0909: throw (SVNException) e;
0910: } else if (e instanceof Error) {
0911: throw (Error) e;
0912: }
0913: throw new SVNException(SVNErrorMessage
0914: .create(SVNErrorCode.UNKNOWN), e);
0915: }
0916: runner.logCompleted(this );
0917: // delete all logs, there shoudn't be left unprocessed.
0918: File[] logsFiles = getAdminDirectory().listFiles();
0919: if (logsFiles != null) {
0920: for (int i = 0; i < logsFiles.length; i++) {
0921: if (logsFiles[i].getName().startsWith("log")
0922: && logsFiles[i].isFile()) {
0923: SVNFileUtil.deleteFile(logsFiles[i]);
0924: }
0925: }
0926: }
0927: }
0928:
0929: private static void markLogProcessed(File logFile)
0930: throws SVNException {
0931: SVNFileUtil.deleteFile(logFile);
0932: SVNFileUtil.createEmptyFile(logFile);
0933: }
0934:
0935: public void removeFromRevisionControl(String name,
0936: boolean deleteWorkingFiles, boolean reportInstantError)
0937: throws SVNException {
0938: getWCAccess().checkCancelled();
0939: boolean isFile = !getThisDirName().equals(name);
0940: boolean leftSomething = false;
0941:
0942: if (isFile) {
0943: File path = getFile(name);
0944: boolean textModified = false;
0945: boolean wasSpecial = false;
0946: boolean isSpecial = false;
0947: if (deleteWorkingFiles) {
0948: wasSpecial = getProperties(name).containsProperty(
0949: SVNProperty.SPECIAL);
0950: isSpecial = SVNFileType.getType(path) == SVNFileType.SYMLINK;
0951: if (!(!wasSpecial && isSpecial)) {
0952: textModified = hasTextModifications(name, false);
0953: if (reportInstantError && textModified) {
0954: SVNErrorMessage err = SVNErrorMessage.create(
0955: SVNErrorCode.WC_LEFT_LOCAL_MOD,
0956: "File ''{0}'' has local modifications",
0957: path);
0958: SVNErrorManager.error(err);
0959: }
0960: }
0961: }
0962: SVNPropertiesManager.deleteWCProperties(this , name, false);
0963: deleteEntry(name);
0964: saveEntries(false);
0965:
0966: SVNFileUtil.deleteFile(getFile(SVNAdminUtil
0967: .getTextBasePath(name, false)));
0968: SVNFileUtil.deleteFile(getFile(SVNAdminUtil.getPropPath(
0969: name, isFile ? SVNNodeKind.FILE : SVNNodeKind.DIR,
0970: false)));
0971: SVNFileUtil.deleteFile(getFile(SVNAdminUtil
0972: .getPropBasePath(name, isFile ? SVNNodeKind.FILE
0973: : SVNNodeKind.DIR, false)));
0974: if (deleteWorkingFiles) {
0975: if (textModified || (!wasSpecial && isSpecial)) {
0976: SVNErrorMessage err = SVNErrorMessage
0977: .create(SVNErrorCode.WC_LEFT_LOCAL_MOD);
0978: SVNErrorManager.error(err);
0979: } else if (myCommitParameters == null
0980: || myCommitParameters.onFileDeletion(path)) {
0981: SVNFileUtil.deleteFile(path);
0982: }
0983: }
0984: } else {
0985: SVNEntry dirEntry = getEntry(getThisDirName(), false);
0986: dirEntry.setIncomplete(true);
0987: saveEntries(false);
0988: SVNPropertiesManager.deleteWCProperties(this ,
0989: getThisDirName(), false);
0990: for (Iterator entries = entries(false); entries.hasNext();) {
0991: SVNEntry entry = (SVNEntry) entries.next();
0992: String entryName = getThisDirName().equals(
0993: entry.getName()) ? null : entry.getName();
0994: if (entry.isFile()) {
0995: try {
0996: removeFromRevisionControl(entryName,
0997: deleteWorkingFiles, reportInstantError);
0998: } catch (SVNException e) {
0999: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LEFT_LOCAL_MOD) {
1000: if (reportInstantError) {
1001: throw e;
1002: }
1003: leftSomething = true;
1004: } else {
1005: throw e;
1006: }
1007: }
1008: } else if (entryName != null && entry.isDirectory()) {
1009: File entryPath = getFile(entryName);
1010: if (getWCAccess().isMissing(entryPath)) {
1011: deleteEntry(entryName);
1012: } else {
1013: try {
1014: SVNAdminArea entryArea = getWCAccess()
1015: .retrieve(entryPath);
1016: entryArea.removeFromRevisionControl(
1017: getThisDirName(),
1018: deleteWorkingFiles,
1019: reportInstantError);
1020: } catch (SVNException e) {
1021: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LEFT_LOCAL_MOD) {
1022: if (reportInstantError) {
1023: throw e;
1024: }
1025: leftSomething = true;
1026: } else {
1027: throw e;
1028: }
1029: }
1030: }
1031: }
1032: }
1033: if (!getWCAccess().isWCRoot(getRoot())) {
1034: getWCAccess().retrieve(getRoot().getParentFile())
1035: .deleteEntry(getRoot().getName());
1036: getWCAccess().retrieve(getRoot().getParentFile())
1037: .saveEntries(false);
1038: }
1039: destroyAdminArea();
1040: if (deleteWorkingFiles && !leftSomething) {
1041: if ((myCommitParameters == null || myCommitParameters
1042: .onDirectoryDeletion(getRoot()))
1043: && !getRoot().delete()) {
1044: // shouldn't throw exception when directory was intentionally left non-empty.
1045: leftSomething = true;
1046: }
1047: }
1048:
1049: }
1050: // remove cached props from this
1051: if (myBaseProperties != null) {
1052: myBaseProperties.remove(name);
1053: }
1054: if (myProperties != null) {
1055: myProperties.remove(name);
1056: }
1057: if (leftSomething && myCommitParameters == null) {
1058: SVNErrorMessage err = SVNErrorMessage
1059: .create(SVNErrorCode.WC_LEFT_LOCAL_MOD);
1060: SVNErrorManager.error(err);
1061: }
1062: }
1063:
1064: public void foldScheduling(String name, Map attributes,
1065: boolean force) throws SVNException {
1066: if (!attributes.containsKey(SVNProperty
1067: .shortPropertyName(SVNProperty.SCHEDULE))
1068: || force) {
1069: return;
1070: }
1071: String schedule = (String) attributes.get(SVNProperty
1072: .shortPropertyName(SVNProperty.SCHEDULE));
1073: schedule = "".equals(schedule) ? null : schedule;
1074:
1075: SVNEntry entry = getEntry(name, true);
1076: if (entry == null) {
1077: if (SVNProperty.SCHEDULE_ADD.equals(schedule)) {
1078: return;
1079: }
1080: SVNErrorMessage err = SVNErrorMessage.create(
1081: SVNErrorCode.WC_SCHEDULE_CONFLICT,
1082: "''{0}'' is not under version control", name);
1083: SVNErrorManager.error(err);
1084: }
1085:
1086: SVNEntry this DirEntry = getEntry(getThisDirName(), true);
1087: if (!getThisDirName().equals(entry.getName())
1088: && this DirEntry.isScheduledForDeletion()) {
1089: if (SVNProperty.SCHEDULE_ADD.equals(schedule)) {
1090: SVNErrorMessage err = SVNErrorMessage
1091: .create(
1092: SVNErrorCode.WC_SCHEDULE_CONFLICT,
1093: "Can''t add ''{0}'' to deleted directory; try undeleting its parent directory first",
1094: name);
1095: SVNErrorManager.error(err);
1096: } else if (SVNProperty.SCHEDULE_REPLACE.equals(schedule)) {
1097: SVNErrorMessage err = SVNErrorMessage
1098: .create(
1099: SVNErrorCode.WC_SCHEDULE_CONFLICT,
1100: "Can''t replace ''{0}'' in deleted directory; try undeleting its parent directory first",
1101: name);
1102: SVNErrorManager.error(err);
1103: }
1104: }
1105:
1106: if (entry.isAbsent()
1107: && SVNProperty.SCHEDULE_ADD.equals(schedule)) {
1108: SVNErrorMessage err = SVNErrorMessage
1109: .create(
1110: SVNErrorCode.WC_SCHEDULE_CONFLICT,
1111: "''{0}'' is marked as absent, so it cannot be scheduled for addition",
1112: name);
1113: SVNErrorManager.error(err);
1114: }
1115:
1116: if (SVNProperty.SCHEDULE_ADD.equals(entry.getSchedule())) {
1117: if (SVNProperty.SCHEDULE_DELETE.equals(schedule)) {
1118: if (!entry.isDeleted()) {
1119: deleteEntry(name);
1120: } else {
1121: attributes.put(SVNProperty
1122: .shortPropertyName(SVNProperty.SCHEDULE),
1123: null);
1124: }
1125: } else {
1126: attributes.remove(SVNProperty
1127: .shortPropertyName(SVNProperty.SCHEDULE));
1128: }
1129: } else if (SVNProperty.SCHEDULE_DELETE.equals(entry
1130: .getSchedule())) {
1131: if (SVNProperty.SCHEDULE_DELETE.equals(schedule)) {
1132: attributes.remove(SVNProperty
1133: .shortPropertyName(SVNProperty.SCHEDULE));
1134: } else if (SVNProperty.SCHEDULE_ADD.equals(schedule)) {
1135: attributes.put(SVNProperty
1136: .shortPropertyName(SVNProperty.SCHEDULE),
1137: SVNProperty.SCHEDULE_REPLACE);
1138: }
1139: } else if (SVNProperty.SCHEDULE_REPLACE.equals(entry
1140: .getSchedule())) {
1141: if (SVNProperty.SCHEDULE_DELETE.equals(schedule)) {
1142: attributes.put(SVNProperty
1143: .shortPropertyName(SVNProperty.SCHEDULE),
1144: SVNProperty.SCHEDULE_DELETE);
1145: } else if (SVNProperty.SCHEDULE_ADD.equals(schedule)
1146: || SVNProperty.SCHEDULE_REPLACE.equals(schedule)) {
1147: attributes.remove(SVNProperty
1148: .shortPropertyName(SVNProperty.SCHEDULE));
1149: }
1150: } else {
1151: if (SVNProperty.SCHEDULE_ADD.equals(schedule)
1152: && !entry.isDeleted()) {
1153: SVNErrorMessage err = SVNErrorMessage
1154: .create(
1155: SVNErrorCode.WC_SCHEDULE_CONFLICT,
1156: "Entry ''{0}'' is already under version control",
1157: name);
1158: SVNErrorManager.error(err);
1159: } else if (schedule == null) {
1160: attributes.remove(SVNProperty
1161: .shortPropertyName(SVNProperty.SCHEDULE));
1162: }
1163: }
1164: }
1165:
1166: public void modifyEntry(String name, Map attributes, boolean save,
1167: boolean force) throws SVNException {
1168: if (name == null) {
1169: name = getThisDirName();
1170: }
1171:
1172: boolean deleted = false;
1173: if (attributes.containsKey(SVNProperty
1174: .shortPropertyName(SVNProperty.SCHEDULE))) {
1175: SVNEntry entryBefore = getEntry(name, true);
1176: foldScheduling(name, attributes, force);
1177: SVNEntry entryAfter = getEntry(name, true);
1178: if (entryBefore != null && entryAfter == null) {
1179: deleted = true;
1180: }
1181: }
1182:
1183: if (!deleted) {
1184: SVNEntry entry = getEntry(name, true);
1185: if (entry == null) {
1186: entry = addEntry(name);
1187: }
1188:
1189: Map entryAttrs = entry.asMap();
1190: for (Iterator atts = attributes.keySet().iterator(); atts
1191: .hasNext();) {
1192: String attName = (String) atts.next();
1193: String value = (String) attributes.get(attName);
1194: if (SVNProperty.CACHABLE_PROPS.equals(attName)
1195: || SVNProperty.PRESENT_PROPS.equals(attName)) {
1196: String[] propsArray = SVNAdminArea.fromString(
1197: value, " ");
1198: entryAttrs.put(attName, propsArray);
1199: continue;
1200: } else if (!(SVNProperty.HAS_PROPS.equals(attName) || SVNProperty.HAS_PROP_MODS
1201: .equals(attName))) {
1202: attName = SVNProperty.SVN_ENTRY_PREFIX + attName;
1203: }
1204:
1205: if (value != null) {
1206: entryAttrs.put(attName, value);
1207: } else {
1208: entryAttrs.remove(attName);
1209: }
1210: }
1211:
1212: if (!entry.isDirectory()) {
1213: SVNEntry rootEntry = getEntry(getThisDirName(), true);
1214: if (rootEntry != null) {
1215: if (!SVNRevision.isValidRevisionNumber(entry
1216: .getRevision())) {
1217: entry.setRevision(rootEntry.getRevision());
1218: }
1219: if (entry.getURL() == null) {
1220: entry.setURL(SVNPathUtil.append(rootEntry
1221: .getURL(), SVNEncodingUtil
1222: .uriEncode(name)));
1223: }
1224: if (entry.getRepositoryRoot() == null) {
1225: entry.setRepositoryRoot(rootEntry
1226: .getRepositoryRoot());
1227: }
1228: if (entry.getUUID() == null
1229: && !entry.isScheduledForAddition()
1230: && !entry.isScheduledForReplacement()) {
1231: entry.setUUID(rootEntry.getUUID());
1232: }
1233: if (isEntryPropertyApplicable(SVNProperty.CACHABLE_PROPS)) {
1234: if (entry.getCachableProperties() == null) {
1235: entry.setCachableProperties(rootEntry
1236: .getCachableProperties());
1237: }
1238: }
1239: }
1240: }
1241:
1242: if (attributes.containsKey(SVNProperty
1243: .shortPropertyName(SVNProperty.SCHEDULE))
1244: && entry.isScheduledForDeletion()) {
1245: entry.setCopied(false);
1246: entry.setCopyFromRevision(-1);
1247: entry.setCopyFromURL(null);
1248: }
1249: }
1250:
1251: if (save) {
1252: saveEntries(false);
1253: }
1254: }
1255:
1256: public void deleteEntry(String name) throws SVNException {
1257: Map entries = loadEntries();
1258: if (entries != null) {
1259: entries.remove(name);
1260: }
1261: }
1262:
1263: public SVNEntry getEntry(String name, boolean hidden)
1264: throws SVNException {
1265: Map entries = loadEntries();
1266: if (entries != null && entries.containsKey(name)) {
1267: SVNEntry entry = (SVNEntry) entries.get(name);
1268: if (!hidden && entry.isHidden()) {
1269: return null;
1270: }
1271: return entry;
1272: }
1273: return null;
1274: }
1275:
1276: public SVNEntry addEntry(String name) throws SVNException {
1277: Map entries = loadEntries();
1278: if (entries == null) {
1279: myEntries = new HashMap();
1280: entries = myEntries;
1281: }
1282:
1283: SVNEntry entry = entries.containsKey(name) ? (SVNEntry) entries
1284: .get(name) : new SVNEntry(new HashMap(), this , name);
1285: entries.put(name, entry);
1286: return entry;
1287: }
1288:
1289: public Iterator entries(boolean hidden) throws SVNException {
1290: Map entries = loadEntries();
1291: if (entries == null) {
1292: return Collections.EMPTY_LIST.iterator();
1293: }
1294: List copy = new ArrayList(entries.values());
1295: if (!hidden) {
1296: for (Iterator iterator = copy.iterator(); iterator
1297: .hasNext();) {
1298: SVNEntry entry = (SVNEntry) iterator.next();
1299: if (entry.isHidden()) {
1300: iterator.remove();
1301: }
1302: }
1303: }
1304: Collections.sort(copy);
1305: return copy.iterator();
1306: }
1307:
1308: public Map getEntries() throws SVNException {
1309: return loadEntries();
1310: }
1311:
1312: public void cleanup() throws SVNException {
1313: getWCAccess().checkCancelled();
1314: for (Iterator entries = entries(false); entries.hasNext();) {
1315: SVNEntry entry = (SVNEntry) entries.next();
1316: if (entry.getKind() == SVNNodeKind.DIR
1317: && !getThisDirName().equals(entry.getName())) {
1318: File childDir = getFile(entry.getName());
1319: if (childDir.isDirectory()) {
1320: try {
1321: SVNAdminArea child = getWCAccess().open(
1322: childDir, true, true, 0);
1323: child.cleanup();
1324: } catch (SVNException e) {
1325: if (e instanceof SVNCancelException) {
1326: throw e;
1327: }
1328: if (isSafeCleanup()) {
1329: SVNDebugLog.getDefaultLog().info(
1330: "CLEANUP FAILED for " + childDir);
1331: SVNDebugLog.getDefaultLog().info(e);
1332: continue;
1333: }
1334: throw e;
1335: }
1336: }
1337: } else {
1338: hasPropModifications(entry.getName());
1339: if (entry.getKind() == SVNNodeKind.FILE) {
1340: hasTextModifications(entry.getName(), false);
1341: }
1342: }
1343: }
1344: if (isKillMe()) {
1345: removeFromRevisionControl(getThisDirName(), true, false);
1346: } else {
1347: runLogs();
1348: }
1349: SVNFileUtil.deleteAll(getAdminFile("tmp"), false);
1350: }
1351:
1352: public boolean hasTextConflict(String name) throws SVNException {
1353: SVNEntry entry = getEntry(name, false);
1354: if (entry == null || entry.getKind() != SVNNodeKind.FILE) {
1355: return false;
1356: }
1357: boolean conflicted = false;
1358: if (entry.getConflictNew() != null) {
1359: conflicted = SVNFileType.getType(getFile(entry
1360: .getConflictNew())) == SVNFileType.FILE;
1361: }
1362: if (!conflicted && entry.getConflictWorking() != null) {
1363: conflicted = SVNFileType.getType(getFile(entry
1364: .getConflictWorking())) == SVNFileType.FILE;
1365: }
1366: if (!conflicted && entry.getConflictOld() != null) {
1367: conflicted = SVNFileType.getType(getFile(entry
1368: .getConflictOld())) == SVNFileType.FILE;
1369: }
1370: return conflicted;
1371: }
1372:
1373: public boolean hasPropConflict(String name) throws SVNException {
1374: SVNEntry entry = getEntry(name, false);
1375: if (entry != null && entry.getPropRejectFile() != null) {
1376: return SVNFileType.getType(getFile(entry
1377: .getPropRejectFile())) == SVNFileType.FILE;
1378: }
1379: return false;
1380: }
1381:
1382: public File getRoot() {
1383: return myDirectory;
1384: }
1385:
1386: public File getAdminDirectory() {
1387: return myAdminRoot;
1388: }
1389:
1390: public File getAdminFile(String name) {
1391: return new File(getAdminDirectory(), name);
1392: }
1393:
1394: public File getFile(String name) {
1395: if (name == null) {
1396: return null;
1397: }
1398: return new File(getRoot(), name);
1399: }
1400:
1401: public SVNWCAccess getWCAccess() {
1402: return myWCAccess;
1403: }
1404:
1405: public void setWCAccess(SVNWCAccess wcAccess) {
1406: myWCAccess = wcAccess;
1407: }
1408:
1409: public void closeVersionedProperties() {
1410: myProperties = null;
1411: myBaseProperties = null;
1412: }
1413:
1414: public void closeWCProperties() {
1415: myWCProperties = null;
1416: }
1417:
1418: public void closeEntries() {
1419: myEntries = null;
1420: }
1421:
1422: public File getBaseFile(String name, boolean tmp) {
1423: String path = tmp ? "tmp/" : "";
1424: path += "text-base/" + name + ".svn-base";
1425: return getAdminFile(path);
1426: }
1427:
1428: protected abstract void writeEntries(Writer writer)
1429: throws IOException;
1430:
1431: protected abstract int getFormatVersion();
1432:
1433: protected abstract Map fetchEntries() throws SVNException;
1434:
1435: protected SVNAdminArea(File dir) {
1436: myDirectory = dir;
1437: myAdminRoot = new File(dir, SVNFileUtil.getAdminDirectoryName());
1438: }
1439:
1440: protected File getBasePropertiesFile(String name, boolean tmp) {
1441: String path = !tmp ? "" : "tmp/";
1442: path += getThisDirName().equals(name) ? "dir-prop-base"
1443: : "prop-base/" + name + ".svn-base";
1444: File propertiesFile = getAdminFile(path);
1445: return propertiesFile;
1446: }
1447:
1448: protected File getRevertPropertiesFile(String name, boolean tmp) {
1449: String path = !tmp ? "" : "tmp/";
1450: path += getThisDirName().equals(name) ? "dir-prop-revert"
1451: : "prop-base/" + name + ".svn-revert";
1452: File propertiesFile = getAdminFile(path);
1453: return propertiesFile;
1454: }
1455:
1456: public File getPropertiesFile(String name, boolean tmp) {
1457: String path = !tmp ? "" : "tmp/";
1458: path += getThisDirName().equals(name) ? "dir-props" : "props/"
1459: + name + ".svn-work";
1460: File propertiesFile = getAdminFile(path);
1461: return propertiesFile;
1462: }
1463:
1464: protected Map loadEntries() throws SVNException {
1465: if (myEntries != null) {
1466: return myEntries;
1467: }
1468: myEntries = fetchEntries();
1469: return myEntries;
1470: }
1471:
1472: protected Map getBasePropertiesStorage(boolean create) {
1473: if (myBaseProperties == null && create) {
1474: myBaseProperties = new HashMap();
1475: }
1476: return myBaseProperties;
1477: }
1478:
1479: protected Map getRevertPropertiesStorage(boolean create) {
1480: if (myRevertProperties == null && create) {
1481: myRevertProperties = new HashMap();
1482: }
1483: return myRevertProperties;
1484: }
1485:
1486: protected Map getPropertiesStorage(boolean create) {
1487: if (myProperties == null && create) {
1488: myProperties = new HashMap();
1489: }
1490: return myProperties;
1491: }
1492:
1493: protected Map getWCPropertiesStorage(boolean create) {
1494: if (myWCProperties == null && create) {
1495: myWCProperties = new HashMap();
1496: }
1497: return myWCProperties;
1498: }
1499:
1500: public static String asString(String[] array, String delimiter) {
1501: String str = null;
1502: if (array != null) {
1503: str = "";
1504: for (int i = 0; i < array.length; i++) {
1505: str += array[i];
1506: if (i < array.length - 1) {
1507: str += delimiter;
1508: }
1509: }
1510: }
1511: return str;
1512: }
1513:
1514: public static String[] fromString(String str, String delimiter) {
1515: if (str == null) {
1516: return new String[0];
1517: }
1518: LinkedList list = new LinkedList();
1519: int startInd = 0;
1520: int ind = -1;
1521: while ((ind = str.indexOf(delimiter, startInd)) != -1) {
1522: list.add(str.substring(startInd, ind));
1523: startInd = ind;
1524: while (startInd < str.length()
1525: && str.charAt(startInd) == ' ') {
1526: startInd++;
1527: }
1528: }
1529: if (startInd < str.length()) {
1530: list.add(str.substring(startInd));
1531: }
1532: return (String[]) list.toArray(new String[list.size()]);
1533: }
1534:
1535: private void destroyAdminArea() throws SVNException {
1536: if (!isLocked()) {
1537: SVNErrorMessage err = SVNErrorMessage.create(
1538: SVNErrorCode.WC_NOT_LOCKED,
1539: "Write-lock stolen in ''{0}''", getRoot());
1540: SVNErrorManager.error(err);
1541: }
1542: SVNFileUtil.deleteAll(getAdminDirectory(), getWCAccess());
1543: getWCAccess().closeAdminArea(getRoot());
1544: }
1545:
1546: public void commit(String target, SVNCommitInfo info,
1547: Map wcPropChanges, boolean removeLock, boolean recursive,
1548: Collection explicitCommitPaths, ISVNCommitParameters params)
1549: throws SVNException {
1550:
1551: SVNAdminArea anchor = getWCAccess().retrieve(
1552: getWCAccess().getAnchor());
1553: String path = getRelativePath(anchor);
1554: path = "".equals(target) ? path : SVNPathUtil.append(path,
1555: target);
1556: if (!explicitCommitPaths.contains(path)) {
1557: // if this item is explicitly copied -> skip it.
1558: SVNEntry entry = getEntry(target, true);
1559: if (entry != null && entry.getCopyFromURL() != null) {
1560: return;
1561: }
1562: }
1563:
1564: SVNLog log = getLog();
1565: //
1566: String checksum = null;
1567: Map command = new HashMap();
1568: if (!"".equals(target)) {
1569: File baseFile = getBaseFile(target, true);
1570: SVNFileType baseType = SVNFileType.getType(baseFile);
1571: if (baseType == SVNFileType.NONE) {
1572: baseFile = getBaseFile(target, false);
1573: baseType = SVNFileType.getType(baseFile);
1574: }
1575: if (baseType == SVNFileType.FILE) {
1576: checksum = SVNFileUtil.computeChecksum(baseFile);
1577: }
1578: File textRevertFile = getFile(SVNAdminUtil
1579: .getTextRevertPath(target, false));
1580: File propRevertFile = getFile(SVNAdminUtil
1581: .getPropRevertPath(target, SVNNodeKind.FILE, false));
1582: if (textRevertFile.isFile()) {
1583: command.put(SVNLog.NAME_ATTR, SVNAdminUtil
1584: .getTextRevertPath(target, false));
1585: log.addCommand(SVNLog.DELETE, command, false);
1586: command.clear();
1587: }
1588: if (propRevertFile.isFile()) {
1589: command.put(SVNLog.NAME_ATTR, SVNAdminUtil
1590: .getPropRevertPath(target, SVNNodeKind.FILE,
1591: false));
1592: log.addCommand(SVNLog.DELETE, command, false);
1593: command.clear();
1594: }
1595: command.clear();
1596: recursive = false;
1597: } else {
1598:
1599: }
1600: if (info != null) {
1601: command.put(SVNLog.NAME_ATTR, target);
1602: command.put(SVNProperty
1603: .shortPropertyName(SVNProperty.COMMITTED_REVISION),
1604: Long.toString(info.getNewRevision()));
1605: command.put(SVNProperty
1606: .shortPropertyName(SVNProperty.COMMITTED_DATE),
1607: SVNTimeUtil.formatDate(info.getDate()));
1608: command.put(SVNProperty
1609: .shortPropertyName(SVNProperty.LAST_AUTHOR), info
1610: .getAuthor());
1611: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
1612: command.clear();
1613: }
1614: if (checksum != null) {
1615: command.put(SVNLog.NAME_ATTR, target);
1616: command.put(SVNProperty
1617: .shortPropertyName(SVNProperty.CHECKSUM), checksum);
1618: log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
1619: command.clear();
1620: }
1621: if (removeLock) {
1622: command.put(SVNLog.NAME_ATTR, target);
1623: log.addCommand(SVNLog.DELETE_LOCK, command, false);
1624: command.clear();
1625: }
1626: command.put(SVNLog.NAME_ATTR, target);
1627: command.put(SVNLog.REVISION_ATTR, info == null ? null : Long
1628: .toString(info.getNewRevision()));
1629: if (!explicitCommitPaths.contains(path)) {
1630: command.put("implicit", "true");
1631: }
1632: log.addCommand(SVNLog.COMMIT, command, false);
1633: command.clear();
1634: if (wcPropChanges != null && !wcPropChanges.isEmpty()) {
1635: for (Iterator propNames = wcPropChanges.keySet().iterator(); propNames
1636: .hasNext();) {
1637: String propName = (String) propNames.next();
1638: String propValue = (String) wcPropChanges.get(propName);
1639: command.put(SVNLog.NAME_ATTR, target);
1640: command.put(SVNLog.PROPERTY_NAME_ATTR, propName);
1641: command.put(SVNLog.PROPERTY_VALUE_ATTR, propValue);
1642: log.addCommand(SVNLog.MODIFY_WC_PROPERTY, command,
1643: false);
1644: command.clear();
1645: }
1646: }
1647: log.save();
1648: runLogs();
1649:
1650: if (recursive) {
1651: for (Iterator ents = entries(true); ents.hasNext();) {
1652: SVNEntry entry = (SVNEntry) ents.next();
1653: if ("".equals(entry.getName())) {
1654: continue;
1655: }
1656: if (entry.getKind() == SVNNodeKind.DIR) {
1657: File childPath = getFile(entry.getName());
1658: SVNAdminArea childDir = getWCAccess().retrieve(
1659: childPath);
1660: if (childDir != null) {
1661: childDir.commit("", info, null, removeLock,
1662: true, explicitCommitPaths, params);
1663: }
1664: } else {
1665: commit(entry.getName(), info, null, removeLock,
1666: false, explicitCommitPaths, params);
1667: }
1668: }
1669: }
1670: }
1671:
1672: protected void setLocked(boolean locked) {
1673: myWasLocked = locked;
1674: }
1675:
1676: public void setCommitParameters(
1677: ISVNCommitParameters commitParameters) {
1678: myCommitParameters = commitParameters;
1679: }
1680: }
|