![JAR search and dependency download from the Maven repository](/logo.png)
org.codenarc.rule.formatting.ClassEndsWithBlankLineRule.groovy Maven / Gradle / Ivy
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codenarc.rule.formatting
import groovy.transform.Memoized
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.ClassNode
import org.codenarc.rule.AbstractAstVisitorRule
import org.codenarc.rule.AbstractAstVisitor
import org.codenarc.rule.Violation
import org.codenarc.util.AstUtil
/**
* Check whether the class ends with a blank line. By default, it enforces that there must be a blank line before
* the closing class brace, except:
*
* - If the class is synthetic (generated)
* - If the class is empty and is written in a single line
* - If the class is a Script class
*
*
* A blank line is defined as any line that does not contain any visible characters.
*
* This rule can be configured with the following properties:
*
* - ignoreSingleLineClasses: a boolean property to forbid single line classes.
* If it is false, then single line classes are considered a violation. Default value is true
* - ignoreInnerClasses: a boolean property to ignore inner classes.
* If it is false, then inner classes can cause violations. Default value is false.
* - blankLineRequired: a boolean property to define if there may be a blank line before the closing
* class brace. If it is false, the last line before the brace must not be blank. Otherwise, it must be blank. Default
* value is true
*
*
* @author David Ausín
*/
class ClassEndsWithBlankLineRule extends AbstractAstVisitorRule {
String name = 'ClassEndsWithBlankLine'
int priority = 3
boolean ignoreSingleLineClasses = true
boolean ignoreInnerClasses = false
boolean blankLineRequired = true
Class astVisitorClass = ClassEndsWithBlankLineAstVisitor
}
class ClassEndsWithBlankLineAstVisitor extends AbstractAstVisitor {
private static final int PENULTIMATE_LINE_OFFSET = 2
private static final char CLOSE_BRACE_CHARACTER = '}'
@Override
protected void visitClassComplete(ClassNode classNode) {
if (classNode.lineNumber == -1) { return }
if (classNode.isScript()) { return }
if (classNode.outerClass && rule.ignoreInnerClasses) { return }
if (isSingleLineClassViolation() && isSingleLineClass(classNode)) { return }
if (rule.blankLineRequired) {
checkIfThereIsBlankLineBeforeClosingBrace(classNode)
} else {
checkIfThereIsNotBlankLineBeforeClosingBrace(classNode)
}
}
private boolean isSingleLineClassViolation() {
rule.ignoreSingleLineClasses || !rule.blankLineRequired
}
private void checkIfThereIsNotBlankLineBeforeClosingBrace(ClassNode classNode) {
String trimmedLineBeforeClosingBrace = getPenultimateLine(classNode).trim()
String trimmedLineOfClosingBrace = getLastLine(classNode).trim()
if (trimmedLineOfClosingBrace == CLOSE_BRACE_CHARACTER && trimmedLineBeforeClosingBrace.isEmpty()) {
addViolation(classNode, 'Class ends with an empty line before the closing brace', classNode.getLastLineNumber())
}
}
private void checkIfThereIsBlankLineBeforeClosingBrace(ClassNode classNode) {
if (isSingleLineClass(classNode)) {
addViolation(classNode, 'Single line classes are not allowed', classNode.getLastLineNumber())
return
}
String trimmedLineBeforeClosingBrace = getPenultimateLine(classNode).trim()
String trimmedLineOfClosingBrace = getLastLine(classNode).trim()
if (trimmedLineOfClosingBrace != CLOSE_BRACE_CHARACTER || !trimmedLineBeforeClosingBrace.isEmpty()) {
addViolation(classNode, 'Class does not end with a blank line before the closing brace', classNode.getLastLineNumber())
}
}
private String getPenultimateLine(ClassNode classNode) {
Integer penultimateLastLineNumber = classNode.lastLineNumber - PENULTIMATE_LINE_OFFSET
return AstUtil.getRawLine(sourceCode, penultimateLastLineNumber)
}
private String getLastLine(ClassNode classNode) {
return AstUtil.getLastLineOfNodeText(classNode, sourceCode)
}
@Memoized
private Boolean isSingleLineClass(ClassNode classNode) {
return AstUtil.getNodeText(classNode, sourceCode) == AstUtil.getLastLineOfNodeText(classNode, sourceCode)
}
private void addViolation(ASTNode node, String message, int lineNumber) {
if (lineNumber >= 0) {
String sourceLine = AstUtil.getLastLineOfNodeText(node, getSourceCode())
Violation violation = new Violation()
violation.rule = rule
violation.lineNumber = lineNumber
violation.sourceLine = sourceLine
violation.message = message
violations.add(violation)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy