CheckStyle – Enforcing a coding style: part 2
- CheckStyle – Enforcing a coding style: part 1
- CheckStyle – Enforcing a coding style: part 2
- CheckStyle – Enforcing a coding style: part 3
- CheckStyle – Enforcing a coding style: part 4
Cranking up the challenge – Making my own rules.
In the second of three posts on configuring CheckStyle as a code format enforcer, I will be looking at how to set my own rules by producing my own checkstyle.xml
Disclaimer time. I was aware that there is a plugin for Eclipse to make this task easy. There were however two reasons that I didn’t want to do that.
- The point of this whole blog is to learn about the tools I am using in far greater depth than I have time to while integrating things at work.
- I don’t have Eclipse installed and I really don’t want to have to deal with installing another IDE just to run a plugin.
So as I mentioned last time, the CheckStyle team has their own rule configuration which is well commented including covering options that they choose not to use. I’ll start this out with a bit of commentary on their configuration. Be warned, it is a LONG file, so feel free to browse only sections that catch your interest. I have split the code up into sections and added my comments.
[expand title=”xml header” startwrap=”” endwrap=”“]
<?xml version="1.0"?> <!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
[/expand]
[expand title=”set up” startwrap=”” endwrap=”“]
<module name="Checker"> <!-- If you set the basedir property below, then all reported file names will be relative to the specified directory. See http://checkstyle.sourceforge.net/config.html#Checker <property name="basedir" value="${basedir}"/> --> <metadata name="com.atlas-sw.eclipse" value="I like Sydney"/> <property name="cacheFile" value="${checkstyle.cache.file}"/> <property name="severity" value="error"/> <property name="fileExtensions" value="java, properties, xml, vm, g, g4, dtd"/> <!-- BeforeExecutionFileFilters is required for sources that are based on java9 --> <module name="BeforeExecutionExclusionFileFilter"> <property name="fileNamePattern" value="module\-info\.java$" /> </module>
‘Checker’ is the parent module of everything: Most filters will be within the ‘Treewalker’ module which is a child of ‘Checker’. Items being moved between the two will occasionally break compatibility as suppression filters moved in 8.1. This is always announced in the release notes and as you define the version of the api you are using in the build plugin configuration, you can update to a new version at your leisure.
As it explains, the ‘basedir’ property is for setting in your build tool. It works nicely in a simple project like this one, but getting it working in a complex multi-module build is, quite frankly a pain. More on that in part three.
[/expand]
[expand title=”severity and suppression file configuration” startwrap=”” endwrap=”“]
<!-- Filters --> <module name="SeverityMatchFilter"> <!-- report all violations except ignore --> <property name="severity" value="ignore"/> <property name="acceptOnMatch" value="false"/> </module> <module name="SuppressionFilter"> <property name="file" value="${checkstyle.suppressions.file}"/> </module> <module name="SuppressWarningsFilter"/>
The suppression file allows you to hide some warnings, either completely or in particular scopes. e.g. you might want to ignore repeated use of string literals in test classes.
[/expand]
[expand title=”file header checks” startwrap=”” endwrap=”“]
<!-- Headers --> <module name="Header"> <property name="headerFile" value="${checkstyle.header.file}"/> <property name="fileExtensions" value="java"/> <property name="id" value="header"/> </module> <module name="RegexpHeader"> <property name="headerFile" value="${checkstyle.regexp.header.file}"/> <property name="fileExtensions" value="java"/> </module>
You can define a header format that must be included in all files.
[/expand]
[expand title=”package Javadoc” startwrap=”” endwrap=”“]
<!-- Javadoc Comments --> <module name="JavadocPackage"> <property name="allowLegacy" value="false"/> </module>
Require package-info.java Javadoc files for each package.[/expand]
[expand title=”miscellaneous” startwrap=”” endwrap=”“]
<!-- Miscellaneous --> <module name="NewlineAtEndOfFile"/> <module name="Translation"> <property name="requiredTranslations" value="de, fr, fi, es, pt, ja, tr, zh"/> </module> <module name="UniqueProperties"/>
I can see ‘requiredTranslations’ being very useful and certainly UniqueProperties very much so. I hate seeing multiple definitions of the same property.[/expand]
[expand title=”regex definitions” startwrap=”” endwrap=”“]
<!-- Regexp --> <module name="RegexpMultiline"/> <module name="RegexpMultiline"> <property name="format" value="\r?\n[\t ]*\r?\n[\t ]*\r?\n"/> <property name="fileExtensions" value="java,xml,properties"/> <property name="message" value="Unnecessary consecutive lines"/> </module> <module name="RegexpMultiline"> <property name="format" value="/\*\*\W+\* +\p{javaLowerCase}"/> <property name="fileExtensions" value="java"/> <property name="message" value="First sentence in a comment should start with a capital letter"/> </module> <module name="RegexpSingleline"> <property name="format" value="\s+$"/> <property name="minimum" value="0"/> <property name="maximum" value="0"/> </module> <module name="RegexpSingleline"> <property name="format" value="/\*\* +\p{javaLowerCase}"/> <property name="fileExtensions" value="java"/> <property name="message" value="First sentence in a comment should start with a capital letter"/> </module> <module name="RegexpSingleline"> <property name="format" value="^(?!(.*http|import)).{101,}$"/> <property name="fileExtensions" value="g, g4"/> <property name="message" value="Line should not be longer then 100 symbols"/> </module> <module name="RegexpOnFilename" /> <module name="RegexpOnFilename"> <property name="folderPattern" value="[\\/]src[\\/]\w+[\\/]java[\\/]"/> <property name="fileNamePattern" value="\.java$"/> <property name="match" value="false"/> <message key="regexp.filepath.mismatch" value="Only java files should be located in the ''src/*/java'' folders."/> </module> <module name="RegexpOnFilename"> <property name="folderPattern" value="[\\/]src[\\/]xdocs[\\/]"/> <property name="fileNamePattern" value="\.(xml)|(vm)$"/> <property name="match" value="false"/> <message key="regexp.filepath.mismatch" value="All files in the ''src/xdocs'' folder should have the ''xml'' or ''vm'' extension."/> </module> <module name="RegexpOnFilename"> <property name="folderPattern" value="[\\/]src[\\/]it[\\/]java[\\/]"/> <property name="fileNamePattern" value="^((\w+Test)|(Abstract\w+))\.java$"/> <property name="match" value="false"/> <message key="regexp.filepath.mismatch" value="All files in the ''src/it/java'' folder should be named ''*Test.java'' or ''Abstract*.java''."/> </module>
I can see use in some of these. I haven’t come across the use of ‘src/it/java’ before: I can see that being a helpful tip.[/expand]
[expand title=”file length warning and TAB or spaces definition” startwrap=”” endwrap=”“]
<!-- Size Violations --> <module name="FileLength"> <property name="fileExtensions" value="java"/> </module> <!-- Whitespace --> <module name="FileTabCharacter"> <property name="eachLine" value="false"/> </module>
FileTabCharacter – definitely. This is such a no brainer to get working as it is such a pain when an incorrectly configured IDE leads to check ins with the wrong type of spacing.
FileLength checks are useful, but I think I would have as info or warn: Yes we want to refactor long classes, but in reality this is probably just going to be suppressed, at least initially, whenever it appears.[/expand]
[expand title=”Treewalker starts with annotation rules” startwrap=”” endwrap=”“]
<!-- Annotations --> <module name="AnnotationLocation"> <property name="tokens" value="ANNOTATION_DEF"/> <property name="tokens" value="ANNOTATION_FIELD_DEF"/> <property name="allowSamelineSingleParameterlessAnnotation" value="false"/> </module> <module name="AnnotationLocation"> <property name="tokens" value="PARAMETER_DEF"/> <property name="allowSamelineMultipleAnnotations" value="true"/> </module> <module name="AnnotationOnSameLine"> <!-- we can not use it as it conflicts with AnnotationLocation --> <property name="severity" value="ignore"/> <property name="tokens" value="METHOD_DEF"/> <property name="tokens" value="CTOR_DEF"/> <property name="tokens" value="TYPECAST"/> <property name="tokens" value="DOT"/> <property name="tokens" value="CLASS_DEF"/> <property name="tokens" value="ENUM_DEF"/> <property name="tokens" value="INTERFACE_DEF"/> <property name="tokens" value="TYPE_ARGUMENT"/> <property name="tokens" value="ANNOTATION_DEF"/> <property name="tokens" value="LITERAL_NEW"/> <property name="tokens" value="LITERAL_THROWS"/> <property name="tokens" value="VARIABLE_DEF"/> <property name="tokens" value="PARAMETER_DEF"/> <property name="tokens" value="IMPLEMENTS_CLAUSE"/> <property name="tokens" value="ANNOTATION_FIELD_DEF"/> </module> <module name="AnnotationUseStyle"/> <module name="MissingDeprecated"/> <module name="MissingOverride"> <property name="javaFiveCompatibility" value="true"/> </module> <module name="PackageAnnotation"/> <module name="SuppressWarnings"> <property name="format" value="^((?!unchecked|deprecation|rawtypes).)*$"/> <message key="suppressed.warning.not.allowed" value="The warning ''{0}'' cannot be suppressed at this location. Only few javac warnings are allowed to suppress. If try to suppress checkstyle/pmd/..... violation please do this in their config file. If you try to suppress IntelliJ IDEA inspection, please use javadoc block tag @noinspection" /> </module> <module name="SuppressWarningsHolder"/>
Treewalker contains the majority of the check modules.
A lot of information on suppression annotations here.[/expand]
[expand title=”block checks” startwrap=”” endwrap=”“]
<!-- Block Checks --> <module name="AvoidNestedBlocks"> <property name="allowInSwitchCase" value="true"/> </module> <module name="EmptyBlock"> <property name="tokens" value="LITERAL_CATCH"/> <property name="tokens" value="ARRAY_INIT"/> <property name="tokens" value="LITERAL_DEFAULT"/> <property name="tokens" value="LITERAL_CASE"/> <property name="tokens" value="INSTANCE_INIT"/> <property name="tokens" value="LITERAL_DO"/> <property name="tokens" value="LITERAL_ELSE"/> <property name="tokens" value="LITERAL_FINALLY"/> <property name="tokens" value="LITERAL_FOR"/> <property name="tokens" value="LITERAL_IF"/> <property name="tokens" value="LITERAL_SWITCH"/> <property name="tokens" value="LITERAL_SYNCHRONIZED"/> <property name="tokens" value="LITERAL_TRY"/> <property name="tokens" value="LITERAL_WHILE"/> <property name="tokens" value="STATIC_INIT"/> <property name="option" value="text"/> </module> <module name="EmptyCatchBlock"/> <module name="LeftCurly"/> <module name="NeedBraces"/> <module name="NeedBraces"> <property name="tokens" value="LAMBDA"/> <property name="allowSingleLineStatement" value="true"/> </module> <module name="RightCurly"> <property name="tokens" value="METHOD_DEF"/> <property name="tokens" value="CTOR_DEF"/> <property name="tokens" value="CLASS_DEF"/> <property name="tokens" value="INSTANCE_INIT"/> <property name="tokens" value="LITERAL_FOR"/> <property name="tokens" value="STATIC_INIT"/> <property name="tokens" value="LITERAL_WHILE"/> <property name="tokens" value="LITERAL_CATCH"/> <property name="tokens" value="LITERAL_ELSE"/> <property name="tokens" value="LITERAL_FINALLY"/> <property name="tokens" value="LITERAL_IF"/> <property name="tokens" value="LITERAL_TRY"/> <property name="option" value="alone"/> </module> <module name="RightCurly"> <property name="tokens" value="LITERAL_DO"/> <property name="option" value="same"/> </module>
This all looks fine.[/expand]
[expand title=”class design” startwrap=”” endwrap=”“]
<!-- Class Design --> <module name="DesignForExtension"> <property name="ignoredAnnotations" value="Override, Test, Before, After, BeforeClass, AfterClass"/> </module> <module name="FinalClass"/> <module name="HideUtilityClassConstructor"/> <module name="InnerTypeLast"/> <module name="InterfaceIsType"/> <module name="MutableException"/> <module name="OneTopLevelClass"/> <module name="ThrowsCount"> <property name="max" value="2"/> </module> <module name="VisibilityModifier"/>
These again include some more controversial programming patterns. As ever they have benefits and downsides so its up to you are your teams philosophy to make that call.
[/expand]
[expand title=”coding rules” startwrap=”” endwrap=”“]
<!-- Coding --> <module name="ArrayTrailingComma"/> <module name="AvoidInlineConditionals"/> <module name="CovariantEquals"/> <module name="DeclarationOrder"/> <module name="DefaultComesLast"/> <module name="EmptyStatement"/> <module name="EqualsAvoidNull"/> <module name="EqualsHashCode"/> <module name="ExplicitInitialization"/> <module name="FallThrough"/> <module name="FinalLocalVariable"/> <module name="HiddenField"> <property name="ignoreConstructorParameter" value="true"/> <property name="ignoreSetter" value="true"/> <property name="setterCanReturnItsClass" value="true"/> </module> <module name="IllegalCatch"> <property name="illegalClassNames" value="java.lang.Exception, java.lang.Throwable, java.lang.RuntimeException, java.lang.NullPointerException"/> </module> <module name="IllegalInstantiation"> <property name="classes" value="org.xml.sax.SAXException, org.xml.sax.SAXParseException, org.apache.commons.beanutils.ConversionException, org.antlr.v4.runtime.misc.ParseCancellationException, antlr.RecognitionException, antlr.TokenStreamException, antlr.TokenStreamRecognitionException, antlr.ANTLRException"/> </module> <module name="IllegalThrows"/> <module name="IllegalToken"> <property name="tokens" value="LABELED_STAT"/> <property name="tokens" value="LITERAL_NATIVE"/> <property name="tokens" value="LITERAL_VOLATILE"/> <property name="tokens" value="LITERAL_ASSERT"/> </module> <module name="IllegalTokenText"> <property name="tokens" value="STRING_LITERAL"/> <property name="format" value="^(US-ASCII|ISO-8859-1|UTF-8|UTF-16BE|UTF-16LE|UTF-16)$"/> <property name="ignoreCase" value="true"/> </module> <module name="IllegalType"/> <module name="InnerAssignment"/> <module name="MagicNumber"/> <module name="MissingCtor"> <!-- we will not use that fanatic validation, extra code is not good But this Check will exists as it was created by community demand. --> <property name="severity" value="ignore"/> </module> <module name="MissingSwitchDefault"/> <module name="ModifiedControlVariable"/> <module name="MultipleStringLiterals"/> <module name="MultipleVariableDeclarations"/> <module name="NestedForDepth"> <property name="max" value="2"/> </module> <module name="NestedIfDepth"> <property name="max" value="3"/> </module> <module name="NestedTryDepth"/> <module name="NoClone"/> <module name="NoFinalizer"/> <module name="OneStatementPerLine"/> <module name="OverloadMethodsDeclarationOrder"/> <module name="PackageDeclaration"/> <module name="ParameterAssignment"/> <module name="RequireThis"/> <module name="ReturnCount"> <property name="max" value="1"/> <property name="maxForVoid" value="0"/> </module> <module name="SimplifyBooleanExpression"/> <module name="SimplifyBooleanReturn"/> <module name="StringLiteralEquality"/> <module name="SuperClone"/> <module name="SuperFinalize"/> <module name="UnnecessaryParentheses"/> <module name="VariableDeclarationUsageDistance"/>
Here we have the first example of a check put in by community demand despite the fact that CheckStyle describe it as fanatical. In this case, the lack of a constructor. More decisions for your team.
[/expand]
[expand title=”rules for suppressing the results of these checks” startwrap=”” endwrap=”“]
<!-- Filters--> <module name="SuppressionCommentFilter"> <!-- Use suppressions.xml for suppressions, this is only example. checkFormat will prevent suppression comments from being valid. --> <property name="checkFormat" value="IGNORETHIS"/> <property name="offCommentFormat" value="CSOFF\: .*"/> <property name="onCommentFormat" value="CSON\: .*"/> </module> <module name="SuppressWithNearbyCommentFilter"> <property name="commentFormat" value="-@cs\[(\w{8,}(\|\w{8,})*)\] \w[\(\)\-\.\'\`\,\:\;\w ]{10,}"/> <property name="checkFormat" value="$1"/> <property name="influenceFormat" value="3"/> </module>
This gives an explanation of how to use annotations to suppress results. Do note that this only prevents the results from being reported, the check still completes so if you have a file that causes an exception, this isn’t going to fix it.
The CheckStyle team prefer to have their suppressions in the suppression file it seems. This makes it easier to see where your hidden problems are in one place, but I prefer to have the annotations next against the problem method or class. I make a habit of checking if there are any, and if they are still required when checking in changes. I’m sure many others are less likely to bother.
It looks like I could use this to disable the suppressions if I want to run a report of the suppressed issues.
[/expand]
[expand title=”import rules and ordering” startwrap=”” endwrap=”“]
<!-- Imports --> <module name="AvoidStarImport"/> <module name="AvoidStaticImport"/> <module name="CustomImportOrder"> <property name="customImportOrderRules" value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS"/> <property name="specialImportsRegExp" value="^org\."/> <property name="sortImportsInGroupAlphabetically" value="true"/> <property name="separateLineBetweenGroups" value="true"/> </module> <module name="IllegalImport"/> <module name="ImportControl"> <property name="id" value="ImportControlMain"/> <property name="file" value="${checkstyle.importcontrol.file}"/> <property name="path" value="^.*[\\/]src[\\/]main[\\/].*$"/> </module> <module name="ImportControl"> <property name="id" value="ImportControlTest"/> <property name="file" value="${checkstyle.importcontroltest.file}"/> <property name="path" value="^.*[\\/]src[\\/]test[\\/].*$"/> </module> <module name="ImportOrder"> <property name="groups" value="/^java\./,javax,org"/> <property name="ordered" value="true"/> <property name="separated" value="true"/> <property name="option" value="top"/> <property name="sortStaticImportsAlphabetically" value="true"/> </module> <module name="RedundantImport"/> <module name="UnusedImports"/>
One of the most useful checks I think is to ensure the removal of unneeded imports. Intellij IDEA hides them away, and this reminds me. Sometimes however it can be a real pain getting things in the right order!
[/expand]
[expand title=”Javadoc rules” startwrap=”” endwrap=”“]
<!-- Javadoc Comments --> <module name="AtclauseOrder"/> <module name="JavadocMethod"> <property name="allowUndeclaredRTE" value="true"/> <property name="allowThrowsTagsForSubclasses" value="true"/> <property name="allowMissingPropertyJavadoc" value="true"/> </module> <module name="JavadocParagraph"/> <module name="JavadocStyle"> <property name="scope" value="public"/> </module> <module name="JavadocTagContinuationIndentation"/> <module name="JavadocType"> <property name="authorFormat" value="\S"/> <!-- avoid errors on tag '@noinspection' --> <property name="allowUnknownTags" value="true"/> </module> <module name="JavadocVariable"/> <module name="NonEmptyAtclauseDescription"/> <module name="SingleLineJavadoc"/> <module name="WriteTag"/> <module name="SummaryJavadoc"/>
[/expand]
[expand title=”code smells and complexity” startwrap=”” endwrap=”“]
<!-- Metrics --> <module name="BooleanExpressionComplexity"> <property name="max" value="7"/> </module> <module name="ClassDataAbstractionCoupling"> <!-- Default classes are also listed--> <property name="excludedClasses" value="boolean, byte, char, double, float, int, long, short, void, Boolean, Byte, Character, Double, Float, Integer, Long, Short, Void, Object, Class, String, StringBuffer, StringBuilder, ArrayIndexOutOfBoundsException, Exception, RuntimeException, IllegalArgumentException, IllegalStateException, IndexOutOfBoundsException, NullPointerException, Throwable, SecurityException, UnsupportedOperationException, List, ArrayList, Deque, Queue, LinkedList, Set, HashSet, SortedSet, TreeSet, Map, HashMap, SortedMap, TreeMap, DetailsAST, CheckstyleException, UnsupportedEncodingException, BuildException, ConversionException, FileNotFoundException, TestException"/> </module> <module name="ClassFanOutComplexity"> <property name="max" value="25"/> <!-- Default classes are also listed--> <property name="excludedClasses" value="boolean, byte, char, double, float, int, long, short, void, Boolean, Byte, Character, Double, Float, Integer, Long, Short, Void, Object, Class, String, StringBuffer, StringBuilder, ArrayIndexOutOfBoundsException, Exception, RuntimeException, IllegalArgumentException, IllegalStateException, IndexOutOfBoundsException, NullPointerException, Throwable, SecurityException, UnsupportedOperationException, List, ArrayList, Deque, Queue, LinkedList, Set, HashSet, SortedSet, TreeSet, Map, HashMap, SortedMap, TreeMap, DetailsAST, CheckstyleException, UnsupportedEncodingException, BuildException, ConversionException, FileNotFoundException, TestException, Log, Sets, Multimap, TokenStreamRecognitionException, RecognitionException, TokenStreamException, IOException"/> </module> <module name="CyclomaticComplexity"> <property name="switchBlockAsSingleDecisionPoint" value="true"/> </module> <module name="JavaNCSS"/> <module name="NPathComplexity"/> <!-- Misc --> <module name="ArrayTypeStyle"/> <module name="AvoidEscapedUnicodeCharacters"> <property name="allowIfAllCharactersEscaped" value="true"/> </module> <module name="CommentsIndentation"/> <module name="DescendantToken"/> <module name="FinalParameters"> <!-- we will not use that fanatic validation, extra modifiers pollute a code it is better to use extra validation(Check) that argument is reassigned But this Check will exists as it was created by community demand. --> <property name="severity" value="ignore"/> </module> <module name="Indentation"> <property name="basicOffset" value="4"/> <property name="braceAdjustment" value="0"/> <property name="caseIndent" value="4"/> <property name="throwsIndent" value="8"/> </module> <module name="OuterTypeFilename"/> <module name="TodoComment"> <property name="format" value="(TODO)|(FIXME)" /> </module> <module name="TrailingComment"/> <module name="UncommentedMain"> <property name="excludedClasses" value="\.Main$"/> </module> <module name="UpperEll"/>
Note the controversial ‘final’ issue is first covered here and again CheckStyle refer to it as fanatical. Checking complexity is good, although often has to be suppressed, I think this is one of those things I would prefer to leave SonarQube / lint to handle.
[/expand]
[expand title=”ensure modifiers are required and in the correct order” startwrap=”” endwrap=”“]
<!-- Modifiers --> <module name="ModifierOrder"/> <module name="RedundantModifier"/>
I like these although it can be a pain sometimes working out what is the correct order.[/expand]
[expand title=”naming conventions” startwrap=”” endwrap=”“]
<!-- Naming Conventions --> <module name="AbbreviationAsWordInName"> <property name="ignoreFinal" value="false"/> <property name="allowedAbbreviationLength" value="0"/> <property name="allowedAbbreviations" value="AST"/> </module> <module name="AbstractClassName"/> <module name="ClassTypeParameterName"/> <module name="ConstantName"/> <module name="InterfaceTypeParameterName"/> <module name="LocalFinalVariableName"/> <module name="LocalVariableName"> <property name="format" value="^(id)|([a-z][a-z0-9][a-zA-Z0-9]+)$"/> <property name="allowOneCharVarInForLoop" value="true"/> </module> <module name="MemberName"> <property name="format" value="^(id)|([a-z][a-z0-9][a-zA-Z0-9]+)$"/> </module> <module name="MethodName"/> <module name="MethodTypeParameterName"/> <module name="PackageName"/> <module name="ParameterName"> <property name="format" value="^(id)|([a-z][a-z0-9][a-zA-Z0-9]+)$"/> <property name="ignoreOverridden" value="true"/> </module> <module name="CatchParameterName"> <property name="format" value="^(ex|[a-z][a-z][a-zA-Z]+)$"/> </module> <module name="StaticVariableName"> <property name="format" value="^(id)|([a-z][a-z0-9][a-zA-Z0-9]+)$"/> </module> <module name="TypeName"/>
These are I think really important, although the thought of trying to fix this in a large code base makes me shudder. The risk of merge conflicts or regressions is very high. You have comprehensive automated regression testing in place before attempting this right?
[/expand]
[expand title=”more regex” startwrap=”” endwrap=”“]
<!-- Regexp --> <module name="Regexp"/> <module name="RegexpSinglelineJava"/> <module name="RegexpSinglelineJava"> <property name="format" value="[^\p{ASCII}]"/> <property name="ignoreComments" value="true"/> </module>
[/expand]
[expand title=”size violations” startwrap=”” endwrap=”“]
<!-- Size Violations --> <module name="AnonInnerLength"/> <module name="ExecutableStatementCount"> <property name="max" value="30"/> </module> <module name="LineLength"> <property name="max" value="100"/> <property name="ignorePattern" value="^ *\* *[^ ]+$"/> </module> <module name="MethodCount"> <property name="maxTotal" value="34"/> </module> <module name="MethodLength"/> <module name="OuterTypeNumber"/> <module name="ParameterNumber"/>
Not everyone is happy about 100 characters per line.
As for parameter number, sadly this is not always easy to fix. High potential for requiring suppression.[/expand]
[expand title=”whitespace checks to finish us off” startwrap=”” endwrap=”“]
<!-- Whitespace --> <module name="EmptyForInitializerPad"/> <module name="EmptyForIteratorPad"/> <module name="EmptyLineSeparator"> <property name="allowNoEmptyLineBetweenFields" value="true"/> <property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/> </module> <module name="GenericWhitespace"/> <module name="MethodParamPad"/> <module name="NoLineWrap"/> <module name="NoWhitespaceAfter"> <property name="tokens" value="ARRAY_INIT"/> <property name="tokens" value="AT"/> <property name="tokens" value="BNOT"/> <property name="tokens" value="DEC"/> <property name="tokens" value="DOT"/> <property name="tokens" value="INC"/> <property name="tokens" value="LNOT"/> <property name="tokens" value="UNARY_MINUS"/> <property name="tokens" value="UNARY_PLUS"/> <property name="tokens" value="ARRAY_DECLARATOR"/> <property name="tokens" value="INDEX_OP"/> <property name="tokens" value="METHOD_REF"/> </module> <module name="NoWhitespaceBefore"/> <module name="NoWhitespaceBefore"> <property name="tokens" value="DOT"/> <property name="tokens" value="METHOD_REF"/> <property name="allowLineBreaks" value="true"/> </module> <module name="OperatorWrap"> <property name="tokens" value="QUESTION"/> <property name="tokens" value="COLON"/> <property name="tokens" value="EQUAL"/> <property name="tokens" value="NOT_EQUAL"/> <property name="tokens" value="DIV"/> <property name="tokens" value="PLUS"/> <property name="tokens" value="MINUS"/> <property name="tokens" value="STAR"/> <property name="tokens" value="MOD"/> <property name="tokens" value="SR"/> <property name="tokens" value="BSR"/> <property name="tokens" value="GE"/> <property name="tokens" value="GT"/> <property name="tokens" value="SL"/> <property name="tokens" value="LE"/> <property name="tokens" value="LT"/> <property name="tokens" value="BXOR"/> <property name="tokens" value="BOR"/> <property name="tokens" value="LOR"/> <property name="tokens" value="BAND"/> <property name="tokens" value="LAND"/> <property name="tokens" value="TYPE_EXTENSION_AND"/> <property name="tokens" value="LITERAL_INSTANCEOF"/> <property name="tokens" value="METHOD_REF"/> <property name="option" value="nl"/> </module> <module name="OperatorWrap"> <property name="tokens" value="ASSIGN"/> <property name="tokens" value="DIV_ASSIGN"/> <property name="tokens" value="PLUS_ASSIGN"/> <property name="tokens" value="MINUS_ASSIGN"/> <property name="tokens" value="STAR_ASSIGN"/> <property name="tokens" value="MOD_ASSIGN"/> <property name="tokens" value="SR_ASSIGN"/> <property name="tokens" value="BSR_ASSIGN"/> <property name="tokens" value="SL_ASSIGN"/> <property name="tokens" value="BXOR_ASSIGN"/> <property name="tokens" value="BOR_ASSIGN"/> <property name="tokens" value="BAND_ASSIGN"/> <property name="option" value="eol"/> </module> <module name="ParenPad"/> <module name="SeparatorWrap"> <property name="tokens" value="DOT"/> <property name="tokens" value="AT"/> <property name="tokens" value="METHOD_REF"/> <property name="option" value="nl"/> </module> <module name="SeparatorWrap"> <property name="tokens" value="COMMA"/> <property name="tokens" value="RBRACK"/> <property name="tokens" value="ARRAY_DECLARATOR"/> <property name="tokens" value="ELLIPSIS"/> <property name="tokens" value="SEMI"/> <property name="option" value="EOL"/> </module> <module name="SingleSpaceSeparator"> <property name="validateComments" value="false"/> </module> <module name="TypecastParenPad"/> <module name="WhitespaceAfter"/> <module name="WhitespaceAround"/> </module> </module>
I think these are generally a good idea.[/expand]
Progress made:
- CheckStyle’s own example rules studied.
- How to use ‘Collapse-O-Matic’ and ‘SyntaxHighlighter Evolved’ to format a large xml file in a readable way.
Lessons learnt:
- Working out how to reasonably format a nearly 600 line java file into WordPress and annotate them in an understandable way has been a challenge.
- Useful as this is, it hasn’t really helped me to write my own rule set.
- Experiments to implement a hacked together version didn’t go so well either.
- Sometimes ‘the advice from everyone’ really is right: I should have installed Eclipse and used EclipseCS to write my own rules.
Next time I will be running through how I wrote this post before getting back to using Eclipse-CS plugin after that.
Phew. Writing this was painful!! I think I deserve a trip to see ‘The Last Jedi’ tonight. Now where did I put my lightsaber?
One thought on “CheckStyle – Enforcing a coding style: part 2”
Comments are closed.