0001: package org.antmod.scm.impl;
0002:
0003: import java.io.*;
0004: import java.util.ArrayList;
0005: import java.util.Collections;
0006:
0007: import org.antmod.conf.AntmodProperties;
0008: import org.antmod.scm.ScmDifference;
0009: import org.antmod.scm.ScmSystem;
0010: import org.antmod.scm.ScmUrl;
0011: import org.antmod.scm.ScmVersion;
0012: import org.antmod.util.ProcessLauncher;
0013: import org.apache.commons.io.FileUtils;
0014: import org.apache.commons.io.HexDump;
0015: import org.apache.commons.io.IOUtils;
0016: import org.apache.commons.lang.StringUtils;
0017: import org.apache.commons.lang.SystemUtils;
0018: import org.apache.tools.ant.BuildException;
0019: import org.apache.tools.ant.taskdefs.FixCRLF;
0020:
0021: /**
0022: * CVS repository provider, providing access to CVS
0023: * repositories from Antmod.
0024: * <p/>
0025: * This CVS repository provider is a thin layer on top
0026: * of the "cvs" commandline executable, and as such
0027: * requires the "cvs" executable to be in the PATH
0028: * of the system.
0029: *
0030: * @author Klaas Waslander
0031: */
0032: public class CvsSystemImpl implements ScmSystem {
0033: public final static char REVISION_NAME_SEPARATOR = '_';
0034: public final static char REVISION_VERSION_SEPARATOR = '-';
0035:
0036: private ScmUrl url;
0037: private String standardOutput;
0038: private String errorOutput;
0039:
0040: /**
0041: * Public default onstructor.
0042: */
0043: public CvsSystemImpl() {
0044: }
0045:
0046: public String getStandardOutput() {
0047: return standardOutput;
0048: }
0049:
0050: public String getErrorOutput() {
0051: return errorOutput;
0052: }
0053:
0054: /* (non-Javadoc)
0055: * @see org.antmod.scm.ScmSystem#getUrl()
0056: */
0057: public ScmUrl getUrl() {
0058: return url;
0059: }
0060:
0061: /* (non-Javadoc)
0062: * @see org.antmod.scm.ScmSystem#setUrl(org.antmod.scm.ScmUrl)
0063: */
0064: public void setUrl(ScmUrl providerUrl) {
0065: this .url = providerUrl;
0066: }
0067:
0068: public void doAdd(File file, boolean recursive) {
0069: // check for parent "CVS" folder
0070: File parentDir = file.getParentFile();
0071: File parentCVSDir = new File(parentDir, "CVS");
0072: boolean cvsDirCreated = false;
0073: if (!parentCVSDir.exists()) {
0074: // let's create CVS directory temporarily...
0075: try {
0076: //
0077: // IMPORTANT NOTE (Related to issue #36):
0078: // When adding SystemUtils.LINE_SEPARATOR to these 3 CVS files
0079: // the cvs client with cygwin and windows does not work at all.
0080: // Removed addition of line separator, as it was not needed
0081: //
0082:
0083: parentCVSDir.mkdir();
0084:
0085: File cvsRootFile = new File(parentCVSDir, "Root");
0086: FileUtils.touch(cvsRootFile);
0087: FileUtils.writeStringToFile(cvsRootFile,
0088: renderUrlToCvsRoot(getUrl()),
0089: SystemUtils.FILE_ENCODING);
0090:
0091: String reposString = ".";
0092: if (!StringUtils.isBlank(getUrl().getModule())) {
0093: reposString = getUrl().getModule();
0094: }
0095: File cvsReposFile = new File(parentCVSDir, "Repository");
0096: FileUtils.touch(cvsReposFile);
0097: FileUtils.writeStringToFile(cvsReposFile, reposString,
0098: SystemUtils.FILE_ENCODING);
0099:
0100: // empty Entries file, to prevent warning by cvs command
0101: File cvsEntriesFile = new File(parentCVSDir, "Entries");
0102: FileUtils.touch(cvsEntriesFile);
0103: } catch (IOException e) {
0104: throw new IllegalStateException(
0105: "Creating CVS/ directory entries not possible: "
0106: + e);
0107: }
0108: cvsDirCreated = true;
0109: }
0110:
0111: // recursively add
0112: ArrayList cvsCommandList = new ArrayList(2);
0113: cvsCommandList.add("add");
0114: cvsCommandList.add(file.getName());
0115: run(parentDir, cvsCommandList);
0116: if (recursive) {
0117: recursiveAdd(file);
0118: }
0119: }
0120:
0121: // recursively add files in given dir to CVS, assuming dir itself has already been added
0122: private void recursiveAdd(File parentDir) {
0123: if (!parentDir.isDirectory()) {
0124: return;
0125: }
0126: File[] files = parentDir.listFiles();
0127: if (files != null) {
0128: for (int i = 0; i < files.length; i++) {
0129: File childFile = files[i];
0130: if (!childFile.getName().equals("CVS")) {
0131: ArrayList cvsCommandList = new ArrayList(2);
0132: cvsCommandList.add("add");
0133: cvsCommandList.add(childFile.getName());
0134: run(parentDir, cvsCommandList);
0135: if (childFile.isDirectory()) {
0136: recursiveAdd(childFile);
0137: }
0138: }
0139: }
0140: }
0141: }
0142:
0143: /* (non-Javadoc)
0144: * @see org.antmod.scm.ScmSystem#doCheckout(java.lang.String, java.lang.String, java.lang.String, boolean)
0145: */
0146: public void doCheckout(String moduleName, File destDir,
0147: ScmVersion version, boolean reallyQuiet) {
0148: if (destDir == null) {
0149: throw new IllegalArgumentException(
0150: "destDir attribute for cvs checkout must not be null");
0151: }
0152: if (destDir.getParentFile() == null
0153: || !destDir.getParentFile().exists()) {
0154: throw new IllegalArgumentException(
0155: "destDir parent directory (basedir for the checkout) for cvs checkout does not exist on filesystem: "
0156: + destDir.getParentFile());
0157: }
0158: if (StringUtils.isBlank(moduleName)) {
0159: throw new IllegalArgumentException(
0160: "No modulename specified for CVS checkout to directory: "
0161: + destDir.getPath());
0162: }
0163:
0164: ArrayList cvsCommandList = new ArrayList();
0165: if (reallyQuiet) {
0166: cvsCommandList.add("-Q");
0167: }
0168: cvsCommandList.add("checkout");
0169: cvsCommandList.add("-P");
0170: cvsCommandList.add("-d");
0171: cvsCommandList.add(destDir.getName());
0172: addCvsRevisionCommand(version, cvsCommandList);
0173: cvsCommandList.add(moduleName);
0174:
0175: boolean suppressErrorOutput = reallyQuiet;
0176: boolean suppressStandardOutput = reallyQuiet;
0177: run(destDir.getParentFile(), cvsCommandList,
0178: suppressErrorOutput, suppressStandardOutput);
0179: if (!suppressStandardOutput) {
0180: this .standardOutput = null;
0181: }
0182: }
0183:
0184: /* (non-Javadoc)
0185: * @see org.antmod.scm.ScmSystem#doExport(java.lang.String, java.lang.String, java.lang.String, boolean)
0186: */
0187: public void doExport(String moduleName, File destDir,
0188: ScmVersion version, boolean reallyQuiet) {
0189: if (destDir == null) {
0190: throw new IllegalArgumentException(
0191: "destDir attribute for cvs export must not be null");
0192: }
0193: if (destDir.getParentFile() == null
0194: || !destDir.getParentFile().exists()) {
0195: throw new IllegalArgumentException(
0196: "destDir parent directory (basedir for the export) for cvs export does not exist on filesystem: "
0197: + destDir.getParentFile());
0198: }
0199:
0200: //
0201: // do the export
0202: //
0203: ArrayList cvsCommandList = new ArrayList();
0204: if (reallyQuiet) {
0205: cvsCommandList.add("-Q");
0206: }
0207: cvsCommandList.add("export");
0208: cvsCommandList.add("-d ");
0209: cvsCommandList.add(destDir.getName());
0210:
0211: if (version == null || version.isTrunk()) {
0212: // in export, a "-r" is always needed; handle that here
0213: cvsCommandList.add("-r");
0214: cvsCommandList.add("HEAD");
0215: } else {
0216: addCvsRevisionCommand(version, cvsCommandList);
0217: }
0218:
0219: cvsCommandList.add(moduleName);
0220:
0221: boolean suppressErrorOutput = reallyQuiet;
0222: boolean suppressStandardOutput = reallyQuiet;
0223: run(destDir.getParentFile(), cvsCommandList,
0224: suppressErrorOutput, suppressStandardOutput);
0225: if (!suppressStandardOutput) {
0226: this .standardOutput = null;
0227: }
0228: }
0229:
0230: public void doCheckoutOrUpdate(String packageName, File destDir,
0231: ScmVersion version, boolean reallyQuiet) {
0232: if (isCheckoutDir(destDir)) {
0233: doUpdate(packageName, destDir, version, reallyQuiet);
0234: } else {
0235: doCheckout(packageName, destDir, version, reallyQuiet);
0236: }
0237: }
0238:
0239: public void doMerge(File moduleDir, ScmVersion version) {
0240: doMerge(moduleDir, version, false);
0241: }
0242:
0243: public void doMerge(File moduleDir, ScmVersion version,
0244: boolean reallyQuiet) {
0245: if (moduleDir == null) {
0246: throw new IllegalArgumentException(
0247: "moduleDir attribute for cvs merge must not be null");
0248: }
0249: if (!moduleDir.exists()) {
0250: throw new IllegalArgumentException(
0251: "moduleDir for cvs merge does not exist on filesystem: "
0252: + moduleDir.getPath());
0253: }
0254: ArrayList cvsCommandList = new ArrayList();
0255: cvsCommandList.add("update");
0256: cvsCommandList.add("-dP");
0257: cvsCommandList.add("-j");
0258: if (version == null || version.isTrunk()) {
0259: cvsCommandList.add("HEAD");
0260: } else {
0261: cvsCommandList.add(version.getModuleName()
0262: + REVISION_NAME_SEPARATOR
0263: + version.toString(REVISION_VERSION_SEPARATOR));
0264: }
0265:
0266: // run merge, suppress output properly
0267: boolean suppressErrorOutput = reallyQuiet;
0268: boolean suppressStandardOutput = reallyQuiet;
0269: if (moduleDir.isDirectory()) {
0270: run(moduleDir, cvsCommandList, suppressErrorOutput,
0271: suppressStandardOutput);
0272: } else {
0273: cvsCommandList.add(moduleDir.getName());
0274: run(moduleDir.getParentFile(), cvsCommandList,
0275: suppressErrorOutput, suppressStandardOutput);
0276: }
0277: if (!suppressStandardOutput) {
0278: this .standardOutput = null;
0279: }
0280: }
0281:
0282: public void doUpdate(File file, ScmVersion version) {
0283: doUpdate(file.getName(), file, version, false);
0284: }
0285:
0286: public void doUpdate(String packageName, File file,
0287: ScmVersion version, boolean reallyQuiet) {
0288: if (file == null) {
0289: throw new IllegalArgumentException(
0290: "file attribute for cvs update must not be null");
0291: }
0292: if (!file.exists()) {
0293: throw new IllegalArgumentException(
0294: "file for cvs update does not exist on filesystem: "
0295: + file.getPath());
0296: }
0297: ArrayList cvsCommandList = new ArrayList();
0298: cvsCommandList.add("update");
0299: cvsCommandList.add("-dP");
0300: if (version != null) {
0301: addCvsRevisionCommand(version, cvsCommandList);
0302: }
0303:
0304: boolean suppressErrorOutput = reallyQuiet;
0305: boolean suppressStandardOutput = reallyQuiet;
0306: if (file.isDirectory()) {
0307: run(file, cvsCommandList, suppressErrorOutput,
0308: suppressStandardOutput);
0309: } else {
0310: cvsCommandList.add(file.getName());
0311: run(file.getParentFile(), cvsCommandList,
0312: suppressErrorOutput, suppressStandardOutput);
0313: }
0314: /*
0315: if (!reallyQuiet && !StringUtils.isBlank(resultOutput)) {
0316: System.err.println(resultOutput.trim());
0317: this.standardOutput = null;
0318: }
0319: */
0320: if (!suppressStandardOutput) {
0321: this .standardOutput = null;
0322: }
0323: }
0324:
0325: /**
0326: * Commit the given file or a while directory to CVS.
0327: * @param file
0328: */
0329: public void doCommit(File file, String message) {
0330: if (file == null) {
0331: throw new IllegalArgumentException(
0332: "file attribute for cvs commit must not be null");
0333: }
0334: if (message == null) {
0335: throw new IllegalArgumentException(
0336: "message attribute for cvs commit must not be null");
0337: }
0338:
0339: ArrayList cvsCommandList = new ArrayList();
0340: cvsCommandList.add("commit");
0341: cvsCommandList.add("-m");
0342: cvsCommandList.add(message);
0343: // REMIND: Klaas, Feb 2005: previously, spaces needed to be replaced, now probably this is not necessary anymore
0344: //cvsCommandList.add(message.replace(' ', '_'));
0345:
0346: // commit either whole directory, or just one file.
0347: if (file.isDirectory()) {
0348: run(file, cvsCommandList);
0349: } else {
0350: cvsCommandList.add(file.getName());
0351: run(file.getParentFile(), cvsCommandList);
0352: }
0353: }
0354:
0355: /**
0356: * Returns the latest file revision.
0357: */
0358: public String getRevisionNumber(File file) {
0359: if (file.isDirectory()) {
0360: throw new IllegalArgumentException(
0361: "File for 'getRevisionNumber' is a directory: "
0362: + file);
0363: }
0364:
0365: ArrayList cvsCommandList = new ArrayList();
0366: cvsCommandList.add("status");
0367: cvsCommandList.add(file.getName());
0368: String status = run(file.getParentFile(), cvsCommandList);
0369: String searchKey = "Working revision:";
0370: int index = status.indexOf(searchKey);
0371: if (index > 0) {
0372: status = status.substring(index + searchKey.length())
0373: .trim();
0374: int endIndex = Math.min(status.indexOf("\t"), status
0375: .indexOf(SystemUtils.LINE_SEPARATOR));
0376: if (endIndex > 0) {
0377: return status.substring(0, endIndex).trim();
0378: }
0379: }
0380: return null;
0381: }
0382:
0383: /**
0384: * If the given module directory is not a tag, returns the latest version for that directory.
0385: * @return null if no latest version is found
0386: */
0387: public ScmVersion getLatestVersion(File moduleDir) {
0388: ScmVersion localVersion = getLocalVersion(moduleDir);
0389: if (localVersion.isTag()) {
0390: throw new RuntimeException(
0391: "getLatestVersion is not possible on a cvs tag.");
0392: }
0393:
0394: if (!new File(moduleDir, "module.xml").exists()) {
0395: throw new RuntimeException(
0396: "FATAL: File module.xml does not exist in the module "
0397: + moduleDir.getName() + " !!!");
0398: }
0399:
0400: ScmVersion[] revs = getVersionsInBranch(new File(moduleDir,
0401: "module.xml"), localVersion);
0402: if (revs.length > 0) {
0403: return revs[0];
0404: } else {
0405: return null;
0406: }
0407: }
0408:
0409: /**
0410: * Returns the currently checked out version of the module in the given directory.
0411: * @param moduleDir The directory where the module is currently checked out
0412: * @return The current local version of the module
0413: * @throws org.apache.tools.ant.BuildException
0414: */
0415: public ScmVersion getLocalVersion(File moduleDir)
0416: throws BuildException {
0417: String localVersion = readTagString(moduleDir);
0418: if (localVersion == null || localVersion.equals("HEAD")) {
0419: localVersion = ScmVersion.VERSIONSTRING_TRUNK;
0420: } else {
0421: // strip until last "_", as that is separator between modulename and version
0422: int lastUnderscore = localVersion.lastIndexOf("_");
0423: if (lastUnderscore > 0) {
0424: localVersion = localVersion
0425: .substring(lastUnderscore + 1);
0426: } else {
0427: throw new BuildException("Module "
0428: + moduleDir.getName()
0429: + " has invalid local version tag: "
0430: + localVersion);
0431: }
0432: }
0433: return parseCvsRevision(moduleDir.getName(), localVersion);
0434: }
0435:
0436: /**
0437: * Reads current CVS tag from the given module directory. Returns empty
0438: * string if nothing could be read. [quickie version of local CVS status]
0439: *
0440: * @param moduleDir Module directory to read tag from
0441: * @return CVS tag.
0442: * @throws org.apache.tools.ant.BuildException If unable to read CVS tag.
0443: */
0444: private static String readTagString(File moduleDir)
0445: throws BuildException {
0446: String tag = null;
0447: try {
0448: File fh = new File(moduleDir + File.separator + "CVS"
0449: + File.separator + "Tag");
0450: if (fh.exists()) {
0451: BufferedReader rd = new BufferedReader(new FileReader(
0452: fh));
0453: rd.read();
0454: tag = rd.readLine();
0455: rd.close();
0456: } else {
0457: //
0458: // If "Tag" file does not exist,
0459: // Let's read the "Entries" file and parse that to figure out the CSV tag.
0460: fh = new File(moduleDir + File.separator + "CVS"
0461: + File.separator + "Entries");
0462: if (fh.exists()) {
0463: BufferedReader rd = new BufferedReader(
0464: new FileReader(fh));
0465: rd.read();
0466:
0467: // try each line
0468: String searchFor = "/T" + moduleDir.getName();
0469: String line;
0470: while ((line = rd.readLine()) != null) {
0471: int searchForIndex = line.indexOf(searchFor);
0472: if (searchForIndex > 0) {
0473: tag = line.substring(searchForIndex + 2);
0474: break;
0475: }
0476: }
0477: rd.close();
0478: }
0479: }
0480: } catch (Exception e) {
0481: throw new BuildException("Problem reading local tag: " + e);
0482: }
0483: return tag;
0484: }
0485:
0486: /**
0487: * Returns all available versions for the given file in the given branch,
0488: * with the newest number first and the oldest number last (oldest is usually the ".0" version).
0489: */
0490: public ScmVersion[] getVersionsInBranch(File file, ScmVersion branch) {
0491: if (file == null) {
0492: throw new IllegalArgumentException(
0493: "File attribute for cvs revisions must not be null");
0494: }
0495: String branchName = renderCvsRevision(branch);
0496:
0497: // do two digit versions?
0498: boolean twoDigit = false;
0499: if (branch == null || branch.isTrunk()) {
0500: // 2 digit versions only
0501: twoDigit = true;
0502: }
0503:
0504: ArrayList result = new ArrayList();
0505:
0506: //
0507: // get cvs output and PARSE it
0508: //
0509: File moduleFile = file.getParentFile();
0510: ArrayList cvsCommandList = new ArrayList(3);
0511: cvsCommandList.add("status");
0512: cvsCommandList.add("-v");
0513: cvsCommandList.add(file.getName());
0514: String cvsResult = run(moduleFile, cvsCommandList);
0515: try {
0516: BufferedReader reader = new BufferedReader(
0517: new StringReader(cvsResult));
0518: String line;
0519: boolean started = false; // started reading tags already?
0520: while ((line = reader.readLine()) != null) {
0521: if (started) {
0522: //System.err.println("Consider line: " + line);
0523: if (line.trim().length() > 0) {
0524: // let's see if we need to add this tag...
0525: line = line.trim();
0526: int tabIndex = line.indexOf("\t");
0527: if (tabIndex > 0) {
0528: line = line.substring(0, tabIndex).trim();
0529: }
0530: //System.err.println(branchName + " POTENTIAL TAG FOUND: \"" + line + "\"");
0531:
0532: // tagname must start with the name of this module
0533: if (line.startsWith(file.getParentFile()
0534: .getName()
0535: + REVISION_NAME_SEPARATOR)) {
0536: if (twoDigit) {
0537: //System.err.println(branchName + " TWO DIGIT " + countVersionChars(REVISION_VERSION_SEPARATOR, line));
0538: if (countVersionChars(
0539: REVISION_VERSION_SEPARATOR,
0540: line) == 1) {
0541: result
0542: .add(parseCvsRevision(
0543: moduleFile
0544: .getName(),
0545: line));
0546: }
0547: } else {
0548: //System.err.println("startswith: " + line.trim().startsWith(branchName) + ", COUNTCHARS:" + countVersionChars(REVISION_VERSION_SEPARATOR, line));
0549: // only add versions starting with branch name
0550: if (line.trim().startsWith(branchName)
0551: && countVersionChars(
0552: REVISION_VERSION_SEPARATOR,
0553: line) > 1) {
0554: //System.err.println(" ADD 3 digit TAG!!!");
0555: result
0556: .add(parseCvsRevision(
0557: moduleFile
0558: .getName(),
0559: line));
0560: }
0561: }
0562: }
0563: } else {
0564: //System.err.println("BAILOUT ON LINE: " + line);
0565: // we are past the last tag, let's bail out
0566: //break;
0567: }
0568: }
0569:
0570: // check whether to start with parsing versions at the next line
0571: if (line.toLowerCase().indexOf("existing tags") >= 0) {
0572: started = true;
0573: }
0574: }
0575:
0576: } catch (IOException ioe) {
0577: ioe.printStackTrace();
0578: }
0579:
0580: Collections.sort(result);
0581:
0582: ScmVersion[] array = new ScmVersion[result.size()];
0583: result.toArray(array);
0584: return array;
0585: }
0586:
0587: /**
0588: * Implementation of the ScmDifference interface for returning cvs diff entries in this class.
0589: * @author Klaas Waslander
0590: */
0591: private static class ScmDifferenceImpl implements ScmDifference {
0592: String filename;
0593: boolean conflict = false;
0594: StringBuffer log = new StringBuffer();
0595:
0596: ScmDifferenceImpl(String filename) {
0597: this .filename = filename;
0598: }
0599:
0600: public String getFilename() {
0601: return filename;
0602: }
0603:
0604: public boolean hasConflict() {
0605: return conflict;
0606: }
0607:
0608: private void setConflict(boolean conflict) {
0609: this .conflict = conflict;
0610: }
0611:
0612: public String getLog() {
0613: return log.toString();
0614: }
0615:
0616: private void addLogLine(String logLine) {
0617: this .log.append(logLine);
0618: this .log.append(HexDump.EOL);
0619: }
0620: }
0621:
0622: /**
0623: * Check whether the given checkout directory is up-to-date
0624: * when comparing it to the repository contents.
0625: * @param checkoutDir The directory with locally checked out contents
0626: * @return Whether the checkoutDir is up-to-date
0627: */
0628: public boolean isUpToDate(File checkoutDir) {
0629: //
0630: // get cvs output and PARSE it
0631: //
0632: ArrayList cvsCommandList = new ArrayList();
0633: cvsCommandList.add("-Q");
0634: cvsCommandList.add("status");
0635: String cvsResult = run(checkoutDir, cvsCommandList);
0636: try {
0637: BufferedReader reader = new BufferedReader(
0638: new StringReader(cvsResult));
0639: String line;
0640:
0641: while ((line = reader.readLine()) != null) {
0642: if (line.startsWith("File:")) {
0643: int statusIndex = line.indexOf("Status:");
0644: if (statusIndex < 0) {
0645: throw new IllegalStateException(
0646: "Every cvs-status line should contain 'Status:', but this line doesn't? \""
0647: + line + "\"");
0648: }
0649: String statusString = line.substring(
0650: statusIndex + 7).trim();
0651: if (!statusString.equals("Up-to-date")
0652: && !statusString.equals("Unknown")) {
0653: return false;
0654: }
0655: }
0656: }
0657: } catch (IOException ioe) {
0658: ioe.printStackTrace();
0659: }
0660: return true;
0661: }
0662:
0663: /**
0664: * Returns the files that have changed between the two given cvs revisions.
0665: */
0666: public ScmDifference[] getDifferences(ScmVersion version1,
0667: ScmVersion version2) {
0668: if (version1.getModuleName() == null) {
0669: throw new IllegalArgumentException(
0670: "Modulename attribute (of version1) for cvsdiffs must not be null");
0671: }
0672: if (version2.getModuleName() == null) {
0673: throw new IllegalArgumentException(
0674: "Modulename attribute (of version2) for cvsdiffs must not be null");
0675: }
0676: if (!version1.getModuleName().equals(version2.getModuleName())) {
0677: throw new IllegalArgumentException("Module of version1 \""
0678: + version1.getModuleName()
0679: + "\" is not the same as module of version2 \""
0680: + version2.getModuleName() + "\"");
0681: }
0682:
0683: String cvsRev1 = renderCvsRevision(version1);
0684: if (version1.isTrunk()) {
0685: cvsRev1 = "HEAD";
0686: }
0687: String cvsRev2 = renderCvsRevision(version2);
0688: if (version2.isTrunk()) {
0689: cvsRev2 = "HEAD";
0690: }
0691:
0692: ArrayList result = new ArrayList();
0693:
0694: //
0695: // get cvs output and PARSE it
0696: //
0697: ArrayList cvsCommandList = new ArrayList();
0698: cvsCommandList.add("rdiff");
0699: cvsCommandList.add("-R");
0700: cvsCommandList.add("-r");
0701: cvsCommandList.add(cvsRev1);
0702: cvsCommandList.add("-r");
0703: cvsCommandList.add(cvsRev2);
0704: cvsCommandList.add(version1.getModuleName());
0705: String cvsResult = run(null, cvsCommandList);
0706: try {
0707: BufferedReader reader = new BufferedReader(
0708: new StringReader(cvsResult));
0709: String line;
0710: ScmDifferenceImpl currentEntry = null;
0711:
0712: while ((line = reader.readLine()) != null) {
0713: if (line.startsWith("Index:")) {
0714: // strip off "Index:"
0715: String file = line.substring(7).trim();
0716: // strip off module name
0717: file = file.substring(version1.getModuleName()
0718: .length() + 1);
0719: // add diff entry object
0720: currentEntry = new ScmDifferenceImpl(file);
0721: result.add(currentEntry);
0722:
0723: } else if (currentEntry != null) {
0724: currentEntry.addLogLine(line);
0725:
0726: if (line.startsWith("! ")) {
0727: currentEntry.setConflict(true);
0728: }
0729: }
0730: }
0731: } catch (IOException ioe) {
0732: ioe.printStackTrace();
0733: }
0734:
0735: ScmDifference[] array = new ScmDifference[result.size()];
0736: result.toArray(array);
0737: return array;
0738: }
0739:
0740: /**
0741: * Creates a new branch in the HEAD of the given module.
0742: */
0743: public String createBranchInTrunk(ScmVersion newBranchForModule) {
0744: if (newBranchForModule == null) {
0745: throw new IllegalArgumentException(
0746: "newBranchForModule attribute for createBranchInTrunk must not be null");
0747: }
0748: ArrayList cvsCommandList = new ArrayList();
0749: cvsCommandList.add("rtag");
0750: cvsCommandList.add("-b");
0751: cvsCommandList.add("-R");
0752: cvsCommandList.add(renderCvsRevision(newBranchForModule));
0753: cvsCommandList.add(newBranchForModule.getModuleName());
0754: //return run(null, "rtag -b -R " + renderCvsRevision(newBranchForModule) + " " + newBranchForModule.getModuleName());
0755: return run(null, cvsCommandList);
0756: }
0757:
0758: /**
0759: * Creates a new tag in the given BRANCH of the given module.
0760: */
0761: public String createTagInBranch(ScmVersion existingBranch,
0762: ScmVersion newTag) {
0763: if (existingBranch == null) {
0764: throw new IllegalArgumentException(
0765: "existingBranch attribute for createTagInBranch must not be null");
0766: }
0767: if (newTag == null) {
0768: throw new IllegalArgumentException(
0769: "newTag attribute for createTagInBranch must not be null");
0770: }
0771: if (existingBranch.getModuleName() == null) {
0772: throw new IllegalArgumentException(
0773: "moduleName of existing branch for createTagInBranch must not be null");
0774: }
0775: ArrayList cvsCommandList = new ArrayList();
0776: cvsCommandList.add("rtag");
0777: cvsCommandList.add("-R");
0778: cvsCommandList.add("-r");
0779: cvsCommandList.add(renderCvsRevision(existingBranch));
0780: cvsCommandList.add(renderCvsRevision(newTag));
0781: cvsCommandList.add(existingBranch.getModuleName());
0782: //return run(null, "rtag -R -r " + renderCvsRevision(existingBranch) + " " + renderCvsRevision(newTag) + " " + existingBranch.getModuleName());
0783: return run(null, cvsCommandList);
0784: }
0785:
0786: /**
0787: * Utility method for counting the number of given characters in the given string.
0788: */
0789: static int countVersionChars(char c, String s) {
0790: // first strip off modulename
0791: int lastUnderscore = s.lastIndexOf("_");
0792: if (lastUnderscore > 0) {
0793: s = s.substring(lastUnderscore + 1);
0794: }
0795:
0796: // next count the occurences of char c in string s
0797: return StringUtils.countMatches(s, String.valueOf(c));
0798: }
0799:
0800: /**
0801: * Runs the given cvs command in the given directory, and returns standard output!
0802: * @param baseDir
0803: * @param command
0804: * @return
0805: */
0806: private String run(File baseDir, ArrayList commandList) {
0807: return run(baseDir, commandList, false);
0808: }
0809:
0810: private String run(File baseDir, ArrayList commandList,
0811: boolean suppressErrorOutput) {
0812: return run(baseDir, commandList, suppressErrorOutput, true);
0813: }
0814:
0815: /**
0816: * Runs the given cvs command in the given directory, and returns standard output!
0817: * @param baseDir
0818: * @param command
0819: * @return
0820: */
0821: private String run(File baseDir, ArrayList commandList,
0822: boolean suppressErrorOutput,
0823: final boolean suppressStandardOutput) {
0824: // work with copy for local changes
0825: commandList = new ArrayList(commandList);
0826: ArrayList orgCommandList = new ArrayList(commandList);
0827:
0828: // make it quiet if not done already
0829: if (commandList.indexOf("-q") < 0
0830: && commandList.indexOf("-Q") < 0) {
0831: commandList.add(0, "-q");
0832: }
0833: // add CVS root
0834: commandList.add(0, "-d");
0835: commandList.add(1, renderUrlToCvsRoot(getUrl()));
0836:
0837: // of course, prepend the "cvs" executable to invoke
0838: commandList.add(0, AntmodProperties
0839: .getProperty("antmod.scm.cvs.executable"));
0840:
0841: // create launcher
0842: ProcessLauncher launcher = new ProcessLauncher(commandList,
0843: baseDir);
0844:
0845: final StringBuffer stdOut = new StringBuffer();
0846: final StringBuffer stdErr = new StringBuffer();
0847: stdOut.setLength(0);
0848: stdErr.setLength(0);
0849: launcher
0850: .addOutputListener(new ProcessLauncher.OutputListener() {
0851: public void standardOutput(char[] output) {
0852: stdOut.append(output);
0853:
0854: if (!suppressStandardOutput) {
0855: System.err.print(output);
0856: }
0857: }
0858:
0859: public void errorOutput(char[] output) {
0860: stdErr.append(output);
0861: }
0862: });
0863:
0864: // launch and wait until done...
0865: launcher.launch();
0866:
0867: // if there is error output, log it for now
0868: if (!suppressErrorOutput && stdErr.length() > 0) {
0869: //System.err.println("CVS command 'cvs " + command + "' produced error output:");
0870: System.err.println(stdErr.toString());
0871: }
0872:
0873: // return standard output
0874: this .standardOutput = stdOut.toString();
0875: this .errorOutput = stdErr.toString();
0876: return stdOut.toString();
0877: }
0878:
0879: /*
0880: private String run(File baseDir, String command) {
0881: return run(baseDir, command, false);
0882: }
0883:
0884: private String run(File baseDir, String command, boolean reallyQuiet) {
0885: // make it quiet if not done already
0886: if (command.indexOf("-q") < 0 && command.indexOf("-Q") < 0) {
0887: command = "-q " + command;
0888: }
0889: // add CVS root
0890: command = "-d " + renderUrlToCvsRoot(getUrl()) + " " + command;
0891:
0892: // create launcher
0893: ProcessLauncher launcher = new ProcessLauncher(AntmodProperties.getProperty("antmod.scm.cvs.executable") + " " + command, baseDir);
0894:
0895: final StringBuffer stdOut = new StringBuffer();
0896: final StringBuffer stdErr = new StringBuffer();
0897: stdOut.setLength(0);
0898: stdErr.setLength(0);
0899: launcher.addOutputListener(new ProcessLauncher.OutputListener() {
0900: public void standardOutput(char[] output) {
0901: stdOut.append(output);
0902: }
0903:
0904: public void errorOutput(char[] output) {
0905: stdErr.append(output);
0906: }
0907: });
0908:
0909: // launch and wait until done...
0910: launcher.launch();
0911:
0912: // if there is error output, log it for now
0913: if (!reallyQuiet && stdErr.length() > 0) {
0914: //System.err.println("CVS command 'cvs " + command + "' produced error output:");
0915: System.err.println(stdErr.toString());
0916: }
0917:
0918: // return standard output
0919: this.standardOutput = stdOut.toString();
0920: this.errorOutput = stdErr.toString();
0921: return stdOut.toString();
0922: }
0923: */
0924:
0925: static ScmVersion parseCvsRevision(String moduleName,
0926: String versionString) {
0927: // parse the version string
0928: int major = -1;
0929: int minor = -1;
0930: int patch = -1;
0931: if (versionString != null
0932: && !versionString.trim().equalsIgnoreCase("HEAD")
0933: && !versionString.trim().equalsIgnoreCase(
0934: ScmVersion.VERSIONSTRING_TRUNK)) {
0935: int startversionIndex = versionString
0936: .lastIndexOf(REVISION_NAME_SEPARATOR);
0937: if (startversionIndex < 0) {
0938: // no modulename in version string
0939: startversionIndex = -1;
0940: }
0941:
0942: int dashIndex = versionString.indexOf(
0943: REVISION_VERSION_SEPARATOR, startversionIndex + 1);
0944: major = Integer.parseInt(versionString.substring(
0945: startversionIndex + 1, dashIndex));
0946:
0947: int secondDashIndex = versionString.indexOf(
0948: REVISION_VERSION_SEPARATOR, dashIndex + 1);
0949: if (secondDashIndex > 0) {
0950: minor = Integer.parseInt(versionString.substring(
0951: dashIndex + 1, secondDashIndex));
0952: patch = Integer.parseInt(versionString
0953: .substring(secondDashIndex + 1));
0954: } else {
0955: minor = Integer.parseInt(versionString
0956: .substring(dashIndex + 1));
0957: }
0958: }
0959:
0960: return new ScmVersion(moduleName, major, minor, patch);
0961: }
0962:
0963: static String renderCvsRevision(ScmVersion ver) {
0964: if (ver == null) {
0965: return ScmVersion.VERSIONSTRING_TRUNK;
0966: }
0967: if (ver.getModuleName() == null
0968: || ver.getModuleName().trim().length() == 0) {
0969: throw new RuntimeException(
0970: "Modulename unknown - cvs revision string not possible.");
0971: }
0972: if (!ver.isTrunk()) {
0973: return ver.getModuleName() + REVISION_NAME_SEPARATOR
0974: + ver.toString(REVISION_VERSION_SEPARATOR);
0975: } else {
0976: return ver.toString();
0977: }
0978: }
0979:
0980: static String renderUrlToCvsRoot(ScmUrl url) {
0981: // TODO: password support not tested yet!
0982: StringBuffer cvsRoot = new StringBuffer();
0983: if (url.getProtocol() != null) {
0984: cvsRoot.append(":");
0985: cvsRoot.append(url.getProtocol());
0986: cvsRoot.append(":");
0987: }
0988: if (url.getUser() != null) {
0989: cvsRoot.append(url.getUser());
0990: if (url.getPassword() != null) {
0991: cvsRoot.append(":");
0992: cvsRoot.append(url.getPassword());
0993: }
0994: if (url.getHost() != null) {
0995: cvsRoot.append("@");
0996: cvsRoot.append(url.getHost());
0997: }
0998: cvsRoot.append(":");
0999: }
1000: cvsRoot.append(url.getPath());
1001:
1002: return cvsRoot.toString();
1003: }
1004:
1005: /**
1006: * @deprecated Use addCvsRevisionCommand instead
1007: */
1008: static String constructCvsRevisionCommand(ScmVersion version) {
1009: if (version == null || version.isTrunk()) {
1010: return "-A";
1011: } else {
1012: // prepend module name if needed
1013: return "-r " + version.getModuleName()
1014: + REVISION_NAME_SEPARATOR
1015: + version.toString(REVISION_VERSION_SEPARATOR);
1016: }
1017: }
1018:
1019: /**
1020: * Constructs the correct Cvs command line options for a module's revision.
1021: * This is useful for Cvs checkout and update commands.
1022: * @param packageName
1023: * @param revision
1024: * @return
1025: */
1026: static void addCvsRevisionCommand(ScmVersion version,
1027: ArrayList commandList) {
1028: if (version == null || version.isTrunk()) {
1029: commandList.add("-A");
1030: } else {
1031: // prepend module name if needed
1032: commandList.add("-r");
1033: commandList.add(version.getModuleName()
1034: + REVISION_NAME_SEPARATOR
1035: + version.toString(REVISION_VERSION_SEPARATOR));
1036: }
1037: }
1038:
1039: public boolean isCheckoutDir(File directory) {
1040: return new File(directory, "CVS").exists();
1041: }
1042: }
|