change signature detection (I)

This commit is contained in:
anna
2010-09-09 13:09:34 +04:00
parent b36a711a3d
commit ba8f2e8187
12 changed files with 734 additions and 36 deletions
@@ -82,7 +82,12 @@ public class ChangeSignatureProcessor extends ChangeSignatureProcessorBase {
ThrownExceptionInfo[] thrownExceptions,
Set<PsiMethod> propagateParametersMethods,
Set<PsiMethod> propagateExceptionsMethods) {
super(project, generateChangeInfo(method, generateDelegate, newVisibility, newName, newType, parameterInfo, thrownExceptions, propagateParametersMethods, propagateExceptionsMethods));
this(project, generateChangeInfo(method, generateDelegate, newVisibility, newName, newType, parameterInfo, thrownExceptions,
propagateParametersMethods, propagateExceptionsMethods));
}
public ChangeSignatureProcessor(Project project, final JavaChangeInfo changeInfo) {
super(project, changeInfo);
LOG.assertTrue(myChangeInfo.getMethod().isValid());
}
@@ -40,10 +40,10 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
@Modifier
final String newVisibility;
private PsiMethod method;
final String oldName;
String oldName;
final String oldType;
final String[] oldParameterNames;
final String[] oldParameterTypes;
String[] oldParameterNames;
String[] oldParameterTypes;
final String newName;
final CanonicalTypes.Type newReturnType;
final ParameterInfoImpl[] newParms;
@@ -82,6 +82,24 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
boolean generateDelegate,
Set<PsiMethod> propagateParametersMethods,
Set<PsiMethod> propagateExceptionsMethods) {
this(newVisibility, method, newName, newType, newParms, newExceptions, generateDelegate, propagateParametersMethods,
propagateExceptionsMethods, method.getName());
}
/**
* @param newExceptions null if not changed
* @param oldName
*/
public JavaChangeInfoImpl(String newVisibility,
PsiMethod method,
String newName,
CanonicalTypes.Type newType,
@NotNull ParameterInfoImpl[] newParms,
ThrownExceptionInfo[] newExceptions,
boolean generateDelegate,
Set<PsiMethod> propagateParametersMethods,
Set<PsiMethod> propagateExceptionsMethods,
String oldName) {
this.newVisibility = newVisibility;
this.method = method;
this.newName = newName;
@@ -93,7 +111,7 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
this.propagateExceptionsMethods=propagateExceptionsMethods;
this.propagateParametersMethods=propagateParametersMethods;
oldName = method.getName();
this.oldName = oldName;
final PsiManager manager = method.getManager();
if (!method.isConstructor()){
oldType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeElement(method.getReturnType()).getText();
@@ -101,19 +119,11 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
else{
oldType = null;
}
PsiParameter[] parameters = method.getParameterList().getParameters();
oldParameterNames = new String[parameters.length];
oldParameterTypes = new String[parameters.length];
for(int i = 0; i < parameters.length; i++){
PsiParameter parameter = parameters[i];
oldParameterNames[i] = parameter.getName();
oldParameterTypes[i] =
JavaPsiFacade.getInstance(parameter.getProject()).getElementFactory().createTypeElement(parameter.getType()).getText();
}
fillOldParams(method);
isVisibilityChanged = !method.hasModifierProperty(newVisibility);
isNameChanged = !newName.equals(oldName);
isNameChanged = !newName.equals(this.oldName);
if (!method.isConstructor()){
try {
isReturnTypeChanged = !newReturnType.getType(this.method, manager).equals(this.method.getReturnType());
@@ -122,22 +132,22 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
isReturnTypeChanged = true;
}
}
if (parameters.length != newParms.length){
if (oldParameterNames.length != newParms.length){
isParameterSetOrOrderChanged = true;
}
else {
for(int i = 0; i < newParms.length; i++){
ParameterInfoImpl parmInfo = newParms[i];
PsiParameter parameter = parameters[i];
if (i != parmInfo.oldParameterIndex){
isParameterSetOrOrderChanged = true;
break;
}
if (!parmInfo.getName().equals(parameter.getName())){
if (!parmInfo.getName().equals(oldParameterNames[i])){
isParameterNamesChanged = true;
}
try {
if (!parmInfo.createType(method, manager).equals(parameter.getType())){
if (!parmInfo.getTypeText().equals(oldParameterTypes[i])){
isParameterTypesChanged = true;
}
}
@@ -147,11 +157,11 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
}
}
setupPropagationEnabled(parameters, newParms);
setupPropagationEnabled(method.getParameterList().getParameters(), newParms);
setupExceptions(newExceptions, method);
toRemoveParm = new boolean[parameters.length];
toRemoveParm = new boolean[oldParameterNames.length];
Arrays.fill(toRemoveParm, true);
for (ParameterInfoImpl info : newParms) {
if (info.oldParameterIndex < 0) continue;
@@ -183,8 +193,7 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
obtainsVarags = lastNewParm.isVarargType();
retainsVarargs = lastNewParm.oldParameterIndex >= 0 && obtainsVarags;
if (retainsVarargs) {
final PsiType oldTypeForVararg = parameters[lastNewParm.oldParameterIndex].getType();
arrayToVarargs = (oldTypeForVararg instanceof PsiArrayType && !(oldTypeForVararg instanceof PsiEllipsisType));
arrayToVarargs = oldParameterTypes[lastNewParm.oldParameterIndex].endsWith("[]");
}
else {
arrayToVarargs = false;
@@ -197,6 +206,18 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
}
protected void fillOldParams(PsiMethod method) {
PsiParameter[] parameters = method.getParameterList().getParameters();
oldParameterNames = new String[parameters.length];
oldParameterTypes = new String[parameters.length];
for(int i = 0; i < parameters.length; i++){
PsiParameter parameter = parameters[i];
oldParameterNames[i] = parameter.getName();
oldParameterTypes[i] =
JavaPsiFacade.getInstance(parameter.getProject()).getElementFactory().createTypeElement(parameter.getType()).getText();
}
}
@NotNull
public JavaParameterInfo[] getNewParameters() {
return newParms;
@@ -212,7 +233,7 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
}
private void setupExceptions(ThrownExceptionInfo[] newExceptions, final PsiMethod method) {
if (newExceptions == null) newExceptions = extractExceptions(method);
if (newExceptions == null) newExceptions = JavaThrownExceptionInfo.extractExceptions(method);
this.newExceptions = newExceptions;
@@ -236,7 +257,7 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
isExceptionSetOrOrderChanged |= isExceptionSetChanged;
}
private void setupPropagationEnabled(final PsiParameter[] parameters, final ParameterInfoImpl[] newParms) {
protected void setupPropagationEnabled(final PsiParameter[] parameters, final ParameterInfoImpl[] newParms) {
if (parameters.length >= newParms.length) {
isPropagationEnabled = false;
}
@@ -251,16 +272,6 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
}
}
//create identity mapping
private static ThrownExceptionInfo[] extractExceptions(PsiMethod method) {
PsiClassType[] types = method.getThrowsList().getReferencedTypes();
ThrownExceptionInfo[] result = new ThrownExceptionInfo[types.length];
for (int i = 0; i < result.length; i++) {
result[i] = new JavaThrownExceptionInfo(i, types[i]);
}
return result;
}
public PsiMethod getMethod() {
return method;
}
@@ -370,4 +381,78 @@ class JavaChangeInfoImpl implements JavaChangeInfo {
public boolean[] toRemoveParm() {
return toRemoveParm;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JavaChangeInfoImpl)) return false;
JavaChangeInfoImpl that = (JavaChangeInfoImpl)o;
if (arrayToVarargs != that.arrayToVarargs) return false;
if (isExceptionSetChanged != that.isExceptionSetChanged) return false;
if (isExceptionSetOrOrderChanged != that.isExceptionSetOrOrderChanged) return false;
if (isGenerateDelegate != that.isGenerateDelegate) return false;
if (isNameChanged != that.isNameChanged) return false;
if (isParameterNamesChanged != that.isParameterNamesChanged) return false;
if (isParameterSetOrOrderChanged != that.isParameterSetOrOrderChanged) return false;
if (isParameterTypesChanged != that.isParameterTypesChanged) return false;
if (isPropagationEnabled != that.isPropagationEnabled) return false;
if (isReturnTypeChanged != that.isReturnTypeChanged) return false;
if (isVisibilityChanged != that.isVisibilityChanged) return false;
if (obtainsVarags != that.obtainsVarags) return false;
if (retainsVarargs != that.retainsVarargs) return false;
if (wasVararg != that.wasVararg) return false;
if (!Arrays.equals(defaultValues, that.defaultValues)) return false;
if (!method.equals(that.method)) return false;
if (!Arrays.equals(newExceptions, that.newExceptions)) return false;
if (!newName.equals(that.newName)) return false;
if (newNameIdentifier != null ? !newNameIdentifier.equals(that.newNameIdentifier) : that.newNameIdentifier != null) return false;
if (!Arrays.equals(newParms, that.newParms)) return false;
if (newReturnType != null ? !newReturnType.equals(that.newReturnType) : that.newReturnType != null) return false;
if (newVisibility != null ? !newVisibility.equals(that.newVisibility) : that.newVisibility != null) return false;
if (!oldName.equals(that.oldName)) return false;
if (!Arrays.equals(oldParameterNames, that.oldParameterNames)) return false;
if (!Arrays.equals(oldParameterTypes, that.oldParameterTypes)) return false;
if (oldType != null ? !oldType.equals(that.oldType): that.oldType != null) return false;
if (!propagateExceptionsMethods.equals(that.propagateExceptionsMethods)) return false;
if (!propagateParametersMethods.equals(that.propagateParametersMethods)) return false;
if (!Arrays.equals(toRemoveParm, that.toRemoveParm)) return false;
return true;
}
@Override
public int hashCode() {
int result = newVisibility != null ? newVisibility.hashCode() : 0;
result = 31 * result + method.hashCode();
result = 31 * result + oldName.hashCode();
result = 31 * result +(oldType != null ? oldType.hashCode() : 0);
result = 31 * result + Arrays.hashCode(oldParameterNames);
result = 31 * result + Arrays.hashCode(oldParameterTypes);
result = 31 * result + newName.hashCode();
result = 31 * result + (newReturnType != null ? newReturnType.hashCode() : 0);
result = 31 * result + Arrays.hashCode(newParms);
result = 31 * result + Arrays.hashCode(newExceptions);
result = 31 * result + Arrays.hashCode(toRemoveParm);
result = 31 * result + (isVisibilityChanged ? 1 : 0);
result = 31 * result + (isNameChanged ? 1 : 0);
result = 31 * result + (isReturnTypeChanged ? 1 : 0);
result = 31 * result + (isParameterSetOrOrderChanged ? 1 : 0);
result = 31 * result + (isExceptionSetChanged ? 1 : 0);
result = 31 * result + (isExceptionSetOrOrderChanged ? 1 : 0);
result = 31 * result + (isParameterNamesChanged ? 1 : 0);
result = 31 * result + (isParameterTypesChanged ? 1 : 0);
result = 31 * result + (isPropagationEnabled ? 1 : 0);
result = 31 * result + (wasVararg ? 1 : 0);
result = 31 * result + (retainsVarargs ? 1 : 0);
result = 31 * result + (obtainsVarags ? 1 : 0);
result = 31 * result + (arrayToVarargs ? 1 : 0);
result = 31 * result + (newNameIdentifier != null ? newNameIdentifier.hashCode() : 0);
result = 31 * result + (defaultValues != null ? Arrays.hashCode(defaultValues) : 0);
result = 31 * result + (isGenerateDelegate ? 1 : 0);
result = 31 * result + propagateParametersMethods.hashCode();
result = 31 * result + propagateExceptionsMethods.hashCode();
return result;
}
}
@@ -0,0 +1,199 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.changeSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
/**
* User: anna
* Date: Sep 6, 2010
*/
public class JavaChangeSignatureDetector implements ChangeSignatureGestureDetectorExtension {
private static final Logger LOG = Logger.getInstance("#" + JavaChangeSignatureDetector.class.getName());
@Override
public ChangeInfo createCurrentChangeSignature(final @NotNull PsiElement element,
final @Nullable ChangeInfo changeInfo) {
PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method != null && isInsideMethodSignature(element, method.getBody())) {
final String newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
final PsiType returnType = method.getReturnType();
final CanonicalTypes.Type newReturnType = returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null;
final ParameterInfoImpl[] parameterInfos = ParameterInfoImpl.fromMethod(method);
final MyJavaChangeInfo fromMethod = new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, null, method.getName());
if (changeInfo == null) { //before replacement
fromMethod.setSuperMethod(method.findDeepestSuperMethod());
return fromMethod;
} else {
final MyJavaChangeInfo info = (MyJavaChangeInfo)changeInfo;
if (!info.getMethod().equals(method)) return null;
if (!info.equals(fromMethod)) {
final JavaParameterInfo[] oldParameters = info.getNewParameters();
for (int i = 0; i < parameterInfos.length; i++) {
ParameterInfoImpl parameterInfo = parameterInfos[i];
JavaParameterInfo oldParameter = null;
for (JavaParameterInfo parameter : oldParameters) {
if (Comparing.strEqual(parameter.getName(), parameterInfo.getName()) &&
Comparing.strEqual(parameter.getTypeText(), parameterInfo.getTypeText())) {
oldParameter = parameter;
break;
}
}
if (oldParameter == null && oldParameters.length > i && info.getOldParameterNames().length > i) {
if (Comparing.strEqual(info.getOldParameterNames()[i], parameterInfo.getName()) ||
Comparing.strEqual(info.getOldParameterTypes()[i], parameterInfo.getTypeText())) {
oldParameter = oldParameters[i];
}
}
final int oldParameterIndex = oldParameter != null ? oldParameter.getOldIndex() : -1;
parameterInfos[i] = new ParameterInfoImpl(oldParameterIndex,
parameterInfo.getName(),
parameterInfo.getTypeWrapper().getType(element, element.getManager()),
oldParameterIndex == -1 ? "intellijidearulezzz" : "");
}
final MyJavaChangeInfo javaChangeInfo =
new MyJavaChangeInfo(newVisibility, method, newReturnType, parameterInfos, info.getNewExceptions(), info.getOldName()) {
@Override
protected void fillOldParams(PsiMethod method) {
oldParameterNames = info.getOldParameterNames();
oldParameterTypes = info.getOldParameterTypes();
}
};
javaChangeInfo.setSuperMethod(info.getSuperMethod());
return javaChangeInfo;
}
}
}
return null;
}
private static class MyJavaChangeInfo extends JavaChangeInfoImpl {
private PsiMethod mySuperMethod;
private MyJavaChangeInfo(String newVisibility,
PsiMethod method,
CanonicalTypes.Type newType,
@NotNull ParameterInfoImpl[] newParms,
ThrownExceptionInfo[] newExceptions,
String oldName) {
super(newVisibility, method, method.getName(), newType, newParms, newExceptions, false,
new HashSet<PsiMethod>(),
new HashSet<PsiMethod>(), oldName);
}
@Override
protected void setupPropagationEnabled(PsiParameter[] parameters, ParameterInfoImpl[] newParms) {
isPropagationEnabled = false;
}
public PsiMethod getSuperMethod() {
if (mySuperMethod == null) {
return getMethod();
}
return mySuperMethod;
}
public void setSuperMethod(PsiMethod superMethod) {
mySuperMethod = superMethod;
}
}
@Override
public boolean showDialog(ChangeInfo changeInfo, @NotNull final String oldText) {
if (changeInfo instanceof MyJavaChangeInfo) {
final MyJavaChangeInfo info = (MyJavaChangeInfo)changeInfo;
final PsiMethod method = info.getSuperMethod();
new JavaChangeSignatureDialog(method.getProject(), new JavaMethodDescriptor(info.getMethod()){
@Override
public String getReturnTypeText() {
return info.getNewReturnType().getTypeText();
}
}, true, method) {
protected BaseRefactoringProcessor createRefactoringProcessor() {
return new ChangeSignatureProcessor(myProject, new MyJavaChangeInfo(info.getNewVisibility(), info.getSuperMethod(), info.getNewReturnType(),
(ParameterInfoImpl[])info.getNewParameters(), info.getNewExceptions(), info.getOldName()));
}
@Override
protected void invokeRefactoring(final BaseRefactoringProcessor processor) {
CommandProcessor.getInstance().executeCommand(myProject, new Runnable(){
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final PsiFile file = method.getContainingFile();
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
final Document document = documentManager.getDocument(file);
if (document != null) {
document.setText(oldText);
documentManager.commitDocument(document);
}
}
});
doRefactor(processor);
}
}, RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(info.getMethod())), null);
}
private void doRefactor(BaseRefactoringProcessor processor) {
super.invokeRefactoring(processor);
}
}.show();
return true;
}
return false;
}
@Override
public boolean isChangeSignatureAvailable(PsiElement element, ChangeInfo currentInfo) {
if (currentInfo instanceof JavaChangeInfo) {
return Comparing.equal(currentInfo.getMethod(), element);
}
return false;
}
@Nullable
@Override
public TextRange getHighlightingRange(PsiElement element) {
if (element instanceof PsiMethod) {
final PsiCodeBlock body = ((PsiMethod)element).getBody();
return new TextRange(element.getTextRange().getStartOffset(), body == null ? element.getTextRange().getEndOffset() : body.getTextRange().getStartOffset() - 1);
}
return null;
}
private static boolean isInsideMethodSignature(PsiElement element, PsiCodeBlock body) {
return body == null || element.getTextOffset() < body.getTextOffset();
}
}
@@ -40,6 +40,16 @@ public class JavaThrownExceptionInfo implements ThrownExceptionInfo {
setType(type);
}
//create identity mapping
public static ThrownExceptionInfo[] extractExceptions(PsiMethod method) {
PsiClassType[] types = method.getThrowsList().getReferencedTypes();
ThrownExceptionInfo[] result = new ThrownExceptionInfo[types.length];
for (int i = 0; i < result.length; i++) {
result[i] = new JavaThrownExceptionInfo(i, types[i]);
}
return result;
}
public void setType(PsiClassType type) {
myType = CanonicalTypes.createTypeWrapper(type);
}