summaryrefslogtreecommitdiff
path: root/plugins/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/XmlMatchingVisitor.java
blob: d2e1e210534b44119f1819c9036c2152f9427f38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.intellij.structuralsearch.impl.matcher;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.xml.*;
import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;

import java.util.ArrayList;
import java.util.List;

/**
* @author Eugene.Kudelevsky
*/
public class XmlMatchingVisitor extends XmlElementVisitor {
  private final GlobalMatchingVisitor myMatchingVisitor;
  private final boolean myCaseSensitive;

  public XmlMatchingVisitor(GlobalMatchingVisitor matchingVisitor) {
    myMatchingVisitor = matchingVisitor;
    myCaseSensitive = myMatchingVisitor.getMatchContext().getOptions().isCaseSensitiveMatch();
  }

  @Override
  public void visitElement(final PsiElement element) {
    myMatchingVisitor.setResult(element.textMatches(element));
  }

  @Override public void visitXmlAttribute(XmlAttribute attribute) {
    final XmlAttribute another = (XmlAttribute)myMatchingVisitor.getElement();
    final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(attribute.getName());

    myMatchingVisitor.setResult(matches(attribute.getName(), another.getName()) || isTypedVar);
    if (myMatchingVisitor.getResult()) {
      myMatchingVisitor.setResult(myMatchingVisitor.match(attribute.getValueElement(), another.getValueElement()));
    }

    if (myMatchingVisitor.getResult() && isTypedVar) {
      MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(attribute.getName());
      myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(another, myMatchingVisitor.getMatchContext()));
    }
  }

  @Override public void visitXmlAttributeValue(XmlAttributeValue value) {
    final XmlAttributeValue another = (XmlAttributeValue)myMatchingVisitor.getElement();
    final String text = value.getValue();

    final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(text);
    MatchingHandler handler;

    if (isTypedVar && (handler = myMatchingVisitor.getMatchContext().getPattern().getHandler( text )) instanceof SubstitutionHandler) {
      String text2 = another.getText();
      int offset = text2.length() > 0 && ( text2.charAt(0) == '"' || text2.charAt(0) == '\'') ? 1:0;
      myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(another, offset, text2.length() - offset,
                                                                        myMatchingVisitor.getMatchContext()));
    } else {
      myMatchingVisitor.setResult(matches(text, another.getValue()));
    }
  }

  @Override public void visitXmlTag(XmlTag tag) {
    final XmlTag another = (XmlTag)myMatchingVisitor.getElement();
    final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(tag.getName());

    myMatchingVisitor.setResult((matches(tag.getName(), another.getName()) || isTypedVar) &&
                                myMatchingVisitor.matchInAnyOrder(tag.getAttributes(), another.getAttributes()));

    if(myMatchingVisitor.getResult()) {
      final XmlTagChild[] contentChildren = tag.getValue().getChildren();

      if (contentChildren.length > 0) {
        PsiElement[] patternNodes = contentChildren;
        PsiElement[] matchedNodes = another.getValue().getChildren();

        if (contentChildren.length != 1) {
          patternNodes = expandXmlTexts(patternNodes);
          matchedNodes = expandXmlTexts(matchedNodes);
        }

        myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(
          new ArrayBackedNodeIterator(patternNodes),
          new ArrayBackedNodeIterator(matchedNodes)
        ));
      }
    }

    if (myMatchingVisitor.getResult() && isTypedVar) {
      MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler( tag.getName() );
      myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(another, myMatchingVisitor.getMatchContext()));
    }
  }

  private static PsiElement[] expandXmlTexts(PsiElement[] children) {
    List<PsiElement> result = new ArrayList<PsiElement>(children.length);
    for(PsiElement c:children) {
      if (c instanceof XmlText) {
        for(PsiElement p:c.getChildren()) {
          if (!(p instanceof PsiWhiteSpace)) result.add(p);
        }
      } else if (!(c instanceof PsiWhiteSpace)) {
        result.add(c);
      }
    }
    return PsiUtilCore.toPsiElementArray(result);
  }

  @Override public void visitXmlText(XmlText text) {
    myMatchingVisitor.setResult(myMatchingVisitor.matchSequentially(text.getFirstChild(), myMatchingVisitor.getElement().getFirstChild()));
  }

  @Override public void visitXmlToken(XmlToken token) {
    if (token.getTokenType() == XmlTokenType.XML_DATA_CHARACTERS) {
      String text = token.getText();
      final boolean isTypedVar = myMatchingVisitor.getMatchContext().getPattern().isTypedVar(text);

      if (isTypedVar) {
        myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(token, myMatchingVisitor.getElement()));
      } else {
        myMatchingVisitor.setResult(matches(text, myMatchingVisitor.getElement().getText()));
      }
    }
  }

  private boolean matches(String a, String b) {
    return myCaseSensitive ? a.equals(b) : a.equalsIgnoreCase(b);
  }
}