/*
* ====================================================================
* 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() {
}
}