001: /*
002: * Author: Chris Seguin
003: *
004: * This software has been developed under the copyleft
005: * rules of the GNU General Public License. Please
006: * consult the GNU General Public License for more
007: * details about use and distribution of this software.
008: */
009: package org.acm.seguin.refactor.field;
010:
011: import java.util.Iterator;
012: import java.util.StringTokenizer;
013: import net.sourceforge.jrefactory.ast.*;
014: import net.sourceforge.jrefactory.ast.ASTArguments;
015: import net.sourceforge.jrefactory.ast.ASTConstructorDeclaration;
016: import net.sourceforge.jrefactory.ast.ASTFieldDeclaration;
017: import net.sourceforge.jrefactory.ast.ASTFormalParameter;
018: import net.sourceforge.jrefactory.ast.ASTFormalParameters;
019: import net.sourceforge.jrefactory.ast.ASTMethodDeclaration;
020: import net.sourceforge.jrefactory.ast.ASTName;
021: import net.sourceforge.jrefactory.ast.ASTPackageDeclaration;
022: import net.sourceforge.jrefactory.ast.ASTPrimaryExpression;
023: import net.sourceforge.jrefactory.ast.ASTPrimaryPrefix;
024: import net.sourceforge.jrefactory.ast.ASTPrimarySuffix;
025: import net.sourceforge.jrefactory.ast.ASTUnmodifiedClassDeclaration;
026: import net.sourceforge.jrefactory.ast.ASTUnmodifiedInterfaceDeclaration;
027: import net.sourceforge.jrefactory.ast.ASTVariableDeclarator;
028: import net.sourceforge.jrefactory.ast.ASTVariableDeclaratorId;
029: import net.sourceforge.jrefactory.parser.ChildrenVisitor;
030: import org.acm.seguin.summary.LocalVariableSummary;
031: import org.acm.seguin.summary.MethodSummary;
032: import org.acm.seguin.summary.PackageSummary;
033: import org.acm.seguin.summary.ParameterSummary;
034: import org.acm.seguin.summary.Summary;
035: import org.acm.seguin.summary.TypeSummary;
036: import org.acm.seguin.summary.VariableSummary;
037: import org.acm.seguin.summary.query.GetMethodSummary;
038: import org.acm.seguin.summary.query.GetTypeSummary;
039: import org.acm.seguin.summary.query.ImportsType;
040: import org.acm.seguin.summary.query.LookupVariable;
041:
042: /**
043: * Visitor that traverses an AST and removes a specified field
044: *
045: * @author Chris Seguin
046: * @since 2.4.0
047: */
048: public class RenameFieldVisitor extends ChildrenVisitor {
049: /**
050: * Visit a package declaration
051: *
052: * @param node the class body node
053: * @param data the data for the visitor
054: * @return the field if it is found
055: * @since 2.4.0
056: */
057: public Object visit(ASTPackageDeclaration node, Object data) {
058: RenameFieldData rfd = (RenameFieldData) data;
059:
060: ASTName name = (ASTName) node.jjtGetFirstChild();
061: PackageSummary packageSummary = PackageSummary
062: .getPackageSummary(name.getName());
063: rfd.setCurrentSummary(packageSummary);
064:
065: return super .visit(node, data);
066: }
067:
068: /**
069: * Visit a class declaration
070: *
071: * @param node the class body node
072: * @param data the data for the visitor
073: * @return the field if it is found
074: * @since 2.4.0
075: */
076: public Object visit(ASTUnmodifiedClassDeclaration node, Object data) {
077: RenameFieldData rfd = (RenameFieldData) data;
078: Summary current = rfd.getCurrentSummary();
079:
080: if (current == null) {
081: rfd.setCurrentSummary(GetTypeSummary.query("", node
082: .getName()));
083: } else if (current instanceof PackageSummary) {
084: rfd.setCurrentSummary(GetTypeSummary.query(
085: (PackageSummary) current, node.getName()));
086: } else if (current instanceof TypeSummary) {
087: rfd.setCurrentSummary(GetTypeSummary.query(
088: (TypeSummary) current, node.getName()));
089: } else if (current instanceof MethodSummary) {
090: rfd.setCurrentSummary(GetTypeSummary.query(
091: (MethodSummary) current, node.getName()));
092: }
093:
094: Object result = super .visit(node, data);
095:
096: rfd.setCurrentSummary(current);
097: return result;
098: }
099:
100: /**
101: * Visit a class declaration
102: *
103: * @param node the class body node
104: * @param data the data for the visitor
105: * @return the field if it is found
106: * @since 2.4.0
107: */
108: public Object visit(ASTUnmodifiedInterfaceDeclaration node,
109: Object data) {
110: RenameFieldData rfd = (RenameFieldData) data;
111: Summary current = rfd.getCurrentSummary();
112:
113: if (current == null) {
114: rfd.setCurrentSummary(GetTypeSummary.query("", node
115: .getName()));
116: } else if (current instanceof PackageSummary) {
117: rfd.setCurrentSummary(GetTypeSummary.query(
118: (PackageSummary) current, node.getName()));
119: } else if (current instanceof TypeSummary) {
120: rfd.setCurrentSummary(GetTypeSummary.query(
121: (TypeSummary) current, node.getName()));
122: } else if (current instanceof MethodSummary) {
123: rfd.setCurrentSummary(GetTypeSummary.query(
124: (MethodSummary) current, node.getName()));
125: }
126:
127: Object result = super .visit(node, data);
128:
129: rfd.setCurrentSummary(current);
130: return result;
131: }
132:
133: /**
134: * Visit a field declaration
135: *
136: * @param node the class body node
137: * @param data the data for the visitor
138: * @return the field if it is found
139: * @since 2.4.0
140: */
141: public Object visit(ASTFieldDeclaration node, Object data) {
142: RenameFieldData rfd = (RenameFieldData) data;
143:
144: if (rfd.getCurrentSummary() == rfd.getTypeSummary()) {
145: int childNo = node.skipAnnotations();
146: for (int ndx = childNo + 1; ndx < node.jjtGetNumChildren(); ndx++) {
147: ASTVariableDeclarator next = (ASTVariableDeclarator) node
148: .jjtGetChild(ndx);
149: ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) next
150: .jjtGetFirstChild();
151: if (id.getName().equals(rfd.getOldName())) {
152: id.setName(rfd.getNewName());
153: }
154: }
155: }
156:
157: return super .visit(node, data);
158: }
159:
160: /**
161: * Visit a primary expression
162: *
163: * @param node the class body node
164: * @param data the data for the visitor
165: * @return the field if it is found
166: * @since 2.4.0
167: */
168: public Object visit(ASTPrimaryExpression node, Object data) {
169: RenameFieldData rfd = (RenameFieldData) data;
170: ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) node
171: .jjtGetFirstChild();
172: if ("this".equals(prefix.getName())) {
173: processThisExpression(rfd, node, prefix);
174: } else if ((prefix.jjtGetNumChildren() >= 1)
175: && (prefix.jjtGetFirstChild() instanceof ASTName)) {
176: processNameExpression(rfd, node, prefix);
177: } else if ((prefix.jjtGetNumChildren() >= 1)
178: && isCastExpression(prefix)) {
179: processCastExpression(rfd, node, prefix);
180: }
181:
182: return super .visit(node, data);
183: }
184:
185: /**
186: * Visit a method declaration
187: *
188: * @param node Description of Parameter
189: * @param data Description of Parameter
190: * @return Description of the Returned Value
191: * @since 2.4.0
192: */
193: public Object visit(ASTMethodDeclaration node, Object data) {
194: RenameFieldData rfd = (RenameFieldData) data;
195:
196: Summary current = rfd.getCurrentSummary();
197: MethodSummary found = GetMethodSummary.query(
198: (TypeSummary) current, node);
199: rfd.setCurrentSummary(found);
200: rfd
201: .setMustInsertThis(isAlreadyPresent(found, rfd
202: .getNewName()));
203:
204: boolean this Required = LookupVariable.getLocal(found, rfd
205: .getOldName()) != null;
206: rfd.setThisRequired(this Required);
207:
208: Object result = super .visit(node, data);
209:
210: rfd.setThisRequired(false);
211: rfd.setCurrentSummary(current);
212:
213: return result;
214: }
215:
216: /**
217: * Visit a constructor declaration
218: *
219: * @param node Description of Parameter
220: * @param data Description of Parameter
221: * @return Description of the Returned Value
222: * @since 2.4.0
223: */
224: public Object visit(ASTConstructorDeclaration node, Object data) {
225: RenameFieldData rfd = (RenameFieldData) data;
226:
227: Summary current = rfd.getCurrentSummary();
228: MethodSummary found = GetMethodSummary.query(
229: (TypeSummary) current, node);
230: rfd.setCurrentSummary(found);
231: rfd
232: .setMustInsertThis(isAlreadyPresent(found, rfd
233: .getNewName()));
234:
235: boolean this Required = LookupVariable.getLocal(found, rfd
236: .getOldName()) != null;
237: rfd.setThisRequired(this Required);
238:
239: Object result = super .visit(node, data);
240:
241: rfd.setThisRequired(false);
242: rfd.setCurrentSummary(current);
243:
244: return result;
245: }
246:
247: /**
248: * Determine if the new name is already present in the method
249: *
250: * @param method Description of Parameter
251: * @param newName Description of Parameter
252: * @return The AlreadyPresent value
253: * @since 2.4.0
254: */
255: private boolean isAlreadyPresent(MethodSummary method,
256: String newName) {
257: Iterator iter = method.getParameters();
258: if (iter != null) {
259: while (iter.hasNext()) {
260: ParameterSummary next = (ParameterSummary) iter.next();
261: if (next.getName().equals(newName)) {
262: return true;
263: }
264: }
265: }
266:
267: iter = method.getDependencies();
268: if (iter != null) {
269: while (iter.hasNext()) {
270: Summary next = (Summary) iter.next();
271: if ((next instanceof LocalVariableSummary)
272: && (next.getName().equals(newName))) {
273: return true;
274: }
275: }
276: }
277:
278: return false;
279: }
280:
281: /**
282: * Description of the Method
283: *
284: * @param name Description of Parameter
285: * @param oldName Description of Parameter
286: * @param current Description of Parameter
287: * @param hasSuffixArguments Description of Parameter
288: * @param changingType Description of Parameter
289: * @return Description of the Returned Value
290: * @since 2.4.0
291: */
292: private int shouldChangePart(ASTName name, String oldName,
293: Summary current, boolean hasSuffixArguments,
294: TypeSummary changingType) {
295: int last = name.getNameSize() - 1;
296: if (hasSuffixArguments) {
297: last--;
298: }
299:
300: int forwardTo = -1;
301: for (int ndx = last; ndx >= 0; ndx--) {
302: if (name.getNamePart(ndx).equals(oldName)) {
303: forwardTo = ndx;
304: }
305: }
306:
307: if (forwardTo == -1) {
308: return -1;
309: }
310:
311: VariableSummary varSummary = LookupVariable.query(
312: (MethodSummary) current, name.getNamePart(0));
313: if (varSummary == null) {
314: return -1;
315: }
316: TypeSummary currentType = GetTypeSummary.query(varSummary
317: .getTypeDecl());
318:
319: for (int ndx = 1; ndx < forwardTo; ndx++) {
320: varSummary = LookupVariable.queryFieldSummary(currentType,
321: name.getNamePart(ndx));
322: if (varSummary == null) {
323: return -1;
324: }
325: currentType = GetTypeSummary
326: .query(varSummary.getTypeDecl());
327: }
328:
329: if (currentType == changingType) {
330: return forwardTo;
331: }
332:
333: return -1;
334: }
335:
336: /**
337: * Description of the Method
338: *
339: * @param rfd Description of Parameter
340: * @param node Description of Parameter
341: * @param prefix Description of Parameter
342: * @since 2.4.0
343: */
344: private void processThisExpression(RenameFieldData rfd,
345: ASTPrimaryExpression node, ASTPrimaryPrefix prefix) {
346: if (rfd.isAllowedToChangeThis()
347: && (node.jjtGetNumChildren() >= 2)) {
348: ASTPrimarySuffix suffix = (ASTPrimarySuffix) node
349: .jjtGetChild(1);
350: if (rfd.getOldName().equals(suffix.getName())) {
351: boolean change = true;
352:
353: if (node.jjtGetNumChildren() >= 3) {
354: ASTPrimarySuffix next = (ASTPrimarySuffix) node
355: .jjtGetChild(2);
356: if ((next.jjtGetFirstChild() != null)
357: && (next.jjtGetFirstChild() instanceof ASTArguments)) {
358: change = false;
359: }
360: }
361:
362: if (change) {
363: suffix.setName(rfd.getNewName());
364: }
365: }
366: }
367: }
368:
369: /**
370: * Description of the Method
371: *
372: * @param rfd Description of Parameter
373: * @param node Description of Parameter
374: * @param prefix Description of Parameter
375: * @since 2.4.0
376: */
377: private void processNameExpression(RenameFieldData rfd,
378: ASTPrimaryExpression node, ASTPrimaryPrefix prefix) {
379: ASTName name = (ASTName) prefix.jjtGetFirstChild();
380:
381: if (!rfd.isThisRequired()) {
382: boolean hasSuffixArguments = false;
383:
384: if (node.jjtGetNumChildren() >= 2) {
385: ASTPrimarySuffix next = (ASTPrimarySuffix) node
386: .jjtGetChild(1);
387: if ((next.jjtGetFirstChild() != null)
388: && (next.jjtGetFirstChild() instanceof ASTArguments)) {
389: hasSuffixArguments = true;
390: }
391: }
392:
393: if ((name.getNameSize() > 1) || !hasSuffixArguments) {
394: if ((rfd.isAllowedToChangeFirst())
395: && (name.getNamePart(0)
396: .equals(rfd.getOldName()))) {
397: name.setNamePart(0, rfd.getNewName());
398: if (rfd.isMustInsertThis()) {
399: name.insertNamePart(0, "this");
400: }
401: } else {
402: int index = shouldChangePart(name,
403: rfd.getOldName(), rfd.getCurrentSummary(),
404: hasSuffixArguments, rfd.getTypeSummary());
405: if (index > -1) {
406: name.setNamePart(index, rfd.getNewName());
407: }
408: }
409: }
410: }
411:
412: if (rfd.getOldField().isStatic()) {
413: String nameString = name.getName();
414: if (nameString.startsWith(rfd.getFullName())) {
415: replaceNamePart(name, rfd.getFullName(), rfd
416: .getNewName());
417: } else if (nameString.startsWith(rfd.getImportedName())
418: && ImportsType.query(rfd.getCurrentSummary(), rfd
419: .getTypeSummary())) {
420: replaceNamePart(name, rfd.getImportedName(), rfd
421: .getNewName());
422: }
423: }
424: }
425:
426: private boolean isCastExpression(ASTPrimaryPrefix prefix) {
427: //System.out.println("isCastExpression("+ prefix+")");
428: Node this Node = prefix;
429: while (this Node != null) {
430: if (this Node instanceof ASTCastExpression) {
431: return true;
432: }
433: this Node = (this Node.jjtGetNumChildren() > 0) ? this Node
434: .jjtGetFirstChild() : null;
435: }
436: return false;
437: }
438:
439: /**
440: * Description of the Method
441: *
442: * @param rfd Description of Parameter
443: * @param node Description of Parameter
444: * @param prefix Description of Parameter
445: * @since 2.4.0
446: */
447: private void processCastExpression(RenameFieldData rfd,
448: ASTPrimaryExpression node, ASTPrimaryPrefix prefix) {
449: System.out.println("isCastExpression(" + node + "," + prefix
450: + ")");
451: Node this Node = prefix;
452: while (this Node != null) {
453: if (this Node instanceof ASTCastExpression) {
454: ASTCastExpression cast = (ASTCastExpression) this Node;
455: if (cast.jjtGetFirstChild() instanceof ASTType) {
456: ASTType type = (ASTType) cast.jjtGetFirstChild();
457: if (type.jjtGetFirstChild() instanceof ASTReferenceType) {
458: ASTReferenceType refType = (ASTReferenceType) type
459: .jjtGetFirstChild();
460: if (refType.jjtGetFirstChild() instanceof ASTClassOrInterfaceType) {
461: ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType) refType
462: .jjtGetFirstChild();
463: String className = clazz.getName();
464: System.out.println(" className="
465: + className
466: + ", rfd.getImportedName()="
467: + rfd.getImportedName());
468: System.out.println("rfd=");
469: System.out.println("" + rfd);
470: if (className.startsWith(rfd
471: .getImportedName())
472: && ImportsType.query(rfd
473: .getCurrentSummary(), rfd
474: .getTypeSummary())) {
475: System.out.println(" - rename");
476: }
477: }
478: }
479: }
480: }
481: this Node = (this Node.jjtGetNumChildren() > 0) ? this Node
482: .jjtGetFirstChild() : null;
483: }
484: }
485:
486: /**
487: * Description of the Method
488: *
489: * @param name Description of Parameter
490: * @param form Description of Parameter
491: * @param newName Description of Parameter
492: * @since 2.4.0
493: */
494: private void replaceNamePart(ASTName name, String form,
495: String newName) {
496: StringTokenizer tok = new StringTokenizer(form, ".");
497: int count = -1;
498: String finalPart = null;
499: while (tok.hasMoreTokens()) {
500: finalPart = tok.nextToken();
501: count++;
502: }
503:
504: if (name.getNamePart(count).equals(finalPart)) {
505: name.setNamePart(count, newName);
506: }
507: }
508: }
|