/*
 * 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.
 */

package com.intellij.execution;

import com.intellij.execution.junit.JUnitUtil;
import com.intellij.execution.junit.TestClassFilter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.search.PsiElementProcessorAdapter;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.search.searches.AnnotatedMembersSearch;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Set;

public class ConfigurationUtil {
  // return true if there is JUnit4 test
  public static boolean findAllTestClasses(final TestClassFilter testClassFilter, final Set<PsiClass> found) {
    final PsiManager manager = testClassFilter.getPsiManager();

    final Project project = manager.getProject();
    GlobalSearchScope projectScopeWithoutLibraries = GlobalSearchScope.projectScope(project);
    final GlobalSearchScope scope = projectScopeWithoutLibraries.intersectWith(testClassFilter.getScope());
    ClassInheritorsSearch.search(testClassFilter.getBase(), scope, true).forEach(new PsiElementProcessorAdapter<PsiClass>(new PsiElementProcessor<PsiClass>() {
      public boolean execute(@NotNull final PsiClass aClass) {
        if (testClassFilter.isAccepted(aClass)) found.add(aClass);
        return true;
      }
    }));

    // classes having suite() method
    final PsiMethod[] suiteMethods = ApplicationManager.getApplication().runReadAction(
        new Computable<PsiMethod[]>() {
          public PsiMethod[] compute() {
            return PsiShortNamesCache.getInstance(project).getMethodsByName(JUnitUtil.SUITE_METHOD_NAME, scope);
          }
        }
    );
    for (final PsiMethod method : suiteMethods) {
      final PsiClass containingClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
        public PsiClass compute() {
          return method.getContainingClass();
        }
      });
      if (containingClass == null) continue;
      if (containingClass instanceof PsiAnonymousClass) continue;
      if (containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
      if (containingClass.getContainingClass() != null && !containingClass.hasModifierProperty(PsiModifier.STATIC)) continue;
      if (ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
        public Boolean compute() {
          return JUnitUtil.isSuiteMethod(method, project);
        }
      }).booleanValue()) {
        found.add(containingClass);
      }
    }

    boolean hasJunit4 = addAnnotatedMethodsAnSubclasses(manager, scope, testClassFilter, found, "org.junit.Test", true);
    hasJunit4 |= addAnnotatedMethodsAnSubclasses(manager, scope, testClassFilter, found, "org.junit.runner.RunWith", false);
    return hasJunit4;
  }

  private static boolean addAnnotatedMethodsAnSubclasses(final PsiManager manager, final GlobalSearchScope scope, final TestClassFilter testClassFilter,
                                                         final Set<PsiClass> found,
                                                         final String annotation,
                                                         final boolean isMethod) {
    final Ref<Boolean> isJUnit4 = new Ref<Boolean>(Boolean.FALSE);
    // annotated with @Test
    final PsiClass testAnnotation = ApplicationManager.getApplication().runReadAction(
        new Computable<PsiClass>() {
          @Nullable
          public PsiClass compute() {
            return JavaPsiFacade.getInstance(manager.getProject()).findClass(annotation, GlobalSearchScope.allScope(manager.getProject()));
          }
        }
    );
    if (testAnnotation != null) {
      AnnotatedMembersSearch.search(testAnnotation, GlobalSearchScope.allScope(manager.getProject())).forEach(new Processor<PsiMember>() {
        public boolean process(final PsiMember annotated) {
          final PsiClass containingClass = annotated instanceof PsiClass ? (PsiClass)annotated : annotated.getContainingClass();
          if (containingClass != null && annotated instanceof PsiMethod == isMethod) {
            if (scope.contains(PsiUtilBase.getVirtualFile(containingClass)) && ApplicationManager.getApplication().runReadAction(
              new Computable<Boolean>() {
                @Override
                public Boolean compute() {
                  return testClassFilter.isAccepted(containingClass);
                }
              })) {
              found.add(containingClass);
              isJUnit4.set(Boolean.TRUE);
            }
            ClassInheritorsSearch.search(containingClass, scope, true)
              .forEach(new PsiElementProcessorAdapter<PsiClass>(new PsiElementProcessor<PsiClass>() {
                public boolean execute(@NotNull final PsiClass aClass) {
                  if (testClassFilter.isAccepted(aClass)) {
                    found.add(aClass);
                    isJUnit4.set(Boolean.TRUE);
                  }
                  return true;
                }
              }));
          }
          return true;
        }
      });
    }

    return isJUnit4.get().booleanValue();
  }
}

@@ -2707,41 +2707,8 @@
     
- final PsiClass containingClass =
  App
@@ -2762,85 +2762,91 @@
 new 
-Computable%3CPsiClass%3E() %7B%0A        public PsiClass compute() %7B%0A          return
+Runnable() %7B%0A        public void run() %7B%0A          final PsiClass containingClass =
  met
@@ -2875,32 +2875,16 @@
 ();%0A    
-    %7D%0A      %7D);%0A
       if
@@ -2910,26 +2910,28 @@
 = null) 
-continue;%0A
+return;%0A    
       if
@@ -2978,26 +2978,28 @@
 sClass) 
-continue;%0A
+return;%0A    
       if
@@ -3059,26 +3059,28 @@
 TRACT)) 
-continue;%0A
+return;%0A    
       if
@@ -3191,237 +3191,78 @@
 C)) 
-continue;%0A      if (ApplicationManager.getApplication().runReadAction(new Computable%3CBoolean%3E() %7B%0A        public Boolean compute() %7B%0A          return JUnitUtil.isSuiteMethod(method, project);%0A        %7D%0A      %7D).booleanValue()) %7B%0A
+return;%0A          if (JUnitUtil.isSuiteMethod(method, project)) %7B%0A    
     
@@ -3295,25 +3295,49 @@
 ass);%0A      
-%7D
+    %7D%0A        %7D%0A      %7D);
 %0A    %7D%0A%0A    

read access