// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md). // Licensed under the Apache License, Version 2.0 (see LICENSE). package com.twitter.intellij.pants.quickfix; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.packageDependencies.DependencyValidationManager; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.twitter.intellij.pants.model.PantsTargetAddress; import com.twitter.intellij.pants.util.PantsUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class PantsUnresolvedReferenceFixFinder { @NotNull public static List<PantsQuickFix> findMissingDependencies(@NotNull PsiReference reference) { final PsiElement unresolvedPsiElement = reference.getElement(); @NonNls final String referenceName = reference.getRangeInElement().substring(unresolvedPsiElement.getText()); final PsiFile containingFile = unresolvedPsiElement.getContainingFile(); return containingFile != null ? findMissingDependencies(referenceName, containingFile) : Collections.<PantsQuickFix>emptyList(); } @NotNull public static List<PantsQuickFix> findMissingDependencies(@NotNull String referenceName, @NotNull PsiFile containingFile) { final VirtualFile containingClassFile = containingFile.getVirtualFile(); if (containingClassFile == null) return Collections.emptyList(); final Project project = containingFile.getProject(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); final Module containingModule = fileIndex.getModuleForFile(containingClassFile); final List<PantsTargetAddress> addresses = PantsUtil.getTargetAddressesFromModule(containingModule); if (addresses.size() != 1) return Collections.emptyList(); final PantsTargetAddress currentAddress = addresses.iterator().next(); final PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(referenceName, GlobalSearchScope.allScope(project)); final List<PsiClass> allowedDependencies = filterAllowedDependencies(containingFile, classes); final List<PantsQuickFix> result = new ArrayList<PantsQuickFix>(); for (PsiClass dependency : allowedDependencies) { final Module module = ModuleUtil.findModuleForPsiElement(dependency); for (PantsTargetAddress addressToAdd : PantsUtil.getTargetAddressesFromModule(module)) { result.add(new AddPantsTargetDependencyFix(currentAddress, addressToAdd)); } // todo(fkoroktov): handle jars } return result; } private static List<PsiClass> filterAllowedDependencies(PsiFile fromFile, PsiClass[] classes) { final DependencyValidationManager dependencyValidationManager = DependencyValidationManager.getInstance(fromFile.getProject()); final List<PsiClass> result = new ArrayList<PsiClass>(); for (PsiClass psiClass : classes) { if (dependencyValidationManager.getViolatorDependencyRule(fromFile, psiClass.getContainingFile()) == null) { result.add(psiClass); } } return result; } }