IDEA-58049 Provide add type parameter quick-fix intention implemented

This commit is contained in:
Danila Ponomarenko
2012-06-25 18:11:54 +04:00
parent 6772fd01ea
commit b83dfbf637
16 changed files with 117 additions and 258 deletions
@@ -15,14 +15,13 @@
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.refactoring.changeClassSignature.ChangeClassSignatureDialog;
import com.intellij.refactoring.changeClassSignature.TypeParameterInfo;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -62,119 +61,50 @@ public class ChangeClassSignatureFromUsageFix extends BaseIntentionAction {
return false;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
Map<PsiTypeParameter, Boolean> typeParameterBooleanMap = createTypeParameters(
factory,
classTypeParameterList.getTypeParameters(),
myParameterList.getTypeParameterElements()
);
if (isAddOneTypeParameter(typeParameterBooleanMap)) {
setText(QuickFixBundle.message("add.type.parameter.text", myClass.getName()));
}
else {
setText(QuickFixBundle.message("change.class.signature.text", myClass.getName(), parametersToSignatureText(typeParameterBooleanMap)));
}
setText(QuickFixBundle.message("change.class.signature.text", myClass.getName(), myParameterList.getText()));
return true;
}
private static boolean isAddOneTypeParameter(@NotNull Map<PsiTypeParameter, Boolean> map) {
boolean oneParameter = false;
for (Boolean b : map.values()) {
if (b == Boolean.TRUE) {
if (oneParameter) {
return false;
}
oneParameter = true;
}
}
return true;
}
@NotNull
private static String parametersToSignatureText(@NotNull Map<PsiTypeParameter, Boolean> map) {
final StringBuilder result = new StringBuilder("&lt;");
for (Map.Entry<PsiTypeParameter, Boolean> e : map.entrySet()) {
final String text = e.getKey().getText();
if (e.getValue() == Boolean.TRUE) {
result.append("<b>").append(text).append("</b>");
}
else {
result.append(text);
}
result.append(", ");
}
final int lng = result.length();
result.delete(lng - 2, lng);
return result.append("&gt;").toString();
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
final PsiTypeParameterList classTypeParameterList = myClass.getTypeParameterList();
if (classTypeParameterList == null) {
return;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiElement newTypeParameterList = classTypeParameterList.replace(
createTypeParameterList(
factory,
classTypeParameterList.getTypeParameters(),
myParameterList.getTypeParameterElements()
)
ChangeClassSignatureDialog dialog = new ChangeClassSignatureDialog(
myClass,
createTypeParameters(
JavaCodeFragmentFactory.getInstance(project),
Arrays.asList(classTypeParameterList.getTypeParameters()),
Arrays.asList(myParameterList.getTypeParameterElements())
),
false
);
navigateTo(newTypeParameterList);
}
private static void navigateTo(@NotNull PsiElement element) {
element.getContainingFile().navigate(false);
final Editor editor = PsiUtilBase.findEditor(element);
if (editor == null) {
return;
}
editor.getCaretModel().moveToOffset(element.getTextRange().getStartOffset());
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
dialog.show();
}
@NotNull
private static PsiTypeParameterList createTypeParameterList(@NotNull PsiElementFactory factory,
@NotNull PsiTypeParameter[] classTypeParameters,
@NotNull PsiTypeElement[] typeElements) {
final PsiTypeParameterList result = factory.createTypeParameterList();
for (PsiTypeParameter p : createTypeParameters(factory, classTypeParameters, typeElements).keySet()) {
result.add(p);
}
return result;
}
@NotNull
private static Map<PsiTypeParameter, Boolean> createTypeParameters(@NotNull PsiElementFactory factory,
@NotNull PsiTypeParameter[] classTypeParameters,
@NotNull PsiTypeElement[] typeElements) {
final LinkedHashMap<PsiTypeParameter, Boolean> result = new LinkedHashMap<PsiTypeParameter, Boolean>();
private static Map<TypeParameterInfo, PsiTypeCodeFragment> createTypeParameters(@NotNull JavaCodeFragmentFactory factory,
@NotNull List<PsiTypeParameter> classTypeParameters,
@NotNull List<PsiTypeElement> typeElements) {
final LinkedHashMap<TypeParameterInfo, PsiTypeCodeFragment> result = new LinkedHashMap<TypeParameterInfo, PsiTypeCodeFragment>();
final TypeParameterNameSuggester suggester = new TypeParameterNameSuggester(classTypeParameters);
final Queue<PsiTypeParameter> classTypeParametersQueue = new LinkedList<PsiTypeParameter>(Arrays.asList(classTypeParameters));
int listIndex = 0;
for (PsiTypeElement typeElement : typeElements) {
if (!classTypeParametersQueue.isEmpty()) {
final PsiTypeParameter typeParameter = classTypeParametersQueue.peek();
if (listIndex < classTypeParameters.size()) {
final PsiTypeParameter typeParameter = classTypeParameters.get(listIndex);
if (isAssignable(typeParameter, typeElement.getType())) {
result.put(typeParameter, false);
classTypeParametersQueue.poll();
result.put(new TypeParameterInfo(listIndex++), null);
continue;
}
}
result.put(toTypeParameter(factory, suggester, typeElement), true);
final PsiClassType type = (PsiClassType)typeElement.getType();
result.put(new TypeParameterInfo(suggester.suggest(type), type), factory.createTypeCodeFragment(type.getClassName(), typeElement, true));
}
return result;
}
@@ -189,25 +119,20 @@ public class ChangeClassSignatureFromUsageFix extends BaseIntentionAction {
return true;
}
@NotNull
private static PsiTypeParameter toTypeParameter(@NotNull PsiElementFactory factory,
@NotNull TypeParameterNameSuggester suggester,
@NotNull PsiTypeElement typeElement) {
final PsiType type = typeElement.getType();
return factory.createTypeParameter(suggester.suggest((PsiClassType)type), PsiClassType.EMPTY_ARRAY);
}
@Override
public boolean startInWriteAction() {
return true;
return false;
}
private static class TypeParameterNameSuggester {
private final Set<String> usedNames = new HashSet<String>();
public TypeParameterNameSuggester(@NotNull PsiTypeParameter[] typeParameters) {
public TypeParameterNameSuggester(@NotNull PsiTypeParameter... typeParameters) {
this(Arrays.asList(typeParameters));
}
public TypeParameterNameSuggester(@NotNull Collection<PsiTypeParameter> typeParameters) {
for (PsiTypeParameter p : typeParameters) {
usedNames.add(p.getName());
}
@@ -25,12 +25,12 @@ import com.intellij.refactoring.ui.JavaCodeFragmentTableCellEditor;
import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.refactoring.ui.StringTableCellEditor;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.EditableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -39,17 +39,15 @@ import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.*;
import java.util.List;
import java.util.Map;
/**
* @author dsl
* @author Konstantin Bulenkov
*/
public class ChangeClassSignatureDialog extends RefactoringDialog {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeClassSignature.ChangeClassSignatureDialog");
private static final Logger LOG = Logger.getInstance(ChangeClassSignatureDialog.class);
private static final int NAME_COLUMN = 0;
private static final int VALUE_COLUMN = 1;
@@ -61,19 +59,53 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
private final MyTableModel myTableModel;
private JBTable myTable;
static final String REFACTORING_NAME = RefactoringBundle.message("changeClassSignature.refactoring.name");
private boolean myHideDefaultValueColumn;
public ChangeClassSignatureDialog(PsiClass aClass) {
public ChangeClassSignatureDialog(@NotNull PsiClass aClass, boolean hideDefaultValueColumn) {
this(
aClass,
initTypeParameterInfos(aClass.getTypeParameters().length),
initTypeCodeFragment(aClass.getTypeParameters().length),
hideDefaultValueColumn
);
}
@NotNull
private static List<TypeParameterInfo> initTypeParameterInfos(int length) {
final List<TypeParameterInfo> result = new ArrayList<TypeParameterInfo>();
for (int i = 0; i < length; i++) {
result.add(new TypeParameterInfo(i));
}
return result;
}
@NotNull
private static List<PsiTypeCodeFragment> initTypeCodeFragment(int length) {
final List<PsiTypeCodeFragment> result = new ArrayList<PsiTypeCodeFragment>();
for (int i = 0; i < length; i++) {
result.add(null);
}
return result;
}
public ChangeClassSignatureDialog(@NotNull PsiClass aClass,
@NotNull Map<TypeParameterInfo, PsiTypeCodeFragment> parameters,
boolean hideDefaultValueColumn) {
this(aClass, parameters.keySet(), parameters.values(), hideDefaultValueColumn);
}
public ChangeClassSignatureDialog(@NotNull PsiClass aClass,
@NotNull Collection<TypeParameterInfo> typeParameterInfos,
@NotNull Collection<PsiTypeCodeFragment> typeCodeFragments,
boolean hideDefaultValueColumn) {
super(aClass.getProject(), true);
myHideDefaultValueColumn = hideDefaultValueColumn;
setTitle(REFACTORING_NAME);
myClass = aClass;
myProject = myClass.getProject();
myTypeParameterInfos = new ArrayList<TypeParameterInfo>();
myTypeCodeFragments = new ArrayList<PsiTypeCodeFragment>();
myOriginalParameters = myClass.getTypeParameters();
for (int i = 0; i < myOriginalParameters.length; i++) {
myTypeParameterInfos.add(new TypeParameterInfo(i));
myTypeCodeFragments.add(null);
}
myTypeParameterInfos = new ArrayList<TypeParameterInfo>(typeParameterInfos);
myTypeCodeFragments = new ArrayList<PsiTypeCodeFragment>(typeCodeFragments);
myTableModel = new MyTableModel();
init();
}
@@ -115,26 +147,28 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
myTable.setCellSelectionEnabled(true);
myTable.setFocusCycleRoot(true);
final TableColumn defaultValue = myTable.getColumnModel().getColumn(1);
myTable.removeColumn(defaultValue);
myTable.getModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
myTable.getModel().removeTableModelListener(this);
final TableColumnAnimator animator = new TableColumnAnimator(myTable);
animator.setStep(20);
animator.addColumn(defaultValue, myTable.getWidth() / 2);
animator.startAndDoWhenDone(new Runnable() {
@Override
public void run() {
myTable.editCellAt(myTable.getRowCount() - 1, 0);
}
});
animator.start();
if (myHideDefaultValueColumn) {
final TableColumn defaultValue = myTable.getColumnModel().getColumn(VALUE_COLUMN);
myTable.removeColumn(defaultValue);
myTable.getModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
myTable.getModel().removeTableModelListener(this);
final TableColumnAnimator animator = new TableColumnAnimator(myTable);
animator.setStep(20);
animator.addColumn(defaultValue, myTable.getWidth() / 2);
animator.startAndDoWhenDone(new Runnable() {
@Override
public void run() {
myTable.editCellAt(myTable.getRowCount() - 1, 0);
}
});
animator.start();
}
}
}
});
});
}
final JPanel panel = new JPanel(new BorderLayout());
panel.add(SeparatorFactory.createSeparator(RefactoringBundle.message("changeClassSignature.parameters.panel.border.title"), myTable), BorderLayout.NORTH);
@@ -208,12 +242,13 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
}
public Object getValueAt(int rowIndex, int columnIndex) {
switch(columnIndex) {
switch (columnIndex) {
case NAME_COLUMN:
TypeParameterInfo info = myTypeParameterInfos.get(rowIndex);
if (info.isForExistingParameter()) {
return myOriginalParameters[info.getOldParameterIndex()].getName();
} else {
}
else {
return info.getNewName();
}
case VALUE_COLUMN:
@@ -228,18 +263,21 @@ public class ChangeClassSignatureDialog extends RefactoringDialog {
}
public String getColumnName(int column) {
switch(column) {
case NAME_COLUMN: return RefactoringBundle.message("column.name.name");
case VALUE_COLUMN: return RefactoringBundle.message("changeSignature.default.value.column");
default: LOG.assertTrue(false);
switch (column) {
case NAME_COLUMN:
return RefactoringBundle.message("column.name.name");
case VALUE_COLUMN:
return RefactoringBundle.message("changeSignature.default.value.column");
default:
LOG.assertTrue(false);
}
return null;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
switch(columnIndex) {
switch (columnIndex) {
case NAME_COLUMN:
myTypeParameterInfos.get(rowIndex).setNewName((String) aValue);
myTypeParameterInfos.get(rowIndex).setNewName((String)aValue);
break;
case VALUE_COLUMN:
break;
@@ -25,17 +25,17 @@ import org.jetbrains.annotations.NonNls;
/**
* @author dsl
*/
class TypeParameterInfo {
public class TypeParameterInfo {
private final int myOldParameterIndex;
private String myNewName;
private CanonicalTypes.Type myDefaultValue;
TypeParameterInfo(int oldIndex) {
public TypeParameterInfo(int oldIndex) {
myOldParameterIndex = oldIndex;
myDefaultValue = null;
}
TypeParameterInfo(String name, PsiType aType) {
public TypeParameterInfo(String name, PsiType aType) {
myOldParameterIndex = -1;
myNewName = name;
if (aType != null) {
@@ -20,6 +20,8 @@ import com.intellij.ide.util.SuperMethodWarningUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
@@ -99,8 +101,12 @@ public class JavaChangeSignatureHandler implements ChangeSignatureHandler {
}
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) return;
ChangeClassSignatureDialog dialog = new ChangeClassSignatureDialog(aClass);
ChangeClassSignatureDialog dialog = new ChangeClassSignatureDialog(aClass, true);
//if (!ApplicationManager.getApplication().isUnitTestMode()){
dialog.show();
//}else {
// dialog.showAndGetOk()
//}
}
@Nullable