/*
 * Copyright 2000-2013 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.junit;

import com.intellij.compiler.CompilerConfiguration;
import com.intellij.execution.configurations.ConfigurationUtil;
import com.intellij.execution.testframework.SourceScope;
import com.intellij.ide.util.ClassFilter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class TestClassFilter implements ClassFilter.ClassFilterWithScope {
  private final PsiClass myBase;
  private final Project myProject;
  private final GlobalSearchScope myScope;

  private TestClassFilter(@NotNull PsiClass base, final GlobalSearchScope scope) {
    myBase = base;
    myProject = base.getProject();
    myScope = scope;
  }

  public PsiManager getPsiManager() { return PsiManager.getInstance(myProject); }

  public Project getProject() { return myProject; }

  public boolean isAccepted(final PsiClass aClass) {
    return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
      @Override
      public Boolean compute() {
        if (aClass.getQualifiedName() != null && ConfigurationUtil.PUBLIC_INSTANTIATABLE_CLASS.value(aClass) &&
            (aClass.isInheritor(myBase, true) || JUnitUtil.isTestClass(aClass))) {
          final CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(getProject());
          final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(aClass);
          if (virtualFile == null) return false;
          return !compilerConfiguration.isExcludedFromCompilation(virtualFile) &&
                 !ProjectRootManager.getInstance(myProject).getFileIndex().isUnderSourceRootOfType(virtualFile, JavaModuleSourceRootTypes.RESOURCES);
        }
        return false;
      }
    });
  }

  public TestClassFilter intersectionWith(final GlobalSearchScope scope) {
    return new TestClassFilter(myBase, myScope.intersectWith(scope));
  }

  public static TestClassFilter create(final SourceScope sourceScope, Module module) throws JUnitUtil.NoJUnitException {
    if (sourceScope == null) throw new JUnitUtil.NoJUnitException();
    PsiClass testCase = module == null ? JUnitUtil.getTestCaseClass(sourceScope) : JUnitUtil.getTestCaseClass(module);
    return new TestClassFilter(testCase, sourceScope.getGlobalSearchScope());
  }

  public static TestClassFilter create(final SourceScope sourceScope, Module module, final String pattern) throws JUnitUtil.NoJUnitException {
    if (sourceScope == null) throw new JUnitUtil.NoJUnitException();
    PsiClass testCase = module == null ? JUnitUtil.getTestCaseClass(sourceScope) : JUnitUtil.getTestCaseClass(module);
    final String[] patterns = pattern.split("\\|\\|");
    final List<Pattern> compilePatterns = new ArrayList<Pattern>();
    for (String p : patterns) {
      final Pattern compilePattern = getCompilePattern(p);
      if (compilePattern != null) {
        compilePatterns.add(compilePattern);
      }
    }
    return new TestClassFilter(testCase, sourceScope.getGlobalSearchScope()){
      @Override
      public boolean isAccepted(final PsiClass aClass) {
        if (super.isAccepted(aClass)) {
          final String qualifiedName = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
            @Override
            public String compute() {
              return aClass.getQualifiedName();
            }
          });
          for (Pattern compilePattern : compilePatterns) {
            if (compilePattern.matcher(qualifiedName).matches()) {
              return true;
            }
          }
        }
        return false;
      }
    };
  }

  private static Pattern getCompilePattern(String pattern) {
    Pattern compilePattern;
    try {
      compilePattern = Pattern.compile(pattern.trim());
    }
    catch (PatternSyntaxException e) {
      compilePattern = null;
    }
    return compilePattern;
  }

  public GlobalSearchScope getScope() { return myScope; }
  public PsiClass getBase() { return myBase; }
}

@@ -3146,32 +3146,38 @@
 ope sourceScope,
+ final
  Module module) 
@@ -3220,124 +3220,34 @@
     
-if (sourceScope == null) throw new JUnitUtil.NoJUnitException();%0A    PsiClass testCase = module == null ? JUnitUtil.
+final PsiClass testCase = 
 getT
@@ -3245,37 +3245,32 @@
 se = getTestCase
-Class
 (sourceScope) : 
@@ -3269,39 +3269,10 @@
 cope
-) : JUnitUtil.getTestCaseClass(
+, 
 modu
@@ -3358,37 +3358,38 @@
 ));%0A  %7D%0A%0A  p
-ublic
+rivate
  static 
 TestClassFil
@@ -3376,37 +3376,35 @@
  static 
-Test
+Psi
 Class
-Filter creat
+ getTestCas
 e(final 
@@ -3427,16 +3427,22 @@
 ceScope,
+ final
  Module 
@@ -3451,30 +3451,8 @@
 dule
-, final String pattern
 ) th
@@ -3562,114 +3562,755 @@
     
-PsiClass testCase = module == null ? JUnitUtil.getTestCaseClass(sourceScope) : JUnitUtil.getTestCaseClass(
+final JUnitUtil.NoJUnitException%5B%5D ex = new JUnitUtil.NoJUnitException%5B1%5D;%0A    final PsiClass testCase = ApplicationManager.getApplication().runReadAction(new Computable%3CPsiClass%3E() %7B%0A      @Override%0A      public PsiClass compute() %7B%0A        try %7B%0A          return module == null ? JUnitUtil.getTestCaseClass(sourceScope) : JUnitUtil.getTestCaseClass(module);%0A        %7D%0A        catch (JUnitUtil.NoJUnitException e) %7B%0A          ex%5B0%5D = e;%0A          return null;%0A        %7D%0A      %7D%0A    %7D);%0A    if (ex%5B0%5D != null) throw ex%5B0%5D;%0A    return testCase;%0A  %7D%0A%0A  public static TestClassFilter create(final SourceScope sourceScope, Module module, final String pattern) throws JUnitUtil.NoJUnitException %7B%0A    final PsiClass testCase = getTestCase(sourceScope, 
 modu

read access