package com.symcor.jlint.plugin;
import org.apache.maven.plugin.logging.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.net.URL;
import org.apache.commons.io.FilenameUtils;
import com.symcor.jlint.plugin.Constants;
public class JlintViolationHandler
{
private ArrayList<JlintMessageItem> configuration;
private String currentClass;
private Boolean firstBug;
private Log logger;
private String targetDir;
private String classesDir;
public JlintViolationHandler (Log logger) {
firstBug = Boolean.TRUE;
this.logger = logger;
loadJlintConfigFile();
}
public void setTargetDir(String dir) {
this.targetDir = dir;
}
public void setClassesDir(String dir) {
this.classesDir = dir;
}
/**
On windows, the output produced by Jlint does not include the full
pathname. E.g. this is the output of Jlint on windows when the following command
is run
c:\> jlint <project dir>\target\classes
org/jgroups/demos/TotalTokenDemo.java:175: Comparison always produces the same result.
org/jgroups/demos/TotalTokenDemo.java:192: Comparison always produces the same result.
Jlint does not output the full path of the java source file. It only outputs the
package structure - which is what is expected.
On Linux/Ubuntu 9.04, Jlint outputs the full path name. E.g.
target/classes/com/symcor/bs/common/util/jmx/JMXUtils.java:92: Value
of referenced variable 'servers' may be NULL.
This leading directory path information has to be stripped out,
if it exists.
*/
public String getErrorClass(String message)
{
/*
In some cases jlint appends the path upto the "target" directory
and then the package structure - leaving out the "classes" directory
In other cases jlint appends the full "target" dirpath + /classes
to the package structure
Hence remove this path prefix in 2 steps if it exists
*/
// Step 1 - Check for the path upto the "target" directory
// Step 1a - change the value in TargetDir to Unix Path naming. Since this issue does not appear on
// windows, it will not affect jlint output generated on Windows.
logger.debug("jlintViolationHandler: getErrorClass(): message = [" + message + "]");
String unixTargetDir = FilenameUtils.separatorsToUnix(targetDir);
logger.debug("jlintViolationHandler: getErrorClass(): unixTargetDir = [" + unixTargetDir + "]");
if (unixTargetDir != null ) {
logger.debug("JlintViolationHandler: TargetDir: " + unixTargetDir);
message = message.replaceFirst(unixTargetDir, "");
logger.debug("JlintViolationHandler: Message: " + message);
}
// Step 2 - Check if the remaining string starts with "/classes"
if (classesDir != null && message.startsWith(classesDir)) {
logger.debug("JlintViolationHandler: ClassesDir: " + classesDir);
message = message.replaceFirst(classesDir, "");
}
// if there is a leading "/" - remove it.
message = message.replaceFirst("^/", "");
// By now we shoud be left with just the package structure
return message.replaceAll(".java","").replace("/",".");
}
public ArrayList<JlintMessageItem> getDefaultMessageList() {
return configuration;
}
private void loadJlintConfigFile() {
String configLine;
String[] configFields;
JlintMessageItem configItem;
// DEBUG STUFF
URL c = this.getClass().getResource("/config/Jlint_msg.cfg");
logger.debug("CFG FILE: " + c);
// end DEBUG
configuration = new ArrayList<JlintMessageItem>();
try {
BufferedReader configFileReader = new BufferedReader(new
InputStreamReader(this.getClass().getResourceAsStream(Constants.JLINT_CFG_FILENAME)));
while ((configLine = configFileReader.readLine()) != null) {
//DEBUG
//logger.debug("CONFIGLINE: " + configLine);
configFields = configLine.split(Constants.FIELD_SEPARATOR, 5);
//DEBUG
//logger.debug("CONFIGFILEDS: " + configFields[0] +
// " / " + configFields[1] +
// " / " + configFields[2]);
configItem = new JlintMessageItem(configFields);
//DEBUG
//logger.debug("CREATED: " + configItem);
configuration.add(configItem);
}
}
catch (IOException ioe)
{
logger.error("ERROR: " + ioe.toString());
ioe.printStackTrace();
}
}
public String violationToXML(String violation) {
String error_class;
String error_message;
String lineNo;
StringBuilder xmlViolation;
logger.debug("jlintViolationHandler: violatioToXML(): violation [" + violation + "]");
String[] message = violation.split(":",3);
logger.debug("jlintViolationHandler: violatioToXML(): message[0] = [" + message[0] + "]");
logger.debug("jlintViolationHandler: violatioToXML(): message[1] = [" + message[1] + "]");
logger.debug("jlintViolationHandler: violatioToXML(): message[2] = [" + message[2] + "]");
error_class = getErrorClass(message[0]);
lineNo = new String(message[1]);
error_message=new String(message[2]);
JlintMessageItem configItem = getConfigItemForMessage(error_message);
if (configItem == null) {
logger.error("FATAL ERROR: Cannot find matching message from config file.");
logger.error("Message: " + error_message);
System.exit(-1);
}
final String newLine = System.getProperty("line.separator");
xmlViolation = new StringBuilder();
if (!error_class.equals(currentClass)) {
if (firstBug == Boolean.FALSE) {
xmlViolation.append(Constants.FILE_END_TAG);
xmlViolation.append(newLine);
}
else {
xmlViolation.append(newLine);
firstBug = Boolean.FALSE;
}
xmlViolation.append(newLine);
xmlViolation.append(Constants.FILE_START_TAG);
xmlViolation.append("\"");
xmlViolation.append(error_class);
xmlViolation.append("\" >");
xmlViolation.append(newLine);
currentClass = error_class;
}
xmlViolation.append(Constants.BUG_START_TAG);
xmlViolation.append(Constants.BUGATTR_TYPE);
xmlViolation.append("\"");
xmlViolation.append(configItem.getType());
xmlViolation.append("\"");
xmlViolation.append(Constants.BUGATTR_PRIORITY);
xmlViolation.append("\"");
xmlViolation.append(configItem.getPriority());
xmlViolation.append("\"");
xmlViolation.append(Constants.BUGATTR_CATEGORY);
xmlViolation.append("\"");
xmlViolation.append(configItem.getCategory());
xmlViolation.append("\"");
xmlViolation.append(Constants.BUGATTR_MESSAGE);
xmlViolation.append("\"");
xmlViolation.append(convertToValidXML(error_message));
xmlViolation.append("\"");
xmlViolation.append(Constants.BUGATTR_LINENO);
xmlViolation.append("\"");
xmlViolation.append(lineNo);
xmlViolation.append("\"");
xmlViolation.append(Constants.BUG_END_TAG);
xmlViolation.append(newLine);
return xmlViolation.toString();
}
private JlintMessageItem getConfigItemForMessage(String message) {
String tmp;
logger.debug("getConfigItemForMessage: Parameter (message): [" + message + "]");
for (JlintMessageItem ci : configuration ) {
// DEBUG
logger.debug("");
logger.debug("PATTERN: ["+ci.getUniqueMessagePattern() + "]");
//The replace function is called to remove leading space in the string
tmp = formatMessage(message);
// DEBUG
logger.debug("MESSAGE: [" + tmp + "]");
if ( tmp.matches(ci.getUniqueMessagePattern())) {
logger.debug("******** MATCH FOUND ***********");
logger.debug(ci.toString());
return new JlintMessageItem(ci);
}
}
// This will raise an error in the calling function
return null;
}
private String formatMessage(String message) {
return message.replaceFirst(" ","");
}
private String convertToValidXML(String message) {
// Replace the 5 illegal XML characters with valid
// XML representation and remove the leading space
return message.replace("&","&").
replace("\"",""").
replace("<","<").
replace(">",">").
replace("'","'").
replaceFirst(" ","");
}
}