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.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      privateong> 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      //remove = Fire.
59      //value = someFire.Fighter
60      //        0123456789012345
61      //index = 4
62      //remove.size() = 5
63      //value.substring(0,4) = some
64      //value.substring(4 + remove.size()) = Fighter
65      //return "someFighter"
66      private static String stripString(String remove, String value) {
67          String returnValue;
68          int index = value.indexOf(remove);
69          if (index != -1) {	//if the package name can start anywhere but 0 plese inform the author because this will break
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                 //Get name and strip off all superfluous data
149                 //strip off package name if it is current package
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                 //strip off outer class names
157                 //try OuterClass, then try OuterClass.InnerClass, then try OuterClass.InnerClass.InnerClass2, etc...
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                             //strip off name and exit
165                             m_Name = m_Name.substring(findName.length());
166                             break;
167                         }
168                     }
169                 }
170             } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
171                 //this is incomplete because I dont need it.
172                 //				child 0 could be primitive or object (ASTName or ASTPrimitiveType)
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         //check constructors of outerIterator
210         //against allocations of innerIterator
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                         //if the constructor matches the instantiation
225                         //flag the instantiation as a generator of an extra class
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                 //store the names of any outer classes of this class in the classQualifyingName List
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             //store the names of any outer classes of this class in the classQualifyingName List
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         // outer classes
289         String className = node.getImage();
290         classDataList.clear();
291         setClassID(0);//first class
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         // TODO
315         // this is a hack to bail out here
316         // but I'm not sure why this is happening
317         // TODO
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             //ad.show();
325         }
326         return super.visit(node, data);
327     }
328 }