com.cflint.plugins.core.LiteralChecker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of CFLint Show documentation
Show all versions of CFLint Show documentation
A static code analysis tool for ColdFusion (in the spirit of FindBugs and Lint). With CFLint, you are able to analyze your ColdFusion code base for code violations.
package com.cflint.plugins.core;
import java.util.HashMap;
import java.util.Map;
import com.cflint.BugList;
import com.cflint.plugins.CFLintScannerAdapter;
import com.cflint.plugins.Context;
import cfml.parsing.cfscript.CFExpression;
import cfml.parsing.cfscript.CFLiteral;
import cfml.parsing.cfscript.script.CFCompDeclStatement;
import cfml.parsing.cfscript.script.CFScriptStatement;
import ro.fortsoft.pf4j.Extension;
@Extension
public class LiteralChecker extends CFLintScannerAdapter {
final protected int REPEAT_THRESHOLD = 3;
final protected int WARNING_THRESHOLD = 5;
protected int threshold = REPEAT_THRESHOLD;
protected int warningThreshold = WARNING_THRESHOLD;
protected Map globalLiterals = new HashMap();
protected Map functionListerals = new HashMap();
// May want to consider resetting literal map on new components but this way
// it
// detects duplicated literals across files which is useful
@Override
public void expression(final CFExpression expression, final Context context, final BugList bugs) {
final String repeatThreshold = getParameter("Maximum");
final String maxWarnings = getParameter("MaxWarnings");
final String warningScope = getParameter("WarningScope");
if (repeatThreshold != null) {
threshold = Integer.parseInt(repeatThreshold);
}
if (maxWarnings != null) {
warningThreshold = Integer.parseInt(maxWarnings);
}
if (expression instanceof CFLiteral) {
final CFLiteral literal = (CFLiteral) expression;
final String name = literal.Decompile(0).replace("'", "");
if (isCommon(name)) {
return;
}
final int lineNo = literal.getLine() + context.startLine() - 1;
if (warningScope == null || warningScope.equals("global")) {
literalCount(name, lineNo, globalLiterals, true, context, bugs);
} else if (warningScope.equals("local")) {
literalCount(name, lineNo, functionListerals, false, context, bugs);
}
}
}
@Override
public void expression(final CFScriptStatement expression, final Context context, final BugList bugs) {
if (expression instanceof CFCompDeclStatement) {
functionListerals.clear();
}
}
protected void literalCount(final String name, final int lineNo, final Map literals,
final boolean global, final Context context, final BugList bugs) {
int count = 1;
if (literals.get(name) == null) {
literals.put(name, count);
} else {
count = literals.get(name);
count++;
literals.put(name, count);
}
if (count > threshold && (warningThreshold == -1 || (count - threshold) <= warningThreshold)) {
if (global) {
magicGlobalValue(name, lineNo, context, bugs);
} else {
magicLocalValue(name, lineNo, context, bugs);
}
}
}
protected boolean isCommon(final String name) {
return name.equals("1") || name.equals("0") || name.equals("") || name.equals("true") || name.equals("false");
}
public void magicLocalValue(final String name, final int lineNo, final Context context, final BugList bugs) {
if (!isSpecial(name.toLowerCase()) ){
context.addUniqueMessage("LOCAL_LITERAL_VALUE_USED_TOO_OFTEN", name, this, lineNo);
}
}
/**
* Checks if the literal is a special case that should not fire the literal checker rule
* @param name
* @return
*/
private boolean isSpecial(String name) {
//Empty literals do not flag
if(name == null || name.length()==0){
return true;
}
//Punctuation literals excepted (Look for absense of a word character)
if(!name.matches(".*\\w.*")){
return true;
}
//Exclude datatype for cfquery/cfproc
if(name.startsWith("cf_sql_")){
return true;
}
//Check ignore words list from the configuration
return getParameterAsList("IgnoreWords").contains(name.toLowerCase());
}
public void magicGlobalValue(final String name, final int lineNo, final Context context, final BugList bugs) {
if (!isSpecial(name.toLowerCase()) ){
context.addUniqueMessage("GLOBAL_LITERAL_VALUE_USED_TOO_OFTEN", name, this, lineNo);
}
}
}