/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.design; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.ClassScope; /** * 1. Note all private constructors. 2. Note all instantiations from outside of * the class by way of the private constructor. 3. Flag instantiations. * * <p> * Parameter types can not be matched because they can come as exposed members * of classes. In this case we have no way to know what the type is. We can make * a best effort though which can filter some? * </p> * * @author CL Gilbert (dnoyeb@users.sourceforge.net) * @author David Konecny (david.konecny@) * @author Romain PELISSE, belaran@gmail.com, patch bug#1807370 * @author Juan Martin Sotuyo Dodero (juansotuyo@gmail.com), complete rewrite */ public class AccessorClassGenerationRule extends AbstractJavaRule { private Map<String, List<ASTConstructorDeclaration>> privateConstructors = new HashMap<>(); public AccessorClassGenerationRule() { super(); /* * Order is important. Visit constructors first to find the private * ones, then visit allocations to find violations */ addRuleChainVisit(ASTConstructorDeclaration.class); addRuleChainVisit(ASTAllocationExpression.class); } @Override public void end(final RuleContext ctx) { super.end(ctx); // Clean up all references to the AST privateConstructors.clear(); } @Override public Object visit(final ASTConstructorDeclaration node, final Object data) { if (node.isPrivate()) { final String className = node.jjtGetParent().jjtGetParent().jjtGetParent().getImage(); if (!privateConstructors.containsKey(className)) { privateConstructors.put(className, new ArrayList<ASTConstructorDeclaration>()); } privateConstructors.get(className).add(node); } return data; } @Override public Object visit(final ASTAllocationExpression node, final Object data) { if (node.jjtGetChild(0) instanceof ASTClassOrInterfaceType) { // Ignore primitives final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.jjtGetChild(0); final List<ASTConstructorDeclaration> constructors = privateConstructors.get(type.getImage()); if (constructors != null) { final ASTArguments callArguments = node.getFirstChildOfType(ASTArguments.class); // Is this really a constructor call and not an array? if (callArguments != null) { final ClassScope enclosingScope = node.getScope().getEnclosingScope(ClassScope.class); for (final ASTConstructorDeclaration cd : constructors) { // Are we within the same class scope? if (cd.getScope().getEnclosingScope(ClassScope.class) == enclosingScope) { break; } if (cd.getParameterCount() == callArguments.getArgumentCount()) { // TODO : Check types addViolation(data, node); break; } } } } } return data; } }