package org.codehaus.mojo.jlint; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import org.apache.commons.io.FilenameUtils; import org.apache.maven.plugin.logging.Log; 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( " ", "" ); } }