1
2
3
4 package net.sourceforge.pmd.lang.java.rule.strings;
5
6 import java.util.Iterator;
7 import java.util.List;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
11 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
13 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
14 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
15 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
16 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
17 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTName;
19 import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
20 import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
21 import net.sourceforge.pmd.lang.java.ast.ASTType;
22 import net.sourceforge.pmd.lang.java.ast.AccessNode;
23 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
24 import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration;
25 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
26 import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
27
28
29
30
31
32
33
34
35
36
37 public class InefficientStringBufferingRule extends AbstractJavaRule {
38
39 @Override
40 public Object visit(ASTAdditiveExpression node, Object data) {
41 ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class);
42 if (bs == null) {
43 return data;
44 }
45
46 int immediateLiterals = 0;
47 int immediateStringLiterals = 0;
48 List<ASTLiteral> nodes = node.findDescendantsOfType(ASTLiteral.class);
49 for (ASTLiteral literal: nodes) {
50 if (literal.getNthParent(3) instanceof ASTAdditiveExpression) {
51 immediateLiterals++;
52 if (literal.isStringLiteral()) {
53 immediateStringLiterals++;
54 }
55 }
56 if (literal.isIntLiteral() || literal.isFloatLiteral() || literal.isDoubleLiteral() || literal.isLongLiteral()) {
57 return data;
58 }
59 }
60
61 if (immediateLiterals > 1) {
62 return data;
63 }
64
65
66 List<ASTName> nameNodes = node.findDescendantsOfType(ASTName.class);
67 for (ASTName name: nameNodes) {
68 if (name.getNameDeclaration() != null && name.getNameDeclaration() instanceof VariableNameDeclaration) {
69 VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
70 AccessNode accessNodeParent = vnd.getAccessNodeParent();
71 if (accessNodeParent.isFinal() && accessNodeParent.isStatic()) {
72 return data;
73 }
74 }
75 }
76
77
78 boolean stringFound = false;
79 for (ASTName name: nameNodes) {
80 if (!isPrimitiveType(name) && isStringType(name)) {
81 stringFound = true;
82 break;
83 }
84 }
85 if (!stringFound && immediateStringLiterals == 0) {
86 return data;
87 }
88
89 if (bs.isAllocation()) {
90 for (Iterator<ASTName> iterator = nameNodes.iterator(); iterator.hasNext();) {
91 ASTName name = iterator.next();
92 if (!name.getImage().endsWith("length")) {
93 break;
94 } else if (!iterator.hasNext()) {
95 return data;
96 }
97 }
98
99 if (isAllocatedStringBuffer(node)) {
100 addViolation(data, node);
101 }
102 } else if (isInStringBufferOperation(node, 6, "append")) {
103 addViolation(data, node);
104 }
105 return data;
106 }
107
108 private boolean isStringType(ASTName name) {
109 ASTType type = getTypeNode(name);
110 if (type != null) {
111 List<ASTClassOrInterfaceType> types = type.findDescendantsOfType(ASTClassOrInterfaceType.class);
112 if (!types.isEmpty()) {
113 ASTClassOrInterfaceType typeDeclaration = types.get(0);
114 if (String.class == typeDeclaration.getType() || "String".equals(typeDeclaration.getImage())) {
115 return true;
116 }
117 }
118 }
119 return false;
120 }
121
122 private boolean isPrimitiveType(ASTName name) {
123 ASTType type = getTypeNode(name);
124 if (type != null && !type.findChildrenOfType(ASTPrimitiveType.class).isEmpty()) {
125 return true;
126 }
127 return false;
128 }
129
130 private ASTType getTypeNode(ASTName name) {
131 if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
132 VariableNameDeclaration vnd = (VariableNameDeclaration) name.getNameDeclaration();
133 if (vnd.getAccessNodeParent() instanceof ASTLocalVariableDeclaration) {
134 ASTLocalVariableDeclaration l = (ASTLocalVariableDeclaration)vnd.getAccessNodeParent();
135 return l.getTypeNode();
136 } else if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
137 ASTFormalParameter p = (ASTFormalParameter) vnd.getAccessNodeParent();
138 return p.getTypeNode();
139 }
140 }
141 return null;
142 }
143
144 protected static boolean isInStringBufferOperation(Node node, int length, String methodName) {
145 if (!(node.getNthParent(length) instanceof ASTStatementExpression)) {
146 return false;
147 }
148 ASTStatementExpression s = node.getFirstParentOfType(ASTStatementExpression.class);
149 if (s == null) {
150 return false;
151 }
152 ASTName n = s.getFirstDescendantOfType(ASTName.class);
153 if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof TypedNameDeclaration)) {
154 return false;
155 }
156
157
158
159
160
161 ASTArgumentList argList = s.getFirstDescendantOfType(ASTArgumentList.class);
162 if (argList == null || argList.jjtGetNumChildren() > 1) {
163 return false;
164 }
165 return TypeHelper.isEither((TypedNameDeclaration)n.getNameDeclaration(), StringBuffer.class, StringBuilder.class);
166 }
167
168 private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
169 ASTAllocationExpression ao = node.getFirstParentOfType(ASTAllocationExpression.class);
170 if (ao == null) {
171 return false;
172 }
173
174 ASTClassOrInterfaceType an = ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
175 return an != null && TypeHelper.isEither(an, StringBuffer.class, StringBuilder.class);
176 }
177 }
178