1package annotator.find;
2
3import java.util.List;
4
5import annotations.el.BoundLocation;
6
7import com.sun.source.tree.ClassTree;
8import com.sun.source.tree.MethodTree;
9import com.sun.source.tree.Tree;
10import com.sun.source.tree.TypeParameterTree;
11import com.sun.source.util.TreePath;
12import com.sun.tools.javac.code.Type;
13import com.sun.tools.javac.tree.JCTree.JCExpression;
14
15public class BoundLocationCriterion implements Criterion {
16
17  private Criterion parentCriterion;
18  private final int boundIndex;
19  private final int paramIndex;
20
21
22  public BoundLocationCriterion(BoundLocation boundLoc) {
23    this(boundLoc.boundIndex, boundLoc.paramIndex);
24  }
25
26  private BoundLocationCriterion(int boundIndex, int paramIndex) {
27    this.boundIndex = boundIndex;
28    this.paramIndex = paramIndex;
29
30    if (boundIndex != -1) {
31      this.parentCriterion = new BoundLocationCriterion(-1, paramIndex);
32    } else if (paramIndex != -1) {
33      this.parentCriterion = null;
34    }
35  }
36
37  /** {@inheritDoc} */
38  @Override
39  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
40    assert path == null || path.getLeaf() == leaf;
41    return isSatisfiedBy(path);
42  }
43
44  /** {@inheritDoc} */
45  @Override
46  public boolean isSatisfiedBy(TreePath path) {
47    if (path == null) {
48      return false;
49    }
50
51    Tree leaf = path.getLeaf();
52
53    // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n", path, leaf, leaf.getClass());
54
55    TreePath parentPath = path.getParentPath();
56    if (parentPath == null) {
57      return false;
58    }
59
60    Tree parent = parentPath.getLeaf();
61    if (parent == null) {
62      return false;
63    }
64
65    boolean returnValue = false;
66
67    // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n  parent=%s (%s)%n", path, leaf, leaf.getClass(), parent, parent.getClass());
68
69    // if boundIndex is not null, need to check that this is right bound
70    // in parent
71    if (boundIndex != -1) {
72      if (parent instanceof TypeParameterTree) {
73        List<? extends Tree> bounds = ((TypeParameterTree) parent).getBounds();
74        int ix = boundIndex;
75        if (!bounds.isEmpty() && isInterface((JCExpression) bounds.get(0))) {
76          --ix;
77        }
78        if (ix < 0 || ix < bounds.size() && bounds.get(ix) == leaf) {
79          returnValue = parentCriterion.isSatisfiedBy(parentPath);
80        }
81      } else if (boundIndex == 0 && leaf instanceof TypeParameterTree) {
82        List<? extends Tree> bounds = ((TypeParameterTree) leaf).getBounds();
83        if (bounds.isEmpty() || isInterface((JCExpression) bounds.get(0))) {
84          // If the bound is implicit (i.e., a missing "extends Object"),
85          // then permit the match here.
86          returnValue = parentCriterion.isSatisfiedBy(path);
87        } else {
88          Type type = ((JCExpression) bounds.get(0)).type;
89          if (type != null && type.tsym != null && type.tsym.isInterface()) {
90            returnValue = parentCriterion.isSatisfiedBy(parentPath);
91          }
92        }
93      }
94    } else if (paramIndex != -1) {
95      // if paramIndex is not null, need to ensure this present
96      // typeparameter tree represents the correct parameter
97      if (parent instanceof MethodTree || parent instanceof ClassTree) {
98        List<? extends TypeParameterTree> params = null;
99
100        if (parent instanceof MethodTree) {
101          params = ((MethodTree) parent).getTypeParameters();
102        } else if (parent instanceof ClassTree) {
103          params = ((ClassTree) parent).getTypeParameters();
104        }
105
106        if (paramIndex < params.size()) {
107          if (params.get(paramIndex) == leaf) {
108            returnValue = true;
109          }
110        }
111      }
112    }
113
114    if (!returnValue) {
115      return this.isSatisfiedBy(parentPath);
116    } else {
117      return true;
118    }
119  }
120
121  private boolean isInterface(JCExpression bound) {
122    Type type = bound.type;
123    return type != null && type.tsym != null && type.tsym.isInterface();
124  }
125
126  /** {@inheritDoc} */
127  @Override
128  public Kind getKind() {
129    return Kind.BOUND_LOCATION;
130  }
131
132  /** {@inheritDoc} */
133  @Override
134  public String toString() {
135    return "BoundCriterion: at param index: " + paramIndex +
136      " at bound index: " + boundIndex;
137  }
138}
139