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