com.intellij.codeInspection.ex.GlobalJavaInspectionContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.codeInspection.ex.GlobalJavaInspectionContextImpl.java

Source

/*
 * Copyright 2000-2009 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.
 */

/*
 * User: anna
 * Date: 19-Dec-2007
 */
package com.intellij.codeInspection.ex;

import gnu.trove.THashMap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.jetbrains.annotations.NotNull;
import com.intellij.CommonBundle;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.GlobalInspectionContext;
import com.intellij.codeInspection.GlobalJavaInspectionContext;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.reference.RefClass;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefField;
import com.intellij.codeInspection.reference.RefJavaManager;
import com.intellij.codeInspection.reference.RefManager;
import com.intellij.codeInspection.reference.RefMethod;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.SdkOrderEntry;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.search.PsiElementProcessorAdapter;
import com.intellij.psi.search.PsiReferenceProcessor;
import com.intellij.psi.search.PsiReferenceProcessorAdapter;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Processor;

public class GlobalJavaInspectionContextImpl extends GlobalJavaInspectionContext {
    private static final Logger LOG = Logger.getInstance("#" + GlobalJavaInspectionContextImpl.class.getName());

    private THashMap<SmartPsiElementPointer, List<DerivedMethodsProcessor>> myDerivedMethodsRequests;
    private THashMap<SmartPsiElementPointer, List<DerivedClassesProcessor>> myDerivedClassesRequests;
    private THashMap<SmartPsiElementPointer, List<UsagesProcessor>> myMethodUsagesRequests;
    private THashMap<SmartPsiElementPointer, List<UsagesProcessor>> myFieldUsagesRequests;
    private THashMap<SmartPsiElementPointer, List<UsagesProcessor>> myClassUsagesRequests;

    @Override
    public void enqueueClassUsagesProcessor(RefClass refClass, UsagesProcessor p) {
        if (myClassUsagesRequests == null)
            myClassUsagesRequests = new THashMap<SmartPsiElementPointer, List<UsagesProcessor>>();
        enqueueRequestImpl(refClass, myClassUsagesRequests, p);

    }

    @Override
    public void enqueueDerivedClassesProcessor(RefClass refClass, DerivedClassesProcessor p) {
        if (myDerivedClassesRequests == null)
            myDerivedClassesRequests = new THashMap<SmartPsiElementPointer, List<DerivedClassesProcessor>>();
        enqueueRequestImpl(refClass, myDerivedClassesRequests, p);
    }

    @Override
    public void enqueueDerivedMethodsProcessor(RefMethod refMethod, DerivedMethodsProcessor p) {
        if (refMethod.isConstructor() || refMethod.isStatic())
            return;
        if (myDerivedMethodsRequests == null)
            myDerivedMethodsRequests = new THashMap<SmartPsiElementPointer, List<DerivedMethodsProcessor>>();
        enqueueRequestImpl(refMethod, myDerivedMethodsRequests, p);
    }

    @Override
    public void enqueueFieldUsagesProcessor(RefField refField, UsagesProcessor p) {
        if (myFieldUsagesRequests == null)
            myFieldUsagesRequests = new THashMap<SmartPsiElementPointer, List<UsagesProcessor>>();
        enqueueRequestImpl(refField, myFieldUsagesRequests, p);
    }

    @Override
    public void enqueueMethodUsagesProcessor(RefMethod refMethod, UsagesProcessor p) {
        if (myMethodUsagesRequests == null)
            myMethodUsagesRequests = new THashMap<SmartPsiElementPointer, List<UsagesProcessor>>();
        enqueueRequestImpl(refMethod, myMethodUsagesRequests, p);
    }

    @Override
    public EntryPointsManager getEntryPointsManager(final RefManager manager) {
        return manager.getExtension(RefJavaManager.MANAGER).getEntryPointsManager();
    }

    @SuppressWarnings({ "UseOfSystemOutOrSystemErr" })
    public static boolean isInspectionsEnabled(final boolean online, @NotNull Project project) {
        final Module[] modules = ModuleManager.getInstance(project).getModules();
        if (online) {
            if (modules.length == 0) {
                Messages.showMessageDialog(project,
                        InspectionsBundle.message("inspection.no.modules.error.message"),
                        CommonBundle.message("title.error"), Messages.getErrorIcon());
                return false;
            }
            while (isBadSdk(project, modules)) {
                Messages.showMessageDialog(project, InspectionsBundle.message("inspection.no.jdk.error.message"),
                        CommonBundle.message("title.error"), Messages.getErrorIcon());
                final Sdk projectJdk = ProjectSettingsService.getInstance(project).chooseAndSetSdk();
                if (projectJdk == null)
                    return false;
            }
        } else {
            if (modules.length == 0) {
                System.err.println(InspectionsBundle.message("inspection.no.modules.error.message"));
                return false;
            }
            if (isBadSdk(project, modules)) {
                System.err.println(InspectionsBundle.message("inspection.no.jdk.error.message"));
                System.err.println(InspectionsBundle.message("offline.inspections.jdk.not.found",
                        ""/*ProjectRootManager.getInstance(project).getProjectSdkName()*/));
                return false;
            }
            for (Module module : modules) {
                final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
                final OrderEntry[] entries = rootManager.getOrderEntries();
                for (OrderEntry entry : entries) {
                    if (entry instanceof SdkOrderEntry) {
                        if (/*!ModuleType.get(module).isValidSdk(module, null)*/Boolean.FALSE) {
                            System.err.println(InspectionsBundle.message("offline.inspections.module.jdk.not.found",
                                    ((SdkOrderEntry) entry).getSdkName(), module.getName()));
                            return false;
                        }
                    } else if (entry instanceof LibraryOrderEntry) {
                        final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry) entry;
                        final Library library = libraryOrderEntry.getLibrary();
                        if (library == null || library.getFiles(OrderRootType.CLASSES).length < library
                                .getUrls(OrderRootType.CLASSES).length) {
                            System.err.println(
                                    InspectionsBundle.message("offline.inspections.library.was.not.resolved",
                                            libraryOrderEntry.getPresentableName(), module.getName()));
                        }
                    }
                }
            }
        }
        return true;
    }

    private static boolean isBadSdk(final Project project, final Module[] modules) {
        /* boolean anyModuleAcceptsSdk = false;
         boolean anyModuleUsesProjectSdk = false;
         Sdk projectSdk = ProjectRootManager.getInstance(project).getProjectSdk();
         for (Module module : modules) {
           if (ModuleRootManager.getInstance(module).isSdkInherited()) {
             anyModuleUsesProjectSdk = true;
             /*if (ModuleType.get(module).isValidSdk(module, projectSdk)) {
               anyModuleAcceptsSdk = true;
             }
           }
         } */
        return false;
    }

    private static <T extends Processor> void enqueueRequestImpl(RefElement refElement,
            Map<SmartPsiElementPointer, List<T>> requestMap, T processor) {
        List<T> requests = requestMap.get(refElement.getPointer());
        if (requests == null) {
            requests = new ArrayList<T>();
            requestMap.put(refElement.getPointer(), requests);
        }
        requests.add(processor);
    }

    @Override
    public void cleanup() {
        myDerivedMethodsRequests = null;
        myDerivedClassesRequests = null;
        myMethodUsagesRequests = null;
        myFieldUsagesRequests = null;
        myClassUsagesRequests = null;
    }

    public void processSearchRequests(final GlobalInspectionContext context) {
        final RefManager refManager = context.getRefManager();
        final AnalysisScope scope = refManager.getScope();

        final SearchScope searchScope = new GlobalSearchScope(refManager.getProject()) {
            @Override
            public boolean contains(VirtualFile file) {
                return !scope.contains(file) || file.getFileType() != JavaFileType.INSTANCE;
            }

            @Override
            public int compare(VirtualFile file1, VirtualFile file2) {
                return 0;
            }

            @Override
            public boolean isSearchInModuleContent(@NotNull Module aModule) {
                return true;
            }

            @Override
            public boolean isSearchInLibraries() {
                return false;
            }
        };

        if (myDerivedClassesRequests != null) {
            final List<SmartPsiElementPointer> sortedIDs = getSortedIDs(myDerivedClassesRequests);
            for (SmartPsiElementPointer sortedID : sortedIDs) {
                final PsiClass psiClass = (PsiClass) dereferenceInReadAction(sortedID);
                if (psiClass == null)
                    continue;
                context.incrementJobDoneAmount(context.getStdJobDescriptors().FIND_EXTERNAL_USAGES,
                        ApplicationManager.getApplication().runReadAction(new Computable<String>() {
                            @Override
                            public String compute() {
                                return psiClass.getQualifiedName();
                            }
                        }));

                final List<DerivedClassesProcessor> processors = myDerivedClassesRequests.get(sortedID);
                LOG.assertTrue(processors != null, psiClass.getClass().getName());
                ClassInheritorsSearch.search(psiClass, searchScope, false)
                        .forEach(createMembersProcessor(processors, scope));
            }

            myDerivedClassesRequests = null;
        }

        if (myDerivedMethodsRequests != null) {
            final List<SmartPsiElementPointer> sortedIDs = getSortedIDs(myDerivedMethodsRequests);
            for (SmartPsiElementPointer sortedID : sortedIDs) {
                final PsiMethod psiMethod = (PsiMethod) dereferenceInReadAction(sortedID);
                if (psiMethod == null)
                    continue;
                final RefMethod refMethod = (RefMethod) refManager.getReference(psiMethod);

                context.incrementJobDoneAmount(context.getStdJobDescriptors().FIND_EXTERNAL_USAGES,
                        refManager.getQualifiedName(refMethod));

                final List<DerivedMethodsProcessor> processors = myDerivedMethodsRequests.get(sortedID);
                LOG.assertTrue(processors != null, psiMethod.getClass().getName());
                OverridingMethodsSearch.search(psiMethod, searchScope, true)
                        .forEach(createMembersProcessor(processors, scope));
            }

            myDerivedMethodsRequests = null;
        }

        if (myFieldUsagesRequests != null) {
            final List<SmartPsiElementPointer> sortedIDs = getSortedIDs(myFieldUsagesRequests);
            for (SmartPsiElementPointer sortedID : sortedIDs) {
                final PsiField psiField = (PsiField) dereferenceInReadAction(sortedID);
                if (psiField == null)
                    continue;
                final List<UsagesProcessor> processors = myFieldUsagesRequests.get(sortedID);

                LOG.assertTrue(processors != null, psiField.getClass().getName());
                context.incrementJobDoneAmount(context.getStdJobDescriptors().FIND_EXTERNAL_USAGES,
                        refManager.getQualifiedName(refManager.getReference(psiField)));

                ReferencesSearch.search(psiField, searchScope, false)
                        .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors, context)));
            }

            myFieldUsagesRequests = null;
        }

        if (myClassUsagesRequests != null) {
            final List<SmartPsiElementPointer> sortedIDs = getSortedIDs(myClassUsagesRequests);
            for (SmartPsiElementPointer sortedID : sortedIDs) {
                final PsiClass psiClass = (PsiClass) dereferenceInReadAction(sortedID);
                if (psiClass == null)
                    continue;
                final List<UsagesProcessor> processors = myClassUsagesRequests.get(sortedID);

                LOG.assertTrue(processors != null, psiClass.getClass().getName());
                context.incrementJobDoneAmount(context.getStdJobDescriptors().FIND_EXTERNAL_USAGES,
                        ApplicationManager.getApplication().runReadAction(new Computable<String>() {
                            @Override
                            public String compute() {
                                return psiClass.getQualifiedName();
                            }
                        }));

                ReferencesSearch.search(psiClass, searchScope, false)
                        .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors, context)));
            }

            myClassUsagesRequests = null;
        }

        if (myMethodUsagesRequests != null) {
            List<SmartPsiElementPointer> sortedIDs = getSortedIDs(myMethodUsagesRequests);
            for (SmartPsiElementPointer sortedID : sortedIDs) {
                final PsiMethod psiMethod = (PsiMethod) dereferenceInReadAction(sortedID);
                if (psiMethod == null)
                    continue;
                final List<UsagesProcessor> processors = myMethodUsagesRequests.get(sortedID);

                LOG.assertTrue(processors != null, psiMethod.getClass().getName());
                context.incrementJobDoneAmount(context.getStdJobDescriptors().FIND_EXTERNAL_USAGES,
                        refManager.getQualifiedName(refManager.getReference(psiMethod)));

                MethodReferencesSearch.search(psiMethod, searchScope, true)
                        .forEach(new PsiReferenceProcessorAdapter(createReferenceProcessor(processors, context)));
            }

            myMethodUsagesRequests = null;
        }
    }

    private static PsiElement dereferenceInReadAction(final SmartPsiElementPointer sortedID) {
        return ApplicationManager.getApplication().runReadAction(new Computable<PsiElement>() {
            @Override
            public PsiElement compute() {
                return sortedID.getElement();
            }
        });
    }

    private static <Member extends PsiMember, P extends Processor<Member>> PsiElementProcessorAdapter<Member> createMembersProcessor(
            final List<P> processors, final AnalysisScope scope) {
        return new PsiElementProcessorAdapter<Member>(new PsiElementProcessor<Member>() {
            @Override
            public boolean execute(@NotNull Member member) {
                if (scope.contains(member))
                    return true;
                final List<P> processorsArrayed = new ArrayList<P>(processors);
                for (P processor : processorsArrayed) {
                    if (!processor.process(member)) {
                        processors.remove(processor);
                    }
                }
                return !processors.isEmpty();
            }
        });
    }

    private int getRequestCount() {
        int sum = 0;

        sum += getRequestListSize(myClassUsagesRequests);
        sum += getRequestListSize(myDerivedClassesRequests);
        sum += getRequestListSize(myDerivedMethodsRequests);
        sum += getRequestListSize(myFieldUsagesRequests);
        sum += getRequestListSize(myMethodUsagesRequests);

        return sum;
    }

    private static int getRequestListSize(THashMap list) {
        if (list == null)
            return 0;
        return list.size();
    }

    private static List<SmartPsiElementPointer> getSortedIDs(final Map<SmartPsiElementPointer, ?> requests) {
        final List<SmartPsiElementPointer> result = new ArrayList<SmartPsiElementPointer>();

        ApplicationManager.getApplication().runReadAction(new Runnable() {
            @Override
            public void run() {
                for (SmartPsiElementPointer id : requests.keySet()) {
                    if (id != null) {
                        final PsiElement psi = id.getElement();
                        if (psi != null) {
                            result.add(id);
                        }
                    }
                }
                Collections.sort(result, new Comparator<SmartPsiElementPointer>() {
                    @Override
                    public int compare(final SmartPsiElementPointer o1, final SmartPsiElementPointer o2) {
                        PsiElement p1 = o1.getElement();
                        PsiElement p2 = o2.getElement();
                        final PsiFile psiFile1 = p1 != null ? p1.getContainingFile() : null;
                        LOG.assertTrue(psiFile1 != null);
                        final PsiFile psiFile2 = p2 != null ? p2.getContainingFile() : null;
                        LOG.assertTrue(psiFile2 != null);
                        return psiFile1.getName().compareTo(psiFile2.getName());
                    }
                });
            }
        });

        return result;
    }

    private static PsiReferenceProcessor createReferenceProcessor(@NotNull final List<UsagesProcessor> processors,
            final GlobalInspectionContext context) {
        return new PsiReferenceProcessor() {
            @Override
            public boolean execute(PsiReference reference) {
                AnalysisScope scope = context.getRefManager().getScope();
                if (scope.contains(reference.getElement())
                        && reference.getElement().getLanguage() == JavaLanguage.INSTANCE
                        || PsiTreeUtil.getParentOfType(reference.getElement(), PsiDocComment.class) != null) {
                    return true;
                }

                synchronized (processors) {
                    UsagesProcessor[] processorsArrayed = processors
                            .toArray(new UsagesProcessor[processors.size()]);
                    for (UsagesProcessor processor : processorsArrayed) {
                        if (!processor.process(reference)) {
                            processors.remove(processor);
                        }
                    }
                }

                return !processors.isEmpty();
            }
        };
    }

    @Override
    public void performPreRunActivities(@NotNull final List<Tools> globalTools,
            @NotNull final List<Tools> localTools, @NotNull final GlobalInspectionContext context) {
        getEntryPointsManager(context.getRefManager()).resolveEntryPoints(context.getRefManager());
        // UnusedDeclarationInspection should run first
        for (int i = 0; i < globalTools.size(); i++) {
            InspectionToolWrapper toolWrapper = globalTools.get(i).getTool();
            if (UnusedDeclarationInspection.SHORT_NAME.equals(toolWrapper.getShortName())) {
                Collections.swap(globalTools, i, 0);
                break;
            }
        }
    }

    @Override
    public void performPostRunActivities(@NotNull List<InspectionToolWrapper> needRepeatSearchRequest,
            @NotNull final GlobalInspectionContext context) {
        JobDescriptor progress = context.getStdJobDescriptors().FIND_EXTERNAL_USAGES;
        progress.setTotalAmount(getRequestCount());

        do {
            processSearchRequests(context);
            InspectionToolWrapper[] requestors = needRepeatSearchRequest
                    .toArray(new InspectionToolWrapper[needRepeatSearchRequest.size()]);
            InspectionManager inspectionManager = InspectionManager.getInstance(context.getProject());
            for (InspectionToolWrapper toolWrapper : requestors) {
                boolean result = false;
                if (toolWrapper instanceof GlobalInspectionToolWrapper) {
                    InspectionToolPresentation presentation = ((GlobalInspectionContextImpl) context)
                            .getPresentation(toolWrapper);
                    result = ((GlobalInspectionToolWrapper) toolWrapper).getTool()
                            .queryExternalUsagesRequests(inspectionManager, context, presentation);
                }
                if (!result) {
                    needRepeatSearchRequest.remove(toolWrapper);
                }
            }
            int oldSearchRequestCount = progress.getTotalAmount();
            int oldDoneAmount = progress.getDoneAmount();
            int totalAmount = oldSearchRequestCount + getRequestCount();
            progress.setTotalAmount(totalAmount);
            progress.setDoneAmount(oldDoneAmount);
        } while (!needRepeatSearchRequest.isEmpty());
    }

}