All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.siyeh.ig.abstraction.MethodOnlyUsedFromInnerClassInspection Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition java-analysis-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2005-2012 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.abstraction;

import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Processor;
import com.intellij.util.Query;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.ClassUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;

public class MethodOnlyUsedFromInnerClassInspection extends BaseInspection {

  @SuppressWarnings({"PublicField"})
  public boolean ignoreMethodsAccessedFromAnonymousClass = false;

  @SuppressWarnings({"PublicField"})
  public boolean ignoreStaticMethodsFromNonStaticInnerClass = false;

  @SuppressWarnings({"PublicField"})
  public boolean onlyReportStaticMethods = false;

  @Override
  @NotNull
  public String getDisplayName() {
    return InspectionGadgetsBundle.message("method.only.used.from.inner.class.display.name");
  }

  @Override
  @NotNull
  protected String buildErrorString(Object... infos) {
    final PsiNamedElement element = (PsiNamedElement)infos[0];
    final String name = element.getName();
    if (infos.length > 1) {
      if (Boolean.TRUE.equals(infos[1])) {
        return InspectionGadgetsBundle.message("method.only.used.from.inner.class.problem.descriptor.anonymous.extending", name);
      }
      return InspectionGadgetsBundle.message("method.only.used.from.inner.class.problem.descriptor.anonymous.implementing", name);
    }
    return InspectionGadgetsBundle.message(
      "method.only.used.from.inner.class.problem.descriptor", name);
  }

  @Override
  @Nullable
  public JComponent createOptionsPanel() {
    final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
    panel.addCheckbox(InspectionGadgetsBundle.message("method.only.used.from.inner.class.ignore.option"),
                      "ignoreMethodsAccessedFromAnonymousClass");
    panel.addCheckbox(InspectionGadgetsBundle.message("ignore.static.methods.accessed.from.a.non.static.inner.class"),
                      "ignoreStaticMethodsFromNonStaticInnerClass");
    panel.addCheckbox(InspectionGadgetsBundle.message("only.report.static.methods"), "onlyReportStaticMethods");
    return panel;
  }

  @Override
  public BaseInspectionVisitor buildVisitor() {
    return new MethodOnlyUsedFromInnerClassVisitor();
  }

  private class MethodOnlyUsedFromInnerClassVisitor
    extends BaseInspectionVisitor {

    @Override
    public void visitMethod(PsiMethod method) {
      super.visitMethod(method);
      if (!method.hasModifierProperty(PsiModifier.PRIVATE) || method.isConstructor()) {
        return;
      }
      if (onlyReportStaticMethods && !method.hasModifierProperty(PsiModifier.STATIC)) {
        return;
      }
      if (method.getNameIdentifier() == null) {
        return;
      }
      final MethodReferenceFinder processor = new MethodReferenceFinder(method);
      if (!processor.isOnlyAccessedFromInnerClass()) {
        return;
      }
      final PsiClass containingClass = processor.getContainingClass();
      if (ignoreStaticMethodsFromNonStaticInnerClass && method.hasModifierProperty(PsiModifier.STATIC)) {
        final PsiElement parent = containingClass.getParent();
        if (parent instanceof PsiClass && !containingClass.hasModifierProperty(PsiModifier.STATIC)) {
          return;
        }
      }
      if (containingClass instanceof PsiAnonymousClass) {
        final PsiClass[] interfaces = containingClass.getInterfaces();
        final PsiClass superClass;
        if (interfaces.length == 1) {
          superClass = interfaces[0];
          registerMethodError(method, superClass, Boolean.valueOf(false));
        }
        else {
          superClass = containingClass.getSuperClass();
          if (superClass == null) {
            return;
          }
          registerMethodError(method, superClass, Boolean.valueOf(true));
        }
      }
      else {
        registerMethodError(method, containingClass);
      }
    }
  }

  private class MethodReferenceFinder implements Processor {

    private final PsiClass methodClass;
    private final PsiMethod method;
    private boolean onlyAccessedFromInnerClass = false;

    private PsiClass cache = null;

    MethodReferenceFinder(@NotNull PsiMethod method) {
      this.method = method;
      methodClass = method.getContainingClass();
    }

    @Override
    public boolean process(PsiReference reference) {
      final PsiElement element = reference.getElement();
      final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
      if (method.equals(containingMethod)) {
        return true;
      }
      final PsiClass containingClass = ClassUtils.getContainingClass(element);
      if (containingClass == null) {
        onlyAccessedFromInnerClass = false;
        return false;
      }
      if (containingClass instanceof PsiAnonymousClass) {
        final PsiAnonymousClass anonymousClass = (PsiAnonymousClass)containingClass;
        final PsiExpressionList argumentList = anonymousClass.getArgumentList();
        if (PsiTreeUtil.isAncestor(argumentList, element, true)) {
          onlyAccessedFromInnerClass = false;
          return false;
        }
        if (ignoreMethodsAccessedFromAnonymousClass) {
          onlyAccessedFromInnerClass = false;
          return false;
        }
      }
      if (cache != null) {
        if (!cache.equals(containingClass)) {
          onlyAccessedFromInnerClass = false;
          return false;
        }
      }
      else if (!PsiTreeUtil.isAncestor(methodClass, containingClass, true)) {
        onlyAccessedFromInnerClass = false;
        return false;
      }
      onlyAccessedFromInnerClass = true;
      cache = containingClass;
      return true;
    }

    public boolean isOnlyAccessedFromInnerClass() {
      final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(method.getProject());
      final ProgressManager progressManager = ProgressManager.getInstance();
      final ProgressIndicator progressIndicator = progressManager.getProgressIndicator();
      final PsiSearchHelper.SearchCostResult searchCost =
        searchHelper.isCheapEnoughToSearch(method.getName(), method.getResolveScope(), null, progressIndicator);
      if (searchCost == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES ||
          searchCost == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) {
        return onlyAccessedFromInnerClass;
      }
      final Query query = ReferencesSearch.search(method);
      query.forEach(this);
      return onlyAccessedFromInnerClass;
    }

    public PsiClass getContainingClass() {
      return cache;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy