Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 307 |  | Methods: | 23
| NCLOC: | 214 |  | Classes: | 3
Source file | Conditionals | Statements | Methods | TOTAL
AccessorClassGeneration.java | 75% | 82.3% | 95.7% | 82.2% |

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;
40 |  |
41 | 0 | public Object visit(ASTEnumDeclaration node, Object data) {
42 | 0 | return data; // just skip Enums
43 |  | }
44 |  |
45 | 6 | public Object visit(ASTCompilationUnit node, Object data) {
46 | 6 | classDataList.clear();
47 | 6 | packageName = node.getScope().getEnclosingSourceFileScope().getPackageName();
48 | 6 | 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 | 12 | public ClassData(String className) {
61 | 12 | m_ClassName = className;
62 | 12 | m_PrivateConstructors = new ArrayList();
63 | 12 | m_Instantiations = new ArrayList();
64 | 12 | m_ClassQualifyingNames = new ArrayList();
65 |  | }
66 |  |
67 | 6 | public void addInstantiation(AllocData ad) {
68 | 6 | m_Instantiations.add(ad);
69 |  | }
70 |  |
71 | 4 | public Iterator getInstantiationIterator() {
72 | 4 | return m_Instantiations.iterator();
73 |  | }
74 |  |
75 | 4 | public void addConstructor(ASTConstructorDeclaration cd) {
76 | 4 | m_PrivateConstructors.add(cd);
77 |  | }
78 |  |
79 | 12 | public Iterator getPrivateConstructorIterator() {
80 | 12 | return m_PrivateConstructors.iterator();
81 |  | }
82 |  |
83 | 10 | public String getClassName() {
84 | 10 | return m_ClassName;
85 |  | }
86 |  |
87 | 6 | public void addClassQualifyingName(String name) {
88 | 6 | m_ClassQualifyingNames.add(name);
89 |  | }
90 |  |
91 | 7 | public List getClassQualifyingNamesList() {
92 | 7 | 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 | 7 | public AllocData(ASTAllocationExpression node, String aPackageName, List classQualifyingNames) {
103 | 7 | if (node.jjtGetChild(1) instanceof ASTArguments) {
104 | 6 | ASTArguments aa = (ASTArguments) node.jjtGetChild(1);
105 | 6 | m_ArgumentCount = aa.getArgumentCount();
106 |  | //Get name and strip off all superfluous data
107 |  | //strip off package name if it is current package
108 | 6 | if (!(node.jjtGetChild(0) instanceof ASTClassOrInterfaceType)) {
109 | 0 | throw new RuntimeException("BUG: Expected a ASTClassOrInterfaceType, got a " + node.jjtGetChild(0).getClass());
110 |  | }
111 | 6 | ASTClassOrInterfaceType an = (ASTClassOrInterfaceType) node.jjtGetChild(0);
112 | 6 | m_Name = stripString(aPackageName + ".", an.getImage());
113 |  |
114 |  | //strip off outer class names
115 |  | //try OuterClass, then try OuterClass.InnerClass, then try OuterClass.InnerClass.InnerClass2, etc...
116 | 6 | String findName = "";
117 | 6 | for (ListIterator li = classQualifyingNames.listIterator(classQualifyingNames.size()); li.hasPrevious();) {
118 | 1 | String aName = (String) li.previous();
119 | 1 | findName = aName + "." + findName;
120 | 1 | if (m_Name.startsWith(findName)) {
121 |  | //strip off name and exit
122 | 0 | m_Name = m_Name.substring(findName.length());
123 | 0 | break;
124 |  | }
125 |  | }
126 | 1 | } else if (node.jjtGetChild(1) instanceof ASTArrayDimsAndInits) {
127 |  | //this is incomplete because I dont need it.
128 |  | // child 0 could be primitive or object (ASTName or ASTPrimitiveType)
129 | 1 | isArray = true;
130 |  | }
131 | 7 | m_ASTAllocationExpression = node;
132 |  | }
133 |  |
134 | 4 | public String getName() {
135 | 4 | return m_Name;
136 |  | }
137 |  |
138 | 4 | public int getArgumentCount() {
139 | 4 | return m_ArgumentCount;
140 |  | }
141 |  |
142 | 4 | public ASTAllocationExpression getASTAllocationExpression() {
143 | 4 | return m_ASTAllocationExpression;
144 |  | }
145 |  |
146 | 7 | public boolean isArray() {
147 | 7 | return isArray;
148 |  | }
149 |  | }
150 |  |
151 |  | /**
152 |  | * Outer interface visitation
153 |  | */
154 | 12 | public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
155 | 12 | if (node.isInterface()) {
156 | 1 | if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
157 |  | // not a top level interface
158 | 0 | String interfaceName = node.getImage();
159 | 0 | int formerID = getClassID();
160 | 0 | setClassID(classDataList.size());
161 | 0 | ClassData newClassData = new ClassData(interfaceName);
162 |  | //store the names of any outer classes of this class in the classQualifyingName List
163 | 0 | ClassData formerClassData = (ClassData) classDataList.get(formerID);
164 | 0 | newClassData.addClassQualifyingName(formerClassData.getClassName());
165 | 0 | classDataList.add(getClassID(), newClassData);
166 | 0 | Object o = super.visit(node, data);
167 | 0 | setClassID(formerID);
168 | 0 | return o;
169 |  | } else {
170 | 1 | String interfaceName = node.getImage();
171 | 1 | classDataList.clear();
172 | 1 | setClassID(0);
173 | 1 | classDataList.add(getClassID(), new ClassData(interfaceName));
174 | 1 | Object o = super.visit(node, data);
175 | 1 | if (o != null) {
176 | 0 | processRule(o);
177 |  | } else {
178 | 1 | processRule(data);
179 |  | }
180 | 1 | setClassID(-1);
181 | 1 | return o;
182 |  | }
183 | 11 | } else if (!(node.jjtGetParent().jjtGetParent() instanceof ASTCompilationUnit)) {
184 |  | // not a top level class
185 | 6 | String className = node.getImage();
186 | 6 | int formerID = getClassID();
187 | 6 | setClassID(classDataList.size());
188 | 6 | ClassData newClassData = new ClassData(className);
189 |  | // TODO
190 |  | // this is a hack to bail out here
191 |  | // but I'm not sure why this is happening
192 |  | // TODO
193 | 6 | if (formerID == -1 || formerID >= classDataList.size()) {
194 | 0 | return null;
195 |  | }
196 |  | //store the names of any outer classes of this class in the classQualifyingName List
197 | 6 | ClassData formerClassData = (ClassData) classDataList.get(formerID);
198 | 6 | newClassData.addClassQualifyingName(formerClassData.getClassName());
199 | 6 | classDataList.add(getClassID(), newClassData);
200 | 6 | Object o = super.visit(node, data);
201 | 6 | setClassID(formerID);
202 | 6 | return o;
203 |  | }
204 |  | // outer classes
205 | 5 | String className = node.getImage();
206 | 5 | classDataList.clear();
207 | 5 | setClassID(0);//first class
208 | 5 | classDataList.add(getClassID(), new ClassData(className));
209 | 5 | Object o = super.visit(node, data);
210 | 5 | if (o != null) {
211 | 0 | processRule(o);
212 |  | } else {
213 | 5 | processRule(data);
214 |  | }
215 | 5 | setClassID(-1);
216 | 5 | return o;
217 |  | }
218 |  |
219 |  | /**
220 |  | * Store all target constructors
221 |  | */
222 | 5 | public Object visit(ASTConstructorDeclaration node, Object data) {
223 | 5 | if (node.isPrivate()) {
224 | 4 | getCurrentClassData().addConstructor(node);
225 |  | }
226 | 5 | return super.visit(node, data);
227 |  | }
228 |  |
229 | 7 | public Object visit(ASTAllocationExpression node, Object data) {
230 |  | // TODO
231 |  | // this is a hack to bail out here
232 |  | // but I'm not sure why this is happening
233 |  | // TODO
234 | 7 | if (classID == -1 || getCurrentClassData() == null) {
235 | 0 | return data;
236 |  | }
237 | 7 | AllocData ad = new AllocData(node, packageName, getCurrentClassData().getClassQualifyingNamesList());
238 | 7 | if (!ad.isArray()) {
239 | 6 | getCurrentClassData().addInstantiation(ad);
240 |  | }
241 | 7 | return super.visit(node, data);
242 |  | }
243 |  |
244 | 6 | private void processRule(Object ctx) {
245 |  | //check constructors of outerIterator against allocations of innerIterator
246 | 6 | for (Iterator outerIterator = classDataList.iterator(); outerIterator.hasNext();) {
247 | 12 | ClassData outerDataSet = (ClassData) outerIterator.next();
248 | 12 | for (Iterator constructors = outerDataSet.getPrivateConstructorIterator(); constructors.hasNext();) {
249 | 4 | ASTConstructorDeclaration cd = (ASTConstructorDeclaration) constructors.next();
250 |  |
251 | 4 | for (Iterator innerIterator = classDataList.iterator(); innerIterator.hasNext();) {
252 | 8 | ClassData innerDataSet = (ClassData) innerIterator.next();
253 | 8 | if (outerDataSet == innerDataSet) {
254 | 4 | continue;
255 |  | }
256 | 4 | for (Iterator allocations = innerDataSet.getInstantiationIterator(); allocations.hasNext();) {
257 | 4 | AllocData ad = (AllocData) allocations.next();
258 |  | //if the constructor matches the instantiation
259 |  | //flag the instantiation as a generator of an extra class
260 | 4 | if (outerDataSet.getClassName().equals(ad.getName()) && (cd.getParameterCount() == ad.getArgumentCount())) {
261 | 4 | addViolation(ctx, ad.getASTAllocationExpression());
262 |  | }
263 |  | }
264 |  | }
265 |  | }
266 |  | }
267 |  | }
268 |  |
269 | 24 | private ClassData getCurrentClassData() {
270 |  | // TODO
271 |  | // this is a hack to bail out here
272 |  | // but I'm not sure why this is happening
273 |  | // TODO
274 | 24 | if (classID >= classDataList.size()) {
275 | 0 | return null;
276 |  | }
277 | 24 | return (ClassData) classDataList.get(classID);
278 |  | }
279 |  |
280 | 24 | private void setClassID(int ID) {
281 | 24 | classID = ID;
282 |  | }
283 |  |
284 | 18 | private int getClassID() {
285 | 18 | return classID;
286 |  | }
287 |  |
288 |  | //remove = Fire.
289 |  | //value = someFire.Fighter
290 |  | // 0123456789012345
291 |  | //index = 4
292 |  | //remove.size() = 5
293 |  | //value.substring(0,4) = some
294 |  | //value.substring(4 + remove.size()) = Fighter
295 |  | //return "someFighter"
296 | 6 | private static String stripString(String remove, String value) {
297 | 6 | String returnValue;
298 | 6 | int index = value.indexOf(remove);
299 | 6 | if (index != -1) { //if the package name can start anywhere but 0 please inform the author because this will break
300 | 0 | returnValue = value.substring(0, index) + value.substring(index + remove.length());
301 |  | } else {
302 | 6 | returnValue = value;
303 |  | }
304 | 6 | return returnValue;
305 |  | }
306 |  |
307 |  | }