Skip to content

Instantly share code, notes, and snippets.

@josefbetancourt
Last active December 15, 2015 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josefbetancourt/5188857 to your computer and use it in GitHub Desktop.
Save josefbetancourt/5188857 to your computer and use it in GitHub Desktop.
Transform SVN log diff using Java XPath example for blog post http://octodecillion.com/blog/svn-report-java-xpath/
/**
*
*/
package com.octodecillion.utils;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* Transform SVN diff and log XML output files.
*
*/
@SuppressWarnings("javadoc")
public class SvnOutputTransform {
/** Example run */
public static void main(final String[] args) {
try {
SvnOutputTransform svnTransform = new SvnOutputTransform();
svnTransform.diffSummaryToCsv("data/diff-summary.xml",
"bin/SvnDiff.csv");
svnTransform.logSummaryToCsv("data/log-summary.xml",
"bin/SvnLog.csv");
} catch (Exception e) {
e.printStackTrace();
}
}
/** Call back interface */
interface RowProcess {
/** accept method of Visitor pattern */
public void doRows(NodeList nodeList) throws Exception;
}
/**
*
* @param dataFilePath
* @param reportFilePath
* @param nodeString
* @param processRows
*/
public void generateReport(final String dataFilePath,
final String reportFilePath, final String nodeString,
final RowProcess processRows) {
try {
NodeList nodeList = setup(dataFilePath, reportFilePath, nodeString);
reportOut.println(HEADER_COLUMN);
processRows.doRows(nodeList);
} catch (Exception e) {
throw new SvnTransformException("", e);
} finally {
finallyHandler(reportOut, fr);
}
}
/**
*
* @param dataFilePath
* @param reportFilePath
* @param xpathString
* @return
* @throws Exception
*/
private NodeList setup(final String dataFilePath,
final String reportFilePath, final String xpathString) throws Exception {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
xp = XPathFactory.newInstance().newXPath();
fr = new FileReader(dataFilePath);
dom = builder.parse(new InputSource(fr));
reportOut = new PrintWriter(new File(reportFilePath));
Object nodes = xp.evaluate(xpathString, dom, XPathConstants.NODESET);
NodeList nodeList = (NodeList) nodes;
return nodeList;
}
/**
* Convert SVN generated diff summary xml file to CSV.
*
* The format of the input XML is:
* <diff>
* <paths>
* <path props="none" kind="file" item="modified">
* full path of resource
* </path>
* </paths>
* </diff>
*
* @param dataFilePath xml file generated by svn diff --xml --summary ....
* @param reportFilePath destination of CSV file
* @throws SvnTransformException
*/
public void diffSummaryToCsv(final String dataFilePath,
final String reportFilePath) {
preconditionCheck(dataFilePath, reportFilePath);
generateReport(dataFilePath, reportFilePath, "//diff/paths/path",
new RowProcess() {
@SuppressWarnings("synthetic-access")
@Override
public void doRows(final NodeList nodeList) throws Exception {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
String kind = xp.evaluate(KIND_ATTR_NAME, node);
String item = xp.evaluate(ITEM_ATTR_NAME, node);
String pathEntry = xp.evaluate("text()", node);
// row
reportOut.println(String.format(
"%s,%s,%s,%s,%s,%s,%s", DIFFUSER, revision,
date, item, kind, pathEntry, message));
}
}
});
}
/**
* Convert SVN generated log summary xml file to CSV.
*
* <log>
* <logentry revision="10879">
* <author>T16205</author>
* <date>2013-03-15T18:10:07.264531Z</date>
* <paths>
* <path kind="file" action="A">
* /2013/Amica/branches/SOW114-LifeAPP/Test/Resources/Properties/Test/HomeQuotingFields.inix
* </path>
* </paths>
* </logentry>
* </log>
*
* @throws SvnTransformException
*/
public void logSummaryToCsv(final String dataFilePath,
final String reportFilePath) {
preconditionCheck(dataFilePath, reportFilePath);
generateReport(dataFilePath, reportFilePath, "//log/logentry",
new RowProcess() {
@SuppressWarnings("synthetic-access")
@Override
public void doRows(final NodeList nodeList) throws Exception {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
String author = xp.evaluate(AUTHOR_NODE, node);
String date = xp.evaluate(DATE_NODE, node);
String revision = xp.evaluate("@revision", node);
String message = "\"" + xp.evaluate(MSG_NODE, node,XPathConstants.STRING) + "\"";
NodeList paths = (NodeList) xp.evaluate(
"paths/path", node, XPathConstants.NODESET);
if (paths != null) {
for (int k = 0; k < paths.getLength(); k++) {
Node aPath = paths.item(k);
String action = xp.evaluate("@action",
aPath);
action = actionToName(action);
String filePath = xp.evaluate("text()",
aPath);
// row
reportOut.println(String.format(
"%s,%s,%s,%s,%s,%s", author,
revision, date.split("T")[0],
action, filePath, message));
}
}
} // end each logentry
}
});
} // end logToCsv
/** */
private String actionToName(final String n) {
try {
return ACTION.valueOf(n).getName();
} catch (Exception e) {
return n;
}
}
private void finallyHandler(final Writer reportOut, final Reader fr) {
if (reportOut != null) {
try {
reportOut.flush();
reportOut.close();
} catch (Exception e) {
//
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
//
}
}
}
/**
* @param dataFilePath
* @param reportFilePath
* @throws IllegalArgumentException
*/
private void preconditionCheck(final String dataFilePath,
final String reportFilePath) throws IllegalArgumentException {
if ((dataFilePath == null) || (reportFilePath == null)) {
throw new IllegalArgumentException(String.format(
"dataFilePath='%s',reportFilePath='%s'", dataFilePath,
reportFilePath));
}
}
/**
* SVN action codes.
*
*/
enum ACTION {
A("add"), M("modify"), D("delete"), Z("z");
private final String name;
private ACTION(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
/** Svn transform runtime exception */
static public class SvnTransformException extends RuntimeException {
private static final long serialVersionUID = 1L;
/** */
public SvnTransformException(final String message, final Throwable cause) {
super(message, cause);
}
}
private static final String HEADER_COLUMN = "Dev,Revision,Date,Action,Kind,Path";
private static final String ITEM_ATTR_NAME = "@item";
private static final String KIND_ATTR_NAME = "@kind";
private static final String MSG_NODE = "msg";
private static final String DATE_NODE = "date";
private static final String AUTHOR_NODE = "author";
private PrintWriter reportOut;
private FileReader fr;
private static final String DIFFUSER = "DIFF";
private final String revision = "";
private final String date = "";
private final String message = "";
private DocumentBuilder builder;
private XPath xp;
private Document dom;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment