summaryrefslogtreecommitdiff
path: root/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java
diff options
context:
space:
mode:
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.java271
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