001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
011: * o inline call that is used in a field initializer
012: * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
013: * o Allow 'this' constructor to be inlined
014: * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
015: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.code;
016:
017: import java.util.ArrayList;
018: import java.util.HashMap;
019: import java.util.List;
020: import java.util.Map;
021:
022: import org.eclipse.text.edits.MultiTextEdit;
023: import org.eclipse.text.edits.TextEdit;
024: import org.eclipse.text.edits.TextEditGroup;
025:
026: import org.eclipse.core.runtime.Assert;
027: import org.eclipse.core.runtime.CoreException;
028: import org.eclipse.core.runtime.IProgressMonitor;
029: import org.eclipse.core.runtime.OperationCanceledException;
030: import org.eclipse.core.runtime.SubProgressMonitor;
031:
032: import org.eclipse.core.resources.IFile;
033: import org.eclipse.core.resources.IResource;
034:
035: import org.eclipse.ltk.core.refactoring.Change;
036: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
037: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
038: import org.eclipse.ltk.core.refactoring.TextChange;
039: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
040: import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
041:
042: import org.eclipse.jdt.core.Flags;
043: import org.eclipse.jdt.core.IClassFile;
044: import org.eclipse.jdt.core.ICompilationUnit;
045: import org.eclipse.jdt.core.IJavaElement;
046: import org.eclipse.jdt.core.IJavaProject;
047: import org.eclipse.jdt.core.IMethod;
048: import org.eclipse.jdt.core.IType;
049: import org.eclipse.jdt.core.ITypeHierarchy;
050: import org.eclipse.jdt.core.ITypeRoot;
051: import org.eclipse.jdt.core.JavaModelException;
052: import org.eclipse.jdt.core.dom.AST;
053: import org.eclipse.jdt.core.dom.ASTNode;
054: import org.eclipse.jdt.core.dom.BodyDeclaration;
055: import org.eclipse.jdt.core.dom.CompilationUnit;
056: import org.eclipse.jdt.core.dom.ConstructorInvocation;
057: import org.eclipse.jdt.core.dom.ExpressionStatement;
058: import org.eclipse.jdt.core.dom.IMethodBinding;
059: import org.eclipse.jdt.core.dom.ITypeBinding;
060: import org.eclipse.jdt.core.dom.MethodDeclaration;
061: import org.eclipse.jdt.core.dom.MethodInvocation;
062: import org.eclipse.jdt.core.dom.Modifier;
063: import org.eclipse.jdt.core.dom.SuperMethodInvocation;
064: import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
065: import org.eclipse.jdt.core.refactoring.descriptors.InlineMethodDescriptor;
066: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
067:
068: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
069: import org.eclipse.jdt.internal.corext.refactoring.Checks;
070: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
071: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
072: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
073: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
074: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
075: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
076: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
077: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
078: import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
079: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
080: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
081: import org.eclipse.jdt.internal.corext.util.Messages;
082:
083: import org.eclipse.jdt.ui.JavaElementLabels;
084:
085: import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
086:
087: /*
088: * Open items:
089: * - generate import statements for newly generated local variable declarations.
090: * - forbid cases like foo(foo(10)) when inlining foo().
091: * - case ref.foo(); and we want to inline foo. Inline a method in a different context;
092: * - optimize code when the method to be inlined returns an argument and that one is
093: * assigned to a parameter again. No need for a separate local (important to be able
094: * to reverse extract method correctly).
095: */
096: public class InlineMethodRefactoring extends ScriptableRefactoring {
097:
098: private static final String ATTRIBUTE_MODE = "mode"; //$NON-NLS-1$
099: private static final String ATTRIBUTE_DELETE = "delete"; //$NON-NLS-1$
100:
101: public static class Mode {
102: private Mode() {
103: }
104:
105: public static final Mode INLINE_ALL = new Mode();
106: public static final Mode INLINE_SINGLE = new Mode();
107: }
108:
109: private ITypeRoot fInitialTypeRoot;
110: private ASTNode fInitialNode;
111: private TextChangeManager fChangeManager;
112: private SourceProvider fSourceProvider;
113: private TargetProvider fTargetProvider;
114: /**
115: * must never be true if fInitialUnit instanceof IClassFile
116: */
117: private boolean fDeleteSource;
118: private Mode fCurrentMode;
119: private Mode fInitialMode;
120: private int fSelectionStart;
121: private int fSelectionLength;
122:
123: private InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node,
124: int offset, int length) {
125: Assert.isNotNull(typeRoot);
126: Assert.isTrue(JavaElementUtil.isSourceAvailable(typeRoot));
127: Assert.isNotNull(node);
128: fInitialTypeRoot = typeRoot;
129: fInitialNode = node;
130: fSelectionStart = offset;
131: fSelectionLength = length;
132: }
133:
134: private InlineMethodRefactoring(ICompilationUnit unit,
135: MethodInvocation node, int offset, int length) {
136: this (unit, (ASTNode) node, offset, length);
137: fTargetProvider = TargetProvider.create(unit, node);
138: fInitialMode = fCurrentMode = Mode.INLINE_SINGLE;
139: fDeleteSource = false;
140: }
141:
142: private InlineMethodRefactoring(ICompilationUnit unit,
143: SuperMethodInvocation node, int offset, int length) {
144: this (unit, (ASTNode) node, offset, length);
145: fTargetProvider = TargetProvider.create(unit, node);
146: fInitialMode = fCurrentMode = Mode.INLINE_SINGLE;
147: fDeleteSource = false;
148: }
149:
150: private InlineMethodRefactoring(ICompilationUnit unit,
151: ConstructorInvocation node, int offset, int length) {
152: this (unit, (ASTNode) node, offset, length);
153: fTargetProvider = TargetProvider.create(unit, node);
154: fInitialMode = fCurrentMode = Mode.INLINE_SINGLE;
155: fDeleteSource = false;
156: }
157:
158: private InlineMethodRefactoring(ITypeRoot typeRoot,
159: MethodDeclaration node, int offset, int length) {
160: this (typeRoot, (ASTNode) node, offset, length);
161: fSourceProvider = new SourceProvider(typeRoot, node);
162: fTargetProvider = TargetProvider.create(node);
163: fInitialMode = fCurrentMode = Mode.INLINE_ALL;
164: fDeleteSource = canEnableDeleteSource();
165: }
166:
167: /**
168: * Creates a new inline method refactoring
169: * @param unit the compilation unit or class file
170: * @param node the compilation unit node
171: * @param selectionStart
172: * @param selectionLength
173: * @return returns the refactoring
174: */
175: public static InlineMethodRefactoring create(ITypeRoot unit,
176: CompilationUnit node, int selectionStart,
177: int selectionLength) {
178: ASTNode target = getTargetNode(unit, node, selectionStart,
179: selectionLength);
180: if (target == null)
181: return null;
182: if (target.getNodeType() == ASTNode.METHOD_DECLARATION) {
183:
184: return new InlineMethodRefactoring(unit,
185: (MethodDeclaration) target, selectionStart,
186: selectionLength);
187: } else {
188: ICompilationUnit cu = (ICompilationUnit) unit;
189: if (target.getNodeType() == ASTNode.METHOD_INVOCATION) {
190: return new InlineMethodRefactoring(cu,
191: (MethodInvocation) target, selectionStart,
192: selectionLength);
193: } else if (target.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) {
194: return new InlineMethodRefactoring(cu,
195: (SuperMethodInvocation) target, selectionStart,
196: selectionLength);
197: } else if (target.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) {
198: return new InlineMethodRefactoring(cu,
199: (ConstructorInvocation) target, selectionStart,
200: selectionLength);
201: }
202: }
203: return null;
204: }
205:
206: public String getName() {
207: return RefactoringCoreMessages.InlineMethodRefactoring_name;
208: }
209:
210: public boolean canEnableDeleteSource() {
211: return !(fSourceProvider.getTypeRoot() instanceof IClassFile);
212: }
213:
214: public boolean getDeleteSource() {
215: return fDeleteSource;
216: }
217:
218: public void setDeleteSource(boolean remove) {
219: if (remove)
220: Assert.isTrue(canEnableDeleteSource());
221: fDeleteSource = remove;
222: }
223:
224: public Mode getInitialMode() {
225: return fInitialMode;
226: }
227:
228: public RefactoringStatus setCurrentMode(Mode mode)
229: throws JavaModelException {
230: if (fCurrentMode == mode)
231: return new RefactoringStatus();
232: Assert.isTrue(getInitialMode() == Mode.INLINE_SINGLE);
233: fCurrentMode = mode;
234: if (mode == Mode.INLINE_SINGLE) {
235: if (fInitialNode instanceof MethodInvocation)
236: fTargetProvider = TargetProvider.create(
237: (ICompilationUnit) fInitialTypeRoot,
238: (MethodInvocation) fInitialNode);
239: else if (fInitialNode instanceof SuperMethodInvocation)
240: fTargetProvider = TargetProvider.create(
241: (ICompilationUnit) fInitialTypeRoot,
242: (SuperMethodInvocation) fInitialNode);
243: else if (fInitialNode instanceof ConstructorInvocation)
244: fTargetProvider = TargetProvider.create(
245: (ICompilationUnit) fInitialTypeRoot,
246: (ConstructorInvocation) fInitialNode);
247: else
248: throw new IllegalStateException(String
249: .valueOf(fInitialNode));
250: } else {
251: fTargetProvider = TargetProvider.create(fSourceProvider
252: .getDeclaration());
253: }
254: return fTargetProvider.checkActivation();
255: }
256:
257: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
258: throws CoreException {
259: RefactoringStatus result = new RefactoringStatus();
260: if (fSourceProvider == null
261: && Invocations.isInvocation(fInitialNode)) {
262: fSourceProvider = resolveSourceProvider(result,
263: fInitialTypeRoot, fInitialNode);
264: if (result.hasFatalError())
265: return result;
266: }
267: fTargetProvider.setSourceProvider(fSourceProvider);
268: result.merge(fSourceProvider.checkActivation());
269: result.merge(fTargetProvider.checkActivation());
270: return result;
271: }
272:
273: public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
274: throws CoreException {
275: pm.beginTask("", 20); //$NON-NLS-1$
276: fChangeManager = new TextChangeManager();
277: RefactoringStatus result = new RefactoringStatus();
278: fSourceProvider.initialize();
279: fTargetProvider.initialize();
280: pm
281: .setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_searching);
282: RefactoringStatus searchStatus = new RefactoringStatus();
283: ICompilationUnit[] units = fTargetProvider
284: .getAffectedCompilationUnits(searchStatus,
285: new SubProgressMonitor(pm, 1));
286: if (searchStatus.hasFatalError()) {
287: result.merge(searchStatus);
288: return result;
289: }
290: IFile[] filesToBeModified = getFilesToBeModified(units);
291: result.merge(Checks.validateModifiesFiles(filesToBeModified,
292: getValidationContext()));
293: if (result.hasFatalError())
294: return result;
295: result.merge(ResourceChangeChecker.checkFilesToBeChanged(
296: filesToBeModified, new SubProgressMonitor(pm, 1)));
297: checkOverridden(result, new SubProgressMonitor(pm, 4));
298: IProgressMonitor sub = new SubProgressMonitor(pm, 15);
299: sub.beginTask("", units.length * 3); //$NON-NLS-1$
300: for (int c = 0; c < units.length; c++) {
301: ICompilationUnit unit = units[c];
302: sub
303: .subTask(Messages
304: .format(
305: RefactoringCoreMessages.InlineMethodRefactoring_processing,
306: unit.getElementName()));
307: CallInliner inliner = null;
308: try {
309: boolean added = false;
310: MultiTextEdit root = new MultiTextEdit();
311: CompilationUnitChange change = (CompilationUnitChange) fChangeManager
312: .get(unit);
313: change.setEdit(root);
314: BodyDeclaration[] bodies = fTargetProvider
315: .getAffectedBodyDeclarations(unit,
316: new SubProgressMonitor(pm, 1));
317: if (bodies.length == 0)
318: continue;
319: inliner = new CallInliner(unit,
320: (CompilationUnit) bodies[0].getRoot(),
321: fSourceProvider);
322: for (int b = 0; b < bodies.length; b++) {
323: BodyDeclaration body = bodies[b];
324: inliner.initialize(body);
325: RefactoringStatus nestedInvocations = new RefactoringStatus();
326: ASTNode[] invocations = removeNestedCalls(
327: nestedInvocations, unit, fTargetProvider
328: .getInvocations(body,
329: new SubProgressMonitor(sub,
330: 2)));
331: for (int i = 0; i < invocations.length; i++) {
332: ASTNode invocation = invocations[i];
333: result.merge(inliner.initialize(invocation,
334: fTargetProvider.getStatusSeverity()));
335: if (result.hasFatalError())
336: break;
337: if (result.getSeverity() < fTargetProvider
338: .getStatusSeverity()) {
339: added = true;
340: TextEditGroup group = new TextEditGroup(
341: RefactoringCoreMessages.InlineMethodRefactoring_edit_inline);
342: change.addTextEditGroup(group);
343: result.merge(inliner.perform(group));
344: } else {
345: fDeleteSource = false;
346: }
347: }
348: // do this after we have inlined the method calls. We still want
349: // to generate the modifications.
350: if (!nestedInvocations.isOK()) {
351: result.merge(nestedInvocations);
352: fDeleteSource = false;
353: }
354: }
355: if (!added) {
356: fChangeManager.remove(unit);
357: } else {
358: root.addChild(inliner.getModifications());
359: ImportRewrite rewrite = inliner.getImportEdit();
360: if (rewrite.hasRecordedChanges()) {
361: TextEdit edit = rewrite.rewriteImports(null);
362: if (edit instanceof MultiTextEdit ? ((MultiTextEdit) edit)
363: .getChildrenSize() > 0
364: : true) {
365: root.addChild(edit);
366: change
367: .addTextEditGroup(new TextEditGroup(
368: RefactoringCoreMessages.InlineMethodRefactoring_edit_import,
369: new TextEdit[] { edit }));
370: }
371: }
372: }
373: } finally {
374: if (inliner != null)
375: inliner.dispose();
376: }
377: sub.worked(1);
378: if (sub.isCanceled())
379: throw new OperationCanceledException();
380: }
381: result.merge(searchStatus);
382: sub.done();
383: pm.done();
384: return result;
385: }
386:
387: public Change createChange(IProgressMonitor pm)
388: throws CoreException {
389: if (fDeleteSource && fCurrentMode == Mode.INLINE_ALL) {
390: TextChange change = fChangeManager
391: .get((ICompilationUnit) fSourceProvider
392: .getTypeRoot());
393: TextEdit delete = fSourceProvider.getDeleteEdit();
394: TextEditGroup description = new TextEditGroup(
395: RefactoringCoreMessages.InlineMethodRefactoring_edit_delete,
396: new TextEdit[] { delete });
397: TextEdit root = change.getEdit();
398: if (root != null) {
399: // TODO instead of finding the right insert position the call inliner should
400: // reuse the AST & rewriter of the source provide and we should rewrite the
401: // whole AST at the end. However, since recursive calls aren't allowed there
402: // shouldn't be a text edit overlap.
403: // root.addChild(delete);
404: TextChangeCompatibility.insert(root, delete);
405: } else {
406: change.setEdit(delete);
407: }
408: change.addTextEditGroup(description);
409: }
410: final Map arguments = new HashMap();
411: String project = null;
412: IJavaProject javaProject = fInitialTypeRoot.getJavaProject();
413: if (javaProject != null)
414: project = javaProject.getElementName();
415: int flags = RefactoringDescriptor.STRUCTURAL_CHANGE
416: | JavaRefactoringDescriptor.JAR_REFACTORING
417: | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
418: final IMethodBinding binding = fSourceProvider.getDeclaration()
419: .resolveBinding();
420: final ITypeBinding declaring = binding.getDeclaringClass();
421: if (!Modifier.isPrivate(binding.getModifiers()))
422: flags |= RefactoringDescriptor.MULTI_CHANGE;
423: final String description = Messages
424: .format(
425: RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description_short,
426: binding.getName());
427: final String header = Messages
428: .format(
429: RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description,
430: new String[] {
431: BindingLabelProvider
432: .getBindingLabel(
433: binding,
434: JavaElementLabels.ALL_FULLY_QUALIFIED),
435: BindingLabelProvider
436: .getBindingLabel(
437: declaring,
438: JavaElementLabels.ALL_FULLY_QUALIFIED) });
439: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
440: project, this , header);
441: comment
442: .addSetting(Messages
443: .format(
444: RefactoringCoreMessages.InlineMethodRefactoring_original_pattern,
445: BindingLabelProvider
446: .getBindingLabel(
447: binding,
448: JavaElementLabels.ALL_FULLY_QUALIFIED)));
449: if (fDeleteSource)
450: comment
451: .addSetting(RefactoringCoreMessages.InlineMethodRefactoring_remove_method);
452: if (fCurrentMode == Mode.INLINE_ALL)
453: comment
454: .addSetting(RefactoringCoreMessages.InlineMethodRefactoring_replace_references);
455: final InlineMethodDescriptor descriptor = new InlineMethodDescriptor(
456: project, description, comment.asString(), arguments,
457: flags);
458: arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT,
459: JavaRefactoringDescriptorUtil.elementToHandle(project,
460: fInitialTypeRoot));
461: arguments
462: .put(
463: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION,
464: new Integer(fSelectionStart).toString()
465: + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
466: arguments.put(ATTRIBUTE_DELETE, Boolean.valueOf(fDeleteSource)
467: .toString());
468: arguments.put(ATTRIBUTE_MODE, new Integer(
469: fCurrentMode == Mode.INLINE_ALL ? 1 : 0).toString());
470: return new DynamicValidationRefactoringChange(
471: descriptor,
472: RefactoringCoreMessages.InlineMethodRefactoring_edit_inlineCall,
473: fChangeManager.getAllChanges());
474: }
475:
476: private static SourceProvider resolveSourceProvider(
477: RefactoringStatus status, ITypeRoot typeRoot,
478: ASTNode invocation) throws JavaModelException {
479: CompilationUnit root = (CompilationUnit) invocation.getRoot();
480: IMethodBinding methodBinding = Invocations
481: .resolveBinding(invocation);
482: if (methodBinding == null) {
483: status
484: .addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration);
485: return null;
486: }
487: MethodDeclaration declaration = (MethodDeclaration) root
488: .findDeclaringNode(methodBinding);
489: if (declaration != null) {
490: return new SourceProvider(typeRoot, declaration);
491: }
492: IMethod method = (IMethod) methodBinding.getJavaElement();
493: if (method != null) {
494: CompilationUnit methodDeclarationAstRoot;
495: ICompilationUnit methodCu = method.getCompilationUnit();
496: if (methodCu != null) {
497: methodDeclarationAstRoot = new RefactoringASTParser(
498: AST.JLS3).parse(methodCu, true);
499: } else {
500: IClassFile classFile = method.getClassFile();
501: if (!JavaElementUtil.isSourceAvailable(classFile)) {
502: String methodLabel = JavaElementLabels
503: .getTextLabel(
504: method,
505: JavaElementLabels.M_FULLY_QUALIFIED
506: | JavaElementLabels.M_PARAMETER_TYPES);
507: status
508: .addFatalError(Messages
509: .format(
510: RefactoringCoreMessages.InlineMethodRefactoring_error_classFile,
511: methodLabel));
512: return null;
513: }
514: methodDeclarationAstRoot = new RefactoringASTParser(
515: AST.JLS3).parse(classFile, true);
516: }
517: ASTNode node = methodDeclarationAstRoot
518: .findDeclaringNode(methodBinding
519: .getMethodDeclaration().getKey());
520: if (node instanceof MethodDeclaration) {
521: return new SourceProvider(methodDeclarationAstRoot
522: .getTypeRoot(), (MethodDeclaration) node);
523: }
524: }
525: status
526: .addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration);
527: return null;
528: }
529:
530: private static ASTNode getTargetNode(ITypeRoot typeRoot,
531: CompilationUnit root, int offset, int length) {
532: ASTNode node = null;
533: try {
534: node = checkNode(NodeFinder.perform(root, offset, length,
535: typeRoot), typeRoot);
536: } catch (JavaModelException e) {
537: // Do nothing
538: }
539: if (node != null)
540: return node;
541: return checkNode(NodeFinder.perform(root, offset, length),
542: typeRoot);
543: }
544:
545: private static ASTNode checkNode(ASTNode node, IJavaElement unit) {
546: if (node == null)
547: return null;
548: if (node.getNodeType() == ASTNode.SIMPLE_NAME) {
549: node = node.getParent();
550: } else if (node.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
551: node = ((ExpressionStatement) node).getExpression();
552: }
553: switch (node.getNodeType()) {
554: case ASTNode.METHOD_DECLARATION:
555: return node;
556: case ASTNode.METHOD_INVOCATION:
557: case ASTNode.SUPER_METHOD_INVOCATION:
558: case ASTNode.CONSTRUCTOR_INVOCATION:
559: return unit instanceof ICompilationUnit ? node : null; // don't start on invocations in binary
560: }
561: return null;
562: }
563:
564: private IFile[] getFilesToBeModified(ICompilationUnit[] units) {
565: List result = new ArrayList(units.length + 1);
566: IFile file;
567: for (int i = 0; i < units.length; i++) {
568: file = getFile(units[i]);
569: if (file != null)
570: result.add(file);
571: }
572: if (fDeleteSource) {
573: file = getFile((ICompilationUnit) fSourceProvider
574: .getTypeRoot());
575: if (file != null && !result.contains(file))
576: result.add(file);
577: }
578: return (IFile[]) result.toArray(new IFile[result.size()]);
579: }
580:
581: private IFile getFile(ICompilationUnit unit) {
582: unit = unit.getPrimary();
583: IResource resource = unit.getResource();
584: if (resource != null && resource.getType() == IResource.FILE)
585: return (IFile) resource;
586: return null;
587: }
588:
589: private void checkOverridden(RefactoringStatus status,
590: IProgressMonitor pm) throws JavaModelException {
591: pm.beginTask("", 9); //$NON-NLS-1$
592: pm
593: .setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden);
594: MethodDeclaration decl = fSourceProvider.getDeclaration();
595: IMethod method = (IMethod) decl.resolveBinding()
596: .getJavaElement();
597: if (method == null || Flags.isPrivate(method.getFlags())) {
598: pm.worked(8);
599: return;
600: }
601: IType type = method.getDeclaringType();
602: ITypeHierarchy hierarchy = type
603: .newTypeHierarchy(new SubProgressMonitor(pm, 6));
604: checkSubTypes(status, method, hierarchy.getAllSubtypes(type),
605: new SubProgressMonitor(pm, 1));
606: checkSuperClasses(status, method, hierarchy
607: .getAllSuperclasses(type),
608: new SubProgressMonitor(pm, 1));
609: checkSuperInterfaces(status, method, hierarchy
610: .getAllSuperInterfaces(type), new SubProgressMonitor(
611: pm, 1));
612: pm.setTaskName(""); //$NON-NLS-1$
613: }
614:
615: private void checkSubTypes(RefactoringStatus result,
616: IMethod method, IType[] types, IProgressMonitor pm) {
617: checkTypes(
618: result,
619: method,
620: types,
621: RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden_error,
622: pm);
623: }
624:
625: private void checkSuperClasses(RefactoringStatus result,
626: IMethod method, IType[] types, IProgressMonitor pm) {
627: checkTypes(
628: result,
629: method,
630: types,
631: RefactoringCoreMessages.InlineMethodRefactoring_checking_overrides_error,
632: pm);
633: }
634:
635: private void checkSuperInterfaces(RefactoringStatus result,
636: IMethod method, IType[] types, IProgressMonitor pm) {
637: checkTypes(
638: result,
639: method,
640: types,
641: RefactoringCoreMessages.InlineMethodRefactoring_checking_implements _error,
642: pm);
643: }
644:
645: private void checkTypes(RefactoringStatus result, IMethod method,
646: IType[] types, String key, IProgressMonitor pm) {
647: pm.beginTask("", types.length); //$NON-NLS-1$
648: for (int i = 0; i < types.length; i++) {
649: pm.worked(1);
650: IMethod[] overridden = types[i].findMethods(method);
651: if (overridden != null && overridden.length > 0) {
652: result.addError(Messages.format(key, types[i]
653: .getElementName()), JavaStatusContext
654: .create(overridden[0]));
655: }
656: }
657: }
658:
659: private ASTNode[] removeNestedCalls(RefactoringStatus status,
660: ICompilationUnit unit, ASTNode[] invocations) {
661: if (invocations.length <= 1)
662: return invocations;
663: ASTNode[] parents = new ASTNode[invocations.length];
664: for (int i = 0; i < invocations.length; i++) {
665: parents[i] = invocations[i].getParent();
666: }
667: for (int i = 0; i < invocations.length; i++) {
668: removeNestedCalls(status, unit, parents, invocations, i);
669: }
670: List result = new ArrayList();
671: for (int i = 0; i < invocations.length; i++) {
672: if (invocations[i] != null)
673: result.add(invocations[i]);
674: }
675: return (ASTNode[]) result.toArray(new ASTNode[result.size()]);
676: }
677:
678: private void removeNestedCalls(RefactoringStatus status,
679: ICompilationUnit unit, ASTNode[] parents,
680: ASTNode[] invocations, int index) {
681: ASTNode invocation = invocations[index];
682: for (int i = 0; i < parents.length; i++) {
683: ASTNode parent = parents[i];
684: while (parent != null) {
685: if (parent == invocation) {
686: status
687: .addError(
688: RefactoringCoreMessages.InlineMethodRefactoring_nestedInvocation,
689: JavaStatusContext.create(unit,
690: parent));
691: invocations[index] = null;
692: }
693: parent = parent.getParent();
694: }
695: }
696: }
697:
698: public RefactoringStatus initialize(
699: final RefactoringArguments arguments) {
700: if (arguments instanceof JavaRefactoringArguments) {
701: final JavaRefactoringArguments generic = (JavaRefactoringArguments) arguments;
702: final String delete = generic
703: .getAttribute(ATTRIBUTE_DELETE);
704: if (delete != null) {
705: fDeleteSource = Boolean.valueOf(delete).booleanValue();
706: } else
707: return RefactoringStatus
708: .createFatalErrorStatus(Messages
709: .format(
710: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
711: ATTRIBUTE_DELETE));
712: final String value = generic.getAttribute(ATTRIBUTE_MODE);
713: if (value != null && !"".equals(value)) {//$NON-NLS-1$
714: int mode = 0;
715: try {
716: mode = Integer.parseInt(value);
717: } catch (NumberFormatException exception) {
718: return RefactoringStatus
719: .createFatalErrorStatus(Messages
720: .format(
721: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
722: ATTRIBUTE_MODE));
723: }
724: try {
725: setCurrentMode(mode == 1 ? Mode.INLINE_ALL
726: : Mode.INLINE_SINGLE);
727: } catch (JavaModelException exception) {
728: return RefactoringStatus
729: .createFatalErrorStatus(exception
730: .getLocalizedMessage());
731: }
732: }
733: } else
734: return RefactoringStatus
735: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
736: return new RefactoringStatus();
737: }
738: }
|