diff options
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java')
-rw-r--r-- | platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java new file mode 100644 index 000000000000..c53b08412b6b --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java @@ -0,0 +1,271 @@ +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; + } +}
\ No newline at end of file |