blob: c53b08412b6b220fe7bb09f000dda4c5d6c930eb [file] [log] [blame]
package com.intellij.structuralsearch.impl.matcher.handlers;
import com.intellij.dupLocator.iterators.NodeIterator;
import com.intellij.dupLocator.util.NodeFilter;
import com.intellij.psi.*;
import com.intellij.structuralsearch.StructuralSearchUtil;
import com.intellij.structuralsearch.impl.matcher.CompiledPattern;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.MatchResultImpl;
import com.intellij.structuralsearch.impl.matcher.filters.DefaultFilter;
import com.intellij.structuralsearch.impl.matcher.predicates.BinaryPredicate;
import com.intellij.structuralsearch.impl.matcher.predicates.NotPredicate;
import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate;
import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy;
import java.util.HashSet;
import java.util.Set;
/**
* Root of handlers for pattern node matching. Handles simplest type of the match.
*/
public abstract class MatchingHandler extends MatchPredicate {
protected NodeFilter filter;
private PsiElement pinnedElement;
public void setFilter(NodeFilter filter) {
this.filter = filter;
}
/**
* Matches given handler node against given value.
* @param matchedNode for matching
* @param context of the matching
* @return true if matching was successful and false otherwise
*/
public boolean match(PsiElement patternNode,PsiElement matchedNode, int start, int end, MatchContext context) {
return match(patternNode,matchedNode,context);
}
/**
* Matches given handler node against given value.
* @param matchedNode for matching
* @param context of the matching
* @return true if matching was successful and false otherwise
*/
public boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context) {
if (patternNode == null) {
return matchedNode == null;
}
return canMatch(patternNode, matchedNode);
}
public boolean canMatch(final PsiElement patternNode, final PsiElement matchedNode) {
if (filter!=null) {
return filter.accepts(matchedNode);
} else {
return DefaultFilter.accepts(patternNode, matchedNode);
}
}
public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) {
PsiElement patternElement;
MatchingHandler handler;
MatchingStrategy strategy = context.getPattern().getStrategy();
skipIfNecessary(nodes, nodes2, strategy);
skipIfNecessary(nodes2, nodes, strategy);
if (nodes2.hasNext() &&
(handler = context.getPattern().getHandler(nodes.current())).match(patternElement = nodes.current(),nodes2.current(),context)) {
nodes.advance();
if (shouldAdvanceTheMatchFor(patternElement, nodes2.current())) {
nodes2.advance();
skipIfNecessary(nodes, nodes2, strategy);
}
skipIfNecessary(nodes2, nodes, strategy);
if (nodes.hasNext()) {
final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current());
if (nextHandler.matchSequentially(nodes,nodes2,context)) {
// match was found!
return true;
} else {
// rewind, we was not able to match descendants
nodes.rewind();
nodes2.rewind();
}
} else {
// match was found
return handler.isMatchSequentiallySucceeded(nodes2);
}
}
return false;
}
private static void skipIfNecessary(NodeIterator nodes, NodeIterator nodes2, MatchingStrategy strategy) {
while (strategy.shouldSkip(nodes2.current(), nodes.current())) {
nodes2.advance();
}
}
protected boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) {
return !nodes2.hasNext();
}
private static MatchPredicate findRegExpPredicate(MatchPredicate start) {
if (start==null) return null;
if (start instanceof RegExpPredicate) return start;
if(start instanceof BinaryPredicate) {
BinaryPredicate binary = (BinaryPredicate)start;
final MatchPredicate result = findRegExpPredicate(binary.getFirst());
if (result!=null) return result;
return findRegExpPredicate(binary.getSecond());
} else if (start instanceof NotPredicate) {
return null;
}
return null;
}
public static RegExpPredicate getSimpleRegExpPredicate(SubstitutionHandler handler) {
if (handler == null) return null;
return (RegExpPredicate)findRegExpPredicate(handler.getPredicate());
}
static class ClearStateVisitor extends PsiRecursiveElementWalkingVisitor {
private CompiledPattern pattern;
ClearStateVisitor() {
super(true);
}
@Override public void visitElement(PsiElement element) {
// We do not reset certain handlers because they are also bound to higher level nodes
// e.g. Identifier handler in name is also bound to PsiMethod
if (pattern.isToResetHandler(element)) {
MatchingHandler handler = pattern.getHandlerSimple(element);
if (handler instanceof SubstitutionHandler) {
handler.reset();
}
}
super.visitElement(element);
}
synchronized void clearState(CompiledPattern _pattern, PsiElement el) {
pattern = _pattern;
el.acceptChildren(this);
pattern = null;
}
}
protected static ClearStateVisitor clearingVisitor = new ClearStateVisitor();
public boolean matchInAnyOrder(NodeIterator patternNodes, NodeIterator matchedNodes, final MatchContext context) {
final MatchResultImpl saveResult = context.hasResult() ? context.getResult() : null;
context.setResult(null);
try {
if (patternNodes.hasNext() && !matchedNodes.hasNext()) {
return validateSatisfactionOfHandlers(patternNodes, context);
}
Set<PsiElement> matchedElements = null;
for(; patternNodes.hasNext(); patternNodes.advance()) {
final PsiElement patternNode = patternNodes.current();
final CompiledPattern pattern = context.getPattern();
final MatchingHandler handler = pattern.getHandler(patternNode);
final PsiElement startMatching = matchedNodes.current();
do {
final PsiElement element = handler.getPinnedNode(null);
final PsiElement matchedNode = element != null ? element : matchedNodes.current();
if (element == null) matchedNodes.advance();
if (!matchedNodes.hasNext()) matchedNodes.reset();
if (matchedElements == null || !matchedElements.contains(matchedNode)) {
if (handler.match(patternNode, matchedNode, context)) {
if (matchedElements == null) matchedElements = new HashSet<PsiElement>();
matchedElements.add(matchedNode);
if (handler.shouldAdvanceThePatternFor(patternNode, matchedNode)) {
break;
}
} else if (element != null) {
return false;
}
// clear state of dependent objects
clearingVisitor.clearState(pattern, patternNode);
}
// passed of elements and does not found the match
if (startMatching == matchedNodes.current()) {
final boolean result = validateSatisfactionOfHandlers(patternNodes,context);
if (result && context.getMatchedElementsListener() != null) {
context.getMatchedElementsListener().matchedElements(matchedElements);
}
return result;
}
} while(true);
if (!handler.shouldAdvanceThePatternFor(patternNode, null)) {
patternNodes.rewind();
}
}
final boolean result = validateSatisfactionOfHandlers(patternNodes, context);
if (result && context.getMatchedElementsListener() != null) {
context.getMatchedElementsListener().matchedElements(matchedElements);
}
return result;
} finally {
if (saveResult!=null) {
if (context.hasResult()) {
saveResult.getMatches().addAll(context.getResult().getMatches());
}
context.setResult(saveResult);
}
}
}
protected static boolean validateSatisfactionOfHandlers(NodeIterator nodes, MatchContext context) {
while(nodes.hasNext()) {
final PsiElement element = nodes.current();
final MatchingHandler handler = context.getPattern().getHandler( element );
if (handler instanceof SubstitutionHandler) {
if (!((SubstitutionHandler)handler).validate(
context, StructuralSearchUtil.getProfileByPsiElement(element).getElementContextByPsi(element))) {
return false;
}
} else {
return false;
}
nodes.advance();
}
return true;
}
public NodeFilter getFilter() {
return filter;
}
public boolean shouldAdvanceThePatternFor(PsiElement patternElement, PsiElement matchedElement) {
return true;
}
public boolean shouldAdvanceTheMatchFor(PsiElement patternElement, PsiElement matchedElement) {
return true;
}
public void reset() {
//pinnedElement = null;
}
public PsiElement getPinnedNode(PsiElement context) {
return pinnedElement;
}
public void setPinnedElement(final PsiElement pinnedElement) {
this.pinnedElement = pinnedElement;
}
}