1
2
3
4 package net.sourceforge.pmd.lang.java.rule.design;
5
6 import java.util.ArrayList;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.ListIterator;
10
11 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTArguments;
13 import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits;
14 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
15 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
16 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17 import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
19 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
20 import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 public class AccessorClassGenerationRule extends AbstractJavaRule {
36
37 private List<ClassData> classDataList = new ArrayList<ClassData>();
38 private int classID = -1;
39 private String packageName;
40
41 public Object visit(ASTEnumDeclaration node, Object data) {
42 return data;
43 }
44
45 public Object visit(ASTCompilationUnit node, Object data) {
46 classDataList.clear();
47 packageName = node.getScope().getEnclosingScope(SourceFileScope.class).getPackageName();
48 return super.visit(node, data);
49 }
50
51 private static class ClassData {
52 private String className;
53 private List<ASTConstructorDeclaration> privateConstructors;
54 private List<AllocData> instantiations;
55
56
57
58 private List<String> classQualifyingNames;
59
60 public ClassData(String className) {
61 this.className = className;
62 this.privateConstructors = new ArrayList<ASTConstructorDeclaration>();
63 this.instantiations = new ArrayList<AllocData>();
64 this.classQualifyingNames = new ArrayList<String>();
65 }
66
67 public void addInstantiation(AllocData ad) {
68 instantiations.add(ad);
69 }
70
71 public Iterator<AllocData> getInstantiationIterator() {
72 return instantiations.iterator();
73 }
74
75 public void addConstructor(ASTConstructorDeclaration cd) {
76 privateConstructors.add(cd);
77 }
78
79 public Iterator<ASTConstructorDeclaration> getPrivateConstructorIterator() {
80 return privateConstructors.iterator();
81 }
82
83 public String getClassName() {
84 return className;
85 }
86
87 public void addClassQualifyingName(String name) {
88 classQualifyingNames.add(name);
89 }
90
91 public List<String> getClassQualifyingNamesList() {
92 return classQualifyingNames;
93 }
94 }
95
96 private static class AllocData {
97 private String name;
98 private int argumentCount;
99 private ASTAllocationExpression allocationExpression;
100 private boolean isArray;
101
102 public AllocData(ASTAllocationExpression node, String aPackageName, List<String> classQualifyingNames) {
103 if (node.jjtGetChild(1) instanceof ASTArguments) {
104 ASTArguments aa = (ASTArguments) node.jjtGetChild(1);
105 argumentCount = aa.getArgumentCount();
106
107
108 if (!(node.jjtGetChild(0) instanceof ASTClassOrInterfaceType)) {
109 throw new RuntimeException("BUG: Expected a ASTClassOrInterfaceType, got a "
110 + node.jjtGetChild(0).getClass());
111 }
112 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) node.jjtGetChild(0);
113 name = stripString(aPackageName + '.', an.getImage());
114
115
116
117
118 String findName = "";
119 for (ListIterator<String> li = classQualifyingNames.listIterator(classQualifyingNames.size()); li
120 .hasPrevious();) {
121 String aName = li.previous();
122 findName = aName + '.' + findName;
123 if (name.startsWith(findName)) {
124
125 name = name.substring(findName.length());
126 break;
127 }
128 }
129 } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
130
131
132
133 isArray = true;
134 }
135 allocationExpression = node;
136 }
137
138 public String getName() {
139 return name;
140 }
141
142 public int getArgumentCount() {
143 return argumentCount;
144 }
145
146 public ASTAllocationExpression getASTAllocationExpression() {
147 return allocationExpression;
148 }
149
150 public boolean isArray() {
151 return isArray;
152 }
153 }
154
155
156
157
158 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
159 if (node.isInterface()) {
160 if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
161
162 String interfaceName = node.getImage();
163 int formerID = getClassID();
164 setClassID(classDataList.size());
165 ClassData newClassData = new ClassData(interfaceName);
166
167
168 ClassData formerClassData = classDataList.get(formerID);
169 newClassData.addClassQualifyingName(formerClassData.getClassName());
170 classDataList.add(getClassID(), newClassData);
171 Object o = super.visit(node, data);
172 setClassID(formerID);
173 return o;
174 } else {
175 String interfaceName = node.getImage();
176 classDataList.clear();
177 setClassID(0);
178 classDataList.add(getClassID(), new ClassData(interfaceName));
179 Object o = super.visit(node, data);
180 if (o != null) {
181 processRule(o);
182 } else {
183 processRule(data);
184 }
185 setClassID(-1);
186 return o;
187 }
188 } else if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
189
190 int formerID = getClassID();
191 setClassID(classDataList.size());
192
193
194
195
196 if (formerID == -1 || formerID >= classDataList.size()) {
197 return null;
198 }
199
200
201 ClassData formerClassData = classDataList.get(formerID);
202 String className = node.getImage();
203 ClassData newClassData = new ClassData(className);
204 newClassData.addClassQualifyingName(formerClassData.getClassName());
205 classDataList.add(getClassID(), newClassData);
206 Object o = super.visit(node, data);
207 setClassID(formerID);
208 return o;
209 }
210
211 if (!node.isStatic()) {
212 String className = node.getImage();
213 classDataList.clear();
214 setClassID(0);
215 classDataList.add(getClassID(), new ClassData(className));
216 }
217 Object o = super.visit(node, data);
218 if (o != null && !node.isStatic()) {
219 processRule(o);
220 } else {
221 processRule(data);
222 }
223 setClassID(-1);
224 return o;
225 }
226
227
228
229
230 public Object visit(ASTConstructorDeclaration node, Object data) {
231 if (node.isPrivate()) {
232 getCurrentClassData().addConstructor(node);
233 }
234 return super.visit(node, data);
235 }
236
237 public Object visit(ASTAllocationExpression node, Object data) {
238
239
240
241
242 if (classID == -1 || getCurrentClassData() == null) {
243 return data;
244 }
245 AllocData ad = new AllocData(node, packageName, getCurrentClassData().getClassQualifyingNamesList());
246 if (!ad.isArray()) {
247 getCurrentClassData().addInstantiation(ad);
248 }
249 return super.visit(node, data);
250 }
251
252 private void processRule(Object ctx) {
253
254
255 for (ClassData outerDataSet : classDataList) {
256 for (Iterator<ASTConstructorDeclaration> constructors = outerDataSet.getPrivateConstructorIterator(); constructors
257 .hasNext();) {
258 ASTConstructorDeclaration cd = constructors.next();
259
260 for (ClassData innerDataSet : classDataList) {
261 if (outerDataSet.equals(innerDataSet)) {
262 continue;
263 }
264 for (Iterator<AllocData> allocations = innerDataSet.getInstantiationIterator(); allocations
265 .hasNext();) {
266 AllocData ad = allocations.next();
267
268
269
270 if (outerDataSet.getClassName().equals(ad.getName())
271 && cd.getParameterCount() == ad.getArgumentCount()) {
272 addViolation(ctx, ad.getASTAllocationExpression());
273 }
274 }
275 }
276 }
277 }
278 }
279
280 private ClassData getCurrentClassData() {
281
282
283
284
285 if (classID >= classDataList.size()) {
286 return null;
287 }
288 return classDataList.get(classID);
289 }
290
291 private void setClassID(int id) {
292 classID = id;
293 }
294
295 private int getClassID() {
296 return classID;
297 }
298
299
300
301
302
303
304
305
306
307
308
309 private static String stripString(String remove, String value) {
310 String returnValue;
311 int index = value.indexOf(remove);
312 if (index != -1) {
313
314 returnValue = value.substring(0, index) + value.substring(index + remove.length());
315 } else {
316 returnValue = value;
317 }
318 return returnValue;
319 }
320
321 }