package hudson.plugins.coverage.impl;
import hudson.plugins.coverage.model.Instance;
import hudson.plugins.coverage.model.JavaModel;
import hudson.plugins.coverage.model.Recorder;
import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.stax2.XMLOutputFactory2;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.*;
import java.util.*;
/**
* Created by IntelliJ IDEA.
*
* @author connollys
* @since Nov 18, 2008 2:58:27 PM
*/
public class CoberturaRecorder implements Recorder {
// ------------------------------ FIELDS ------------------------------
/**
* This field is populated when the recorder is being constructed for a newly built project, for old builds this
* will be null.
*/
private final transient Set<File> coberturaXmlResults;
/**
* This field is populated when the recorder is being constructed for a newly built project, for old builds this
* will be null.
*/
private final transient File sourceCodeRoot;
// -------------------------- STATIC METHODS --------------------------
private static void safelyClose(XMLEventReader xmlEventReader) {
if (xmlEventReader != null) {
try {
xmlEventReader.close();
} catch (XMLStreamException e) {
// ignore
}
}
}
private static void safelyClose(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
// ignore
}
}
}
// --------------------------- CONSTRUCTORS ---------------------------
/**
* Default constructor, will be used to reconstruct results for old builds.
*/
public CoberturaRecorder() {
this.coberturaXmlResults = null;
this.sourceCodeRoot = null;
}
/**
* Creates a new CoberturaRecorder for parsing a new build.
*
* @param coberturaXmlResults The cobertura XML result files.
* @param sourceCodeRoot The root directory within which the source files should be hiding.
*/
public CoberturaRecorder(Set<File> coberturaXmlResults, File sourceCodeRoot) {
this.coberturaXmlResults = coberturaXmlResults;
this.sourceCodeRoot = sourceCodeRoot;
}
// ------------------------ INTERFACE METHODS ------------------------
// --------------------- Interface Recorder ---------------------
/**
* {@inheritDoc}
*/
public void identifySourceFiles(Instance root) {
// for the time being I think this is what we want
reidentifySourceFiles(root, coberturaXmlResults, sourceCodeRoot);
}
/**
* {@inheritDoc}
*/
public void reidentifySourceFiles(Instance root, Set<File> measurementFiles, File sourceCodeDirectory) {
XMLInputFactory inputFactory = newXMLInputFactory();
for (File measurementFile : measurementFiles) {
if (measurementFile.isFile()) {
FileInputStream fis = null;
BufferedInputStream bis = null;
XMLEventReader r = null;
try {
fis = new FileInputStream(measurementFile);
bis = new BufferedInputStream(fis);
r = inputFactory.createXMLEventReader(bis);
boolean inSources = false;
boolean inPackages = false;
boolean inCoverage = false;
Instance javaInstance = null;
Instance packageInstance = null;
Set<File> sourceRoots = new HashSet<File>();
if (sourceCodeDirectory != null) {
sourceRoots.add(sourceCodeDirectory);
}
while (r.hasNext()) {
final XMLEvent event = r.nextEvent();
if (event.isStartElement()) {
StartElement start = event.asStartElement();
final String localPart = start.getName().getLocalPart();
if (inCoverage) {
if (inSources) {
if ("source".equals(localPart)) {
String sourceDir = r.getElementText();
File source = new File(sourceDir);
if (!source.isAbsolute()) {
if (sourceCodeDirectory != null) {
source = new File(sourceCodeDirectory, sourceDir);
} else {
source = null;
}
}
if (source != null) {
sourceRoots.add(source);
}
System.out.println("source " + source);
}
} else if (inPackages) {
if ("package".equals(localPart)) {
String packageName = getAttributeValue(start, "name");
if (packageName != null) {
packageInstance =
javaInstance.findOrCreateChild(JavaModel.PACKAGE, packageName);
} else {
packageInstance = null;
}
System.out.println("package " + packageName);
} else if (packageInstance != null && "class".equals(localPart)) {
String className = getAttributeValue(start, "name");
String fileName = getAttributeValue(start, "filename");
if (fileName != null && className != null) {
StringWriter sw = new StringWriter();
XMLOutputFactory2 of = (XMLOutputFactory2) XMLOutputFactory2.newInstance();
of.configureForSpeed();
XMLEventWriter writer = of.createXMLEventWriter(sw);
writer.add(start);
int depth = 1;
while (depth >= 1 && r.hasNext()) {
final XMLEvent event1 = r.nextEvent();
if (event1.isStartElement()) {
depth++;
} else if (event1.isEndElement()) {
depth--;
}
writer.add(event1);
}
writer.close();
packageInstance.findOrCreateChild(JavaModel.FILE, fileName,
findFileFromParents(sourceRoots, fileName))
.addRecorder(this, measurementFile, sw.toString());
}
System.out.println("class " + className + " (" + fileName + ")");
}
} else if ("packages".equals(localPart)) {
inPackages = true;
} else if ("sources".equals(localPart)) {
inSources = true;
}
} else {
if ("coverage".equals(localPart)) {
inCoverage = true;
inSources = false;
inPackages = false;
javaInstance = root.findOrCreateChild(JavaModel.LANGUAGE, "");
}
}
} else if (event.isEndElement()) {
EndElement end = event.asEndElement();
final String localPart = end.getName().getLocalPart();
if (inCoverage) {
if (inSources) {
if ("sources".equals(localPart)) {
inSources = false;
}
} else if (inPackages) {
if ("package".equals(localPart)) {
packageInstance = null;
} else if ("packages".equals(localPart)) {
inPackages = false;
}
} else {
if ("coverage".equals(localPart)) {
break;
}
}
}
}
}
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
safelyClose(r);
safelyClose(bis);
safelyClose(fis);
}
}
}
}
private XMLInputFactory newXMLInputFactory() {
XMLInputFactory inputFactory = XMLInputFactory2.newInstance();
inputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
inputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION, Boolean.TRUE);
inputFactory.setProperty(XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, Boolean.TRUE);
return inputFactory;
}
/**
* {@inheritDoc}
*/
public void parseSourceResults(Instance sourceFile, File measurementFile, Collection<Object> memos) {
if (sourceFile.getElement().isFileLevel() && measurementFile.isFile() && memos != null && !memos.isEmpty()) {
Collection<String> results = Collection.class.cast(memos);
// arse arse arse... how to efficiently re-read this file
XMLEventReader r = null;
try {
r = newXMLInputFactory().createXMLEventReader(new StringReader(result));
while (r.hasNext()) {
final XMLEvent event = r.nextEvent();
if (event.isStartElement()) {
StartElement start = event.asStartElement();
System.out.println(start.getName());
return;
} else if (event.isEndElement()) {
}
}
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
safelyClose(r);
}
}
}
private SortedSet<Location> convertMemosToSortedLocations(Collection<Object> memos) {
SortedSet<Location> locations = new TreeSet<Location>(new Comparator<Location>() {
public int compare(Location o1, Location o2) {
int p1 = o1.getCharacterOffset();
int p2 = o2.getCharacterOffset();
return p1 > p2 ? 1 : p1 == p2 ? 0 : -1;
}
});
for (Object memo : memos) {
if (memo instanceof Location) {
locations.add((Location) memo);
}
}
return locations;
}
// -------------------------- OTHER METHODS --------------------------
private File findFileFromParents(Set<File> sourceRoots, String fileName) {
File file = null;
for (File sourceDir : sourceRoots) {
file = new File(sourceDir, fileName);
if (file.isFile()) {
return file;
}
}
return file;
}
private String getAttributeValue(StartElement start, String localPart) {
final QName qName = new QName(start.getName().getNamespaceURI(), localPart);
final Attribute attribute = start.getAttributeByName(qName);
return attribute == null ? null : attribute.getValue();
}
static final class FileResult {
private final ClassResult[] classes;
public FileResult(ClassResult[] classes) {
this.classes = classes;
}
}
static final class ClassResult {
private final String name;
private final MethodResult[] methods;
public ClassResult(String name, MethodResult[] methods) {
this.name = name;
this.methods = methods;
}
}
static final class MethodResult {
private final String name;
private final LineResult[] lines;
public MethodResult(String name, LineResult[] lines) {
this.name = name;
this.lines = lines;
}
}
static final class LineResult {
private final ConditionResult[] conditions;
public LineResult(ConditionResult[] conditions) {
this.conditions = conditions;
}
}
static final class ConditionResult {
}
}