1package annotator.find;
2
3import java.util.AbstractSet;
4import java.util.ArrayDeque;
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Comparator;
9import java.util.Deque;
10import java.util.HashMap;
11import java.util.Iterator;
12import java.util.LinkedHashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.NoSuchElementException;
16import java.util.Set;
17import java.util.SortedMap;
18import java.util.TreeMap;
19import java.util.TreeSet;
20
21import javax.lang.model.element.Name;
22import javax.lang.model.type.TypeKind;
23
24import annotations.el.InnerTypeLocation;
25import annotations.io.ASTIndex;
26import annotations.io.ASTPath;
27import annotations.io.ASTRecord;
28import type.*;
29
30import com.sun.source.tree.AnnotatedTypeTree;
31import com.sun.source.tree.AnnotationTree;
32import com.sun.source.tree.ArrayTypeTree;
33import com.sun.source.tree.CompilationUnitTree;
34import com.sun.source.tree.ExpressionTree;
35import com.sun.source.tree.IdentifierTree;
36import com.sun.source.tree.MemberSelectTree;
37import com.sun.source.tree.NewArrayTree;
38import com.sun.source.tree.ParameterizedTypeTree;
39import com.sun.source.tree.PrimitiveTypeTree;
40import com.sun.source.tree.Tree;
41import com.sun.source.tree.TreeVisitor;
42import com.sun.source.tree.TypeParameterTree;
43import com.sun.source.tree.WildcardTree;
44import com.sun.source.util.TreePath;
45import com.sun.tools.javac.code.Kinds;
46import com.sun.tools.javac.code.Symbol.ClassSymbol;
47import com.sun.tools.javac.code.Symbol.MethodSymbol;
48import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
49import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
50import com.sun.tools.javac.code.TypeTag;
51import com.sun.tools.javac.tree.JCTree;
52import com.sun.tools.javac.tree.JCTree.JCExpression;
53import com.sun.tools.javac.util.Pair;
54
55/**
56 * @author dbro
57 *
58 * An indexed collection (though not {@link java.util.Collection}, only
59 * {@link java.lang.Iterable}) of {@link Insertion}s with methods to
60 * select those specified for a given class or for an outer class along
61 * with its local classes.  This class is especially useful when a
62 * single JAIF stores annotations for many source files, as it reduces
63 * the number of insertions to be considered for any AST node.
64 *
65 * The class now serves a second purpose, which should probably be
66 * separated out (according to OO dogma, at least): It attaches
67 * {@link annotations.io.ASTPath}-based inner type {@link Insertion}s to
68 * a {@link TypedInsertion} on the outer type if one exists (see
69 * {@link #organizeTypedInsertions(CompilationUnitTree, String, Collection)}.
70 * Since getting these insertions right depends on this organization,
71 * this class is now essential for correctness, not merely for
72 * performance.
73 */
74public class Insertions implements Iterable<Insertion> {
75  private static int kindLevel(Insertion i) {
76    // ordered so insertion that depends on another gets inserted after other
77    switch (i.getKind()) {
78    case CONSTRUCTOR:
79      return 3;
80    case NEW:
81    case RECEIVER:
82      return 2;
83    case CAST:
84        return 1;
85    // case ANNOTATION:
86    // case CLOSE_PARENTHESIS:
87    default:
88      return 0;
89    }
90  }
91
92  private static final Comparator<Insertion> byASTRecord =
93      new Comparator<Insertion>() {
94        @Override
95        public int compare(Insertion o1, Insertion o2) {
96          Criteria c1 = o1.getCriteria();
97          Criteria c2 = o2.getCriteria();
98          ASTPath p1 = c1.getASTPath();
99          ASTPath p2 = c2.getASTPath();
100          ASTRecord r1 = new ASTRecord(null,
101              c1.getClassName(), c1.getMethodName(), c1.getFieldName(),
102              p1 == null ? ASTPath.empty() : p1);
103          ASTRecord r2 = new ASTRecord(null,
104              c2.getClassName(), c2.getMethodName(), c2.getFieldName(),
105              p2 == null ? ASTPath.empty() : p2);
106          int c = r1.compareTo(r2);
107          if (c == 0) {
108            // c = o1.getKind().compareTo(o2.getKind());
109            c = Integer.compare(kindLevel(o2), kindLevel(o1));  // descending
110            if (c == 0) { c = o1.toString().compareTo(o2.toString()); }
111          }
112          return c;
113        }
114      };
115
116  // store indexes insertions by (qualified) outer class name and inner
117  // class path (if any)
118  private Map<String, Map<String, Set<Insertion>>> store;
119  private int size;
120
121  private Pair<String, String> nameSplit(String name) {
122    int i = name.indexOf('$');  // FIXME: don't split on '$' in source
123    return i < 0
124        ? Pair.of(name, "")
125        : Pair.of(name.substring(0, i), name.substring(i));
126  }
127
128  public Insertions() {
129    store = new HashMap<String, Map<String, Set<Insertion>>>();
130    size = 0;
131  }
132
133  // auxiliary for following two methods
134  private void forClass(CompilationUnitTree cut,
135      String qualifiedClassName, Set<Insertion> result) {
136    Pair<String, String> pair = nameSplit(qualifiedClassName);
137    Map<String, Set<Insertion>> map = store.get(pair.fst);
138    if (map != null) {
139      Set<Insertion> set = new TreeSet<Insertion>(byASTRecord);
140      set.addAll(map.get(pair.snd));
141      if (set != null) {
142        set = organizeTypedInsertions(cut, qualifiedClassName, set);
143        result.addAll(set);
144      }
145    }
146  }
147
148  /**
149   * Selects {@link Insertion}s relevant to a given class.
150   *
151   * @param cut the current compilation unit
152   * @param qualifiedClassName the fully qualified class name
153   * @return {@link java.util.Set} of {@link Insertion}s with an
154   *          {@link InClassCriterion} for the given class
155   */
156  public Set<Insertion> forClass(CompilationUnitTree cut,
157      String qualifiedClassName) {
158    Set<Insertion> set = new LinkedHashSet<Insertion>();
159    forClass(cut, qualifiedClassName, set);
160    return set;
161  }
162
163  /**
164   * Selects {@link Insertion}s relevant to a given outer class and its
165   * local classes.
166   *
167   * @param cut the current compilation unit
168   * @param qualifiedOuterClassName the fully qualified outer class name
169   * @return {@link java.util.Set} of {@link Insertion}s with an
170   *          {@link InClassCriterion} for the given outer class or one
171   *          of its local classes
172   */
173  public Set<Insertion> forOuterClass(CompilationUnitTree cut,
174      String qualifiedOuterClassName) {
175    Map<String, Set<Insertion>> map = store.get(qualifiedOuterClassName);
176    if (map == null || map.isEmpty()) {
177      return Collections.<Insertion>emptySet();
178    } else {
179      Set<Insertion> set = new LinkedHashSet<Insertion>();
180      for (String key : map.keySet()) {
181        String qualifiedClassName = qualifiedOuterClassName + key;
182        forClass(cut, qualifiedClassName, set);
183      }
184      return set;
185    }
186  }
187
188  /**
189   * Add an {@link Insertion} to this collection.
190   */
191  public void add(Insertion ins) {
192    InClassCriterion icc = ins.getCriteria().getInClass();
193    String k1 = "";
194    String k2 = "";
195    Map<String, Set<Insertion>> map;
196    Set<Insertion> set;
197
198    if (icc != null) {
199      Pair<String, String> triple = nameSplit(icc.className);
200      k1 = triple.fst;
201      k2 = triple.snd;
202    }
203    map = store.get(k1);
204    if (map == null) {
205      map = new HashMap<String, Set<Insertion>>();
206      store.put(k1, map);
207    }
208    set = map.get(k2);
209    if (set == null) {
210      set = new LinkedHashSet<Insertion>();
211      map.put(k2, set);
212    }
213
214    size -= set.size();
215    set.add(ins);
216    size += set.size();
217  }
218
219  /**
220   * Add all {@link Insertion}s in the given
221   * {@link java.util.Collection} to this collection.
222   */
223  public void addAll(Collection<? extends Insertion> c) {
224    for (Insertion ins : c) {
225      add(ins);
226    }
227  }
228
229  /**
230   * Returns the number of {@link Insertion}s in this collection.
231   */
232  public int size() {
233    return size;
234  }
235
236  @Override
237  public Iterator<Insertion> iterator() {
238    return new Iterator<Insertion>() {
239      private Iterator<Map<String, Set<Insertion>>> miter =
240          store.values().iterator();
241      private Iterator<Set<Insertion>> siter =
242          Collections.<Set<Insertion>>emptySet().iterator();
243      private Iterator<Insertion> iiter =
244          Collections.<Insertion>emptySet().iterator();
245
246      @Override
247      public boolean hasNext() {
248        if (iiter.hasNext()) { return true; }
249        while (siter.hasNext()) {
250          iiter = siter.next().iterator();
251          if (iiter.hasNext()) { return true; }
252        }
253        while (miter.hasNext()) {
254          siter = miter.next().values().iterator();
255          while (siter.hasNext()) {
256            iiter = siter.next().iterator();
257            if (iiter.hasNext()) { return true; }
258          }
259        }
260        return false;
261      }
262
263      @Override
264      public Insertion next() {
265        if (hasNext()) { return iiter.next(); }
266        throw new NoSuchElementException();
267      }
268
269      @Override
270      public void remove() {
271        throw new UnsupportedOperationException();
272      }
273    };
274  }
275
276  /**
277   * Returns a {@link java.util.List} containing all {@link Insertion}s
278   * in this collection.
279   */
280  public List<Insertion> toList() {
281    List<Insertion> list = new ArrayList<Insertion>(size);
282    for (Insertion ins : this) { list.add(ins); }
283    return null;
284  }
285
286  /*
287   * This method detects inner type relationships among ASTPath-based
288   * insertion specifications and organizes the insertions accordingly.
289   * This step is necessary because 1) insertion proceeds from the end to
290   * the beginning of the source and 2) the insertion location does not
291   * always exist prior to the top-level type insertion.
292   */
293  private Set<Insertion> organizeTypedInsertions(CompilationUnitTree cut,
294      String className, Collection<Insertion> insertions) {
295    ASTRecordMap<TypedInsertion> map = new ASTRecordMap<TypedInsertion>();
296    Set<Insertion> organized = new LinkedHashSet<Insertion>();
297    Set<Insertion> unorganized = new LinkedHashSet<Insertion>();
298    List<Insertion> list = new ArrayList<Insertion>();
299
300    // First divide the insertions into three buckets: TypedInsertions
301    // on outer types (map), ASTPath-based insertions on local types
302    // (unorganized -- built as list and then sorted, since building as
303    // a set spuriously removes "duplicates" according to the
304    // comparator), and everything else (organized -- where all
305    // eventually land).
306    for (Insertion ins : insertions) {
307      if (ins.getInserted()) { continue; }
308      Criteria criteria = ins.getCriteria();
309      GenericArrayLocationCriterion galc =
310          criteria.getGenericArrayLocation();
311      ASTPath p = criteria.getASTPath();
312      if (p == null || p.isEmpty()
313          || galc != null && !galc.getLocation().isEmpty()
314          || ins instanceof CastInsertion
315          || ins instanceof CloseParenthesisInsertion) {
316        organized.add(ins);
317      } else {
318        ASTRecord rec = new ASTRecord(cut, criteria.getClassName(),
319            criteria.getMethodName(), criteria.getFieldName(), p);
320        ASTPath.ASTEntry entry = rec.astPath.get(-1);
321        Tree node;
322
323        if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY
324            && entry.childSelectorIs(ASTPath.TYPE)
325            && entry.getArgument() == 0) {
326          ASTPath temp = rec.astPath.getParentPath();
327          node = ASTIndex.getNode(cut, rec.replacePath(temp));
328          node = node instanceof JCTree.JCNewArray
329              ? TypeTree.fromType(((JCTree.JCNewArray) node).type)
330              : null;
331        } else {
332          node = ASTIndex.getNode(cut, rec);
333        }
334
335        if (ins instanceof TypedInsertion) {
336          TypedInsertion tins = map.get(rec);
337          if (ins instanceof NewInsertion) {
338            NewInsertion nins = (NewInsertion) ins;
339            if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY
340                && entry.childSelectorIs(ASTPath.TYPE)) {
341              int a = entry.getArgument();
342              List<TypePathEntry> loc0 = new ArrayList<TypePathEntry>(a);
343              ASTRecord rec0 = null;
344              if (a == 0) {
345                rec0 = rec.replacePath(p.getParentPath());
346                Tree t = ASTIndex.getNode(cut, rec0);
347                if (t == null || t.toString().startsWith("{")) {
348                  rec0 = null;
349                } else {
350                  rec = rec0;
351                  rec0 = rec.extend(Tree.Kind.NEW_ARRAY,
352                      ASTPath.TYPE, 0);
353                }
354              } else if (node != null
355                  && !nins.getInnerTypeInsertions().isEmpty()) {
356                if (node.getKind() == Tree.Kind.IDENTIFIER) {
357                  node = ASTIndex.getNode(cut,
358                      rec.replacePath(p.getParentPath()));
359                }
360                if ((node.getKind() == Tree.Kind.NEW_ARRAY
361                    || node.getKind() == Tree.Kind.ARRAY_TYPE)
362                    && !node.toString().startsWith("{")) {
363                  rec = rec.replacePath(p.getParentPath());
364
365                  Collections.fill(loc0, TypePathEntry.ARRAY);
366                  // irec = rec;
367                  // if (node.getKind() == Tree.Kind.NEW_ARRAY) {
368                  rec0 = rec.extend(Tree.Kind.NEW_ARRAY,
369                      ASTPath.TYPE, 0);
370                  // }
371                }
372              }
373
374              if (rec0 != null) {
375                for (Insertion inner : nins.getInnerTypeInsertions()) {
376                  Criteria icriteria = inner.getCriteria();
377                  GenericArrayLocationCriterion igalc =
378                      icriteria.getGenericArrayLocation();
379                  if (igalc != null) {
380                    ASTRecord rec1;
381                    int b = igalc.getLocation().size();
382                    List<TypePathEntry> loc =
383                        new ArrayList<TypePathEntry>(a + b);
384                    loc.addAll(loc0);
385                    loc.addAll(igalc.getLocation());
386                    rec1 = extendToInnerType(rec0, loc, node);
387                    icriteria.add(new GenericArrayLocationCriterion());
388                    icriteria.add(new ASTPathCriterion(rec1.astPath));
389                    inner.setInserted(false);
390                    organized.add(inner);
391                  }
392                }
393                nins.getInnerTypeInsertions().clear();
394              }
395            }
396          }
397          if (tins == null) {
398            map.put(rec, (TypedInsertion) ins);
399          } else if (tins.getType().equals(((TypedInsertion) ins).getType())) {
400            mergeTypedInsertions(tins, (TypedInsertion) ins);
401          }
402        } else {
403          int d = newArrayInnerTypeDepth(p);
404          if (d > 0) {
405            ASTPath temp = p;
406            while (!temp.isEmpty()
407                && (node == null || node.getKind() != Tree.Kind.NEW_ARRAY)) {
408              // TODO: avoid repeating work of newArrayInnerTypeDepth()
409              temp = temp.getParentPath();
410              node = ASTIndex.getNode(cut, rec.replacePath(temp));
411            }
412            if (node == null) {
413              // TODO: ???
414            }
415            temp = temp.extend(
416                new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0));
417            if (node.toString().startsWith("{")) {
418              TypedInsertion tins = map.get(rec.replacePath(temp));
419              if (tins == null) {
420                // TODO
421              } else {
422                tins.getInnerTypeInsertions().add(ins);
423                ins.setInserted(true);
424              }
425            } else {
426              List<? extends ExpressionTree> dims =
427                  ((NewArrayTree) node).getDimensions();
428              ASTRecord irec = rec.replacePath(p.getParentPath())
429                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0);
430              GenericArrayLocationCriterion igalc =
431                  criteria.getGenericArrayLocation();
432              for (int i = 0 ; i < d; i++) {
433                irec = irec.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
434              }
435              if (igalc != null) {
436                List<TypePathEntry> loc = igalc.getLocation();
437                if (!loc.isEmpty()) {
438                  try {
439                    Tree dim = dims.get(d-1);
440                    irec = extendToInnerType(irec, loc, dim);
441                    criteria.add(new ASTPathCriterion(irec.astPath));
442                    criteria.add(new GenericArrayLocationCriterion());
443                  } catch (RuntimeException e) {}
444                }
445              }
446            }
447          }
448          list.add(ins);
449        }
450      }
451    }
452    // if (map.isEmpty()) {
453    //  organized.addAll(unorganized);
454    //  return organized;
455    // }
456    Collections.sort(list, byASTRecord);
457    unorganized.addAll(list);
458
459    // Each Insertion in unorganized gets attached to a TypedInsertion
460    // in map if possible; otherwise, it gets dumped into organized.
461    for (Insertion ins : unorganized) {
462      Criteria criteria = ins.getCriteria();
463      String methodName = criteria.getMethodName();
464      String fieldName = criteria.getFieldName();
465      ASTPath ap1 = criteria.getASTPath();
466      List<TypePathEntry> tpes = new ArrayList<TypePathEntry>();
467      if (ap1 == null) {
468          // || methodName == null && fieldName == null)
469        organized.add(ins);
470        continue;
471      }
472
473      // First find the relevant "top-level" insertion, if any.
474      // ap0: path to top-level type; ap1: path to local type
475      ASTRecord rec;
476      Tree.Kind kind;
477      Deque<ASTPath> astack = new ArrayDeque<ASTPath>(ap1.size());
478      ASTPath ap0 = ap1;
479      do {
480        astack.push(ap0);
481        ap0 = ap0.getParentPath();
482      } while (!ap0.isEmpty());
483      do {
484        ap0 = astack.pop();
485        kind = ap0.get(-1).getTreeKind();
486        rec = new ASTRecord(cut, className, methodName, fieldName, ap0);
487      } while (!(astack.isEmpty() || map.containsKey(rec)));
488
489      TypedInsertion tins = map.get(rec);
490      TreePath path = ASTIndex.getTreePath(cut, rec);
491      Tree node = path == null ? null : path.getLeaf();
492      if (node == null && ap0.isEmpty()) {
493        organized.add(ins);
494        continue;
495      }
496
497      // Try to create a top-level insertion if none exists (e.g., if
498      // there is an insertion for NewArray.type 1 but not for 0).
499      if (tins == null) {
500        GenericArrayLocationCriterion galc =
501            criteria.getGenericArrayLocation();
502        if (node == null) {
503          // TODO: figure out from rec?
504          organized.add(ins);
505          continue;
506        } else {
507          Tree t = path.getLeaf();
508          switch (t.getKind()) {
509          case NEW_ARRAY:
510            int d = 0;
511            ASTPath.ASTEntry e = ap1.get(-1);
512            List<TypePathEntry> loc = null;
513            List<Insertion> inners = new ArrayList<Insertion>();
514            Type type = TypeTree.conv(((JCTree.JCNewArray) t).type);
515            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) {
516              d += e.getArgument();
517            }
518            if (galc != null) {
519              loc = galc.getLocation();
520              int n = loc.size();
521              while (--n >= 0 && loc.get(n).tag == TypePathEntryKind.ARRAY) {
522                ++d;
523              }
524              loc = n < 0 ? null : loc.subList(0, ++n);
525            }
526            criteria.add(new ASTPathCriterion(
527                rec.astPath.getParentPath().extendNewArray(d)));
528            criteria.add(loc == null || loc.isEmpty()
529                ? new GenericArrayLocationCriterion()
530                : new GenericArrayLocationCriterion(
531                    new InnerTypeLocation(loc)));
532            inners.add(ins);
533            tins = new NewInsertion(type, criteria, inners);
534            tins.setInserted(true);
535            map.put(rec, tins);
536            break;
537          default:
538            break;
539          }
540          path = path.getParentPath();
541        }
542      }
543
544      // The sought node may or may not be found in the tree; if not, it
545      // may need to be created later.  Use whatever part of the path
546      // exists already to distinguish MEMBER_SELECT nodes that indicate
547      // qualifiers from those that indicate local types.  Assume any
548      // MEMBER_SELECTs in the AST path that don't correspond to
549      // existing nodes are part of a type use.
550      if (node == null) {
551        ASTPath ap = ap0;
552        if (!ap.isEmpty()) {
553          do {
554            ap = ap.getParentPath();
555            node = ASTIndex.getNode(cut, rec.replacePath(ap));
556          } while (node == null && !ap.isEmpty());
557        }
558        if (node == null) {
559          organized.add(ins);
560          continue;
561        }
562
563        // find actual type
564        ClassSymbol csym = null;
565        switch (tins.getKind()) {
566        case CONSTRUCTOR:
567          if (node instanceof JCTree.JCMethodDecl) {
568            MethodSymbol msym = ((JCTree.JCMethodDecl) node).sym;
569            csym = (ClassSymbol) msym.owner;
570            node = TypeTree.fromType(csym.type);
571            break;
572          } else if (node instanceof JCTree.JCClassDecl) {
573            csym = ((JCTree.JCClassDecl) node).sym;
574            if (csym.owner instanceof ClassSymbol) {
575              csym = (ClassSymbol) csym.owner;
576              node = TypeTree.fromType(csym.type);
577              break;
578            }
579          }
580          throw new RuntimeException();
581        case NEW:
582          if (node instanceof JCTree.JCNewArray) {
583            if (node.toString().startsWith("{")) {
584              node = TypeTree.fromType(((JCTree.JCNewArray) node).type);
585              break;
586            } else {
587              organized.add(ins);
588              continue;
589            }
590          }
591          throw new RuntimeException();
592        case RECEIVER:
593          if (node instanceof JCTree.JCMethodDecl) {
594            JCTree.JCMethodDecl jmd = (JCTree.JCMethodDecl) node;
595            csym = (ClassSymbol) jmd.sym.owner;
596            if ("<init>".equals(jmd.name.toString())) {
597              csym = (ClassSymbol) csym.owner;
598            }
599          } else if (node instanceof JCTree.JCClassDecl) {
600            csym = ((JCTree.JCClassDecl) node).sym;
601          }
602          if (csym != null) {
603            node = TypeTree.fromType(csym.type);
604            break;
605          }
606          throw new RuntimeException();
607        default:
608          throw new RuntimeException();
609        }
610      }
611
612      /*
613       * Inner types require special consideration due to the
614       * structural differences between an AST that represents a type
615       * (subclass of com.sun.source.Tree) and the type's logical
616       * representation (subclass of type.Type).  The differences are
617       * most prominent in the case of a type with a parameterized
618       * local type.  For example, the AST for A.B.C<D> looks like
619       * this:
620       *
621       *                     ParameterizedType
622       *                    /                 \
623       *               MemberSelect       Identifier
624       *              /            \           |
625       *        MemberSelect      (Name)       D
626       *         /      \           |
627       *  Identifier   (Name)       C
628       *        |        |
629       *        A        B
630       *
631       * (Technically, the Names are not AST nodes but rather
632       * attributes of their parent MemberSelect nodes.)  The logical
633       * representation seems more intuitive:
634       *
635       *       DeclaredType
636       *      /     |      \
637       *    Name  Params  Inner
638       *     |      |       |
639       *     A      -  DeclaredType
640       *              /     |      \
641       *            Name  Params  Inner
642       *             |      |       |
643       *             B      -  DeclaredType
644       *                      /     |      \
645       *                    Name  Params  Inner
646       *                     |      |       |
647       *                     C      D       -
648       *
649       * The opposing "chirality" of local type nesting means that the
650       * usual recursive descent strategy doesn't work for finding a
651       * logical type path in an AST; in effect, local types have to
652       * be "turned inside-out".
653       *
654       * Worse yet, the actual tree structure may not exist in the tree!
655       * It is possible to recover the actual type from the symbol
656       * table, but the methods to create AST nodes are not visible
657       * here.  Hence, the conversion relies on custom implementations
658       * of the interfaces in com.sun.source.tree.Tree, which are
659       * defined in the local class TypeTree.
660       */
661      int i = ap0.size();
662      int n = ap1.size();
663      int actualDepth = 0;  // inner type levels seen
664      int expectedDepth = 0;  // inner type levels anticipated
665
666      // skip any declaration nodes
667      while (i < n) {
668        ASTPath.ASTEntry entry = ap1.get(i);
669        kind = entry.getTreeKind();
670        if (kind != Tree.Kind.METHOD && kind != Tree.Kind.VARIABLE) {
671          break;
672        }
673        ++i;
674      }
675
676      // now build up the type path in JVM's format
677      while (i < n) {
678        ASTPath.ASTEntry entry = ap1.get(i);
679        rec = rec.extend(entry);
680        kind = entry.getTreeKind();
681
682        while (node.getKind() == Tree.Kind.ANNOTATED_TYPE) {  // skip
683          node = ((AnnotatedTypeTree) node).getUnderlyingType();
684        }
685        if (expectedDepth == 0) {
686          expectedDepth = localDepth(node);
687        }
688
689        switch (kind) {
690        case ARRAY_TYPE:
691          if (expectedDepth == 0 && node.getKind() == kind) {
692            node = ((ArrayTypeTree) node).getType();
693            while (--actualDepth >= 0) {
694              tpes.add(TypePathEntry.INNER_TYPE);
695            }
696            tpes.add(TypePathEntry.ARRAY);
697            break;
698          }
699          throw new RuntimeException();
700
701        case MEMBER_SELECT:
702          if (--expectedDepth >= 0) {  // otherwise, shouldn't have MEMBER_SELECT
703            node = ((MemberSelectTree) node).getExpression();
704            ++actualDepth;
705            break;
706          }
707          throw new RuntimeException();
708
709        case NEW_ARRAY:
710          assert tpes.isEmpty();
711          ap0 = ap0.add(new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY,
712              ASTPath.TYPE, 0));
713          if (expectedDepth == 0 && node.getKind() == kind) {
714            if (node instanceof JCTree.JCNewArray) {
715              int arg = entry.getArgument();
716              if (arg > 0) {
717                node = ((JCTree.JCNewArray) node).elemtype;
718                tpes.add(TypePathEntry.ARRAY);
719                while (--arg > 0 && node instanceof JCTree.JCArrayTypeTree) {
720                  node = ((JCTree.JCArrayTypeTree) node).elemtype;
721                  tpes.add(TypePathEntry.ARRAY);
722                }
723                if (arg > 0) { throw new RuntimeException(); }
724              } else {
725                node = TypeTree.fromType(((JCTree.JCNewArray) node).type);
726              }
727            } else {
728              throw new RuntimeException("NYI");  // TODO
729            }
730            break;
731          }
732          throw new RuntimeException();
733
734        case PARAMETERIZED_TYPE:
735          if (node.getKind() == kind) {
736            ParameterizedTypeTree ptt = (ParameterizedTypeTree) node;
737            if (entry.childSelectorIs(ASTPath.TYPE)) {
738              node = ptt.getType();
739              break;  // ParameterizedType.type is "transparent" wrt type path
740            } else if (expectedDepth == 0
741                && entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
742              List<? extends Tree> typeArgs = ptt.getTypeArguments();
743              int j = entry.getArgument();
744              if (j >= 0 && j < typeArgs.size()) {
745                // make sure any inner types are accounted for
746                actualDepth = 0;
747                expectedDepth = localDepth(ptt.getType());
748                while (--expectedDepth >= 0) {
749                  tpes.add(TypePathEntry.INNER_TYPE);
750                }
751                node = typeArgs.get(j);
752                tpes.add(
753                    new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, j));
754                break;
755              }
756            }
757          }
758          throw new RuntimeException();
759
760        case UNBOUNDED_WILDCARD:
761          if (ASTPath.isWildcard(node.getKind())) {
762            if (expectedDepth == 0
763                && (i < 1
764                    || ap1.get(i-1).getTreeKind() != Tree.Kind.INSTANCE_OF)
765                && (i < 2
766                    || ap1.get(i-2).getTreeKind() != Tree.Kind.ARRAY_TYPE)) {
767              while (--actualDepth >= 0) {
768                tpes.add(TypePathEntry.INNER_TYPE);
769              }
770              tpes.add(TypePathEntry.WILDCARD);
771              break;
772            }
773          }
774          throw new RuntimeException();
775
776        default:
777          node = ASTIndex.getNode(cut, rec);
778          break;
779        }
780
781        ++i;
782      }
783
784      while (--actualDepth >= 0) {
785        tpes.add(TypePathEntry.INNER_TYPE);
786      }
787
788      organized.add(ins);
789      if (tpes.isEmpty()) {
790        // assert ap1.equals(ap0) && !map.containsKey(ap0);
791//        organized.add(ins);
792        // map.put(rec, (TypedInsertion) ins);
793      } else {
794        criteria.add(new ASTPathCriterion(ap0));
795        criteria.add(new GenericArrayLocationCriterion(
796            new InnerTypeLocation(tpes)));
797        tins.getInnerTypeInsertions().add(ins);
798      }
799    }
800    organized.addAll(map.values());
801    return organized;
802  }
803
804  private int newArrayInnerTypeDepth(ASTPath path) {
805    int d = 0;
806    if (path != null) {
807      while (!path.isEmpty()) {
808        ASTPath.ASTEntry entry = path.get(-1);
809        switch (entry.getTreeKind()) {
810        case ANNOTATED_TYPE:
811        case MEMBER_SELECT:
812        case PARAMETERIZED_TYPE:
813        case UNBOUNDED_WILDCARD:
814          d = 0;
815          break;
816        case ARRAY_TYPE:
817          ++d;
818          break;
819        case NEW_ARRAY:
820          if (entry.childSelectorIs(ASTPath.TYPE) && entry.hasArgument()) {
821            d += entry.getArgument();
822          }
823          return d;
824        default:
825          return 0;
826        }
827        path = path.getParentPath();
828      }
829    }
830    return 0;
831  }
832
833  /**
834   * Find an {@link ASTRecord} for the tree corresponding to a nested
835   * type of the type (use) to which the given record corresponds.
836   *
837   * @param rec record of (outer) type AST to be annotated
838   * @param loc inner type path
839   * @return record that locates the (nested) type in the source
840   */
841  private ASTRecord extendToInnerType(ASTRecord rec, List<TypePathEntry> loc) {
842    ASTRecord r = rec;
843    Iterator<TypePathEntry> iter = loc.iterator();
844    int depth = 0;
845
846    while (iter.hasNext()) {
847      TypePathEntry tpe = iter.next();
848      switch (tpe.tag) {
849      case ARRAY:
850        while (depth-- > 0) {
851          r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
852        }
853        r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
854        break;
855      case INNER_TYPE:
856        ++depth;
857        break;
858      case TYPE_ARGUMENT:
859        depth = 0;
860        r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE_ARGUMENT,
861            tpe.arg);
862        break;
863      case WILDCARD:
864        while (depth-- > 0) {
865          r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
866        }
867        r = r.extend(Tree.Kind.UNBOUNDED_WILDCARD, ASTPath.BOUND);
868        break;
869      default:
870        throw new RuntimeException();
871      }
872    }
873    while (depth-- > 0) {
874      r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
875    }
876    return r;
877  }
878
879  /**
880   * Find an {@link ASTRecord} for the tree corresponding to a nested
881   * type of the type (use) to which the given tree and record
882   * correspond.
883   *
884   * @param rec record that locates {@code node} in the source
885   * @param loc inner type path
886   * @param node starting point for inner type path
887   * @return record that locates the nested type in the source
888   */
889  private ASTRecord extendToInnerType(ASTRecord rec,
890      List<TypePathEntry> loc, Tree node) {
891    ASTRecord r = rec;
892    Tree t = node;
893    Iterator<TypePathEntry> iter = loc.iterator();
894    TypePathEntry tpe = iter.next();
895
896outer:
897    while (true) {
898      int d = localDepth(node);
899
900      switch (t.getKind()) {
901      case ANNOTATED_TYPE:
902        r = r.extend(Tree.Kind.ANNOTATED_TYPE, ASTPath.TYPE);
903        t = ((JCTree.JCAnnotatedType) t).getUnderlyingType();
904        break;
905
906      case ARRAY_TYPE:
907        if (d == 0 && tpe.tag == TypePathEntryKind.ARRAY) {
908          int a = 0;
909          if (!r.astPath.isEmpty()) {
910            ASTPath.ASTEntry e = r.astPath.get(-1);
911            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY
912                && e.childSelectorIs(ASTPath.TYPE)) {
913              a = 1 + e.getArgument();
914            }
915          }
916          r = a > 0
917              ? r.replacePath(r.astPath.getParentPath())
918                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a)
919              : r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
920          t = ((ArrayTypeTree) t).getType();
921          break;
922        }
923        throw new RuntimeException();
924
925      case MEMBER_SELECT:
926        if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) {
927          Tree temp = t;
928          do {
929            temp = ((JCTree.JCFieldAccess) temp).getExpression();
930            if (!iter.hasNext()) {
931              do {
932                r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
933              } while (--d > 0);
934              return r;
935            }
936            tpe = iter.next();
937            if (--d == 0) {
938              continue outer;  // avoid next() at end of loop
939            }
940          } while (tpe.tag == TypePathEntryKind.INNER_TYPE);
941        }
942        throw new RuntimeException();
943
944      case NEW_ARRAY:
945        if (d == 0) {
946          if (!r.astPath.isEmpty()) {
947            ASTPath.ASTEntry e = r.astPath.get(-1);
948            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) {
949              int a = 0;
950              while (tpe.tag == TypePathEntryKind.ARRAY) {
951                ++a;
952                if (!iter.hasNext()) { break; }
953                tpe = iter.next();
954              }
955              r = r.replacePath(r.astPath.getParentPath())
956                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a);
957              break;
958            }
959          }
960          r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
961          t = ((JCTree.JCArrayTypeTree) t).getType();
962          break;
963        }
964        throw new RuntimeException();
965
966      case PARAMETERIZED_TYPE:
967        if (d == 0 && tpe.tag == TypePathEntryKind.TYPE_ARGUMENT) {
968          r = r.extend(Tree.Kind.PARAMETERIZED_TYPE,
969              ASTPath.TYPE_ARGUMENT, tpe.arg);
970          t = ((JCTree.JCTypeApply) t).getTypeArguments().get(tpe.arg);
971          break;
972        } else if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) {
973          Tree temp = ((JCTree.JCTypeApply) t).getType();
974          r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE);
975          t = temp;
976          do {
977            temp = ((JCTree.JCFieldAccess) temp).getExpression();
978            if (!iter.hasNext()) {
979              do {
980                r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
981              } while (--d > 0);
982              return r;
983            }
984            tpe = iter.next();
985            if (--d == 0) {
986              continue outer;  // avoid next() at end of loop
987            }
988          } while (tpe.tag == TypePathEntryKind.INNER_TYPE);
989        }
990        throw new RuntimeException();
991
992      case EXTENDS_WILDCARD:
993      case SUPER_WILDCARD:
994      case UNBOUNDED_WILDCARD:
995        if (tpe.tag == TypePathEntryKind.WILDCARD) {
996          t = ((JCTree.JCWildcard) t).getBound();
997          break;
998        }
999        throw new RuntimeException();
1000
1001      default:
1002        if (iter.hasNext()) {
1003          throw new RuntimeException();
1004        }
1005      }
1006
1007      if (!iter.hasNext()) { return r; }
1008      tpe = iter.next();
1009    }
1010  }
1011
1012  // merge annotations, assuming types are structurally identical
1013  private void mergeTypedInsertions(TypedInsertion ins0, TypedInsertion ins1) {
1014    mergeTypes(ins0.getType(), ins1.getType());
1015  }
1016
1017  private void mergeTypes(Type t0, Type t1) {
1018    if (t0 == t1) { return; }
1019    switch (t0.getKind()) {
1020    case ARRAY:
1021      {
1022        ArrayType at0 = (ArrayType) t0;
1023        ArrayType at1 = (ArrayType) t1;
1024        mergeTypes(at0.getComponentType(), at1.getComponentType());
1025        return;
1026      }
1027    case BOUNDED:
1028      {
1029        BoundedType bt0 = (BoundedType) t0;
1030        BoundedType bt1 = (BoundedType) t1;
1031        if (bt0.getBoundKind() != bt1.getBoundKind()) { break; }
1032        mergeTypes(bt0.getBound(), bt1.getBound());
1033        mergeTypes(bt0.getName(), bt1.getName());
1034        return;
1035      }
1036    case DECLARED:
1037      {
1038        DeclaredType dt0 = (DeclaredType) t0;
1039        DeclaredType dt1 = (DeclaredType) t1;
1040        List<Type> tps0 = dt0.getTypeParameters();
1041        List<Type> tps1 = dt1.getTypeParameters();
1042        int n = tps0.size();
1043        if (tps1.size() != n) { break; }
1044        mergeTypes(dt0.getInnerType(), dt1.getInnerType());
1045        for (String anno : dt1.getAnnotations()) {
1046          if (!dt0.getAnnotations().contains(anno)) {
1047            dt0.addAnnotation(anno);
1048          }
1049        }
1050        for (int i = 0; i < n; i++) {
1051          mergeTypes(tps0.get(i), tps1.get(i));
1052        }
1053        return;
1054      }
1055    }
1056    throw new RuntimeException();
1057  }
1058
1059  // Returns the depth of the innermost local type of a type AST.
1060  private int localDepth(Tree node) {
1061    Tree t = node;
1062    int n = 0;
1063loop:
1064    while (t != null) {
1065      switch (t.getKind()) {
1066      case ANNOTATED_TYPE:
1067        t = ((AnnotatedTypeTree) t).getUnderlyingType();
1068        break;
1069      case MEMBER_SELECT:
1070        if (t instanceof JCTree.JCFieldAccess) {
1071          JCTree.JCFieldAccess jfa = (JCTree.JCFieldAccess) t;
1072          if (jfa.sym.kind == Kinds.PCK) {
1073            t = jfa.getExpression();
1074            continue;
1075          }
1076        }
1077        t = ((MemberSelectTree) t).getExpression();
1078        ++n;
1079        break;
1080      default:
1081        break loop;
1082      }
1083    }
1084    return n;
1085  }
1086
1087  // Provides an additional level of indexing.
1088  class ASTRecordMap<E> implements Map<ASTRecord, E> {
1089    Map<ASTRecord, SortedMap<ASTPath, E>> back;
1090
1091    ASTRecordMap() {
1092      back = new HashMap<ASTRecord, SortedMap<ASTPath, E>>();
1093    }
1094
1095    private SortedMap<ASTPath, E> getMap(ASTRecord rec) {
1096      ASTRecord key = rec.replacePath(ASTPath.empty());
1097      SortedMap<ASTPath, E> map = back.get(key);
1098      if (map == null) {
1099        map = new TreeMap<ASTPath, E>();
1100        back.put(key, map);
1101      }
1102      return map;
1103    }
1104
1105    @Override
1106    public int size() {
1107      int n = 0;
1108      for (SortedMap<ASTPath, E> map : back.values()) {
1109        n += map.size();
1110      }
1111      return n;
1112    }
1113
1114    @Override
1115    public boolean isEmpty() {
1116      return size() == 0;
1117    }
1118
1119    @Override
1120    public boolean containsKey(Object key) {
1121      ASTRecord rec = (ASTRecord) key;
1122      SortedMap<ASTPath, E> m = getMap(rec);
1123      return m != null && m.containsKey(rec.astPath);
1124    }
1125
1126    @Override
1127    public boolean containsValue(Object value) {
1128      @SuppressWarnings("unchecked")
1129      E e = (E) value;
1130      for (SortedMap<ASTPath, E> map : back.values()) {
1131        if (map.containsValue(e)) { return true; }
1132      }
1133      return false;
1134    }
1135
1136    @Override
1137    public E get(Object key) {
1138      ASTRecord rec = (ASTRecord) key;
1139      SortedMap<ASTPath, E> map = getMap(rec);
1140      return map == null ? null : map.get(rec.astPath);
1141    }
1142
1143    @Override
1144    public E put(ASTRecord key, E value) {
1145      ASTRecord rec = key;
1146      SortedMap<ASTPath, E> map = getMap(rec);
1147      return map == null ? null : map.put(rec.astPath, value);
1148    }
1149
1150    @Override
1151    public E remove(Object key) {
1152      ASTRecord rec = (ASTRecord) key;
1153      SortedMap<ASTPath, E> map = getMap(rec);
1154      return map == null ? null : map.remove(rec.astPath);
1155    }
1156
1157    @Override
1158    public void putAll(Map<? extends ASTRecord, ? extends E> m) {
1159      for (Map.Entry<? extends ASTRecord, ? extends E> entry : m.entrySet()) {
1160        put(entry.getKey(), entry.getValue());
1161      }
1162    }
1163
1164    @Override
1165    public void clear() {
1166      back.clear();
1167    }
1168
1169    @Override
1170    public Set<ASTRecord> keySet() {
1171      return back.keySet();
1172    }
1173
1174    @Override
1175    public Collection<E> values() {
1176      Set<E> ret = new LinkedHashSet<E>();
1177      for (SortedMap<ASTPath, E> m : back.values()) {
1178        ret.addAll(m.values());
1179      }
1180      return ret;
1181    }
1182
1183    @Override
1184    public Set<Map.Entry<ASTRecord, E>> entrySet() {
1185      final int size = size();
1186      return new AbstractSet<Map.Entry<ASTRecord, E>>() {
1187        @Override
1188        public Iterator<Map.Entry<ASTRecord, E>> iterator() {
1189          return new Iterator<Map.Entry<ASTRecord, E>>() {
1190            Iterator<Map.Entry<ASTRecord, SortedMap<ASTPath, E>>> iter0 =
1191                back.entrySet().iterator();
1192            Iterator<Map.Entry<ASTPath, E>> iter1 =
1193                Collections.<Map.Entry<ASTPath, E>>emptyIterator();
1194            ASTRecord rec = null;
1195
1196            @Override
1197            public boolean hasNext() {
1198              if (iter1.hasNext()) { return true; }
1199              while (iter0.hasNext()) {
1200                Map.Entry<ASTRecord, SortedMap<ASTPath, E>> entry =
1201                    iter0.next();
1202                rec = entry.getKey();
1203                iter1 = entry.getValue().entrySet().iterator();
1204                if (iter1.hasNext()) { return true; }
1205              }
1206              iter1 = Collections.<Map.Entry<ASTPath, E>>emptyIterator();
1207              return false;
1208            }
1209
1210            @Override
1211            public Map.Entry<ASTRecord, E> next() {
1212              if (!hasNext()) { throw new NoSuchElementException(); }
1213              final Map.Entry<ASTPath, E> e0 = iter1.next();
1214              return new Map.Entry<ASTRecord, E>() {
1215                final ASTRecord key = rec.replacePath(e0.getKey());
1216                final E val = e0.getValue();
1217                @Override public ASTRecord getKey() { return key; }
1218                @Override public E getValue() { return val; }
1219                @Override public E setValue(E value) {
1220                  throw new UnsupportedOperationException();
1221                }
1222              };
1223            }
1224
1225            @Override
1226            public void remove() {
1227              throw new UnsupportedOperationException();
1228            }
1229          };
1230        }
1231
1232        @Override
1233        public int size() { return size; }
1234      };
1235    }
1236  }
1237
1238  // Simple AST implementation used only in determining type paths.
1239  static abstract class TypeTree implements ExpressionTree {
1240    private static Map<String, TypeTag> primTags =
1241        new HashMap<String, TypeTag>();
1242    {
1243      primTags.put("byte", TypeTag.BYTE);
1244      primTags.put("char", TypeTag.CHAR);
1245      primTags.put("short", TypeTag.SHORT);
1246      primTags.put("long", TypeTag.LONG);
1247      primTags.put("float", TypeTag.FLOAT);
1248      primTags.put("int", TypeTag.INT);
1249      primTags.put("double", TypeTag.DOUBLE);
1250      primTags.put("boolean", TypeTag.BOOLEAN);
1251    }
1252
1253    static TypeTree fromJCTree(JCTree jt) {
1254      if (jt != null) {
1255        Kind kind = jt.getKind();
1256        switch (kind) {
1257        case ANNOTATED_TYPE:
1258          return fromJCTree(
1259              ((JCTree.JCAnnotatedType) jt).getUnderlyingType());
1260        case IDENTIFIER:
1261          return new IdenT(
1262              ((JCTree.JCIdent) jt).sym.getSimpleName().toString());
1263        case ARRAY_TYPE:
1264          return new ArrT(
1265              fromJCTree(((JCTree.JCArrayTypeTree) jt).getType()));
1266        case MEMBER_SELECT:
1267          return new LocT(
1268              fromJCTree(((JCTree.JCFieldAccess) jt).getExpression()),
1269              ((JCTree.JCFieldAccess) jt).getIdentifier());
1270        case EXTENDS_WILDCARD:
1271        case SUPER_WILDCARD:
1272          return new WildT(kind,
1273              fromJCTree(((JCTree.JCWildcard) jt).getBound()));
1274        case UNBOUNDED_WILDCARD:
1275          return new WildT();
1276        case PARAMETERIZED_TYPE:
1277          com.sun.tools.javac.util.List<JCExpression> typeArgs =
1278            ((JCTree.JCTypeApply) jt).getTypeArguments();
1279          List<Tree> args = new ArrayList<Tree>(typeArgs.size());
1280          for (JCTree.JCExpression typeArg : typeArgs) {
1281            args.add(fromJCTree(typeArg));
1282          }
1283          return new ParT(
1284              fromJCTree(((JCTree.JCTypeApply) jt).getType()),
1285              args);
1286        default:
1287          break;
1288        }
1289      }
1290      return null;
1291    }
1292
1293    static TypeTree fromType(final Type type) {
1294      switch (type.getKind()) {
1295      case ARRAY:
1296        final ArrayType atype = (ArrayType) type;
1297        final TypeTree componentType = fromType(atype.getComponentType());
1298        return new ArrT(componentType);
1299      case BOUNDED:
1300        final BoundedType btype = (BoundedType) type;
1301        final BoundedType.BoundKind bk = btype.getBoundKind();
1302        final String bname = btype.getName().getName();
1303        final TypeTree bound = fromType(btype.getBound());
1304        return new Param(bname, bk, bound);
1305      case DECLARED:
1306        final DeclaredType dtype = (DeclaredType) type;
1307        if (dtype.isWildcard()) {
1308          return new WildT();
1309        } else {
1310          final String dname = dtype.getName();
1311          TypeTag typeTag = primTags.get(dname);
1312          if (typeTag == null) {
1313            final TypeTree base = new IdenT(dname);
1314            TypeTree ret = base;
1315            List<Type> params = dtype.getTypeParameters();
1316            DeclaredType inner = dtype.getInnerType();
1317            if (!params.isEmpty()) {
1318              final List<Tree> typeArgs = new ArrayList<Tree>(params.size());
1319              for (Type t : params) { typeArgs.add(fromType(t)); }
1320              ret = new ParT(base, typeArgs);
1321            }
1322            return inner == null ? ret : meld(fromType(inner), ret);
1323          } else {
1324            final TypeKind typeKind = typeTag.getPrimitiveTypeKind();
1325            return new PrimT(typeKind);
1326          }
1327        }
1328      default:
1329        throw new RuntimeException("unknown type kind " + type.getKind());
1330      }
1331    }
1332
1333    static TypeTree fromType(final com.sun.tools.javac.code.Type type) {
1334      return fromType(conv(type));
1335    }
1336
1337    /**
1338     * @param jtype
1339     * @return
1340     */
1341    static Type conv(final com.sun.tools.javac.code.Type jtype) {
1342      Type type = null;
1343      DeclaredType d;
1344      com.sun.tools.javac.code.Type t;
1345      switch (jtype.getKind()) {
1346      case ARRAY:
1347        t = ((com.sun.tools.javac.code.Type.ArrayType) jtype).elemtype;
1348        type = new ArrayType(conv(t));
1349        break;
1350      case DECLARED:
1351        t = jtype;
1352        d = null;
1353        do {
1354          DeclaredType d0 = d;
1355          com.sun.tools.javac.code.Type.ClassType ct =
1356              (com.sun.tools.javac.code.Type.ClassType) t;
1357          d = new DeclaredType(ct.tsym.name.toString());
1358          d.setInnerType(d0);
1359          d0 = d;
1360          for (com.sun.tools.javac.code.Type a : ct.getTypeArguments()) {
1361            d.addTypeParameter(conv(a));
1362          }
1363          t = ct.getEnclosingType();
1364        } while (t.getKind() == TypeKind.DECLARED);
1365        type = d;
1366        break;
1367      case WILDCARD:
1368        BoundedType.BoundKind k;
1369        t = ((com.sun.tools.javac.code.Type.WildcardType) jtype).bound;
1370        switch (((com.sun.tools.javac.code.Type.WildcardType) jtype).kind) {
1371        case EXTENDS:
1372          k = BoundedType.BoundKind.EXTENDS;
1373          break;
1374        case SUPER:
1375          k = BoundedType.BoundKind.SUPER;
1376          break;
1377        case UNBOUND:
1378          k = null;
1379          type = new DeclaredType("?");
1380          break;
1381        default:
1382          throw new RuntimeException();
1383        }
1384        if (k != null) {
1385          d = new DeclaredType(jtype.tsym.name.toString());
1386          type = new BoundedType(d, k, (DeclaredType) conv(t));
1387        }
1388        break;
1389      case TYPEVAR:
1390        t = ((com.sun.tools.javac.code.Type.TypeVar) jtype).getUpperBound();
1391        type = conv(t);
1392        if (type.getKind() == Type.Kind.DECLARED) {
1393          type = new BoundedType(new DeclaredType(jtype.tsym.name.toString()),
1394              BoundedType.BoundKind.EXTENDS, (DeclaredType) type);
1395        }  // otherwise previous conv should have been here already
1396        break;
1397      case INTERSECTION:
1398        t = jtype.tsym.erasure_field;  // ???
1399        type = new DeclaredType(t.tsym.name.toString());
1400        break;
1401      case UNION:
1402        // TODO
1403        break;
1404      case BOOLEAN:
1405      case BYTE:
1406      case CHAR:
1407      case DOUBLE:
1408      case LONG:
1409      case SHORT:
1410      case FLOAT:
1411      case INT:
1412        type = new DeclaredType(jtype.tsym.name.toString());
1413        break;
1414      // case ERROR:
1415      // case EXECUTABLE:
1416      // case NONE:
1417      // case NULL:
1418      // case OTHER:
1419      // case PACKAGE:
1420      // case VOID:
1421      default:
1422        break;
1423      }
1424      return type;
1425    }
1426
1427    private static TypeTree meld(final TypeTree t0, final TypeTree t1) {
1428      switch (t0.getKind()) {
1429      case IDENTIFIER:
1430        IdenT it = (IdenT) t0;
1431        return new LocT(t1, it.getName());
1432      case MEMBER_SELECT:
1433        LocT lt = (LocT) t0;
1434        return new LocT(meld(lt.getExpression(), t1), lt.getIdentifier());
1435      case PARAMETERIZED_TYPE:
1436        ParT pt = (ParT) t0;
1437        return new ParT(meld(pt.getType(), t1), pt.getTypeArguments());
1438      default:
1439        throw new IllegalArgumentException("unexpected type " + t0);
1440      }
1441    }
1442
1443    static final class ArrT extends TypeTree implements ArrayTypeTree {
1444      private final TypeTree componentType;
1445
1446      ArrT(TypeTree componentType) {
1447        this.componentType = componentType;
1448      }
1449
1450      @Override
1451      public Kind getKind() { return Kind.ARRAY_TYPE; }
1452
1453      @Override
1454      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1455        return visitor.visitArrayType(this, data);
1456      }
1457
1458      @Override
1459      public TypeTree getType() { return componentType; }
1460
1461      @Override
1462      public String toString() { return componentType + "[]"; }
1463    }
1464
1465    static final class LocT extends TypeTree implements MemberSelectTree {
1466      private final TypeTree expr;
1467      private final Name name;
1468
1469      LocT(TypeTree expr, Name name) {
1470        this.expr = expr;
1471        this.name = name;
1472      }
1473
1474      @Override
1475      public Kind getKind() { return Kind.MEMBER_SELECT; }
1476
1477      @Override
1478      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1479        return visitor.visitMemberSelect(this, data);
1480      }
1481
1482      @Override
1483      public TypeTree getExpression() { return expr; }
1484
1485      @Override
1486      public Name getIdentifier() { return name; }
1487
1488      @Override
1489      public String toString() { return expr + "." + name; }
1490    }
1491
1492    static final class ParT extends TypeTree implements ParameterizedTypeTree {
1493      private final TypeTree base;
1494      private final List<? extends Tree> typeArgs;
1495
1496      ParT(TypeTree base, List<? extends Tree> typeArgs) {
1497        this.base = base;
1498        this.typeArgs = typeArgs;
1499      }
1500
1501      @Override
1502      public Kind getKind() { return Kind.PARAMETERIZED_TYPE; }
1503
1504      @Override
1505      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1506        return visitor.visitParameterizedType(this, data);
1507      }
1508
1509      @Override
1510      public TypeTree getType() { return base; }
1511
1512      @Override
1513      public List<? extends Tree> getTypeArguments() {
1514        return typeArgs;
1515      }
1516
1517      @Override
1518      public String toString() {
1519        StringBuilder sb = new StringBuilder(base.toString());
1520        String s = "<";
1521        for (Tree t : typeArgs) {
1522          sb.append(s);
1523          sb.append(t.toString());
1524          s = ", ";
1525        }
1526        sb.append('>');
1527        return sb.toString();
1528      }
1529    }
1530
1531    static final class PrimT extends TypeTree implements PrimitiveTypeTree {
1532      private final TypeKind typeKind;
1533
1534      PrimT(TypeKind typeKind) {
1535        this.typeKind = typeKind;
1536      }
1537
1538      @Override
1539      public Kind getKind() { return Kind.PRIMITIVE_TYPE; }
1540
1541      @Override
1542      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1543        return visitor.visitPrimitiveType(this, data);
1544      }
1545
1546      @Override
1547      public TypeKind getPrimitiveTypeKind() { return typeKind; }
1548
1549      @Override
1550      public String toString() {
1551        switch (typeKind) {
1552        case BOOLEAN: return "boolean";
1553        case BYTE: return "byte";
1554        case CHAR: return "char";
1555        case DOUBLE: return "double";
1556        case FLOAT: return "float";
1557        case INT: return "int";
1558        case LONG: return "long";
1559        case SHORT: return "short";
1560        // case VOID: return "void";
1561        // case WILDCARD: return "?";
1562        default:
1563          throw new IllegalArgumentException("unexpected type kind "
1564              + typeKind);
1565        }
1566      }
1567    }
1568
1569    static final class IdenT extends TypeTree implements IdentifierTree {
1570      private final String name;
1571
1572      IdenT(String dname) {
1573        this.name = dname;
1574      }
1575
1576      @Override
1577      public Kind getKind() { return Kind.IDENTIFIER; }
1578
1579      @Override
1580      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1581        return visitor.visitIdentifier(this, data);
1582      }
1583
1584      @Override
1585      public Name getName() { return new TypeName(name); }
1586
1587      @Override
1588      public String toString() { return name; }
1589    }
1590
1591    static final class WildT extends TypeTree implements WildcardTree {
1592      private final TypeTree bound;
1593      private final Kind kind;
1594
1595      WildT() {
1596        this(Kind.UNBOUNDED_WILDCARD, null);
1597      }
1598
1599      WildT(TypeTree bound, BoundedType.BoundKind bk) {
1600        this(bk == BoundedType.BoundKind.SUPER
1601                ? Kind.SUPER_WILDCARD
1602                : Kind.EXTENDS_WILDCARD,
1603            bound);
1604      }
1605
1606      WildT(Kind kind, TypeTree bound) {
1607        this.kind = kind;
1608        this.bound = bound;
1609      }
1610
1611      @Override
1612      public Kind getKind() { return kind; }
1613
1614      @Override
1615      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1616        return visitor.visitWildcard(this, data);
1617      }
1618
1619      @Override
1620      public Tree getBound() { return bound; }
1621
1622      @Override
1623      public String toString() { return "?"; }
1624    }
1625
1626    static final class Param extends TypeTree implements TypeParameterTree {
1627      private final String bname;
1628      private final BoundedType.BoundKind bk;
1629      private final Tree bound;
1630
1631      Param(String bname, BoundedType.BoundKind bk, TypeTree bound) {
1632        this.bname = bname;
1633        this.bk = bk;
1634        this.bound = bound;
1635      }
1636
1637      @Override
1638      public Kind getKind() { return Kind.TYPE_PARAMETER; }
1639
1640      @Override
1641      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
1642        return visitor.visitTypeParameter(this, data);
1643      }
1644
1645      @Override
1646      public Name getName() { return new TypeName(bname); }
1647
1648      @Override
1649      public List<? extends Tree> getBounds() {
1650        return Collections.singletonList(bound);
1651      }
1652
1653      @Override
1654      public List<? extends AnnotationTree> getAnnotations() {
1655        return Collections.emptyList();
1656      }
1657
1658      @Override
1659      public String toString() {
1660        return bname + " " + bk.toString() + " " + bound.toString();
1661      }
1662    }
1663
1664    static final class TypeName implements Name {
1665      private final String str;
1666
1667      TypeName(String str) {
1668        this.str = str;
1669      }
1670
1671      @Override
1672      public int length() { return str.length(); }
1673
1674      @Override
1675      public char charAt(int index) { return str.charAt(index); }
1676
1677      @Override
1678      public CharSequence subSequence(int start, int end) {
1679        return str.subSequence(start, end);
1680      }
1681
1682      @Override
1683      public boolean contentEquals(CharSequence cs) {
1684        if (cs != null) {
1685          int n = length();
1686          if (cs.length() == n) {
1687            for (int i = 0; i < n; i++) {
1688              if (charAt(i) != cs.charAt(i)) { return false; }
1689            }
1690            return true;
1691          }
1692        }
1693        return false;
1694      }
1695
1696      @Override
1697      public String toString() { return str; }
1698    }
1699  }
1700}
1701