From 94105ecee4d0424e0387c0532504bcf7fe227c11 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Thu, 20 Oct 2016 19:01:21 +0200 Subject: [PATCH] inline to anonymous: allow to inline from library (IDEA-162893) --- .../inline/InlineToAnonymousClassDialog.java | 7 +++- .../inline/InlineToAnonymousClassHandler.java | 22 ++++++------ .../InlineToAnonymousClassProcessor.java | 33 +++++++++++------- ...InlineToAnonymousConstructorProcessor.java | 5 +-- .../multifile/fromLibrary/after/Test.java | 7 ++++ .../multifile/fromLibrary/before/Test.java | 6 ++++ .../multifile/fromLibrary/lib/simple.jar | Bin 0 -> 433 bytes .../multifile/fromLibrary/lib/src/p/P.java | 6 ++++ .../InlineToAnonymousClassMultifileTest.java | 28 ++++++++++++++- 9 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/after/Test.java create mode 100644 java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/before/Test.java create mode 100644 java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/simple.jar create mode 100644 java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/src/p/P.java diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassDialog.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassDialog.java index b97da5d12b1f..6964062e1169 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -81,6 +81,11 @@ public class InlineToAnonymousClassDialog extends InlineOptionsWithSearchSetting protected void saveSearchInCommentsAndStrings(boolean searchInComments) { JavaRefactoringSettings.getInstance().INLINE_CLASS_SEARCH_IN_COMMENTS = searchInComments; } + + @Override + protected boolean allowInlineAll() { + return true; + } @Override protected void saveSearchInTextOccurrences(boolean searchInTextOccurrences) { diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java index 4803b249a2d7..9f8876c1a70c 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -25,6 +25,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.patterns.ElementPattern; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.psi.search.searches.ReferencesSearch; @@ -115,7 +116,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { } final Ref errorMessage = new Ref<>(); - if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> errorMessage.set(getCannotInlineMessage(psiClass))), "Check if inline is possible...", true, project)) return; + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> errorMessage.set(getCannotInlineMessage((PsiClass)psiClass.getNavigationElement()))), "Check if inline is possible...", true, project)) return; if (errorMessage.get() != null) { CommonRefactoringUtil.showErrorHint(project, editor, errorMessage.get(), RefactoringBundle.message("inline.to.anonymous.refactoring"), null); return; @@ -201,7 +202,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { if (psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) { return RefactoringBundle.message("inline.to.anonymous.no.abstract"); } - if (!psiClass.getManager().isInProject(psiClass)) { + if (psiClass instanceof PsiCompiledElement) { return "Library classes cannot be inlined"; } @@ -230,6 +231,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { } } + final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(psiClass.getProject()); final PsiMethod[] methods = psiClass.getMethods(); for(PsiMethod method: methods) { if (method.isConstructor()) { @@ -238,7 +240,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { } } else if (method.findSuperMethods().length == 0) { - if (!ReferencesSearch.search(method).forEach(new AllowedUsagesProcessor(psiClass))) { + if (!ReferencesSearch.search(method, searchScope).forEach(new AllowedUsagesProcessor(psiClass))) { return "Class cannot be inlined because there are usages of its methods not inherited from its superclass or interface"; } } @@ -253,7 +255,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { if (classModifiers.hasModifierProperty(PsiModifier.STATIC)) { return "Class cannot be inlined because it has static inner classes"; } - if (!ReferencesSearch.search(innerClass).forEach(new AllowedUsagesProcessor(psiClass))) { + if (!ReferencesSearch.search(innerClass, searchScope).forEach(new AllowedUsagesProcessor(psiClass))) { return "Class cannot be inlined because it has usages of its inner classes"; } } @@ -274,7 +276,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { return "Class cannot be inlined because it has static fields with non-constant initializers"; } } - if (!ReferencesSearch.search(field).forEach(new AllowedUsagesProcessor(psiClass))) { + if (!ReferencesSearch.search(field, searchScope).forEach(new AllowedUsagesProcessor(psiClass))) { return "Class cannot be inlined because it has usages of fields not inherited from its superclass"; } } @@ -305,7 +307,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { @Nullable private static String getCannotInlineDueToUsagesMessage(final PsiClass aClass) { boolean hasUsages = false; - for(PsiReference reference : ReferencesSearch.search(aClass)) { + for(PsiReference reference : ReferencesSearch.search(aClass, GlobalSearchScope.projectScope(aClass.getProject()))) { final PsiElement element = reference.getElement(); if (element == null) continue; if (!PsiTreeUtil.isAncestor(aClass, element, false)) { @@ -359,10 +361,10 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { @Override public boolean process(final PsiReference psiReference) { - if (PsiTreeUtil.isAncestor(myPsiElement, psiReference.getElement(), false)) { + PsiElement element = psiReference.getElement(); + if (element != null && PsiTreeUtil.isAncestor(myPsiElement, element.getNavigationElement(), false)) { return true; } - PsiElement element = psiReference.getElement(); if (element instanceof PsiReferenceExpression) { PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression(); while (qualifier instanceof PsiParenthesizedExpression) { @@ -371,7 +373,7 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { if (qualifier instanceof PsiNewExpression) { PsiNewExpression newExpr = (PsiNewExpression) qualifier; PsiJavaCodeReferenceElement classRef = newExpr.getClassReference(); - if (classRef != null && myPsiElement.equals(classRef.resolve())) { + if (classRef != null && myPsiElement.isEquivalentTo(classRef.resolve())) { return true; } } diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java index 4bf245ccfc4c..967613b7e0ed 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -76,7 +76,8 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { return new UsageInfo[] { new UsageInfo(myCallToInline) }; } Set usages = new HashSet<>(); - for (PsiReference reference : ReferencesSearch.search(myClass)) { + final GlobalSearchScope searchScope = GlobalSearchScope.projectScope(myProject); + for (PsiReference reference : ReferencesSearch.search(myClass, searchScope)) { usages.add(new UsageInfo(reference.getElement())); } @@ -89,9 +90,8 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { } if (mySearchInNonJavaFiles) { - GlobalSearchScope projectScope = GlobalSearchScope.projectScope(myClass.getProject()); - TextOccurrencesUtil.addTextOccurences(myClass, qName, projectScope, nonCodeUsages, - new NonCodeUsageInfoFactory(myClass, qName)); + TextOccurrencesUtil.addTextOccurences(myClass, qName, searchScope, nonCodeUsages, + new NonCodeUsageInfoFactory(myClass, qName)); } usages.addAll(nonCodeUsages); } @@ -99,6 +99,15 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { return usages.toArray(new UsageInfo[usages.size()]); } + @NotNull + @Override + protected Collection getElementsToWrite(@NotNull UsageViewDescriptor descriptor) { + if (!myInlineThisOnly && !myClass.isWritable()) { + return Collections.emptyList(); + } + return super.getElementsToWrite(descriptor); + } + protected void refreshElements(@NotNull PsiElement[] elements) { assert elements.length == 1; myClass = (PsiClass) elements [0]; @@ -156,7 +165,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { @Override public void visitParameter(PsiParameter parameter) { super.visitParameter(parameter); - if (PsiUtil.resolveClassInType(parameter.getType()) != myClass) return; + if (!myClass.isEquivalentTo(PsiUtil.resolveClassInType(parameter.getType()))) return; for (PsiReference psiReference : ReferencesSearch.search(parameter)) { final PsiElement refElement = psiReference.getElement(); @@ -180,7 +189,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { @Override public void visitNewExpression(PsiNewExpression expression) { super.visitNewExpression(expression); - if (PsiUtil.resolveClassInType(expression.getType()) != myClass) return; + if (!myClass.isEquivalentTo(PsiUtil.resolveClassInType(expression.getType()))) return; result.putValue(expression, "Class cannot be inlined because a call to its constructor inside body"); } @@ -189,7 +198,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { super.visitMethodCallExpression(expression); final PsiReferenceExpression methodExpression = expression.getMethodExpression(); final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); - if (qualifierExpression != null && PsiUtil.resolveClassInType(qualifierExpression.getType()) != myClass) return; + if (qualifierExpression != null && !myClass.isEquivalentTo(PsiUtil.resolveClassInType(qualifierExpression.getType()))) return; final PsiElement resolved = methodExpression.resolve(); if (resolved instanceof PsiMethod) { final PsiMethod method = (PsiMethod)resolved; @@ -244,7 +253,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { LOG.error(e); } } - if (!myInlineThisOnly) { + if (!myInlineThisOnly && myClass.getOriginalElement().isWritable()) { try { myClass.delete(); } @@ -262,7 +271,7 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { else { PsiClass target = superType.resolve(); assert target != null : superType; - PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); + PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory(); PsiJavaCodeReferenceElement element = factory.createClassReferenceElement(target); PsiJavaCodeReferenceElement reference = psiNewExpression.getClassReference(); assert reference != null : psiNewExpression; @@ -275,11 +284,11 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor { } private void replaceWithSuperType(final PsiTypeElement typeElement, final PsiClassType superType) { - PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); + PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory(); PsiClassType psiType = (PsiClassType) typeElement.getType(); PsiClassType.ClassResolveResult classResolveResult = psiType.resolveGenerics(); PsiType substType = classResolveResult.getSubstitutor().substitute(superType); - assert classResolveResult.getElement() == myClass; + assert myClass.isEquivalentTo(classResolveResult.getElement()); try { PsiElement replaced = typeElement.replace(factory.createTypeElement(substType)); JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(replaced); diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousConstructorProcessor.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousConstructorProcessor.java index 010aff367ce0..a731ee0dad43 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousConstructorProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousConstructorProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -82,7 +82,8 @@ class InlineToAnonymousConstructorProcessor { checkInlineChainingConstructor(); JavaResolveResult classResolveResult = myNewExpression.getClassReference().advancedResolve(false); JavaResolveResult methodResolveResult = myNewExpression.resolveMethodGenerics(); - myConstructor = (PsiMethod) methodResolveResult.getElement(); + final PsiElement element = methodResolveResult.getElement(); + myConstructor = element != null ? (PsiMethod) element.getNavigationElement() : null; myConstructorArguments = myNewExpression.getArgumentList(); PsiSubstitutor classResolveSubstitutor = classResolveResult.getSubstitutor(); diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/after/Test.java b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/after/Test.java new file mode 100644 index 000000000000..e5577a73b86b --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/after/Test.java @@ -0,0 +1,7 @@ +class Test { + { + Object p = new Object() { + public void foo() {} + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/before/Test.java b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/before/Test.java new file mode 100644 index 000000000000..d5ae32e6907e --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/before/Test.java @@ -0,0 +1,6 @@ +import p.P; +class Test { + { + P p = new P(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/simple.jar b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/simple.jar new file mode 100644 index 0000000000000000000000000000000000000000..55a54c91cf5d254f179c6e58a5452198ca7721a7 GIT binary patch literal 433 zcmWIWW@h1HVBlb22xd$*G{)??U&*1i=a>e4uN(C2HErh48F7jGozyAnJ_OkFv0xnw9-?zsqb^A|D zdGkeiZkLW&tI@BtsHbjkIm^Dt>K(6noR|H@VVug5yrBHTQN1msfvawhGOjzWTUqkK~4flmGKCJSqL*&Mx_i zwI*_h{Ze=EJMLot+m#j17~sv$!5XA}?kdoy+KiyEU}WNAz#U>p0SFEoNcb@_i7+5S g5ZOFX2qJ(AkO>dZ0B=?{kQ5UTW&>#hp!Ez40I27G*Z=?k literal 0 HcmV?d00001 diff --git a/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/src/p/P.java b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/src/p/P.java new file mode 100644 index 000000000000..1ed7a9ddf67e --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineToAnonymousClass/multifile/fromLibrary/lib/src/p/P.java @@ -0,0 +1,6 @@ +package p; +public class P { + public P() { + } + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassMultifileTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassMultifileTest.java index de5a9af59284..33adbe1b57ee 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassMultifileTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineToAnonymousClassMultifileTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -16,7 +16,13 @@ package com.intellij.refactoring.inline; import com.intellij.JavaTestUtil; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; @@ -47,6 +53,10 @@ public class InlineToAnonymousClassMultifileTest extends RefactoringTestCase { doTest("p1.Inlined"); } + public void testFromLibrary() throws Exception { // IDEADEV-18745 + doTest("p.P"); + } + private String getRoot() { return JavaTestUtil.getJavaTestDataPath() + "/refactoring/inlineToAnonymousClass/multifile/" + getTestName(true); } @@ -55,7 +65,23 @@ public class InlineToAnonymousClassMultifileTest extends RefactoringTestCase { String rootBefore = getRoot() + "/before"; PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17()); final VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, rootBefore, myFilesToDelete); + String path = getRoot() + "/lib/simple.jar"; + VirtualFile libJarLocal = LocalFileSystem.getInstance().findFileByPath(path); + if (libJarLocal != null) { + ModuleRootModificationUtil.updateModel(myModule, model -> { + LibraryTable libraryTable = model.getModuleLibraryTable(); + Library library = libraryTable.createLibrary("test"); + Library.ModifiableModel libraryModel = library.getModifiableModel(); + VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(libJarLocal); + assertNotNull(jarRoot); + libraryModel.addRoot(jarRoot, OrderRootType.CLASSES); + libraryModel.addRoot(VfsUtilCore.pathToUrl(getRoot() + "/lib/src"), OrderRootType.SOURCES); + libraryModel.commit(); + }); + } + PsiClass classToInline = myJavaFacade.findClass(className, ProjectScope.getAllScope(myProject)); + classToInline = (PsiClass)classToInline.getNavigationElement(); assertEquals(null, InlineToAnonymousClassHandler.getCannotInlineMessage(classToInline)); InlineToAnonymousClassProcessor processor = new InlineToAnonymousClassProcessor(myProject, classToInline,