summaryrefslogtreecommitdiff
path: root/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java')
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java314
1 files changed, 314 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java
new file mode 100644
index 000000000000..130751adec67
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/GlobalCompilingVisitor.java
@@ -0,0 +1,314 @@
+package com.intellij.structuralsearch.impl.matcher.compiler;
+
+import com.intellij.dupLocator.util.NodeFilter;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.StructuralSearchProfile;
+import com.intellij.structuralsearch.StructuralSearchUtil;
+import com.intellij.structuralsearch.impl.matcher.filters.CompositeFilter;
+import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
+import com.intellij.structuralsearch.impl.matcher.handlers.LiteralWithSubstitutionHandler;
+import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
+import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
+import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.intellij.structuralsearch.MatchOptions.INSTANCE_MODIFIER_NAME;
+import static com.intellij.structuralsearch.MatchOptions.MODIFIER_ANNOTATION_NAME;
+
+/**
+ * @author maxim
+ */
+public class GlobalCompilingVisitor {
+ @NonNls private static final String SUBSTITUTION_PATTERN_STR = "\\b(__\\$_\\w+)\\b";
+ private static final Pattern ourSubstitutionPattern = Pattern.compile(SUBSTITUTION_PATTERN_STR);
+ private static final Set<String> ourReservedWords = new HashSet<String>(Arrays.asList(MODIFIER_ANNOTATION_NAME, INSTANCE_MODIFIER_NAME)) {
+ {
+ for (StructuralSearchProfile profile : Extensions.getExtensions(StructuralSearchProfile.EP_NAME)) {
+ addAll(profile.getReservedWords());
+ }
+ }
+ };
+ private static final Pattern ourAlternativePattern = Pattern.compile("^\\((.+)\\)$");
+ @NonNls private static final String WORD_SEARCH_PATTERN_STR = ".*?\\b(.+?)\\b.*?";
+ private static final Pattern ourWordSearchPattern = Pattern.compile(WORD_SEARCH_PATTERN_STR);
+ private CompileContext context;
+ private final ArrayList<PsiElement> myLexicalNodes = new ArrayList<PsiElement>();
+
+ private int myCodeBlockLevel;
+
+ private static final NodeFilter ourFilter = LexicalNodesFilter.getInstance();
+
+ public static NodeFilter getFilter() {
+ return ourFilter;
+ }
+
+ public void setHandler(PsiElement element, MatchingHandler handler) {
+ MatchingHandler realHandler = context.getPattern().getHandlerSimple(element);
+
+ if (realHandler instanceof SubstitutionHandler) {
+ ((SubstitutionHandler)realHandler).setMatchHandler(handler);
+ }
+ else {
+ // @todo care about composite handler in this case of simple handler!
+ context.getPattern().setHandler(element, handler);
+ }
+ }
+
+ public final void handle(PsiElement element) {
+
+ if ((!ourFilter.accepts(element) ||
+ StructuralSearchUtil.isIdentifier(element)) &&
+ context.getPattern().isRealTypedVar(element) &&
+ context.getPattern().getHandlerSimple(element) == null
+ ) {
+ String name = context.getPattern().getTypedVarString(element);
+ // name is the same for named element (clazz,methods, etc) and token (name of ... itself)
+ // @todo need fix this
+ final SubstitutionHandler handler;
+
+ context.getPattern().setHandler(
+ element,
+ handler = (SubstitutionHandler)context.getPattern().getHandler(name)
+ );
+
+ if (handler != null && context.getOptions().getVariableConstraint(handler.getName()).isPartOfSearchResults()) {
+ handler.setTarget(true);
+ context.getPattern().setTargetNode(element);
+ }
+ }
+ }
+
+ public CompileContext getContext() {
+ return context;
+ }
+
+ public int getCodeBlockLevel() {
+ return myCodeBlockLevel;
+ }
+
+ public void setCodeBlockLevel(int codeBlockLevel) {
+ this.myCodeBlockLevel = codeBlockLevel;
+ }
+
+ static void setFilter(MatchingHandler handler, NodeFilter filter) {
+ if (handler.getFilter() != null &&
+ handler.getFilter().getClass() != filter.getClass()
+ ) {
+ // for constructor we will have the same handler for class and method and tokens itselfa
+ handler.setFilter(
+ new CompositeFilter(
+ filter,
+ handler.getFilter()
+ )
+ );
+ }
+ else {
+ handler.setFilter(filter);
+ }
+ }
+
+ public ArrayList<PsiElement> getLexicalNodes() {
+ return myLexicalNodes;
+ }
+
+ public void addLexicalNode(PsiElement node) {
+ myLexicalNodes.add(node);
+ }
+
+ void compile(PsiElement[] elements, CompileContext context) {
+ myCodeBlockLevel = 0;
+ this.context = context;
+ final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(context.getOptions().getFileType());
+ assert profile != null;
+ profile.compile(elements, this);
+
+ if (context.getPattern().getStrategy() == null) {
+ System.out.println();
+ }
+ }
+
+ @Nullable
+ public MatchingHandler processPatternStringWithFragments(String pattern, OccurenceKind kind) {
+ return processPatternStringWithFragments(pattern, kind, ourSubstitutionPattern);
+ }
+
+ @Nullable
+ public MatchingHandler processPatternStringWithFragments(String pattern, OccurenceKind kind, Pattern substitutionPattern) {
+ String content;
+
+ if (kind == OccurenceKind.LITERAL) {
+ content = pattern.substring(1, pattern.length() - 1);
+ }
+ else if (kind == OccurenceKind.COMMENT) {
+ content = pattern;
+ }
+ else {
+ return null;
+ }
+
+ @NonNls StringBuilder buf = new StringBuilder(content.length());
+ Matcher matcher = substitutionPattern.matcher(content);
+ List<SubstitutionHandler> handlers = null;
+ int start = 0;
+ String word;
+ boolean hasLiteralContent = false;
+
+ SubstitutionHandler handler = null;
+ while (matcher.find()) {
+ if (handlers == null) handlers = new ArrayList<SubstitutionHandler>();
+ handler = (SubstitutionHandler)getContext().getPattern().getHandler(matcher.group(1));
+ if (handler != null) handlers.add(handler);
+
+ word = content.substring(start, matcher.start());
+
+ if (word.length() > 0) {
+ buf.append(StructuralSearchUtil.shieldSpecialChars(word));
+ hasLiteralContent = true;
+
+ processTokenizedName(word, false, kind);
+ }
+
+ RegExpPredicate predicate = MatchingHandler.getSimpleRegExpPredicate(handler);
+
+ if (predicate == null || !predicate.isWholeWords()) {
+ buf.append("(.*?)");
+ }
+ else {
+ buf.append(".*?\\b(").append(predicate.getRegExp()).append(")\\b.*?");
+ }
+
+ if (isSuitablePredicate(predicate, handler)) {
+ processTokenizedName(predicate.getRegExp(), false, kind);
+ }
+
+ start = matcher.end();
+ }
+
+ word = content.substring(start, content.length());
+
+ if (word.length() > 0) {
+ hasLiteralContent = true;
+ buf.append(StructuralSearchUtil.shieldSpecialChars(word));
+
+ processTokenizedName(word, false, kind);
+ }
+
+ if (hasLiteralContent) {
+ if (kind == OccurenceKind.LITERAL) {
+ buf.insert(0, "[\"']");
+ buf.append("[\"']");
+ }
+ buf.append("$");
+ }
+
+ if (handlers != null) {
+ return hasLiteralContent ? new LiteralWithSubstitutionHandler(buf.toString(), handlers) : handler;
+ }
+
+ return null;
+ }
+
+ @Contract("null,_ -> false")
+ static boolean isSuitablePredicate(RegExpPredicate predicate, SubstitutionHandler handler) {
+ return predicate != null && handler.getMinOccurs() != 0 && predicate.couldBeOptimized();
+ }
+
+ public static void addFilesToSearchForGivenWord(String refname,
+ boolean endTransaction,
+ GlobalCompilingVisitor.OccurenceKind kind,
+ CompileContext compileContext) {
+ if (!compileContext.getSearchHelper().doOptimizing()) {
+ return;
+ }
+ if (ourReservedWords.contains(refname)) return; // skip our special annotations !!!
+
+ boolean addedSomething = false;
+
+ if (kind == GlobalCompilingVisitor.OccurenceKind.CODE) {
+ addedSomething = compileContext.getSearchHelper().addWordToSearchInCode(refname);
+ }
+ else if (kind == GlobalCompilingVisitor.OccurenceKind.COMMENT) {
+ addedSomething = compileContext.getSearchHelper().addWordToSearchInComments(refname);
+ }
+ else if (kind == GlobalCompilingVisitor.OccurenceKind.LITERAL) {
+ addedSomething = compileContext.getSearchHelper().addWordToSearchInLiterals(refname);
+ }
+
+ if (addedSomething && endTransaction) {
+ compileContext.getSearchHelper().endTransaction();
+ }
+ }
+
+ public void processTokenizedName(String name, boolean skipComments, GlobalCompilingVisitor.OccurenceKind kind) {
+ WordTokenizer tokenizer = new WordTokenizer(name);
+ for (Iterator<String> i = tokenizer.iterator(); i.hasNext();) {
+ String nextToken = i.next();
+ if (skipComments &&
+ (nextToken.equals("/*") || nextToken.equals("/**") || nextToken.equals("*/") || nextToken.equals("*") || nextToken.equals("//"))
+ ) {
+ continue;
+ }
+
+ Matcher matcher = ourAlternativePattern.matcher(nextToken);
+ if (matcher.matches()) {
+ StringTokenizer alternatives = new StringTokenizer(matcher.group(1), "|");
+ while (alternatives.hasMoreTokens()) {
+ addFilesToSearchForGivenWord(alternatives.nextToken(), !alternatives.hasMoreTokens(), kind, getContext());
+ }
+ }
+ else {
+ addFilesToSearchForGivenWord(nextToken, true, kind, getContext());
+ }
+ }
+ }
+
+ public enum OccurenceKind {
+ LITERAL, COMMENT, CODE
+ }
+
+ private static class WordTokenizer {
+ private final List<String> myWords = new ArrayList<String>();
+
+ WordTokenizer(String text) {
+ final StringTokenizer tokenizer = new StringTokenizer(text);
+ Matcher matcher = null;
+
+ while (tokenizer.hasMoreTokens()) {
+ String nextToken = tokenizer.nextToken();
+ if (matcher == null) {
+ matcher = ourWordSearchPattern.matcher(nextToken);
+ }
+ else {
+ matcher.reset(nextToken);
+ }
+
+ nextToken = (matcher.matches()) ? matcher.group(1) : nextToken;
+ int lastWordStart = 0;
+ int i;
+ for (i = 0; i < nextToken.length(); ++i) {
+ if (!Character.isJavaIdentifierStart(nextToken.charAt(i))) {
+ if (i != lastWordStart) {
+ myWords.add(nextToken.substring(lastWordStart, i));
+ }
+ lastWordStart = i + 1;
+ }
+ }
+
+ if (i != lastWordStart) {
+ myWords.add(nextToken.substring(lastWordStart, i));
+ }
+ }
+ }
+
+ Iterator<String> iterator() {
+ return myWords.iterator();
+ }
+ }
+}