intention to convert class type params (IDEA-120721)
authorAnna Kozlova <anna.kozlova@jetbrains.com>
Wed, 4 Jun 2014 16:05:44 +0000 (20:05 +0400)
committerAnna Kozlova <anna.kozlova@jetbrains.com>
Wed, 4 Jun 2014 16:48:35 +0000 (20:48 +0400)
plugins/typeMigration/src/META-INF/plugin.xml
plugins/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java [new file with mode: 0644]
plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/after.java.template [new file with mode: 0644]
plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/before.java.template [new file with mode: 0644]
plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/description.html [new file with mode: 0644]

index 1b668325b9891f6228b33523880f875155b492e6..93ad2032892c9e98beb0f5a6b1f83b27dcf82daa 100644 (file)
       <className>com.intellij.refactoring.typeMigration.intentions.ConvertFieldToThreadLocalIntention</className>
       <category>Concurrency</category>
     </intentionAction>
+    <intentionAction>
+      <className>com.intellij.refactoring.typeMigration.intentions.ChangeClassParametersIntention</className>
+      <category>Declaration</category>
+    </intentionAction>
   </extensions>
   <depends>Structural Search</depends>
 </idea-plugin>
diff --git a/plugins/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java b/plugins/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java
new file mode 100644 (file)
index 0000000..9571807
--- /dev/null
@@ -0,0 +1,124 @@
+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();
+        return result.getElement() != null && ((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;
+  }
+}
diff --git a/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/after.java.template b/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/after.java.template
new file mode 100644 (file)
index 0000000..413db79
--- /dev/null
@@ -0,0 +1,10 @@
+public class X {
+    {
+        new Comparable<String>() {
+            @Override
+            public int compareTo(String o) {
+              return 0;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/before.java.template b/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/before.java.template
new file mode 100644 (file)
index 0000000..96eb1b4
--- /dev/null
@@ -0,0 +1,10 @@
+public class X {
+    {
+        new Comparable<<spam>Object</spam>>() {
+            @Override
+            public int compareTo(Object o) {
+              return 0;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/description.html b/plugins/typeMigration/src/intentionDescriptions/ChangeClassParametersIntention/description.html
new file mode 100644 (file)
index 0000000..d4dc0a2
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<body>
+Changes anonymous class type parameters in live template.
+</body>
+</html>
\ No newline at end of file