IDEA-57833 Groovy/Grails Refactoring Map to class. Change return type of method...
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sun, 12 Sep 2010 09:45:35 +0000 (13:45 +0400)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Sun, 12 Sep 2010 09:45:35 +0000 (13:45 +0400)
plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertMapToClassIntention.java
plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/ConvertMapToClassTest.java
plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Expected.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test_after.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Expected.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test.groovy [new file with mode: 0644]
plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test_after.groovy [new file with mode: 0644]

index 6cc4bf4ab0dca2a58120e447498347979c007181..0afe35cfae2a37a11e55c51f133ccd3a553ff3f0 100644 (file)
@@ -27,10 +27,12 @@ import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiManager;
 import com.intellij.psi.PsiType;
+import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.GroovyBundle;
 import org.jetbrains.plugins.groovy.annotator.intentions.CreateClassActionBase;
+import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
 import org.jetbrains.plugins.groovy.intentions.base.Intention;
 import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
@@ -39,15 +41,20 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
 
+import java.util.List;
+
 /**
  * @author Maxim.Medvedev
  */
@@ -90,6 +97,28 @@ public class ConvertMapToClassIntention extends Intention {
     final GrExpression newExpression = GroovyPsiElementFactory.getInstance(project)
       .createExpressionFromText("new " + generatedClass.getQualifiedName() + "(" + text.substring(begin, end) + ")");
     final GrExpression replacedNewExpression = ((GrExpression)map.replace(newExpression));
+    checkForVariableDeclaration(replacedNewExpression);
+    checkForReturnFromMethod(replacedNewExpression);
+    PsiUtil.shortenReferences(replacedNewExpression);
+  }
+
+  private static void checkForReturnFromMethod(GrExpression replacedNewExpression) {
+    final PsiElement parent = PsiUtil.skipParentheses(replacedNewExpression.getParent(), true);
+    final GrMethod method = PsiTreeUtil.getParentOfType(replacedNewExpression, GrMethod.class, true, GrClosableBlock.class);
+    if (method == null) return;
+
+    final List<GrStatement> returns = ControlFlowUtils.collectReturns(method.getBlock());
+    if (returns.size() > 1 || !returns.contains(parent)) return;
+
+    if (method.getReturnTypeElementGroovy() == null) return;
+    final PsiType returnType = method.getReturnType();
+    final PsiType type = replacedNewExpression.getType();
+    if (TypesUtil.isAssignable(returnType, type, replacedNewExpression.getManager(), replacedNewExpression.getResolveScope())) return;
+
+    method.setReturnType(type);
+  }
+
+  private static void checkForVariableDeclaration(GrExpression replacedNewExpression) {
     final PsiElement parent = PsiUtil.skipParentheses(replacedNewExpression.getParent(), true);
     if (parent instanceof GrVariable &&
         !(parent instanceof GrField) &&
@@ -98,7 +127,6 @@ public class ConvertMapToClassIntention extends Intention {
         replacedNewExpression.getType() != null) {
       ((GrVariable)parent).setType(replacedNewExpression.getType());
     }
-    PsiUtil.shortenReferences(replacedNewExpression);
   }
 
   public static GrTypeDefinition createClass(Project project, GrNamedArgument[] namedArguments, String packageName, String className) {
index 2fd0ec1763fe831d71784ac8fa0f58741d965ec2..e262d1532df451c24d78b47a6104ef5e74a7a359 100644 (file)
@@ -47,6 +47,14 @@ public class ConvertMapToClassTest extends GrIntentionTestCase {
     doTest(false);
   }
 
+  public void testChangeReturnType() {
+    doTest();
+  }
+
+  public void testNotChangeReturnType() {
+    doTest();
+  }
+
   private void doTest() {
     doTest(true);
   }
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Expected.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Expected.groovy
new file mode 100644 (file)
index 0000000..25ea0e8
--- /dev/null
@@ -0,0 +1,4 @@
+class Foo {
+int a
+int b
+}
\ No newline at end of file
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test.groovy
new file mode 100644 (file)
index 0000000..f3e81ea
--- /dev/null
@@ -0,0 +1,5 @@
+def Map foo() {
+  return [a<caret>:2, b:4];
+}
+
+print foo().a
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test_after.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/changeReturnType/Test_after.groovy
new file mode 100644 (file)
index 0000000..8e51e14
--- /dev/null
@@ -0,0 +1,5 @@
+def Foo foo() {
+  return new Foo(a: 2, b: 4);
+}
+
+print foo().a
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Expected.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Expected.groovy
new file mode 100644 (file)
index 0000000..25ea0e8
--- /dev/null
@@ -0,0 +1,4 @@
+class Foo {
+int a
+int b
+}
\ No newline at end of file
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test.groovy
new file mode 100644 (file)
index 0000000..ff30699
--- /dev/null
@@ -0,0 +1,5 @@
+def foo() {
+  return [a<caret>:2, b:4];
+}
+
+print foo().a
diff --git a/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test_after.groovy b/plugins/groovy/testdata/intentions/convertMapToClass/notChangeReturnType/Test_after.groovy
new file mode 100644 (file)
index 0000000..d866261
--- /dev/null
@@ -0,0 +1,5 @@
+def foo() {
+  return new Foo(a: 2, b: 4);
+}
+
+print foo().a