001: package org.acm.seguin.refactor.type;
002:
003: import java.io.File;
004: import java.util.Iterator;
005: import java.util.LinkedList;
006: import java.util.StringTokenizer;
007: import net.sourceforge.jrefactory.ast.ASTName;
008: import org.acm.seguin.summary.*;
009: import org.acm.seguin.summary.query.FileSummaryGetter;
010: import org.acm.seguin.summary.query.MovingTypeList;
011: import org.acm.seguin.summary.query.StayingTypeList;
012: import org.acm.seguin.refactor.AddImportTransform;
013: import org.acm.seguin.refactor.TransformAST;
014: import org.acm.seguin.refactor.RemoveImportTransform;
015: import org.acm.seguin.refactor.ComplexTransform;
016:
017: /**
018: * Scans through the summary objects to create a list of files that reference
019: * a particular class.
020: *
021: *@author Chris Seguin
022: */
023: public abstract class TypeChangeVisitor extends TraversalVisitor {
024: // Instance Variables
025: private ComplexTransform refactoring;
026:
027: /**
028: * Visitor for type changes
029: *
030: *@param complex Description of Parameter
031: */
032: public TypeChangeVisitor(ComplexTransform complex) {
033: refactoring = complex;
034: }
035:
036: /**
037: * Visit a summary node. This is the default method.
038: *
039: *@param node the summary that we are visiting
040: *@param data the data that was passed in
041: *@return the result
042: */
043: public Object visit(Summary node, Object data) {
044: // Shouldn't have to do anything about one of these nodes
045: return data;
046: }
047:
048: /**
049: * Visit a file summary.
050: *
051: *@param node the summary that we are visiting
052: *@param data the data that was passed in
053: *@return the result
054: */
055: public Object visit(FileSummary node, Object data) {
056: if (node.getFile() == null) {
057: return null;
058: }
059:
060: if (!preconditions(node)) {
061: return null;
062: }
063:
064: refactoring.clear();
065: LinkedList list = getAppropriateClasses(node);
066:
067: Iterator iter = list.iterator();
068: while (iter.hasNext()) {
069: // Get the name of the class
070: String className = (String) iter.next();
071:
072: // First check to see if any of the classes were imported
073: boolean foundImport = checkImports(node, className);
074:
075: // Now we get down to the business of checking individual types
076: if (checkTypes(node, getState(foundImport, node, className))) {
077: AddImportTransform ait = getNewImports(node, className);
078: if (ait != null) {
079: ait.setIgnorePackageName(true);
080: refactoring.add(ait);
081: }
082: addRenamingTransforms(refactoring, node, className);
083: }
084: }
085:
086: refactoring.add(getFileSpecificTransform(node));
087:
088: if (refactoring.hasAnyChanges()) {
089: File oldFile = node.getFile();
090: File newFile = getNewFile(node);
091: refactoring.add(new RemoveSamePackageTransform());
092: refactoring.apply(oldFile, newFile);
093: }
094:
095: // Return some value
096: return refactoring;
097: }
098:
099: /**
100: * Visit a import summary.
101: *
102: *@param node the summary that we are visiting
103: *@param data the data that was passed in
104: *@return the result
105: */
106: public Object visit(ImportSummary node, Object data) {
107: // Local Variables
108: boolean importedClass = false;
109: boolean importedPackage = false;
110: boolean gettingPackage = node.getPackage().getName().equals(
111: getCurrentPackage());
112:
113: // Check to see if we have a specific class
114: if (gettingPackage) {
115: String typeName = node.getType();
116: if (typeName == null) {
117: importedPackage = true;
118: } else {
119: String className = (String) data;
120: importedClass = (className.equals(typeName));
121: }
122: }
123:
124: // At this point we know if we specifically imported the class
125: if (importedClass) {
126: refactoring.add(getRemoveImportTransform(node));
127: }
128:
129: // Return an integer code for what was found in this import
130: return new Boolean(importedPackage || importedClass);
131: }
132:
133: /**
134: * Visit a type summary.
135: *
136: *@param node the summary that we are visiting
137: *@param data the data that was passed in
138: *@return the result
139: */
140: public Object visit(TypeSummary node, Object data) {
141: Boolean result = new Boolean(false);
142:
143: // Check extension
144: TypeDeclSummary parent = node.getParentClass();
145: if (parent != null) {
146: result = (Boolean) parent.accept(this , data);
147: }
148: if (result.booleanValue()) {
149: return result;
150: }
151:
152: // Check list of implemented interfaces
153: Iterator iter = node.getImplementedInterfaces();
154: if (iter != null) {
155: while (iter.hasNext()) {
156: TypeDeclSummary next = (TypeDeclSummary) iter.next();
157: result = (Boolean) next.accept(this , data);
158: if (result.booleanValue()) {
159: return result;
160: }
161: }
162: }
163:
164: // Over the fields
165: iter = node.getFields();
166: if (iter != null) {
167: while (iter.hasNext()) {
168: FieldSummary next = (FieldSummary) iter.next();
169: result = (Boolean) next.accept(this , data);
170: if (result.booleanValue()) {
171: return result;
172: }
173: }
174: }
175:
176: // Over the methods
177: iter = node.getMethods();
178: if (iter != null) {
179: while (iter.hasNext()) {
180: MethodSummary next = (MethodSummary) iter.next();
181: result = (Boolean) next.accept(this , data);
182: if (result.booleanValue()) {
183: return result;
184: }
185: }
186: }
187:
188: // Over the types
189: iter = node.getTypes();
190: if (iter != null) {
191: while (iter.hasNext()) {
192: TypeSummary next = (TypeSummary) iter.next();
193: result = (Boolean) next.accept(this , data);
194: if (result.booleanValue()) {
195: return result;
196: }
197: }
198: }
199:
200: // Return the last false value
201: return result;
202: }
203:
204: /**
205: * Visit a method summary.
206: *
207: *@param node the summary that we are visiting
208: *@param data the data that was passed in
209: *@return the result
210: */
211: public Object visit(MethodSummary node, Object data) {
212: Boolean result = new Boolean(false);
213:
214: // Check the return type
215: TypeDeclSummary returnType = node.getReturnType();
216: if (returnType != null) {
217: result = (Boolean) returnType.accept(this , data);
218: if (result.booleanValue()) {
219: return result;
220: }
221: }
222:
223: // Check the parameters
224: Iterator iter = node.getParameters();
225: if (iter != null) {
226: while (iter.hasNext()) {
227: ParameterSummary next = (ParameterSummary) iter.next();
228: result = (Boolean) next.accept(this , data);
229: if (result.booleanValue()) {
230: return result;
231: }
232: }
233: }
234:
235: // Check the exceptions
236: iter = node.getExceptions();
237: if (iter != null) {
238: while (iter.hasNext()) {
239: TypeDeclSummary next = (TypeDeclSummary) iter.next();
240: result = (Boolean) next.accept(this , data);
241: if (result.booleanValue()) {
242: return result;
243: }
244: }
245: }
246:
247: // Check the dependencies
248: iter = node.getDependencies();
249: if (iter != null) {
250: while (iter.hasNext()) {
251: Summary next = (Summary) iter.next();
252: result = (Boolean) next.accept(this , data);
253: if (result.booleanValue()) {
254: return result;
255: }
256: }
257: }
258:
259: return result;
260: }
261:
262: /**
263: * Visit a field summary.
264: *
265: *@param node the summary that we are visiting
266: *@param data the data that was passed in
267: *@return the result
268: */
269: public Object visit(FieldSummary node, Object data) {
270: return visit((VariableSummary) node, data);
271: }
272:
273: /**
274: * Visit a parameter summary.
275: *
276: *@param node the summary that we are visiting
277: *@param data the data that was passed in
278: *@return the result
279: */
280: public Object visit(ParameterSummary node, Object data) {
281: return visit((VariableSummary) node, data);
282: }
283:
284: /**
285: * Visit a local variable summary.
286: *
287: *@param node the summary that we are visiting
288: *@param data the data that was passed in
289: *@return the result
290: */
291: public Object visit(LocalVariableSummary node, Object data) {
292: return visit((VariableSummary) node, data);
293: }
294:
295: /**
296: * Visit a variable summary.
297: *
298: *@param node the summary that we are visiting
299: *@param data the data that was passed in
300: *@return the result
301: */
302: public Object visit(VariableSummary node, Object data) {
303: return node.getTypeDecl().accept(this , data);
304: }
305:
306: /**
307: * Visit a type declaration summary.
308: *
309: *@param node the summary that we are visiting
310: *@param data the data that was passed in
311: *@return the result
312: */
313: public Object visit(TypeDeclSummary node, Object data) {
314: // Local Variables
315: State state = (State) data;
316: boolean mustUsePackage = state.isPackageRequired();
317: String className = state.getClassName();
318: String nodePackageName = node.getPackage();
319:
320: // Check if the package names match
321: if (isMatchingPackage(nodePackageName, mustUsePackage)) {
322: // Check for the specific type name
323: return new Boolean(className.equals(node.getType()));
324: }
325:
326: return new Boolean(false);
327: }
328:
329: /**
330: * Visit a message send summary.
331: *
332: *@param node the summary that we are visiting
333: *@param data the data that was passed in
334: *@return the result
335: */
336: public Object visit(MessageSendSummary node, Object data) {
337: // Local Variables
338: State state = (State) data;
339: boolean mustUsePackage = state.isPackageRequired();
340: String className = state.getClassName();
341:
342: // Check if the package names match
343: boolean classNameMatches = (node.getObjectName() != null)
344: && (node.getObjectName().equals(className));
345:
346: boolean packageNameMatches = isMatchingPackage(node
347: .getPackageName(), mustUsePackage);
348:
349: return new Boolean(classNameMatches && packageNameMatches);
350: }
351:
352: /**
353: * Visit a field access summary.
354: *
355: *@param node the summary that we are visiting
356: *@param data the data that was passed in
357: *@return the result
358: */
359: public Object visit(FieldAccessSummary node, Object data) {
360: // Local Variables
361: State state = (State) data;
362: boolean mustUsePackage = state.isPackageRequired();
363: String className = state.getClassName();
364:
365: boolean classNameMatches = (node.getObjectName() != null)
366: && (node.getObjectName().equals(className));
367:
368: boolean packageNameMatches = isMatchingPackage(node
369: .getPackageName(), mustUsePackage);
370:
371: return new Boolean(classNameMatches && packageNameMatches);
372: }
373:
374: /**
375: * Returns the state object to be used to determine if the particular type
376: * we are deleting is present
377: *
378: *@param foundImport Description of Parameter
379: *@param node Description of Parameter
380: *@param className Description of Parameter
381: *@return The State value
382: */
383: protected State getState(boolean foundImport, FileSummary node,
384: String className) {
385: boolean mustUsesFullPackageName = !(foundImport || isSamePackage(node));
386: return new State(className, mustUsesFullPackageName);
387: }
388:
389: /**
390: * Gets the File Specific Transform
391: *
392: *@param summary Gets a file specific transform
393: *@return The FileSpecificTransform value
394: */
395: protected abstract TransformAST getFileSpecificTransform(
396: FileSummary summary);
397:
398: /**
399: * Gets the New Imports transform
400: *
401: *@param node the file summary
402: *@param className the name of the class that is changing
403: *@return The NewImports value
404: */
405: protected abstract AddImportTransform getNewImports(
406: FileSummary node, String className);
407:
408: /**
409: * Gets the Remove Imports transform
410: *
411: *@param node the import summary
412: *@return The transform
413: */
414: protected abstract RemoveImportTransform getRemoveImportTransform(
415: ImportSummary node);
416:
417: /**
418: * Gets the list of classes to iterate over
419: *
420: *@param node the file summary
421: *@return The list
422: */
423: protected abstract LinkedList getAppropriateClasses(FileSummary node);
424:
425: /**
426: * Gets the reference to the file where the refactored output should be sent
427: *
428: *@param node the files summary
429: *@return The NewFile value
430: */
431: protected abstract File getNewFile(FileSummary node);
432:
433: /**
434: * Return the current package
435: *
436: *@return the current package of the class
437: */
438: protected abstract String getCurrentPackage();
439:
440: /**
441: * Checks any preconditions
442: *
443: *@param summary Description of Parameter
444: *@return Description of the Returned Value
445: */
446: protected boolean preconditions(FileSummary summary) {
447: return true;
448: }
449:
450: /**
451: * Gets the RenamingTransform
452: *
453: *@param refactoring the refactoring
454: *@param node the file summary to reference
455: *@param className the name of the class that is changing
456: */
457: protected abstract void addRenamingTransforms(
458: ComplexTransform refactoring, FileSummary node,
459: String className);
460:
461: /**
462: * Returns true if the package is the same
463: *
464: *@param node the current node
465: *@return true if the object is in the package
466: */
467: private boolean isSamePackage(FileSummary node) {
468: PackageSummary parent = (PackageSummary) node.getParent();
469: return parent.getName().equals(getCurrentPackage());
470: }
471:
472: /**
473: * Determines if the package matches
474: *
475: *@param nodePackageName The node's package
476: *@param mustUsePackage must it use the full package name
477: *@return true if the package matches
478: */
479: private boolean isMatchingPackage(String nodePackageName,
480: boolean mustUsePackage) {
481: boolean nullPackageName = (nodePackageName == null);
482: if (mustUsePackage && nullPackageName) {
483: return false;
484: }
485:
486: return nullPackageName
487: || (nodePackageName.equals(getCurrentPackage()));
488: }
489:
490: /**
491: * Determine if there was anything by that name imported
492: *
493: *@param node The file summary node
494: *@param data Data used for traversing the tree
495: *@return true if the data was imported
496: */
497: private boolean checkImports(FileSummary node, Object data) {
498: // Iterate over the import statements
499: Iterator iter = node.getImports();
500:
501: if (iter != null) {
502: while (iter.hasNext()) {
503: ImportSummary next = (ImportSummary) iter.next();
504: Object nodeReturn = next.accept(this , data);
505: if (((Boolean) nodeReturn).booleanValue()) {
506: return true;
507: }
508: }
509: }
510:
511: // Not found in import statements
512: return false;
513: }
514:
515: /**
516: * Description of the Method
517: *
518: *@param node the file summary node to traverse
519: *@param data data to help with the traversal
520: *@return return true if the types used the specified class
521: */
522: private boolean checkTypes(FileSummary node, Object data) {
523: Iterator iter = node.getTypes();
524:
525: if (iter != null) {
526: while (iter.hasNext()) {
527: TypeSummary next = (TypeSummary) iter.next();
528: if (((Boolean) next.accept(this , data)).booleanValue()) {
529: return true;
530: }
531: }
532: }
533:
534: return false;
535: }
536: }
|