Loading...

Monday, January 10, 2011

Groovy Goodness: Create CodeNarc Reports with XSLT

CodeNarc is a tool to analyze Groovy code for style issues, bad practices and defects. The default output after running CodeNarc is a HTML report. In this report we can see which packages and files have violated the rules. We can change the default HTML report output and set it to XML output. With the XML output we can create flexible reports using XSLT. Now we can replace the more basic HTML default output with our own defined HTML reports. For example we can include our own logo or only display rule information for the rules that are violated. In this post we see how we can create a custom HTML CodeNarc report.

Suppose we have the following XML output from CodeNarc for a project:

<?xml version='1.0'?>
<CodeNarc url='http://www.codenarc.org' version='0.11'>
    <Report timestamp='Jan 10, 2011 2:05:22 PM'/>
    <Project title='Sample Groovy Project by mrhaki'>
        <SourceDirectory>src/main/groovy</SourceDirectory>
    </Project>
    <PackageSummary totalFiles='3' filesWithViolations='3' priority1='2' priority2='5' priority3='1'></PackageSummary>
    <Package path='com' totalFiles='3' filesWithViolations='3' priority1='2' priority2='5' priority3='1'></Package>
    <Package path='com/mrhaki' totalFiles='3' filesWithViolations='3' priority1='2' priority2='5'
             priority3='1'></Package>
    <Package path='com/mrhaki/app' totalFiles='3' filesWithViolations='3' priority1='2' priority2='5' priority3='1'>
        <File name='App.groovy'>
            <Violation ruleName='Println' priority='1' lineNumber='6'>
                <SourceLine><![CDATA[println 'Ready for running the App']]></SourceLine>
            </Violation>
            <Violation ruleName='Println' priority='1' lineNumber='10'>
                <SourceLine><![CDATA[println "Found 'mrhaki': ${PersonService.findByNickname('mrhaki')}"]]></SourceLine>
            </Violation>
            <Violation ruleName='VariableName' priority='2' lineNumber='8'>
                <SourceLine><![CDATA[def PersonService = new PersonService()]]></SourceLine>
            </Violation>
        </File>
        <File name='Person.groovy'>
            <Violation ruleName='PropertyName' priority='2' lineNumber='4'>
                <SourceLine><![CDATA[String NAME]]></SourceLine>
            </Violation>
        </File>
        <File name='PersonService.groovy'>
            <Violation ruleName='GStringAsMapKey' priority='2' lineNumber='8'>
                <SourceLine>
                    <![CDATA[new Person(NAME: 'Hubert Klein Ikkink', "${keyNickname}": 'mrhaki', country: 'The Netherlands')]]></SourceLine>
                <Message><![CDATA[GString as a key in a map is unsafe]]></Message>
            </Violation>
            <Violation ruleName='GStringAsMapKey' priority='2' lineNumber='8'>
                <SourceLine>
                    <![CDATA[new Person(NAME: 'Hubert Klein Ikkink', "${keyNickname}": 'mrhaki', country: 'The Netherlands')]]></SourceLine>
                <Message><![CDATA[GString as a key in a map is unsafe]]></Message>
            </Violation>
            <Violation ruleName='ParameterName' priority='2' lineNumber='12'>
                <SourceLine><![CDATA[return persons.find { P -> nickname == P.nickname }]]></SourceLine>
            </Violation>
            <Violation ruleName='UnnecessaryReturnKeyword' priority='3' lineNumber='12'>
                <SourceLine><![CDATA[return persons.find { P -> nickname == P.nickname }]]></SourceLine>
            </Violation>
        </File>
    </Package>
    <Rules>
        <Rule name='AbcComplexity'>
            <Description>
                <![CDATA[Checks the ABC metric of size/complexity for methods/classes.A method (or "closure field") with an ABC complexity value (score) greater than the maxMethodComplexity property (which defaults to 60) causes a violation. Likewise, a class that has an (average method) ABC complexityvalue greater than the maxClassAverageMethodComplexity property (which defaults to 60) causes a violation.]]></Description>
        </Rule>
        <Rule name='AbstractClassName'>
            <Description>
                <![CDATA[Verifies that the name of an abstract class matches a regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active. This rule ignores interfaces.]]></Description>
        </Rule>
        <Rule name='BigDecimalInstantiation'>
            <Description>
                <![CDATA[Checks for calls to the BigDecimal constructors that take a double parameter, which may result in an unexpected BigDecimal value.]]></Description>
        </Rule>
        <Rule name='BooleanInstantiation'>
            <Description>
                <![CDATA[Use Boolean.valueOf() for variable values or Boolean.TRUE and Boolean.FALSE for constant values instead of calling the Boolean() constructor directly or calling Boolean.valueOf(true) or Boolean.valueOf(false).]]></Description>
        </Rule>
        <Rule name='BooleanMethodReturnsNull'>
            <Description>
                <![CDATA[Method with Boolean return type returns explicit null. A method that returns either Boolean.TRUE, Boolean.FALSE or null is an accident waiting to happen. This method can be invoked as though it returned a value of type boolean, and the compiler will insert automatic unboxing of the Boolean value. If a null value is returned, this will result in a NullPointerException.]]></Description>
        </Rule>
        <Rule name='CatchError'>
            <Description>
                <![CDATA[Catching Error is dangerous; it can catch exceptions such as ThreadDeath and OutOfMemoryError.]]></Description>
        </Rule>
        <Rule name='CatchException'>
            <Description>
                <![CDATA[Catching Exception is often too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.]]></Description>
        </Rule>
        <Rule name='CatchIllegalMonitorStateException'>
            <Description>
                <![CDATA[Dubious catching of IllegalMonitorStateException. IllegalMonitorStateException is generally only thrown in case of a design flaw in your code (calling wait or notify on an object you do not hold a lock on).]]></Description>
        </Rule>
        <Rule name='CatchNullPointerException'>
            <Description>
                <![CDATA[Catching NullPointerException is never appropriate. It should be avoided in the first place with proper null checking, and it can mask underlying errors.]]></Description>
        </Rule>
        <Rule name='CatchRuntimeException'>
            <Description>
                <![CDATA[Catching RuntimeException is often too broad or general. It should usually be restricted to framework or infrastructure code, rather than application code.]]></Description>
        </Rule>
        <Rule name='CatchThrowable'>
            <Description>
                <![CDATA[Catching Throwable is dangerous; it can catch exceptions such as ThreadDeath and OutOfMemoryError.]]></Description>
        </Rule>
        <Rule name='ClassName'>
            <Description>
                <![CDATA[Verifies that the name of a class matches a regular expression. By default it checks that the class name starts with an uppercase letter and is followed by zero or more word characters (letters, numbers or underscores). The regex property specifies the regular expression used to validate the class name.]]></Description>
        </Rule>
        <Rule name='ClassSize'>
            <Description>
                <![CDATA[Checks if the size of a class exceeds the number of lines specified by the maxLines property, which defaults to 1000.]]></Description>
        </Rule>
        <Rule name='CloneableWithoutClone'>
            <Description>
                <![CDATA[A class that implements java.lang.Cloneable should define a clone() method.]]></Description>
        </Rule>
        <Rule name='ConfusingClassNamedException'>
            <Description>
                <![CDATA[This class is not derived from another exception, but ends with 'Exception'. This will be confusing to users of this class.]]></Description>
        </Rule>
        <Rule name='ConfusingMethodName'>
            <Description>
                <![CDATA[Checks for confusing method names. The referenced methods have names that differ only by capitalization. This is very confusing because if the capitalization were identical then one of the methods would override the other.]]></Description>
        </Rule>
        <Rule name='ConstantIfExpression'>
            <Description>
                <![CDATA[Checks for if statements with a constant value for the if expression, such as true, false, null, or a literal constant value.]]></Description>
        </Rule>
        <Rule name='ConstantTernaryExpression'>
            <Description>
                <![CDATA[Checks for ternary expressions with a constant value for the boolean expression, such as true, false, null, or a literal constant value.]]></Description>
        </Rule>
        <Rule name='CyclomaticComplexity'>
            <Description>
                <![CDATA[Checks the cyclomatic complexity for methods/classes.A method (or "closure field") with a cyclomatic complexity value greater than the maxMethodComplexity property (which defaults to 20) causes a violation. Likewise, a class that has an (average method) cyclomatic complexityvalue greater than the maxClassAverageMethodComplexity property (which defaults to 20) causes a violation.]]></Description>
        </Rule>
        <Rule name='DeadCode'>
            <Description>
                <![CDATA[Dead code appears after a return statement or an exception is thrown. If code appears after one of these statements then it will never be executed and can be safely deleted. ]]></Description>
        </Rule>
        <Rule name='DoubleNegative'>
            <Description>
                <![CDATA[There is no point in using a double negative, it is always positive. For instance !!x can always be simplified to x. And !(!x) can as well.]]></Description>
        </Rule>
        <Rule name='DuplicateCaseStatement'>
            <Description>
                <![CDATA[Check for duplicate case statements in a switch block, such as two equal integers or strings.]]></Description>
        </Rule>
        <Rule name='DuplicateImport'>
            <Description><![CDATA[Duplicate import statements are unnecessary.]]></Description>
        </Rule>
        <Rule name='DuplicateNumberLiteral'>
            <Description>
                <![CDATA[Code containing duplicate number literals can usually be improved by declaring the number as a constant field.  The ignoreNumbers property can optionally specify a comma-separated list of numbers to ignore.]]></Description>
        </Rule>
        <Rule name='DuplicateStringLiteral'>
            <Description>
                <![CDATA[Code containing duplicate String literals can usually be improved by declaring the String as a constant field. The ignoreStrings property can optionally specify a comma-separated list of Strings to ignore.]]></Description>
        </Rule>
        <Rule name='ElseBlockBraces'>
            <Description>
                <![CDATA[Use braces for else blocks, even for a single statement. By default, braces are not required for an else if it is followed immediately by an if. Set the bracesRequiredForElseIf property to true to require braces is that situation as well.]]></Description>
        </Rule>
        <Rule name='EmptyCatchBlock'>
            <Description>
                <![CDATA[In most cases, exceptions should not be caught and ignored (swallowed).]]></Description>
        </Rule>
        <Rule name='EmptyElseBlock'>
            <Description><![CDATA[Empty else blocks are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptyFinallyBlock'>
            <Description><![CDATA[Empty finally blocks are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptyForStatement'>
            <Description><![CDATA[Empty for statements are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptyIfStatement'>
            <Description><![CDATA[Empty if statements are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptySwitchStatement'>
            <Description><![CDATA[Empty switch statements are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptySynchronizedStatement'>
            <Description><![CDATA[Empty synchronized statements are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptyTryBlock'>
            <Description><![CDATA[Empty try blocks are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EmptyWhileStatement'>
            <Description><![CDATA[Empty while statements are confusing and serve no purpose.]]></Description>
        </Rule>
        <Rule name='EqualsAndHashCode'>
            <Description>
                <![CDATA[If either the equals(Object) or the hashCode() methods are overridden within a class, then both must be overridden.]]></Description>
        </Rule>
        <Rule name='ExplicitArrayListInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of an ArrayList. In Groovy, it is best to write "new ArrayList()" as "[]", which creates the same object.]]></Description>
        </Rule>
        <Rule name='ExplicitCallToAndMethod'>
            <Description>
                <![CDATA[This rule detects when the and(Object) method is called directly in code instead of using the & operator. A groovier way to express this: a.and(b) is this: a & b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToCompareToMethod'>
            <Description>
                <![CDATA[This rule detects when the compareTo(Object) method is called directly in code instead of using the <=>, >, >=, <, and <= operators. A groovier way to express this: a.compareTo(b) is this: a <=> b, or using the other operators.]]></Description>
        </Rule>
        <Rule name='ExplicitCallToDivMethod'>
            <Description>
                <![CDATA[This rule detects when the div(Object) method is called directly in code instead of using the / operator. A groovier way to express this: a.div(b) is this: a / b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToEqualsMethod'>
            <Description>
                <![CDATA[This rule detects when the equals(Object) method is called directly in code instead of using the == or != operator. A groovier way to express this: a.equals(b) is this: a == b and a groovier way to express : !a.equals(b) is : a != b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToGetAtMethod'>
            <Description>
                <![CDATA[This rule detects when the getAt(Object) method is called directly in code instead of using the [] index operator. A groovier way to express this: a.getAt(b) is this: a[b]]]></Description>
        </Rule>
        <Rule name='ExplicitCallToLeftShiftMethod'>
            <Description>
                <![CDATA[This rule detects when the leftShift(Object) method is called directly in code instead of using the << operator. A groovier way to express this: a.leftShift(b) is this: a << b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToMinusMethod'>
            <Description>
                <![CDATA[This rule detects when the minus(Object) method is called directly in code instead of using the - operator. A groovier way to express this: a.minus(b) is this: a - b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToModMethod'>
            <Description>
                <![CDATA[This rule detects when the mod(Object) method is called directly in code instead of using the % operator. A groovier way to express this: a.mod(b) is this: a % b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToMultiplyMethod'>
            <Description>
                <![CDATA[This rule detects when the minus(Object) method is called directly in code instead of using the * operator. A groovier way to express this: a.multiply(b) is this: a * b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToOrMethod'>
            <Description>
                <![CDATA[This rule detects when the or(Object) method is called directly in code instead of using the | operator. A groovier way to express this: a.or(b) is this: a | b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToPlusMethod'>
            <Description>
                <![CDATA[This rule detects when the plus(Object) method is called directly in code instead of using the + operator. A groovier way to express this: a.plus(b) is this: a + b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToPowerMethod'>
            <Description>
                <![CDATA[This rule detects when the power(Object) method is called directly in code instead of using the ** operator. A groovier way to express this: a.power(b) is this: a ** b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToRightShiftMethod'>
            <Description>
                <![CDATA[This rule detects when the rightShift(Object) method is called directly in code instead of using the >> operator. A groovier way to express this: a.rightShift(b) is this: a >> b]]></Description>
        </Rule>
        <Rule name='ExplicitCallToXorMethod'>
            <Description>
                <![CDATA[This rule detects when the xor(Object) method is called directly in code instead of using the ^ operator. A groovier way to express this: a.xor(b) is this: a ^ b]]></Description>
        </Rule>
        <Rule name='ExplicitHashMapInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of a HashMap. In Groovy, it is best to write "new HashMap()" as "[:]", which creates the same object.]]></Description>
        </Rule>
        <Rule name='ExplicitHashSetInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of a HashSet. In Groovy, it is best to write "new HashSet()" as "[] as Set", which creates the same object.]]></Description>
        </Rule>
        <Rule name='ExplicitLinkedListInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of a LinkedList. In Groovy, it is best to write "new LinkedList()" as "[] as Queue", which creates the same object.]]></Description>
        </Rule>
        <Rule name='ExplicitStackInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of a Stack. In Groovy, it is best to write "new Stack()" as "[] as Stack", which creates the same object.]]></Description>
        </Rule>
        <Rule name='ExplicitTreeSetInstantiation'>
            <Description>
                <![CDATA[This rule checks for the explicit instantiation of a TreeSet. In Groovy, it is best to write "new TreeSet()" as "[] as SortedSet", which creates the same object.]]></Description>
        </Rule>
        <Rule name='FieldName'>
            <Description>
                <![CDATA[Verifies that the name of each field matches a regular expression. By default it checks that non-'final' field names start with a lowercase letter and contains only letters or numbers, and 'final' field names start with an uppercase letter and contain only uppercase letters, numbers and underscores. The regex property specifies the default regular expression used to validate field names. The finalRegex property specifies the regular expression to validate 'final' field names. The staticRegex property specifies the regular expression to validate 'static' field names. The staticFinalRegex property specifies the regular expression to validate 'static final' field names.  The ignoreFieldNames property can specify field names that should be ignored, optionally containing wildcard characters ('*' or '?').]]></Description>
        </Rule>
        <Rule name='ForStatementBraces'>
            <Description><![CDATA[Use braces for for statements, even for a single statement.]]></Description>
        </Rule>
        <Rule name='GStringAsMapKey'>
            <Description>
                <![CDATA[A GString should not be used as a map key since its hashcode is not guaranteed to be stable. Consider calling key.toString().]]></Description>
        </Rule>
        <Rule name='IfStatementBraces'>
            <Description><![CDATA[Use braces for if statements, even for a single statement.]]></Description>
        </Rule>
        <Rule name='ImplementationAsType'>
            <Description>
                <![CDATA[Checks for use of a predefined set of concrete classes (e.g. ArrayList, Hashtable, ConcurrentHashMap) when specifying the type of a method parameter, closure parameter, constructor parameter, method return type or field type. The associated interfaces should be used to specify the type instead.]]></Description>
        </Rule>
        <Rule name='ImportFromSamePackage'>
            <Description><![CDATA[An import of a class that is within the same package is unnecessary.]]></Description>
        </Rule>
        <Rule name='InterfaceName'>
            <Description>
                <![CDATA[Verifies that the name of an interface matches a regular expression specified in the regex property. If that property is null or empty, then this rule is not applied (i.e., it does nothing). It defaults to null, so this rule must be explicitly configured to be active.]]></Description>
        </Rule>
        <Rule name='InvertedIfElse'>
            <Description>
                <![CDATA[An inverted if-else statement is one in which there is a single if statement with a single else branch and the boolean test of the if is negated. For instance "if (!x) false else true". It is usually clearer to write this as "if (x) true else false". ]]></Description>
        </Rule>
        <Rule name='MethodCount'>
            <Description>
                <![CDATA[A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to have more fine grained objects. The <em>maxMethods</em> property specifies the threshhold,which defaults to 30.]]></Description>
        </Rule>
        <Rule name='MethodName'>
            <Description>
                <![CDATA[Verifies that the name of each method matches a regular expression. By default it checks that the method name starts with a lowercase letter. The regex property specifies the regular expression to check the method name against.  The ignoreMethodNames property can specify method names that should be ignored, optionally containing wildcard characters ('*' or '?').]]></Description>
        </Rule>
        <Rule name='MethodSize'>
            <Description>
                <![CDATA[Checks if the size of a method exceeds the number of lines specified by the maxLines property, which defaults to 100.]]></Description>
        </Rule>
        <Rule name='NestedBlockDepth'>
            <Description>
                <![CDATA[Checks for blocks or closures nested more than maxNestedBlockDepth levels deep, which defaults to 5.]]></Description>
        </Rule>
        <Rule name='NestedSynchronization'>
            <Description>
                <![CDATA[Nested synchronized statements should be avoided. Nested synchronized statements are either useless (if the lock objects are identical) or prone to deadlock.]]></Description>
        </Rule>
        <Rule name='ObjectOverrideMisspelledMethodName'>
            <Description>
                <![CDATA[Verifies that the names of the most commonly overridden methods of Object: equals, hashCode and toString, are correct.]]></Description>
        </Rule>
        <Rule name='PackageName'>
            <Description>
                <![CDATA[Verifies that the package name for a class matches a regular expression. By default it checks that the package name consists of only lowercase letters, separated by periods. The regex property specifies the regular expression used to validate the package name. The packageNameRequired property indicates whether a package name declaration is required for all classes.]]></Description>
        </Rule>
        <Rule name='ParameterName'>
            <Description>
                <![CDATA[Verifies that the name of each parameter matches a regular expression. This rule applies to method parameters, constructor parameters and closure parameters. By default it checks that parameter names start with a lowercase letter and contains only letters or numbers. The regex property specifies the default regular expression used to validate the parameter name.  The ignoreParameterNames property can specify parameter names that should be ignored, optionally containing wildcard characters ('*' or '?').]]></Description>
        </Rule>
        <Rule name='PrintStackTrace'>
            <Description><![CDATA[Checks for calls to printStackTrace().]]></Description>
        </Rule>
        <Rule name='Println'>
            <Description><![CDATA[Checks for calls to this.print(), this.println() or this.printf().]]></Description>
        </Rule>
        <Rule name='PropertyName'>
            <Description>
                <![CDATA[Verifies that the name of each property matches a regular expression. By default it checks that property names other than 'static final' start with a lowercase letter and contains only letters or numbers, and 'static final' property names start with an uppercase letter and contain only uppercase letters, numbers and underscores. The regex property specifies the default regular expression used to validate property names. The finalRegex property specifies the regular expression to validate 'final' property names. The staticRegex property specifies the regular expression to validate 'static' property names. The staticFinalRegex property specifies the regular expression to validate 'static final' property names.  The ignorePropertyNames property can specify property names that should be ignored, optionally containing wildcard characters ('*' or '?').]]></Description>
        </Rule>
        <Rule name='RemoveAllOnSelf'>
            <Description>
                <![CDATA[Don't use removeAll to clear a collection. If you want to remove all elements from a collection c, use c.clear, not c.removeAll(c). Calling c.removeAll(c) to clear a collection is less clear, susceptible to errors from typos, less efficient and for some collections, might throw a ConcurrentModificationException.]]></Description>
        </Rule>
        <Rule name='ReturnFromFinallyBlock'>
            <Description>
                <![CDATA[Returning from a finally block is confusing and can hide the original exception.]]></Description>
        </Rule>
        <Rule name='ReturnNullFromCatchBlock'>
            <Description>
                <![CDATA[Returning null from a catch block often masks errors and requires the client to handle error codes. In some coding styles this is discouraged. ]]></Description>
        </Rule>
        <Rule name='ReturnsNullInsteadOfEmptyArray'>
            <Description>
                <![CDATA[Consider returning a zero length array rather than null. It is often a better design to return a length zero array rather than a null reference to indicate that there are no results (i.e., an empty list of results). This way, no explicit check for null is needed by clients of the method.]]></Description>
        </Rule>
        <Rule name='ReturnsNullInsteadOfEmptyCollection'>
            <Description>
                <![CDATA[Consider returning a zero length collection rather than null. It is often a better design to return a length zero collection rather than a null reference to indicate that there are no results (i.e., an empty list of results). This way, no explicit check for null is needed by clients of the method.]]></Description>
        </Rule>
        <Rule name='SerialVersionUID'>
            <Description>
                <![CDATA[A serialVersionUID is normally intended to be used with Serialization. It needs to be of type long, static, and final. Also, it should have a visibility modifier such as public or private. Providing no modifier creates a Property and Groovy generates a getter, which is probably not intended.]]></Description>
        </Rule>
        <Rule name='StringInstantiation'>
            <Description>
                <![CDATA[Use a String literal (e.g., "...") instead of calling the corresponding String constructor (new String("..")) directly.]]></Description>
        </Rule>
        <Rule name='SynchronizedMethod'>
            <Description>
                <![CDATA[This rule reports uses of the synchronized keyword on methods. Synchronized methods are the same as synchronizing on 'this', which effectively make your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.]]></Description>
        </Rule>
        <Rule name='SynchronizedOnGetClass'>
            <Description>
                <![CDATA[Synchronization on getClass rather than class literal. This instance method synchronizes on this.getClass(). If this class is subclassed, subclasses will synchronize on the class object for the subclass, which isn't likely what was intended.]]></Description>
        </Rule>
        <Rule name='SynchronizedOnThis'>
            <Description>
                <![CDATA[This rule reports uses of the synchronized blocks where the synchronization reference is 'this'. Doing this effectively makes your synchronization policy public and modifiable by other objects. To avoid possibilities of deadlock, it is better to synchronize on internal objects.]]></Description>
        </Rule>
        <Rule name='SystemErrPrint'>
            <Description>
                <![CDATA[Checks for calls to System.err.print(), System.err.println() or System.err.printf().]]></Description>
        </Rule>
        <Rule name='SystemOutPrint'>
            <Description>
                <![CDATA[Checks for calls to System.out.print(), System.out.println() or System.out.printf().]]></Description>
        </Rule>
        <Rule name='SystemRunFinalizersOnExit'>
            <Description>
                <![CDATA[Method calls to System.runFinalizersOnExit() should not be allowed. This method is inherently non-thread-safe, may result in data corruption, deadlock, and may effect parts of the program far removed from it's call point. It is deprecated, and it's use strongly discouraged.]]></Description>
        </Rule>
        <Rule name='ThreadLocalNotStaticFinal'>
            <Description>
                <![CDATA[ThreadLocal fields should be static and final. In the most common case a java.lang.ThreadLocal instance associates state with a thread. A non-static non-final java.lang.ThreadLocal field associates state with an instance-thread combination. This is seldom necessary and often a bug which can cause memory leaks and possibly incorrect behavior.]]></Description>
        </Rule>
        <Rule name='ThreadYield'>
            <Description>
                <![CDATA[Method calls to Thread.yield() should not be allowed. This method has no useful guaranteed semantics, and is often used by inexperienced programmers to mask race conditions.]]></Description>
        </Rule>
        <Rule name='ThrowError'>
            <Description><![CDATA[Checks for throwing an instance of java.lang.Error.]]></Description>
        </Rule>
        <Rule name='ThrowException'>
            <Description><![CDATA[Checks for throwing an instance of java.lang.Exception.]]></Description>
        </Rule>
        <Rule name='ThrowExceptionFromFinallyBlock'>
            <Description>
                <![CDATA[Throwing an exception from a finally block is confusing and can hide the original exception.]]></Description>
        </Rule>
        <Rule name='ThrowNullPointerException'>
            <Description><![CDATA[Checks for throwing an instance of java.lang.NullPointerException.]]></Description>
        </Rule>
        <Rule name='ThrowRuntimeException'>
            <Description><![CDATA[Checks for throwing an instance of java.lang.RuntimeException.]]></Description>
        </Rule>
        <Rule name='ThrowThrowable'>
            <Description><![CDATA[Checks for throwing an instance of java.lang.Throwable.]]></Description>
        </Rule>
        <Rule name='UnnecessaryBooleanExpression'>
            <Description>
                <![CDATA[Checks for unnecessary boolean expressions, including ANDing (&&) or ORing (||) with true, false, null, or a Map/List/String/Number literal. Also checks for negation (!) of true, false, null, or a Map/List/String/Number literal.]]></Description>
        </Rule>
        <Rule name='UnnecessaryCollectionCall'>
            <Description>
                <![CDATA[Useless call to collections. This call doesn't make sense. For any collection c, calling c.containsAll(c) should always be true, and c.retainAll(c) should have no effect.]]></Description>
        </Rule>
        <Rule name='UnnecessaryConstructor'>
            <Description>
                <![CDATA[This rule detects when a constructor is not necessary; i.e., when there's only one constructor, it's public, has an empty body, and takes no arguments.]]></Description>
        </Rule>
        <Rule name='UnnecessaryGroovyImport'>
            <Description>
                <![CDATA[A Groovy file does not need to include an import for classes from java.lang, java.util, java.io, java.net, groovy.lang and groovy.util, as well as the classes java.math.BigDecimal and java.math.BigInteger.]]></Description>
        </Rule>
        <Rule name='UnnecessaryIfStatement'>
            <Description>
                <![CDATA[Checks for if statements where the if and else blocks are merely returning true and false constants. These cases can be replaced by a simple return statement.]]></Description>
        </Rule>
        <Rule name='UnnecessaryOverridingMethod'>
            <Description>
                <![CDATA[The overriding method merely calls the same method defined in a superclass]]></Description>
        </Rule>
        <Rule name='UnnecessaryReturnKeyword'>
            <Description>
                <![CDATA[In Groovy, the return keyword is often optional. If a statement is the last line in a method or closure then you do not need to have the return keyword.]]></Description>
        </Rule>
        <Rule name='UnnecessaryTernaryExpression'>
            <Description>
                <![CDATA[Checks for ternary expressions where the conditional expression always evaluates to a boolean and the true and false expressions are merely returning true and false constants. Also checks for ternary expressions where both expressions are the same constant or variable.]]></Description>
        </Rule>
        <Rule name='UnusedArray'>
            <Description>
                <![CDATA[Checks for array allocations that are not assigned or used, unless it is the last statement within a block.]]></Description>
        </Rule>
        <Rule name='UnusedImport'>
            <Description>
                <![CDATA[Imports for a class that is never referenced within the source file is unnecessary.]]></Description>
        </Rule>
        <Rule name='UnusedObject'>
            <Description>
                <![CDATA[Checks for object allocations that are not assigned or used, unless it is the last statement within a block.]]></Description>
        </Rule>
        <Rule name='UnusedPrivateField'>
            <Description>
                <![CDATA[Checks for private fields that are not referenced within the same class.]]></Description>
        </Rule>
        <Rule name='UnusedPrivateMethod'>
            <Description>
                <![CDATA[Checks for private methods that are not referenced within the same class.]]></Description>
        </Rule>
        <Rule name='UnusedVariable'>
            <Description><![CDATA[Checks for variables that are never referenced.]]></Description>
        </Rule>
        <Rule name='UseOfNotifyMethod'>
            <Description>
                <![CDATA[This code calls notify() rather than notifyAll(). Java monitors are often used for multiple conditions. Calling notify() only wakes up one thread, meaning that the thread woken up might not be the one waiting for the condition that the caller just satisfied.]]></Description>
        </Rule>
        <Rule name='VariableName'>
            <Description>
                <![CDATA[Verifies that the name of each method matches a regular expression. By default it checks that non-'final' variable names start with a lowercase letter and contains only letters or numbers, and 'final' variable names start with an uppercase letter and contain only uppercase letters, numbers and underscores. The regex property specifies the default regular expression used to validate a non-'final' variable name. The finalRegex property specifies the regular expression used to validate 'final' variable names. The ignoreVariableNames property can specify variable names that should be ignored, optionally containing wildcard characters ('*' or '?').]]></Description>
        </Rule>
        <Rule name='VolatileLongOrDoubleField'>
            <Description>
                <![CDATA[Long or double fields should not be declared as volatile. Java specifies that reads and writes from such fields are atomic, but many JVM's have violated this specification. Unless you are certain of your JVM, it is better to synchronize access to such fields rather than declare them volatile. This rule flags fields marked volatile when their type is double or long or the name of their type is "Double" or "Long".]]></Description>
        </Rule>
        <Rule name='WhileStatementBraces'>
            <Description><![CDATA[Use braces for while statements, even for a single statement.]]></Description>
        </Rule>
    </Rules>
</CodeNarc>

With the following XSLT file (inspired by Checkstyle reports and the Github website) we can transform the XML to a custom HTML CodeNarc report:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" indent="yes"/>
    <xsl:decimal-format decimal-separator="." grouping-separator=","/>

    <xsl:key name="violations" match="Violation" use="@ruleName"/>

    <xsl:template match="CodeNarc">
        <html>
            <head>
                <title><xsl:value-of select="Project/@title"/></title>
                <link rel="stylesheet" href="default.css" type="text/css"/>
            </head>
            <body>
                <a name="top"></a>

                <div class="header">
                    <h1>
                        <img src="haki-logo.png" width="48" height="48"/>
                        <xsl:value-of select="Project/@title"/>
                     </h1>
                </div>

                <div class="rule"></div>

                <div class="summary">
                    <div class="first">
                        <dl>
                            <dt>Generated</dt>
                            <dd><xsl:value-of select="Report/@timestamp"/></dd>
                        </dl>
                        <dl>
                            <dt>Tool</dt>
                            <dd><a href="{@url}">CodeNarc</a></dd>
                        </dl>
                        <dl>
                            <dt>Version</dt>
                            <dd><xsl:value-of select="@version"/></dd>
                        </dl>

                        <dl>
                            <dt>Sources</dt>
                            <dl>
                            <xsl:for-each select="Project/SourceDirectory">
                                <xsl:value-of select="."/>
                                <br />
                            </xsl:for-each>
                            </dl>
                        </dl>
                    </div>
                    <div class="last">
                        <xsl:apply-templates select="PackageSummary"/>
                    </div>
                </div>

                <div class="rule"></div>

                <xsl:apply-templates select="." mode="summary"/>

                <div class="rule"></div>

                <xsl:apply-templates select="." mode="full"/>

                <div class="rule"></div>

                <xsl:apply-templates select="Rules"/>

                <div class="rule"></div>

                <p>XSLT created by <a href="http://www.mrhaki.com">Hubert A. Klein Ikkink (mrhaki)</a></p>

            </body>
        </html>
    </xsl:template>

    <xsl:template match="PackageSummary">
        <ul>
            <li>
                <strong>
                    <xsl:value-of select="@totalFiles"/>
                </strong>
                <span>total files</span>
            </li>
            <li>
                <strong>
                    <xsl:value-of select="@filesWithViolations"/>
                </strong>
                <span>file violations</span>
            </li>
            <li>
                <strong>
                    <xsl:value-of select="@priority1"/>
                </strong>
                <span>priority 1</span>
            </li>
            <li>
                <strong>
                    <xsl:value-of select="@priority2"/>
                </strong>
                <span>priority 2</span>
            </li>
            <li>
                <strong>
                    <xsl:value-of select="@priority3"/>
                </strong>
                <span>priority 3</span>
            </li>
        </ul>
    </xsl:template>

    <xsl:template match="Rules">
        <h2>Rules</h2>

        <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tr>
            <th>Name</th>
            <th>Description</th>
            <th>Violations</th>
        </tr>

        <xsl:apply-templates select="Rule"/>

        </table>
    </xsl:template>

    <xsl:template match="Rule">
        <xsl:variable name="ruleName" select="@name"/>
        <xsl:variable name="violationCount" select="count(key('violations', $ruleName))"/>
        <xsl:if test="$violationCount > 0">
        <tr>
            <td>
                <a name="r-{$ruleName}"> </a>
                <xsl:value-of select="$ruleName"/>
            </td>
            <td>
                <xsl:value-of select="Description"/>
            </td>
            <td>
                <xsl:value-of select="$violationCount"/>
            </td>
        </tr>
        </xsl:if>
    </xsl:template>

    <xsl:template match="CodeNarc" mode="summary">
        <h2>Files</h2>

        <table border="0" width="100%" cellpadding="0" cellspacing="0">
        <tr>
            <th>Name</th>
            <th>Violations</th>
        </tr>
        <xsl:for-each select="Package/File">
            <xsl:sort select="@name"/>
            <xsl:apply-templates select="." mode="summary"/>
        </xsl:for-each>
        </table>
    </xsl:template>

    <xsl:template match="CodeNarc" mode="full">
        <xsl:for-each select="Package/File">
            <xsl:sort select="@name"/>
            <xsl:apply-templates select="." mode="full"/>
            <p/>
            <p/>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="File" mode="summary">
        <xsl:variable name="violationCount" select="count(Violation)"/>
        <tr>
            <td><a href="#f-{@name}"><xsl:value-of select="../@path"/>/<xsl:value-of select="@name"/></a></td>
            <td><xsl:value-of select="$violationCount"/></td>
        </tr>
    </xsl:template>

    <xsl:template match="File" mode="full">
        <a name="f-{@name}"></a>
        <h3>
            File
            <xsl:value-of select="@name"/>
        </h3>

        <table border="0" width="100%" cellpadding="0" cellspacing="0">
            <tr>
                <th>Priority</th>
                <th>Rule</th>
                <th>Violation Description</th>
                <th>Line</th>
                <th>Source</th>
            </tr>
            <xsl:for-each select="Violation">
                <xsl:sort select="@priority"/>
                <tr>
                    <td>
                        <xsl:value-of select="@priority"/>
                    </td>
                    <td>
                        <a href="#r-{@ruleName}">
                            <xsl:value-of select="@ruleName"/>
                        </a>
                    </td>
                    <td>
                        <xsl:value-of select="Message"/>
                    </td>
                    <td>
                        <xsl:value-of select="@lineNumber"/>
                    </td>
                    <td>
                        <code>
                            <xsl:value-of select="SourceLine"/>
                        </code>
                    </td>
                </tr>
            </xsl:for-each>
        </table>
        <a href="#top">Back to top</a>
    </xsl:template>

</xsl:stylesheet>

Here is an image of the resulting HTML report:

The XSLT source file and supporting CSS and images are at Github.

0 comments:

Post a Comment