/*
 * Copyright 2000-2017 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.structuralsearch.impl.matcher.predicates;

import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.structuralsearch.MalformedPatternException;
import com.intellij.structuralsearch.MatchOptions;
import com.intellij.structuralsearch.MatchResult;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.ConfigurationManager;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Maxim.Mossienko
 */
public class WithinPredicate extends MatchPredicate {
  private static final Set<String> recursionGuard = new HashSet<>();
  private final MatchOptions myMatchOptions;
  private final Matcher matcher;

  public WithinPredicate(String within, FileType fileType, Project project) {
    if (StringUtil.isQuotedString(within)) {
      // keep old configurations working
      myMatchOptions = new MatchOptions();
      myMatchOptions.setLooseMatching(true);
      myMatchOptions.setFileType(fileType);
      myMatchOptions.fillSearchCriteria(StringUtil.unquoteString(within));
      matcher = new Matcher(project, myMatchOptions);
    }
    else {
      if (!recursionGuard.add(within)) {
        throw new MalformedPatternException("Pattern recursively contained within itself");
      }
      try {
        final Configuration configuration = ConfigurationManager.getInstance(project).findConfigurationByName(within);
        if (configuration == null) {
          throw new MalformedPatternException("Configuration '" + within + "' not found");
        }
        myMatchOptions = configuration.getMatchOptions();
        matcher = new Matcher(project, myMatchOptions);
      } finally {
        recursionGuard.remove(within);
      }
    }
  }

  @Override
  public boolean match(PsiElement matchedNode, int start, int end, MatchContext context) {
    final List<MatchResult> results = matcher.matchByDownUp(matchedNode, myMatchOptions);
    for (MatchResult result : results) {
      if (PsiTreeUtil.isAncestor(result.getMatch(), matchedNode, false)) {
        return true;
      }
    }
    return false;
  }
}
@@ -1447,16 +1447,57 @@
 icate %7B%0A
+  @SuppressWarnings(%22SSBasedInspection%22)%0A
   privat
@@ -1511,16 +1511,28 @@
 c final 
+ThreadLocal%3C
 Set%3CStri
@@ -1534,18 +1534,22 @@
 %3CString%3E
- r
+%3E ourR
 ecursion
@@ -1555,16 +1555,46 @@
 nGuard =
+ ThreadLocal.withInitial(() -%3E
  new Has
@@ -1601,16 +1601,17 @@
 hSet%3C%3E()
+)
 ;%0A  priv
@@ -2135,27 +2135,71 @@
     
-if (!recursionGuard
+final Set%3CString%3E set = ourRecursionGuard.get();%0A      if (!set
 .add
@@ -2719,17 +2719,130 @@
         
-r
+set.remove(within);%0A        if (set.isEmpty()) %7B%0A          // we're finished with this thread local%0A          ourR
 ecursion
@@ -2850,32 +2850,36 @@
 uard.remove(
-within);
+);%0A        %7D
 %0A      %7D%0A   

make guard thread safe