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