1 package annotations.el; 2 3 /*>>> 4 import org.checkerframework.checker.nullness.qual.*; 5 */ 6 7 import java.util.ArrayList; 8 import java.util.List; 9 10 import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; 11 import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind; 12 13 /** 14 * A {@link TypeASTMapper} traverses a client-maintained abstract syntax 15 * tree representing a type in parallel with an {@link ATypeElement} from 16 * the annotation scene library, indicating corresponding pairs of AST nodes and 17 * {@link AElement}s to the client so the client can process them in some 18 * fashion. 19 * 20 * <p> 21 * To use {@link TypeASTMapper}, write a subclass for your particular AST. 22 * Implement {@link #getElementType}, {@link #numTypeArguments}, and 23 * {@link #getTypeArgument} so that the mapper knows how to traverse your 24 * AST; implement {@link #map} to perform whatever processing you desire on 25 * each AST node and its corresponding {@link AElement}. Then, pass the root 26 * of your AST and the corresponding {@link ATypeElement} from your 27 * annotation scene to {@link #traverse}. 28 * 29 * <p> 30 * {@link TypeASTMapper} itself saves no state, but subclasses may save state 31 * if they wish. 32 * 33 * {@link ATypeElement} objects that will be traversed 34 * @param <N> common supertype of the AST nodes 35 */ 36 public abstract class TypeASTMapper<N> { 37 /** 38 * Constructs a {@link TypeASTMapper}. A {@link TypeASTMapper} stores no 39 * state. 40 */ 41 protected TypeASTMapper() { 42 } 43 44 private static ATypeElement getInnerType(ATypeElement te, 45 List<TypePathEntry> ls) { 46 if (ls.isEmpty()) { 47 return te; 48 } else { 49 return te.innerTypes.vivify(new InnerTypeLocation(ls)); 50 } 51 } 52 53 /** 54 * Traverses the type AST rooted at <code>tastRoot</code>, calling 55 * {@link #map} with each AST node and the corresponding {@link AElement} 56 * from <code>aslRoot</code>. 57 * 58 * If a node of the AST has no corresponding inner type in 59 * <code>aslRoot</code>, an inner type {@link AElement} is vivified to hold 60 * any annotations that {@link #map} might wish to store in it. Thus, 61 * a call to {@link #traverse} may write to <code>aslRoot</code> even if 62 * {@link #map} does not write to its {@link AElement} argument. You 63 * may wish to {@linkplain AElement#prune prune} <code>aslRoot</code> after 64 * traversal. 65 */ 66 public void traverse(N tastRoot, ATypeElement aslRoot) { 67 // Elements are added and removed from the end of this sole mutable 68 // list during the traversal. 69 List<TypePathEntry> ls = new ArrayList<TypePathEntry>(); 70 traverse1(tastRoot, aslRoot, ls); 71 } 72 73 // "Sane": top-level or type argument 74 private void traverse1(N n, ATypeElement te, List<TypePathEntry> ls) { 75 N elType = getElementType(n); 76 if (elType == null) { 77 // no array, so the prefix corresponds to the type right here 78 // System.out.printf("non-array: map(%s, getInnerType(%s, %s)=%s)%n", 79 // n, te, ls, getInnerType(te, ls)); 80 map(n, getInnerType(te, ls)); 81 int nta = numTypeArguments(n); 82 for (int tai = 0; tai < nta; tai++) { 83 ls.add(new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, tai)); 84 traverse1(getTypeArgument(n, tai), te, ls); 85 ls.remove(ls.size() - 1); 86 } 87 } else { 88 // System.out.printf("array top-level: map(%s, getInnerType(%s, %s)=%s)%n", 89 // n, te, ls, getInnerType(te, ls)); 90 map(n, getInnerType(te, ls)); 91 92 // at least one array layer to confuse us 93 int layers = 0; 94 while ((elType = getElementType(n)) != null) { 95 ls.add(TypePathEntry.ARRAY); 96 // System.out.printf("layers=%d, map(%s, getInnerType(%s, %s)=%s)%n", 97 // layers, elType, te, ls, getInnerType(te, ls)); 98 map(elType, getInnerType(te, ls)); 99 n = elType; 100 layers++; 101 } 102 // // n is now the innermost element type 103 // // map it to the prefix 104 // map(n, getInnerType(te, ls)); 105 106 int nta = numTypeArguments(n); 107 for (int tai = 0; tai < nta; tai++) { 108 ls.add(new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, tai)); 109 traverse1(getTypeArgument(n, tai), te, ls); 110 ls.remove(ls.size() - 1); 111 } 112 113 for (int i = 0; i < layers; i++) { 114 ls.remove(ls.size() - 1); 115 } 116 } 117 } 118 119 /** 120 * If <code>n</code> represents an array type, {@link #getElementType} 121 * returns the node for the element type of the array; otherwise it returns 122 * <code>null</code>. 123 */ 124 protected abstract N getElementType(N n); 125 126 /** 127 * If <code>n</code> represents a parameterized type, 128 * {@link #numTypeArguments} returns the number of type arguments; 129 * otherwise it returns 0. 130 */ 131 protected abstract int numTypeArguments(N n); 132 133 /** 134 * Returns the node corresponding to the type argument of <code>n</code> 135 * (which must be a parameterized type) at the given index. The caller 136 * must ensure that 137 * <code>0 <= index < {@link #numTypeArguments}(n)</code>. 138 */ 139 protected abstract N getTypeArgument(N n, int index); 140 141 /** 142 * Signals to the client that <code>n</code> corresponds to <code>e</code>. 143 * The client may, for example, set flags in <code>n</code> based on the 144 * annotations in 145 * <code>e.{@link AElement#tlAnnotationsHere tlAnnotationsHere}</code>. 146 * The {@link TypeASTMapper} calls {@link #map} on <code>n</code> before it 147 * calls {@link #map} on sub-nodes of <code>n</code> but not necessarily 148 * before it explores the structure of <code>n</code>'s subtree. 149 */ 150 protected abstract void map(N n, ATypeElement e); 151 } 152