org.codenarc.rule.logging.LoggerForDifferentClassRule.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of CodeNarc Show documentation
Show all versions of CodeNarc Show documentation
The CodeNarc project provides a static analysis tool for Groovy code.
/*
* Copyright 2011 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.logging
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.InnerClassNode
import org.codehaus.groovy.ast.expr.Expression
import org.codenarc.rule.AbstractAstVisitorRule
import org.codenarc.rule.AbstractFieldVisitor
/**
* Rule that checks for instantiating a logger for a class other than the current class. Supports logger
* instantiations for Log4J, Logback, SLF4J, Apache Commons Logging, Java Logging API (java.util.logging).
*
* Limitations:
*
* - Only checks Loggers instantiated within a class field or property (not variables or expressions within a method)
* - For Log4J: Does not catch Logger instantiations if you specify the full package name for the Logger class:
* e.g. org.apache.log4.Logger.getLogger(..)
* - For SLF4J and Logback: Does not catch Log instantiations if you specify the full package name for the LoggerFactory
* class: e.g. org.slf4j.LoggerFactory.getLogger(..)
* - For Commons Logging: Does not catch Log instantiations if you specify the full package name for the LogFactory
* class: e.g. org.apache.commons.logging.LogFactory.getLog(..)
* - For Java Logging API: Does not catch Logger instantiations if you specify the full package name for the Logger
* class: e.g. java.util.logging.Logger.getLogger(..)
*
*
* @author Chris Mair
*/
class LoggerForDifferentClassRule extends AbstractAstVisitorRule {
String name = 'LoggerForDifferentClass'
int priority = 2
boolean allowDerivedClasses = false
Class astVisitorClass = LoggerForDifferentClassAstVisitor
}
class LoggerForDifferentClassAstVisitor extends AbstractFieldVisitor {
@Override
void visitField(FieldNode fieldNode) {
def expression = fieldNode.getInitialExpression()
if (LogUtil.isMatchingLoggerDefinition(expression)) {
def firstArg = expression.arguments?.expressions?.get(0)
def argText = firstArg.text
final CLASSNAME_WITHOUT_PACKAGE
if (fieldNode.owner instanceof InnerClassNode) {
CLASSNAME_WITHOUT_PACKAGE = fieldNode.owner.nameWithoutPackage - "$fieldNode.owner.outerClass.nameWithoutPackage\$"
} else {
CLASSNAME_WITHOUT_PACKAGE = fieldNode.declaringClass.nameWithoutPackage
}
if (!rule.allowDerivedClasses && isCapitalized(argText) && !isEqualToCurrentClassOrClassName(argText, CLASSNAME_WITHOUT_PACKAGE)) {
addViolation(fieldNode, "Logger is defined in $CLASSNAME_WITHOUT_PACKAGE but initialized with $argText")
} else if (rule.allowDerivedClasses && !isLoggerForDerivedClass(fieldNode)) {
addViolation(fieldNode, "Logger is defined in $CLASSNAME_WITHOUT_PACKAGE but initialized with $argText")
}
}
}
private static boolean isEqualToCurrentClassOrClassName(String argText, classNameWithoutPackage) {
return isEqualToCurrentClass(argText, classNameWithoutPackage) || isEqualToCurrentClassName(argText, classNameWithoutPackage)
}
private static boolean isEqualToCurrentClass(String argText, classNameWithoutPackage) {
return (argText == classNameWithoutPackage) || (argText == classNameWithoutPackage + '.class')
}
private static boolean isLoggerForDerivedClass(FieldNode fieldNode) {
Expression methodArgument = fieldNode.getInitialValueExpression().arguments.expressions.first()
methodArgument.text in ['this.class', 'this.getClass()', 'getClass()']
}
private static boolean isEqualToCurrentClassName(String argText, classNameWithoutPackage) {
def classNameOptions = [
classNameWithoutPackage + '.getClass().getName()',
classNameWithoutPackage + '.getClass().name',
classNameWithoutPackage + '.class.getName()',
classNameWithoutPackage + '.class.name',
classNameWithoutPackage + '.name',
]
return argText in classNameOptions
}
private static boolean isCapitalized(String text) {
def firstCharacter = text[0]
return firstCharacter.toUpperCase() == firstCharacter
}
}