content: /*******************************************************************************
   * Copyright (c) 2000, 2009 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
   *       o inline call that is used in a field initializer 
   *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
   *       o Allow 'this' constructor to be inlined  
   *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.refactoring.code;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.OperationCanceledException;
  import org.eclipse.core.runtime.SubProgressMonitor;
  import org.eclipse.ltk.core.refactoring.Change;
  import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
  import org.eclipse.ltk.core.refactoring.RefactoringStatus;
  import org.eclipse.ltk.core.refactoring.TextChange;
  import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
  import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
  import org.eclipse.text.edits.MultiTextEdit;
  import org.eclipse.text.edits.TextEdit;
  import org.eclipse.text.edits.TextEditGroup;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IClassFile;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.ITypeHierarchy;
  import org.eclipse.wst.jsdt.core.ITypeRoot;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.BodyDeclaration;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.ExpressionStatement;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.Modifier;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings;
  import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor;
  import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.TextChangeCompatibility;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavaElementUtil;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextChangeManager;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  
  /*
   * Open items:
   *  - generate import statements for newly generated local variable declarations.
   *  - forbid cases like foo(foo(10)) when inlining foo().
   *  - case ref.foo(); and we want to inline foo. Inline a method in a different context;
   *  - optimize code when the method to be inlined returns an argument and that one is
   *    assigned to a parameter again. No need for a separate local (important to be able
   *    to reverse extract method correctly).
   */
  public class InlineMethodRefactoring extends ScriptableRefactoring {
  
  	private static final String ATTRIBUTE_MODE= "mode"; //$NON-NLS-1$
  	private static final String ATTRIBUTE_DELETE= "delete";	 //$NON-NLS-1$
  
  	public static class Mode {
  		private Mode() {
  		}
  		public static final Mode INLINE_ALL= new Mode();
  		public static final Mode INLINE_SINGLE= new Mode();
  	}
  
  	private ITypeRoot fInitialTypeRoot;
  	private ASTNode fInitialNode;
  	private TextChangeManager fChangeManager;
  	private SourceProvider fSourceProvider;
  	private TargetProvider fTargetProvider;
  	/**
  	 * must never be true if fInitialUnit instanceof IClassFile
  	 */
  	private boolean fDeleteSource;
  	private Mode fCurrentMode;
  	private Mode fInitialMode;
  	private int fSelectionStart;
  	private int fSelectionLength;
  
  	private InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node, int offset, int length) {
  		Assert.isNotNull(typeRoot);
  		Assert.isTrue(JavaElementUtil.isSourceAvailable(typeRoot));
  		Assert.isNotNull(node);
  		fInitialTypeRoot= typeRoot;
  		fInitialNode= node;
  		fSelectionStart= offset;
  		fSelectionLength= length;
  	}
  
  	private InlineMethodRefactoring(IJavaScriptUnit unit, FunctionInvocation node, int offset, int length) {
  		this(unit, (ASTNode)node, offset, length);
  		fTargetProvider= TargetProvider.create(unit, node);
  		fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
  		fDeleteSource= false;
  	}
  
  	private InlineMethodRefactoring(IJavaScriptUnit unit, SuperMethodInvocation node, int offset, int length) {
  		this(unit, (ASTNode)node, offset, length);
  		fTargetProvider= TargetProvider.create(unit, node);
  		fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
  		fDeleteSource= false;
  	}
  
  	private InlineMethodRefactoring(IJavaScriptUnit unit, ConstructorInvocation node, int offset, int length) {
  		this(unit, (ASTNode)node, offset, length);
  		fTargetProvider= TargetProvider.create(unit, node);
  		fInitialMode= fCurrentMode= Mode.INLINE_SINGLE;
  		fDeleteSource= false;
  	}
  
  	private InlineMethodRefactoring(ITypeRoot typeRoot, FunctionDeclaration node, int offset, int length) {
  		this(typeRoot, (ASTNode)node, offset, length);
  		fSourceProvider= new SourceProvider(typeRoot, node);
  		fTargetProvider= TargetProvider.create(node);
  		fInitialMode= fCurrentMode= Mode.INLINE_ALL;
  		fDeleteSource= canEnableDeleteSource();
  	}
  	
  	/**
  	 * Creates a new inline method refactoring
  	 * @param unit the compilation unit or class file
  	 * @param node the compilation unit node
  	 * @param selectionStart
  	 * @param selectionLength
  	 */
  	public static InlineMethodRefactoring create(ITypeRoot unit, JavaScriptUnit node, int selectionStart, int selectionLength) {
  		ASTNode target= getTargetNode(unit, node, selectionStart, selectionLength);
  		if (target == null)
  			return null;
  		if (target.getNodeType() == ASTNode.FUNCTION_DECLARATION) {
  			
  			return new InlineMethodRefactoring(unit, (FunctionDeclaration)target, selectionStart, selectionLength);
  		} else {
  			IJavaScriptUnit cu= (IJavaScriptUnit) unit;
  			if (target.getNodeType() == ASTNode.FUNCTION_INVOCATION) {
  				return new InlineMethodRefactoring(cu, (FunctionInvocation)target, selectionStart, selectionLength);
  			} else if (target.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) {
  				return new InlineMethodRefactoring(cu, (SuperMethodInvocation)target, selectionStart, selectionLength);
  			} else if (target.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) {
  				return new InlineMethodRefactoring(cu, (ConstructorInvocation)target, selectionStart, selectionLength);
  			}
  		}
  		return null;
  	}
  	
  	public String getName() {
  		return RefactoringCoreMessages.InlineMethodRefactoring_name; 
  	}
  	
  	public boolean canEnableDeleteSource() {
  		return ! (fSourceProvider.getTypeRoot() instanceof IClassFile);
  	}
  	
  	public boolean getDeleteSource() {
  		return fDeleteSource;
  	}
  
  	public void setDeleteSource(boolean remove) {
  		if (remove)
  			Assert.isTrue(canEnableDeleteSource());
  		fDeleteSource= remove;
  	}
  	
  	public Mode getInitialMode() {
  		return fInitialMode;
  	}
  	
  	public RefactoringStatus setCurrentMode(Mode mode) throws JavaScriptModelException {
  		if (fCurrentMode == mode)
  			return new RefactoringStatus();
  		Assert.isTrue(getInitialMode() == Mode.INLINE_SINGLE);
  		fCurrentMode= mode;
  		if (mode == Mode.INLINE_SINGLE) {
  			if (fInitialNode instanceof FunctionInvocation)
  				fTargetProvider= TargetProvider.create((IJavaScriptUnit) fInitialTypeRoot, (FunctionInvocation)fInitialNode);
  			else if (fInitialNode instanceof SuperMethodInvocation)
  				fTargetProvider= TargetProvider.create((IJavaScriptUnit) fInitialTypeRoot, (SuperMethodInvocation)fInitialNode);
  			else if (fInitialNode instanceof ConstructorInvocation)
  				fTargetProvider= TargetProvider.create((IJavaScriptUnit) fInitialTypeRoot, (ConstructorInvocation)fInitialNode);
  			else
  				throw new IllegalStateException(String.valueOf(fInitialNode));
  		} else {
  			fTargetProvider= TargetProvider.create(fSourceProvider.getDeclaration());
  		}
  		return fTargetProvider.checkActivation();
  	}
  	
  	public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
  		RefactoringStatus result= new RefactoringStatus();
  		if (fSourceProvider == null && Invocations.isInvocation(fInitialNode)) {
  			fSourceProvider= resolveSourceProvider(result, fInitialTypeRoot, fInitialNode);
  			if (result.hasFatalError())
  				return result;
  		}
  		fTargetProvider.setSourceProvider(fSourceProvider);
  		result.merge(fSourceProvider.checkActivation());
  		result.merge(fTargetProvider.checkActivation());
  		return result;
  	}
  	
  	public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
  		pm.beginTask("", 20); //$NON-NLS-1$
  		fChangeManager= new TextChangeManager();
  		RefactoringStatus result= new RefactoringStatus();
  		fSourceProvider.initialize();
  		fTargetProvider.initialize();
  		pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_searching);
  		RefactoringStatus searchStatus= new RefactoringStatus();
  		IJavaScriptUnit[] units= fTargetProvider.getAffectedCompilationUnits(searchStatus, new SubProgressMonitor(pm, 1));
  		if (searchStatus.hasFatalError()) {
  			result.merge(searchStatus);
  			return result;
  		}
  		IFile[] filesToBeModified= getFilesToBeModified(units);
  		result.merge(Checks.validateModifiesFiles(filesToBeModified, getValidationContext()));
  		if (result.hasFatalError())
  			return result;
  		result.merge(ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1)));
  		checkOverridden(result, new SubProgressMonitor(pm, 4));
  		IProgressMonitor sub= new SubProgressMonitor(pm, 15);
  		sub.beginTask("", units.length * 3); //$NON-NLS-1$
  		for (int c= 0; c < units.length; c++) {
  			IJavaScriptUnit unit= units[c];
  			sub.subTask(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_processing,  unit.getElementName())); 
  			CallInliner inliner= null;
  			try {
  				boolean added= false;
  				MultiTextEdit root= new MultiTextEdit();
  				CompilationUnitChange change= (CompilationUnitChange)fChangeManager.get(unit);
  				change.setEdit(root);
  				BodyDeclaration[] bodies= fTargetProvider.getAffectedBodyDeclarations(unit, new SubProgressMonitor(pm, 1));
  				if (bodies.length == 0)
  					continue;
  				inliner= new CallInliner(unit, (JavaScriptUnit) bodies[0].getRoot(), fSourceProvider);
  				for (int b= 0; b < bodies.length; b++) {
  					BodyDeclaration body= bodies[b];
  					inliner.initialize(body);
  					RefactoringStatus nestedInvocations= new RefactoringStatus();
  					ASTNode[] invocations= removeNestedCalls(nestedInvocations, unit, 
  						fTargetProvider.getInvocations(body, new SubProgressMonitor(sub, 2)));
  					for (int i= 0; i < invocations.length; i++) {
  						ASTNode invocation= invocations[i];
  						result.merge(inliner.initialize(invocation, fTargetProvider.getStatusSeverity()));
  						if (result.hasFatalError())
  							break;
  						if (result.getSeverity() < fTargetProvider.getStatusSeverity()) {
  							added= true;
  							TextEditGroup group= new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_inline); 
  							change.addTextEditGroup(group);
  							result.merge(inliner.perform(group)); 
  						} else {
  							fDeleteSource= false;
  						}
  					}
  					// do this after we have inlined the method calls. We still want
  					// to generate the modifications.
  					if (!nestedInvocations.isOK()) {
  						result.merge(nestedInvocations);
  						fDeleteSource= false;
  					}
  				}
  				if (!added) {
  					fChangeManager.remove(unit);
  				} else {
  					root.addChild(inliner.getModifications());
  					ImportRewrite rewrite= inliner.getImportEdit();
  					if (rewrite.hasRecordedChanges()) {
  						TextEdit edit= rewrite.rewriteImports(null);
  						if (edit instanceof MultiTextEdit ? ((MultiTextEdit)edit).getChildrenSize() > 0 : true) {
  							root.addChild(edit);
  							change.addTextEditGroup(
  								new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_import, new TextEdit[] {edit})); 
  						}
  					}
  				}
  			} finally {
  				if (inliner != null)
  					inliner.dispose();
  			}
  			sub.worked(1);
  			if (sub.isCanceled())
  				throw new OperationCanceledException();
  		}
  		result.merge(searchStatus);
  		sub.done();
  		pm.done();
  		return result;
  	}
  
  	public Change createChange(IProgressMonitor pm) throws CoreException {
  		if (fDeleteSource && fCurrentMode == Mode.INLINE_ALL) {
  			TextChange change= fChangeManager.get((IJavaScriptUnit) fSourceProvider.getTypeRoot());
  			TextEdit delete= fSourceProvider.getDeleteEdit();
  			TextEditGroup description= new TextEditGroup(
  				RefactoringCoreMessages.InlineMethodRefactoring_edit_delete, new TextEdit[] { delete }); 
  			TextEdit root= change.getEdit();
  			if (root != null) {
  				// TODO instead of finding the right insert position the call inliner should
  				// reuse the AST & rewriter of the source provide and we should rewrite the
  				// whole AST at the end. However, since recursive calls aren't allowed there
  				// shouldn't be a text edit overlap.
  				// root.addChild(delete);
  				TextChangeCompatibility.insert(root, delete);
  			} else {
  				change.setEdit(delete);
  			}
  			change.addTextEditGroup(description);
  		}
  		final Map arguments= new HashMap();
  		String project= null;
  		IJavaScriptProject javaProject= fInitialTypeRoot.getJavaScriptProject();
  		if (javaProject != null)
  			project= javaProject.getElementName();
  		int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaScriptRefactoringDescriptor.JAR_REFACTORING | JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
  		final IFunctionBinding binding= fSourceProvider.getDeclaration().resolveBinding();
  		final ITypeBinding declaring= binding.getDeclaringClass();
  		if (!Modifier.isPrivate(binding.getModifiers()))
  			flags|= RefactoringDescriptor.MULTI_CHANGE;
  		final String description= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description_short, binding.getName());
  		final String header= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(declaring, JavaScriptElementLabels.ALL_FULLY_QUALIFIED)});
  		final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
  		comment.addSetting(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.ALL_FULLY_QUALIFIED)));
  		if (fDeleteSource)
  			comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_remove_method);
  		if (fCurrentMode == Mode.INLINE_ALL)
  			comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_replace_references);
  		final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.INLINE_METHOD, project, description, comment.asString(), arguments, flags);
  		arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fInitialTypeRoot));
  		arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$
  		arguments.put(ATTRIBUTE_DELETE, Boolean.valueOf(fDeleteSource).toString());
  		arguments.put(ATTRIBUTE_MODE, Integer.valueOf(fCurrentMode == Mode.INLINE_ALL ? 1 : 0).toString());
  		return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineMethodRefactoring_edit_inlineCall, fChangeManager.getAllChanges());
  	}
  	
  	private static SourceProvider resolveSourceProvider(RefactoringStatus status, ITypeRoot typeRoot, ASTNode invocation) throws JavaScriptModelException {
  		JavaScriptUnit root= (JavaScriptUnit)invocation.getRoot();
  		IFunctionBinding methodBinding= Invocations.resolveBinding(invocation);
  		if (methodBinding == null) {
  			status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); 
  			return null;
  		}
  		FunctionDeclaration declaration= (FunctionDeclaration)root.findDeclaringNode(methodBinding);
  		if (declaration != null) {
  			return new SourceProvider(typeRoot, declaration);
  		}
  		IFunction method= (IFunction)methodBinding.getJavaElement();
  		if (method != null) {
  			JavaScriptUnit methodDeclarationAstRoot;
  			IJavaScriptUnit methodCu= method.getJavaScriptUnit();
  			if (methodCu != null) {
  				methodDeclarationAstRoot= new RefactoringASTParser(AST.JLS3).parse(methodCu, true);
  			} else {
  				IClassFile classFile= method.getClassFile();
  				if (! JavaElementUtil.isSourceAvailable(classFile)) {
  					String methodLabel= JavaScriptElementLabels.getTextLabel(method, JavaScriptElementLabels.M_FULLY_QUALIFIED | JavaScriptElementLabels.M_PARAMETER_TYPES);
  					status.addFatalError(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_error_classFile, methodLabel)); 
  					return null;
  				}				
  				methodDeclarationAstRoot= new RefactoringASTParser(AST.JLS3).parse(classFile, true);
  			}
  			ASTNode node= methodDeclarationAstRoot.findDeclaringNode(methodBinding.getMethodDeclaration().getKey());
  			if (node instanceof FunctionDeclaration) {
  				return new SourceProvider(methodDeclarationAstRoot.getTypeRoot(), (FunctionDeclaration) node);
  			}
  		}
  		status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); 
  		return null;
  	}
  	
  	private static ASTNode getTargetNode(ITypeRoot typeRoot, JavaScriptUnit root, int offset, int length) {
  		ASTNode node= null;
  		try {
  			node= checkNode(NodeFinder.perform(root, offset, length, typeRoot), typeRoot);
  		} catch(JavaScriptModelException e) {
  			// Do nothing
  		}
  		if (node != null)
  			return node;
  		return checkNode(NodeFinder.perform(root, offset, length), typeRoot);
  	}
  
  	private static ASTNode checkNode(ASTNode node, IJavaScriptElement unit) {
  		if (node == null)
  			return null;
  		if (node.getNodeType() == ASTNode.SIMPLE_NAME) {
  			node= node.getParent();
  		} else if (node.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
  			node= ((ExpressionStatement)node).getExpression();
  		}
  		switch(node.getNodeType()) {
  			case ASTNode.FUNCTION_DECLARATION:
  					return node;
  			case ASTNode.FUNCTION_INVOCATION:
  			case ASTNode.SUPER_METHOD_INVOCATION:
  			case ASTNode.CONSTRUCTOR_INVOCATION:
  				return unit instanceof IJavaScriptUnit ? node : null; // don't start on invocations in binary
  		}
  		return null;
  	}
  	
  	private IFile[] getFilesToBeModified(IJavaScriptUnit[] units) {
  		List result= new ArrayList(units.length + 1);
  		IFile file;
  		for (int i= 0; i < units.length; i++) {
  			file= getFile(units[i]);
  			if (file != null)
  				result.add(file);
  		}
  		if (fDeleteSource) {
  			file= getFile((IJavaScriptUnit) fSourceProvider.getTypeRoot());
  			if (file != null && !result.contains(file))
  				result.add(file);
  		}
  		return (IFile[])result.toArray(new IFile[result.size()]);
  	}
  	
  	private IFile getFile(IJavaScriptUnit unit) {
  		unit= unit.getPrimary();
  		IResource resource= unit.getResource();
  		if (resource != null && resource.getType() == IResource.FILE)
  			return (IFile)resource;
  		return null;
  	}
  	
  	private void checkOverridden(RefactoringStatus status, IProgressMonitor pm) throws JavaScriptModelException {
  		pm.beginTask("", 9); //$NON-NLS-1$
  		pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden); 
  		FunctionDeclaration decl= fSourceProvider.getDeclaration();
  		IFunction method= (IFunction) decl.resolveBinding().getJavaElement();
  		if (method == null || Flags.isPrivate(method.getFlags())) {
  			pm.worked(8);
  			return;
  		}
  		IType type= method.getDeclaringType();
  		if (type==null) {
  			pm.worked(8);
  			return;
  		}
  		ITypeHierarchy hierarchy= type.newTypeHierarchy(new SubProgressMonitor(pm, 6));
  		checkSubTypes(status, method, hierarchy.getAllSubtypes(type), new SubProgressMonitor(pm, 1));
  		checkSuperClasses(status, method, hierarchy.getAllSuperclasses(type), new SubProgressMonitor(pm, 1));
  		pm.setTaskName(""); //$NON-NLS-1$
  	}
  
  	private void checkSubTypes(RefactoringStatus result, IFunction method, IType[] types, IProgressMonitor pm) {
  		checkTypes(
  			result, method, types, 
  			RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden_error,
  			pm);
  	}
  	
  	private void checkSuperClasses(RefactoringStatus result, IFunction method, IType[] types, IProgressMonitor pm) {
  		checkTypes(
  			result, method, types, 
  			RefactoringCoreMessages.InlineMethodRefactoring_checking_overrides_error,
  			pm);
  	}
  	private void checkTypes(RefactoringStatus result, IFunction method, IType[] types, String key, IProgressMonitor pm) {
  		pm.beginTask("", types.length); //$NON-NLS-1$
  		for (int i= 0; i < types.length; i++) {
  			pm.worked(1);
  			IFunction[] overridden= types[i].findMethods(method);
  			if (overridden != null && overridden.length > 0) {
  				result.addError(
  					Messages.format(key, types[i].getElementName()), 
  					JavaStatusContext.create(overridden[0]));
  			}
  		}
  	}
  	
  	private ASTNode[] removeNestedCalls(RefactoringStatus status, IJavaScriptUnit unit, ASTNode[] invocations) {
  		if (invocations.length <= 1)
  			return invocations;
  		ASTNode[] parents= new ASTNode[invocations.length];
  		for (int i= 0; i < invocations.length; i++) {
  			parents[i]= invocations[i].getParent();
  		}
  		for (int i= 0; i < invocations.length; i++) {
  			removeNestedCalls(status, unit, parents, invocations, i);
  		}
  		List result= new ArrayList();
  		for (int i= 0; i < invocations.length; i++) {
  			if (invocations[i] != null)
  				result.add(invocations[i]);
  		}
  		return (ASTNode[])result.toArray(new ASTNode[result.size()]);
  	}
  	
  	private void removeNestedCalls(RefactoringStatus status, IJavaScriptUnit unit, ASTNode[] parents, ASTNode[] invocations, int index) {
  		ASTNode invocation= invocations[index];
  		for (int i= 0; i < parents.length; i++) {
  			ASTNode parent= parents[i];
  			while (parent != null) {
  				if (parent == invocation) {
  					status.addError(RefactoringCoreMessages.InlineMethodRefactoring_nestedInvocation,  
  						JavaStatusContext.create(unit, parent));
  					invocations[index]= null;
  				}
  				parent= parent.getParent();
  			}
  		}
  	}
  
  	public RefactoringStatus initialize(final RefactoringArguments arguments) {
  		if (arguments instanceof JavaRefactoringArguments) {
  			final JavaRefactoringArguments generic= (JavaRefactoringArguments) arguments;
  			final String delete= generic.getAttribute(ATTRIBUTE_DELETE);
  			if (delete != null) {
  				fDeleteSource= Boolean.valueOf(delete).booleanValue();
  			} else
  				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELETE));
  			final String value= generic.getAttribute(ATTRIBUTE_MODE);
  			if (value != null && !"".equals(value)) {//$NON-NLS-1$
  				int mode= 0;
  				try {
  					mode= Integer.parseInt(value);
  				} catch (NumberFormatException exception) {
  					return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_MODE));
  				}
  				try {
  					setCurrentMode(mode == 1 ? Mode.INLINE_ALL : Mode.INLINE_SINGLE);
  				} catch (JavaScriptModelException exception) {
  					return RefactoringStatus.createFatalErrorStatus(exception.getLocalizedMessage());
  				}
  			}
  		} else
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
  		return new RefactoringStatus();
  	}
  }
  