All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codenarc.rule.generic.StatelessClassRule.groovy Maven / Gradle / Ivy

There is a newer version: 3.5.0-groovy-4.0
Show newest version
/*
 * Copyright 2009 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.generic

import static org.codenarc.util.AstUtil.hasAnnotation

import org.codehaus.groovy.ast.FieldNode
import org.codenarc.rule.AbstractAstVisitorRule
import org.codenarc.rule.AbstractFieldVisitor
import org.codenarc.util.WildcardPattern

/**
 * Rule that checks for non-final fields on a class. The intent of this rule is
 * to check a configured set of classes that should remain "stateless" and reentrant. One
 * example might be Grails service classes, which are, by default, a singleton, and so they
 * should be reentrant. DAO classes are also often kept stateless.
 * 

* This rule ignores final fields (either instance or static). Fields that are * static and non-final, however, do cause a violation. *

* This rule also ignores all classes annotated with the @Immutable transformation. * See http://groovy.codehaus.org/Immutable+transformation. *

* This rule also ignores fields annotated with the @Inject annotation. *

* You can configure this rule to ignore certain fields either by name or by type. This can be * useful to ignore fields that hold references to (static) dependencies (such as DAOs or * Service objects) or static configuration. *

* The ignoreFieldNames property specifies one or more (comma-separated) field names * that should be ignored (i.e., that should not cause a rule violation). The name(s) may optionally * include wildcard characters ('*' or '?'). You can add to the field names to be ignored by setting * the (write-only) addIgnoreFieldNames property. This is a "special" property -- each * call to setAddIgnoreFieldNames() adds to the existing ignoreFieldNames * property value. *

* The ignoreFieldTypes property specifies one or more (comma-separated) field type names * that should be ignored (i.e., that should not cause a rule violation). The type name(s) may optionally * include wildcard characters ('*' or '?'). *

* Note: The ignoreFieldTypes property matches the field type name as indicated * in the field declaration, only including a full package specification IF it is included in * the source code. For example, the field declaration BigDecimal value matches * an ignoreFieldTypes value of BigDecimal, but not * java.lang.BigDecimal. *

* There is one exception for the ignoreFieldTypes property: if the field is declared * with a modifier/type of def, then the type resolves to java.lang.Object. * * @author Chris Mair * @author Hamlet D'Arcy */ class StatelessClassRule extends AbstractAstVisitorRule { String name = 'StatelessClass' int priority = 2 String ignoreFieldNames String ignoreFieldTypes Class astVisitorClass = StatelessClassAstVisitor boolean isReady() { applyToClassNames || applyToFileNames || applyToFilesMatching } /** * Add more field names to the existing ignoreFieldNames property value. * @param moreFieldNames - specifies one or more (comma-separated) field names that should be * ignored (i.e., that should not cause a rule violation). The name(s) may optionally * include wildcard characters ('*' or '?'). Any names specified here are joined to * the existing ignoreFieldNames property value. */ void setAddToIgnoreFieldNames(String moreFieldNames) { this.ignoreFieldNames = this.ignoreFieldNames ? this.ignoreFieldNames + ',' + moreFieldNames : moreFieldNames } /** * Subclasses can optionally override to provide more specific filtering of fields */ protected boolean shouldIgnoreField(FieldNode fieldNode) { return hasAnnotation(fieldNode.owner, 'Immutable') || hasAnnotation(fieldNode, 'Inject') || fieldNode.isFinal() || matchesIgnoreFieldNames(fieldNode) || matchesIgnoreFieldTypes(fieldNode) } private boolean matchesIgnoreFieldNames(FieldNode fieldNode) { ignoreFieldNames && new WildcardPattern(ignoreFieldNames).matches(fieldNode.name) } private boolean matchesIgnoreFieldTypes(FieldNode fieldNode) { ignoreFieldTypes && new WildcardPattern(ignoreFieldTypes).matches(fieldNode.type.name) } } class StatelessClassAstVisitor extends AbstractFieldVisitor { void visitField(FieldNode fieldNode) { boolean ignore = rule.shouldIgnoreField(fieldNode) if (!ignore) { addViolation(fieldNode, "The class is marked as stateless but contains the non-final field '$fieldNode.name'") } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy