summaryrefslogtreecommitdiff
path: root/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers')
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java8
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java46
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java49
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java27
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchingHandler.java271
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java20
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java108
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java557
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java21
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java85
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java21
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java23
12 files changed, 1236 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java
new file mode 100644
index 000000000000..c95a85623753
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/DelegatingHandler.java
@@ -0,0 +1,8 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public interface DelegatingHandler {
+ MatchingHandler getDelegate();
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java
new file mode 100644
index 000000000000..34941cfa0257
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LightTopLevelMatchingHandler.java
@@ -0,0 +1,46 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+import com.intellij.dupLocator.iterators.NodeIterator;
+import org.jetbrains.annotations.NotNull;
+
+public final class LightTopLevelMatchingHandler extends MatchingHandler implements DelegatingHandler {
+ private final MatchingHandler myDelegate;
+
+ public LightTopLevelMatchingHandler(@NotNull MatchingHandler delegate) {
+ myDelegate = delegate;
+ }
+
+ public boolean match(final PsiElement patternNode, final PsiElement matchedNode, final MatchContext matchContext) {
+ return myDelegate.match(patternNode, matchedNode, matchContext);
+ }
+
+ @Override
+ public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) {
+ return myDelegate.canMatch(patternNode, matchedNode);
+ }
+
+ @Override
+ public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) {
+ return myDelegate.matchSequentially(nodes, nodes2, context);
+ }
+
+ public boolean match(final PsiElement patternNode,
+ final PsiElement matchedNode, final int start, final int end, final MatchContext context) {
+ return myDelegate.match(patternNode, matchedNode, start, end, context);
+ }
+
+ public boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) {
+ return true;
+ }
+
+ @Override
+ public boolean shouldAdvanceTheMatchFor(final PsiElement patternElement, final PsiElement matchedElement) {
+ return myDelegate.shouldAdvanceTheMatchFor(patternElement, matchedElement);
+ }
+
+ public MatchingHandler getDelegate() {
+ return myDelegate;
+ }
+} \ No newline at end of file
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java
new file mode 100644
index 000000000000..78967106755f
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/LiteralWithSubstitutionHandler.java
@@ -0,0 +1,49 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Maxim.Mossienko
+ * Date: Jun 30, 2004
+ * Time: 5:07:33 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class LiteralWithSubstitutionHandler extends MatchingHandler {
+ private final String matchExpression;
+ private Matcher matcher;
+ private final List<SubstitutionHandler> handlers;
+
+ public LiteralWithSubstitutionHandler(String _matchedExpression,List<SubstitutionHandler> _handlers) {
+ matchExpression = _matchedExpression;
+ handlers = _handlers;
+ }
+
+ public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) {
+ final String text = RegExpPredicate.getMeaningfulText(matchedNode);
+ int offset = matchedNode.getText().indexOf(text);
+ if (matcher==null) {
+ matcher = Pattern.compile(matchExpression).matcher(text);
+ } else {
+ matcher.reset(text);
+ }
+
+ while (matcher.find()) {
+ for (int i = 0; i < handlers.size(); ++i) {
+ SubstitutionHandler handler = handlers.get(i);
+
+ if (!handler.handle(matchedNode,offset + matcher.start(i+1), offset + matcher.end(i+1),context)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java
new file mode 100644
index 000000000000..cba96681ab71
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/MatchPredicate.java
@@ -0,0 +1,27 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+
+/**
+ * Root of handlers for pattern node matching. Handles simpliest type of the match.
+ */
+public abstract class MatchPredicate {
+ /**
+ * Matches given handler node against given value.
+ * @param matchedNode for matching
+ * @param context of the matching
+ * @return true if matching was successfull 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 successfull and false otherwise
+ */
+ public abstract boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context);
+}
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
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java
new file mode 100644
index 000000000000..3dffe792e308
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SimpleHandler.java
@@ -0,0 +1,20 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+
+/**
+ * Root of handlers for pattern node matching. Handles simpliest type of the match.
+ */
+public final class SimpleHandler extends MatchingHandler {
+ /**
+ * Matches given handler node against given value.
+ * @param matchedNode for matching
+ * @param context of the matching
+ * @return true if matching was successfull and false otherwise
+ */
+ public boolean match(PsiElement patternNode,PsiElement matchedNode, MatchContext context) {
+ if (!super.match(patternNode,matchedNode,context)) return false;
+ return context.getMatcher().match(patternNode,matchedNode);
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java
new file mode 100644
index 000000000000..389c4a7916c2
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SkippingHandler.java
@@ -0,0 +1,108 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.dupLocator.equivalence.EquivalenceDescriptor;
+import com.intellij.dupLocator.iterators.NodeIterator;
+import com.intellij.dupLocator.util.DuplocatorUtil;
+import com.intellij.dupLocator.util.NodeFilter;
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene.Kudelevsky
+ */
+public class SkippingHandler extends MatchingHandler implements DelegatingHandler {
+
+ private final MatchingHandler myDelegate;
+
+ public SkippingHandler(@NotNull MatchingHandler delegate) {
+ myDelegate = delegate;
+ }
+
+ public boolean match(PsiElement patternNode, PsiElement matchedNode, final MatchContext matchContext) {
+ if (patternNode == null || matchedNode == null || matchedNode.getClass() == patternNode.getClass()) {
+ return myDelegate.match(patternNode, matchedNode, matchContext);
+ }
+
+ /*if (patternNode != null && matchedNode != null && patternNode.getClass() == matchedNode.getClass()) {
+ //return myDelegate.match(patternNode, matchedNode, matchContext);
+ }*/
+ PsiElement newPatternNode = skipNodeIfNeccessary(patternNode);
+ matchedNode = skipNodeIfNeccessary(matchedNode);
+
+ if (newPatternNode != patternNode) {
+ return matchContext.getPattern().getHandler(newPatternNode).match(newPatternNode, matchedNode, matchContext);
+ }
+
+ return myDelegate.match(patternNode, matchedNode, matchContext);
+ }
+
+ @Override
+ public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) {
+ return myDelegate.canMatch(patternNode, matchedNode);
+ }
+
+ @Override
+ public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) {
+ return myDelegate.matchSequentially(nodes, nodes2, context);
+ }
+
+ public boolean match(PsiElement patternNode,
+ PsiElement matchedNode,
+ final int start,
+ final int end,
+ final MatchContext context) {
+ if (patternNode == null || matchedNode == null || patternNode.getClass() == matchedNode.getClass()) {
+ return myDelegate.match(patternNode, matchedNode, start, end, context);
+ }
+
+ PsiElement newPatternNode = skipNodeIfNeccessary(patternNode);
+ matchedNode = skipNodeIfNeccessary(matchedNode);
+
+ if (newPatternNode != patternNode) {
+ return context.getPattern().getHandler(newPatternNode).match(newPatternNode, matchedNode, start, end, context);
+ }
+
+ return myDelegate.match(patternNode, matchedNode, start, end, context);
+ }
+
+ protected boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) {
+ return myDelegate.isMatchSequentiallySucceeded(nodes2);
+ }
+
+ @Override
+ public boolean shouldAdvanceTheMatchFor(PsiElement patternElement, PsiElement matchedElement) {
+ return true;
+ }
+
+ public MatchingHandler getDelegate() {
+ return myDelegate;
+ }
+
+ @Nullable
+ public static PsiElement getOnlyNonWhitespaceChild(PsiElement element) {
+ PsiElement onlyChild = null;
+ for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (DuplocatorUtil.isIgnoredNode(element) || child.getTextLength() == 0) {
+ continue;
+ }
+ if (onlyChild != null) {
+ return null;
+ }
+ onlyChild = child;
+ }
+ return onlyChild;
+ }
+
+ @Nullable
+ public static PsiElement skipNodeIfNeccessary(PsiElement element) {
+ return skipNodeIfNeccessary(element, null, null);
+ }
+
+ @Nullable
+ public static PsiElement skipNodeIfNeccessary(PsiElement element, EquivalenceDescriptor descriptor, NodeFilter filter) {
+ return DuplocatorUtil.skipNodeIfNeccessary(element, descriptor, filter != null ? filter : LexicalNodesFilter.getInstance());
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java
new file mode 100644
index 000000000000..0c84e3bb2436
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SubstitutionHandler.java
@@ -0,0 +1,557 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.dupLocator.iterators.FilteringNodeIterator;
+import com.intellij.dupLocator.iterators.NodeIterator;
+import com.intellij.dupLocator.util.NodeFilter;
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.MatchResult;
+import com.intellij.structuralsearch.StructuralSearchProfile;
+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.plugin.ui.Configuration;
+import com.intellij.structuralsearch.plugin.util.SmartPsiPointer;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Matching handler that manages substitutions matching
+ */
+public class SubstitutionHandler extends MatchingHandler {
+ private final String name;
+ private final int maxOccurs;
+ private final int minOccurs;
+ private final boolean greedy;
+ private boolean target;
+ private MatchPredicate predicate;
+ private MatchingHandler matchHandler;
+ private boolean subtype;
+ private boolean strictSubtype;
+ // matchedOccurs + 1 = number of item being matched
+ private int matchedOccurs;
+ private int totalMatchedOccurs = -1;
+ private MatchResultImpl myNestedResult;
+
+ private static final NodeFilter VARS_DELIM_FILTER = new NodeFilter() {
+ @Override
+ public boolean accepts(PsiElement element) {
+ if (element == null) {
+ return false;
+ }
+
+ final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(element);
+ if (profile == null) {
+ return false;
+ }
+
+ return profile.canBeVarDelimeter(element);
+ }
+ };
+
+ public SubstitutionHandler(final String name, final boolean target, int minOccurs,
+ int maxOccurs, boolean greedy) {
+ this.name = name;
+ this.maxOccurs = maxOccurs;
+ this.minOccurs = minOccurs;
+ this.target = target;
+ this.greedy = greedy;
+ }
+
+ public SubstitutionHandler(final SubstitutionHandler substitutionHandler) {
+ this(substitutionHandler.getName(),substitutionHandler.isTarget(), substitutionHandler.getMinOccurs(),
+ substitutionHandler.getMaxOccurs(), substitutionHandler.greedy);
+ }
+
+ public boolean isSubtype() {
+ return subtype;
+ }
+
+ public boolean isStrictSubtype() {
+ return strictSubtype;
+ }
+
+ public void setStrictSubtype(boolean strictSubtype) {
+ this.strictSubtype = strictSubtype;
+ }
+
+ public void setSubtype(boolean subtype) {
+ this.subtype = subtype;
+ }
+
+ public void setPredicate(MatchPredicate handler) {
+ predicate = handler;
+ }
+
+ // Matcher
+
+ public MatchPredicate getPredicate() {
+ return predicate;
+ }
+
+ private static boolean validateOneMatch(final PsiElement match, int start, int end, final MatchResultImpl result, final MatchContext matchContext) {
+ final boolean matchresult;
+
+ if (match!=null) {
+ if (start==0 && end==-1 && result.getStart()==0 && result.getEnd()==-1) {
+ matchresult = matchContext.getMatcher().match(match,result.getMatchRef().getElement());
+ } else {
+ matchresult = StructuralSearchUtil.getProfileByPsiElement(match).getText(match, start, end).equals(
+ result.getMatchImage()
+ );
+ }
+ } else {
+ matchresult = result.isMatchImageNull();
+ }
+
+ return matchresult;
+ }
+
+ public boolean validate(final PsiElement match, int start, int end, MatchContext context) {
+ if (predicate!=null) {
+ if(!predicate.match(null,match,start,end,context)) return false;
+ }
+
+ if (maxOccurs==0) {
+ totalMatchedOccurs++;
+ return false;
+ }
+
+ MatchResultImpl result = context.getResult().findSon(name);
+
+ if (result == null && context.getPreviousResult() != null) {
+ result = context.getPreviousResult().findSon(name);
+ }
+
+ if (result!=null) {
+ if (minOccurs == 1 && maxOccurs == 1) {
+ // check if they are the same
+ return validateOneMatch(match, start, end, result,context);
+ } else if (maxOccurs > 1 && totalMatchedOccurs!=-1) {
+ final int size = result.getAllSons().size();
+ if (matchedOccurs >= size) {
+ return false;
+ }
+ result = size == 0 ?result:(MatchResultImpl)result.getAllSons().get(matchedOccurs);
+ // check if they are the same
+ return validateOneMatch(match, start, end, result, context);
+ }
+ }
+
+ return true;
+ }
+
+ public boolean match(final PsiElement node, final PsiElement match, MatchContext context) {
+ if (!super.match(node,match,context)) return false;
+
+ return matchHandler == null ?
+ context.getMatcher().match(node, match):
+ matchHandler.match(node,match,context);
+ }
+
+ public boolean handle(final PsiElement match, MatchContext context) {
+ return handle(match,0,-1,context);
+ }
+
+ public void addResult(PsiElement match,int start, int end,MatchContext context) {
+ if (totalMatchedOccurs == -1) {
+ final MatchResultImpl matchResult = context.getResult();
+ final MatchResultImpl substitution = matchResult.findSon(name);
+
+ if (substitution == null) {
+ matchResult.addSon( createMatch(match,start,end) );
+ } else if (maxOccurs > 1) {
+ final MatchResultImpl result = createMatch(match,start,end);
+
+ if (!substitution.isMultipleMatch()) {
+ // adding intermediate node to contain all multiple matches
+ MatchResultImpl sonresult = new MatchResultImpl(
+ substitution.getName(),
+ substitution.getMatchImage(),
+ substitution.getMatchRef(),
+ substitution.getStart(),
+ substitution.getEnd(),
+ target
+ );
+
+ sonresult.setParent(substitution);
+ substitution.setMatchRef(
+ new SmartPsiPointer(match == null ? null : match)
+ );
+
+ substitution.setMultipleMatch(true);
+
+ if (substitution.isScopeMatch()) {
+ substitution.setScopeMatch(false);
+ sonresult.setScopeMatch(true);
+ for(MatchResult r:substitution.getAllSons()) sonresult.addSon((MatchResultImpl)r);
+ substitution.clearMatches();
+ }
+
+ substitution.addSon( sonresult);
+ }
+
+ result.setParent(substitution);
+ substitution.addSon( result );
+ }
+ }
+ }
+
+ public boolean handle(final PsiElement match, int start, int end, MatchContext context) {
+ if (!validate(match,start,end,context)) {
+ myNestedResult = null;
+
+ //if (maxOccurs==1 && minOccurs==1) {
+ // if (context.hasResult()) context.getResult().removeSon(name);
+ //}
+ // @todo we may fail fast the match by throwing an exception
+
+ return false;
+ }
+
+ if (!Configuration.CONTEXT_VAR_NAME.equals(name)) addResult(match, start, end, context);
+
+ return true;
+ }
+
+ private MatchResultImpl createMatch(final PsiElement match, int start, int end) {
+ final String image = match == null ? null : StructuralSearchUtil.getProfileByPsiElement(match).getText(match, start, end);
+ final SmartPsiPointer ref = new SmartPsiPointer(match);
+
+ final MatchResultImpl result = myNestedResult == null ? new MatchResultImpl(
+ name,
+ image,
+ ref,
+ start,
+ end,
+ target
+ ) : myNestedResult;
+
+ if (myNestedResult != null) {
+ myNestedResult.setName( name );
+ myNestedResult.setMatchImage( image );
+ myNestedResult.setMatchRef( ref );
+ myNestedResult.setStart( start );
+ myNestedResult.setEnd( end );
+ myNestedResult.setTarget( target );
+ myNestedResult = null;
+ }
+
+ return result;
+ }
+
+ boolean validate(MatchContext context, Class elementContext) {
+ MatchResult substitution = context.getResult().findSon(name);
+
+ if (minOccurs >= 1 &&
+ ( substitution == null ||
+ StructuralSearchUtil.getProfileByFileType(context.getOptions().getFileType()).getElementContextByPsi(substitution.getMatchRef().getElement()) != elementContext
+ )
+ ) {
+ return false;
+ } else if (maxOccurs <= 1 &&
+ substitution!=null && substitution.hasSons()
+ ) {
+ return false;
+ } else if (maxOccurs==0 && totalMatchedOccurs!=-1) {
+ return false;
+ }
+ return true;
+ }
+
+ public int getMinOccurs() {
+ return minOccurs;
+ }
+
+ public int getMaxOccurs() {
+ return maxOccurs;
+ }
+
+ private void removeLastResults(int numberOfResults, MatchContext context) {
+ if (numberOfResults == 0) return;
+ final MatchResultImpl substitution = context.getResult().findSon(name);
+
+ if (substitution!=null) {
+ final List<PsiElement> matchedNodes = context.getMatchedNodes();
+
+ if (substitution.hasSons()) {
+ final List<MatchResult> sons = substitution.getMatches();
+
+ while(numberOfResults > 0) {
+ --numberOfResults;
+ final MatchResult matchResult = sons.remove(sons.size() - 1);
+ if (matchedNodes != null) matchedNodes.remove(matchResult.getMatch());
+ }
+
+ if (sons.isEmpty()) {
+ context.getResult().removeSon(name);
+ }
+ } else {
+ final MatchResultImpl matchResult = context.getResult().removeSon(name);
+ if (matchedNodes != null) matchedNodes.remove(matchResult.getMatch());
+ }
+ }
+ }
+
+ 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()) {
+ int matchedOccurs = 0;
+ 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 (matchedOccurs <= maxOccurs &&
+ (matchedElements == null || !matchedElements.contains(matchedNode))) {
+
+ if (handler.match(patternNode, matchedNode, context)) {
+ ++matchedOccurs;
+ 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) &&
+ matchedOccurs >= minOccurs && matchedOccurs <= maxOccurs;
+ 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);
+ }
+ }
+ }
+
+ public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) {
+ return doMatchSequentially(nodes, nodes2, context);
+ }
+
+ protected boolean doMatchSequentiallyBySimpleHandler(NodeIterator nodes, NodeIterator nodes2, MatchContext context) {
+ final boolean oldValue = context.shouldRecursivelyMatch();
+ context.setShouldRecursivelyMatch(false);
+ final boolean result = super.matchSequentially(nodes, nodes2, context);
+ context.setShouldRecursivelyMatch(oldValue);
+ return result;
+ }
+
+ protected boolean doMatchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) {
+ final int previousMatchedOccurs = matchedOccurs;
+
+ FilteringNodeIterator fNodes2 = new FilteringNodeIterator(nodes2, VARS_DELIM_FILTER);
+
+ try {
+ MatchingHandler handler = context.getPattern().getHandler(nodes.current());
+ matchedOccurs = 0;
+
+ boolean flag = false;
+
+ while(fNodes2.hasNext() && matchedOccurs < minOccurs) {
+ if (handler.match(nodes.current(), nodes2.current(), context)) {
+ ++matchedOccurs;
+ } else {
+ break;
+ }
+ fNodes2.advance();
+ flag = true;
+ }
+
+ if (matchedOccurs!=minOccurs) {
+ // failed even for min occurs
+ removeLastResults(matchedOccurs, context);
+ fNodes2.rewind(matchedOccurs);
+ return false;
+ }
+
+ if (greedy) {
+ // go greedily to maxOccurs
+
+ while(fNodes2.hasNext() && matchedOccurs < maxOccurs) {
+ if (handler.match(nodes.current(), nodes2.current(), context)) {
+ ++matchedOccurs;
+ } else {
+ // no more matches could take!
+ break;
+ }
+ fNodes2.advance();
+ flag = true;
+ }
+
+ if (flag) {
+ fNodes2.rewind();
+ nodes2.advance();
+ }
+
+ nodes.advance();
+
+ if (nodes.hasNext()) {
+ final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current());
+
+ while(matchedOccurs >= minOccurs) {
+ if (nextHandler.matchSequentially(nodes, nodes2, context)) {
+ totalMatchedOccurs = matchedOccurs;
+ // match found
+ return true;
+ }
+
+ if (matchedOccurs > 0) {
+ nodes2.rewind();
+ removeLastResults(1,context);
+ }
+ --matchedOccurs;
+ }
+
+ if (matchedOccurs > 0) {
+ removeLastResults(matchedOccurs, context);
+ }
+ nodes.rewind();
+ return false;
+ } else {
+ // match found
+ if (handler.isMatchSequentiallySucceeded(nodes2)) {
+ return checkSameOccurrencesConstraint();
+ }
+ removeLastResults(matchedOccurs, context);
+ return false;
+ }
+ } else {
+ nodes.advance();
+
+ if (flag) {
+ fNodes2.rewind();
+ nodes2.advance();
+ }
+
+ if (nodes.hasNext()) {
+ final MatchingHandler nextHandler = context.getPattern().getHandler(nodes.current());
+
+ flag = false;
+
+ while(nodes2.hasNext() && matchedOccurs <= maxOccurs) {
+ if (nextHandler.matchSequentially(nodes, nodes2, context)) {
+ return checkSameOccurrencesConstraint();
+ }
+
+ if (flag) {
+ nodes2.rewind();
+ fNodes2.advance();
+ }
+
+ if (handler.match(nodes.current(), nodes2.current(), context)) {
+ matchedOccurs++;
+ } else {
+ nodes.rewind();
+ removeLastResults(matchedOccurs,context);
+ return false;
+ }
+ nodes2.advance();
+ flag = true;
+ }
+
+ nodes.rewind();
+ removeLastResults(matchedOccurs,context);
+ return false;
+ } else {
+ return checkSameOccurrencesConstraint();
+ }
+ }
+ } finally {
+ matchedOccurs = previousMatchedOccurs;
+ }
+ }
+
+ private boolean checkSameOccurrencesConstraint() {
+ if (totalMatchedOccurs == -1) {
+ totalMatchedOccurs = matchedOccurs;
+ return true;
+ }
+ else {
+ return totalMatchedOccurs == matchedOccurs;
+ }
+ }
+
+ public void setTarget(boolean target) {
+ this.target = target;
+ }
+
+ public MatchingHandler getMatchHandler() {
+ return matchHandler;
+ }
+
+ public void setMatchHandler(MatchingHandler matchHandler) {
+ this.matchHandler = matchHandler;
+ }
+
+ public boolean isTarget() {
+ return target;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void reset() {
+ super.reset();
+ totalMatchedOccurs = -1;
+ }
+
+ public boolean shouldAdvanceThePatternFor(PsiElement patternElement, PsiElement matchedElement) {
+ if(maxOccurs > 1) return false;
+ return super.shouldAdvanceThePatternFor(patternElement,matchedElement);
+ }
+
+ public void setNestedResult(final MatchResultImpl nestedResult) {
+ myNestedResult = nestedResult;
+ }
+
+ public MatchResultImpl getNestedResult() {
+ return myNestedResult;
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java
new file mode 100644
index 000000000000..4d1f9a852940
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/SymbolHandler.java
@@ -0,0 +1,21 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+
+/**
+ * Search handler for symbol search
+ */
+public class SymbolHandler extends MatchingHandler {
+ private final SubstitutionHandler handler;
+
+ public SymbolHandler(SubstitutionHandler handler) {
+ this.handler = handler;
+ }
+
+ public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) {
+ // there is no need to do filtering since this is delegate of Substituion handler
+
+ return handler.handle(matchedNode,context);
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java
new file mode 100644
index 000000000000..6711bcb1fc7f
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TopLevelMatchingHandler.java
@@ -0,0 +1,85 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.dupLocator.iterators.NodeIterator;
+import com.intellij.dupLocator.iterators.SiblingNodeIterator;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.StructuralSearchUtil;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class TopLevelMatchingHandler extends MatchingHandler implements DelegatingHandler {
+ private final MatchingHandler delegate;
+
+ public TopLevelMatchingHandler(@NotNull MatchingHandler _delegate) {
+ delegate = _delegate;
+ setFilter(_delegate.getFilter());
+ }
+
+ public boolean match(final PsiElement patternNode, final PsiElement matchedNode, final MatchContext matchContext) {
+ final boolean matched = delegate.match(patternNode, matchedNode, matchContext);
+
+ if (matched) {
+ List<PsiElement> matchedNodes = matchContext.getMatchedNodes();
+ if (matchedNodes == null) {
+ matchedNodes = new ArrayList<PsiElement>();
+ matchContext.setMatchedNodes(matchedNodes);
+ }
+
+ PsiElement elementToAdd = matchedNode;
+
+ if (patternNode instanceof PsiComment && StructuralSearchUtil.isDocCommentOwner(matchedNode)) {
+ // psicomment and psidoccomment are placed inside the psimember next to them so
+ // simple topdown matching should do additional "dances" to cover this case.
+ elementToAdd = matchedNode.getFirstChild();
+ assert elementToAdd instanceof PsiComment;
+ }
+
+ matchedNodes.add(elementToAdd);
+ }
+
+ if ((!matched || matchContext.getOptions().isRecursiveSearch()) &&
+ matchContext.getPattern().getStrategy().continueMatching(matchedNode) &&
+ matchContext.shouldRecursivelyMatch()
+ ) {
+ matchContext.getMatcher().matchContext(
+ new SsrFilteringNodeIterator(
+ new SiblingNodeIterator(matchedNode.getFirstChild())
+ )
+ );
+ }
+ return matched;
+ }
+
+ @Override
+ public boolean canMatch(PsiElement patternNode, PsiElement matchedNode) {
+ return delegate.canMatch(patternNode, matchedNode);
+ }
+
+ @Override
+ public boolean matchSequentially(final NodeIterator nodes, final NodeIterator nodes2, final MatchContext context) {
+ return delegate.matchSequentially(nodes, nodes2, context);
+ }
+
+ public boolean match(final PsiElement patternNode,
+ final PsiElement matchedNode, final int start, final int end, final MatchContext context) {
+ return match(patternNode, matchedNode, context);
+ }
+
+ public boolean isMatchSequentiallySucceeded(final NodeIterator nodes2) {
+ return true;
+ }
+
+ @Override
+ public boolean shouldAdvanceTheMatchFor(final PsiElement patternElement, final PsiElement matchedElement) {
+ return delegate.shouldAdvanceTheMatchFor(patternElement, matchedElement);
+ }
+
+ public MatchingHandler getDelegate() {
+ return delegate;
+ }
+} \ No newline at end of file
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java
new file mode 100644
index 000000000000..08e335614d5a
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/TypedSymbolHandler.java
@@ -0,0 +1,21 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiDeclarationStatement;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+
+/**
+ * Search handler for typed symbol ('T<a*>)
+ */
+public class TypedSymbolHandler extends MatchingHandler {
+ public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) {
+ if (!super.match(patternNode,matchedNode,context)) {
+ return false;
+ }
+
+ return context.getMatcher().match(
+ patternNode.getFirstChild(),
+ matchedNode
+ );
+ }
+}
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java
new file mode 100644
index 000000000000..cc71c02ee403
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/handlers/XmlTextHandler.java
@@ -0,0 +1,23 @@
+package com.intellij.structuralsearch.impl.matcher.handlers;
+
+import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
+import com.intellij.dupLocator.iterators.NodeIterator;
+import com.intellij.psi.PsiElement;
+import com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor;
+import com.intellij.structuralsearch.impl.matcher.MatchContext;
+import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
+
+/**
+ * Root of handlers for pattern node matching. Handles simpliest type of the match.
+ */
+public final class XmlTextHandler extends MatchingHandler {
+ public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2, MatchContext context) {
+ final PsiElement psiElement = nodes.current();
+
+ return GlobalMatchingVisitor.continueMatchingSequentially(
+ new SsrFilteringNodeIterator( new ArrayBackedNodeIterator(psiElement.getChildren()) ),
+ nodes2,
+ context
+ );
+ }
+}