/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.design; import java.util.Set; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.util.CollectionUtil; /** * Using a DateFormatter (SimpleDateFormatter) which is static can cause * unexpected results when used in a multi-threaded environment. This rule will * find static (Simple)DateFormatters which are used in an unsynchronized * manner. * * <p>Refer to these Bug Parade issues: * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4093418.html">4093418</a> * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4228335.html">4228335</a> * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4261469.html">4261469</a></p> * * @author Allan Caplan * @see <a href="https://sourceforge.net/p/pmd/feature-requests/226/">feature #226 Check for SimpleDateFormat as singleton?</a> */ public class UnsynchronizedStaticDateFormatterRule extends AbstractJavaRule { private static Set<String> targets = CollectionUtil.asSet( new String[] { "DateFormat", "SimpleDateFormat", "java.text.DateFormat", "java.text.SimpleDateFormat" }); @Override public Object visit(ASTFieldDeclaration node, Object data) { if (!node.isStatic()) { return data; } ASTClassOrInterfaceType cit = node.getFirstDescendantOfType(ASTClassOrInterfaceType.class); if (cit == null || !targets.contains(cit.getImage())) { return data; } ASTVariableDeclaratorId var = node.getFirstDescendantOfType(ASTVariableDeclaratorId.class); for (NameOccurrence occ : var.getUsages()) { Node n = occ.getLocation(); if (n.getFirstParentOfType(ASTSynchronizedStatement.class) != null) { continue; } // ignore usages, that don't call a method. if (!n.getImage().contains(".")) { continue; } ASTMethodDeclaration method = n.getFirstParentOfType(ASTMethodDeclaration.class); if (method != null && !method.isSynchronized()) { addViolation(data, n); } } return data; } }