package com.intellij.lang.javascript.flex.flexunit;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.javascript.psi.JSFile;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.lang.javascript.psi.ecmal4.JSQualifiedNamedElement;
import com.intellij.lang.javascript.psi.ecmal4.XmlBackedJSClassFactory;
import com.intellij.lang.javascript.psi.impl.JSPsiImplUtils;
import com.intellij.lang.javascript.psi.resolve.JSResolveUtil;
import com.intellij.lang.javascript.psi.stubs.JSNameIndex;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopes;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlFile;
import com.intellij.testIntegration.TestFinder;
import com.intellij.testIntegration.TestFinderHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class FlexUnitTestFinder implements TestFinder {
@Nullable
public JSClass findSourceElement(@NotNull final PsiElement element) {
return findContextClass(element);
}
@Nullable
static JSClass findContextClass(final @NotNull PsiElement element) {
if (FlexUnitSupport.getModuleAndSupport(element) == null) {
return null;
}
PsiFile psiFile = PsiTreeUtil.getParentOfType(element, PsiFile.class, false);
if (psiFile instanceof JSFile) {
psiFile = InjectedLanguageManager.getInstance(psiFile.getProject()).getTopLevelFile(psiFile);
}
if (psiFile instanceof JSFile) {
return JSPsiImplUtils.findClass((JSFile)psiFile);
}
else if (psiFile instanceof XmlFile) {
return XmlBackedJSClassFactory.getXmlBackedClass((XmlFile)psiFile);
}
return null;
}
@NotNull
public Collection<PsiElement> findTestsForClass(@NotNull final PsiElement element) {
final VirtualFile file = element.getContainingFile().getVirtualFile();
final JSClass jsClass = findSourceElement(element);
final String className = jsClass == null ? null : jsClass.getName();
final Pair<Module, FlexUnitSupport> moduleAndSupport = FlexUnitSupport.getModuleAndSupport(element);
final Module module = moduleAndSupport == null ? null : moduleAndSupport.first;
final FlexUnitSupport flexUnitSupport = moduleAndSupport == null ? null : moduleAndSupport.second;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex();
if (className == null || module == null || flexUnitSupport == null || (file != null && fileIndex.isInTestSourceContent(file))) {
return Collections.emptyList();
}
final Collection<String> allNames = StubIndex.getInstance().getAllKeys(JSNameIndex.KEY, element.getProject());
final GlobalSearchScope scope = GlobalSearchScope.moduleWithDependentsScope(module);
final List<Pair<? extends PsiNamedElement, Integer>> classesWithProximity = new ArrayList<>();
for (final String possibleTestName : allNames) {
if (possibleTestName.contains(className)) {
for (final JSQualifiedNamedElement jsElement : JSResolveUtil.findElementsByName(possibleTestName, element.getProject(), scope)) {
final VirtualFile f = jsElement.getContainingFile().getVirtualFile();
final boolean inTestSource = f != null && fileIndex.isInTestSourceContent(f);
if (jsElement instanceof JSClass && jsElement != jsClass &&
((inTestSource && possibleTestName.contains("Test")) || flexUnitSupport.isTestClass((JSClass)jsElement, true))) {
classesWithProximity.add(Pair.create(jsElement, TestFinderHelper.calcTestNameProximity(className, jsElement.getName())));
}
}
}
}
return TestFinderHelper.getSortedElements(classesWithProximity, true);
}
@NotNull
public Collection<PsiElement> findClassesForTest(@NotNull final PsiElement element) {
final JSClass jsClass = findSourceElement(element);
final String className = jsClass == null ? null : jsClass.getName();
final Pair<Module, FlexUnitSupport> moduleAndSupport = FlexUnitSupport.getModuleAndSupport(element);
final Module module = moduleAndSupport == null ? null : moduleAndSupport.first;
final FlexUnitSupport flexUnitSupport = moduleAndSupport == null ? null : moduleAndSupport.second;
if (className == null || module == null || flexUnitSupport == null) {
return Collections.emptyList();
}
final GlobalSearchScope scope =
GlobalSearchScope.moduleWithDependenciesScope(module).intersectWith(GlobalSearchScopes.projectProductionScope(module.getProject()));
final List<Pair<? extends PsiNamedElement, Integer>> classesWithWeights = new ArrayList<>();
for (Pair<String, Integer> nameWithWeight : TestFinderHelper.collectPossibleClassNamesWithWeights(className)) {
for (final JSQualifiedNamedElement jsElement : JSResolveUtil.findElementsByName(nameWithWeight.first, module.getProject(), scope)) {
if (jsElement instanceof JSClass && jsElement != jsClass && !((JSClass)jsElement).isInterface() &&
!flexUnitSupport.isTestClass((JSClass)jsElement, true)) {
classesWithWeights.add(Pair.create(jsElement, nameWithWeight.second));
}
}
}
return TestFinderHelper.getSortedElements(classesWithWeights, false);
}
public boolean isTest(@NotNull final PsiElement element) {
final JSClass jsClass = findSourceElement(element);
final Pair<Module, FlexUnitSupport> moduleAndSupport = FlexUnitSupport.getModuleAndSupport(element);
final VirtualFile file = element.getContainingFile().getVirtualFile();
return jsClass != null && moduleAndSupport != null &&
((file != null && file.getName().contains("Test") &&
ModuleRootManager.getInstance(moduleAndSupport.first).getFileIndex().isInTestSourceContent(file))
|| moduleAndSupport.second.isTestClass(jsClass, true));
}
}