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