| package com.intellij.refactoring.typeMigration.intentions; |
| |
| import com.intellij.codeInsight.FileModificationService; |
| import com.intellij.codeInsight.hint.HintManager; |
| import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; |
| import com.intellij.codeInsight.intention.impl.TypeExpression; |
| import com.intellij.codeInsight.template.*; |
| import com.intellij.codeInsight.template.impl.TemplateState; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.refactoring.typeMigration.TypeMigrationProcessor; |
| import com.intellij.refactoring.typeMigration.TypeMigrationRules; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import org.jetbrains.annotations.NotNull; |
| |
| /** |
| * @author anna |
| */ |
| public class ChangeClassParametersIntention extends PsiElementBaseIntentionAction { |
| |
| private static final Logger LOG = Logger.getInstance("#" + ChangeClassParametersIntention.class); |
| |
| @NotNull |
| @Override |
| public String getText() { |
| return getFamilyName(); |
| } |
| |
| @NotNull |
| @Override |
| public String getFamilyName() { |
| return "Change class type parameter"; |
| } |
| |
| @Override |
| public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { |
| final PsiTypeElement typeElement = PsiTreeUtil.getTopmostParentOfType(element, PsiTypeElement.class); |
| final PsiElement parent = typeElement != null ? typeElement.getParent() : null; |
| final PsiReferenceParameterList parameterList = parent instanceof PsiReferenceParameterList ? (PsiReferenceParameterList)parent : null; |
| if (parameterList != null && parameterList.getTypeArguments().length > 0) { |
| final PsiMember member = PsiTreeUtil.getParentOfType(parameterList, PsiMember.class); |
| if (member instanceof PsiAnonymousClass) { |
| final PsiClassType.ClassResolveResult result = ((PsiAnonymousClass)member).getBaseClassType().resolveGenerics(); |
| final PsiClass baseClass = result.getElement(); |
| return baseClass != null && baseClass.getTypeParameters().length == parameterList.getTypeParameterElements().length && |
| ((PsiAnonymousClass)member).getBaseClassReference().getParameterList() == parameterList; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException { |
| if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) return; |
| |
| final PsiTypeElement typeElement = PsiTreeUtil.getTopmostParentOfType(element, PsiTypeElement.class); |
| final PsiReferenceParameterList parameterList = PsiTreeUtil.getParentOfType(element, PsiReferenceParameterList.class); |
| if (parameterList != null && typeElement != null) { |
| final PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); |
| if (aClass instanceof PsiAnonymousClass) { |
| editor.getCaretModel().moveToOffset(aClass.getTextOffset()); |
| final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements(); |
| final int changeIdx = ArrayUtil.find(typeElements, typeElement); |
| |
| final PsiClassType.ClassResolveResult result = ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics(); |
| final PsiClass baseClass = result.getElement(); |
| LOG.assertTrue(baseClass != null); |
| final PsiTypeParameter typeParameter = baseClass.getTypeParameters()[changeIdx]; |
| |
| final TemplateBuilderImpl templateBuilder = (TemplateBuilderImpl)TemplateBuilderFactory.getInstance().createTemplateBuilder(aClass); |
| |
| final String oldTypeText = typeElement.getText(); |
| final String varName = "param"; |
| templateBuilder.replaceElement(typeElement, varName, new TypeExpression(project, new PsiType[]{typeElement.getType()}), true); |
| |
| final Template template = templateBuilder.buildInlineTemplate(); |
| TemplateManager.getInstance(project).startTemplate(editor, template, false, null, new TemplateEditingAdapter() { |
| private String myNewType; |
| |
| @Override |
| public void beforeTemplateFinished(TemplateState state, Template template) { |
| final TextResult value = state.getVariableValue(varName); |
| myNewType = value != null ? value.getText() : ""; |
| final int segmentsCount = state.getSegmentsCount(); |
| final Document document = state.getEditor().getDocument(); |
| for (int i = 0; i < segmentsCount; i++) { |
| final TextRange segmentRange = state.getSegmentRange(i); |
| document.replaceString(segmentRange.getStartOffset(), segmentRange.getEndOffset(), oldTypeText); |
| } |
| } |
| |
| @Override |
| public void templateFinished(Template template, boolean brokenOff) { |
| if (!brokenOff) { |
| final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); |
| try { |
| final PsiType targetParam = elementFactory.createTypeFromText(myNewType, aClass); |
| final TypeMigrationRules myRules = new TypeMigrationRules(((PsiAnonymousClass)aClass).getBaseClassType()); |
| final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, targetParam); |
| final PsiType targetClassType = elementFactory.createType(baseClass, substitutor); |
| myRules.setMigrationRootType(targetClassType); |
| myRules.setBoundScope(new LocalSearchScope(aClass)); |
| new TypeMigrationProcessor(project, ((PsiAnonymousClass)aClass).getBaseClassReference().getParameterList(), myRules).run(); |
| } |
| catch (IncorrectOperationException e) { |
| HintManager.getInstance().showErrorHint(editor, "Incorrect type"); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| @Override |
| public boolean startInWriteAction() { |
| return true; |
| } |
| } |