View Javadoc

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      privateong> 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</strong> 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 }