package hudson.plugins.violations.types.pylint;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.util.AbsoluteFileFinder;
/**
* Parser for parsing PyLint reports.
*
* The parser only supports PyLint report that has the output format
* 'parseable'.
*
* @author Erik Ramfelt
*/
public class PyLintParser implements ViolationsParser {
/** Regex pattern for the PyLint errors. */
private final transient Pattern pattern;
private transient AbsoluteFileFinder absoluteFileFinder = new AbsoluteFileFinder();
/**
* Constructor - create the pattern.
*/
public PyLintParser() {
pattern = Pattern.compile("(.*):(\\d+): \\[(\\D\\d*).*\\] (.*)");
}
/** {@inheritDoc} */
public void parse( FullBuildModel model, File projectPath, String fileName,
String[] sourcePaths) throws IOException {
BufferedReader reader = null;
absoluteFileFinder.addSourcePath(projectPath.getAbsolutePath());
absoluteFileFinder.addSourcePaths(sourcePaths);
try {
reader = new BufferedReader(
new FileReader(new File(projectPath, fileName)));
String line = reader.readLine();
while (line != null) {
parseLine(model, line, projectPath);
line = reader.readLine();
}
} finally {
if (reader != null) {
reader.close();
}
}
}
/**
* Parses a PyLint line and adding a violation if regex
* @param model build model to add violations to
* @param line the line in the file.
* @param projectPath the path to use to resolve the file.
*/
public void parseLine(FullBuildModel model, String line, File projectPath) {
PyLintViolation pyLintViolation = getPyLintViolation(line);
if (pyLintViolation != null) {
Violation violation = new Violation();
violation.setType("pylint");
violation.setLine(pyLintViolation.getLineStr());
violation.setMessage(pyLintViolation.getMessage());
violation.setSource(pyLintViolation.getViolationId());
setServerityLevel(violation, pyLintViolation.getViolationId());
FullFileModel fileModel = getFileModel(model,
pyLintViolation.getFileName(),
absoluteFileFinder.getFileForName(pyLintViolation.getFileName()));
fileModel.addViolation(violation);
}
}
private FullFileModel getFileModel(FullBuildModel model, String name, File sourceFile) {
FullFileModel fileModel = model.getFileModel(name);
File other = fileModel.getSourceFile();
if (sourceFile == null
|| ((other != null) && (
other.equals(sourceFile)
|| other.exists()))) {
return fileModel;
}
fileModel.setSourceFile(sourceFile);
fileModel.setLastModified(sourceFile.lastModified());
return fileModel;
}
/**
* Returns a pylint violation (if it is one)
* @param line a line from the PyLint parseable report
* @return a PyLintViolation if the line contains one; null otherwise
*/
PyLintViolation getPyLintViolation(String line) {
Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 4) {
return new PyLintViolation(matcher);
}
return null;
}
/**
* Returns the Severity level as an int from the PyLint message type.
*
* The different message types are:
* (C) convention, for programming standard violation
* (R) refactor, for bad code smell
* (W) warning, for python specific problems
* (E) error, for much probably bugs in the code
* (F) fatal, if an error occured which prevented pylint from doing
* further processing.
*
* @param messageType the type of PyLint message
* @return an int is matched to the message type.
*/
private void setServerityLevel(Violation violation, String messageType) {
switch (messageType.charAt(0)) {
case 'C':
violation.setSeverity(Severity.LOW);
violation.setSeverityLevel(Severity.LOW_VALUE);
break;
case 'R':
violation.setSeverity(Severity.MEDIUM_LOW);
violation.setSeverityLevel(Severity.MEDIUM_LOW_VALUE);
break;
case 'W':
default:
violation.setSeverity(Severity.MEDIUM);
violation.setSeverityLevel(Severity.MEDIUM_VALUE);
break;
case 'E':
case 'F':
violation.setSeverity(Severity.HIGH);
violation.setSeverityLevel(Severity.HIGH_VALUE);
break;
}
}
class PyLintViolation {
private final transient String lineStr;
private final transient String message;
private final transient String fileName;
private final transient String violationId;
public PyLintViolation(Matcher matcher) {
if (matcher.groupCount() < 4) {
throw new IllegalArgumentException(
"The Regex matcher could not find enough information");
}
lineStr = matcher.group(2);
message = matcher.group(4);
violationId = matcher.group(3);
fileName = matcher.group(1);
}
public String getLineStr() {
return lineStr;
}
public String getMessage() {
return message;
}
public String getFileName() {
return fileName;
}
public String getViolationId() {
return violationId;
}
}
}