blob: 36344aa007858d7da604ca623bb6758cf4460d6a [file] [log] [blame]
/*
* Copyright 2000-2014 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 com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author ven
*/
public class AddOnDemandStaticImportAction extends BaseElementAtCaretIntentionAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction");
@Override
@NotNull
public String getFamilyName() {
return CodeInsightBundle.message("intention.add.on.demand.static.import.family");
}
/**
* Allows to check if static import may be performed for the given element.
*
* @param element element to check
* @return target class that may be statically imported if any; <code>null</code> otherwise
*/
@Nullable
public static PsiClass getClassToPerformStaticImport(@NotNull PsiElement element) {
if (!PsiUtil.isLanguageLevel5OrHigher(element)) return null;
if (!(element instanceof PsiIdentifier) || !(element.getParent() instanceof PsiJavaCodeReferenceElement)) {
return null;
}
PsiJavaCodeReferenceElement refExpr = (PsiJavaCodeReferenceElement)element.getParent();
if (refExpr instanceof PsiMethodReferenceExpression) return null;
final PsiElement gParent = refExpr.getParent();
if (gParent instanceof PsiMethodReferenceExpression) return null;
if (!(gParent instanceof PsiJavaCodeReferenceElement) ||
isParameterizedReference((PsiJavaCodeReferenceElement)gParent)) return null;
PsiElement resolved = refExpr.resolve();
if (!(resolved instanceof PsiClass)) {
return null;
}
PsiClass psiClass = (PsiClass)resolved;
final PsiElement ggParent = gParent.getParent();
if (ggParent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression call = (PsiMethodCallExpression)ggParent.copy();
final PsiElement qualifier = call.getMethodExpression().getQualifier();
if (qualifier == null) return null;
qualifier.delete();
final PsiMethod method = call.resolveMethod();
if (method != null && method.getContainingClass() != psiClass) return null;
}
else {
final PsiJavaCodeReferenceElement copy = (PsiJavaCodeReferenceElement)gParent.copy();
final PsiElement qualifier = copy.getQualifier();
if (qualifier == null) return null;
qualifier.delete();
final PsiElement target = copy.resolve();
if (target != null && PsiTreeUtil.getParentOfType(target, PsiClass.class) != psiClass) return null;
}
if (Comparing.strEqual(psiClass.getName(), psiClass.getQualifiedName()) || psiClass.hasModifierProperty(PsiModifier.PRIVATE)) return null;
PsiFile file = refExpr.getContainingFile();
if (!(file instanceof PsiJavaFile)) return null;
PsiImportList importList = ((PsiJavaFile)file).getImportList();
if (importList == null) return null;
return psiClass;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
PsiClass classToImport = getClassToPerformStaticImport(element);
if (classToImport != null) {
String text = CodeInsightBundle.message("intention.add.on.demand.static.import.text", classToImport.getQualifiedName());
setText(text);
}
return classToImport != null;
}
public static void invoke(final Project project, PsiFile file, final Editor editor, PsiElement element) {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
final PsiJavaCodeReferenceElement refExpr = (PsiJavaCodeReferenceElement)element.getParent();
final PsiClass aClass = (PsiClass)refExpr.resolve();
if (aClass == null) {
return;
}
final PsiClass containingClass = PsiUtil.getTopLevelClass(refExpr);
if (aClass != containingClass) {
PsiImportList importList = ((PsiJavaFile)file).getImportList();
if (importList == null) {
return;
}
boolean alreadyImported = false;
for (PsiImportStaticStatement statement : importList.getImportStaticStatements()) {
if (!statement.isOnDemand()) continue;
PsiClass staticResolve = statement.resolveTargetClass();
if (aClass == staticResolve) {
alreadyImported = true;
break;
}
}
if (!alreadyImported) {
PsiImportStaticStatement importStaticStatement =
JavaPsiFacade.getInstance(file.getProject()).getElementFactory().createImportStaticStatement(aClass, "*");
importList.add(importStaticStatement);
}
}
List<PsiFile> roots = file.getViewProvider().getAllFiles();
for (final PsiFile root : roots) {
PsiElement copy = root.copy();
final PsiManager manager = root.getManager();
final TIntArrayList expressionToDequalifyOffsets = new TIntArrayList();
copy.accept(new JavaRecursiveElementWalkingVisitor() {
int delta = 0;
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement expression) {
if (isParameterizedReference(expression)) return;
PsiElement qualifierExpression = expression.getQualifier();
if (qualifierExpression instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifierExpression).isReferenceTo(aClass)) {
try {
PsiElement resolved = expression.resolve();
int end = expression.getTextRange().getEndOffset();
qualifierExpression.delete();
delta += end - expression.getTextRange().getEndOffset();
PsiElement after = expression.resolve();
if (manager.areElementsEquivalent(after, resolved)) {
expressionToDequalifyOffsets.add(expression.getTextRange().getStartOffset() + delta);
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
super.visitElement(expression);
}
});
expressionToDequalifyOffsets.forEachDescending(new TIntProcedure() {
@Override
public boolean execute(int offset) {
PsiJavaCodeReferenceElement expression = PsiTreeUtil.findElementOfClassAtOffset(root, offset, PsiJavaCodeReferenceElement.class, false);
if (expression == null) {
return false;
}
PsiElement qualifierExpression = expression.getQualifier();
if (qualifierExpression instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifierExpression).isReferenceTo(aClass)) {
qualifierExpression.delete();
HighlightManager.getInstance(project)
.addRangeHighlight(editor, expression.getTextRange().getStartOffset(), expression.getTextRange().getEndOffset(),
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES),
false, null);
}
return true;
}
});
}
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
invoke(project, element.getContainingFile(), editor, element);
}
private static boolean isParameterizedReference(final PsiJavaCodeReferenceElement expression) {
if (expression.getParameterList() == null) {
return false;
}
PsiReferenceParameterList parameterList = expression.getParameterList();
return parameterList != null && parameterList.getFirstChild() != null;
}
}