1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules;
5
6 import net.sourceforge.pmd.AbstractRule;
7 import net.sourceforge.pmd.RuleContext;
8 import net.sourceforge.pmd.ast.ASTAllocationExpression;
9 import net.sourceforge.pmd.ast.ASTArguments;
10 import net.sourceforge.pmd.ast.ASTArrayDimsAndInits;
11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
12 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
13 import net.sourceforge.pmd.ast.ASTCompilationUnit;
14 import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
15 import net.sourceforge.pmd.ast.ASTName;
16 import net.sourceforge.pmd.ast.ASTPackageDeclaration;
17
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.ListIterator;
22
23 /***
24 * 1. Note all private constructors.
25 * 2. Note all instantiations from outside of the class by way of the private
26 * constructor.
27 * 3. Flag instantiations.
28 * <p/>
29 * <p/>
30 * Parameter types can not be matched because they can come as exposed members
31 * of classes. In this case we have no way to know what the type is. We can
32 * make a best effort though which can filter some?
33 *
34 * @author CL Gilbert (dnoyeb@users.sourceforge.net)
35 * @author David Konecny (david.konecny@)
36 */
37 public class AccessorClassGeneration extends AbstractRule {
38 private int classID = -1;
39 private List classDataList;
40 private String packageName/package-summary.html">ong> String packageName;
41
42 private ClassData getCurrentClassData() {
43 return (ClassData) classDataList.get(classID);
44 }
45
46 private void setClassID(int ID) {
47 classID = ID;
48 }
49
50 private int getClassID() {
51 return classID;
52 }
53
54 private String getPackageName() {
55 return</strong> packageName;
56 }
57
58
59
60
61
62
63
64
65
66 private static String stripString(String remove, String value) {
67 String returnValue;
68 int index = value.indexOf(remove);
69 if (index != -1) {
70 returnValue = value.substring(0, index) + value.substring(index + remove.length());
71 } else {
72 returnValue = value;
73 }
74 return returnValue;
75 }
76
77 /***
78 *
79 */
80 private static class ClassData {
81 /***
82 * The name of this class
83 */
84 private String m_ClassName;
85 /***
86 * List of private constructors within this class
87 */
88 private List m_PrivateConstructors;
89 /***
90 * List of instantiations of objects within this class
91 */
92 private List m_Instantiations;
93 /***
94 * List of outer class names that exist above this class
95 */
96 private List m_ClassQualifyingNames;
97
98 public ClassData(String className) {
99 m_ClassName = className;
100 m_PrivateConstructors = new ArrayList();
101 m_Instantiations = new ArrayList();
102 m_ClassQualifyingNames = new ArrayList();
103 }
104
105 public void addInstantiation(AllocData ad) {
106 m_Instantiations.add(ad);
107 }
108
109 public Iterator getInstantiationIterator() {
110 return m_Instantiations.iterator();
111 }
112
113 public void addConstructor(ASTConstructorDeclaration cd) {
114 m_PrivateConstructors.add(cd);
115 }
116
117 public Iterator getPrivateConstructorIterator() {
118 return m_PrivateConstructors.iterator();
119 }
120
121 public String getClassName() {
122 return m_ClassName;
123 }
124
125 public void addClassQualifyingName(String name) {
126 m_ClassQualifyingNames.add(name);
127 }
128
129 public Iterator getClassQualifyingNames() {
130 return m_ClassQualifyingNames.iterator();
131 }
132
133 public List getClassQualifyingNamesList() {
134 return m_ClassQualifyingNames;
135 }
136 }
137
138 private static class AllocData {
139 private String m_Name;
140 private int m_ArgumentCount;
141 private ASTAllocationExpression m_ASTAllocationExpression;
142 private boolean isArray = false;
143
144 public AllocData(ASTAllocationExpression node, String aPackageName, List classQualifyingNames) {
145 if (node.jjtGetChild(1) instanceof ASTArguments) {
146 ASTArguments aa = (ASTArguments) node.jjtGetChild(1);
147 m_ArgumentCount = aa.getArgumentCount();
148
149
150 if (!(node.jjtGetChild(0) instanceof ASTClassOrInterfaceType)) {
151 throw new RuntimeException("BUG: Expected a ASTClassOrInterfaceType, got a " + node.jjtGetChild(0).getClass());
152 }
153 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType)node.jjtGetChild(0);
154 m_Name = stripString(aPackageName + ".", an.getImage());
155
156
157
158 STRIPPING: {
159 String findName = "";
160 for (ListIterator li = classQualifyingNames.listIterator(classQualifyingNames.size()); li.hasPrevious();) {
161 String aName = (String) li.previous();
162 findName = aName + "." + findName;
163 if (m_Name.startsWith(findName)) {
164
165 m_Name = m_Name.substring(findName.length());
166 break;
167 }
168 }
169 }
170 } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
171
172
173 isArray = true;
174 }
175 m_ASTAllocationExpression = node;
176 }
177
178 public String getName() {
179 return m_Name;
180 }
181
182 public int getArgumentCount() {
183 return m_ArgumentCount;
184 }
185
186 public void show() {
187 System.out.println("AllocData: " + getName() + " arguments= " + getArgumentCount());
188 }
189
190 public ASTAllocationExpression getASTAllocationExpression() {
191 return m_ASTAllocationExpression;
192 }
193
194 public boolean isArray() {
195 return isArray;
196 }
197 }
198
199 /***
200 * Work on each file independently.
201 * Assume a new AccessorClassGenerationRule object is created for each run?
202 */
203 public Object visit(ASTCompilationUnit node, Object data) {
204 classDataList = new ArrayList();
205 return super.visit(node, data);
206 }
207
208 private void processRule(RuleContext ctx) {
209
210
211 for (Iterator outerIterator = classDataList.iterator(); outerIterator.hasNext();) {
212
213 ClassData outerDataSet = (ClassData) outerIterator.next();
214 for (Iterator constructors = outerDataSet.getPrivateConstructorIterator(); constructors.hasNext();) {
215 ASTConstructorDeclaration cd = (ASTConstructorDeclaration) constructors.next();
216
217 for (Iterator innerIterator = classDataList.iterator(); innerIterator.hasNext();) {
218 ClassData innerDataSet = (ClassData) innerIterator.next();
219 if (outerDataSet == innerDataSet) {
220 continue;
221 }
222 for (Iterator allocations = innerDataSet.getInstantiationIterator(); allocations.hasNext();) {
223 AllocData ad = (AllocData) allocations.next();
224
225
226
227 if (outerDataSet.getClassName().equals(ad.getName()) && (cd.getParameterCount() == ad.getArgumentCount())) {
228 ctx.getReport().addRuleViolation(createRuleViolation(ctx, ad.getASTAllocationExpression()));
229 }
230 }
231 }
232 }
233 }
234 }
235
236 /***
237 * Store package name to strip off in case necessary
238 */
239 public Object visit(ASTPackageDeclaration node, Object data) {
240 packageName = ((ASTName) node.jjtGetChild(0)).getImage();
241 return super.visit(node, data);
242 }
243
244 /***
245 * Outer interface visitation
246 */
247 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
248 if (node.isInterface()) {
249 if (node.isNested()) {
250 String interfaceName = node.getImage();
251 int formerID = getClassID();
252 setClassID(classDataList.size());
253 ClassData newClassData = new ClassData(interfaceName);
254
255 ClassData formerClassData = (ClassData) classDataList.get(formerID);
256 newClassData.addClassQualifyingName(formerClassData.getClassName());
257 classDataList.add(getClassID(), newClassData);
258 Object o = super.visit(node, data);
259 setClassID(formerID);
260 return o;
261 } else {
262 String interfaceName = node.getImage();
263 classDataList.clear();
264 setClassID(0);
265 classDataList.add(getClassID(), new ClassData(interfaceName));
266 Object o = super.visit(node, data);
267 if (o != null) {
268 processRule((RuleContext) o);
269 } else {
270 processRule((RuleContext) data);
271 }
272 setClassID(-1);
273 return o;
274 }
275 } else if (node.isNested()) {
276 String className = node.getImage();
277 int formerID = getClassID();
278 setClassID(classDataList.size());
279 ClassData newClassData = new ClassData(className);
280
281 ClassData formerClassData = (ClassData) classDataList.get(formerID);
282 newClassData.addClassQualifyingName(formerClassData.getClassName());
283 classDataList.add(getClassID(), newClassData);
284 Object o = super.visit(node, data);
285 setClassID(formerID);
286 return o;
287 }
288
289 String className = node.getImage();
290 classDataList.clear();
291 setClassID(0);
292 classDataList.add(getClassID(), new ClassData(className));
293 Object o = super.visit(node, data);
294 if (o != null) {
295 processRule((RuleContext) o);
296 } else {
297 processRule((RuleContext) data);
298 }
299 setClassID(-1);
300 return o;
301 }
302
303 /***
304 * Store all target constructors
305 */
306 public Object visit(ASTConstructorDeclaration node, Object data) {
307 if (node.isPrivate()) {
308 getCurrentClassData().addConstructor(node);
309 }
310 return super.visit(node, data);
311 }
312
313 public Object visit(ASTAllocationExpression node, Object data) {
314
315
316
317
318 if (classID == -1) {
319 return data;
320 }
321 AllocData ad = new AllocData(node, getPackageName(), getCurrentClassData().getClassQualifyingNamesList());
322 if (ad.isArray() == false) {
323 getCurrentClassData().addInstantiation(ad);
324
325 }
326 return super.visit(node, data);
327 }
328 }