| /* |
| * Copyright 2010-2014 Bas Leijdekkers |
| * |
| * 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.siyeh.ig.style; |
| |
| import com.intellij.codeInspection.CleanupLocalInspectionTool; |
| import com.intellij.codeInspection.ProblemDescriptor; |
| import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.util.IncorrectOperationException; |
| import com.siyeh.InspectionGadgetsBundle; |
| import com.siyeh.ig.InspectionGadgetsFix; |
| import com.siyeh.ig.psiutils.ClassUtils; |
| import com.siyeh.ig.psiutils.HighlightUtils; |
| import com.siyeh.ig.psiutils.ImportUtils; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.util.*; |
| |
| public class UnqualifiedInnerClassAccessInspection extends UnqualifiedInnerClassAccessInspectionBase implements CleanupLocalInspectionTool{ |
| |
| @Override |
| public JComponent createOptionsPanel() { |
| return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("unqualified.inner.class.access.option"), |
| this, "ignoreReferencesToLocalInnerClasses"); |
| } |
| |
| @Override |
| protected InspectionGadgetsFix buildFix(Object... infos) { |
| return new UnqualifiedInnerClassAccessFix(); |
| } |
| |
| private static class UnqualifiedInnerClassAccessFix extends InspectionGadgetsFix { |
| @Override |
| @NotNull |
| public String getFamilyName() { |
| return getName(); |
| } |
| |
| @Override |
| @NotNull |
| public String getName() { |
| return InspectionGadgetsBundle.message( |
| "unqualified.inner.class.access.quickfix"); |
| } |
| |
| @Override |
| protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { |
| final PsiElement element = descriptor.getPsiElement(); |
| if (!(element instanceof PsiJavaCodeReferenceElement)) { |
| return; |
| } |
| final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)element; |
| final PsiElement target = referenceElement.resolve(); |
| if (!(target instanceof PsiClass)) { |
| return; |
| } |
| final PsiClass innerClass = (PsiClass)target; |
| final PsiClass containingClass = innerClass.getContainingClass(); |
| if (containingClass == null) { |
| return; |
| } |
| final String qualifiedName = containingClass.getQualifiedName(); |
| if (qualifiedName == null) { |
| return; |
| } |
| final PsiFile containingFile = referenceElement.getContainingFile(); |
| if (!(containingFile instanceof PsiJavaFile)) { |
| return; |
| } |
| final PsiJavaFile javaFile = (PsiJavaFile)containingFile; |
| final String innerClassName = innerClass.getQualifiedName(); |
| if (innerClassName == null) { |
| return; |
| } |
| final PsiImportList importList = javaFile.getImportList(); |
| if (importList == null) { |
| return; |
| } |
| final PsiImportStatement[] importStatements = importList.getImportStatements(); |
| final int importStatementsLength = importStatements.length; |
| boolean onDemand = false; |
| PsiImportStatement referenceImportStatement = null; |
| for (int i = importStatementsLength - 1; i >= 0; i--) { |
| final PsiImportStatement importStatement = importStatements[i]; |
| final String importString = importStatement.getQualifiedName(); |
| if (importStatement.isOnDemand()) { |
| if (qualifiedName.equals(importString)) { |
| referenceImportStatement = importStatement; |
| onDemand = true; |
| break; |
| } |
| } |
| else { |
| if (innerClassName.equals(importString)) { |
| referenceImportStatement = importStatement; |
| break; |
| } |
| } |
| } |
| final ReferenceCollector referenceCollector; |
| if (onDemand) { |
| referenceCollector = new ReferenceCollector(qualifiedName, true); |
| } |
| else { |
| referenceCollector = new ReferenceCollector(innerClassName, false); |
| } |
| final PsiClass[] classes = javaFile.getClasses(); |
| for (PsiClass psiClass : classes) { |
| psiClass.accept(referenceCollector); |
| } |
| final Collection<PsiJavaCodeReferenceElement> references = referenceCollector.getReferences(); |
| final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project); |
| final List<SmartPsiElementPointer> pointers = new ArrayList(); |
| for (PsiJavaCodeReferenceElement reference : references) { |
| final SmartPsiElementPointer<PsiJavaCodeReferenceElement> pointer = pointerManager.createSmartPsiElementPointer(reference); |
| pointers.add(pointer); |
| } |
| if (referenceImportStatement != null) { |
| referenceImportStatement.delete(); |
| } |
| final PsiClass outerClass = ClassUtils.getOutermostContainingClass(containingClass); |
| ImportUtils.addImportIfNeeded(outerClass, referenceElement); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); |
| final Document document = documentManager.getDocument(containingFile); |
| if (document == null) { |
| return; |
| } |
| documentManager.doPostponedOperationsAndUnblockDocument(document); |
| final String text = buildNewText(javaFile, references, containingClass, new StringBuilder()).toString(); |
| document.replaceString(0, document.getTextLength(), text); |
| documentManager.commitDocument(document); |
| if (pointers.size() > 1) { |
| final List<PsiElement> elements = new ArrayList(); |
| for (SmartPsiElementPointer pointer : pointers) { |
| elements.add(pointer.getElement()); |
| } |
| HighlightUtils.highlightElements(elements); |
| } |
| } |
| |
| private static StringBuilder buildNewText(PsiElement element, Collection<PsiJavaCodeReferenceElement> references, |
| PsiClass aClass, StringBuilder out) { |
| if (element == null) { |
| return out; |
| } |
| //noinspection SuspiciousMethodCalls |
| if (references.contains(element)) { |
| final String shortClassName = getShortClassName(aClass, new StringBuilder()).toString(); |
| if (isReferenceToTargetClass(shortClassName, aClass, element)) { |
| out.append(shortClassName); |
| } |
| else { |
| out.append(aClass.getQualifiedName()); |
| } |
| out.append('.'); |
| return out.append(element.getText()); |
| } |
| final PsiElement[] children = element.getChildren(); |
| if (children.length == 0) { |
| return out.append(element.getText()); |
| } |
| for (PsiElement child : children) { |
| buildNewText(child, references, aClass, out); |
| } |
| return out; |
| } |
| |
| private static StringBuilder getShortClassName(@NotNull PsiClass aClass, @NotNull StringBuilder builder) { |
| final PsiClass containingClass = aClass.getContainingClass(); |
| if (containingClass != null) { |
| getShortClassName(containingClass, builder); |
| builder.append('.'); |
| } |
| builder.append(aClass.getName()); |
| return builder; |
| } |
| |
| private static boolean isReferenceToTargetClass(String referenceText, PsiClass targetClass, PsiElement context) { |
| final PsiManager manager = targetClass.getManager(); |
| final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); |
| final PsiResolveHelper resolveHelper = facade.getResolveHelper(); |
| final PsiClass referencedClass = resolveHelper.resolveReferencedClass(referenceText, context); |
| if (referencedClass == null) { |
| return true; |
| } |
| return manager.areElementsEquivalent(targetClass, referencedClass); |
| } |
| } |
| |
| private static class ReferenceCollector extends JavaRecursiveElementVisitor { |
| |
| private final String name; |
| private final boolean onDemand; |
| private final Set<PsiJavaCodeReferenceElement> references = new HashSet<PsiJavaCodeReferenceElement>(); |
| |
| ReferenceCollector(String name, boolean onDemand) { |
| this.name = name; |
| this.onDemand = onDemand; |
| } |
| |
| @Override |
| public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { |
| super.visitReferenceElement(reference); |
| if (reference.isQualified()) { |
| return; |
| } |
| final PsiElement target = reference.resolve(); |
| if (!(target instanceof PsiClass)) { |
| return; |
| } |
| final PsiClass aClass = (PsiClass)target; |
| if (!onDemand) { |
| final String qualifiedName = aClass.getQualifiedName(); |
| if (name.equals(qualifiedName)) { |
| references.add(reference); |
| } |
| return; |
| } |
| final PsiClass containingClass = aClass.getContainingClass(); |
| if (containingClass == null) { |
| return; |
| } |
| final String qualifiedName = containingClass.getQualifiedName(); |
| if (name.equals(qualifiedName)) { |
| references.add(reference); |
| } |
| } |
| |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| visitReferenceElement(expression); |
| } |
| |
| public Collection<PsiJavaCodeReferenceElement> getReferences() { |
| return references; |
| } |
| } |
| } |