/**
   * Filter the specified collection of classes to return only ones that contain any of the
   * specified values in the specified annotation parameter. For example, this method can be used to
   * return all classes that contain all tesng annotations that are in the groups 'foo' or 'bar'.
   */
  public static Map<PsiClass, Collection<PsiMethod>> filterAnnotations(
      String parameter, Set<String> values, Collection<PsiClass> classes) {
    Map<PsiClass, Collection<PsiMethod>> results = new HashMap<>();
    Set<String> test = new HashSet<>(1);
    test.add(TEST_ANNOTATION_FQN);
    ContainerUtil.addAll(test, CONFIG_ANNOTATIONS_FQN);
    for (PsiClass psiClass : classes) {
      if (isBrokenPsiClass(psiClass)) continue;

      PsiAnnotation annotation;
      try {
        annotation = AnnotationUtil.findAnnotation(psiClass, test);
      } catch (Exception e) {
        LOGGER.error(
            "Exception trying to findAnnotation on "
                + psiClass.getClass().getName()
                + ".\n\n"
                + e.getMessage());
        annotation = null;
      }
      if (annotation != null) {
        if (isAnnotatedWithParameter(annotation, parameter, values)) {
          results.put(psiClass, new LinkedHashSet<>());
        }
      } else {
        Collection<String> matches =
            extractAnnotationValuesFromJavaDoc(getTextJavaDoc(psiClass), parameter);
        for (String s : matches) {
          if (values.contains(s)) {
            results.put(psiClass, new LinkedHashSet<>());
            break;
          }
        }
      }

      // we already have the class, no need to look through its methods
      PsiMethod[] methods = psiClass.getMethods();
      for (PsiMethod method : methods) {
        if (method != null) {
          annotation = AnnotationUtil.findAnnotation(method, test);
          if (annotation != null) {
            if (isAnnotatedWithParameter(annotation, parameter, values)) {
              if (results.get(psiClass) == null) results.put(psiClass, new LinkedHashSet<>());
              results.get(psiClass).add(method);
            }
          } else {
            Collection<String> matches =
                extractAnnotationValuesFromJavaDoc(getTextJavaDoc(psiClass), parameter);
            for (String s : matches) {
              if (values.contains(s)) {
                results.get(psiClass).add(method);
              }
            }
          }
        }
      }
    }
    return results;
  }
  @NotNull
  private List<Pair<PsiClass, VirtualFile>> doFindClasses(
      @NotNull String qName, @NotNull final GlobalSearchScope scope) {
    final Collection<PsiClass> classes =
        JavaFullClassNameIndex.getInstance().get(qName.hashCode(), myManager.getProject(), scope);
    if (classes.isEmpty()) return Collections.emptyList();
    List<Pair<PsiClass, VirtualFile>> result = new ArrayList<>(classes.size());
    for (PsiClass aClass : classes) {
      final String qualifiedName = aClass.getQualifiedName();
      if (qualifiedName == null || !qualifiedName.equals(qName)) continue;

      PsiFile file = aClass.getContainingFile();
      if (file == null) {
        throw new AssertionError("No file for class: " + aClass + " of " + aClass.getClass());
      }
      final boolean valid = file.isValid();
      VirtualFile vFile = file.getVirtualFile();
      if (!valid) {
        LOG.error(
            "Invalid file "
                + file
                + "; virtualFile:"
                + vFile
                + (vFile != null && !vFile.isValid() ? " (invalid)" : "")
                + "; id="
                + (vFile == null ? 0 : ((VirtualFileWithId) vFile).getId()),
            new PsiInvalidElementAccessException(aClass));
        continue;
      }
      if (!hasAcceptablePackage(vFile)) continue;

      result.add(Pair.create(aClass, vFile));
    }

    return result;
  }