package hudson.plugins.violations.types.gendarme;
import hudson.plugins.violations.ViolationsParser;
import hudson.plugins.violations.model.FullBuildModel;
import hudson.plugins.violations.model.FullFileModel;
import hudson.plugins.violations.model.Severity;
import hudson.plugins.violations.model.Violation;
import hudson.plugins.violations.parse.ParseUtil;
import hudson.plugins.violations.types.fxcop.XmlElementUtil;
import hudson.plugins.violations.util.AbsoluteFileFinder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Parse Gendarme violations
* @author mathias.kluba@gmail.com
*
*/
public class GendarmeParser implements ViolationsParser {
static final Logger logger = Logger.getLogger(GendarmeParser.class.toString());
static final String TYPE_NAME = "gendarme";
private FullBuildModel model;
private File reportParentFile;
private File projectPath;
private String[] sourcePaths;
private HashMap<String, GendarmeRule> rules;
public void parse(FullBuildModel model, File projectPath, String fileName,
String[] sourcePaths) throws IOException {
logger.info("Starting Gendarme parsing");
this.projectPath = projectPath;
this.model = model;
this.reportParentFile = new File(fileName).getParentFile();
this.sourcePaths = sourcePaths;
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
try {
docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new FileInputStream(new File(projectPath, fileName)));
NodeList mainNode = doc.getElementsByTagName("gendarme-output");
Element rootElement = (Element) mainNode.item(0);
Element resultsElement = (Element)rootElement.getElementsByTagName("results").item(0);
Element rulesElement = (Element)rootElement.getElementsByTagName("rules").item(0);
// load all rules into the cache
parseRules(XmlElementUtil.getNamedChildElements(rulesElement, "rule"));
// parse each violations
parseViolations(XmlElementUtil.getNamedChildElements(resultsElement, "rule"));
} catch (ParserConfigurationException pce) {
throw new IOException(pce);
} catch (SAXException se) {
throw new IOException(se);
}
}
private void parseViolations(List<Element> ruleElements) {
// searching source files
AbsoluteFileFinder finder = new AbsoluteFileFinder();
// add the project path to the search source
finder.addSourcePath(this.projectPath.getPath());
// if there's additional paths, add them too
if (this.sourcePaths != null) {
finder.addSourcePaths(this.sourcePaths);
}
for(Element ruleElement : ruleElements){
String ruleName = ruleElement.getAttribute("Name");
GendarmeRule rule = this.rules.get(ruleName);
String problem = ruleElement.getElementsByTagName("problem").item(0).getTextContent();
String solution = ruleElement.getElementsByTagName("solution").item(0).getTextContent();
List<Element> targetElements = XmlElementUtil.getNamedChildElements(ruleElement, "target");
for(Element targetElement : targetElements){
//String targetName = targetElement.getAttribute("Name");
String targetAssembly = targetElement.getAttribute("Assembly");
DotNetAssembly assembly = new DotNetAssembly(targetAssembly);
Element defectElement = (Element) targetElement.getElementsByTagName("defect").item(0);
String severityString = defectElement.getAttribute("Severity");
String source = defectElement.getAttribute("Source");
//String location = defectElement.getAttribute("Location");
String confidence = defectElement.getAttribute("Confidence");
String filePath = "";
String fileName = "";
int line = 0;
if(rule.getType() == GendarmeRuleType.Method){
Pattern pattern = Pattern.compile("^(.*)\\(.([0-9]*)\\)$");
Matcher matcher = pattern.matcher(source);
logger.info("matcher.groupCount() : "+matcher.groupCount());
logger.info("matcher.matches() : "+matcher.matches());
logger.info("source : "+source);
if(matcher.matches()) {
for(int cpt = 0; cpt < matcher.groupCount(); cpt++){
logger.info("group("+((int)(cpt+1))+"): "+matcher.group(cpt+1));
}
}
if(matcher.matches()){
String fullPath = matcher.group(1);
File sourceFile = new File(fullPath);
fileName = sourceFile.getName();
filePath = sourceFile.getParent();
line = Integer.parseInt(matcher.group(2));
}
}
// create the violation
Violation violation = new Violation();
// construct the error message
StringBuilder messageBuilder = new StringBuilder();
if(rule.getUrl() != null){
messageBuilder.append("<a href=\"").append(rule.getUrl().toString()).append("\">");
messageBuilder.append(rule.getName());
messageBuilder.append("</a>");
}
else {
messageBuilder.append(rule.getName());
}
messageBuilder.append(" - ").append(problem).append("<br/>");
messageBuilder.append("Solution: ").append(solution).append("<br/>");
messageBuilder.append("Confidence: ").append(confidence);
violation.setMessage(messageBuilder.toString());
violation.setPopupMessage(problem);
// construct the severity
if(severityString.equals("Low")){
violation.setSeverityLevel(Severity.LOW_VALUE);
violation.setSeverity(Severity.LOW);
}
else if(severityString.equals("Medium")){
violation.setSeverityLevel(Severity.MEDIUM_VALUE);
violation.setSeverity(Severity.MEDIUM);
}
else if(severityString.equals("High")){
violation.setSeverityLevel(Severity.HIGH_VALUE);
violation.setSeverity(Severity.HIGH);
}
else {
violation.setSeverityLevel(Severity.MEDIUM_VALUE);
violation.setSeverity(Severity.MEDIUM);
}
violation.setType(TYPE_NAME);
violation.setSource(rule.getName());
// try to get the file
// TODO : test it with Linux Master => Windows Slave node. Unix/Windows path could be a problem.
FullFileModel fileModel;
if ((filePath.length() > 0) && (fileName.length() > 0) ) {
violation.setLine(line);
// get the display name of the file
String displayName = ParseUtil.resolveAbsoluteName(this.projectPath, filePath + File.separatorChar + fileName);
// try to get the source file, add it if not already exists
fileModel = model.getFileModel(displayName);
if (fileModel.getSourceFile() == null) {
finder.addSourcePath(filePath);
File sourceFile = finder.getFileForName(fileName);
if (sourceFile != null && sourceFile.exists()) {
fileModel.setSourceFile(sourceFile);
fileModel.setLastModified(sourceFile.lastModified());
logger.info("fileModel.getSourceFile() : "+fileModel.getSourceFile().getAbsolutePath());
}
else {
logger.info("sourceFile.exists()==false: "+filePath+","+ fileName);
}
}
else{
logger.info("fileModel.getSourceFile() != null: "+displayName);
}
} else {
// if there's no source files, just put the assembly name
fileModel = model.getFileModel(assembly.getName()+".dll");
logger.info("fileModel.getSourceFile() : "+(fileModel.getSourceFile() == null? "null" : fileModel.getSourceFile().getAbsolutePath()));
}
logger.info("fileModel.getDisplayName() : "+fileModel.getDisplayName());
logger.info("reportParentFile : "+reportParentFile);
logger.info("fileName : "+fileName);
logger.info("filePath : "+filePath);
// Add the violation to the model
fileModel.addViolation(violation);
}
}
}
private void parseRules(List<Element> ruleElements) {
rules = new HashMap<String, GendarmeRule>();
for(Element ruleElement : ruleElements){
// create the Gendarme rule
GendarmeRule rule = new GendarmeRule();
rule.setName(ruleElement.getAttribute("Name"));
rule.setTypeName(ruleElement.getTextContent());
String typeString = ruleElement.getAttribute("Type");
if(typeString.equals("Type"))
rule.setType(GendarmeRuleType.Type);
else if(typeString.equals("Method"))
rule.setType(GendarmeRuleType.Method);
else if(typeString.equals("Assembly"))
rule.setType(GendarmeRuleType.Assembly);
try {
rule.setUrl(new URL(ruleElement.getAttribute("Uri")));
} catch (MalformedURLException e) {
rule.setUrl(null);
}
// add the rule to the cache
rules.put(rule.getName(), rule);
}
}
}