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
   *     Felix Pahl (fpahl@web.de) - contributed fix for:
   *       o introduce parameter throws NPE if there are compiler errors
   *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48325)
   *******************************************************************************/
  
  package org.eclipse.wst.jsdt.internal.corext.refactoring.code;
  
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.StringTokenizer;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.SubProgressMonitor;
  import org.eclipse.jface.text.TextSelection;
  import org.eclipse.ltk.core.refactoring.Change;
  import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
  import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
  import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
  import org.eclipse.ltk.core.refactoring.RefactoringStatus;
  import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
  import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ArrayInitializer;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  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.Name;
  import org.eclipse.wst.jsdt.core.dom.NullLiteral;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings;
  import org.eclipse.wst.jsdt.internal.corext.Corext;
  import org.eclipse.wst.jsdt.internal.corext.SourceRange;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.dom.ScopeAnalyzer;
  import org.eclipse.wst.jsdt.internal.corext.dom.fragments.ASTFragmentFactory;
  import org.eclipse.wst.jsdt.internal.corext.dom.fragments.IASTFragment;
  import org.eclipse.wst.jsdt.internal.corext.dom.fragments.IExpressionFragment;
  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.ParameterInfo;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringAvailabilityTester;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.base.RefactoringStatusCodes;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.RefactoringDescriptorChange;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.BodyUpdater;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.ChangeSignatureRefactoring;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IDelegateUpdating;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.actions.SelectionConverter;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  
  public class IntroduceParameterRefactoring extends ScriptableRefactoring implements IDelegateUpdating {
  
  	private static final String ATTRIBUTE_ARGUMENT= "argument"; //$NON-NLS-1$
  
  	private static final String[] KNOWN_METHOD_NAME_PREFIXES= {"get", "is"}; //$NON-NLS-2$ //$NON-NLS-1$
  
  	private IJavaScriptUnit fSourceCU;
  	private int fSelectionStart;
  	private int fSelectionLength;
  	
  	private IFunction fMethod;
  	private ChangeSignatureRefactoring fChangeSignatureRefactoring;
  	private ParameterInfo fParameter;
  	private String fParameterName;
  	private RefactoringArguments fArguments;
  
  	private Expression fSelectedExpression;
  	private String[] fExcludedParameterNames;
  	
  	/**
  	 * Creates a new introduce parameter refactoring.
  	 * @param unit the compilation unit, or <code>null</code> if invoked by scripting
  	 * @param selectionStart
  	 * @param selectionLength
  	 */
  	public IntroduceParameterRefactoring(IJavaScriptUnit unit, int selectionStart, int selectionLength) {
  		Assert.isTrue(selectionStart >= 0);
  		Assert.isTrue(selectionLength >= 0);
  		fSourceCU= unit;
  		fSelectionStart= selectionStart;
  		fSelectionLength= selectionLength;
  	}
  	
  	// ------------------- IDelegateUpdating ----------------------
  
  	public boolean canEnableDelegateUpdating() {
  		return true;
  	}
  
  	public boolean getDelegateUpdating() {
  		return (fChangeSignatureRefactoring != null) ? fChangeSignatureRefactoring.getDelegateUpdating() : false;
  	}
  
  	public void setDelegateUpdating(boolean updating) {
  		if (fChangeSignatureRefactoring != null)
  			fChangeSignatureRefactoring.setDelegateUpdating(updating);
  	}
  
  	public void setDeprecateDelegates(boolean deprecate) {
  		if (fChangeSignatureRefactoring != null)
  			fChangeSignatureRefactoring.setDeprecateDelegates(deprecate);
  	}
  
  	public boolean getDeprecateDelegates() {
  		return (fChangeSignatureRefactoring != null) ? fChangeSignatureRefactoring.getDeprecateDelegates() : false;
  	}
  
  	// ------------------- /IDelegateUpdating ---------------------
  
  	public String getName() {
  		return RefactoringCoreMessages.IntroduceParameterRefactoring_name; 
  	}
  
  	//--- checkActivation
  		
  	public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
  		try {
  			pm.beginTask("", 7); //$NON-NLS-1$
  			
  			if (! fSourceCU.isStructureKnown())		
  				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_syntax_error); 
  			
  			IJavaScriptElement enclosingElement= SelectionConverter.resolveEnclosingElement(fSourceCU, new TextSelection(fSelectionStart, fSelectionLength));
  			if (! (enclosingElement instanceof IFunction))
  				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method); 
  			
  			fMethod= (IFunction) enclosingElement;
  			pm.worked(1);
  
  			RefactoringStatus result= new RefactoringStatus();
  			if (fArguments != null) {
  				// invoked by script
  				fChangeSignatureRefactoring= new ChangeSignatureRefactoring(null);
  				result= fChangeSignatureRefactoring.initialize(fArguments);
  				if (!result.hasFatalError()) {
  					fChangeSignatureRefactoring.setValidationContext(getValidationContext());
  					result.merge(fChangeSignatureRefactoring.checkInitialConditions(new SubProgressMonitor(pm, 2)));
  					if (result.hasFatalError())
  						return result;
  				} else {
  					pm.worked(2);
  					return result;
  				}
  			} else {
  				// first try:
  				fChangeSignatureRefactoring= RefactoringAvailabilityTester.isChangeSignatureAvailable(fMethod) ? new ChangeSignatureRefactoring(fMethod) : null;
  				if (fChangeSignatureRefactoring == null)
  					return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method);
  				fChangeSignatureRefactoring.setValidationContext(getValidationContext());
  				result.merge(fChangeSignatureRefactoring.checkInitialConditions(new SubProgressMonitor(pm, 1)));
  				if (result.hasFatalError()) {
  					RefactoringStatusEntry entry= result.getEntryMatchingSeverity(RefactoringStatus.FATAL);
  					if (entry.getCode() == RefactoringStatusCodes.OVERRIDES_ANOTHER_METHOD || entry.getCode() == RefactoringStatusCodes.METHOD_DECLARED_IN_INTERFACE) {
  						// second try:
  						IFunction method= (IFunction) entry.getData();
  						fChangeSignatureRefactoring= RefactoringAvailabilityTester.isChangeSignatureAvailable(method) ? new ChangeSignatureRefactoring(method) : null;
  						if (fChangeSignatureRefactoring == null) {
  							String msg= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_cannot_introduce, entry.getMessage());
  							return RefactoringStatus.createFatalErrorStatus(msg);
  						}
  						result= fChangeSignatureRefactoring.checkInitialConditions(new SubProgressMonitor(pm, 1));
  						if (result.hasFatalError())
  							return result;
  					} else {
  						return result;
  					}
  				} else {
  					pm.worked(1);
  				}
  			}
  
  			CompilationUnitRewrite cuRewrite= fChangeSignatureRefactoring.getBaseCuRewrite();
  			if (! cuRewrite.getCu().equals(fSourceCU))
  				cuRewrite= new CompilationUnitRewrite(fSourceCU); // TODO: should try to avoid throwing away this AST
  			
  			initializeSelectedExpression(cuRewrite);
  			pm.worked(1);
  		
  			result.merge(checkSelection(cuRewrite, new SubProgressMonitor(pm, 3)));
  			if (result.hasFatalError())
  				return result;
  
  			initializeExcludedParameterNames(cuRewrite);
  			
  			addParameterInfo(cuRewrite);
  			
  			fChangeSignatureRefactoring.setBodyUpdater(new BodyUpdater() {
  				public void updateBody(FunctionDeclaration methodDeclaration, CompilationUnitRewrite rewrite, RefactoringStatus updaterResult) {
  					replaceSelectedExpression(rewrite);
  				}
  			});
  			
  			return result;
  		} finally {
  			pm.done();
  			if (fChangeSignatureRefactoring != null)
  				fChangeSignatureRefactoring.setValidationContext(null);
  		}	
  	}
  
  	private void addParameterInfo(CompilationUnitRewrite cuRewrite) throws JavaScriptModelException {
  		ITypeBinding typeBinding= Bindings.normalizeForDeclarationUse(fSelectedExpression.resolveTypeBinding(), fSelectedExpression.getAST());
  		String typeName= cuRewrite.getImportRewrite().addImport(typeBinding);
  		String name= fParameterName != null ? fParameterName : guessedParameterName();
  		String defaultValue= fSourceCU.getBuffer().getText(fSelectedExpression.getStartPosition(), fSelectedExpression.getLength());
  		fParameter= ParameterInfo.createInfoForAddedParameter(typeBinding, typeName, name, defaultValue);
  		if (fArguments == null) {
  			List parameterInfos= fChangeSignatureRefactoring.getParameterInfos();
  			int parametersCount= parameterInfos.size();
  			if (parametersCount > 0 && ((ParameterInfo) parameterInfos.get(parametersCount - 1)).isOldVarargs())
  				parameterInfos.add(parametersCount - 1, fParameter);
  			else
  				parameterInfos.add(fParameter);
  		}
  	}
  
  	private void replaceSelectedExpression(CompilationUnitRewrite cuRewrite) {
  		if (! fSourceCU.equals(cuRewrite.getCu()))
  			return;
  		// TODO: do for all methodDeclarations and replace matching fragments?
  		
  		// cannot use fSelectedExpression here, since it could be from another AST (if method was replaced by overridden):
  		Expression expression= (Expression) NodeFinder.perform(cuRewrite.getRoot(), fSelectedExpression.getStartPosition(), fSelectedExpression.getLength());
  		
  		ASTNode newExpression= cuRewrite.getRoot().getAST().newSimpleName(fParameter.getNewName());
  		String description= RefactoringCoreMessages.IntroduceParameterRefactoring_replace; 
  		cuRewrite.getASTRewrite().replace(expression, newExpression, cuRewrite.createGroupDescription(description));
  	}
  
  	private void initializeSelectedExpression(CompilationUnitRewrite cuRewrite) throws JavaScriptModelException {
  		IASTFragment fragment= ASTFragmentFactory.createFragmentForSourceRange(
  				new SourceRange(fSelectionStart, fSelectionLength), cuRewrite.getRoot(), cuRewrite.getCu());
  		
  		if (! (fragment instanceof IExpressionFragment))
  			return;
  		
  		//TODO: doesn't handle selection of partial Expressions
  		Expression expression= ((IExpressionFragment) fragment).getAssociatedExpression();
  		if (fragment.getStartPosition() != expression.getStartPosition()
  				|| fragment.getLength() != expression.getLength())
  			return;
  		
  		if (Checks.isInsideJavadoc(expression))
  			return;
  		
  		fSelectedExpression= expression;
  	}
  	
  	private RefactoringStatus checkSelection(CompilationUnitRewrite cuRewrite, IProgressMonitor pm) {
  		if (fSelectedExpression == null){
  			String message= RefactoringCoreMessages.IntroduceParameterRefactoring_select;
  			return CodeRefactoringUtil.checkMethodSyntaxErrors(fSelectionStart, fSelectionLength, cuRewrite.getRoot(), message);
  		}	
  		
  		FunctionDeclaration methodDeclaration= (FunctionDeclaration) ASTNodes.getParent(fSelectedExpression, FunctionDeclaration.class);
  		if (methodDeclaration == null)
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_in_method); 
  		if (methodDeclaration.resolveBinding() == null)
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceParameterRefactoring_no_binding); 
  		//TODO: check for rippleMethods -> find matching fragments, consider callers of all rippleMethods
  		
  		RefactoringStatus result= new RefactoringStatus();
  		result.merge(checkExpression());
  		if (result.hasFatalError())
  			return result;
  		
  		result.merge(checkExpressionBinding());
  		if (result.hasFatalError())
  			return result;				
  		
  //			if (isUsedInForInitializerOrUpdater(getSelectedExpression().getAssociatedExpression()))
  //				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ExtractTempRefactoring.for_initializer_updater")); //$NON-NLS-1$
  //			pm.worked(1);				
  //
  //			if (isReferringToLocalVariableFromFor(getSelectedExpression().getAssociatedExpression()))
  //				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ExtractTempRefactoring.refers_to_for_variable")); //$NON-NLS-1$
  //			pm.worked(1);
  		
  		return result;		
  	}
  
  	private RefactoringStatus checkExpression() {
  		//TODO: adjust error messages (or generalize for all refactorings on expression-selections?)
  		Expression selectedExpression= fSelectedExpression;
  		
  		if (selectedExpression instanceof Name && selectedExpression.getParent() instanceof ClassInstanceCreation)
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_name_in_new); 
  			//TODO: let's just take the CIC automatically (no ambiguity -> no problem -> no dialog ;-)
  		
  		if (selectedExpression instanceof NullLiteral) {
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_null_literals); 
  		} else if (selectedExpression instanceof ArrayInitializer) {
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_array_initializer); 
  		} else if (selectedExpression instanceof Assignment) {
  			if (selectedExpression.getParent() instanceof Expression)
  				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_assignment); 
  			else
  				return null;
  
  		} else if (selectedExpression instanceof SimpleName){
  			if ((((SimpleName)selectedExpression)).isDeclaration())
  				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_names_in_declarations); 
  			if (selectedExpression.getParent() instanceof QualifiedName && selectedExpression.getLocationInParent() == QualifiedName.NAME_PROPERTY
  					|| selectedExpression.getParent() instanceof FieldAccess && selectedExpression.getLocationInParent() == FieldAccess.NAME_PROPERTY)
  				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_select_expression);
  		} 
  		
  		return null;
  	}
  
  	private RefactoringStatus checkExpressionBinding() {
  		return checkExpressionFragmentIsRValue();
  	}
  	
  	// !! +/- same as in ExtractConstantRefactoring & ExtractTempRefactoring
  	private RefactoringStatus checkExpressionFragmentIsRValue() {
  		switch(Checks.checkExpressionIsRValue(fSelectedExpression)) {
  			case Checks.NOT_RVALUE_MISC:
  				return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.IntroduceParameterRefactoring_select, null, Corext.getPluginId(), RefactoringStatusCodes.EXPRESSION_NOT_RVALUE, null); 
  			case Checks.NOT_RVALUE_VOID:
  				return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.IntroduceParameterRefactoring_no_void, null, Corext.getPluginId(), RefactoringStatusCodes.EXPRESSION_NOT_RVALUE_VOID, null); 
  			case Checks.IS_RVALUE:
  				return new RefactoringStatus();
  			default:
  				Assert.isTrue(false); return null;
  		}		
  	}	
  
  	public List getParameterInfos() {
  		return fChangeSignatureRefactoring.getParameterInfos();
  	}
  	
  	public ParameterInfo getAddedParameterInfo() {
  		return fParameter;
  	}
  	
  	public String getMethodSignaturePreview() throws JavaScriptModelException {
  		return fChangeSignatureRefactoring.getNewMethodSignature();
  	}
  	
  //--- Input setting/validation
  
  	public void setParameterName(String name) {
  		Assert.isNotNull(name);
  		fParameter.setNewName(name);
  	}
  	
  	/** 
  	 * must only be called <i>after</i> checkActivation() 
  	 * @return guessed parameter name
  	 */
  	public String guessedParameterName() {
  		String[] proposals= guessParameterNames();
  		if (proposals.length == 0)
  			return ""; //$NON-NLS-1$
  		else
  			return proposals[0];
  	}
  	
  // --- TODO: copied from ExtractTempRefactoring - should extract ------------------------------
  	
  	/**
  	 * Must only be called <i>after</i> checkActivation().
  	 * The first proposal should be used as "best guess" (if it exists).
  	 * @return proposed variable names (may be empty, but not null).
  	 */
  	public String[] guessParameterNames() {
  		LinkedHashSet proposals= new LinkedHashSet(); //retain ordering, but prevent duplicates
  		if (fSelectedExpression instanceof FunctionInvocation){
  			proposals.addAll(guessTempNamesFromMethodInvocation((FunctionInvocation) fSelectedExpression, fExcludedParameterNames));
  		}
  		proposals.addAll(guessTempNamesFromExpression(fSelectedExpression, fExcludedParameterNames));
  		return (String[]) proposals.toArray(new String[proposals.size()]);
  	}
  	
  	private List/*<String>*/ guessTempNamesFromMethodInvocation(FunctionInvocation selectedMethodInvocation, String[] excludedVariableNames) {
  		SimpleName name = selectedMethodInvocation.getName();
  		
  		String methodName;
  		if (name!=null)
  		{
  			methodName= name.getIdentifier();
  		for (int i= 0; i < KNOWN_METHOD_NAME_PREFIXES.length; i++) {
  			String prefix= KNOWN_METHOD_NAME_PREFIXES[i];
  			if (! methodName.startsWith(prefix))
  				continue; //not this prefix
  			if (methodName.length() == prefix.length())
  				return Collections.EMPTY_LIST; // prefix alone -> don't take method name
  			char firstAfterPrefix= methodName.charAt(prefix.length());
  			if (! Character.isUpperCase(firstAfterPrefix))
  				continue; //not uppercase after prefix
  			//found matching prefix
  			String proposal= Character.toLowerCase(firstAfterPrefix) + methodName.substring(prefix.length() + 1);
  			methodName= proposal;
  			break;
  		}
  		}
  		else
  			methodName="indirectFunctionCall"; //$NON-NLS-1$
  		String[] proposals= StubUtility.getLocalNameSuggestions(fSourceCU.getJavaScriptProject(), methodName, 0, excludedVariableNames);
  		return Arrays.asList(proposals);
  	}
  	
  	private List/*<String>*/ guessTempNamesFromExpression(Expression selectedExpression, String[] excluded) {
  		ITypeBinding expressionBinding= Bindings.normalizeForDeclarationUse(
  			selectedExpression.resolveTypeBinding(),
  			selectedExpression.getAST());
  		String typeName= getQualifiedName(expressionBinding);
  		if (typeName.length() == 0)
  			typeName= expressionBinding.getName();
  		if (typeName.length() == 0)			
  			return Collections.EMPTY_LIST;
  		int typeParamStart= typeName.indexOf("<"); //$NON-NLS-1$
  		if (typeParamStart != -1)
  			typeName= typeName.substring(0, typeParamStart);
  		String[] proposals= StubUtility.getLocalNameSuggestions(fSourceCU.getJavaScriptProject(), typeName, expressionBinding.getDimensions(), excluded);
  		return Arrays.asList(proposals);
  	}
  	
  // ----------------------------------------------------------------------
  	
  	private static String getQualifiedName(ITypeBinding typeBinding) {
  		if (typeBinding.isAnonymous())
  			return getQualifiedName(typeBinding.getSuperclass());
  		if (! typeBinding.isArray())
  			return typeBinding.getQualifiedName();
  		else
  			return typeBinding.getElementType().getQualifiedName();
  	}
  
  	private void initializeExcludedParameterNames(CompilationUnitRewrite cuRewrite) {
  		IBinding[] bindings= new ScopeAnalyzer(cuRewrite.getRoot()).getDeclarationsInScope(
  				fSelectedExpression.getStartPosition(), ScopeAnalyzer.VARIABLES);
  		fExcludedParameterNames= new String[bindings.length];
  		for (int i= 0; i < fExcludedParameterNames.length; i++) {
  			fExcludedParameterNames[i]= bindings[i].getName();
  		}
  	}
  	
  	public RefactoringStatus validateInput() {
  		return fChangeSignatureRefactoring.checkSignature();
  	}
  	
  //--- checkInput
  	
  	public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
  		fChangeSignatureRefactoring.setValidationContext(getValidationContext());
  		RefactoringStatus result;
  		try {
  			result= fChangeSignatureRefactoring.checkFinalConditions(pm);
  		} finally {
  			fChangeSignatureRefactoring.setValidationContext(null);
  		}
  		return result;
  	}
  	
  	public Change createChange(IProgressMonitor pm) throws CoreException {
  		fChangeSignatureRefactoring.setValidationContext(getValidationContext());
  		Change result;
  		try {
  			result= fChangeSignatureRefactoring.createChange(pm);
  		} finally {
  			fChangeSignatureRefactoring.setValidationContext(null);
  		}
  		if (result != null) {
  			final ChangeDescriptor descriptor= result.getDescriptor();
  			if (descriptor instanceof RefactoringChangeDescriptor) {
  				final RefactoringDescriptor refactoringDescriptor= ((RefactoringChangeDescriptor) descriptor).getRefactoringDescriptor();
  				if (refactoringDescriptor instanceof JDTRefactoringDescriptor) {
  					final JDTRefactoringDescriptor extended= (JDTRefactoringDescriptor) refactoringDescriptor;
  					final Map arguments= new HashMap();
  					arguments.put(ATTRIBUTE_ARGUMENT, fParameter.getNewName());
  					arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$
  					arguments.putAll(extended.getArguments());
  					String signature= fChangeSignatureRefactoring.getMethodName();
  					try {
  						signature= fChangeSignatureRefactoring.getOldMethodSignature();
  					} catch (JavaScriptModelException exception) {
  						JavaScriptPlugin.log(exception);
  					}
  					final String description= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_descriptor_description_short, fChangeSignatureRefactoring.getMethod().getElementName());
  					final String header= Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_descriptor_description, new String[] { fParameter.getNewName(), signature, ASTNodes.asString(fSelectedExpression)});
  					final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(extended.getProject(), this, header);
  					comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_original_pattern, JavaScriptElementLabels.getTextLabel(fChangeSignatureRefactoring.getMethod(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)));
  					comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_expression_pattern, ASTNodes.asString(fSelectedExpression)));
  					comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceParameterRefactoring_parameter_pattern, getAddedParameterInfo().getNewName()));
  					result= new RefactoringDescriptorChange(new JDTRefactoringDescriptor(IJavaScriptRefactorings.INTRODUCE_PARAMETER, extended.getProject(), description, comment.asString(), arguments, extended.getFlags()), RefactoringCoreMessages.IntroduceParameterRefactoring_name, new Change[] { result});
  				}
  			}
  		}
  		return result;
  	}
  
  	public RefactoringStatus initialize(final RefactoringArguments arguments) {
  		fArguments= arguments;
  		if (arguments instanceof JavaRefactoringArguments) {
  			final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
  			final String selection= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION);
  			if (selection != null) {
  				int offset= -1;
  				int length= -1;
  				final StringTokenizer tokenizer= new StringTokenizer(selection);
  				if (tokenizer.hasMoreTokens())
  					offset= Integer.valueOf(tokenizer.nextToken()).intValue();
  				if (tokenizer.hasMoreTokens())
  					length= Integer.valueOf(tokenizer.nextToken()).intValue();
  				if (offset >= 0 && length >= 0) {
  					fSelectionStart= offset;
  					fSelectionLength= length;
  				} else
  					return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION}));
  			} else
  				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION));
  			final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT);
  			if (handle != null) {
  				final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
  				if (element == null || !element.exists() || element.getElementType() != IJavaScriptElement.JAVASCRIPT_UNIT)
  					return createInputFatalStatus(element, IJavaScriptRefactorings.INTRODUCE_PARAMETER);
  				else
  					fSourceCU= ((IFunction) element).getJavaScriptUnit();
  			} else
  				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
  			final String name= extended.getAttribute(ATTRIBUTE_ARGUMENT);
  			if (name != null && !"".equals(name)) //$NON-NLS-1$
  				fParameterName= name;
  			else
  				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ARGUMENT));
  		} else
  			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
  		return new RefactoringStatus();
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	public String getDelegateUpdatingTitle(boolean plural) {
  		if (plural)
  			return RefactoringCoreMessages.DelegateCreator_keep_original_changed_plural;
  		else
  			return RefactoringCoreMessages.DelegateCreator_keep_original_changed_singular;
  	}
  }
  