/******************************************************************************* * Copyright (c) 2000, 2013 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 *******************************************************************************/ package org.eclipse.wst.jsdt.internal.corext.refactoring.structure; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; 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.RefactoringStatusContext; import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.wst.jsdt.core.Flags; import org.eclipse.wst.jsdt.core.IFunction; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.ITypeHierarchy; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.Signature; import org.eclipse.wst.jsdt.core.compiler.IProblem; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTParser; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation; import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.core.dom.FunctionInvocation; import org.eclipse.wst.jsdt.core.dom.FunctionRef; import org.eclipse.wst.jsdt.core.dom.FunctionRefParameter; import org.eclipse.wst.jsdt.core.dom.IFunctionBinding; import org.eclipse.wst.jsdt.core.dom.ITypeBinding; import org.eclipse.wst.jsdt.core.dom.ImportDeclaration; import org.eclipse.wst.jsdt.core.dom.JSdoc; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.core.dom.MemberRef; import org.eclipse.wst.jsdt.core.dom.Modifier; import org.eclipse.wst.jsdt.core.dom.Name; import org.eclipse.wst.jsdt.core.dom.PrimitiveType; import org.eclipse.wst.jsdt.core.dom.QualifiedName; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration; import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation; import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation; import org.eclipse.wst.jsdt.core.dom.TagElement; import org.eclipse.wst.jsdt.core.dom.TextElement; import org.eclipse.wst.jsdt.core.dom.Type; import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite; import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor; import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants; import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchScope; import org.eclipse.wst.jsdt.core.search.SearchPattern; 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.ASTNodeFactory; 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.ModifierRewrite; import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder; import org.eclipse.wst.jsdt.internal.corext.dom.Selection; import org.eclipse.wst.jsdt.internal.corext.dom.SelectionAnalyzer; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.ExceptionInfo; 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.RefactoringCoreMessages; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringScopeFactory; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringSearchEngine; import org.eclipse.wst.jsdt.internal.corext.refactoring.ReturnTypeInfo; import org.eclipse.wst.jsdt.internal.corext.refactoring.SearchResultGroup; import org.eclipse.wst.jsdt.internal.corext.refactoring.StubTypeContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.TypeContextChecker; import org.eclipse.wst.jsdt.internal.corext.refactoring.TypeContextChecker.IProblemVerifier; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStringStatusContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.RefactoringStatusCodes; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ScriptableRefactoring; import org.eclipse.wst.jsdt.internal.corext.refactoring.delegates.DelegateMethodCreator; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.MethodChecks; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.RefactoringAnalyzeUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.RippleMethodFinder2; import org.eclipse.wst.jsdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer; import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IDelegateUpdating; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavaElementUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavadocUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextChangeManager; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TightSourceRangeComputer; import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil; import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; public class ChangeSignatureRefactoring extends ScriptableRefactoring implements IDelegateUpdating { private static final String ATTRIBUTE_RETURN= "return"; //$NON-NLS-1$ private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$ private static final String ATTRIBUTE_PARAMETER= "parameter"; //$NON-NLS-1$ private static final String ATTRIBUTE_DEFAULT= "default"; //$NON-NLS-1$ private static final String ATTRIBUTE_KIND= "kind"; //$NON-NLS-1$ private static final String ATTRIBUTE_DELEGATE= "delegate"; //$NON-NLS-1$ private static final String ATTRIBUTE_DEPRECATE= "deprecate"; //$NON-NLS-1$ private static final String ATTRIBUTE_FUNCTION_HEAD="function"; //$NON-NLS-1$ private static final boolean PREFIX_FUNCTION_HEAD = true; private List fParameterInfos; private CompilationUnitRewrite fBaseCuRewrite; private List fExceptionInfos; protected TextChangeManager fChangeManager; protected List/*<Change>*/ fOtherChanges; private IFunction fMethod; private IFunction fTopMethod; private IFunction[] fRippleMethods; private SearchResultGroup[] fOccurrences; private ReturnTypeInfo fReturnTypeInfo; private String fMethodName; private int fVisibility; // private static final String CONST_CLASS_DECL = "class A{";//$NON-NLS-1$ private static final String CONST_ASSIGN = " i="; //$NON-NLS-1$ private static final String CONST_CLOSE = ";"; //$NON-NLS-1$ private StubTypeContext fContextCuStartEnd; private int fOldVarargIndex; // initialized in checkVarargs() private BodyUpdater fBodyUpdater; private IDefaultValueAdvisor fDefaultValueAdvisor; private ITypeHierarchy fCachedTypeHierarchy= null; private boolean fDelegateUpdating; private boolean fDelegateDeprecation; /** * Creates a new change signature refactoring. * @param method the method, or <code>null</code> if invoked by scripting framework * @throws JavaScriptModelException */ public ChangeSignatureRefactoring(IFunction method) throws JavaScriptModelException { fMethod= method; fOldVarargIndex= -1; fDelegateUpdating= false; fDelegateDeprecation= true; if (fMethod != null) { fParameterInfos= createParameterInfoList(method); // fExceptionInfos is created in checkInitialConditions fReturnTypeInfo= new ReturnTypeInfo(Signature.toString(Signature.getReturnType(fMethod.getSignature()))); fMethodName= fMethod.getElementName(); fVisibility= JdtFlags.getVisibilityCode(fMethod); } } private static List createParameterInfoList(IFunction method) { try { String[] typeNames= method.getParameterTypes(); String[] oldNames= method.getParameterNames(); List result= new ArrayList(typeNames.length); for (int i= 0; i < oldNames.length; i++){ ParameterInfo parameterInfo; if (i == oldNames.length - 1 && Flags.isVarargs(method.getFlags())) { String varargSignature= typeNames[i]; int arrayCount= Signature.getArrayCount(varargSignature); String baseSignature= Signature.getElementType(varargSignature); if (arrayCount > 1) baseSignature= Signature.createArraySignature(baseSignature, arrayCount - 1); parameterInfo= new ParameterInfo(Signature.toString(baseSignature) + ParameterInfo.ELLIPSIS, oldNames[i], i); } else { parameterInfo= new ParameterInfo(Signature.toString(typeNames[i]), oldNames[i], i); } result.add(parameterInfo); } return result; } catch(JavaScriptModelException e) { JavaScriptPlugin.log(e); return new ArrayList(0); } } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.IRefactoring#getName() */ public String getName() { return RefactoringCoreMessages.ChangeSignatureRefactoring_modify_Parameters; } public IFunction getMethod() { return fMethod; } public String getMethodName() { return fMethodName; } public String getReturnTypeString() { return fReturnTypeInfo.getNewTypeName(); } public void setNewMethodName(String newMethodName){ Assert.isNotNull(newMethodName); fMethodName= newMethodName; } public void setNewReturnTypeName(String newReturnTypeName){ Assert.isNotNull(newReturnTypeName); fReturnTypeInfo.setNewTypeName(newReturnTypeName); } public boolean canChangeNameAndReturnType(){ try { return ! fMethod.isConstructor(); } catch (JavaScriptModelException e) { JavaScriptPlugin.log(e); return false; } } /** * @return visibility * @see org.eclipse.wst.jsdt.core.dom.Modifier */ public int getVisibility(){ return fVisibility; } /** * @param visibility new visibility * @see org.eclipse.wst.jsdt.core.dom.Modifier */ public void setVisibility(int visibility){ Assert.isTrue( visibility == Modifier.PUBLIC || visibility == Modifier.PROTECTED || visibility == Modifier.NONE || visibility == Modifier.PRIVATE); fVisibility= visibility; } /* * @see JdtFlags */ public int[] getAvailableVisibilities() throws JavaScriptModelException{ // if (fTopMethod.getDeclaringType().isInterface()) // return new int[]{Modifier.PUBLIC}; // else if (fTopMethod.getDeclaringType().isEnum() && fTopMethod.isConstructor()) // return new int[]{ Modifier.NONE, // Modifier.PRIVATE}; // else return new int[]{ Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE}; } /** * * @return List of <code>ParameterInfo</code> objects. */ public List getParameterInfos(){ return fParameterInfos; } /** * @return List of <code>ExceptionInfo</code> objects. */ public List getExceptionInfos(){ return fExceptionInfos; } public void setBodyUpdater(BodyUpdater bodyUpdater) { fBodyUpdater= bodyUpdater; } public CompilationUnitRewrite getBaseCuRewrite() { return fBaseCuRewrite; } //------------------- IDelegateUpdating ---------------------- public boolean canEnableDelegateUpdating() { return true; } public boolean getDelegateUpdating() { return fDelegateUpdating; } public void setDelegateUpdating(boolean updating) { fDelegateUpdating= updating; } public void setDeprecateDelegates(boolean deprecate) { fDelegateDeprecation= deprecate; } public boolean getDeprecateDelegates() { return fDelegateDeprecation; } //------------------- /IDelegateUpdating --------------------- public RefactoringStatus checkSignature() { return checkSignature(false, doGetProblemVerifier()); } private RefactoringStatus checkSignature(boolean resolveBindings, IProblemVerifier problemVerifier) { RefactoringStatus result= new RefactoringStatus(); checkMethodName(result); if (result.hasFatalError()) return result; checkParameterNamesAndValues(result); if (result.hasFatalError()) return result; checkForDuplicateParameterNames(result); if (result.hasFatalError()) return result; try { RefactoringStatus[] typeStati; if (resolveBindings) typeStati= TypeContextChecker.checkAndResolveMethodTypes(fMethod, getStubTypeContext(), getNotDeletedInfos(), fReturnTypeInfo, problemVerifier); else typeStati= TypeContextChecker.checkMethodTypesSyntax(fMethod, getNotDeletedInfos(), fReturnTypeInfo); for (int i= 0; i < typeStati.length; i++) result.merge(typeStati[i]); result.merge(checkVarargs()); } catch (CoreException e) { //cannot do anything here throw new RuntimeException(e); } //checkExceptions() unnecessary (IType always ok) return result; } public boolean isSignatureSameAsInitial() throws JavaScriptModelException { if (! isVisibilitySameAsInitial()) return false; if (! isMethodNameSameAsInitial()) return false; if (! isReturnTypeSameAsInitial()) return false; if (! areExceptionsSameAsInitial()) return false; if (fMethod.getNumberOfParameters() == 0 && fParameterInfos.isEmpty()) return true; if (areNamesSameAsInitial() && isOrderSameAsInitial() && areParameterTypesSameAsInitial()) return true; return false; } /** * @return true if the new method cannot coexist with the old method since * the signatures are too much alike * @throws JavaScriptModelException */ public boolean isSignatureClashWithInitial() throws JavaScriptModelException { if (!isMethodNameSameAsInitial()) return false; // name has changed. if (fMethod.getNumberOfParameters() == 0 && fParameterInfos.isEmpty()) return true; // name is equal and both parameter lists are empty // name is equal and there are some parameters. // check if there are more or less parameters than before int no= getNotDeletedInfos().size(); if (fMethod.getNumberOfParameters() != no) return false; // name is equal and parameter count is equal. // check whether types remained the same if (isOrderSameAsInitial()) return areParameterTypesSameAsInitial(); else return false; // could be more specific here } private boolean areParameterTypesSameAsInitial() { for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (! info.isAdded() && ! info.isDeleted() && info.isTypeNameChanged()) return false; } return true; } private boolean isReturnTypeSameAsInitial() throws JavaScriptModelException { return ! fReturnTypeInfo.isTypeNameChanged(); } private boolean isMethodNameSameAsInitial() { return fMethodName.equals(fMethod.getElementName()); } private boolean areExceptionsSameAsInitial() { for (Iterator iter= fExceptionInfos.iterator(); iter.hasNext();) { ExceptionInfo info= (ExceptionInfo) iter.next(); if (! info.isOld()) return false; } return true; } private void checkParameterNamesAndValues(RefactoringStatus result) { int i= 1; for (Iterator iter= fParameterInfos.iterator(); iter.hasNext(); i++) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isDeleted()) continue; checkParameterName(result, info, i); if (result.hasFatalError()) return; if (info.isAdded()) { checkParameterDefaultValue(result, info); if (result.hasFatalError()) return; } } } private void checkParameterName(RefactoringStatus result, ParameterInfo info, int position) { if (info.getNewName().trim().length() == 0) { result.addFatalError(Messages.format( RefactoringCoreMessages.ChangeSignatureRefactoring_param_name_not_empty, Integer.toString(position))); } else { result.merge(Checks.checkTempName(info.getNewName())); } } private void checkMethodName(RefactoringStatus result) { if (isMethodNameSameAsInitial() || ! canChangeNameAndReturnType()) return; if ("".equals(fMethodName.trim())) { //$NON-NLS-1$ String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_method_name_not_empty; result.addFatalError(msg); return; } if (fMethod.getDeclaringType() != null && fMethodName.equals(fMethod.getDeclaringType().getElementName())) { String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_constructor_name; result.addWarning(msg); } result.merge(Checks.checkMethodName(fMethodName)); } private void checkParameterDefaultValue(RefactoringStatus result, ParameterInfo info) { if (fDefaultValueAdvisor != null) return; if (info.isNewVarargs()) { if (! isValidVarargsExpression(info.getDefaultValue())){ String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_invalid_expression, new String[]{info.getDefaultValue()}); result.addFatalError(msg); } return; } if (info.getDefaultValue().trim().equals("")){ //$NON-NLS-1$ String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_default_value, new String[]{info.getNewName()}); result.addFatalError(msg); return; } if (! isValidExpression(info.getDefaultValue())){ String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_invalid_expression, new String[]{info.getDefaultValue()}); result.addFatalError(msg); } } private RefactoringStatus checkVarargs() throws JavaScriptModelException { RefactoringStatus result= checkOriginalVarargs(); if (result != null) return result; if (fRippleMethods != null) { for (int iRipple= 0; iRipple < fRippleMethods.length; iRipple++) { IFunction rippleMethod= fRippleMethods[iRipple]; if (! JdtFlags.isVarargs(rippleMethod)) continue; // Vararg method can override method that takes an array as last argument fOldVarargIndex= rippleMethod.getNumberOfParameters() - 1; List notDeletedInfos= getNotDeletedInfos(); for (int i= 0; i < notDeletedInfos.size(); i++) { ParameterInfo info= (ParameterInfo) notDeletedInfos.get(i); if (fOldVarargIndex != -1 && info.getOldIndex() == fOldVarargIndex && ! info.isNewVarargs()) { String rippleMethodType= JavaModelUtil.getFullyQualifiedName(rippleMethod.getDeclaringType()); String message= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_ripple_cannot_convert_vararg, new Object[] {info.getNewName(), rippleMethodType}); return RefactoringStatus.createFatalErrorStatus(message, JavaStatusContext.create(rippleMethod)); } } } } return null; } private RefactoringStatus checkOriginalVarargs() throws JavaScriptModelException { if (JdtFlags.isVarargs(fMethod)) fOldVarargIndex= fMethod.getNumberOfParameters() - 1; List notDeletedInfos= getNotDeletedInfos(); for (int i= 0; i < notDeletedInfos.size(); i++) { ParameterInfo info= (ParameterInfo) notDeletedInfos.get(i); if (info.isOldVarargs() && ! info.isNewVarargs()) return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_cannot_convert_vararg, info.getNewName())); if (i != notDeletedInfos.size() - 1) { // not the last parameter if (info.isNewVarargs()) return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_vararg_must_be_last, info.getNewName())); } } return null; } private RefactoringStatus checkTypeVariables() throws JavaScriptModelException { if (fRippleMethods.length == 1) return null; RefactoringStatus result= new RefactoringStatus(); if (fReturnTypeInfo.isTypeNameChanged() && fReturnTypeInfo.getNewTypeBinding() != null) { HashSet typeVariablesCollector= new HashSet(); collectTypeVariables(fReturnTypeInfo.getNewTypeBinding(), typeVariablesCollector); if (typeVariablesCollector.size() != 0) { ITypeBinding first= (ITypeBinding) typeVariablesCollector.iterator().next(); String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_return_type_contains_type_variable, new String[] {fReturnTypeInfo.getNewTypeName(), first.getName()}); result.addError(msg); } } for (Iterator iter= getNotDeletedInfos().iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isTypeNameChanged() && info.getNewTypeBinding() != null) { HashSet typeVariablesCollector= new HashSet(); collectTypeVariables(info.getNewTypeBinding(), typeVariablesCollector); if (typeVariablesCollector.size() != 0) { ITypeBinding first= (ITypeBinding) typeVariablesCollector.iterator().next(); String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_parameter_type_contains_type_variable, new String[] {info.getNewTypeName(), info.getNewName(), first.getName()}); result.addError(msg); } } } return result; } private void collectTypeVariables(ITypeBinding typeBinding, Set typeVariablesCollector) { if (typeBinding.isArray()) { collectTypeVariables(typeBinding.getElementType(), typeVariablesCollector); } } public static boolean isValidExpression(String string){ String trimmed= string.trim(); StringBuffer cuBuff= new StringBuffer(); cuBuff.append(CONST_ASSIGN); int offset= cuBuff.length(); cuBuff.append(trimmed) .append(CONST_CLOSE); ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(cuBuff.toString().toCharArray()); JavaScriptUnit cu= (JavaScriptUnit) p.createAST(null); Selection selection= Selection.createFromStartLength(offset, trimmed.length()); SelectionAnalyzer analyzer= new SelectionAnalyzer(selection, false); cu.accept(analyzer); ASTNode selected= analyzer.getFirstSelectedNode(); return (selected instanceof Expression) && trimmed.equals(cuBuff.substring(cu.getExtendedStartPosition(selected), cu.getExtendedStartPosition(selected) + cu.getExtendedLength(selected))); /* if ("".equals(trimmed)) //speed up for a common case //$NON-NLS-1$ return false; StringBuffer cuBuff= new StringBuffer(); cuBuff.append(CONST_CLASS_DECL) .append("Object") //$NON-NLS-1$ .append(CONST_ASSIGN); int offset= cuBuff.length(); cuBuff.append(trimmed) .append(CONST_CLOSE); ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(cuBuff.toString().toCharArray()); JavaScriptUnit cu= (JavaScriptUnit) p.createAST(null); Selection selection= Selection.createFromStartLength(offset, trimmed.length()); SelectionAnalyzer analyzer= new SelectionAnalyzer(selection, false); cu.accept(analyzer); ASTNode selected= analyzer.getFirstSelectedNode(); return (selected instanceof Expression) && trimmed.equals(cuBuff.substring(cu.getExtendedStartPosition(selected), cu.getExtendedStartPosition(selected) + cu.getExtendedLength(selected))); */ } public static boolean isValidVarargsExpression(String string) { String trimmed= string.trim(); if ("".equals(trimmed)) //speed up for a common case //$NON-NLS-1$ return true; StringBuffer cuBuff= new StringBuffer(); cuBuff.append("class A{ {m("); //$NON-NLS-1$ int offset= cuBuff.length(); cuBuff.append(trimmed) .append(");}}"); //$NON-NLS-1$ ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(cuBuff.toString().toCharArray()); JavaScriptUnit cu= (JavaScriptUnit) p.createAST(null); Selection selection= Selection.createFromStartLength(offset, trimmed.length()); SelectionAnalyzer analyzer= new SelectionAnalyzer(selection, false); cu.accept(analyzer); ASTNode[] selectedNodes= analyzer.getSelectedNodes(); if (selectedNodes.length == 0) return false; for (int i= 0; i < selectedNodes.length; i++) { if (! (selectedNodes[i] instanceof Expression)) return false; } return true; } public StubTypeContext getStubTypeContext() { try { if (fContextCuStartEnd == null) fContextCuStartEnd= TypeContextChecker.createStubTypeContext(getCu(), fBaseCuRewrite.getRoot(), fMethod.getSourceRange().getOffset()); } catch (CoreException e) { //cannot do anything here throw new RuntimeException(e); } return fContextCuStartEnd; } private ITypeHierarchy getCachedTypeHierarchy(IProgressMonitor monitor) throws JavaScriptModelException { if (fCachedTypeHierarchy == null) fCachedTypeHierarchy= fMethod.getDeclaringType().newTypeHierarchy(new SubProgressMonitor(monitor, 1)); return fCachedTypeHierarchy; } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor) */ public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException { try { monitor.beginTask("", 5); //$NON-NLS-1$ RefactoringStatus result= Checks.checkIfCuBroken(fMethod); if (result.hasFatalError()) return result; if (fMethod == null || !fMethod.exists()) { String message= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_method_deleted, getCu().getElementName()); return RefactoringStatus.createFatalErrorStatus(message); } // if (fMethod.getDeclaringType().isInterface()) { // fTopMethod= MethodChecks.overridesAnotherMethod(fMethod, fMethod.getDeclaringType().newSupertypeHierarchy(new SubProgressMonitor(monitor, 1))); // monitor.worked(1); // } else if (MethodChecks.isVirtual(fMethod)) { ITypeHierarchy hierarchy= getCachedTypeHierarchy(new SubProgressMonitor(monitor, 1)); fTopMethod= null; if (fTopMethod == null) fTopMethod= MethodChecks.overridesAnotherMethod(fMethod, hierarchy); } if (fTopMethod == null) fTopMethod= fMethod; if (! fTopMethod.equals(fMethod)) { RefactoringStatusContext context= JavaStatusContext.create(fTopMethod); String message= Messages.format(RefactoringCoreMessages.MethodChecks_overrides, new String[]{JavaElementUtil.createMethodSignature(fTopMethod), JavaModelUtil.getFullyQualifiedName(fTopMethod.getDeclaringType())}); return RefactoringStatus.createStatus(RefactoringStatus.FATAL, message, context, Corext.getPluginId(), RefactoringStatusCodes.OVERRIDES_ANOTHER_METHOD, fTopMethod); } if (monitor.isCanceled()) throw new OperationCanceledException(); if (fBaseCuRewrite == null || !fBaseCuRewrite.getCu().equals(getCu())) { fBaseCuRewrite= new CompilationUnitRewrite(getCu()); fBaseCuRewrite.getASTRewrite().setTargetSourceRangeComputer(new TightSourceRangeComputer()); } monitor.worked(1); result.merge(createExceptionInfoList()); monitor.worked(1); return result; } finally { monitor.done(); } } private RefactoringStatus createExceptionInfoList() { if (fExceptionInfos == null || fExceptionInfos.isEmpty()) { fExceptionInfos= new ArrayList(0); try { ASTNode nameNode= NodeFinder.perform(fBaseCuRewrite.getRoot(), fMethod.getNameRange()); if (nameNode == null || !(nameNode instanceof Name) || !(nameNode.getParent() instanceof FunctionDeclaration)) return null; FunctionDeclaration methodDeclaration= (FunctionDeclaration) nameNode.getParent(); List exceptions= methodDeclaration.thrownExceptions(); List result= new ArrayList(exceptions.size()); for (int i= 0; i < exceptions.size(); i++) { Name name= (Name) exceptions.get(i); ITypeBinding typeBinding= name.resolveTypeBinding(); if (typeBinding == null) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_no_exception_binding); IType type= (IType) typeBinding.getJavaElement(); result.add(ExceptionInfo.createInfoForOldException(type, typeBinding)); } fExceptionInfos= result; } catch (JavaScriptModelException e) { JavaScriptPlugin.log(e); } } return null; } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor) */ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { try { pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_checking_preconditions, 8); RefactoringStatus result= new RefactoringStatus(); clearManagers(); fBaseCuRewrite.clearASTAndImportRewrites(); fBaseCuRewrite.getASTRewrite().setTargetSourceRangeComputer(new TightSourceRangeComputer()); if (isSignatureSameAsInitial()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_unchanged); result.merge(checkSignature(true, doGetProblemVerifier())); if (result.hasFatalError()) return result; if (fDelegateUpdating && isSignatureClashWithInitial()) result.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_old_and_new_signatures_not_sufficiently_different )); fRippleMethods= RippleMethodFinder2.getRelatedMethods(fMethod, new SubProgressMonitor(pm, 1), null); result.merge(checkVarargs()); if (result.hasFatalError()) return result; fOccurrences= findOccurrences(new SubProgressMonitor(pm, 1), result); result.merge(checkVisibilityChanges()); result.merge(checkTypeVariables()); //TODO: // We need a common way of dealing with possible compilation errors for all occurrences, // including visibility problems, shadowing and missing throws declarations. if (! isOrderSameAsInitial()) result.merge(checkReorderings(new SubProgressMonitor(pm, 1))); else pm.worked(1); //TODO (bug 58616): check whether changed signature already exists somewhere in the ripple, // - error if exists // - warn if exists with different parameter types (may cause overloading) if (! areNamesSameAsInitial()) result.merge(checkRenamings(new SubProgressMonitor(pm, 1))); else pm.worked(1); if (result.hasFatalError()) return result; // resolveTypesWithoutBindings(new SubProgressMonitor(pm, 1)); // already done in checkSignature(true) createChangeManager(new SubProgressMonitor(pm, 1), result); fCachedTypeHierarchy= null; if (mustAnalyzeAstOfDeclaringCu()) result.merge(checkCompilationofDeclaringCu()); //TODO: should also check in ripple methods (move into createChangeManager) if (result.hasFatalError()) return result; result.merge(validateModifiesFiles()); return result; } finally { pm.done(); } } protected IProblemVerifier doGetProblemVerifier() { return null; } private void clearManagers() { fChangeManager= null; fOtherChanges= new ArrayList(); } private RefactoringStatus checkVisibilityChanges() throws JavaScriptModelException { if (isVisibilitySameAsInitial()) return null; if (fRippleMethods.length == 1) return null; Assert.isTrue(JdtFlags.getVisibilityCode(fMethod) != Modifier.PRIVATE); if (fVisibility == Modifier.PRIVATE) return RefactoringStatus.createWarningStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_non_virtual); return null; } public String getOldMethodSignature() throws JavaScriptModelException{ StringBuffer buff= new StringBuffer(); int flags= getMethod().getFlags(); if(JavaScriptCore.IS_ECMASCRIPT4) { buff.append(getVisibilityString(flags)); if (Flags.isStatic(flags)) buff.append("static "); //$NON-NLS-1$ } if(PREFIX_FUNCTION_HEAD) { buff.append(ATTRIBUTE_FUNCTION_HEAD + " "); //$NON-NLS-1$ } if (! getMethod().isConstructor() && JavaScriptCore.IS_ECMASCRIPT4) buff.append(fReturnTypeInfo.getOldTypeName()) .append(' '); buff.append(JavaScriptElementLabels.getElementLabel(fMethod.getParent(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)); buff.append('.'); buff.append(fMethod.getElementName()) .append(Signature.C_PARAM_START) .append(getOldMethodParameters()) .append(Signature.C_PARAM_END); buff.append(getOldMethodThrows()); return buff.toString(); } public String getNewMethodSignature() throws JavaScriptModelException{ StringBuffer buff= new StringBuffer(); if(PREFIX_FUNCTION_HEAD) { buff.append(ATTRIBUTE_FUNCTION_HEAD + " "); //$NON-NLS-1$ } if(JavaScriptCore.IS_ECMASCRIPT4) { buff.append(getVisibilityString(fVisibility)); if (Flags.isStatic(getMethod().getFlags())) buff.append("static "); //$NON-NLS-1$ } if (! getMethod().isConstructor() && JavaScriptCore.IS_ECMASCRIPT4) buff.append(getReturnTypeString()).append(' '); buff.append(getMethodName()) .append(Signature.C_PARAM_START) .append(getMethodParameters()) .append(Signature.C_PARAM_END); if(JavaScriptCore.IS_ECMASCRIPT4) buff.append(getMethodThrows()); return buff.toString(); } private String getVisibilityString(int visibility) { String visibilityString= JdtFlags.getVisibilityString(visibility); if ("".equals(visibilityString)) //$NON-NLS-1$ return visibilityString; return visibilityString + ' '; } private String getMethodThrows() { final String throwsString= " throws "; //$NON-NLS-1$ StringBuffer buff= new StringBuffer(throwsString); for (Iterator iter= fExceptionInfos.iterator(); iter.hasNext(); ) { ExceptionInfo info= (ExceptionInfo) iter.next(); if (! info.isDeleted()) { buff.append(info.getType().getElementName()); buff.append(", "); //$NON-NLS-1$ } } if (buff.length() == throwsString.length()) return ""; //$NON-NLS-1$ buff.delete(buff.length() - 2, buff.length()); return buff.toString(); } private String getOldMethodThrows() { final String throwsString= " throws "; //$NON-NLS-1$ StringBuffer buff= new StringBuffer(throwsString); for (Iterator iter= fExceptionInfos.iterator(); iter.hasNext(); ) { ExceptionInfo info= (ExceptionInfo) iter.next(); if (! info.isAdded()) { buff.append(info.getType().getElementName()); buff.append(", "); //$NON-NLS-1$ } } if (buff.length() == throwsString.length()) return ""; //$NON-NLS-1$ buff.delete(buff.length() - 2, buff.length()); return buff.toString(); } private void checkForDuplicateParameterNames(RefactoringStatus result){ Set found= new HashSet(); Set doubled= new HashSet(); for (Iterator iter = getNotDeletedInfos().iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo)iter.next(); String newName= info.getNewName(); if (found.contains(newName) && !doubled.contains(newName)){ result.addFatalError(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_duplicate_name, newName)); doubled.add(newName); } else { found.add(newName); } } } private IJavaScriptUnit getCu() { return fMethod.getJavaScriptUnit(); } private boolean mustAnalyzeAstOfDeclaringCu() throws JavaScriptModelException{ if (JdtFlags.isAbstract(getMethod())) return false; else return true; } private RefactoringStatus checkCompilationofDeclaringCu() throws CoreException { IJavaScriptUnit cu= getCu(); TextChange change= fChangeManager.get(cu); String newCuSource= change.getPreviewContent(new NullProgressMonitor()); JavaScriptUnit newCUNode= new RefactoringASTParser(AST.JLS3).parse(newCuSource, cu, true, false, null); IProblem[] problems= RefactoringAnalyzeUtil.getIntroducedCompileProblems(newCUNode, fBaseCuRewrite.getRoot()); RefactoringStatus result= new RefactoringStatus(); for (int i= 0; i < problems.length; i++) { IProblem problem= problems[i]; if (shouldReport(problem, newCUNode)) result.addEntry(new RefactoringStatusEntry((problem.isError() ? RefactoringStatus.ERROR : RefactoringStatus.WARNING), problem.getMessage(), new JavaStringStatusContext(newCuSource, new SourceRange(problem)))); } return result; } private boolean shouldReport(IProblem problem, JavaScriptUnit cu) { if (! problem.isError()) return false; if (problem.getID() == IProblem.UndefinedType) //reported when trying to import return false; ASTNode node= ASTNodeSearchUtil.getAstNode(cu, problem.getSourceStart(), problem.getSourceEnd() - problem.getSourceStart()); IProblemVerifier verifier= doGetProblemVerifier(); if (verifier != null) return verifier.isError(problem, node); return true; } private String getOldMethodParameters() { StringBuffer buff= new StringBuffer(); int i= 0; for (Iterator iter= getNotAddedInfos().iterator(); iter.hasNext(); i++) { ParameterInfo info= (ParameterInfo) iter.next(); if (i != 0 ) buff.append(", "); //$NON-NLS-1$ buff.append(createDeclarationString(info)); } return buff.toString(); } private String getMethodParameters() { StringBuffer buff= new StringBuffer(); int i= 0; for (Iterator iter= getNotDeletedInfos().iterator(); iter.hasNext(); i++) { ParameterInfo info= (ParameterInfo) iter.next(); if (i != 0 ) buff.append(", "); //$NON-NLS-1$ buff.append(createDeclarationString(info)); } return buff.toString(); } private List getAddedInfos(){ List result= new ArrayList(1); for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isAdded()) result.add(info); } return result; } private List getDeletedInfos(){ List result= new ArrayList(1); for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isDeleted()) result.add(info); } return result; } private List getNotAddedInfos(){ List all= new ArrayList(fParameterInfos); all.removeAll(getAddedInfos()); return all; } private List getNotDeletedInfos(){ List all= new ArrayList(fParameterInfos); all.removeAll(getDeletedInfos()); return all; } private boolean areNamesSameAsInitial() { for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isRenamed()) return false; } return true; } private boolean isOrderSameAsInitial(){ int i= 0; for (Iterator iter= fParameterInfos.iterator(); iter.hasNext(); i++) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.getOldIndex() != i) // includes info.isAdded() return false; if (info.isDeleted()) return false; } return true; } private RefactoringStatus checkReorderings(IProgressMonitor pm) throws JavaScriptModelException { try{ pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_checking_preconditions, 1); return checkNativeMethods(); } finally{ pm.done(); } } private RefactoringStatus checkRenamings(IProgressMonitor pm) throws JavaScriptModelException { try{ pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_checking_preconditions, 1); return checkParameterNamesInRippleMethods(); } finally{ pm.done(); } } private RefactoringStatus checkParameterNamesInRippleMethods() throws JavaScriptModelException { RefactoringStatus result= new RefactoringStatus(); Set newParameterNames= getNewParameterNamesList(); for (int i= 0; i < fRippleMethods.length; i++) { String[] paramNames= fRippleMethods[i].getParameterNames(); for (int j= 0; j < paramNames.length; j++) { if (newParameterNames.contains(paramNames[j])){ String[] args= new String[]{JavaElementUtil.createMethodSignature(fRippleMethods[i]), paramNames[j]}; String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_already_has, args); RefactoringStatusContext context= JavaStatusContext.create(fRippleMethods[i].getJavaScriptUnit(), fRippleMethods[i].getNameRange()); result.addError(msg, context); } } } return result; } private Set getNewParameterNamesList() { Set oldNames= getOriginalParameterNames(); Set currentNames= getNamesOfNotDeletedParameters(); currentNames.removeAll(oldNames); return currentNames; } private Set getNamesOfNotDeletedParameters() { Set result= new HashSet(); for (Iterator iter= getNotDeletedInfos().iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); result.add(info.getNewName()); } return result; } private Set getOriginalParameterNames() { Set result= new HashSet(); for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (! info.isAdded()) result.add(info.getOldName()); } return result; } private RefactoringStatus checkNativeMethods() throws JavaScriptModelException{ RefactoringStatus result= new RefactoringStatus(); return result; } private IFile[] getAllFilesToModify(){ return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); } private RefactoringStatus validateModifiesFiles(){ return Checks.validateModifiesFiles(getAllFilesToModify(), getValidationContext()); } public Change createChange(IProgressMonitor pm) { pm.beginTask("", 1); //$NON-NLS-1$ try { final TextChange[] changes= fChangeManager.getAllChanges(); final List list= new ArrayList(changes.length); list.addAll(fOtherChanges); list.addAll(Arrays.asList(changes)); final Map arguments= new HashMap(); String project= null; IJavaScriptProject javaProject= fMethod.getJavaScriptProject(); if (javaProject != null) project= javaProject.getElementName(); int flags= JavaScriptRefactoringDescriptor.JAR_MIGRATION | JavaScriptRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE; try { if (!Flags.isPrivate(fMethod.getFlags())) flags|= RefactoringDescriptor.MULTI_CHANGE; final IType declaring= fMethod.getDeclaringType(); if (declaring!=null && (declaring.isAnonymous() || declaring.isLocal())) flags|= JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); } JDTRefactoringDescriptor descriptor= null; try { final String description= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_descriptor_description_short, fMethod.getElementName()); final String header= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_descriptor_description, new String[] { getOldMethodSignature(), getNewMethodSignature()}); final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); if (!fMethod.getElementName().equals(fMethodName)) comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_new_name_pattern, fMethodName)); if (!isVisibilitySameAsInitial()) { String visibility= JdtFlags.getVisibilityString(fVisibility); if ("".equals(visibility)) //$NON-NLS-1$ visibility= RefactoringCoreMessages.ChangeSignatureRefactoring_default_visibility; comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_new_visibility_pattern, visibility)); } if (fReturnTypeInfo.isTypeNameChanged()) comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_new_return_type_pattern, fReturnTypeInfo.getNewTypeName())); List deleted= new ArrayList(); List added= new ArrayList(); List changed= new ArrayList(); for (final Iterator iterator= fParameterInfos.iterator(); iterator.hasNext();) { final ParameterInfo info= (ParameterInfo) iterator.next(); if (info.isDeleted()) deleted.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_deleted_parameter_pattern, new String[] { info.getOldTypeName(), info.getOldName()})); else if (info.isAdded()) added.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_added_parameter_pattern, new String[] { info.getNewTypeName(), info.getNewName()})); else if (info.isRenamed() || info.isTypeNameChanged() || info.isVarargChanged()) changed.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_changed_parameter_pattern, new String[] { info.getOldTypeName(), info.getOldName()})); } if (!added.isEmpty()) comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_added_parameters, (String[]) added.toArray(new String[added.size()]))); if (!deleted.isEmpty()) comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_removed_parameters, (String[]) deleted.toArray(new String[deleted.size()]))); if (!changed.isEmpty()) comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_changed_parameters, (String[]) changed.toArray(new String[changed.size()]))); added.clear(); deleted.clear(); changed.clear(); for (final Iterator iterator= fExceptionInfos.iterator(); iterator.hasNext();) { final ExceptionInfo info= (ExceptionInfo) iterator.next(); if (info.isAdded()) added.add(info.getType().getElementName()); else if (info.isDeleted()) deleted.add(info.getType().getElementName()); } if (!added.isEmpty()) comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_added_exceptions, (String[]) added.toArray(new String[added.size()]))); if (!deleted.isEmpty()) comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_removed_exceptions, (String[]) deleted.toArray(new String[deleted.size()]))); descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.CHANGE_METHOD_SIGNATURE, project, description, comment.asString(), arguments, flags); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fMethod)); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fMethodName); arguments.put(ATTRIBUTE_DELEGATE, Boolean.valueOf(fDelegateUpdating).toString()); arguments.put(ATTRIBUTE_DEPRECATE, Boolean.valueOf(fDelegateDeprecation).toString()); if (fReturnTypeInfo.isTypeNameChanged()) arguments.put(ATTRIBUTE_RETURN, fReturnTypeInfo.getNewTypeName()); try { if (!isVisibilitySameAsInitial()) arguments.put(ATTRIBUTE_VISIBILITY, Integer.valueOf(fVisibility).toString()); } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); } int count= 1; for (final Iterator iterator= fParameterInfos.iterator(); iterator.hasNext();) { final ParameterInfo info= (ParameterInfo) iterator.next(); final StringBuffer buffer= new StringBuffer(64); buffer.append(info.getOldTypeName()); buffer.append(" "); //$NON-NLS-1$ buffer.append(info.getOldName()); buffer.append(" "); //$NON-NLS-1$ buffer.append(info.getOldIndex()); buffer.append(" "); //$NON-NLS-1$ buffer.append(info.getNewTypeName()); buffer.append(" "); //$NON-NLS-1$ buffer.append(info.getNewName()); buffer.append(" "); //$NON-NLS-1$ buffer.append(info.isDeleted()); arguments.put(ATTRIBUTE_PARAMETER + count, buffer.toString()); final String value= info.getDefaultValue(); if (value != null && !"".equals(value)) //$NON-NLS-1$ arguments.put(ATTRIBUTE_DEFAULT + count, value); count++; } count= 1; for (final Iterator iterator= fExceptionInfos.iterator(); iterator.hasNext();) { final ExceptionInfo info= (ExceptionInfo) iterator.next(); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count, descriptor.elementToHandle(info.getType())); arguments.put(ATTRIBUTE_KIND + count, Integer.valueOf(info.getKind()).toString()); count++; } } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); return null; } return new DynamicValidationRefactoringChange(descriptor, doGetRefactoringChangeName(), (Change[]) list.toArray(new Change[list.size()])); } finally { pm.done(); clearManagers(); } } protected String doGetRefactoringChangeName() { return RefactoringCoreMessages.ChangeSignatureRefactoring_restructure_parameters; } private TextChangeManager createChangeManager(IProgressMonitor pm, RefactoringStatus result) throws CoreException { pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_preview, 2); fChangeManager= new TextChangeManager(); boolean isNoArgConstructor= isNoArgConstructor(); Map namedSubclassMapping= null; if (isNoArgConstructor){ //create only when needed; namedSubclassMapping= createNamedSubclassMapping(new SubProgressMonitor(pm, 1)); }else{ pm.worked(1); } for (int i= 0; i < fOccurrences.length; i++) { if (pm.isCanceled()) throw new OperationCanceledException(); SearchResultGroup group= fOccurrences[i]; IJavaScriptUnit cu= group.getCompilationUnit(); if (cu == null) continue; CompilationUnitRewrite cuRewrite; if (cu.equals(getCu())) { cuRewrite= fBaseCuRewrite; } else { cuRewrite= new CompilationUnitRewrite(cu); cuRewrite.getASTRewrite().setTargetSourceRangeComputer(new TightSourceRangeComputer()); } ASTNode[] nodes= ASTNodeSearchUtil.findNodes(group.getSearchResults(), cuRewrite.getRoot()); //IntroduceParameterObjectRefactoring needs to update declarations first: List/*<OccurrenceUpdate>*/ deferredUpdates= new ArrayList(); for (int j= 0; j < nodes.length; j++) { OccurrenceUpdate update= createOccurrenceUpdate(nodes[j], cuRewrite, result); if (update instanceof DeclarationUpdate) { update.updateNode(); } else { deferredUpdates.add(update); } } for (Iterator iter= deferredUpdates.iterator(); iter.hasNext();) { ((OccurrenceUpdate) iter.next()).updateNode(); } if (isNoArgConstructor && namedSubclassMapping.containsKey(cu)){ //only non-anonymous subclasses may have noArgConstructors to modify - see bug 43444 Set subtypes= (Set)namedSubclassMapping.get(cu); for (Iterator iter= subtypes.iterator(); iter.hasNext();) { IType subtype= (IType) iter.next(); AbstractTypeDeclaration subtypeNode= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(subtype, cuRewrite.getRoot()); if (subtypeNode != null) modifyImplicitCallsToNoArgConstructor(subtypeNode, cuRewrite); } } TextChange change= cuRewrite.createChange(); if (change != null) fChangeManager.manage(cu, change); } pm.done(); return fChangeManager; } //Map<IJavaScriptUnit, Set<IType>> private Map createNamedSubclassMapping(IProgressMonitor pm) throws JavaScriptModelException{ IType[] subclasses= getCachedTypeHierarchy(new SubProgressMonitor(pm, 1)).getSubclasses(fMethod.getDeclaringType()); Map result= new HashMap(); for (int i= 0; i < subclasses.length; i++) { IType subclass= subclasses[i]; if (subclass.isAnonymous()) continue; IJavaScriptUnit cu= subclass.getJavaScriptUnit(); if (! result.containsKey(cu)) result.put(cu, new HashSet()); ((Set)result.get(cu)).add(subclass); } return result; } private void modifyImplicitCallsToNoArgConstructor(AbstractTypeDeclaration subclass, CompilationUnitRewrite cuRewrite) { FunctionDeclaration[] constructors= getAllConstructors(subclass); if (constructors.length == 0){ addNewConstructorToSubclass(subclass, cuRewrite); } else { for (int i= 0; i < constructors.length; i++) { if (! containsImplicitCallToSuperConstructor(constructors[i])) continue; addExplicitSuperConstructorCall(constructors[i], cuRewrite); } } } private void addExplicitSuperConstructorCall(FunctionDeclaration constructor, CompilationUnitRewrite cuRewrite) { SuperConstructorInvocation superCall= constructor.getAST().newSuperConstructorInvocation(); addArgumentsToNewSuperConstructorCall(superCall, cuRewrite); String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_add_super_call; TextEditGroup description= cuRewrite.createGroupDescription(msg); cuRewrite.getASTRewrite().getListRewrite(constructor.getBody(), Block.STATEMENTS_PROPERTY).insertFirst(superCall, description); } private void addArgumentsToNewSuperConstructorCall(SuperConstructorInvocation superCall, CompilationUnitRewrite cuRewrite) { int i= 0; for (Iterator iter= getNotDeletedInfos().iterator(); iter.hasNext(); i++) { ParameterInfo info= (ParameterInfo) iter.next(); Expression newExpression= createNewExpression(info, getParameterInfos(), superCall.arguments(), cuRewrite, (FunctionDeclaration) ASTNodes.getParent(superCall, FunctionDeclaration.class)); if (newExpression != null) superCall.arguments().add(newExpression); } } private static boolean containsImplicitCallToSuperConstructor(FunctionDeclaration constructor) { Assert.isTrue(constructor.isConstructor()); Block body= constructor.getBody(); if (body == null) return false; if (body.statements().size() == 0) return true; if (body.statements().get(0) instanceof ConstructorInvocation) return false; if (body.statements().get(0) instanceof SuperConstructorInvocation) return false; return true; } private void addNewConstructorToSubclass(AbstractTypeDeclaration subclass, CompilationUnitRewrite cuRewrite) { AST ast= subclass.getAST(); FunctionDeclaration newConstructor= ast.newFunctionDeclaration(); newConstructor.setName(ast.newSimpleName(subclass.getName().getIdentifier())); newConstructor.setConstructor(true); newConstructor.setExtraDimensions(0); newConstructor.setJavadoc(null); newConstructor.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getAccessModifier(subclass))); newConstructor.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); Block body= ast.newBlock(); newConstructor.setBody(body); SuperConstructorInvocation superCall= ast.newSuperConstructorInvocation(); addArgumentsToNewSuperConstructorCall(superCall, cuRewrite); body.statements().add(superCall); String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_add_constructor; TextEditGroup description= cuRewrite.createGroupDescription(msg); cuRewrite.getASTRewrite().getListRewrite(subclass, subclass.getBodyDeclarationsProperty()).insertFirst(newConstructor, description); // TODO use AbstractTypeDeclaration } private static int getAccessModifier(AbstractTypeDeclaration subclass) { int modifiers= subclass.getModifiers(); if (Modifier.isPublic(modifiers)) return Modifier.PUBLIC; else if (Modifier.isProtected(modifiers)) return Modifier.PROTECTED; else if (Modifier.isPrivate(modifiers)) return Modifier.PRIVATE; else return Modifier.NONE; } private FunctionDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) { BodyDeclaration decl; List result= new ArrayList(1); for (Iterator it = typeDeclaration.bodyDeclarations().listIterator(); it.hasNext(); ) { decl= (BodyDeclaration) it.next(); if (decl instanceof FunctionDeclaration && ((FunctionDeclaration) decl).isConstructor()) result.add(decl); } return (FunctionDeclaration[]) result.toArray(new FunctionDeclaration[result.size()]); } private boolean isNoArgConstructor() throws JavaScriptModelException { return fMethod.isConstructor() && fMethod.getNumberOfParameters() == 0; } private Expression createNewExpression(ParameterInfo info, List parameterInfos, List nodes, CompilationUnitRewrite cuRewrite, FunctionDeclaration method) { if (info.isNewVarargs() && info.getDefaultValue().trim().length() == 0) return null; else { if (fDefaultValueAdvisor == null) return (Expression) cuRewrite.getASTRewrite().createStringPlaceholder(info.getDefaultValue(), ASTNode.FUNCTION_INVOCATION); else return fDefaultValueAdvisor.createDefaultExpression(nodes, info, parameterInfos, method, false, cuRewrite); } } private boolean isVisibilitySameAsInitial() throws JavaScriptModelException { return fVisibility == JdtFlags.getVisibilityCode(fMethod); } private IJavaScriptSearchScope createRefactoringScope() throws JavaScriptModelException{ return RefactoringScopeFactory.create(fMethod); } private SearchResultGroup[] findOccurrences(IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException{ if (fMethod.isConstructor()){ // workaround for bug 27236: return ConstructorReferenceFinder.getConstructorOccurrences(fMethod, pm, status); }else{ SearchPattern pattern= RefactoringSearchEngine.createOrPattern(fRippleMethods, IJavaScriptSearchConstants.ALL_OCCURRENCES); return RefactoringSearchEngine.search(pattern, createRefactoringScope(), pm, status); } } private static String createDeclarationString(ParameterInfo info) { if(JavaScriptCore.IS_ECMASCRIPT4) { String newTypeName= info.getNewTypeName(); int index= newTypeName.indexOf('.'); if (index != -1){ newTypeName= newTypeName.substring(index+1); } return newTypeName + " " + info.getNewName(); //$NON-NLS-1$ }else { return info.getNewName(); } } private OccurrenceUpdate createOccurrenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { if (isReferenceNode(node)) return new ReferenceUpdate(node, cuRewrite, result); else if (node instanceof SimpleName && node.getParent() instanceof FunctionDeclaration) return new DeclarationUpdate((FunctionDeclaration) node.getParent(), cuRewrite, result); else if (node instanceof MemberRef || node instanceof FunctionRef) return new DocReferenceUpdate(node, cuRewrite, result); else if (ASTNodes.getParent(node, ImportDeclaration.class) != null) return new StaticImportUpdate((ImportDeclaration) ASTNodes.getParent(node, ImportDeclaration.class), cuRewrite, result); else return new NullOccurrenceUpdate(node, cuRewrite, result); } private static boolean isReferenceNode(ASTNode node){ switch (node.getNodeType()) { case ASTNode.FUNCTION_INVOCATION : case ASTNode.SUPER_METHOD_INVOCATION : case ASTNode.CLASS_INSTANCE_CREATION : case ASTNode.CONSTRUCTOR_INVOCATION : case ASTNode.SUPER_CONSTRUCTOR_INVOCATION : return true; default : return false; } } abstract class OccurrenceUpdate { protected final CompilationUnitRewrite fCuRewrite; protected final TextEditGroup fDescription; protected RefactoringStatus fResult; protected OccurrenceUpdate(CompilationUnitRewrite cuRewrite, TextEditGroup description, RefactoringStatus result) { fCuRewrite= cuRewrite; fDescription= description; fResult= result; } protected final ASTRewrite getASTRewrite() { return fCuRewrite.getASTRewrite(); } protected final ImportRewrite getImportRewrite() { return fCuRewrite.getImportRewrite(); } protected final ImportRemover getImportRemover() { return fCuRewrite.getImportRemover(); } protected final CompilationUnitRewrite getCompilationUnitRewrite() { return fCuRewrite; } public abstract void updateNode() throws CoreException; protected void registerImportRemoveNode(ASTNode node) { getImportRemover().registerRemovedNode(node); } protected final void reshuffleElements() { if (isOrderSameAsInitial()) return; //varargs; method(p1, p2, .., pn), call(a1, a2, .., ax) : // if (method_was_vararg) { // assert fOldVarargIndex != -1 // if (vararg_retained) { // assert vararg_is_last_non_deleted (pn) // assert no_other_varargs // => reshuffle [1..n-1] then append remaining nodes [n..x], possibly none // // } else (vararg_deleted) { // assert all_are_non_vararg // => reshuffle [1..n-1], drop all remaining nodes [n..x], possibly none // } // // } else if (method_became_vararg) { // assert n == x // assert fOldVarargIndex == -1 // => reshuffle [1..n] // // } else (JLS2_case) { // assert n == x // assert fOldVarargIndex == -1 // => reshuffle [1..n] // } ListRewrite listRewrite= getParamgumentsRewrite(); Map newOldMap= new LinkedHashMap(); List nodes= listRewrite.getRewrittenList(); Iterator rewriteIter= nodes.iterator(); List original= listRewrite.getOriginalList(); for (Iterator iter= original.iterator(); iter.hasNext();) { newOldMap.put(rewriteIter.next(),iter.next()); } List newNodes= new ArrayList(); // register removed nodes, and collect nodes in new sequence: for (int i= 0; i < fParameterInfos.size(); i++) { ParameterInfo info= (ParameterInfo) fParameterInfos.get(i); int oldIndex= info.getOldIndex(); if (info.isDeleted()) { if (oldIndex != fOldVarargIndex) { registerImportRemoveNode((ASTNode) nodes.get(oldIndex)); } else { //vararg deleted -> remove all remaining nodes: for (int n= oldIndex; n < nodes.size(); n++) { registerImportRemoveNode((ASTNode) nodes.get(n)); } } } else if (info.isAdded()) { ASTNode newParamgument= createNewParamgument(info, fParameterInfos, nodes); if (newParamgument != null) newNodes.add(newParamgument); } else /* parameter stays */ { if (oldIndex != fOldVarargIndex) { ASTNode oldNode= (ASTNode) nodes.get(oldIndex); ASTNode movedNode= moveNode(oldNode, getASTRewrite()); newNodes.add(movedNode); } else { //vararg stays and is last parameter -> copy all remaining nodes: for (int n= oldIndex; n < nodes.size(); n++) { ASTNode oldNode= (ASTNode) nodes.get(n); ASTNode movedNode= moveNode(oldNode, getASTRewrite()); newNodes.add(movedNode); } } } } Iterator nodesIter= nodes.iterator(); Iterator newIter= newNodes.iterator(); //replace existing nodes with new ones: while (nodesIter.hasNext() && newIter.hasNext()) { ASTNode node= (ASTNode) nodesIter.next(); ASTNode newNode= (ASTNode) newIter.next(); if (!ASTNodes.isExistingNode(node)) //XXX:should better be addressed in ListRewriteEvent.replaceEntry(ASTNode, ASTNode) listRewrite.replace((ASTNode) newOldMap.get(node), newNode, fDescription); else listRewrite.replace(node, newNode, fDescription); } //remove remaining existing nodes: while (nodesIter.hasNext()) { ASTNode node= (ASTNode) nodesIter.next(); if (!ASTNodes.isExistingNode(node)) listRewrite.remove((ASTNode) newOldMap.get(node), fDescription); else listRewrite.remove(node, fDescription); } //add additional new nodes: while (newIter.hasNext()) { ASTNode node= (ASTNode) newIter.next(); listRewrite.insertLast(node, fDescription); } } /** * @return ListRewrite of parameters or arguments */ protected abstract ListRewrite getParamgumentsRewrite(); protected final void changeParamguments() { for (Iterator iter= getParameterInfos().iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); if (info.isAdded() || info.isDeleted()) continue; if (info.isRenamed()) changeParamgumentName(info); if (info.isTypeNameChanged()) changeParamgumentType(info); } } protected void changeParamgumentName(ParameterInfo info) { // no-op } protected void changeParamgumentType(ParameterInfo info) { // no-op } protected final void replaceTypeNode(Type typeNode, String newTypeName, ITypeBinding newTypeBinding){ Type newTypeNode= createNewTypeNode(newTypeName, newTypeBinding); getASTRewrite().replace(typeNode, newTypeNode, fDescription); registerImportRemoveNode(typeNode); getTightSourceRangeComputer().addTightSourceNode(typeNode); } /** * @param info * @param parameterInfos TODO * @param nodes TODO * @return a new method parameter or argument, or <code>null</code> for an empty vararg argument */ protected abstract ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes); protected abstract SimpleName getMethodNameNode(); protected final void changeMethodName() { if (! isMethodNameSameAsInitial()) { SimpleName nameNode= getMethodNameNode(); SimpleName newNameNode= nameNode.getAST().newSimpleName(fMethodName); getASTRewrite().replace(nameNode, newNameNode, fDescription); registerImportRemoveNode(nameNode); getTightSourceRangeComputer().addTightSourceNode(nameNode); } } protected final Type createNewTypeNode(String newTypeName, ITypeBinding newTypeBinding) { Type newTypeNode; if (newTypeBinding == null) { if (fDefaultValueAdvisor != null) newTypeNode= fDefaultValueAdvisor.createType(newTypeName, getMethodNameNode().getStartPosition(), getCompilationUnitRewrite()); else newTypeNode= (Type) getASTRewrite().createStringPlaceholder(newTypeName, ASTNode.SIMPLE_TYPE); //Don't import if not resolved. } else { newTypeNode= getImportRewrite().addImport(newTypeBinding, fCuRewrite.getAST()); getImportRemover().registerAddedImports(newTypeNode); } return newTypeNode; } protected final TightSourceRangeComputer getTightSourceRangeComputer() { return (TightSourceRangeComputer) fCuRewrite.getASTRewrite().getExtendedSourceRangeComputer(); } } class ReferenceUpdate extends OccurrenceUpdate { /** isReferenceNode(fNode) */ private ASTNode fNode; protected ReferenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_update_reference), result); fNode= node; //holds: Assert.isTrue(isReferenceNode(node)); } public void updateNode() { reshuffleElements(); changeMethodName(); } /** @return {@inheritDoc} (element type: Expression) */ protected ListRewrite getParamgumentsRewrite() { if (fNode instanceof FunctionInvocation) return getASTRewrite().getListRewrite(fNode, FunctionInvocation.ARGUMENTS_PROPERTY); if (fNode instanceof SuperMethodInvocation) return getASTRewrite().getListRewrite(fNode, SuperMethodInvocation.ARGUMENTS_PROPERTY); if (fNode instanceof ClassInstanceCreation) return getASTRewrite().getListRewrite(fNode, ClassInstanceCreation.ARGUMENTS_PROPERTY); if (fNode instanceof ConstructorInvocation) return getASTRewrite().getListRewrite(fNode, ConstructorInvocation.ARGUMENTS_PROPERTY); if (fNode instanceof SuperConstructorInvocation) return getASTRewrite().getListRewrite(fNode, SuperConstructorInvocation.ARGUMENTS_PROPERTY); return null; } protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) { CompilationUnitRewrite cuRewrite= getCompilationUnitRewrite(); FunctionDeclaration declaration= (FunctionDeclaration) ASTNodes.getParent(fNode, FunctionDeclaration.class); if (isRecursiveReference()) { return createNewExpressionRecursive(info, parameterInfos, nodes, cuRewrite, declaration); } else return createNewExpression(info, parameterInfos, nodes, cuRewrite, declaration); } private Expression createNewExpressionRecursive(ParameterInfo info, List parameterInfos, List nodes, CompilationUnitRewrite cuRewrite, FunctionDeclaration methodDeclaration) { if (fDefaultValueAdvisor != null && info.isAdded()) { return fDefaultValueAdvisor.createDefaultExpression(nodes, info, parameterInfos, methodDeclaration, true, cuRewrite); } return (Expression) getASTRewrite().createStringPlaceholder(info.getNewName(), ASTNode.FUNCTION_INVOCATION); } protected SimpleName getMethodNameNode() { if (fNode instanceof FunctionInvocation) return ((FunctionInvocation)fNode).getName(); if (fNode instanceof SuperMethodInvocation) return ((SuperMethodInvocation)fNode).getName(); return null; } private boolean isRecursiveReference() { FunctionDeclaration enclosingMethodDeclaration= (FunctionDeclaration) ASTNodes.getParent(fNode, FunctionDeclaration.class); if (enclosingMethodDeclaration == null) return false; IFunctionBinding enclosingMethodBinding= enclosingMethodDeclaration.resolveBinding(); if (enclosingMethodBinding == null) return false; if (fNode instanceof FunctionInvocation) return enclosingMethodBinding == ((FunctionInvocation)fNode).resolveMethodBinding(); if (fNode instanceof SuperMethodInvocation) { IFunctionBinding methodBinding= ((SuperMethodInvocation)fNode).resolveMethodBinding(); return isSameMethod(methodBinding, enclosingMethodBinding); } if (fNode instanceof ClassInstanceCreation) return enclosingMethodBinding == ((ClassInstanceCreation)fNode).resolveConstructorBinding(); if (fNode instanceof ConstructorInvocation) return enclosingMethodBinding == ((ConstructorInvocation)fNode).resolveConstructorBinding(); if (fNode instanceof SuperConstructorInvocation) { return false; //Constructors don't override -> enclosing has not been changed -> no recursion } Assert.isTrue(false); return false; } /** * @param m1 method 1 * @param m2 method 2 * @return true iff * <ul><li>the methods are both constructors with same argument types, or</li> * <li>the methods have the same name and the same argument types</li></ul> */ private boolean isSameMethod(IFunctionBinding m1, IFunctionBinding m2) { if (m1.isConstructor()) { if (! m2.isConstructor()) return false; } else { if (! m1.getName().equals(m2.getName())) return false; } ITypeBinding[] m1Parameters= m1.getParameterTypes(); ITypeBinding[] m2Parameters= m2.getParameterTypes(); if (m1Parameters.length != m2Parameters.length) return false; for (int i= 0; i < m1Parameters.length; i++) { if (m1Parameters[i].getErasure() != m2Parameters[i].getErasure()) return false; } return true; } } class DeclarationUpdate extends OccurrenceUpdate { private FunctionDeclaration fMethDecl; protected DeclarationUpdate(FunctionDeclaration decl, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_change_signature), result); fMethDecl= decl; } // Prevent import removing if delegate is created. protected void registerImportRemoveNode(ASTNode node) { if (!fDelegateUpdating) super.registerImportRemoveNode(node); } public void updateNode() throws CoreException { changeParamguments(); if (canChangeNameAndReturnType()) { changeMethodName(); changeReturnType(); } if (needsVisibilityUpdate()) changeVisibility(); reshuffleElements(); changeExceptions(); changeJavadocTags(); if (fBodyUpdater == null || fBodyUpdater.needsParameterUsedCheck()) checkIfDeletedParametersUsed(); if (fBodyUpdater != null) fBodyUpdater.updateBody(fMethDecl, fCuRewrite, fResult); if (fDelegateUpdating) addDelegate(); } private void addDelegate() throws JavaScriptModelException { DelegateMethodCreator creator= new DelegateMethodCreator(); creator.setDeclaration(fMethDecl); creator.setDeclareDeprecated(fDelegateDeprecation); creator.setSourceRewrite(fCuRewrite); creator.prepareDelegate(); /* * The delegate now contains a call and a javadoc reference to the * old method (i.e., to itself). * * Use ReferenceUpdate() / DocReferenceUpdate() to update these * references like any other reference. */ final ASTNode delegateInvocation= creator.getDelegateInvocation(); if (delegateInvocation != null) // may be null if the delegate is an interface method or // abstract -> no body new ReferenceUpdate(delegateInvocation, creator.getDelegateRewrite(), fResult).updateNode(); new DocReferenceUpdate(creator.getJavadocReference(), creator.getDelegateRewrite(), fResult).updateNode(); creator.createEdit(); } /** @return {@inheritDoc} (element type: SingleVariableDeclaration) */ protected ListRewrite getParamgumentsRewrite() { return getASTRewrite().getListRewrite(fMethDecl, FunctionDeclaration.PARAMETERS_PROPERTY); } protected void changeParamgumentName(ParameterInfo info) { SingleVariableDeclaration param= (SingleVariableDeclaration) fMethDecl.parameters().get(info.getOldIndex()); if (! info.getOldName().equals(param.getName().getIdentifier())) return; //don't change if original parameter name != name in rippleMethod String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_update_parameter_references; TextEditGroup description= fCuRewrite.createGroupDescription(msg); TempOccurrenceAnalyzer analyzer= new TempOccurrenceAnalyzer(param, false); analyzer.perform(); SimpleName[] paramOccurrences= analyzer.getReferenceAndDeclarationNodes(); // @param tags are updated in changeJavaDocTags() for (int j= 0; j < paramOccurrences.length; j++) { SimpleName occurence= paramOccurrences[j]; getASTRewrite().set(occurence, SimpleName.IDENTIFIER_PROPERTY, info.getNewName(), description); } } protected void changeParamgumentType(ParameterInfo info) { SingleVariableDeclaration oldParam= (SingleVariableDeclaration) fMethDecl.parameters().get(info.getOldIndex()); getASTRewrite().set(oldParam, SingleVariableDeclaration.VARARGS_PROPERTY, Boolean.valueOf(info.isNewVarargs()), fDescription); replaceTypeNode(oldParam.getType(), ParameterInfo.stripEllipsis(info.getNewTypeName()), info.getNewTypeBinding()); removeExtraDimensions(oldParam); } private void removeExtraDimensions(SingleVariableDeclaration oldParam) { if (oldParam.getExtraDimensions() != 0) { getASTRewrite().set(oldParam, SingleVariableDeclaration.EXTRA_DIMENSIONS_PROPERTY, Integer.valueOf(0), fDescription); } } private void changeReturnType() throws JavaScriptModelException { if (isReturnTypeSameAsInitial()) return; replaceTypeNode(fMethDecl.getReturnType2(), fReturnTypeInfo.getNewTypeName(), fReturnTypeInfo.getNewTypeBinding()); removeExtraDimensions(fMethDecl); //Remove expression from return statement when changed to void? No, would lose information! //Could add return statement with default value and add todo comment, but compile error is better. } private void removeExtraDimensions(FunctionDeclaration methDecl) { if (methDecl.getExtraDimensions() != 0) getASTRewrite().set(methDecl, FunctionDeclaration.EXTRA_DIMENSIONS_PROPERTY, Integer.valueOf(0), fDescription); } private boolean needsVisibilityUpdate() throws JavaScriptModelException { if (isVisibilitySameAsInitial()) return false; if (isIncreasingVisibility()) return JdtFlags.isHigherVisibility(fVisibility, JdtFlags.getVisibilityCode(fMethDecl)); else return JdtFlags.isHigherVisibility(JdtFlags.getVisibilityCode(fMethDecl), fVisibility); } private boolean isIncreasingVisibility() throws JavaScriptModelException{ return JdtFlags.isHigherVisibility(fVisibility, JdtFlags.getVisibilityCode(fMethod)); } private void changeVisibility() { ModifierRewrite.create(getASTRewrite(), fMethDecl).setVisibility(fVisibility, fDescription); } private void changeExceptions() { for (Iterator iter= fExceptionInfos.iterator(); iter.hasNext();) { ExceptionInfo info= (ExceptionInfo) iter.next(); if (info.isOld()) continue; if (info.isDeleted()) removeExceptionFromNodeList(info, fMethDecl.thrownExceptions()); else addExceptionToNodeList(info, getASTRewrite().getListRewrite(fMethDecl, FunctionDeclaration.THROWN_EXCEPTIONS_PROPERTY)); } } private void removeExceptionFromNodeList(ExceptionInfo toRemove, List exceptionsNodeList) { ITypeBinding typeToRemove= toRemove.getTypeBinding(); for (Iterator iter= exceptionsNodeList.iterator(); iter.hasNext(); ) { Name currentName= (Name) iter.next(); ITypeBinding currentType= currentName.resolveTypeBinding(); /* Maybe remove all subclasses of typeToRemove too. * Problem: * - B extends A; * - A.m() throws IOException, Exception; * - B.m() throws IOException, AWTException; * Removing Exception should remove AWTException, * but NOT remove IOException (or a subclass of JavaScriptModelException). */ // if (Bindings.isSuperType(typeToRemove, currentType)) if (currentType == null) continue; // newly added or unresolvable type if (Bindings.equals(currentType, typeToRemove) || toRemove.getType().getElementName().equals(currentType.getName())) { getASTRewrite().remove(currentName, fDescription); registerImportRemoveNode(currentName); } } } private void addExceptionToNodeList(ExceptionInfo exceptionInfo, ListRewrite exceptionListRewrite) { String fullyQualified= JavaModelUtil.getFullyQualifiedName(exceptionInfo.getType()); for (Iterator iter= exceptionListRewrite.getOriginalList().iterator(); iter.hasNext(); ) { Name exName= (Name) iter.next(); //XXX: existing superclasses of the added exception are redundant and could be removed ITypeBinding typeBinding= exName.resolveTypeBinding(); if (typeBinding == null) continue; // newly added or unresolvable type if (typeBinding.getQualifiedName().equals(fullyQualified)) return; // don't add it again } String importedType= getImportRewrite().addImport(JavaModelUtil.getFullyQualifiedName(exceptionInfo.getType())); getImportRemover().registerAddedImport(importedType); ASTNode exNode= getASTRewrite().createStringPlaceholder(importedType, ASTNode.SIMPLE_NAME); exceptionListRewrite.insertLast(exNode, fDescription); } private void changeJavadocTags() throws JavaScriptModelException { //update tags in javadoc: @param, @return, @exception, @throws, ... JSdoc javadoc= fMethDecl.getJavadoc(); if (javadoc == null) return; ITypeBinding typeBinding= Bindings.getBindingOfParentType(fMethDecl); if (typeBinding == null) return; IFunctionBinding methodBinding= fMethDecl.resolveBinding(); if (methodBinding == null) return; boolean isTopOfRipple= (Bindings.findOverriddenMethod(methodBinding, false) == null); //add tags: only iff top of ripple; change and remove: always. //TODO: should have preference for adding tags in (overriding) methods (with template: todo, inheritDoc, ...) List tags= javadoc.tags(); // List of TagElement ListRewrite tagsRewrite= getASTRewrite().getListRewrite(javadoc, JSdoc.TAGS_PROPERTY); if (! isReturnTypeSameAsInitial()) { if (PrimitiveType.VOID.toString().equals(fReturnTypeInfo.getNewTypeName())) { for (int i = 0; i < tags.size(); i++) { TagElement tag= (TagElement) tags.get(i); if (TagElement.TAG_RETURN.equals(tag.getTagName())) { getASTRewrite().remove(tag, fDescription); registerImportRemoveNode(tag); } } } else if (isTopOfRipple && Signature.SIG_VOID.equals(fMethod.getReturnType())){ TagElement returnNode= createReturnTag(); TagElement previousTag= findTagElementToInsertAfter(tags, TagElement.TAG_RETURN); insertTag(returnNode, previousTag, tagsRewrite); tags= tagsRewrite.getRewrittenList(); } } if (! (areNamesSameAsInitial() && isOrderSameAsInitial())) { ArrayList paramTags= new ArrayList(); // <TagElement>, only not deleted tags with simpleName // delete & rename: for (Iterator iter = tags.iterator(); iter.hasNext(); ) { TagElement tag = (TagElement) iter.next(); String tagName= tag.getTagName(); List fragments= tag.fragments(); if (! (TagElement.TAG_PARAM.equals(tagName) && fragments.size() > 0 && fragments.get(0) instanceof SimpleName)) continue; SimpleName simpleName= (SimpleName) fragments.get(0); String identifier= simpleName.getIdentifier(); boolean removed= false; for (int i= 0; i < fParameterInfos.size(); i++) { ParameterInfo info= (ParameterInfo) fParameterInfos.get(i); if (identifier.equals(info.getOldName())) { if (info.isDeleted()) { getASTRewrite().remove(tag, fDescription); registerImportRemoveNode(tag); removed= true; } else if (info.isRenamed()) { SimpleName newName= simpleName.getAST().newSimpleName(info.getNewName()); getASTRewrite().replace(simpleName, newName, fDescription); registerImportRemoveNode(tag); } break; } } if (! removed) paramTags.add(tag); } tags= tagsRewrite.getRewrittenList(); if (! isOrderSameAsInitial()) { // reshuffle (sort in declaration sequence) & add (only add to top of ripple): TagElement previousTag= findTagElementToInsertAfter(tags, TagElement.TAG_PARAM); boolean first= true; // workaround for bug 92111: preserve first tag if possible // reshuffle: for (Iterator infoIter= fParameterInfos.iterator(); infoIter.hasNext();) { ParameterInfo info= (ParameterInfo) infoIter.next(); String oldName= info.getOldName(); String newName= info.getNewName(); if (info.isAdded()) { first= false; if (! isTopOfRipple) continue; TagElement paramNode= JavadocUtil.createParamTag(newName, fCuRewrite.getRoot().getAST(), fCuRewrite.getCu().getJavaScriptProject()); insertTag(paramNode, previousTag, tagsRewrite); previousTag= paramNode; } else { for (Iterator tagIter= paramTags.iterator(); tagIter.hasNext();) { TagElement tag= (TagElement) tagIter.next(); SimpleName tagName= (SimpleName) tag.fragments().get(0); if (oldName.equals(tagName.getIdentifier())) { tagIter.remove(); if (first) { previousTag= tag; } else { TagElement movedTag= (TagElement) getASTRewrite().createMoveTarget(tag); getASTRewrite().remove(tag, fDescription); insertTag(movedTag, previousTag, tagsRewrite); previousTag= movedTag; } } first= false; } } } // params with bad names: for (Iterator iter= paramTags.iterator(); iter.hasNext();) { TagElement tag= (TagElement) iter.next(); TagElement movedTag= (TagElement) getASTRewrite().createMoveTarget(tag); getASTRewrite().remove(tag, fDescription); insertTag(movedTag, previousTag, tagsRewrite); previousTag= movedTag; } } tags= tagsRewrite.getRewrittenList(); } if (! areExceptionsSameAsInitial()) { // collect exceptionTags and remove deleted: ArrayList exceptionTags= new ArrayList(); // <TagElement>, only not deleted tags with name for (int i= 0; i < tags.size(); i++) { TagElement tag= (TagElement) tags.get(i); if (! TagElement.TAG_THROWS.equals(tag.getTagName()) && ! TagElement.TAG_EXCEPTION.equals(tag.getTagName())) continue; if (! (tag.fragments().size() > 0 && tag.fragments().get(0) instanceof Name)) continue; boolean tagDeleted= false; Name name= (Name) tag.fragments().get(0); for (int j= 0; j < fExceptionInfos.size(); j++) { ExceptionInfo info= (ExceptionInfo) fExceptionInfos.get(j); if (info.isDeleted()) { boolean remove= false; final ITypeBinding nameBinding= name.resolveTypeBinding(); if (nameBinding != null) { final ITypeBinding infoBinding= info.getTypeBinding(); if (infoBinding != null && Bindings.equals(infoBinding, nameBinding)) remove= true; else if (info.getType().getElementName().equals(nameBinding.getName())) remove= true; if (remove) { getASTRewrite().remove(tag, fDescription); registerImportRemoveNode(tag); tagDeleted= true; break; } } } } if (! tagDeleted) exceptionTags.add(tag); } // reshuffle: tags= tagsRewrite.getRewrittenList(); TagElement previousTag= findTagElementToInsertAfter(tags, TagElement.TAG_THROWS); for (Iterator infoIter= fExceptionInfos.iterator(); infoIter.hasNext();) { ExceptionInfo info= (ExceptionInfo) infoIter.next(); if (info.isAdded()) { if (!isTopOfRipple) continue; TagElement excptNode= createExceptionTag(info.getType().getElementName()); insertTag(excptNode, previousTag, tagsRewrite); previousTag= excptNode; } else { for (Iterator tagIter= exceptionTags.iterator(); tagIter.hasNext();) { TagElement tag= (TagElement) tagIter.next(); Name tagName= (Name) tag.fragments().get(0); final ITypeBinding nameBinding= tagName.resolveTypeBinding(); if (nameBinding != null) { boolean process= false; final ITypeBinding infoBinding= info.getTypeBinding(); if (infoBinding != null && Bindings.equals(infoBinding, nameBinding)) process= true; else if (info.getType().getElementName().equals(nameBinding.getName())) process= true; if (process) { tagIter.remove(); TagElement movedTag= (TagElement) getASTRewrite().createMoveTarget(tag); getASTRewrite().remove(tag, fDescription); insertTag(movedTag, previousTag, tagsRewrite); previousTag= movedTag; } } } } } // exceptions with bad names: for (Iterator iter= exceptionTags.iterator(); iter.hasNext();) { TagElement tag= (TagElement) iter.next(); TagElement movedTag= (TagElement) getASTRewrite().createMoveTarget(tag); getASTRewrite().remove(tag, fDescription); insertTag(movedTag, previousTag, tagsRewrite); previousTag= movedTag; } } } private TagElement createReturnTag() { TagElement returnNode= getASTRewrite().getAST().newTagElement(); returnNode.setTagName(TagElement.TAG_RETURN); TextElement textElement= getASTRewrite().getAST().newTextElement(); String text= StubUtility.getTodoTaskTag(fCuRewrite.getCu().getJavaScriptProject()); if (text != null) textElement.setText(text); //TODO: use template with {@todo} ... returnNode.fragments().add(textElement); return returnNode; } private TagElement createExceptionTag(String simpleName) { TagElement excptNode= getASTRewrite().getAST().newTagElement(); excptNode.setTagName(TagElement.TAG_THROWS); SimpleName nameNode= getASTRewrite().getAST().newSimpleName(simpleName); excptNode.fragments().add(nameNode); TextElement textElement= getASTRewrite().getAST().newTextElement(); String text= StubUtility.getTodoTaskTag(fCuRewrite.getCu().getJavaScriptProject()); if (text != null) textElement.setText(text); //TODO: use template with {@todo} ... excptNode.fragments().add(textElement); return excptNode; } private void insertTag(TagElement tag, TagElement previousTag, ListRewrite tagsRewrite) { if (previousTag == null) tagsRewrite.insertFirst(tag, fDescription); else tagsRewrite.insertAfter(tag, previousTag, fDescription); } /** * @param tags existing tags * @param tagName name of tag to add * @return the <code>TagElement<code> just before a new <code>TagElement</code> with name <code>tagName</code>, * or <code>null</code>. */ private TagElement findTagElementToInsertAfter(List tags, String tagName) { List tagOrder= Arrays.asList(new String[] { TagElement.TAG_AUTHOR, TagElement.TAG_VERSION, TagElement.TAG_PARAM, TagElement.TAG_RETURN, TagElement.TAG_THROWS, TagElement.TAG_EXCEPTION, TagElement.TAG_SEE, TagElement.TAG_SINCE, TagElement.TAG_SERIAL, TagElement.TAG_SERIALFIELD, TagElement.TAG_SERIALDATA, TagElement.TAG_DEPRECATED, TagElement.TAG_VALUE }); int goalOrdinal= tagOrder.indexOf(tagName); if (goalOrdinal == -1) // unknown tag -> to end return (tags.size() == 0) ? null : (TagElement) tags.get(tags.size()); for (int i= 0; i < tags.size(); i++) { int tagOrdinal= tagOrder.indexOf(((TagElement) tags.get(i)).getTagName()); if (tagOrdinal >= goalOrdinal) return (i == 0) ? null : (TagElement) tags.get(i-1); } return (tags.size() == 0) ? null : (TagElement) tags.get(tags.size()-1); } //TODO: already reported as compilation error -> don't report there? private void checkIfDeletedParametersUsed() { for (Iterator iter= getDeletedInfos().iterator(); iter.hasNext();) { ParameterInfo info= (ParameterInfo) iter.next(); SingleVariableDeclaration paramDecl= (SingleVariableDeclaration) fMethDecl.parameters().get(info.getOldIndex()); TempOccurrenceAnalyzer analyzer= new TempOccurrenceAnalyzer(paramDecl, false); analyzer.perform(); SimpleName[] paramRefs= analyzer.getReferenceNodes(); if (paramRefs.length > 0){ RefactoringStatusContext context= JavaStatusContext.create(fCuRewrite.getCu(), paramRefs[0]); String typeName= getFullTypeName(fMethDecl); Object[] keys= new String[]{paramDecl.getName().getIdentifier(), fMethDecl.getName().getIdentifier(), typeName}; String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_parameter_used, keys); fResult.addError(msg, context); } } } private String getFullTypeName(FunctionDeclaration decl) { ASTNode node= decl; while (true) { node= node.getParent(); if (node instanceof AbstractTypeDeclaration) { return ((AbstractTypeDeclaration) node).getName().getIdentifier(); } else if (node instanceof ClassInstanceCreation) { ClassInstanceCreation cic= (ClassInstanceCreation) node; return Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_anonymous_subclass, new String[]{ASTNodes.asString(cic.getType())}); } } } protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) { return createNewSingleVariableDeclaration(info); } private SingleVariableDeclaration createNewSingleVariableDeclaration(ParameterInfo info) { SingleVariableDeclaration newP= getASTRewrite().getAST().newSingleVariableDeclaration(); newP.setName(getASTRewrite().getAST().newSimpleName(info.getNewName())); newP.setType(createNewTypeNode(ParameterInfo.stripEllipsis(info.getNewTypeName()), info.getNewTypeBinding())); newP.setVarargs(info.isNewVarargs()); return newP; } protected SimpleName getMethodNameNode() { return fMethDecl.getName(); } } class DocReferenceUpdate extends OccurrenceUpdate { /** instanceof MemberRef || FunctionRef */ private ASTNode fNode; protected DocReferenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_update_javadoc_reference), result); fNode= node; } public void updateNode() { if (fNode instanceof FunctionRef) { changeParamguments(); reshuffleElements(); } if (canChangeNameAndReturnType()) changeMethodName(); } protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) { return createNewMethodRefParameter(info); } private FunctionRefParameter createNewMethodRefParameter(ParameterInfo info) { FunctionRefParameter newP= getASTRewrite().getAST().newFunctionRefParameter(); // only add name iff first parameter already has a name: List parameters= getParamgumentsRewrite().getOriginalList(); if (parameters.size() > 0) if (((FunctionRefParameter) parameters.get(0)).getName() != null) newP.setName(getASTRewrite().getAST().newSimpleName(info.getNewName())); newP.setType(createNewDocRefType(info)); newP.setVarargs(info.isNewVarargs()); return newP; } private Type createNewDocRefType(ParameterInfo info) { String newTypeName= ParameterInfo.stripEllipsis(info.getNewTypeName()); ITypeBinding newTypeBinding= info.getNewTypeBinding(); if (newTypeBinding != null) newTypeBinding= newTypeBinding.getErasure(); //see bug 83127: Javadoc references are raw (erasures) return createNewTypeNode(newTypeName, newTypeBinding); } protected SimpleName getMethodNameNode() { if (fNode instanceof MemberRef) return ((MemberRef) fNode).getName(); if (fNode instanceof FunctionRef) return ((FunctionRef) fNode).getName(); return null; } /** @return {@inheritDoc} (element type: FunctionRefParameter) */ protected ListRewrite getParamgumentsRewrite() { return getASTRewrite().getListRewrite(fNode, FunctionRef.PARAMETERS_PROPERTY); } protected void changeParamgumentName(ParameterInfo info) { if (! (fNode instanceof FunctionRef)) return; FunctionRefParameter oldParam= (FunctionRefParameter) ((FunctionRef) fNode).parameters().get(info.getOldIndex()); SimpleName oldParamName= oldParam.getName(); if (oldParamName != null) getASTRewrite().set(oldParamName, SimpleName.IDENTIFIER_PROPERTY, info.getNewName(), fDescription); } protected void changeParamgumentType(ParameterInfo info) { if (! (fNode instanceof FunctionRef)) return; FunctionRefParameter oldParam= (FunctionRefParameter) ((FunctionRef) fNode).parameters().get(info.getOldIndex()); Type oldTypeNode= oldParam.getType(); Type newTypeNode= createNewDocRefType(info); if (info.isNewVarargs()) { if (info.isOldVarargs() && ! oldParam.isVarargs()) { // leave as array reference of old reference was not vararg newTypeNode= getASTRewrite().getAST().newArrayType(newTypeNode); } else { getASTRewrite().set(oldParam, FunctionRefParameter.VARARGS_PROPERTY, Boolean.TRUE, fDescription); } } else { if (oldParam.isVarargs()) { getASTRewrite().set(oldParam, FunctionRefParameter.VARARGS_PROPERTY, Boolean.FALSE, fDescription); } } getASTRewrite().replace(oldTypeNode, newTypeNode, fDescription); registerImportRemoveNode(oldTypeNode); } } class StaticImportUpdate extends OccurrenceUpdate { private final ImportDeclaration fImportDecl; public StaticImportUpdate(ImportDeclaration importDecl, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { super(cuRewrite, null, result); fImportDecl= importDecl; } public void updateNode() throws JavaScriptModelException { ImportRewrite importRewrite= fCuRewrite.getImportRewrite(); QualifiedName name= (QualifiedName) fImportDecl.getName(); //will be removed by importRemover if not used elsewhere ... importRewrite.removeStaticImport(name.getFullyQualifiedName()); importRewrite.addStaticImport(name.getQualifier().getFullyQualifiedName(), fMethodName, false); } protected ListRewrite getParamgumentsRewrite() { return null; } protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) { return null; } protected SimpleName getMethodNameNode() { return null; } } class NullOccurrenceUpdate extends OccurrenceUpdate { private ASTNode fNode; protected NullOccurrenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { super(cuRewrite, null, result); fNode= node; } public void updateNode() throws JavaScriptModelException { int start= fNode.getStartPosition(); int length= fNode.getLength(); String msg= "Cannot update found node: nodeType=" + fNode.getNodeType() + "; " //$NON-NLS-1$//$NON-NLS-2$ + fNode.toString() + "[" + start + ", " + length + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ JavaScriptPlugin.log(new Exception(msg + ":\n" + fCuRewrite.getCu().getSource().substring(start, start + length))); //$NON-NLS-1$ fResult.addError(msg, JavaStatusContext.create(fCuRewrite.getCu(), fNode)); } protected ListRewrite getParamgumentsRewrite() { return null; } protected ASTNode createNewParamgument(ParameterInfo info, List parameterInfos, List nodes) { return null; } protected SimpleName getMethodNameNode() { return null; } } public RefactoringStatus initialize(final RefactoringArguments arguments) { if (arguments instanceof JavaRefactoringArguments) { final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; 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.METHOD) return createInputFatalStatus(element, IJavaScriptRefactorings.CHANGE_METHOD_SIGNATURE); else { fMethod= (IFunction) element; fMethodName= fMethod.getElementName(); try { fVisibility= JdtFlags.getVisibilityCode(fMethod); fReturnTypeInfo= new ReturnTypeInfo(Signature.toString(Signature.getReturnType(fMethod.getSignature()))); } catch (JavaScriptModelException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, ATTRIBUTE_VISIBILITY)); } } } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); final String name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME); if (name != null) { fMethodName= name; final RefactoringStatus status= Checks.checkMethodName(fMethodName); if (status.hasError()) return status; } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME)); final String type= extended.getAttribute(ATTRIBUTE_RETURN); if (type != null && !"".equals(type)) //$NON-NLS-1$ fReturnTypeInfo= new ReturnTypeInfo(type); final String visibility= extended.getAttribute(ATTRIBUTE_VISIBILITY); if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$ int flag= 0; try { flag= Integer.parseInt(visibility); } catch (NumberFormatException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY)); } fVisibility= flag; } int count= 1; String attribute= ATTRIBUTE_PARAMETER + count; String value= null; fParameterInfos= new ArrayList(3); while ((value= extended.getAttribute(attribute)) != null) { StringTokenizer tokenizer= new StringTokenizer(value); if (tokenizer.countTokens() < 6) return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, ATTRIBUTE_PARAMETER)); String oldTypeName= tokenizer.nextToken(); String oldName= tokenizer.nextToken(); String oldIndex= tokenizer.nextToken(); String newTypeName= tokenizer.nextToken(); String newName= tokenizer.nextToken(); String deleted= tokenizer.nextToken(); ParameterInfo info= null; try { info= new ParameterInfo(oldTypeName, oldName, Integer.valueOf(oldIndex).intValue()); info.setNewTypeName(newTypeName); info.setNewName(newName); if (Boolean.valueOf(deleted).booleanValue()) info.markAsDeleted(); fParameterInfos.add(info); } catch (NumberFormatException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, ATTRIBUTE_PARAMETER)); } final String result= extended.getAttribute(ATTRIBUTE_DEFAULT + count); if (result != null && !"".equals(result)) //$NON-NLS-1$ info.setDefaultValue(result); count++; attribute= ATTRIBUTE_PARAMETER + count; } count= 1; fExceptionInfos= new ArrayList(2); attribute= JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count; while ((value= extended.getAttribute(attribute)) != null) { ExceptionInfo info= null; final String kind= extended.getAttribute(ATTRIBUTE_KIND + count); if (kind != null) { final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), value, false); if (element == null || !element.exists()) return createInputFatalStatus(element, IJavaScriptRefactorings.CHANGE_METHOD_SIGNATURE); else { try { info= new ExceptionInfo((IType) element, Integer.valueOf(kind).intValue(), null); } catch (NumberFormatException exception) { return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, ATTRIBUTE_KIND)); } } } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, ATTRIBUTE_KIND)); fExceptionInfos.add(info); count++; attribute= JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count; } final String deprecate= extended.getAttribute(ATTRIBUTE_DEPRECATE); if (deprecate != null) { fDelegateDeprecation= Boolean.valueOf(deprecate).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DEPRECATE)); final String delegate= extended.getAttribute(ATTRIBUTE_DELEGATE); if (delegate != null) { fDelegateUpdating= Boolean.valueOf(delegate).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELEGATE)); } else return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); return new RefactoringStatus(); } /** * If this occurrence update is called from within a declaration update * (i.e., to update the call inside the newly created delegate), the old * node does not yet exist and therefore cannot be a move target. * * Normally, always use createMoveTarget as this has the advantage of * being able to add changes inside changed nodes (for example, a method * call within a method call, see test case #4) and preserving comments * inside calls. * @param oldNode original node * @param rewrite an AST rewrite * @return the node to insert at the target location */ protected ASTNode moveNode(ASTNode oldNode, ASTRewrite rewrite) { ASTNode movedNode; if (ASTNodes.isExistingNode(oldNode)) movedNode= rewrite.createMoveTarget(oldNode); //node must be one of ast else movedNode= ASTNode.copySubtree(rewrite.getAST(), oldNode); return movedNode; } public String getDelegateUpdatingTitle(boolean plural) { if (plural) return RefactoringCoreMessages.DelegateCreator_keep_original_changed_plural; else return RefactoringCoreMessages.DelegateCreator_keep_original_changed_singular; } public IDefaultValueAdvisor getDefaultValueAdvisor() { return fDefaultValueAdvisor; } public void setDefaultValueAdvisor(IDefaultValueAdvisor defaultValueAdvisor) { fDefaultValueAdvisor= defaultValueAdvisor; } }