diff options
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/RegExpPredicate.java')
-rw-r--r-- | platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/RegExpPredicate.java | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/RegExpPredicate.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/RegExpPredicate.java new file mode 100644 index 000000000000..f7b8ecedcf3d --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/RegExpPredicate.java @@ -0,0 +1,175 @@ +package com.intellij.structuralsearch.impl.matcher.predicates; + +import com.intellij.psi.*; +import com.intellij.structuralsearch.MalformedPatternException; +import com.intellij.structuralsearch.SSRBundle; +import com.intellij.structuralsearch.StructuralSearchProfile; +import com.intellij.structuralsearch.StructuralSearchUtil; +import com.intellij.structuralsearch.impl.matcher.MatchContext; +import com.intellij.structuralsearch.impl.matcher.MatchResultImpl; +import com.intellij.structuralsearch.impl.matcher.MatchUtils; +import com.intellij.structuralsearch.impl.matcher.handlers.MatchPredicate; +import com.intellij.structuralsearch.plugin.util.SmartPsiPointer; +import org.jetbrains.annotations.NonNls; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Root of handlers for pattern node matching. Handles simpliest type of the match. + */ +public final class RegExpPredicate extends MatchPredicate { + private Pattern pattern; + private final String baseHandlerName; + private boolean simpleString; + private final boolean couldBeOptimized; + private final String regexp; + private final boolean caseSensitive; + private boolean multiline; + private final boolean wholeWords; + private final boolean target; + private NodeTextGenerator myNodeTextGenerator; + + public interface NodeTextGenerator { + String getText(PsiElement element); + } + + public RegExpPredicate(final String regexp, final boolean caseSensitive, final String _baseHandlerName, boolean _wholeWords, boolean _target) { + couldBeOptimized = containsRegExp(regexp); + if (!_wholeWords) { + simpleString = couldBeOptimized; + } + + this.regexp = regexp; + this.caseSensitive = caseSensitive; + this.wholeWords = _wholeWords; + baseHandlerName = _baseHandlerName; + + if (!simpleString) { + compilePattern(); + } + target = _target; + } + + private static boolean containsRegExp(final String regexp) { + for(int i=0;i<regexp.length();++i) { + if(MatchUtils.SPECIAL_CHARS.indexOf(regexp.charAt(i))!=-1) { + return false; + } + } + + return true; + } + + private void compilePattern() { + try { + @NonNls String realRegexp = regexp; + if (wholeWords) { + realRegexp = ".*?\\b(?:" + realRegexp + ")\\b.*?"; + } + + pattern = Pattern.compile( + realRegexp, + (caseSensitive ? 0: Pattern.CASE_INSENSITIVE) | (multiline ? Pattern.DOTALL:0) + ); + } catch(PatternSyntaxException ex) { + throw new MalformedPatternException(SSRBundle.message("error.incorrect.regexp.constraint", regexp, baseHandlerName)); + } + } + + public boolean couldBeOptimized() { + return couldBeOptimized; + } + + public String getRegExp() { + return regexp; + } + + /** + * Attempts to match given handler node against given node. + * @param matchedNode for matching + * @param context of the matching + * @return true if matching was successfull and false otherwise + */ + public boolean match(PsiElement node,PsiElement matchedNode, int start, int end, MatchContext context) { + if (matchedNode==null) return false; + String text; + + text = myNodeTextGenerator != null ? myNodeTextGenerator.getText(matchedNode) : getMeaningfulText(matchedNode); + + boolean result = doMatch(text, start, end, context, matchedNode); + + if (!result) { + + if(StructuralSearchUtil.isIdentifier(matchedNode)) { + matchedNode = matchedNode.getParent(); + } + + String alternativeText = context.getPattern().getAlternativeTextToMatch(matchedNode, text); + if (alternativeText != null) { + result = doMatch(alternativeText, start, end, context, matchedNode); + } + } + + return result; + } + + public static String getMeaningfulText(PsiElement matchedNode) { + final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(matchedNode); + return profile != null ? profile.getMeaningfulText(matchedNode) : matchedNode.getText(); + } + + public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) { + return match(patternNode,matchedNode,0,-1,context); + } + + boolean doMatch(String text, MatchContext context, PsiElement matchedElement) { + return doMatch(text,0,-1,context, matchedElement); + } + + boolean doMatch(String text, int from, int end, MatchContext context,PsiElement matchedElement) { + if (from > 0 || end != -1) { + text = text.substring(from, end == -1 || end >= text.length() ? text.length():end); + } + + if (simpleString) { + return (caseSensitive)?text.equals(regexp):text.equalsIgnoreCase(regexp); + } + + if(!multiline && text.contains("\n")) setMultiline(true); + final Matcher matcher = pattern.matcher(text); + + if (matcher.matches()) { + for (int i=1;i<=matcher.groupCount();++i) { + context.getResult().addSon( + new MatchResultImpl( + baseHandlerName + "_" + i, + matcher.group(i), + new SmartPsiPointer(matchedElement), + matcher.start(i), + matcher.end(i), + target + ) + ); + } + return true; + } else { + return false; + } + } + + + public void setNodeTextGenerator(final NodeTextGenerator nodeTextGenerator) { + myNodeTextGenerator = nodeTextGenerator; + } + + public void setMultiline(boolean b) { + multiline = b; + compilePattern(); + } + + public boolean isWholeWords() { + return wholeWords; + } +} |