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

com.siyeh.ig.abstraction.DeclareCollectionAsInterfaceInspection 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 2003-2014 Dave Griffith, 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.ProblemDescriptor;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.SearchScope;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.CollectionUtils;
import com.siyeh.ig.psiutils.LibraryUtil;
import com.siyeh.ig.psiutils.TypeUtils;
import com.siyeh.ig.psiutils.WeakestTypeFinder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class DeclareCollectionAsInterfaceInspection extends BaseInspection {

  /**
   * @noinspection PublicField
   */
  public boolean ignoreLocalVariables = false;
  /**
   * @noinspection PublicField
   */
  public boolean ignorePrivateMethodsAndFields = false;

  @Override
  @NotNull
  public String getID() {
    return "CollectionDeclaredAsConcreteClass";
  }

  @Override
  @NotNull
  public String getDisplayName() {
    return InspectionGadgetsBundle.message(
      "collection.declared.by.class.display.name");
  }

  @Override
  @NotNull
  public String buildErrorString(Object... infos) {
    final String type = (String)infos[0];
    return InspectionGadgetsBundle.message(
      "collection.declared.by.class.problem.descriptor",
      type);
  }

  @Override
  @Nullable
  public JComponent createOptionsPanel() {
    final MultipleCheckboxOptionsPanel optionsPanel =
      new MultipleCheckboxOptionsPanel(this);
    optionsPanel.addCheckbox(InspectionGadgetsBundle.message(
      "collection.declared.by.class.ignore.locals.option"),
                             "ignoreLocalVariables");
    optionsPanel.addCheckbox(InspectionGadgetsBundle.message(
      "collection.declared.by.class.ignore.private.members.option"),
                             "ignorePrivateMethodsAndFields");
    return optionsPanel;
  }

  @Override
  protected InspectionGadgetsFix buildFix(Object... infos) {
    return new DeclareCollectionAsInterfaceFix((String)infos[0]);
  }

  private static class DeclareCollectionAsInterfaceFix
    extends InspectionGadgetsFix {

    private final String typeString;

    DeclareCollectionAsInterfaceFix(String typeString) {
      this.typeString = typeString;
    }

    @Override
    @NotNull
    public String getName() {
      return InspectionGadgetsBundle.message(
        "declare.collection.as.interface.quickfix", typeString);
    }

    @NotNull
    @Override
    public String getFamilyName() {
      return "Weaken type";
    }

    @Override
    protected void doFix(Project project, ProblemDescriptor descriptor)
      throws IncorrectOperationException {
      final PsiElement element = descriptor.getPsiElement();
      final PsiElement parent = element.getParent();
      if (!(parent instanceof PsiJavaCodeReferenceElement)) {
        return;
      }
      final StringBuilder newElementText = new StringBuilder(typeString);
      final PsiJavaCodeReferenceElement referenceElement =
        (PsiJavaCodeReferenceElement)parent;
      final PsiReferenceParameterList parameterList =
        referenceElement.getParameterList();
      if (parameterList != null) {
        final PsiTypeElement[] typeParameterElements =
          parameterList.getTypeParameterElements();
        if (typeParameterElements.length > 0) {
          newElementText.append('<');
          final PsiTypeElement typeParameterElement1 =
            typeParameterElements[0];
          newElementText.append(typeParameterElement1.getText());
          for (int i = 1; i < typeParameterElements.length; i++) {
            newElementText.append(',');
            final PsiTypeElement typeParameterElement =
              typeParameterElements[i];
            newElementText.append(typeParameterElement.getText());
          }
          newElementText.append('>');
        }
      }
      final PsiElement grandParent = parent.getParent();
      if (!(grandParent instanceof PsiTypeElement)) {
        return;
      }
      final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
      final PsiElementFactory factory = facade.getElementFactory();
      final PsiType type = factory.createTypeFromText(
        newElementText.toString(), element);
      final PsiTypeElement newTypeElement = factory.createTypeElement(
        type);
      final PsiElement insertedElement =
        grandParent.replace(newTypeElement);
      final JavaCodeStyleManager styleManager =
        JavaCodeStyleManager.getInstance(project);
      styleManager.shortenClassReferences(insertedElement);
    }
  }

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

  private class DeclareCollectionAsInterfaceVisitor
    extends BaseInspectionVisitor {

    @Override
    public void visitVariable(@NotNull PsiVariable variable) {
      if (isOnTheFly() && !isCheapEnoughToSearch(variable)) {
        return;
      }
      if (ignoreLocalVariables && variable instanceof PsiLocalVariable) {
        return;
      }
      if (ignorePrivateMethodsAndFields) {
        if (variable instanceof PsiField) {
          if (variable.hasModifierProperty(PsiModifier.PRIVATE)) {
            return;
          }
        }
      }
      if (variable instanceof PsiParameter) {
        final PsiParameter parameter = (PsiParameter)variable;
        final PsiElement scope = parameter.getDeclarationScope();
        if (scope instanceof PsiMethod) {
          if (ignorePrivateMethodsAndFields) {
            final PsiMethod method = (PsiMethod)scope;
            if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
              return;
            }
          }
        }
        else if (ignoreLocalVariables) {
          return;
        }
      }
      final PsiType type = variable.getType();
      if (!CollectionUtils.isConcreteCollectionClass(type) || LibraryUtil.isOverrideOfLibraryMethodParameter(variable)) {
        return;
      }
      final PsiTypeElement typeElement = variable.getTypeElement();
      if (typeElement == null) {
        return;
      }
      final PsiJavaCodeReferenceElement reference =
        typeElement.getInnermostComponentReferenceElement();
      if (reference == null) {
        return;
      }
      final PsiElement nameElement = reference.getReferenceNameElement();
      if (nameElement == null) {
        return;
      }
      final Collection weaklings =
        WeakestTypeFinder.calculateWeakestClassesNecessary(variable,
                                                           false, true);
      if (weaklings.isEmpty()) {
        return;
      }
      final List weaklingList = new ArrayList(weaklings);
      final PsiClassType javaLangObject = TypeUtils.getObjectType(variable);
      final PsiClass objectClass = javaLangObject.resolve();
      weaklingList.remove(objectClass);
      if (weaklingList.isEmpty()) {
        final String typeText = type.getCanonicalText();
        final String interfaceText =
          CollectionUtils.getInterfaceForClass(typeText);
        registerError(nameElement, interfaceText);
      }
      else {
        final PsiClass weakling = weaklingList.get(0);
        final String qualifiedName = weakling.getQualifiedName();
        registerError(nameElement, qualifiedName);
      }
    }

    @Override
    public void visitMethod(@NotNull PsiMethod method) {
      super.visitMethod(method);
      if (ignorePrivateMethodsAndFields &&
          method.hasModifierProperty(PsiModifier.PRIVATE)) {
        return;
      }
      if (isOnTheFly() && !isCheapEnoughToSearch(method)) {
        return;
      }
      final PsiType type = method.getReturnType();
      if (!CollectionUtils.isConcreteCollectionClass(type)) {
        return;
      }
      if (LibraryUtil.isOverrideOfLibraryMethod(method)) {
        return;
      }
      final PsiTypeElement typeElement = method.getReturnTypeElement();
      if (typeElement == null) {
        return;
      }
      final PsiJavaCodeReferenceElement referenceElement =
        typeElement.getInnermostComponentReferenceElement();
      if (referenceElement == null) {
        return;
      }
      final PsiElement nameElement =
        referenceElement.getReferenceNameElement();
      if (nameElement == null) {
        return;
      }
      final Collection weaklings =
        WeakestTypeFinder.calculateWeakestClassesNecessary(method,
                                                           false, true);
      if (weaklings.isEmpty()) {
        return;
      }
      final List weaklingList = new ArrayList(weaklings);
      final PsiClassType javaLangObject = TypeUtils.getObjectType(method);
      final PsiClass objectClass = javaLangObject.resolve();
      weaklingList.remove(objectClass);
      if (weaklingList.isEmpty()) {
        final String typeText = type.getCanonicalText();
        final String interfaceText = CollectionUtils.getInterfaceForClass(typeText);
        if (interfaceText == null) {
          return;
        }
        registerError(nameElement, interfaceText);
      }
      else {
        final PsiClass weakling = weaklingList.get(0);
        registerError(nameElement, weakling.getQualifiedName());
      }
    }

    private boolean isCheapEnoughToSearch(PsiNamedElement element) {
      final String name = element.getName();
      if (name == null) {
        return false;
      }
      final ProgressManager progressManager =
        ProgressManager.getInstance();
      final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(element.getProject());
      final SearchScope useScope = element.getUseScope();
      if (useScope instanceof GlobalSearchScope) {
        return searchHelper.isCheapEnoughToSearch(name, (GlobalSearchScope)useScope, null, progressManager.getProgressIndicator()) != PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES;
      }
      return true;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy