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
71 public Iterator getInstantiationIterator() {
72 return m_Instantiations.iterator();
73 }
74
75 public void addConstructor(ASTConstructorDeclaration cd) {
76 m_PrivateConstructors.add(cd);
77 }
78
79 public Iterator getPrivateConstructorIterator() {
80 return m_PrivateConstructors.iterator();
81 }
82
83 public String getClassName() {
84 return m_ClassName;
85 }
86
87 public void addClassQualifyingName(String name) {
88 m_ClassQualifyingNames.add(name);
89 }
90
91 public List getClassQualifyingNamesList() {
92 return m_ClassQualifyingNames;
93 }
94 }
95
96 private static class AllocData {
97 private String m_Name;
98 private int m_ArgumentCount;
99 private ASTAllocationExpression m_ASTAllocationExpression;
100 private boolean isArray;
101
102 public AllocData(ASTAllocationExpression node, String aPackageName, List classQualifyingNames) {
103 if (node.jjtGetChild(1) instanceof ASTArguments) {
104 ASTArguments aa = (ASTArguments) node.jjtGetChild(1);
105 m_ArgumentCount = aa.getArgumentCount();
106
107
108 if (!(node.jjtGetChild(0) instanceof ASTClassOrInterfaceType)) {
109 throw new RuntimeException("BUG: Expected a ASTClassOrInterfaceType, got a " + node.jjtGetChild(0).getClass());
110 }
111 ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) node.jjtGetChild(0);
112 m_Name = stripString(aPackageName + ".", an.getImage());
113
114
115
116 String findName = "";
117 for (ListIterator li = classQualifyingNames.listIterator(classQualifyingNames.size()); li.hasPrevious();) {
118 String aName = (String) li.previous();
119 findName = aName + "." + findName;
120 if (m_Name.startsWith(findName)) {
121
122 m_Name = m_Name.substring(findName.length());
123 break;
124 }
125 }
126 } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
127
128
129 isArray = true;
130 }
131 m_ASTAllocationExpression = node;
132 }
133
134 public String getName() {
135 return m_Name;
136 }
137
138 public int getArgumentCount() {
139 return m_ArgumentCount;
140 }
141
142 public ASTAllocationExpression getASTAllocationExpression() {
143 return m_ASTAllocationExpression;
144 }
145
146 public boolean isArray() {
147 return isArray;
148 }
149 }
150
151 /***
152 * Outer interface visitation
153 */
154 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
155 if (node.isInterface()) {
156 if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
157
158 String interfaceName = node.getImage();
159 int formerID = getClassID();
160 setClassID(classDataList.size());
161 ClassData newClassData = new ClassData(interfaceName);
162
163 ClassData formerClassData = (ClassData) classDataList.get(formerID);
164 newClassData.addClassQualifyingName(formerClassData.getClassName());
165 classDataList.add(getClassID(), newClassData);
166 Object o = super.visit(node, data);
167 setClassID(formerID);
168 return o;
169 } else {
170 String interfaceName = node.getImage();
171 classDataList.clear();
172 setClassID(0);
173 classDataList.add(getClassID(), new ClassData(interfaceName));
174 Object o = super.visit(node, data);
175 if (o != null) {
176 processRule(o);
177 } else {
178 processRule(data);
179 }
180 setClassID(-1);
181 return o;
182 }
183 } else if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
184
185 String className = node.getImage();
186 int formerID = getClassID();
187 setClassID(classDataList.size());
188 ClassData newClassData = new ClassData(className);
189
190
191
192
193 if (formerID == -1 || formerID >= classDataList.size()) {
194 return null;
195 }
196
197 ClassData formerClassData = (ClassData) classDataList.get(formerID);
198 newClassData.addClassQualifyingName(formerClassData.getClassName());
199 classDataList.add(getClassID(), newClassData);
200 Object o = super.visit(node, data);
201 setClassID(formerID);
202 return o;
203 }
204
205 String className = node.getImage();
206 classDataList.clear();
207 setClassID(0);
208 classDataList.add(getClassID(), new ClassData(className));
209 Object o = super.visit(node, data);
210 if (o != null) {
211 processRule(o);
212 } else {
213 processRule(data);
214 }
215 setClassID(-1);
216 return o;
217 }
218
219 /***
220 * Store all target constructors
221 */
222 public Object visit(ASTConstructorDeclaration node, Object data) {
223 if (node.isPrivate()) {
224 getCurrentClassData().addConstructor(node);
225 }
226 return super.visit(node, data);
227 }
228
229 public Object visit(ASTAllocationExpression node, Object data) {
230
231
232
233
234 if (classID == -1 || getCurrentClassData() == null) {
235 return data;
236 }
237 AllocData ad = new AllocData(node, packageName, getCurrentClassData().getClassQualifyingNamesList());
238 if (!ad.isArray()) {
239 getCurrentClassData().addInstantiation(ad);
240 }
241 return super.visit(node, data);
242 }
243
244 private void processRule(Object ctx) {
245
246 for (Iterator outerIterator = classDataList.iterator(); outerIterator.hasNext();) {
247 ClassData outerDataSet = (ClassData) outerIterator.next();
248 for (Iterator constructors = outerDataSet.getPrivateConstructorIterator(); constructors.hasNext();) {
249 ASTConstructorDeclaration cd = (ASTConstructorDeclaration) constructors.next();
250
251 for (Iterator innerIterator = classDataList.iterator(); innerIterator.hasNext();) {
252 ClassData innerDataSet = (ClassData) innerIterator.next();
253 if (outerDataSet == innerDataSet) {
254 continue;
255 }
256 for (Iterator allocations = innerDataSet.getInstantiationIterator(); allocations.hasNext();) {
257 AllocData ad = (AllocData) allocations.next();
258
259
260 if (outerDataSet.getClassName().equals(ad.getName()) && (cd.getParameterCount() == ad.getArgumentCount())) {
261 addViolation(ctx, ad.getASTAllocationExpression());
262 }
263 }
264 }
265 }
266 }
267 }
268
269 private ClassData getCurrentClassData() {
270
271
272
273
274 if (classID >= classDataList.size()) {
275 return null;
276 }
277 return (ClassData) classDataList.get(classID);
278 }
279
280 private void setClassID(int ID) {
281 classID = ID;
282 }
283
284 private int getClassID() {
285 return classID;
286 }
287
288
289
290
291
292
293
294
295
296 private static String stripString(String remove, String value) {
297 String returnValue;
298 int index = value.indexOf(remove);
299 if (index != -1) {
300 returnValue = value.substring(0, index) + value.substring(index + remove.length());
301 } else {
302 returnValue = value;
303 }
304 return returnValue;
305 }
306
307 }