/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.cli.svn; import java.io.File; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.tmatesoft.svn.cli.SVNCommandUtil; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.util.SVNFormatUtil; import org.tmatesoft.svn.core.internal.util.SVNXMLUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNPath; import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNBlameCommand extends SVNXMLCommand implements ISVNAnnotateHandler { private StringBuffer myBuffer; public SVNBlameCommand() { super("blame", new String[] {"praise", "annotate", "ann"}); } public boolean acceptsRevisionRange() { return true; } protected Collection createSupportedOptions() { Collection options = new LinkedList(); options.add(SVNOption.REVISION); options.add(SVNOption.VERBOSE); options.add(SVNOption.USE_MERGE_HISTORY); options.add(SVNOption.INCREMENTAL); options.add(SVNOption.XML); options.add(SVNOption.EXTENSIONS); options.add(SVNOption.FORCE); return options; } public void run() throws SVNException { List targets = getSVNEnvironment().combineTargets(null, true); if (targets.isEmpty()) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_INSUFFICIENT_ARGS), SVNLogType.CLIENT); } SVNRevision start = getSVNEnvironment().getStartRevision(); SVNRevision end = getSVNEnvironment().getEndRevision(); if (end == SVNRevision.UNDEFINED) { if (start != SVNRevision.UNDEFINED) { end = start; start = SVNRevision.create(1); } } if (start == SVNRevision.UNDEFINED) { start = SVNRevision.create(1); } if (getSVNEnvironment().isXML()) { if (getSVNEnvironment().isVerbose()) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "'verbose' option invalid in XML mode"), SVNLogType.CLIENT); } if (!getSVNEnvironment().isIncremental()) { printXMLHeader("blame"); } } else if (getSVNEnvironment().isIncremental()) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "'incremental' option only valid in XML mode"), SVNLogType.CLIENT); } myBuffer = new StringBuffer(); SVNLogClient client = getSVNEnvironment().getClientManager().getLogClient(); client.setDiffOptions(getSVNEnvironment().getDiffOptions()); boolean hasMissingTargets = false; for (Iterator ts = targets.iterator(); ts.hasNext();) { String targetName = (String) ts.next(); SVNPath target = new SVNPath(targetName, true); SVNRevision endRev = end; if (endRev == SVNRevision.UNDEFINED) { if (target.getPegRevision() != SVNRevision.UNDEFINED) { endRev = target.getPegRevision(); } } if (getSVNEnvironment().isXML()) { myBuffer = openXMLTag("target", SVNXMLUtil.XML_STYLE_NORMAL, "path", SVNCommandUtil.getLocalPath(target.getTarget()), myBuffer); } try { if (target.isFile()) { client.doAnnotate(target.getFile(), target.getPegRevision(), start, endRev, getSVNEnvironment().isForce(), getSVNEnvironment().isUseMergeHistory(), this, null); } else { client.doAnnotate(target.getURL(), target.getPegRevision(), start, endRev, getSVNEnvironment().isForce(), getSVNEnvironment().isUseMergeHistory(), this, null); } if (getSVNEnvironment().isXML()) { myBuffer = closeXMLTag("target", myBuffer); getSVNEnvironment().getOut().print(myBuffer); } } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.CLIENT_IS_BINARY_FILE) { getSVNEnvironment().getErr().println("Skipping binary file: '" + SVNCommandUtil.getLocalPath(targetName) + "'"); } else { getSVNEnvironment().handleWarning(e.getErrorMessage(), new SVNErrorCode[] {SVNErrorCode.WC_PATH_NOT_FOUND, SVNErrorCode.FS_NOT_FILE, SVNErrorCode.FS_NOT_FOUND}, getSVNEnvironment().isQuiet()); hasMissingTargets = true; } } myBuffer = myBuffer.delete(0, myBuffer.length()); } if (getSVNEnvironment().isXML() && !getSVNEnvironment().isIncremental()) { printXMLFooter("blame"); } if (hasMissingTargets) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Could not perform blame on all targets because some " + "targets don't exist"); SVNErrorManager.error(err, SVNLogType.CLIENT); } } private int myCurrentLineNumber = 0; public void handleLine(Date date, long revision, String author, String line) throws SVNException { myCurrentLineNumber++; handleLine(date, revision, author, line, null, -1, null, null, myCurrentLineNumber); } public void handleLine(Date date, long revision, String author, String line, Date mergedDate, long mergedRevision, String mergedAuthor, String mergedPath, int lineNumber) throws SVNException { if (getSVNEnvironment().isXML()) { myBuffer = openXMLTag("entry", SVNXMLUtil.XML_STYLE_NORMAL, "line-number", Long.toString(lineNumber + 1), myBuffer); if (SVNRevision.isValidRevisionNumber(revision)) { myBuffer = openXMLTag("commit", SVNXMLUtil.XML_STYLE_NORMAL, "revision", Long.toString(revision), myBuffer); myBuffer = openCDataTag("author", author, myBuffer); myBuffer = openCDataTag("date", ((SVNDate) date).format(), myBuffer); myBuffer = closeXMLTag("commit", myBuffer); } if (getSVNEnvironment().isUseMergeHistory() && SVNRevision.isValidRevisionNumber(mergedRevision)) { myBuffer = openXMLTag("merged", SVNXMLUtil.XML_STYLE_NORMAL, "path", mergedPath, myBuffer); myBuffer = openXMLTag("commit", SVNXMLUtil.XML_STYLE_NORMAL, "revision", Long.toString(mergedRevision), myBuffer); myBuffer = openCDataTag("author", mergedAuthor, myBuffer); myBuffer = openCDataTag("date", ((SVNDate) mergedDate).format(), myBuffer); myBuffer = closeXMLTag("commit", myBuffer); myBuffer = closeXMLTag("merged", myBuffer); } myBuffer = closeXMLTag("entry", myBuffer); } else { String mergedStr = ""; if (getSVNEnvironment().isUseMergeHistory()) { if (revision > mergedRevision) { mergedStr = "G "; date = mergedDate; revision = mergedRevision; author = mergedAuthor; } else { mergedStr = " "; } } String revStr = revision >= 0 ? SVNFormatUtil.formatString(Long.toString(revision), 6, false) : " -"; String authorStr = author != null ? SVNFormatUtil.formatString(author, 10, false) : " -"; if (getSVNEnvironment().isVerbose()) { String dateStr = " -"; if (date != null) { dateStr = SVNDate.formatHumanDate(date, getSVNEnvironment().getClientManager().getOptions()); } getSVNEnvironment().getOut().print(mergedStr + revStr + " " + authorStr + " " + dateStr + " "); if (getSVNEnvironment().isUseMergeHistory() && mergedPath != null) { String pathStr = SVNFormatUtil.formatString(mergedPath, 14, true); getSVNEnvironment().getOut().print(pathStr + " "); } getSVNEnvironment().getOut().println(line); } else { getSVNEnvironment().getOut().println(mergedStr + revStr + " " + authorStr + " " + line); } } } public boolean handleRevision(Date date, long revision, String author, File contents) throws SVNException { return false; } public void handleEOF() { } }