| /******************************************************************************* |
| * Copyright (c) 2000, 2012 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.jdt.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.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.core.resources.IFile; |
| |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| 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.CheckConditionsContext; |
| import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; |
| import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor; |
| import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ConstructorInvocation; |
| import org.eclipse.jdt.core.dom.EnumConstantDeclaration; |
| import org.eclipse.jdt.core.dom.EnumDeclaration; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.ImportDeclaration; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.MemberRef; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.MethodRef; |
| import org.eclipse.jdt.core.dom.MethodRefParameter; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.NodeFinder; |
| import org.eclipse.jdt.core.dom.PrimitiveType; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.SuperConstructorInvocation; |
| import org.eclipse.jdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.jdt.core.dom.TagElement; |
| import org.eclipse.jdt.core.dom.TextElement; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; |
| import org.eclipse.jdt.core.dom.rewrite.ListRewrite; |
| import org.eclipse.jdt.core.refactoring.IJavaRefactorings; |
| import org.eclipse.jdt.core.refactoring.descriptors.ChangeMethodSignatureDescriptor; |
| import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; |
| import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments; |
| import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments.Parameter; |
| import org.eclipse.jdt.core.refactoring.participants.ChangeMethodSignatureArguments.ThrownException; |
| import org.eclipse.jdt.core.refactoring.participants.IRefactoringProcessorIds; |
| import org.eclipse.jdt.core.refactoring.participants.JavaParticipantManager; |
| import org.eclipse.jdt.core.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.MethodReferenceMatch; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.core.search.SearchMatch; |
| import org.eclipse.jdt.core.search.SearchPattern; |
| |
| import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; |
| import org.eclipse.jdt.internal.corext.Corext; |
| import org.eclipse.jdt.internal.corext.SourceRangeFactory; |
| import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; |
| import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.jdt.internal.corext.dom.ModifierRewrite; |
| import org.eclipse.jdt.internal.corext.dom.Selection; |
| import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.CuCollectingSearchRequestor; |
| import org.eclipse.jdt.internal.corext.refactoring.ExceptionInfo; |
| import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; |
| import org.eclipse.jdt.internal.corext.refactoring.ReturnTypeInfo; |
| import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; |
| import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext; |
| import org.eclipse.jdt.internal.corext.refactoring.TypeContextChecker; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaStringStatusContext; |
| import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes; |
| import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; |
| import org.eclipse.jdt.internal.corext.refactoring.code.Invocations; |
| import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator; |
| import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors; |
| import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks; |
| import org.eclipse.jdt.internal.corext.refactoring.rename.RefactoringAnalyzeUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2; |
| import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer; |
| import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating; |
| import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.JavadocUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; |
| import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| import org.eclipse.jdt.internal.corext.util.SearchUtils; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; |
| import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; |
| |
| |
| public class ChangeSignatureProcessor extends RefactoringProcessor 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 List<ParameterInfo> fParameterInfos; |
| |
| private CompilationUnitRewrite fBaseCuRewrite; |
| private List<ExceptionInfo> fExceptionInfos; |
| private TextChangeManager fChangeManager; |
| |
| private IMethod fMethod; |
| private IMethod fTopMethod; |
| private IMethod[] 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; |
| |
| public ChangeSignatureProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) throws JavaModelException { |
| this((IMethod) null); |
| status.merge(initialize(arguments)); |
| } |
| |
| /** |
| * Creates a new change signature refactoring. |
| * @param method the method, or <code>null</code> if invoked by scripting framework |
| * @throws JavaModelException if something's wrong with the given method |
| */ |
| public ChangeSignatureProcessor(IMethod method) throws JavaModelException { |
| 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<ParameterInfo> createParameterInfoList(IMethod method) { |
| try { |
| String[] typeNames= method.getParameterTypes(); |
| String[] oldNames= method.getParameterNames(); |
| List<ParameterInfo> result= new ArrayList<ParameterInfo>(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(JavaModelException e) { |
| JavaPlugin.log(e); |
| return new ArrayList<ParameterInfo>(0); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName() |
| */ |
| @Override |
| public String getProcessorName() { |
| return RefactoringCoreMessages.ChangeSignatureRefactoring_modify_Parameters; |
| } |
| |
| public IMethod 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 (JavaModelException e) { |
| JavaPlugin.log(e); |
| return false; |
| } |
| } |
| |
| /** |
| * @return visibility |
| * @see org.eclipse.jdt.core.dom.Modifier |
| */ |
| public int getVisibility(){ |
| return fVisibility; |
| } |
| |
| /** |
| * @param visibility new visibility |
| * @see org.eclipse.jdt.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 JavaModelException{ |
| 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<ParameterInfo> getParameterInfos(){ |
| return fParameterInfos; |
| } |
| |
| /** |
| * @return List of <code>ExceptionInfo</code> objects. |
| */ |
| public List<ExceptionInfo> 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; |
| } |
| |
| public String getDelegateUpdatingTitle(boolean plural) { |
| if (plural) |
| return RefactoringCoreMessages.DelegateCreator_keep_original_changed_plural; |
| else |
| return RefactoringCoreMessages.DelegateCreator_keep_original_changed_singular; |
| } |
| |
| //------------------- /IDelegateUpdating --------------------- |
| |
| public RefactoringStatus checkSignature() { |
| return checkSignature(false); |
| } |
| |
| private RefactoringStatus checkSignature(boolean resolveBindings) { |
| 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); |
| 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 JavaModelException { |
| 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 |
| */ |
| public boolean isSignatureClashWithInitial() { |
| |
| 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<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (! info.isAdded() && ! info.isDeleted() && info.isTypeNameChanged()) |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isReturnTypeSameAsInitial() { |
| return ! fReturnTypeInfo.isTypeNameChanged(); |
| } |
| |
| private boolean isMethodNameSameAsInitial() { |
| return fMethodName.equals(fMethod.getElementName()); |
| } |
| |
| private boolean areExceptionsSameAsInitial() { |
| for (Iterator<ExceptionInfo> iter= fExceptionInfos.iterator(); iter.hasNext();) { |
| ExceptionInfo info= iter.next(); |
| if (! info.isOld()) |
| return false; |
| } |
| return true; |
| } |
| |
| private void checkParameterNamesAndValues(RefactoringStatus result) { |
| int i= 1; |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext(); i++) { |
| ParameterInfo info= 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(), fMethod)); |
| } |
| } |
| |
| 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 (fMethodName.equals(fMethod.getDeclaringType().getElementName())) { |
| String msg= RefactoringCoreMessages.ChangeSignatureRefactoring_constructor_name; |
| result.addWarning(msg); |
| } |
| result.merge(Checks.checkMethodName(fMethodName, fMethod)); |
| } |
| |
| 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, BasicElementLabels.getJavaElementName(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 JavaModelException { |
| RefactoringStatus result= checkOriginalVarargs(); |
| if (result != null) |
| return result; |
| |
| if (fRippleMethods != null) { |
| for (int iRipple= 0; iRipple < fRippleMethods.length; iRipple++) { |
| IMethod 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<ParameterInfo> notDeletedInfos= getNotDeletedInfos(); |
| for (int i= 0; i < notDeletedInfos.size(); i++) { |
| ParameterInfo info= notDeletedInfos.get(i); |
| if (fOldVarargIndex != -1 && info.getOldIndex() == fOldVarargIndex && ! info.isNewVarargs()) { |
| String rippleMethodType= rippleMethod.getDeclaringType().getFullyQualifiedName('.'); |
| String message= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_ripple_cannot_convert_vararg, new Object[] { BasicElementLabels.getJavaElementName(info.getNewName()), BasicElementLabels.getJavaElementName(rippleMethodType)}); |
| return RefactoringStatus.createFatalErrorStatus(message, JavaStatusContext.create(rippleMethod)); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private RefactoringStatus checkOriginalVarargs() throws JavaModelException { |
| if (JdtFlags.isVarargs(fMethod)) |
| fOldVarargIndex= fMethod.getNumberOfParameters() - 1; |
| List<ParameterInfo> notDeletedInfos= getNotDeletedInfos(); |
| for (int i= 0; i < notDeletedInfos.size(); i++) { |
| ParameterInfo info= notDeletedInfos.get(i); |
| if (info.isOldVarargs() && ! info.isNewVarargs()) |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_cannot_convert_vararg, BasicElementLabels.getJavaElementName(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, BasicElementLabels.getJavaElementName(info.getNewName()))); |
| } |
| } |
| return null; |
| } |
| |
| private RefactoringStatus checkTypeVariables() { |
| if (fRippleMethods.length == 1) |
| return null; |
| |
| RefactoringStatus result= new RefactoringStatus(); |
| if (fReturnTypeInfo.isTypeNameChanged() && fReturnTypeInfo.getNewTypeBinding() != null) { |
| HashSet<ITypeBinding> typeVariablesCollector= new HashSet<ITypeBinding>(); |
| collectTypeVariables(fReturnTypeInfo.getNewTypeBinding(), typeVariablesCollector); |
| if (typeVariablesCollector.size() != 0) { |
| ITypeBinding first= typeVariablesCollector.iterator().next(); |
| String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_return_type_contains_type_variable, new String[] {BasicElementLabels.getJavaElementName(fReturnTypeInfo.getNewTypeName()), BasicElementLabels.getJavaElementName(first.getName())}); |
| result.addError(msg); |
| } |
| } |
| |
| for (Iterator<ParameterInfo> iter= getNotDeletedInfos().iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (info.isTypeNameChanged() && info.getNewTypeBinding() != null) { |
| HashSet<ITypeBinding> typeVariablesCollector= new HashSet<ITypeBinding>(); |
| collectTypeVariables(info.getNewTypeBinding(), typeVariablesCollector); |
| if (typeVariablesCollector.size() != 0) { |
| ITypeBinding first= typeVariablesCollector.iterator().next(); |
| String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_parameter_type_contains_type_variable, new String[] {BasicElementLabels.getJavaElementName(info.getNewTypeName()), BasicElementLabels.getJavaElementName(info.getNewName()), BasicElementLabels.getJavaElementName(first.getName())}); |
| result.addError(msg); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private void collectTypeVariables(ITypeBinding typeBinding, Set<ITypeBinding> typeVariablesCollector) { |
| if (typeBinding.isTypeVariable()) { |
| typeVariablesCollector.add(typeBinding); |
| ITypeBinding[] typeBounds= typeBinding.getTypeBounds(); |
| for (int i= 0; i < typeBounds.length; i++) |
| collectTypeVariables(typeBounds[i], typeVariablesCollector); |
| |
| } else if (typeBinding.isArray()) { |
| collectTypeVariables(typeBinding.getElementType(), typeVariablesCollector); |
| |
| } else if (typeBinding.isParameterizedType()) { |
| ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); |
| for (int i= 0; i < typeArguments.length; i++) |
| collectTypeVariables(typeArguments[i], typeVariablesCollector); |
| |
| } else if (typeBinding.isWildcardType()) { |
| ITypeBinding bound= typeBinding.getBound(); |
| if (bound != null) { |
| collectTypeVariables(bound, typeVariablesCollector); |
| } |
| } |
| } |
| |
| public static boolean isValidExpression(String string){ |
| String trimmed= string.trim(); |
| 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(ASTProvider.SHARED_AST_LEVEL); |
| p.setSource(cuBuff.toString().toCharArray()); |
| CompilationUnit cu= (CompilationUnit) 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(ASTProvider.SHARED_AST_LEVEL); |
| p.setSource(cuBuff.toString().toCharArray()); |
| CompilationUnit cu= (CompilationUnit) 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 JavaModelException { |
| if (fCachedTypeHierarchy == null) |
| fCachedTypeHierarchy= fMethod.getDeclaringType().newTypeHierarchy(new SubProgressMonitor(monitor, 1)); |
| return fCachedTypeHierarchy; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| 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, BasicElementLabels.getFileName(getCu())); |
| 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= MethodChecks.isDeclaredInInterface(fMethod, hierarchy, new SubProgressMonitor(monitor, 1)); |
| if (fTopMethod == null) |
| fTopMethod= MethodChecks.overridesAnotherMethod(fMethod, hierarchy); |
| } |
| if (fTopMethod == null) |
| fTopMethod= fMethod; |
| if (! fTopMethod.equals(fMethod)) { |
| if (fTopMethod.getDeclaringType().isInterface()) { |
| RefactoringStatusContext context= JavaStatusContext.create(fTopMethod); |
| String message= Messages.format(RefactoringCoreMessages.MethodChecks_implements, |
| new String[]{JavaElementUtil.createMethodSignature(fTopMethod), BasicElementLabels.getJavaElementName(fTopMethod.getDeclaringType().getFullyQualifiedName('.'))}); |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, message, context, Corext.getPluginId(), RefactoringStatusCodes.METHOD_DECLARED_IN_INTERFACE, fTopMethod); |
| } else { |
| RefactoringStatusContext context= JavaStatusContext.create(fTopMethod); |
| String message= Messages.format(RefactoringCoreMessages.MethodChecks_overrides, |
| new String[]{JavaElementUtil.createMethodSignature(fTopMethod), BasicElementLabels.getJavaElementName(fTopMethod.getDeclaringType().getFullyQualifiedName('.'))}); |
| 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()); |
| } |
| RefactoringStatus[] status= TypeContextChecker.checkMethodTypesSyntax(fMethod, getParameterInfos(), fReturnTypeInfo); |
| for (int i= 0; i < status.length; i++) { |
| result.merge(status[i]); |
| } |
| 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<ExceptionInfo>(0); |
| try { |
| ASTNode nameNode= NodeFinder.perform(fBaseCuRewrite.getRoot(), fMethod.getNameRange()); |
| if (nameNode == null || !(nameNode instanceof Name) || !(nameNode.getParent() instanceof MethodDeclaration)) |
| return null; |
| MethodDeclaration methodDeclaration= (MethodDeclaration) nameNode.getParent(); |
| List<Name> exceptions= methodDeclaration.thrownExceptions(); |
| List<ExceptionInfo> result= new ArrayList<ExceptionInfo>(exceptions.size()); |
| for (int i= 0; i < exceptions.size(); i++) { |
| Name name= exceptions.get(i); |
| ITypeBinding typeBinding= name.resolveTypeBinding(); |
| if (typeBinding == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_no_exception_binding); |
| IJavaElement element= typeBinding.getJavaElement(); |
| result.add(ExceptionInfo.createInfoForOldException(element, typeBinding)); |
| } |
| fExceptionInfos= result; |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext) |
| */ |
| @Override |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException { |
| 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)); |
| if (result.hasFatalError()) |
| return result; |
| |
| if (fDelegateUpdating && isSignatureClashWithInitial()) |
| result.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.ChangeSignatureRefactoring_old_and_new_signatures_not_sufficiently_different )); |
| |
| String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(getMethodName())); |
| ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription); |
| |
| fRippleMethods= RippleMethodFinder2.getRelatedMethods(fMethod, binaryRefs, new SubProgressMonitor(pm, 1), null); |
| result.merge(checkVarargs()); |
| if (result.hasFatalError()) |
| return result; |
| |
| fOccurrences= findOccurrences(new SubProgressMonitor(pm, 1), binaryRefs, result); |
| binaryRefs.addErrorIfNecessary(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; |
| |
| Checks.addModifiedFilesToChecker(getAllFilesToModify(), context); |
| return result; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| protected void clearManagers() { |
| fChangeManager= null; |
| } |
| |
| private RefactoringStatus checkVisibilityChanges() throws JavaModelException { |
| 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 JavaModelException{ |
| StringBuffer buff= new StringBuffer(); |
| |
| int flags= getMethod().getFlags(); |
| buff.append(getVisibilityString(flags)); |
| if (Flags.isStatic(flags)) |
| buff.append("static "); //$NON-NLS-1$ |
| if (! getMethod().isConstructor()) |
| buff.append(fReturnTypeInfo.getOldTypeName()) |
| .append(' '); |
| |
| buff.append(JavaElementLabels.getElementLabel(fMethod.getParent(), JavaElementLabels.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 BasicElementLabels.getJavaCodeString(buff.toString()); |
| } |
| |
| public String getNewMethodSignature() throws JavaModelException{ |
| StringBuffer buff= new StringBuffer(); |
| |
| buff.append(getVisibilityString(fVisibility)); |
| if (Flags.isStatic(getMethod().getFlags())) |
| buff.append("static "); //$NON-NLS-1$ |
| if (! getMethod().isConstructor()) |
| buff.append(getReturnTypeString()) |
| .append(' '); |
| |
| buff.append(getMethodName()) |
| .append(Signature.C_PARAM_START) |
| .append(getMethodParameters()) |
| .append(Signature.C_PARAM_END); |
| |
| buff.append(getMethodThrows()); |
| |
| return BasicElementLabels.getJavaCodeString(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<ExceptionInfo> iter= fExceptionInfos.iterator(); iter.hasNext(); ) { |
| ExceptionInfo info= iter.next(); |
| if (! info.isDeleted()) { |
| buff.append(info.getElement().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<ExceptionInfo> iter= fExceptionInfos.iterator(); iter.hasNext(); ) { |
| ExceptionInfo info= iter.next(); |
| if (! info.isAdded()) { |
| buff.append(info.getElement().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<String> found= new HashSet<String>(); |
| Set<String> doubled= new HashSet<String>(); |
| for (Iterator<ParameterInfo> iter = getNotDeletedInfos().iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| String newName= info.getNewName(); |
| if (found.contains(newName) && !doubled.contains(newName)){ |
| result.addFatalError(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_duplicate_name, BasicElementLabels.getJavaElementName(newName))); |
| doubled.add(newName); |
| } else { |
| found.add(newName); |
| } |
| } |
| } |
| |
| private ICompilationUnit getCu() { |
| return fMethod.getCompilationUnit(); |
| } |
| |
| private boolean mustAnalyzeAstOfDeclaringCu() throws JavaModelException{ |
| if (JdtFlags.isAbstract(getMethod())) |
| return false; |
| else if (JdtFlags.isNative(getMethod())) |
| return false; |
| else if (getMethod().getDeclaringType().isInterface()) |
| return false; |
| else |
| return true; |
| } |
| |
| private RefactoringStatus checkCompilationofDeclaringCu() throws CoreException { |
| ICompilationUnit cu= getCu(); |
| TextChange change= fChangeManager.get(cu); |
| String newCuSource= change.getPreviewContent(new NullProgressMonitor()); |
| CompilationUnit newCUNode= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).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, SourceRangeFactory.create(problem)))); |
| } |
| return result; |
| } |
| |
| /** |
| * Evaluates if a problem needs to be reported. |
| * @param problem the problem |
| * @param cu the AST containing the new source |
| * @return return <code>true</code> if the problem needs to be reported |
| */ |
| protected boolean shouldReport(IProblem problem, CompilationUnit cu) { |
| if (! problem.isError()) |
| return false; |
| if (problem.getID() == IProblem.UndefinedType) //reported when trying to import |
| return false; |
| return true; |
| } |
| |
| private String getOldMethodParameters() { |
| StringBuffer buff= new StringBuffer(); |
| int i= 0; |
| for (Iterator<ParameterInfo> iter= getNotAddedInfos().iterator(); iter.hasNext(); i++) { |
| ParameterInfo info= 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<ParameterInfo> iter= getNotDeletedInfos().iterator(); iter.hasNext(); i++) { |
| ParameterInfo info= iter.next(); |
| if (i != 0 ) |
| buff.append(", "); //$NON-NLS-1$ |
| buff.append(createDeclarationString(info)); |
| } |
| return buff.toString(); |
| } |
| |
| private List<ParameterInfo> getAddedInfos(){ |
| List<ParameterInfo> result= new ArrayList<ParameterInfo>(1); |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (info.isAdded()) |
| result.add(info); |
| } |
| return result; |
| } |
| |
| private List<ParameterInfo> getDeletedInfos(){ |
| List<ParameterInfo> result= new ArrayList<ParameterInfo>(1); |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (info.isDeleted()) |
| result.add(info); |
| } |
| return result; |
| } |
| |
| private List<ParameterInfo> getNotAddedInfos(){ |
| List<ParameterInfo> all= new ArrayList<ParameterInfo>(fParameterInfos); |
| all.removeAll(getAddedInfos()); |
| return all; |
| } |
| |
| private List<ParameterInfo> getNotDeletedInfos(){ |
| List<ParameterInfo> all= new ArrayList<ParameterInfo>(fParameterInfos); |
| all.removeAll(getDeletedInfos()); |
| return all; |
| } |
| |
| private boolean areNamesSameAsInitial() { |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (info.isRenamed()) |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isOrderSameAsInitial(){ |
| int i= 0; |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext(); i++) { |
| ParameterInfo info= iter.next(); |
| if (info.getOldIndex() != i) // includes info.isAdded() |
| return false; |
| if (info.isDeleted()) |
| return false; |
| } |
| return true; |
| } |
| |
| private RefactoringStatus checkReorderings(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_checking_preconditions, 1); |
| return checkNativeMethods(); |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private RefactoringStatus checkRenamings(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| pm.beginTask(RefactoringCoreMessages.ChangeSignatureRefactoring_checking_preconditions, 1); |
| return checkParameterNamesInRippleMethods(); |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private RefactoringStatus checkParameterNamesInRippleMethods() throws JavaModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| Set<String> 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]), BasicElementLabels.getJavaElementName(paramNames[j])}; |
| String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_already_has, args); |
| RefactoringStatusContext context= JavaStatusContext.create(fRippleMethods[i].getCompilationUnit(), fRippleMethods[i].getNameRange()); |
| result.addError(msg, context); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private Set<String> getNewParameterNamesList() { |
| Set<String> oldNames= getOriginalParameterNames(); |
| Set<String> currentNames= getNamesOfNotDeletedParameters(); |
| currentNames.removeAll(oldNames); |
| return currentNames; |
| } |
| |
| private Set<String> getNamesOfNotDeletedParameters() { |
| Set<String> result= new HashSet<String>(); |
| for (Iterator<ParameterInfo> iter= getNotDeletedInfos().iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| result.add(info.getNewName()); |
| } |
| return result; |
| } |
| |
| private Set<String> getOriginalParameterNames() { |
| Set<String> result= new HashSet<String>(); |
| for (Iterator<ParameterInfo> iter= fParameterInfos.iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (! info.isAdded()) |
| result.add(info.getOldName()); |
| } |
| return result; |
| } |
| |
| private RefactoringStatus checkNativeMethods() throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| for (int i= 0; i < fRippleMethods.length; i++) { |
| if (JdtFlags.isNative(fRippleMethods[i])){ |
| String message= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_native, |
| new String[]{JavaElementUtil.createMethodSignature(fRippleMethods[i]), BasicElementLabels.getJavaElementName(fRippleMethods[i].getDeclaringType().getFullyQualifiedName('.'))}); |
| result.addError(message, JavaStatusContext.create(fRippleMethods[i])); |
| } |
| } |
| return result; |
| } |
| |
| private IFile[] getAllFilesToModify(){ |
| return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); |
| } |
| |
| public Change[] getAllChanges() { |
| return fChangeManager.getAllChanges(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| public Change createChange(IProgressMonitor pm) { |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| try { |
| return new DynamicValidationRefactoringChange(createDescriptor(), doGetRefactoringChangeName(), getAllChanges()); |
| } finally { |
| clearManagers(); |
| pm.done(); |
| } |
| } |
| |
| private ChangeMethodSignatureArguments getParticipantArguments() { |
| ArrayList<Parameter> parameterList= new ArrayList<Parameter>(); |
| List<ParameterInfo> pis= getParameterInfos(); |
| String[] originalParameterTypeSigs= fMethod.getParameterTypes(); |
| |
| for (Iterator<ParameterInfo> iter= pis.iterator(); iter.hasNext();) { |
| ParameterInfo pi= iter.next(); |
| if (!pi.isDeleted()) { |
| int oldIndex= pi.isAdded() ? -1 : pi.getOldIndex(); |
| String newName= pi.getNewName(); |
| String typeSig; |
| if (pi.isTypeNameChanged()) { |
| String newType= pi.getNewTypeName(); |
| if (pi.isNewVarargs()) { |
| newType= ParameterInfo.stripEllipsis(newType) + "[]"; //$NON-NLS-1$ |
| } |
| typeSig= Signature.createTypeSignature(newType, false); |
| } else { |
| typeSig= originalParameterTypeSigs[pi.getOldIndex()]; |
| } |
| String defaultValue= pi.getDefaultValue(); |
| parameterList.add(new Parameter(oldIndex, newName, typeSig, defaultValue)); |
| } |
| } |
| Parameter[] parameters= parameterList.toArray(new Parameter[parameterList.size()]); |
| |
| ArrayList<ThrownException> exceptionList= new ArrayList<ThrownException>(); |
| List<ExceptionInfo> exceptionInfos= getExceptionInfos(); |
| for (int i= 0; i < exceptionInfos.size(); i++) { |
| ExceptionInfo ei= exceptionInfos.get(i); |
| if (!ei.isDeleted()) { |
| int oldIndex= ei.isAdded() ? -1 : i; |
| String qualifiedTypeName= ei.getFullyQualifiedName(); |
| String newTypeSig= Signature.createTypeSignature(qualifiedTypeName, true); |
| exceptionList.add(new ThrownException(oldIndex, newTypeSig)); |
| } |
| } |
| ThrownException[] exceptions= exceptionList.toArray(new ThrownException[exceptionList.size()]); |
| String returnTypeSig; |
| if (fReturnTypeInfo.isTypeNameChanged()) { |
| returnTypeSig= Signature.createTypeSignature(fReturnTypeInfo.getNewTypeName(), false); |
| } else { |
| try { |
| returnTypeSig= fMethod.getReturnType(); |
| } catch (JavaModelException e) { |
| returnTypeSig= Signature.createTypeSignature(fReturnTypeInfo.getNewTypeName(), false); |
| } |
| } |
| return new ChangeMethodSignatureArguments(fMethodName, returnTypeSig, fVisibility, parameters, exceptions, fDelegateUpdating); |
| } |
| |
| |
| public JavaRefactoringDescriptor createDescriptor() { |
| final Map<String, String> arguments= new HashMap<String, String>(); |
| String project= null; |
| IJavaProject javaProject= fMethod.getJavaProject(); |
| if (javaProject != null) |
| project= javaProject.getElementName(); |
| ChangeMethodSignatureDescriptor descriptor= null; |
| try { |
| final String description= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(fMethod.getElementName())); |
| final String header= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_descriptor_description, new String[] { getOldMethodSignature(), getNewMethodSignature()}); |
| final JDTRefactoringDescriptorComment comment= createComment(project, header); |
| descriptor= RefactoringSignatureDescriptorFactory.createChangeMethodSignatureDescriptor(project, description, comment.asString(), arguments, getDescriptorFlags()); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project,fMethod)); |
| arguments.put(JavaRefactoringDescriptorUtil.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, new Integer(fVisibility).toString()); |
| } catch (JavaModelException exception) { |
| JavaPlugin.log(exception); |
| } |
| int count= 1; |
| for (final Iterator<ParameterInfo> iterator= fParameterInfos.iterator(); iterator.hasNext();) { |
| final ParameterInfo info= iterator.next(); |
| final StringBuffer buffer= new StringBuffer(64); |
| if (info.isAdded()) |
| buffer.append("{added}"); //$NON-NLS-1$ |
| else |
| buffer.append(info.getOldTypeName()); |
| buffer.append(" "); //$NON-NLS-1$ |
| if (info.isAdded()) |
| buffer.append("{added}"); //$NON-NLS-1$ |
| else |
| buffer.append(info.getOldName()); |
| buffer.append(" "); //$NON-NLS-1$ |
| buffer.append(info.getOldIndex()); |
| buffer.append(" "); //$NON-NLS-1$ |
| if (info.isDeleted()) |
| buffer.append("{deleted}"); //$NON-NLS-1$ |
| else |
| buffer.append(info.getNewTypeName().replaceAll(" ", "")); //$NON-NLS-1$//$NON-NLS-2$ |
| buffer.append(" "); //$NON-NLS-1$ |
| if (info.isDeleted()) |
| buffer.append("{deleted}"); //$NON-NLS-1$ |
| else |
| 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<ExceptionInfo> iterator= fExceptionInfos.iterator(); iterator.hasNext();) { |
| final ExceptionInfo info= iterator.next(); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count, JavaRefactoringDescriptorUtil.elementToHandle(project,info.getElement())); |
| arguments.put(ATTRIBUTE_KIND + count, new Integer(info.getKind()).toString()); |
| count++; |
| } |
| } catch (JavaModelException exception) { |
| JavaPlugin.log(exception); |
| return null; |
| } |
| return descriptor; |
| } |
| |
| protected int getDescriptorFlags() { |
| int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE; |
| try { |
| if (!Flags.isPrivate(fMethod.getFlags())) |
| flags|= RefactoringDescriptor.MULTI_CHANGE; |
| final IType declaring= fMethod.getDeclaringType(); |
| if (declaring.isAnonymous() || declaring.isLocal()) |
| flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| } catch (JavaModelException exception) { |
| JavaPlugin.log(exception); |
| } |
| return flags; |
| } |
| |
| private JDTRefactoringDescriptorComment createComment(String project, final String header) throws JavaModelException { |
| final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); |
| if (!fMethod.getElementName().equals(fMethodName)) |
| comment.addSetting(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_new_name_pattern, BasicElementLabels.getJavaElementName(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, BasicElementLabels.getJavaElementName(fReturnTypeInfo.getNewTypeName()))); |
| List<String> deleted= new ArrayList<String>(); |
| List<String> added= new ArrayList<String>(); |
| List<String> changed= new ArrayList<String>(); |
| for (final Iterator<ParameterInfo> iterator= fParameterInfos.iterator(); iterator.hasNext();) { |
| final ParameterInfo info= iterator.next(); |
| if (info.isDeleted()) |
| deleted.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_deleted_parameter_pattern, new String[] { BasicElementLabels.getJavaElementName(info.getOldTypeName()), BasicElementLabels.getJavaElementName(info.getOldName())})); |
| else if (info.isAdded()) |
| added.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_added_parameter_pattern, new String[] { BasicElementLabels.getJavaElementName(info.getNewTypeName()), BasicElementLabels.getJavaElementName(info.getNewName())})); |
| else if (info.isRenamed() || info.isTypeNameChanged() || info.isVarargChanged()) |
| changed.add(Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_changed_parameter_pattern, new String[] { BasicElementLabels.getJavaElementName(info.getOldTypeName()), BasicElementLabels.getJavaElementName(info.getOldName())})); |
| } |
| if (!added.isEmpty()) |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_added_parameters, added.toArray(new String[added.size()]))); |
| if (!deleted.isEmpty()) |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_removed_parameters, deleted.toArray(new String[deleted.size()]))); |
| if (!changed.isEmpty()) |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_changed_parameters, changed.toArray(new String[changed.size()]))); |
| added.clear(); |
| deleted.clear(); |
| changed.clear(); |
| for (final Iterator<ExceptionInfo> iterator= fExceptionInfos.iterator(); iterator.hasNext();) { |
| final ExceptionInfo info= iterator.next(); |
| if (info.isAdded()) |
| added.add(info.getElement().getElementName()); |
| else if (info.isDeleted()) |
| deleted.add(info.getElement().getElementName()); |
| } |
| if (!added.isEmpty()) |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_added_exceptions, added.toArray(new String[added.size()]))); |
| if (!deleted.isEmpty()) |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ChangeSignatureRefactoring_removed_exceptions, deleted.toArray(new String[deleted.size()]))); |
| return comment; |
| } |
| |
| 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<ICompilationUnit, Set<IType>> 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]; |
| ICompilationUnit 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<? extends ASTNode>> deferredUpdates= new ArrayList<OccurrenceUpdate<? extends ASTNode>>(); |
| for (int j= 0; j < nodes.length; j++) { |
| OccurrenceUpdate<? extends ASTNode> update= createOccurrenceUpdate(nodes[j], cuRewrite, result); |
| if (update instanceof DeclarationUpdate) { |
| update.updateNode(); |
| } else { |
| deferredUpdates.add(update); |
| } |
| } |
| for (Iterator<OccurrenceUpdate<? extends ASTNode>> iter= deferredUpdates.iterator(); iter.hasNext();) { |
| iter.next().updateNode(); |
| } |
| |
| if (isNoArgConstructor && namedSubclassMapping.containsKey(cu)){ |
| //only non-anonymous subclasses may have noArgConstructors to modify - see bug 43444 |
| Set<IType> subtypes= namedSubclassMapping.get(cu); |
| for (Iterator<IType> iter= subtypes.iterator(); iter.hasNext();) { |
| IType subtype= iter.next(); |
| AbstractTypeDeclaration subtypeNode= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(subtype, cuRewrite.getRoot()); |
| if (subtypeNode != null) |
| modifyImplicitCallsToNoArgConstructor(subtypeNode, cuRewrite); |
| } |
| } |
| TextChange change= cuRewrite.createChange(true); |
| if (change != null) |
| fChangeManager.manage(cu, change); |
| } |
| |
| pm.done(); |
| return fChangeManager; |
| } |
| |
| private Map<ICompilationUnit, Set<IType>> createNamedSubclassMapping(IProgressMonitor pm) throws JavaModelException{ |
| IType[] subclasses= getCachedTypeHierarchy(new SubProgressMonitor(pm, 1)).getSubclasses(fMethod.getDeclaringType()); |
| Map<ICompilationUnit, Set<IType>> result= new HashMap<ICompilationUnit, Set<IType>>(); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType subclass= subclasses[i]; |
| if (subclass.isAnonymous()) |
| continue; |
| ICompilationUnit cu= subclass.getCompilationUnit(); |
| if (! result.containsKey(cu)) |
| result.put(cu, new HashSet<IType>()); |
| result.get(cu).add(subclass); |
| } |
| return result; |
| } |
| |
| private void modifyImplicitCallsToNoArgConstructor(AbstractTypeDeclaration subclass, CompilationUnitRewrite cuRewrite) { |
| MethodDeclaration[] 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(MethodDeclaration 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) { |
| Iterator<ParameterInfo> iter= getNotDeletedInfos().iterator(); |
| while (iter.hasNext()) { |
| ParameterInfo info= iter.next(); |
| Expression newExpression= createNewExpression(info, getParameterInfos(), superCall.arguments(), cuRewrite, (MethodDeclaration) ASTNodes.getParent(superCall, MethodDeclaration.class)); |
| if (newExpression != null) |
| superCall.arguments().add(newExpression); |
| } |
| } |
| |
| private static boolean containsImplicitCallToSuperConstructor(MethodDeclaration 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(); |
| MethodDeclaration newConstructor= ast.newMethodDeclaration(); |
| 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 MethodDeclaration[] getAllConstructors(AbstractTypeDeclaration typeDeclaration) { |
| BodyDeclaration decl; |
| List<BodyDeclaration> result= new ArrayList<BodyDeclaration>(1); |
| for (Iterator<BodyDeclaration> it = typeDeclaration.bodyDeclarations().listIterator(); it.hasNext(); ) { |
| decl= it.next(); |
| if (decl instanceof MethodDeclaration && ((MethodDeclaration) decl).isConstructor()) |
| result.add(decl); |
| } |
| return result.toArray(new MethodDeclaration[result.size()]); |
| } |
| |
| private boolean isNoArgConstructor() throws JavaModelException { |
| return fMethod.isConstructor() && fMethod.getNumberOfParameters() == 0; |
| } |
| |
| private Expression createNewExpression(ParameterInfo info, List<ParameterInfo> parameterInfos, List<Expression> nodes, CompilationUnitRewrite cuRewrite, MethodDeclaration method) { |
| if (info.isNewVarargs() && info.getDefaultValue().trim().length() == 0) |
| return null; |
| else { |
| if (fDefaultValueAdvisor == null) |
| return (Expression) cuRewrite.getASTRewrite().createStringPlaceholder(info.getDefaultValue(), ASTNode.METHOD_INVOCATION); |
| else |
| return fDefaultValueAdvisor.createDefaultExpression(nodes, info, parameterInfos, method, false, cuRewrite); |
| } |
| } |
| |
| private boolean isVisibilitySameAsInitial() throws JavaModelException { |
| return fVisibility == JdtFlags.getVisibilityCode(fMethod); |
| } |
| |
| private IJavaSearchScope createRefactoringScope() throws JavaModelException{ |
| return RefactoringScopeFactory.create(fMethod, true, false); |
| } |
| |
| private SearchResultGroup[] findOccurrences(IProgressMonitor pm, ReferencesInBinaryContext binaryRefs, RefactoringStatus status) throws JavaModelException{ |
| final boolean isConstructor= fMethod.isConstructor(); |
| CuCollectingSearchRequestor requestor= new CuCollectingSearchRequestor(binaryRefs) { |
| @Override |
| protected void acceptSearchMatch(ICompilationUnit unit, SearchMatch match) throws CoreException { |
| // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=27236 : |
| if (isConstructor && match instanceof MethodReferenceMatch) { |
| MethodReferenceMatch mrm= (MethodReferenceMatch) match; |
| if (mrm.isSynthetic()) { |
| return; |
| } |
| } |
| collectMatch(match); |
| } |
| }; |
| |
| SearchPattern pattern; |
| if (isConstructor) { |
| |
| // // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=226151 : don't find binary refs for constructors for now |
| // return ConstructorReferenceFinder.getConstructorOccurrences(fMethod, pm, status); |
| |
| // SearchPattern occPattern= SearchPattern.createPattern(fMethod, IJavaSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); |
| SearchPattern declPattern= SearchPattern.createPattern(fMethod, IJavaSearchConstants.DECLARATIONS, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); |
| SearchPattern refPattern= SearchPattern.createPattern(fMethod, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); |
| // pattern= SearchPattern.createOrPattern(declPattern, refPattern); |
| // pattern= occPattern; |
| |
| // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=226151 : do two searches |
| try { |
| SearchEngine engine= new SearchEngine(); |
| engine.search(declPattern, SearchUtils.getDefaultSearchParticipants(), createRefactoringScope(), requestor, new NullProgressMonitor()); |
| engine.search(refPattern, SearchUtils.getDefaultSearchParticipants(), createRefactoringScope(), requestor, pm); |
| } catch (CoreException e) { |
| throw new JavaModelException(e); |
| } |
| return RefactoringSearchEngine.groupByCu(requestor.getResults(), status); |
| |
| } else { |
| pattern= RefactoringSearchEngine.createOrPattern(fRippleMethods, IJavaSearchConstants.ALL_OCCURRENCES); |
| } |
| return RefactoringSearchEngine.search(pattern, createRefactoringScope(), requestor, pm, status); |
| } |
| |
| private static String createDeclarationString(ParameterInfo info) { |
| String newTypeName= info.getNewTypeName(); |
| int varargsIndex= newTypeName.lastIndexOf("..."); //$NON-NLS-1$ |
| if (varargsIndex != -1) { |
| newTypeName= newTypeName.substring(0, varargsIndex); |
| } |
| int index= newTypeName.lastIndexOf('.'); |
| if (index != -1) { |
| newTypeName= newTypeName.substring(index+1); |
| } |
| return newTypeName + (varargsIndex != -1 ? "..." : "") + " " + info.getNewName(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| private static final boolean BUG_89686= true; //see bug 83693: Search for References to methods/constructors: do ranges include parameter lists? |
| |
| private OccurrenceUpdate<? extends ASTNode> createOccurrenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { |
| if (BUG_89686 && node instanceof SimpleName && node.getParent() instanceof EnumConstantDeclaration) |
| node= node.getParent(); |
| |
| if (Invocations.isInvocationWithArguments(node)) |
| return new ReferenceUpdate(node, cuRewrite, result); |
| |
| else if (node instanceof SimpleName && node.getParent() instanceof MethodDeclaration) |
| return new DeclarationUpdate((MethodDeclaration) node.getParent(), cuRewrite, result); |
| |
| else if (node instanceof MemberRef || node instanceof MethodRef) |
| 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); |
| } |
| |
| abstract class OccurrenceUpdate<N extends ASTNode> { |
| 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<N, N> newOldMap= new LinkedHashMap<N, N>(); |
| List<N> nodes= listRewrite.getRewrittenList(); |
| Iterator<N> rewriteIter= nodes.iterator(); |
| List<N> original= listRewrite.getOriginalList(); |
| for (Iterator<N> iter= original.iterator(); iter.hasNext();) { |
| newOldMap.put(rewriteIter.next(),iter.next()); |
| } |
| List<N> newNodes= new ArrayList<N>(); |
| // register removed nodes, and collect nodes in new sequence: |
| for (int i= 0; i < fParameterInfos.size(); i++) { |
| ParameterInfo info= fParameterInfos.get(i); |
| int oldIndex= info.getOldIndex(); |
| |
| if (info.isDeleted()) { |
| if (oldIndex != fOldVarargIndex) { |
| registerImportRemoveNode(nodes.get(oldIndex)); |
| } else { |
| //vararg deleted -> remove all remaining nodes: |
| for (int n= oldIndex; n < nodes.size(); n++) { |
| registerImportRemoveNode(nodes.get(n)); |
| } |
| } |
| |
| } else if (info.isAdded()) { |
| N newParamgument= createNewParamgument(info, fParameterInfos, nodes); |
| if (newParamgument != null) |
| newNodes.add(newParamgument); |
| |
| } else /* parameter stays */ { |
| if (oldIndex != fOldVarargIndex) { |
| N oldNode= nodes.get(oldIndex); |
| N 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++) { |
| N oldNode= nodes.get(n); |
| N movedNode= moveNode(oldNode, getASTRewrite()); |
| newNodes.add(movedNode); |
| } |
| } |
| } |
| } |
| |
| Iterator<N> nodesIter= nodes.iterator(); |
| Iterator<N> newIter= newNodes.iterator(); |
| //replace existing nodes with new ones: |
| while (nodesIter.hasNext() && newIter.hasNext()) { |
| ASTNode node= nodesIter.next(); |
| ASTNode newNode= newIter.next(); |
| if (!ASTNodes.isExistingNode(node)) //XXX:should better be addressed in ListRewriteEvent.replaceEntry(ASTNode, ASTNode) |
| listRewrite.replace(newOldMap.get(node), newNode, fDescription); |
| else |
| listRewrite.replace(node, newNode, fDescription); |
| } |
| //remove remaining existing nodes: |
| while (nodesIter.hasNext()) { |
| ASTNode node= nodesIter.next(); |
| if (!ASTNodes.isExistingNode(node)) |
| listRewrite.remove(newOldMap.get(node), fDescription); |
| else |
| listRewrite.remove(node, fDescription); |
| } |
| //add additional new nodes: |
| while (newIter.hasNext()) { |
| ASTNode node= newIter.next(); |
| listRewrite.insertLast(node, fDescription); |
| } |
| } |
| |
| /** |
| * @return ListRewrite of parameters or arguments |
| */ |
| protected abstract ListRewrite getParamgumentsRewrite(); |
| |
| protected final void changeParamguments() { |
| for (Iterator<ParameterInfo> iter= getParameterInfos().iterator(); iter.hasNext();) { |
| ParameterInfo info= iter.next(); |
| if (info.isAdded() || info.isDeleted()) |
| continue; |
| |
| if (info.isRenamed()) |
| changeParamgumentName(info); |
| |
| if (info.isTypeNameChanged()) |
| changeParamgumentType(info); |
| } |
| } |
| |
| /** |
| * @param info the parameter info |
| */ |
| protected void changeParamgumentName(ParameterInfo info) { |
| // no-op |
| } |
| |
| /** |
| * @param info the parameter info |
| */ |
| 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 TODO |
| * @param parameterInfos TODO |
| * @param nodes TODO |
| * @return a new method parameter or argument, or <code>null</code> for an empty vararg argument |
| */ |
| protected abstract N createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<N> 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 { |
| ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(fCuRewrite.getRoot(), getMethodNameNode().getStartPosition(), getImportRewrite()); |
| newTypeNode= getImportRewrite().addImport(newTypeBinding, fCuRewrite.getAST(), importRewriteContext); |
| getImportRemover().registerAddedImports(newTypeNode); |
| } |
| return newTypeNode; |
| } |
| |
| protected final TightSourceRangeComputer getTightSourceRangeComputer() { |
| return (TightSourceRangeComputer) fCuRewrite.getASTRewrite().getExtendedSourceRangeComputer(); |
| } |
| } |
| |
| class ReferenceUpdate extends OccurrenceUpdate<Expression> { |
| /** 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)); |
| } |
| |
| @Override |
| public void updateNode() { |
| reshuffleElements(); |
| changeMethodName(); |
| } |
| |
| /** @return {@inheritDoc} (element type: Expression) */ |
| @Override |
| protected ListRewrite getParamgumentsRewrite() { |
| return getASTRewrite().getListRewrite(fNode, Invocations.getArgumentsProperty(fNode)); |
| } |
| |
| @Override |
| protected Expression createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<Expression> nodes) { |
| CompilationUnitRewrite cuRewrite= getCompilationUnitRewrite(); |
| MethodDeclaration declaration= (MethodDeclaration) ASTNodes.getParent(fNode, MethodDeclaration.class); |
| if (isRecursiveReference()) { |
| return createNewExpressionRecursive(info, parameterInfos, nodes, cuRewrite, declaration); |
| } else |
| return createNewExpression(info, parameterInfos, nodes, cuRewrite, declaration); |
| } |
| |
| private Expression createNewExpressionRecursive(ParameterInfo info, List<ParameterInfo> parameterInfos, List<Expression> nodes, CompilationUnitRewrite cuRewrite, MethodDeclaration methodDeclaration) { |
| if (fDefaultValueAdvisor != null && info.isAdded()) { |
| return fDefaultValueAdvisor.createDefaultExpression(nodes, info, parameterInfos, methodDeclaration, true, cuRewrite); |
| } |
| return (Expression) getASTRewrite().createStringPlaceholder(info.getNewName(), ASTNode.METHOD_INVOCATION); |
| } |
| |
| @Override |
| protected SimpleName getMethodNameNode() { |
| if (fNode instanceof MethodInvocation) |
| return ((MethodInvocation)fNode).getName(); |
| |
| if (fNode instanceof SuperMethodInvocation) |
| return ((SuperMethodInvocation)fNode).getName(); |
| |
| return null; |
| } |
| |
| private boolean isRecursiveReference() { |
| MethodDeclaration enclosingMethodDeclaration= (MethodDeclaration) ASTNodes.getParent(fNode, MethodDeclaration.class); |
| if (enclosingMethodDeclaration == null) |
| return false; |
| |
| IMethodBinding enclosingMethodBinding= enclosingMethodDeclaration.resolveBinding(); |
| if (enclosingMethodBinding == null) |
| return false; |
| |
| if (fNode instanceof MethodInvocation) |
| return enclosingMethodBinding == ((MethodInvocation)fNode).resolveMethodBinding(); |
| |
| if (fNode instanceof SuperMethodInvocation) { |
| IMethodBinding 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 |
| } |
| |
| if (fNode instanceof EnumConstantDeclaration) { |
| return false; //cannot define enum constant inside enum constructor |
| } |
| |
| 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(IMethodBinding m1, IMethodBinding 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<SingleVariableDeclaration> { |
| private MethodDeclaration fMethDecl; |
| |
| protected DeclarationUpdate(MethodDeclaration decl, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { |
| super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_change_signature), result); |
| fMethDecl= decl; |
| } |
| |
| // Prevent import removing if delegate is created. |
| @Override |
| protected void registerImportRemoveNode(ASTNode node) { |
| if (!fDelegateUpdating) |
| super.registerImportRemoveNode(node); |
| } |
| |
| @Override |
| 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 JavaModelException { |
| |
| 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(); |
| MethodRef javadocReference= creator.getJavadocReference(); |
| if (javadocReference != null) |
| new DocReferenceUpdate(javadocReference, creator.getDelegateRewrite(), fResult).updateNode(); |
| |
| creator.createEdit(); |
| } |
| |
| /** @return {@inheritDoc} (element type: SingleVariableDeclaration) */ |
| @Override |
| protected ListRewrite getParamgumentsRewrite() { |
| return getASTRewrite().getListRewrite(fMethDecl, MethodDeclaration.PARAMETERS_PROPERTY); |
| } |
| |
| @Override |
| 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); |
| } |
| } |
| |
| @Override |
| 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, new Integer(0), fDescription); |
| } |
| } |
| |
| private void changeReturnType() { |
| 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(MethodDeclaration methDecl) { |
| if (methDecl.getExtraDimensions() != 0) |
| getASTRewrite().set(methDecl, MethodDeclaration.EXTRA_DIMENSIONS_PROPERTY, new Integer(0), fDescription); |
| } |
| |
| private boolean needsVisibilityUpdate() throws JavaModelException { |
| 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 JavaModelException{ |
| return JdtFlags.isHigherVisibility(fVisibility, JdtFlags.getVisibilityCode(fMethod)); |
| } |
| |
| private void changeVisibility() { |
| ModifierRewrite.create(getASTRewrite(), fMethDecl).setVisibility(fVisibility, fDescription); |
| } |
| |
| private void changeExceptions() { |
| for (Iterator<ExceptionInfo> iter= fExceptionInfos.iterator(); iter.hasNext();) { |
| ExceptionInfo info= iter.next(); |
| if (info.isOld()) |
| continue; |
| if (info.isDeleted()) |
| removeExceptionFromNodeList(info, fMethDecl.thrownExceptions()); |
| else |
| addExceptionToNodeList(info, getASTRewrite().getListRewrite(fMethDecl, MethodDeclaration.THROWN_EXCEPTIONS_PROPERTY)); |
| } |
| } |
| |
| private void removeExceptionFromNodeList(ExceptionInfo toRemove, List<Name> list) { |
| ITypeBinding typeToRemove= toRemove.getTypeBinding(); |
| for (Iterator<Name> iter= list.iterator(); iter.hasNext(); ) { |
| Name currentName= 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 JavaModelException). */ |
| // if (Bindings.isSuperType(typeToRemove, currentType)) |
| if (currentType == null) |
| continue; // newly added or unresolvable type |
| if (Bindings.equals(currentType, typeToRemove) || toRemove.getElement().getElementName().equals(currentType.getName())) { |
| getASTRewrite().remove(currentName, fDescription); |
| registerImportRemoveNode(currentName); |
| } |
| } |
| } |
| |
| private void addExceptionToNodeList(ExceptionInfo exceptionInfo, ListRewrite exceptionListRewrite) { |
| String fullyQualified= exceptionInfo.getFullyQualifiedName(); |
| for (Iterator<? extends ASTNode> 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(exceptionInfo.getFullyQualifiedName()); |
| getImportRemover().registerAddedImport(importedType); |
| ASTNode exNode= getASTRewrite().createStringPlaceholder(importedType, ASTNode.SIMPLE_NAME); |
| exceptionListRewrite.insertLast(exNode, fDescription); |
| } |
| |
| private void changeJavadocTags() throws JavaModelException { |
| //update tags in javadoc: @param, @return, @exception, @throws, ... |
| Javadoc javadoc= fMethDecl.getJavadoc(); |
| if (javadoc == null) |
| return; |
| |
| ITypeBinding typeBinding= Bindings.getBindingOfParentType(fMethDecl); |
| if (typeBinding == null) |
| return; |
| IMethodBinding 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<TagElement> tags= javadoc.tags(); |
| ListRewrite tagsRewrite= getASTRewrite().getListRewrite(javadoc, Javadoc.TAGS_PROPERTY); |
| |
| if (! isReturnTypeSameAsInitial()) { |
| if (PrimitiveType.VOID.toString().equals(fReturnTypeInfo.getNewTypeName())) { |
| for (int i = 0; i < tags.size(); i++) { |
| TagElement tag= 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<TagElement> paramTags= new ArrayList<TagElement>(); // <TagElement>, only not deleted tags with simpleName |
| // delete & rename: |
| for (Iterator<TagElement> iter = tags.iterator(); iter.hasNext(); ) { |
| TagElement tag = iter.next(); |
| String tagName= tag.getTagName(); |
| List<? extends ASTNode> 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= 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<ParameterInfo> infoIter= fParameterInfos.iterator(); infoIter.hasNext();) { |
| ParameterInfo info= 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().getJavaProject()); |
| insertTag(paramNode, previousTag, tagsRewrite); |
| previousTag= paramNode; |
| } else { |
| for (Iterator<TagElement> tagIter= paramTags.iterator(); tagIter.hasNext();) { |
| TagElement tag= 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<TagElement> iter= paramTags.iterator(); iter.hasNext();) { |
| TagElement tag= 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<TagElement> exceptionTags= new ArrayList<TagElement>(); // <TagElement>, only not deleted tags with name |
| for (int i= 0; i < tags.size(); i++) { |
| TagElement tag= 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= 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.getElement().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<ExceptionInfo> infoIter= fExceptionInfos.iterator(); infoIter.hasNext();) { |
| ExceptionInfo info= infoIter.next(); |
| if (info.isAdded()) { |
| if (!isTopOfRipple) |
| continue; |
| TagElement excptNode= createExceptionTag(info.getElement().getElementName()); |
| insertTag(excptNode, previousTag, tagsRewrite); |
| previousTag= excptNode; |
| } else { |
| for (Iterator<TagElement> tagIter= exceptionTags.iterator(); tagIter.hasNext();) { |
| TagElement tag= 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.getElement().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<TagElement> iter= exceptionTags.iterator(); iter.hasNext();) { |
| TagElement tag= 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().getJavaProject()); |
| 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().getJavaProject()); |
| 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<TagElement> tags, String tagName) { |
| List<String> 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(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<ParameterInfo> iter= getDeletedInfos().iterator(); iter.hasNext();) { |
| ParameterInfo info= 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[]{ BasicElementLabels.getJavaElementName(paramDecl.getName().getIdentifier()), |
| BasicElementLabels.getJavaElementName(fMethDecl.getName().getIdentifier()), |
| BasicElementLabels.getJavaElementName(typeName)}; |
| String msg= Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_parameter_used, keys); |
| fResult.addError(msg, context); |
| } |
| } |
| } |
| |
| private String getFullTypeName(MethodDeclaration 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, BasicElementLabels.getJavaElementName(ASTNodes.asString(cic.getType()))); |
| } else if (node instanceof EnumConstantDeclaration) { |
| EnumDeclaration ed= (EnumDeclaration) node.getParent(); |
| return Messages.format(RefactoringCoreMessages.ChangeSignatureRefactoring_anonymous_subclass, BasicElementLabels.getJavaElementName(ASTNodes.asString(ed.getName()))); |
| } |
| } |
| } |
| |
| @Override |
| protected SingleVariableDeclaration createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<SingleVariableDeclaration> 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; |
| } |
| |
| @Override |
| protected SimpleName getMethodNameNode() { |
| return fMethDecl.getName(); |
| } |
| |
| } |
| |
| class DocReferenceUpdate extends OccurrenceUpdate<MethodRefParameter> { |
| /** instanceof MemberRef || MethodRef */ |
| private ASTNode fNode; |
| |
| protected DocReferenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { |
| super(cuRewrite, cuRewrite.createGroupDescription(RefactoringCoreMessages.ChangeSignatureRefactoring_update_javadoc_reference), result); |
| fNode= node; |
| } |
| |
| @Override |
| public void updateNode() { |
| if (fNode instanceof MethodRef) { |
| changeParamguments(); |
| reshuffleElements(); |
| } |
| if (canChangeNameAndReturnType()) |
| changeMethodName(); |
| } |
| |
| @Override |
| protected MethodRefParameter createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<MethodRefParameter> nodes) { |
| return createNewMethodRefParameter(info); |
| } |
| |
| private MethodRefParameter createNewMethodRefParameter(ParameterInfo info) { |
| MethodRefParameter newP= getASTRewrite().getAST().newMethodRefParameter(); |
| |
| // only add name iff first parameter already has a name: |
| List<? extends ASTNode> parameters= getParamgumentsRewrite().getOriginalList(); |
| if (parameters.size() > 0) |
| if (((MethodRefParameter) 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); |
| } |
| |
| @Override |
| protected SimpleName getMethodNameNode() { |
| if (fNode instanceof MemberRef) |
| return ((MemberRef) fNode).getName(); |
| |
| if (fNode instanceof MethodRef) |
| return ((MethodRef) fNode).getName(); |
| |
| return null; |
| } |
| |
| /** @return {@inheritDoc} (element type: MethodRefParameter) */ |
| @Override |
| protected ListRewrite getParamgumentsRewrite() { |
| return getASTRewrite().getListRewrite(fNode, MethodRef.PARAMETERS_PROPERTY); |
| } |
| |
| @Override |
| protected void changeParamgumentName(ParameterInfo info) { |
| if (! (fNode instanceof MethodRef)) |
| return; |
| |
| MethodRefParameter oldParam= (MethodRefParameter) ((MethodRef) fNode).parameters().get(info.getOldIndex()); |
| SimpleName oldParamName= oldParam.getName(); |
| if (oldParamName != null) |
| getASTRewrite().set(oldParamName, SimpleName.IDENTIFIER_PROPERTY, info.getNewName(), fDescription); |
| } |
| |
| @Override |
| protected void changeParamgumentType(ParameterInfo info) { |
| if (! (fNode instanceof MethodRef)) |
| return; |
| |
| MethodRefParameter oldParam= (MethodRefParameter) ((MethodRef) 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, MethodRefParameter.VARARGS_PROPERTY, Boolean.TRUE, fDescription); |
| } |
| } else { |
| if (oldParam.isVarargs()) { |
| getASTRewrite().set(oldParam, MethodRefParameter.VARARGS_PROPERTY, Boolean.FALSE, fDescription); |
| } |
| } |
| |
| getASTRewrite().replace(oldTypeNode, newTypeNode, fDescription); |
| registerImportRemoveNode(oldTypeNode); |
| } |
| } |
| |
| class StaticImportUpdate extends OccurrenceUpdate<ASTNode> { |
| |
| private final ImportDeclaration fImportDecl; |
| |
| public StaticImportUpdate(ImportDeclaration importDecl, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { |
| super(cuRewrite, null, result); |
| fImportDecl= importDecl; |
| } |
| |
| @Override |
| public void updateNode() throws JavaModelException { |
| 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); |
| } |
| |
| @Override |
| protected ListRewrite getParamgumentsRewrite() { |
| return null; |
| } |
| |
| @Override |
| protected ASTNode createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<ASTNode> nodes) { |
| return null; |
| } |
| |
| @Override |
| protected SimpleName getMethodNameNode() { |
| return null; |
| } |
| } |
| |
| class NullOccurrenceUpdate extends OccurrenceUpdate<ASTNode> { |
| private ASTNode fNode; |
| protected NullOccurrenceUpdate(ASTNode node, CompilationUnitRewrite cuRewrite, RefactoringStatus result) { |
| super(cuRewrite, null, result); |
| fNode= node; |
| } |
| @Override |
| public void updateNode() throws JavaModelException { |
| 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 + "] in " + fCuRewrite.getCu(); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| JavaPlugin.log(new Exception(msg + ":\n" + fCuRewrite.getCu().getSource().substring(start, start + length))); //$NON-NLS-1$ |
| fResult.addError(msg, JavaStatusContext.create(fCuRewrite.getCu(), fNode)); |
| } |
| @Override |
| protected ListRewrite getParamgumentsRewrite() { |
| return null; |
| } |
| @Override |
| protected ASTNode createNewParamgument(ParameterInfo info, List<ParameterInfo> parameterInfos, List<ASTNode> nodes) { |
| return null; |
| } |
| @Override |
| protected SimpleName getMethodNameNode() { |
| return null; |
| } |
| } |
| |
| private RefactoringStatus initialize(JavaRefactoringArguments arguments) { |
| final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); |
| if (handle != null) { |
| final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false); |
| if (element == null || !element.exists() || element.getElementType() != IJavaElement.METHOD) |
| return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.CHANGE_METHOD_SIGNATURE); |
| else { |
| fMethod= (IMethod) element; |
| fMethodName= fMethod.getElementName(); |
| try { |
| fVisibility= JdtFlags.getVisibilityCode(fMethod); |
| fReturnTypeInfo= new ReturnTypeInfo(Signature.toString(Signature.getReturnType(fMethod.getSignature()))); |
| } catch (JavaModelException exception) { |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { new Integer(fVisibility), |
| ATTRIBUTE_VISIBILITY })); |
| } |
| } |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); |
| final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME); |
| if (name != null) { |
| fMethodName= name; |
| final RefactoringStatus status= Checks.checkMethodName(fMethodName, fMethod); |
| if (status.hasError()) |
| return status; |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME)); |
| final String type= arguments.getAttribute(ATTRIBUTE_RETURN); |
| if (type != null && !"".equals(type)) //$NON-NLS-1$ |
| fReturnTypeInfo.setNewTypeName(type); |
| final String visibility= arguments.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<ParameterInfo>(3); |
| while ((value= arguments.getAttribute(attribute)) != null) { |
| StringTokenizer tokenizer= new StringTokenizer(value, " "); //$NON-NLS-1$ |
| if (tokenizer.countTokens() < 6) |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { value, 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 { |
| int index= Integer.parseInt(oldIndex); |
| if (index == -1) { |
| String result= arguments.getAttribute(ATTRIBUTE_DEFAULT + count); |
| if (result == null) |
| result= ""; //$NON-NLS-1$ |
| info= ParameterInfo.createInfoForAddedParameter(newTypeName, newName, result); |
| } else { |
| info= new ParameterInfo(oldTypeName, oldName, index); |
| 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, new Object[] { value, ATTRIBUTE_PARAMETER })); |
| } |
| count++; |
| attribute= ATTRIBUTE_PARAMETER + count; |
| } |
| count= 1; |
| fExceptionInfos= new ArrayList<ExceptionInfo>(2); |
| attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count; |
| while ((value= arguments.getAttribute(attribute)) != null) { |
| ExceptionInfo info= null; |
| final String kind= arguments.getAttribute(ATTRIBUTE_KIND + count); |
| if (kind != null) { |
| final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), value, false); |
| if (element == null || !element.exists()) |
| return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.CHANGE_METHOD_SIGNATURE); |
| else { |
| try { |
| info= new ExceptionInfo(element, Integer.valueOf(kind).intValue(), null); |
| } catch (NumberFormatException exception) { |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { kind, ATTRIBUTE_KIND })); |
| } |
| } |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { kind, ATTRIBUTE_KIND })); |
| fExceptionInfos.add(info); |
| count++; |
| attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count; |
| } |
| final String deprecate= arguments.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= arguments.getAttribute(ATTRIBUTE_DELEGATE); |
| if (delegate != null) { |
| fDelegateUpdating= Boolean.valueOf(delegate).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELEGATE)); |
| 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 <T extends ASTNode> T moveNode(T oldNode, ASTRewrite rewrite) { |
| T movedNode; |
| if (ASTNodes.isExistingNode(oldNode)) |
| movedNode= (T) rewrite.createMoveTarget(oldNode); //node must be one of ast |
| else |
| movedNode= (T) ASTNode.copySubtree(rewrite.getAST(), oldNode); |
| return movedNode; |
| } |
| |
| public IDefaultValueAdvisor getDefaultValueAdvisor() { |
| return fDefaultValueAdvisor; |
| } |
| |
| public void setDefaultValueAdvisor(IDefaultValueAdvisor defaultValueAdvisor) { |
| fDefaultValueAdvisor= defaultValueAdvisor; |
| } |
| |
| @Override |
| public Object[] getElements() { |
| return new Object[] { fMethod }; |
| } |
| |
| @Override |
| public String getIdentifier() { |
| return IRefactoringProcessorIds.CHANGE_METHOD_SIGNATURE_PROCESSOR; |
| } |
| |
| @Override |
| public boolean isApplicable() throws CoreException { |
| return RefactoringAvailabilityTester.isChangeSignatureAvailable(fMethod); |
| } |
| |
| @Override |
| public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { |
| String[] affectedNatures= JavaProcessors.computeAffectedNatures(fMethod); |
| return JavaParticipantManager.loadChangeMethodSignatureParticipants(status, this, fMethod, getParticipantArguments(), null, affectedNatures, sharedParticipants); |
| } |
| } |