IDEA-57867 Package declaration not updated when Groovy class is moved
authorMaxim Medvedev <maxim.medvedev@jetbrains.com>
Mon, 13 Sep 2010 08:23:20 +0000 (12:23 +0400)
committerMaxim Medvedev <maxim.medvedev@jetbrains.com>
Mon, 13 Sep 2010 08:23:20 +0000 (12:23 +0400)
12 files changed:
plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyFileImpl.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassHandler.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassUtil.java [new file with mode: 0644]
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java
plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptProcessor.java
plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/move/GroovyMoveScriptTest.java
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/a/Y.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/Foo.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/X.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Foo.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Y.groovy [new file with mode: 0644]
plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/b/X.groovy [new file with mode: 0644]

index 35a6f001530c549dc46d14a9603f982be10f8b1f..8ca4752426869b2e3d28f11c354cf690205a1b00 100644 (file)
@@ -326,7 +326,7 @@ public class GroovyFileImpl extends GroovyFileBaseImpl implements GroovyFile {
     Boolean isScript = myScript;
     if (isScript == null) {
       final GrTopStatement[] topStatements = findChildrenByClass(GrTopStatement.class);
-      isScript = topStatements.length == 0;
+      isScript = Boolean.FALSE;
       for (GrTopStatement st : topStatements) {
         if (!(st instanceof GrTypeDefinition || st instanceof GrImportStatement || st instanceof GrPackageDefinition)) {
           isScript = Boolean.TRUE;
index dd807caf9ba296201097461c719b4322eb28d937..3991fe8ba11c1612de9fc5e32a2a9a2a7be9be34 100644 (file)
 
 package org.jetbrains.plugins.groovy.refactoring.move;
 
-import com.intellij.psi.*;
-import com.intellij.psi.impl.source.tree.Factory;
-import com.intellij.psi.impl.source.tree.TreeElement;
-import com.intellij.psi.javadoc.PsiDocComment;
-import com.intellij.psi.search.LocalSearchScope;
-import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
 import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler;
 import com.intellij.util.IncorrectOperationException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.groovy.GroovyFileType;
-import org.jetbrains.plugins.groovy.actions.GroovyTemplatesFactory;
-import org.jetbrains.plugins.groovy.actions.NewGroovyActionBase;
-import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
-import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
-import org.jetbrains.plugins.groovy.refactoring.GroovyChangeContextUtil;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
 
 /**
  * @author Maxim.Medvedev
  */
 public class MoveGroovyClassHandler implements MoveClassHandler {
   public PsiClass doMoveClass(@NotNull PsiClass aClass, @NotNull PsiDirectory moveDestination) throws IncorrectOperationException {
-    if (!aClass.getLanguage().equals(GroovyFileType.GROOVY_LANGUAGE)) return null;
-    PsiFile file = aClass.getContainingFile();
-    final PsiPackage newPackage = JavaDirectoryService.getInstance().getPackage(moveDestination);
-
-    GroovyChangeContextUtil.encodeContextInfo(aClass);
-
-    PsiClass newClass = null;
-    if (file instanceof GroovyFile) {
-      if (((GroovyFile)file).isScript() || ((GroovyFile)file).getClasses().length > 1) {
-        correctSelfReferences(aClass, newPackage);
-        final PsiClass created = ((GroovyFile)GroovyTemplatesFactory
-          .createFromTemplate(moveDestination, aClass.getName(), aClass.getName() + NewGroovyActionBase.GROOVY_EXTENSION,
-                              "GroovyClass.groovy")).getClasses()[0];
-        if (aClass.getDocComment() == null) {
-          final PsiDocComment createdDocComment = created.getDocComment();
-          if (createdDocComment != null) {
-            aClass.addBefore(createdDocComment, null);
-          }
-        }
-        newClass = (PsiClass)created.replace(aClass);
-        correctOldClassReferences(newClass, aClass);
-        aClass.delete();
-      }
-      else if (!moveDestination.equals(file.getContainingDirectory()) && moveDestination.findFile(file.getName()) != null) {
-        // moving second of two classes which were in the same file to a different directory (IDEADEV-3089)
-        correctSelfReferences(aClass, newPackage);
-        PsiFile newFile = moveDestination.findFile(file.getName());
-        TreeElement enter = Factory.createSingleLeafElement(GroovyTokenTypes.mNLS, "\n", 0, 1, null, aClass.getManager());
-        newFile.getNode().addChild(enter);
-        newClass = (PsiClass)newFile.add(aClass);
-        aClass.delete();
-      }
-      else if (!moveDestination.equals(file.getContainingDirectory()) && moveDestination.findFile(file.getName()) == null) {
-        if (!moveDestination.equals(file.getContainingDirectory())) {
-          aClass.getManager().moveFile(file, moveDestination);
-          newClass=((GroovyFile)file).getClasses()[0];
-          if (newPackage != null) {
-            ((PsiClassOwner)file).setPackageName(newPackage.getQualifiedName());
-          }
-        }
-      }
-    }
-    if (newClass != null) GroovyChangeContextUtil.decodeContextInfo(newClass, null, null);
-    return newClass;
-  }
-
-  private static void correctOldClassReferences(final PsiClass newClass, final PsiClass oldClass) {
-    for (PsiReference reference : ReferencesSearch.search(oldClass, new LocalSearchScope(newClass)).findAll()) {
-      reference.bindToElement(newClass);
-    }
-  }
-
-  private static void correctSelfReferences(final PsiClass aClass, final PsiPackage newContainingPackage) {
-    final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(aClass.getContainingFile().getContainingDirectory());
-    if (aPackage == null) {
-      return;
-    }
-
-    for (PsiReference reference : ReferencesSearch.search(aClass, new LocalSearchScope(aClass)).findAll()) {
-      if (reference instanceof GrCodeReferenceElement) {
-        final GrCodeReferenceElement qualifier = ((GrCodeReferenceElement)reference).getQualifier();
-        if (qualifier != null) {
-          qualifier.bindToElement(newContainingPackage);
-        }
-      }
-    }
+    return MoveGroovyClassUtil.moveGroovyClass((GrTypeDefinition)aClass, moveDestination);
   }
 
+  @Nullable
   public String getName(PsiClass clazz) {
     final PsiFile file = clazz.getContainingFile();
     if (!(file instanceof GroovyFile)) return null;
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyClassUtil.java
new file mode 100644 (file)
index 0000000..0d8e6c7
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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 org.jetbrains.plugins.groovy.refactoring.move;
+
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.tree.Factory;
+import com.intellij.psi.impl.source.tree.TreeElement;
+import com.intellij.psi.javadoc.PsiDocComment;
+import com.intellij.psi.search.LocalSearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.groovy.GroovyFileType;
+import org.jetbrains.plugins.groovy.actions.GroovyTemplatesFactory;
+import org.jetbrains.plugins.groovy.actions.NewGroovyActionBase;
+import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
+import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
+import org.jetbrains.plugins.groovy.refactoring.GroovyChangeContextUtil;
+
+/**
+ * @author Maxim.Medvedev
+ */
+public class MoveGroovyClassUtil {
+  private MoveGroovyClassUtil() {
+  }
+
+  @Nullable
+  public static PsiClass moveGroovyClass(@NotNull GrTypeDefinition aClass, @NotNull PsiDirectory moveDestination) {
+    if (!aClass.getLanguage().equals(GroovyFileType.GROOVY_LANGUAGE)) return null;
+    PsiFile file = aClass.getContainingFile();
+    final PsiPackage newPackage = JavaDirectoryService.getInstance().getPackage(moveDestination);
+
+    GroovyChangeContextUtil.encodeContextInfo(aClass);
+
+    PsiClass newClass = null;
+    if (file instanceof GroovyFile) {
+      if (((GroovyFile)file).isScript() || ((GroovyFile)file).getClasses().length > 1) {
+        correctSelfReferences(aClass, newPackage);
+        final PsiClass created = ((GroovyFile)GroovyTemplatesFactory
+          .createFromTemplate(moveDestination, aClass.getName(), aClass.getName() + NewGroovyActionBase.GROOVY_EXTENSION,
+                              "GroovyClass.groovy")).getClasses()[0];
+        if (aClass.getDocComment() == null) {
+          final PsiDocComment createdDocComment = created.getDocComment();
+          if (createdDocComment != null) {
+            aClass.addBefore(createdDocComment, null);
+          }
+        }
+        newClass = (PsiClass)created.replace(aClass);
+        correctOldClassReferences(newClass, aClass);
+        aClass.delete();
+      }
+      else if (!moveDestination.equals(file.getContainingDirectory()) && moveDestination.findFile(file.getName()) != null) {
+        // moving second of two classes which were in the same file to a different directory (IDEADEV-3089)
+        correctSelfReferences(aClass, newPackage);
+        PsiFile newFile = moveDestination.findFile(file.getName());
+        TreeElement enter = Factory.createSingleLeafElement(GroovyTokenTypes.mNLS, "\n", 0, 1, null, aClass.getManager());
+        newFile.getNode().addChild(enter);
+        newClass = (GrTypeDefinition)newFile.add(aClass);
+        aClass.delete();
+      }
+      else if (!moveDestination.equals(file.getContainingDirectory()) && moveDestination.findFile(file.getName()) == null) {
+        if (!moveDestination.equals(file.getContainingDirectory())) {
+          aClass.getManager().moveFile(file, moveDestination);
+          newClass = ((GroovyFile)file).getClasses()[0];
+          if (newPackage != null) {
+            ((PsiClassOwner)file).setPackageName(newPackage.getQualifiedName());
+          }
+        }
+      }
+    }
+    if (newClass != null) GroovyChangeContextUtil.decodeContextInfo(newClass, null, null);
+    return newClass;
+  }
+
+  private static void correctOldClassReferences(final PsiClass newClass, final PsiClass oldClass) {
+    for (PsiReference reference : ReferencesSearch.search(oldClass, new LocalSearchScope(newClass)).findAll()) {
+      reference.bindToElement(newClass);
+    }
+  }
+
+  private static void correctSelfReferences(final PsiClass aClass, final PsiPackage newContainingPackage) {
+    final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(aClass.getContainingFile().getContainingDirectory());
+    if (aPackage == null) {
+      return;
+    }
+
+    for (PsiReference reference : ReferencesSearch.search(aClass, new LocalSearchScope(aClass)).findAll()) {
+      if (reference instanceof GrCodeReferenceElement) {
+        final GrCodeReferenceElement qualifier = ((GrCodeReferenceElement)reference).getQualifier();
+        if (qualifier != null) {
+          qualifier.bindToElement(newContainingPackage);
+        }
+      }
+    }
+  }
+}
index 0ae540a5596eb0eab234e34bc0252c43e1dc99ab..b5c3bd95bf2c62a29d81b0ba752af593ede41f7a 100644 (file)
@@ -29,7 +29,6 @@ import com.intellij.psi.PsiReference;
 import com.intellij.refactoring.HelpID;
 import com.intellij.refactoring.JavaRefactoringSettings;
 import com.intellij.refactoring.move.MoveCallback;
-import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesDialog;
 import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesHandlerBase;
 import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl;
 import com.intellij.refactoring.util.CommonRefactoringUtil;
@@ -55,7 +54,7 @@ public class MoveGroovyScriptHandler extends MoveClassesOrPackagesHandlerBase {
     for (PsiElement element : elements) {
       if (!canMove(element)) return false;
     }
-    return super.canMove(elements, targetContainer);
+    return targetContainer == null || isValidTarget(targetContainer, elements);
   }
 
   private static boolean canMove(PsiElement element) {
@@ -68,7 +67,7 @@ public class MoveGroovyScriptHandler extends MoveClassesOrPackagesHandlerBase {
     }
 
     final PsiClass[] classes = file.getClasses();
-    return classes.length == 1 && classes[0] instanceof GroovyScriptClass;
+    return classes.length > 0 && classes[0] instanceof GroovyScriptClass;
   }
 
   @Override
@@ -100,7 +99,7 @@ public class MoveGroovyScriptHandler extends MoveClassesOrPackagesHandlerBase {
       PsiElement psiElement = elements[i];
       searchTextOccurences = TextOccurrencesUtil.isSearchTextOccurencesEnabled(psiElement);
     }
-    final MoveClassesOrPackagesDialog moveDialog =
+    final MoveGroovyScriptDialog moveDialog =
       new MoveGroovyScriptDialog(project, searchTextOccurences, elements, initialTargetElement, moveCallback);
 
     boolean searchInComments = JavaRefactoringSettings.getInstance().MOVE_SEARCH_IN_COMMENTS;
index 5fec31b46832c4b91fdf94923114b9c0ccafd4e8..091c33eea70807c5f8d27f3c75782e30b020d93f 100644 (file)
@@ -37,7 +37,10 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
 import org.jetbrains.plugins.groovy.refactoring.GroovyChangeContextUtil;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Maxim.Medvedev
@@ -61,11 +64,13 @@ public class MoveGroovyScriptProcessor extends MoveClassesOrPackagesProcessor {
     for (PsiElement element : elements) {
       final GroovyFile groovyFile = (GroovyFile)element;
 
-      final PsiClass scriptClass = groovyFile.getClasses()[0];
-      String newName = getTargetPackage().getQualifiedName() + scriptClass.getName();
+      final PsiClass[] classes = groovyFile.getClasses();
+      for (PsiClass aClass : classes) {
+        String newName = getTargetPackage().getQualifiedName() + aClass.getName();
 
-      final UsageInfo[] usages = MoveClassesOrPackagesUtil.findUsages(scriptClass, isSearchInComments(), isSearchInNonJavaFiles(), newName);
-      allUsages.addAll(new ArrayList<UsageInfo>(Arrays.asList(usages)));
+        final UsageInfo[] usages = MoveClassesOrPackagesUtil.findUsages(aClass, isSearchInComments(), isSearchInNonJavaFiles(), newName);
+        allUsages.addAll(new ArrayList<UsageInfo>(Arrays.asList(usages)));
+      }
     }
     myMoveDestination
       .analyzeModuleConflicts(elements, conflicts, allUsages.toArray(new UsageInfo[allUsages.size()]));
@@ -92,14 +97,15 @@ public class MoveGroovyScriptProcessor extends MoveClassesOrPackagesProcessor {
         GroovyFile file = (GroovyFile)element;
         final RefactoringElementListener elementListener = getTransaction().getElementListener(file);
 
-        PsiClass oldScriptClass = file.getClasses()[0];
-
+        final PsiClass[] oldClasses = file.getClasses();
         GroovyChangeContextUtil.encodeContextInfo(file);
         PsiManager.getInstance(myProject).moveFile(file, myMoveDestination.getTargetDirectory(file));
         file.setPackageName(getTargetPackage().getQualifiedName());
 
-        PsiClass newScriptClass = file.getClasses()[0];
-        oldToNewElementsMapping.put(oldScriptClass, newScriptClass);
+        PsiClass[] newClasses = file.getClasses();
+        for (int i = 0; i < oldClasses.length; i++) {
+          oldToNewElementsMapping.put(oldClasses[i], newClasses[i]);
+        }
 
         elementListener.elementMoved(file);
       }
index 6b51969b187a751e19bc0faf09c57576c5f699af..7fe9cd98b13bae66bd6819358c5bcfaaf3f48796 100644 (file)
@@ -52,7 +52,11 @@ public class GroovyMoveScriptTest extends LightCodeInsightFixtureTestCase {
     doTest("multiMove", new String[]{"a/Script.groovy", "a/Script2.groovy"}, "b");
   }
 
-  private void performAction(String[] fileNames, String newDirName, String dir) throws Exception {
+  public void testScriptWithClasses() {
+    doTest("scriptWithClasses", new String[]{"a/Foo.groovy"}, "b");
+  }
+
+  private void performAction(String[] fileNames, String newDirName, String dir) {
     final PsiFile[] files = new PsiFile[fileNames.length];
     for (int i = 0; i < files.length; i++) {
       String fileName = fileNames[i];
@@ -81,7 +85,7 @@ public class GroovyMoveScriptTest extends LightCodeInsightFixtureTestCase {
     FileDocumentManager.getInstance().saveAllDocuments();
   }
 
-  private void doTest(String testName, String[] fileNames, String newDirName) throws Exception {
+  private void doTest(String testName, String[] fileNames, String newDirName) {
     final VirtualFile actualRoot = myFixture.copyDirectoryToProject(testName + "/before", "");
 
     performAction(fileNames, newDirName, VfsUtil.getRelativePath(actualRoot, myFixture.getTempDirFixture().getFile(""), '/'));
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/a/Y.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/a/Y.groovy
new file mode 100644 (file)
index 0000000..1c86d86
--- /dev/null
@@ -0,0 +1,10 @@
+package a
+import b.Foo
+
+class Y {
+  def foo
+
+  def a() {
+    Foo.main();
+  }
+}
\ No newline at end of file
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/Foo.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/Foo.groovy
new file mode 100644 (file)
index 0000000..628ec5b
--- /dev/null
@@ -0,0 +1,10 @@
+package a
+import a.Y
+
+class A {
+  def foo
+}
+
+print new A().foo
+print new X().foo
+print new Y().foo
\ No newline at end of file
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/X.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/after/b/X.groovy
new file mode 100644 (file)
index 0000000..93e64ac
--- /dev/null
@@ -0,0 +1,10 @@
+package b
+
+class X {
+  def foo
+
+  def a() {
+    A a = new A()
+    print a
+  }
+}
\ No newline at end of file
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Foo.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Foo.groovy
new file mode 100644 (file)
index 0000000..883870c
--- /dev/null
@@ -0,0 +1,10 @@
+package a
+import b.X
+
+class A {
+  def foo
+}
+
+print new A().foo
+print new X().foo
+print new Y().foo
\ No newline at end of file
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Y.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/a/Y.groovy
new file mode 100644 (file)
index 0000000..6d9588b
--- /dev/null
@@ -0,0 +1,9 @@
+package a
+
+class Y {
+  def foo
+
+  def a() {
+    Foo.main();
+  }
+}
\ No newline at end of file
diff --git a/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/b/X.groovy b/plugins/groovy/testdata/refactoring/move/moveScript/scriptWithClasses/before/b/X.groovy
new file mode 100644 (file)
index 0000000..a8e247c
--- /dev/null
@@ -0,0 +1,11 @@
+package b
+import a.A
+
+class X {
+  def foo
+
+  def a() {
+    A a = new A()
+    print a
+  }
+}
\ No newline at end of file