summaryrefslogtreecommitdiff
path: root/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java')
-rw-r--r--platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java315
1 files changed, 315 insertions, 0 deletions
diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java
new file mode 100644
index 000000000000..5577db0d8540
--- /dev/null
+++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/GlobalMatchingVisitor.java
@@ -0,0 +1,315 @@
+package com.intellij.structuralsearch.impl.matcher;
+
+import com.intellij.dupLocator.AbstractMatchingVisitor;
+import com.intellij.dupLocator.iterators.NodeIterator;
+import com.intellij.dupLocator.util.NodeFilter;
+import com.intellij.lang.Language;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.structuralsearch.MatchResult;
+import com.intellij.structuralsearch.StructuralSearchProfile;
+import com.intellij.structuralsearch.StructuralSearchUtil;
+import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
+import com.intellij.structuralsearch.impl.matcher.handlers.DelegatingHandler;
+import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
+import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
+import com.intellij.structuralsearch.plugin.ui.Configuration;
+import com.intellij.structuralsearch.plugin.util.SmartPsiPointer;
+import com.intellij.util.containers.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Visitor class to manage pattern matching
+ */
+@SuppressWarnings({"RefusedBequest"})
+public class GlobalMatchingVisitor extends AbstractMatchingVisitor {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor");
+
+ // the pattern element for visitor check
+ private PsiElement myElement;
+
+ // the result of matching in visitor
+ private boolean myResult;
+
+ // context of matching
+ private MatchContext matchContext;
+
+ private MatchingHandler myLastHandler;
+
+ private Map<Language, PsiElementVisitor> myLanguage2MatchingVisitor = new HashMap<Language, PsiElementVisitor>(1);
+
+ public PsiElement getElement() {
+ return myElement;
+ }
+
+ public boolean getResult() {
+ return myResult;
+ }
+
+ public void setResult(boolean result) {
+ this.myResult = result;
+ }
+
+ public MatchContext getMatchContext() {
+ return matchContext;
+ }
+
+ @Override
+ protected boolean doMatchInAnyOrder(NodeIterator elements, NodeIterator elements2) {
+ return matchContext.getPattern().getHandler(elements.current()).matchInAnyOrder(
+ elements,
+ elements2,
+ matchContext
+ );
+ }
+
+ @NotNull
+ @Override
+ protected NodeFilter getNodeFilter() {
+ return LexicalNodesFilter.getInstance();
+ }
+
+ public final boolean handleTypedElement(final PsiElement typedElement, final PsiElement match) {
+ MatchingHandler handler = matchContext.getPattern().getHandler(typedElement);
+ final MatchingHandler initialHandler = handler;
+ if (handler instanceof DelegatingHandler) {
+ handler = ((DelegatingHandler)handler).getDelegate();
+ }
+ assert handler instanceof SubstitutionHandler :
+ handler != null ? handler.getClass() : "null" + ' ' + (initialHandler != null ? initialHandler.getClass() : "null");
+
+ return ((SubstitutionHandler)handler).handle(match, matchContext);
+ }
+
+ /**
+ * Identifies the match between given element of program tree and pattern element
+ *
+ * @param el1 the pattern for matching
+ * @param el2 the tree element for matching
+ * @return true if equal and false otherwise
+ */
+ public boolean match(final PsiElement el1, final PsiElement el2) {
+ // null
+ if (el1 == el2) return true;
+ if (el2 == null || el1 == null) {
+ // this a bug!
+ return false;
+ }
+
+ // copy changed data to local stack
+ PsiElement prevElement = myElement;
+ myElement = el2;
+
+ try {
+ /*if (el1 instanceof XmlElement) {
+ el1.accept(myXmlVisitor);
+ }
+ else {
+ el1.accept(myJavaVisitor);
+ }*/
+ PsiElementVisitor visitor = getVisitorForElement(el1);
+ if (visitor != null) {
+ el1.accept(visitor);
+ }
+ }
+ catch (ClassCastException ex) {
+ myResult = false;
+ }
+ finally {
+ myElement = prevElement;
+ }
+
+ return myResult;
+ }
+
+ @Nullable
+ private PsiElementVisitor getVisitorForElement(PsiElement element) {
+ Language language = element.getLanguage();
+ PsiElementVisitor visitor = myLanguage2MatchingVisitor.get(language);
+ if (visitor == null) {
+ visitor = createMatchingVisitor(language);
+ myLanguage2MatchingVisitor.put(language, visitor);
+ }
+ return visitor;
+ }
+
+ @Nullable
+ private PsiElementVisitor createMatchingVisitor(Language language) {
+ StructuralSearchProfile profile = StructuralSearchUtil.getProfileByLanguage(language);
+ if (profile == null) {
+ LOG.warn("there is no StructuralSearchProfile for language " + language.getID());
+ return null;
+ }
+ else {
+ return profile.createMatchingVisitor(this);
+ }
+ }
+
+ /**
+ * Matches tree segments starting with given elements to find equality
+ *
+ * @param nodes the pattern element for matching
+ * @param nodes2 the tree element for matching
+ * @return if they are equal and false otherwise
+ */
+ public boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2) {
+ if (!nodes.hasNext()) {
+ return nodes.hasNext() == nodes2.hasNext();
+ }
+
+ myLastHandler = matchContext.getPattern().getHandler(nodes.current());
+ return myLastHandler.matchSequentially(
+ nodes,
+ nodes2,
+ matchContext
+ );
+ }
+
+ public static boolean continueMatchingSequentially(final NodeIterator nodes, final NodeIterator nodes2, MatchContext matchContext) {
+ if (!nodes.hasNext()) {
+ return nodes.hasNext() == nodes2.hasNext();
+ }
+
+ return matchContext.getPattern().getHandler(nodes.current()).matchSequentially(
+ nodes,
+ nodes2,
+ matchContext
+ );
+ }
+
+ /**
+ * Descents the tree in depth finding matches
+ *
+ * @param elements the element for which the sons are looked for match
+ */
+ public void matchContext(final NodeIterator elements) {
+ final CompiledPattern pattern = matchContext.getPattern();
+ final NodeIterator patternNodes = pattern.getNodes().clone();
+ final MatchResultImpl saveResult = matchContext.hasResult() ? matchContext.getResult() : null;
+ final List<PsiElement> saveMatchedNodes = matchContext.getMatchedNodes();
+
+ try {
+ matchContext.setResult(null);
+ matchContext.setMatchedNodes(null);
+
+ if (!patternNodes.hasNext()) return;
+ final MatchingHandler firstMatchingHandler = pattern.getHandler(patternNodes.current());
+
+ for (; elements.hasNext(); elements.advance()) {
+ final PsiElement elementNode = elements.current();
+
+ boolean matched = firstMatchingHandler.matchSequentially(patternNodes, elements, matchContext);
+
+ if (matched) {
+ MatchingHandler matchingHandler = matchContext.getPattern().getHandler(Configuration.CONTEXT_VAR_NAME);
+ if (matchingHandler != null) {
+ matched = ((SubstitutionHandler)matchingHandler).handle(elementNode, matchContext);
+ }
+ }
+
+ final List<PsiElement> matchedNodes = matchContext.getMatchedNodes();
+
+ if (matched) {
+ dispatchMatched(matchedNodes, matchContext.getResult());
+ }
+
+ matchContext.setMatchedNodes(null);
+ matchContext.setResult(null);
+
+ patternNodes.reset();
+ if (matchedNodes != null && matchedNodes.size() > 0 && matched) {
+ elements.rewind();
+ }
+ }
+ }
+ finally {
+ matchContext.setResult(saveResult);
+ matchContext.setMatchedNodes(saveMatchedNodes);
+ }
+ }
+
+ private void dispatchMatched(final List<PsiElement> matchedNodes, MatchResultImpl result) {
+ if (!matchContext.getOptions().isResultIsContextMatch() && doDispatch(result, result)) return;
+
+ // There is no substitutions so show the context
+
+ processNoSubstitutionMatch(matchedNodes, result);
+ matchContext.getSink().newMatch(result);
+ }
+
+ private boolean doDispatch(final MatchResultImpl result, MatchResultImpl context) {
+ boolean ret = false;
+
+ for (MatchResult _r : result.getAllSons()) {
+ final MatchResultImpl r = (MatchResultImpl)_r;
+
+ if ((r.isScopeMatch() && !r.isTarget()) || r.isMultipleMatch()) {
+ ret |= doDispatch(r, context);
+ }
+ else if (r.isTarget()) {
+ r.setContext(context);
+ matchContext.getSink().newMatch(r);
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ private static void processNoSubstitutionMatch(List<PsiElement> matchedNodes, MatchResultImpl result) {
+ boolean complexMatch = matchedNodes.size() > 1;
+ final PsiElement match = matchedNodes.get(0);
+
+ if (!complexMatch) {
+ result.setMatchRef(new SmartPsiPointer(match));
+ result.setMatchImage(match.getText());
+ }
+ else {
+ MatchResultImpl sonresult;
+
+ for (final PsiElement matchStatement : matchedNodes) {
+ result.getMatches().add(
+ sonresult = new MatchResultImpl(
+ MatchResult.LINE_MATCH,
+ matchStatement.getText(),
+ new SmartPsiPointer(matchStatement),
+ true
+ )
+ );
+
+ sonresult.setParent(result);
+ }
+
+ result.setMatchRef(
+ new SmartPsiPointer(match)
+ );
+ result.setMatchImage(
+ match.getText()
+ );
+ result.setName(MatchResult.MULTI_LINE_MATCH);
+ }
+ }
+
+ public void setMatchContext(MatchContext matchContext) {
+ this.matchContext = matchContext;
+ }
+
+ // Matches the sons of given elements to find equality
+ // @param el1 the pattern element for matching
+ // @param el2 the tree element for matching
+ // @return if they are equal and false otherwise
+
+ @Override
+ protected boolean isLeftLooseMatching() {
+ return matchContext.getOptions().isLooseMatching();
+ }
+
+ @Override
+ protected boolean isRightLooseMatching() {
+ return false;
+ }
+}