Last active
December 15, 2015 02:39
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
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