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