001: /*
002: * AccessorClassGenerationRule.java
003: *
004: * Created on February 14, 2003, 9:33 PM
005: */
006:
007: package org.acm.seguin.pmd.rules;
008:
009: import org.acm.seguin.pmd.AbstractRule;
010: import org.acm.seguin.pmd.RuleContext;
011: import net.sourceforge.jrefactory.ast.ASTAllocationExpression;
012: import net.sourceforge.jrefactory.ast.ASTArguments;
013: import net.sourceforge.jrefactory.ast.ASTArrayDimsAndInits;
014: import net.sourceforge.jrefactory.ast.ASTClassDeclaration;
015: import net.sourceforge.jrefactory.ast.ASTCompilationUnit;
016: import net.sourceforge.jrefactory.ast.ASTConstructorDeclaration;
017: import net.sourceforge.jrefactory.ast.ASTInterfaceDeclaration;
018: import net.sourceforge.jrefactory.ast.ASTName;
019: import net.sourceforge.jrefactory.ast.ASTNestedClassDeclaration;
020: import net.sourceforge.jrefactory.ast.ASTNestedInterfaceDeclaration;
021: import net.sourceforge.jrefactory.ast.ASTPackageDeclaration;
022: import net.sourceforge.jrefactory.ast.ASTUnmodifiedClassDeclaration;
023:
024: import java.util.ArrayList;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.ListIterator;
028:
029: /**
030: * 1. Note all private constructors.
031: * 2. Note all instantiations from outside of the class by way of the private
032: * constructor.
033: * 3. Flag instantiations.
034: *
035: *
036: * Parameter types can not be matched because they can come as exposed members
037: * of classes. In this case we have no way to know what the type is. We can
038: * make a best effort though which can filter some?
039: *
040: * @author CL Gilbert (dnoyeb@users.sourceforge.net)
041: * @author David Konecny (david.konecny@)
042: */
043: public class AccessorClassGenerationRule extends AbstractRule {
044: private int classID = -1;
045: private List classDataList;
046: private String packageName;
047:
048: private ClassData getCurrentClassData() {
049: return (ClassData) classDataList.get(classID);
050: }
051:
052: private void setClassID(int ID) {
053: classID = ID;
054: }
055:
056: private int getClassID() {
057: return classID;
058: }
059:
060: private String getPackageName() {
061: return packageName;
062: }
063:
064: //remove = Fire.
065: //value = someFire.Fighter
066: // 0123456789012345
067: //index = 4
068: //remove.size() = 5
069: //value.substring(0,4) = some
070: //value.substring(4 + remove.size()) = Fighter
071: //return "someFighter"
072: private static String stripString(String remove, String value) {
073: String returnValue;
074: int index = value.indexOf(remove);
075: if (index != -1) { //if the package name can start anywhere but 0 plese inform the author because this will break
076: returnValue = value.substring(0, index)
077: + value.substring(index + remove.length());
078: } else {
079: returnValue = value;
080: }
081: return returnValue;
082: }
083:
084: /**
085: *
086: */
087: private class ClassData {
088: /** The name of this class */
089: private String m_ClassName;
090: /** List of private constructors within this class */
091: private List m_PrivateConstructors;
092: /** List of instantiations of objects within this class */
093: private List m_Instantiations;
094: /** List of outer class names that exist above this class */
095: private List m_ClassQualifyingNames;
096:
097: public ClassData(String className) {
098: m_ClassName = className;
099: m_PrivateConstructors = new ArrayList();
100: m_Instantiations = new ArrayList();
101: m_ClassQualifyingNames = new ArrayList();
102: }
103:
104: public void addInstantiation(AllocData ad) {
105: m_Instantiations.add(ad);
106: }
107:
108: public Iterator getInstantiationIterator() {
109: return m_Instantiations.iterator();
110: }
111:
112: public void addConstructor(ASTConstructorDeclaration cd) {
113: m_PrivateConstructors.add(cd);
114: }
115:
116: public Iterator getPrivateConstructorIterator() {
117: return m_PrivateConstructors.iterator();
118: }
119:
120: public String getClassName() {
121: return m_ClassName;
122: }
123:
124: public void addClassQualifyingName(String name) {
125: m_ClassQualifyingNames.add(name);
126: }
127:
128: public Iterator getClassQualifyingNames() {
129: return m_ClassQualifyingNames.iterator();
130: }
131:
132: public List getClassQualifyingNamesList() {
133: return m_ClassQualifyingNames;
134: }
135: }
136:
137: private static class AllocData {
138: private String m_Name;
139: private int m_ArgumentCount;
140: private ASTAllocationExpression m_ASTAllocationExpression;
141: private boolean isArray = false;
142:
143: public AllocData(ASTAllocationExpression node,
144: 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: ASTName an = (ASTName) node.jjtGetFirstChild();
151: m_Name = stripString(aPackageName + ".", an.getImage());
152:
153: //strip off outer class names
154: //try OuterClass, then try OuterClass.InnerClass, then try OuterClass.InnerClass.InnerClass2, etc...
155: STRIPPING: {
156: String findName = "";
157: for (ListIterator li = classQualifyingNames
158: .listIterator(classQualifyingNames.size()); li
159: .hasPrevious();) {
160: String aName = (String) li.previous();
161: findName = aName + "." + findName;
162: if (m_Name.startsWith(findName)) {
163: //strip off name and exit
164: m_Name = m_Name
165: .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()
188: + " arguments= " + getArgumentCount());
189: }
190:
191: public ASTAllocationExpression getASTAllocationExpression() {
192: return m_ASTAllocationExpression;
193: }
194:
195: public boolean isArray() {
196: return isArray;
197: }
198: }
199:
200: /**
201: * Work on each file independently.
202: * Assume a new AccessorClassGenerationRule object is created for each run?
203: */
204: public Object visit(ASTCompilationUnit node, Object data) {
205: classDataList = new ArrayList();
206: return super .visit(node, data);
207: }
208:
209: private void processRule(RuleContext ctx) {
210: //check constructors of outerIterator
211: //against allocations of innerIterator
212: for (Iterator outerIterator = classDataList.iterator(); outerIterator
213: .hasNext();) {
214:
215: ClassData outerDataSet = (ClassData) outerIterator.next();
216: for (Iterator constructors = outerDataSet
217: .getPrivateConstructorIterator(); constructors
218: .hasNext();) {
219: ASTConstructorDeclaration cd = (ASTConstructorDeclaration) constructors
220: .next();
221:
222: for (Iterator innerIterator = classDataList.iterator(); innerIterator
223: .hasNext();) {
224: ClassData innerDataSet = (ClassData) innerIterator
225: .next();
226: if (outerDataSet == innerDataSet) {
227: continue;
228: }
229: for (Iterator allocations = innerDataSet
230: .getInstantiationIterator(); allocations
231: .hasNext();) {
232: AllocData ad = (AllocData) allocations.next();
233: //if the constructor matches the instantiation
234: //flag the instantiation as a generator of an extra class
235:
236: if (outerDataSet.getClassName().equals(
237: ad.getName())
238: && (cd.getParameterCount() == ad
239: .getArgumentCount())) {
240: ctx
241: .getReport()
242: .addRuleViolation(
243: createRuleViolation(
244: ctx,
245: ad
246: .getASTAllocationExpression()
247: .getBeginLine()));
248: }
249: }
250: }
251: }
252: }
253: }
254:
255: /**
256: * Store package name to strip off in case necessary
257: */
258: public Object visit(ASTPackageDeclaration node, Object data) {
259: packageName = ((ASTName) node.jjtGetFirstChild()).getImage();
260: // System.out.println("Package is " + packageName);
261: return super .visit(node, data);
262: }
263:
264: /**
265: * Outer interface visitation
266: */
267: public Object visit(ASTInterfaceDeclaration node, Object data) {
268: String className = node.getUnmodifedInterfaceDeclaration()
269: .getImage();
270: // System.out.println("interface = " + className);
271: classDataList.clear();
272: setClassID(0);
273: classDataList.add(getClassID(), new ClassData(className));
274: Object o = super .visit(node, data);
275: if (o != null) {
276: processRule((RuleContext) o);
277: } else {
278: processRule((RuleContext) data);
279: }
280: setClassID(-1);
281: return o;
282: }
283:
284: /**
285: * Inner interface visitation
286: */
287: public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
288: String className = node.getUnmodifedInterfaceDeclaration()
289: .getImage();
290: // System.out.println("interface = " + className);
291: int formerID = getClassID();
292: setClassID(classDataList.size());
293: ClassData newClassData = new ClassData(className);
294: //store the names of any outer classes of this class in the classQualifyingName List
295: ClassData formerClassData = (ClassData) classDataList
296: .get(formerID);
297: newClassData.addClassQualifyingName(formerClassData
298: .getClassName());
299: classDataList.add(getClassID(), newClassData);
300: Object o = super .visit(node, data);
301: setClassID(formerID);
302: return o;
303: }
304:
305: /**
306: * Outer class declaration
307: */
308: public Object visit(ASTClassDeclaration node, Object data) {
309: String className = ((ASTUnmodifiedClassDeclaration) node
310: .jjtGetFirstChild()).getImage();
311: // System.out.println("classname = " + className);
312: classDataList.clear();
313: setClassID(0);//first class
314: classDataList.add(getClassID(), new ClassData(className));
315: Object o = super .visit(node, data);
316: if (o != null) {
317: processRule((RuleContext) o);
318: } else {
319: processRule((RuleContext) data);
320: }
321: setClassID(-1);
322: return o;
323: }
324:
325: public Object visit(ASTNestedClassDeclaration node, Object data) {
326: String className = ((ASTUnmodifiedClassDeclaration) node
327: .jjtGetFirstChild()).getImage();
328: // System.out.println("classname = " + className);
329: int formerID = getClassID();
330: setClassID(classDataList.size());
331: ClassData newClassData = new ClassData(className);
332: //store the names of any outer classes of this class in the classQualifyingName List
333: ClassData formerClassData = (ClassData) classDataList
334: .get(formerID);
335: newClassData.addClassQualifyingName(formerClassData
336: .getClassName());
337: classDataList.add(getClassID(), newClassData);
338: Object o = super .visit(node, data);
339: setClassID(formerID);
340: return o;
341: }
342:
343: /**
344: * Store all target constructors
345: */
346: public Object visit(ASTConstructorDeclaration node, Object data) {
347: if (node.isPrivate()) {
348: getCurrentClassData().addConstructor(node);
349: }
350: return super .visit(node, data);
351: }
352:
353: public Object visit(ASTAllocationExpression node, Object data) {
354: // TODO
355: // this is a hack to bail out here
356: // but I'm not sure why this is happening
357: // TODO
358: if (classID == -1) {
359: return data;
360: }
361: AllocData ad = new AllocData(node, getPackageName(),
362: getCurrentClassData().getClassQualifyingNamesList());
363: if (ad.isArray() == false) {
364: getCurrentClassData().addInstantiation(ad);
365: //ad.show();
366: }
367: return super.visit(node, data);
368: }
369: }
|