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