summaryrefslogtreecommitdiff
path: root/platform/duplicates-analysis/src/com/intellij/dupLocator/AbstractMatchingVisitor.java
blob: 0b6bd6519edd63aa486cd45a0a8be4e8b8e1a434 (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
package com.intellij.dupLocator;

import com.intellij.dupLocator.util.NodeFilter;
import com.intellij.psi.PsiElement;
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
import com.intellij.dupLocator.iterators.FilteringNodeIterator;
import com.intellij.dupLocator.iterators.NodeIterator;
import com.intellij.dupLocator.iterators.SiblingNodeIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * @author Eugene.Kudelevsky
 */
public abstract class AbstractMatchingVisitor {

  public abstract boolean matchSequentially(NodeIterator nodes, NodeIterator nodes2);

  public abstract boolean match(PsiElement element1, PsiElement element2);

  protected abstract boolean doMatchInAnyOrder(NodeIterator elements, NodeIterator elements2);

  public boolean matchSequentially(@NotNull PsiElement[] elements1, @NotNull PsiElement[] element2) {
    return matchSequentially(new FilteringNodeIterator(new ArrayBackedNodeIterator(elements1), getNodeFilter()),
                             new FilteringNodeIterator(new ArrayBackedNodeIterator(element2), getNodeFilter()));
  }

  @NotNull
  protected abstract NodeFilter getNodeFilter();

  public boolean matchOptionally(@Nullable PsiElement element1, @Nullable PsiElement element2) {
    return element1 == null && isLeftLooseMatching() ||
           element2 == null && isRightLooseMatching() ||
           match(element1, element2);
  }

  public boolean matchSons(final PsiElement el1, final PsiElement el2) {
    if (el1 == null || el2 == null) return el1 == el2;
    return matchSequentially(el1.getFirstChild(), el2.getFirstChild());
  }

  public boolean matchSonsOptionally(final PsiElement element, final PsiElement element2) {
    if (element == null && isLeftLooseMatching()) {
      return true;
    }
    if (element2 == null && isRightLooseMatching()) {
      return true;
    }
    if (element == null || element2 == null) {
      return element == element2;
    }
    return matchSequentiallyOptionally(element.getFirstChild(), element2.getFirstChild());
  }

  public final boolean matchSonsInAnyOrder(PsiElement element1, PsiElement element2) {
    if (element1 == null && isLeftLooseMatching()) {
      return true;
    }
    if (element2 == null && isRightLooseMatching()) {
      return true;
    }
    if (element1 == null || element2 == null) {
      return element1 == element2;
    }
    PsiElement e = element1.getFirstChild();
    PsiElement e2 = element2.getFirstChild();
    return (e == null && isLeftLooseMatching()) ||
           (e2 == null && isRightLooseMatching()) ||
           matchInAnyOrder(new FilteringNodeIterator(new SiblingNodeIterator(e), getNodeFilter()),
                           new FilteringNodeIterator(new SiblingNodeIterator(e2), getNodeFilter()));
  }

  public boolean matchOptionally(@NotNull PsiElement[] elements1, @NotNull PsiElement[] elements2) {
    return (elements1.length == 0 && isLeftLooseMatching()) ||
           (elements2.length == 0 && isRightLooseMatching()) ||
           matchSequentially(elements1, elements2);
  }

  public final boolean matchInAnyOrder(final PsiElement[] elements, final PsiElement[] elements2) {
    if (elements == elements2) return true;

    return matchInAnyOrder(
      new ArrayBackedNodeIterator(elements),
      new ArrayBackedNodeIterator(elements2)
    );
  }

  protected boolean isLeftLooseMatching() {
    return true;
  }

  protected boolean isRightLooseMatching() {
    return true;
  }

  public boolean matchSequentially(PsiElement el1, PsiElement el2) {
    //if (el1==null || el2==null) return el1 == el2;
    return matchSequentially(new FilteringNodeIterator(new SiblingNodeIterator(el1), getNodeFilter()),
                             new FilteringNodeIterator(new SiblingNodeIterator(el2), getNodeFilter()));
  }

  public boolean matchSequentiallyOptionally(PsiElement el1, PsiElement el2) {
    return (el1 == null && isLeftLooseMatching()) ||
           (el2 == null && isRightLooseMatching()) ||
           matchSequentially(new FilteringNodeIterator(new SiblingNodeIterator(el1), getNodeFilter()),
                             new FilteringNodeIterator(new SiblingNodeIterator(el2), getNodeFilter()));
  }

  public final boolean matchInAnyOrder(final NodeIterator elements, final NodeIterator elements2) {
    if ((!elements.hasNext() && isLeftLooseMatching()) ||
        (!elements2.hasNext() && isRightLooseMatching()) ||
        (!elements.hasNext() && !elements2.hasNext())
      ) {
      return true;
    }

    return doMatchInAnyOrder(elements, elements2);
  }
}