refactorings optimization: optimize imports extracted outof write action

This commit is contained in:
anna
2010-06-21 17:28:02 +04:00
parent 6288897837
commit 62cc6669be
11 changed files with 235 additions and 107 deletions
@@ -39,10 +39,17 @@ public class ChangeContextUtil {
private ChangeContextUtil() {}
public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses) {
encodeContextInfo(scope, scope, includeRefClasses);
encodeContextInfo(scope, scope, includeRefClasses, true);
}
private static void encodeContextInfo(PsiElement scope, PsiElement topLevelScope, boolean includeRefClasses) {
public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses, boolean canChangeQualifier) {
encodeContextInfo(scope, scope, includeRefClasses, true);
}
private static void encodeContextInfo(PsiElement scope,
PsiElement topLevelScope,
boolean includeRefClasses,
boolean canChangeQualifier) {
if (scope instanceof PsiThisExpression){
scope.putCopyableUserData(ENCODED_KEY, "");
@@ -84,7 +91,7 @@ public class ChangeContextUtil {
}
}
}
else{
else if (canChangeQualifier) {
refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, canRemoveQualifier(refExpr) ? Boolean.TRUE : Boolean.FALSE);
}
}
@@ -101,7 +108,7 @@ public class ChangeContextUtil {
}
for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
encodeContextInfo(child, topLevelScope, includeRefClasses);
encodeContextInfo(child, topLevelScope, includeRefClasses, canChangeQualifier);
}
}
@@ -109,10 +109,26 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
}
public void removeRedundantImports(@NotNull final PsiJavaFile file) throws IncorrectOperationException {
final Collection<PsiImportStatementBase> redundants = findRedundantImports(file);
if (redundants == null) return;
for (final PsiImportStatementBase importStatement : redundants) {
final PsiJavaCodeReferenceElement ref = importStatement.getImportReference();
//Do not remove non-resolving refs
if (ref == null || ref.resolve() == null) {
continue;
}
importStatement.delete();
}
}
@Nullable
public Collection<PsiImportStatementBase> findRedundantImports(final PsiJavaFile file) {
final PsiImportList importList = file.getImportList();
if (importList == null) return;
if (importList == null) return null;
final PsiImportStatementBase[] imports = importList.getAllImportStatements();
if( imports.length == 0 ) return;
if( imports.length == 0 ) return null;
Set<PsiImportStatementBase> allImports = new THashSet<PsiImportStatementBase>(Arrays.asList(imports));
final Collection<PsiImportStatementBase> redundants;
@@ -158,16 +174,7 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
});
}
}
for (final PsiImportStatementBase importStatement : redundants) {
final PsiJavaCodeReferenceElement ref = importStatement.getImportReference();
//Do not remove non-resolving refs
if (ref == null || ref.resolve() == null) {
continue;
}
importStatement.delete();
}
return redundants;
}
public int findEntryIndex(@NotNull PsiImportStatementBase statement) {
@@ -15,16 +15,19 @@
*/
package com.intellij.refactoring;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.HashSet;
import com.intellij.util.IncorrectOperationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.containers.HashSet;
import java.util.Collection;
import java.util.Set;
public class OptimizeImportsRefactoringHelper implements RefactoringHelper<Set<PsiJavaFile>> {
@@ -45,16 +48,54 @@ public class OptimizeImportsRefactoringHelper implements RefactoringHelper<Set<P
}
public void performOperation(final Project project, final Set<PsiJavaFile> javaFiles) {
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
for (PsiJavaFile file : javaFiles) {
try {
if (file.isValid() && file.getVirtualFile() != null) {
styleManager.removeRedundantImports(file);
final Set<SmartPsiElementPointer<PsiImportStatementBase>> redundants = new HashSet<SmartPsiElementPointer<PsiImportStatementBase>>();
final Runnable findRedundantImports = new Runnable() {
public void run() {
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project);
PsiDocumentManager.getInstance(project).commitAllDocuments();
for (PsiJavaFile file : javaFiles) {
if (file.isValid()) {
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
if (progressIndicator != null) {
progressIndicator.setText2(virtualFile.getPresentableUrl());
}
final Collection<PsiImportStatementBase> perFile = styleManager.findRedundantImports(file);
if (perFile != null) {
for (PsiImportStatementBase redundant : perFile) {
redundants.add(pointerManager.createSmartPsiElementPointer(redundant));
}
}
}
}
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
};
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(findRedundantImports, "Removing redundant imports", false, project)) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
try {
for (final SmartPsiElementPointer<PsiImportStatementBase> pointer : redundants) {
final PsiImportStatementBase importStatement = pointer.getElement();
if (importStatement != null && importStatement.isValid()) {
final PsiJavaCodeReferenceElement ref = importStatement.getImportReference();
//Do not remove non-resolving refs
if (ref == null || ref.resolve() == null) {
continue;
}
importStatement.delete();
}
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
});
}
}
@@ -70,7 +70,7 @@ public class RenameJavaClassProcessor extends RenamePsiElementProcessor {
}
// do actual rename
ChangeContextUtil.encodeContextInfo(aClass, true);
ChangeContextUtil.encodeContextInfo(aClass, true, false);
aClass.setName(newName);
for (UsageInfo usage : usages) {
@@ -10,4 +10,5 @@ class Test {
}
}.InnerClass(i, j);
}
}
@@ -7,4 +7,5 @@ class Test {
}
}.InnerClass(i);
}
}
@@ -6,6 +6,7 @@ package com.intellij.refactoring;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.refactoring.extractMethodObject.ExtractMethodObjectProcessor;
@@ -41,6 +42,7 @@ public class ExtractMethodObjectTest extends LightCodeInsightTestCase {
processor.moveUsedMethodsToInner();
}
DuplicatesImpl.processDuplicates(extractProcessor, getProject(), getEditor());
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
processor.getMethod().delete();
checkResultByFile("/refactoring/extractMethodObject/" + testName + ".java" + ".after");
}
@@ -27,6 +27,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public abstract class JavaCodeStyleManager {
public static JavaCodeStyleManager getInstance(Project project) {
return ServiceManager.getService(project, JavaCodeStyleManager.class);
@@ -167,4 +169,7 @@ public abstract class JavaCodeStyleManager {
* the file is read-only).
*/
public abstract void removeRedundantImports(@NotNull PsiJavaFile file) throws IncorrectOperationException;
@Nullable
public abstract Collection<PsiImportStatementBase> findRedundantImports(PsiJavaFile file);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -147,22 +147,18 @@ public abstract class BaseRefactoringProcessor {
final Runnable findUsagesRunnable = new Runnable() {
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
try {
refUsages.set(findUsages());
}
catch (UnknownReferenceTypeException e) {
refErrorLanguage.set(e.getElementLanguage());
}
catch (ProcessCanceledException e) {
refProcessCanceled.set(Boolean.TRUE);
}
catch (IndexNotReadyException e) {
dumbModeOccured.set(Boolean.TRUE);
}
}
});
try {
refUsages.set(findUsages());
}
catch (UnknownReferenceTypeException e) {
refErrorLanguage.set(e.getElementLanguage());
}
catch (ProcessCanceledException e) {
refProcessCanceled.set(Boolean.TRUE);
}
catch (IndexNotReadyException e) {
dumbModeOccured.set(Boolean.TRUE);
}
}
};
@@ -259,13 +255,9 @@ public abstract class BaseRefactoringProcessor {
protected void execute(final UsageInfo[] usages) {
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
Collection<UsageInfo> usageInfos = new HashSet<UsageInfo>(Arrays.asList(usages));
doRefactoring(usageInfos);
if (isGlobalUndoAction()) CommandProcessor.getInstance().markCurrentCommandAsGlobal(myProject);
}
});
Collection<UsageInfo> usageInfos = new HashSet<UsageInfo>(Arrays.asList(usages));
doRefactoring(usageInfos);
if (isGlobalUndoAction()) CommandProcessor.getInstance().markCurrentCommandAsGlobal(myProject);
}
}, getCommandName(), null, getUndoConfirmationPolicy());
}
@@ -324,14 +316,10 @@ public abstract class BaseRefactoringProcessor {
final Runnable refactoringRunnable = new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
Set<UsageInfo> usagesToRefactor = getUsageInfosToRefactor(usageView);
if (ensureElementsWritable(usagesToRefactor.toArray(new UsageInfo[usagesToRefactor.size()]), viewDescriptor)) {
doRefactoring(usagesToRefactor);
}
}
});
Set<UsageInfo> usagesToRefactor = getUsageInfosToRefactor(usageView);
if (ensureElementsWritable(usagesToRefactor.toArray(new UsageInfo[usagesToRefactor.size()]), viewDescriptor)) {
doRefactoring(usagesToRefactor);
}
}
};
@@ -354,9 +342,7 @@ public abstract class BaseRefactoringProcessor {
}
private void doRefactoring(@NotNull Collection<UsageInfo> usageInfoSet) {
ApplicationManager.getApplication().assertWriteAccessAllowed();
for (Iterator<UsageInfo> iterator = usageInfoSet.iterator(); iterator.hasNext();) {
for (Iterator<UsageInfo> iterator = usageInfoSet.iterator(); iterator.hasNext();) {
UsageInfo usageInfo = iterator.next();
final PsiElement element = usageInfo.getElement();
if (element == null || !element.isWritable()) {
@@ -371,17 +357,34 @@ public abstract class BaseRefactoringProcessor {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
RefactoringListenerManagerImpl listenerManager = (RefactoringListenerManagerImpl)RefactoringListenerManager.getInstance(myProject);
myTransaction = listenerManager.startTransaction();
Map<RefactoringHelper, Object> preparedData = new HashMap<RefactoringHelper, Object>();
for(RefactoringHelper helper: Extensions.getExtensions(RefactoringHelper.EP_NAME)) {
preparedData.put(helper, helper.prepareOperation(writableUsageInfos));
}
performRefactoring(writableUsageInfos);
final Map<RefactoringHelper, Object> preparedData = new HashMap<RefactoringHelper, Object>();
final Runnable prepareHelpersRunnable = new Runnable() {
public void run() {
for (RefactoringHelper helper : Extensions.getExtensions(RefactoringHelper.EP_NAME)) {
preparedData.put(helper, helper.prepareOperation(writableUsageInfos));
}
}
};
ProgressManager.getInstance().runProcessWithProgressSynchronously(prepareHelpersRunnable, "Prepare ...", false, myProject);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ApplicationManager.getApplication().assertWriteAccessAllowed();
performRefactoring(writableUsageInfos);
}
});
for(Map.Entry<RefactoringHelper, Object> e: preparedData.entrySet()) {
//noinspection unchecked
e.getKey().performOperation(myProject, e.getValue());
}
myTransaction.commit();
performPsiSpoilingRefactoring();
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
performPsiSpoilingRefactoring();
}
});
}
finally {
action.finish();
@@ -64,6 +64,10 @@ public class GroovyImportOptimizer implements ImportOptimizer {
new MyProcessor(file, true).run();
}
public List<GrImportStatement> findUnusedImports(GroovyFile file, Set<GrImportStatement> usedImports) {
return new MyProcessor(file, true).findUnusedImports(new HashSet<String>(), new HashSet<String>(),usedImports, new HashSet<String>());
}
public boolean supports(PsiFile file) {
return file instanceof GroovyFile;
}
@@ -86,6 +90,46 @@ public class GroovyImportOptimizer implements ImportOptimizer {
final Set<String> staticallyImportedMembers = new LinkedHashSet<String>();
final Set<GrImportStatement> usedImports = new HashSet<GrImportStatement>();
final Set<String> implicitlyImported = new LinkedHashSet<String>();
final List<GrImportStatement> oldImports =
findUnusedImports(importedClasses, staticallyImportedMembers, usedImports, implicitlyImported);
if (myRemoveUnusedOnly) {
for (GrImportStatement oldImport : oldImports) {
if (!usedImports.contains(oldImport)) {
myFile.removeImport(oldImport);
}
}
return;
}
// Getting aliased imports
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myFile.getProject());
ArrayList<GrImportStatement> aliased = new ArrayList<GrImportStatement>();
for (GrImportStatement oldImport : oldImports) {
if (oldImport.isAliasedImport() && usedImports.contains(oldImport)) {
aliased.add(factory.createImportStatementFromText(oldImport.getText()));
}
}
// Add new import statements
GrImportStatement[] newImports = prepare(importedClasses, staticallyImportedMembers, implicitlyImported);
for (GrImportStatement aliasedImport : aliased) {
myFile.addImport(aliasedImport);
}
for (GrImportStatement newImport : newImports) {
myFile.addImport(newImport);
}
myFile.removeImport(myFile.addImport(factory.createImportStatementFromText("import xxxx"))); //to remove trailing whitespaces
for (GrImportStatement importStatement : oldImports) {
myFile.removeImport(importStatement);
}
}
public List<GrImportStatement> findUnusedImports(final Set<String> importedClasses,
final Set<String> staticallyImportedMembers,
final Set<GrImportStatement> usedImports,
final Set<String> implicitlyImported) {
myFile.accept(new GroovyRecursiveElementVisitor() {
public void visitCodeReferenceElement(GrCodeReferenceElement refElement) {
visitRefElement(refElement);
@@ -163,38 +207,7 @@ public class GroovyImportOptimizer implements ImportOptimizer {
oldImports.add(statement);
}
}
if (myRemoveUnusedOnly) {
for (GrImportStatement oldImport : oldImports) {
if (!usedImports.contains(oldImport)) {
myFile.removeImport(oldImport);
}
}
return;
}
// Getting aliased imports
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myFile.getProject());
ArrayList<GrImportStatement> aliased = new ArrayList<GrImportStatement>();
for (GrImportStatement oldImport : oldImports) {
if (oldImport.isAliasedImport() && usedImports.contains(oldImport)) {
aliased.add(factory.createImportStatementFromText(oldImport.getText()));
}
}
// Add new import statements
GrImportStatement[] newImports = prepare(importedClasses, staticallyImportedMembers, implicitlyImported);
for (GrImportStatement aliasedImport : aliased) {
myFile.addImport(aliasedImport);
}
for (GrImportStatement newImport : newImports) {
myFile.addImport(newImport);
}
myFile.removeImport(myFile.addImport(factory.createImportStatementFromText("import xxxx"))); //to remove trailing whitespaces
for (GrImportStatement importStatement : oldImports) {
myFile.removeImport(importStatement);
}
return oldImports;
}
@Nullable private String getTargetQualifiedName(PsiElement element) {
@@ -16,7 +16,13 @@
package org.jetbrains.plugins.groovy.refactoring;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.RefactoringHelper;
@@ -24,7 +30,11 @@ import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.plugins.groovy.lang.editor.GroovyImportOptimizer;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@@ -45,11 +55,49 @@ public class GroovyImportOptimizerRefactoringHelper implements RefactoringHelper
return files;
}
public void performOperation(Project project, Set<GroovyFile> files) {
public void performOperation(final Project project, final Set<GroovyFile> files) {
final GroovyImportOptimizer optimizer = new GroovyImportOptimizer();
for (GroovyFile file : files) {
optimizer.removeUnusedImports(file);
final ProgressManager progressManager = ProgressManager.getInstance();
final Map<GroovyFile, Pair<List<GrImportStatement>, Set<GrImportStatement>>> redundants =
new HashMap<GroovyFile, Pair<List<GrImportStatement>, Set<GrImportStatement>>>();
final Runnable findUnusedImports = new Runnable() {
public void run() {
final ProgressIndicator progressIndicator = progressManager.getProgressIndicator();
for (final GroovyFile file : files) {
final VirtualFile virtualFile = file.getVirtualFile();
if (!ProjectRootManager.getInstance(project).getFileIndex().isInSource(virtualFile)) {
continue;
}
if (progressIndicator != null) {
progressIndicator.setText2(virtualFile.getPresentableUrl());
}
final Set<GrImportStatement> usedImports = new HashSet<GrImportStatement>();
final List<GrImportStatement> perFile = optimizer.findUnusedImports(file, usedImports);
if (perFile != null) {
redundants.put(file, Pair.create(perFile, usedImports));
}
}
}
};
if (!progressManager.runProcessWithProgressSynchronously(findUnusedImports, "Optimizing imports (Groovy) ... ", false, project)) {
return;
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
for (GroovyFile groovyFile : redundants.keySet()) {
final Pair<List<GrImportStatement>, Set<GrImportStatement>> pair = redundants.get(groovyFile);
final List<GrImportStatement> redundantPerFile = pair.getFirst();
final Set<GrImportStatement> usedInFile = pair.getSecond();
for (GrImportStatement importStatement : redundantPerFile) {
if (!usedInFile.contains(importStatement)) {
groovyFile.removeImport(importStatement);
}
}
}
}
});
}
}