/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.design; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.rule.properties.BooleanProperty; public class NonThreadSafeSingletonRule extends AbstractJavaRule { private Map<String, ASTFieldDeclaration> fieldDecls = new HashMap<>(); private boolean checkNonStaticMethods = true; private boolean checkNonStaticFields = true; private static final BooleanProperty CHECK_NON_STATIC_METHODS_DESCRIPTOR = new BooleanProperty( "checkNonStaticMethods", "Check for non-static methods. Do not set this to false and checkNonStaticFields to true.", true, 1.0f); private static final BooleanProperty CHECK_NON_STATIC_FIELDS_DESCRIPTOR = new BooleanProperty( "checkNonStaticFields", "Check for non-static fields. Do not set this to true and checkNonStaticMethods to false.", false, 2.0f); public NonThreadSafeSingletonRule() { definePropertyDescriptor(CHECK_NON_STATIC_METHODS_DESCRIPTOR); definePropertyDescriptor(CHECK_NON_STATIC_FIELDS_DESCRIPTOR); } @Override public Object visit(ASTCompilationUnit node, Object data) { fieldDecls.clear(); checkNonStaticMethods = getProperty(CHECK_NON_STATIC_METHODS_DESCRIPTOR); checkNonStaticFields = getProperty(CHECK_NON_STATIC_FIELDS_DESCRIPTOR); return super.visit(node, data); } @Override public Object visit(ASTFieldDeclaration node, Object data) { if (checkNonStaticFields || node.isStatic()) { fieldDecls.put(node.getVariableName(), node); } return super.visit(node, data); } @Override public Object visit(ASTMethodDeclaration node, Object data) { if (checkNonStaticMethods && !node.isStatic() || node.isSynchronized()) { return super.visit(node, data); } List<ASTIfStatement> ifStatements = node.findDescendantsOfType(ASTIfStatement.class); for (ASTIfStatement ifStatement : ifStatements) { if (ifStatement.getFirstParentOfType(ASTSynchronizedStatement.class) == null) { if (!ifStatement.hasDescendantOfType(ASTNullLiteral.class)) { continue; } ASTName n = ifStatement.getFirstDescendantOfType(ASTName.class); if (n == null || !fieldDecls.containsKey(n.getImage())) { continue; } List<ASTAssignmentOperator> assigmnents = ifStatement .findDescendantsOfType(ASTAssignmentOperator.class); boolean violation = false; for (int ix = 0; ix < assigmnents.size(); ix++) { ASTAssignmentOperator oper = assigmnents.get(ix); if (!(oper.jjtGetParent() instanceof ASTStatementExpression)) { continue; } ASTStatementExpression expr = (ASTStatementExpression) oper.jjtGetParent(); if (expr.jjtGetChild(0) instanceof ASTPrimaryExpression && ((ASTPrimaryExpression) expr.jjtGetChild(0)).jjtGetNumChildren() == 1 && ((ASTPrimaryExpression) expr.jjtGetChild(0)) .jjtGetChild(0) instanceof ASTPrimaryPrefix) { ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ((ASTPrimaryExpression) expr.jjtGetChild(0)) .jjtGetChild(0); String name = null; if (pp.usesThisModifier()) { ASTPrimarySuffix priSuf = expr.getFirstDescendantOfType(ASTPrimarySuffix.class); name = priSuf.getImage(); } else { ASTName astName = (ASTName) pp.jjtGetChild(0); name = astName.getImage(); } if (fieldDecls.containsKey(name)) { violation = true; } } } if (violation) { addViolation(data, ifStatement); } } } return super.visit(node, data); } }