blob: f7b8ecedcf3dfd628d1fadff33ca2d53a37e68cf [file] [log] [blame]
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;
}
}