package annotations.el; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Set; import annotations.Annotation; import annotations.util.coll.VivifyingMap; /*>>> import org.checkerframework.checker.nullness.qual.Nullable; */ /** * An <code>AElement</code> represents a Java element and the annotations it * carries. Some <code>AElements</code> may contain others; for example, an * {@link AClass} may contain {@link AMethod}s. Every <code>AElement</code> * usually belongs directly or indirectly to an {@link AScene}. Each subclass * of <code>AElement</code> represents one kind of annotatable element; its * name should make this clear. */ public class AElement implements Cloneable { static <K extends Object> VivifyingMap<K, AElement> newVivifyingLHMap_AE() { return new VivifyingMap<K, AElement>( new LinkedHashMap<K, AElement>()) { @Override public AElement createValueFor(K k) { return new AElement(k); } @Override public boolean subPrune(AElement v) { return v.prune(); } }; } // Different from the above in that the elements are guaranteed to // contain a non-null "type" field. static <K extends Object> VivifyingMap<K, AElement> newVivifyingLHMap_AET() { return new VivifyingMap<K, AElement>( new LinkedHashMap<K, AElement>()) { @Override public AElement createValueFor(K k) { return new AElement(k, true); } @Override public boolean subPrune(AElement v) { return v.prune(); } }; } @SuppressWarnings("unchecked") <K, V extends AElement> void copyMapContents(VivifyingMap<K, V> orig, VivifyingMap<K, V> copy) { for (K key : orig.keySet()) { V val = orig.get(key); copy.put(key, (V) val.clone()); } } /** * The top-level annotations directly on this element. Annotations on * subelements are in those subelements' <code>tlAnnotationsHere</code> * sets, not here. */ public final Set<Annotation> tlAnnotationsHere; /** The type of a field or a method parameter */ public final ATypeElement type; // initialized in constructor public Annotation lookup(String name) { for (Annotation anno : tlAnnotationsHere) { if (anno.def.name.equals(name)) { return anno; } } return null; } // general description of the element final Object description; AElement(Object description) { this(description, false); } AElement(Object description, boolean hasType) { this(description, hasType ? new ATypeElement("type of " + description) : null); } AElement(Object description, ATypeElement type) { tlAnnotationsHere = new LinkedHashSet<Annotation>(); this.description = description; this.type = type; } AElement(AElement elem) { this(elem, elem.type); } AElement(AElement elem, ATypeElement type) { this(elem.description, type == null ? null : type.clone()); tlAnnotationsHere.addAll(elem.tlAnnotationsHere); } AElement(AElement elem, Object description) { this(description, elem.type == null ? null : elem.type.clone()); tlAnnotationsHere.addAll(elem.tlAnnotationsHere); } // Q: Are there any fields other than elements and maps that can't be shared? @Override public AElement clone() { return new AElement(this); } /** * Returns whether this {@link AElement} equals <code>o</code> (see * warnings below). Generally speaking, two {@link AElement}s are equal if * they are of the same type, have the same {@link #tlAnnotationsHere}, and * have recursively equal, corresponding subelements. Two warnings: * * <ul> * <li>While subelement collections usually remember the order in which * subelements were added and regurgitate them in this order, two * {@link AElement}s are equal even if order of subelements differs. * <li>Two {@link AElement}s are unequal if one has a subelement that the * other does not, even if the tree of elements rooted at the subelement * contains no annotations. Thus, if you want to compare the * <em>annotations</em>, you should {@link #prune} both {@link AElement}s * first. * </ul> */ @Override // Was final. Removed that so that AnnotationDef can redefine. public boolean equals(Object o) { return o instanceof AElement && ((AElement) o).equals(this); } /** * Returns whether this {@link AElement} equals <code>o</code>; a * slightly faster variant of {@link #equals(Object)} for when the argument * is statically known to be another nonnull {@link AElement}. */ /* * We need equals to be symmetric and operate correctly over the class * hierarchy. Let x and y be objects of subclasses S and T, respectively, * of AElement. x.equals((AElement) y) shall check that y is an S. If so, * it shall call ((S) y).equalsS(x), which checks that x is a T and then * compares fields. */ public boolean equals(AElement o) { return o.equalsElement(this); } final boolean equalsElement(AElement o) { return o.tlAnnotationsHere.equals(tlAnnotationsHere) && (o.type == null ? type == null : o.type.equals(type)); } /** * {@inheritDoc} */ @Override public int hashCode() { return getClass().getName().hashCode() + tlAnnotationsHere.hashCode() + (type == null ? 0 : type.hashCode()); } /** * Removes empty subelements of this {@link AElement} depth-first; returns * whether this {@link AElement} is itself empty after pruning. */ // In subclasses, we use & (not &&) because we want complete evaluation: // we should prune everything even if the first subelement is nonempty. public boolean prune() { return tlAnnotationsHere.isEmpty() & (type == null || type.prune()); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("AElement: "); sb.append(description); sb.append(" : "); tlAnnotationsHereFormatted(sb); if (type!=null) { sb.append(' '); sb.append(type.toString()); } // typeargs? return sb.toString(); } public void tlAnnotationsHereFormatted(StringBuilder sb) { boolean first = true; for (Annotation aElement : tlAnnotationsHere) { if (!first) { sb.append(", "); } first = false; sb.append(aElement.toString()); } } public <R, T> R accept(ElementVisitor<R, T> v, T t) { return v.visitElement(this, t); } }