--- old/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeMetadata.java 2015-02-23 20:10:42.000000000 +0100 +++ new/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeMetadata.java 2015-02-23 20:10:41.000000000 +0100 @@ -30,46 +30,63 @@ import com.sun.tools.javac.util.List; import java.util.EnumMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; /** - * A super-interface for all type metadata elements. Metadata classes - * can be created for any metadata on types with the following - * properties: + * TypeMetadata is essentially an immutable {@code EnumMap>} * - * + * A metadata class represented by a subtype of Entry can express a property on a Type instance. + * Thers should be at most one instance of an Entry per Entry.Kind on any given Type instance. + * + * Metadata classes of a specific kind are responsible for how they combine themselvs. + * + * @implNote {@code Entry:combine} need not be commutative. */ public class TypeMetadata { + public static final TypeMetadata EMPTY = new TypeMetadata(); - public static final TypeMetadata empty = new TypeMetadata(); - private final EnumMap contents; + private final EnumMap contents; + /** + * Create a new empty TypeMetadata map. + */ private TypeMetadata() { - contents = new EnumMap(Element.Kind.class); + contents = new EnumMap<>(Entry.Kind.class); } - public TypeMetadata(final Element elem) { + /** + * Create a new TypeMetadata map containing the Entry {@code elem}. + * + * @param elem the sole contents of this map + */ + public TypeMetadata(Entry elem) { this(); + Assert.checkNonNull(elem); contents.put(elem.kind(), elem); } - public TypeMetadata(final TypeMetadata other) { + /** + * Creates a copy of TypeMetadata {@code other} with a shallow copy the other's metadata contents. + * + * @param other the TypeMetadata to copy contents from. + */ + public TypeMetadata(TypeMetadata other) { + Assert.checkNonNull(other); contents = other.contents.clone(); } - public TypeMetadata copy() { - return new TypeMetadata(this); - } + /** + * Return a copy of this TypeMetadata with the metadata entry for {@code elem.kind()} combined + * with {@code elem}. + * + * @param elem the new value + * @return a new TypeMetadata updated with {@code Entry elem} + */ + public TypeMetadata combine(Entry elem) { + Assert.checkNonNull(elem); - public TypeMetadata combine(final Element elem) { - final TypeMetadata out = new TypeMetadata(this); - final Element.Kind key = elem.kind(); + TypeMetadata out = new TypeMetadata(this); + Entry.Kind key = elem.kind(); if (contents.containsKey(key)) { out.add(key, this.contents.get(key).combine(elem)); } else { @@ -78,17 +95,26 @@ return out; } - public TypeMetadata combine(final TypeMetadata other) { - final TypeMetadata out = new TypeMetadata(); - final Set keys = new HashSet<>(this.contents.keySet()); + /** + * Return a copy of this TypeMetadata with the metadata entry for all kinds from {@code other} + * combined with the same kind from this. + * + * @param other the TypeMetadata to combine with this + * @return a new TypeMetadata updated with all entries from {@code other} + */ + public TypeMetadata combineAll(TypeMetadata other) { + Assert.checkNonNull(other); + + TypeMetadata out = new TypeMetadata(); + Set keys = new HashSet<>(contents.keySet()); keys.addAll(other.contents.keySet()); - for(final Element.Kind key : keys) { - if (this.contents.containsKey(key)) { + for(Entry.Kind key : keys) { + if (contents.containsKey(key)) { if (other.contents.containsKey(key)) { - out.add(key, this.contents.get(key).combine(other.contents.get(key))); + out.add(key, contents.get(key).combine(other.contents.get(key))); } else { - out.add(key, this.contents.get(key)); + out.add(key, contents.get(key)); } } else if (other.contents.containsKey(key)) { out.add(key, other.contents.get(key)); @@ -97,7 +123,24 @@ return out; } - public Element get(final Element.Kind kind) { + /** + * Return a TypeMetadata with the metadata entry for {@code kind} removed. + * + * This may be the same instance or a new TypeMetadata. + * + * @param kind the {@code Kind} to remove metadata for + * @return a new TypeMetadata without {@code Kind kind} + */ + public TypeMetadata without(Entry.Kind kind) { + if (isEmpty() || contents.get(kind) == null) + return this; + + TypeMetadata out = new TypeMetadata(this); + out.contents.remove(kind); + return out; + } + + public Entry get(Entry.Kind kind) { return contents.get(kind); } @@ -105,18 +148,14 @@ return contents.isEmpty(); } - private void add(final Element.Kind kind, final Element elem) { + private void add(Entry.Kind kind, Entry elem) { contents.put(kind, elem); } - private void addAll(final Map m) { - contents.putAll(m); - } - - public interface Element { + public interface Entry { public enum Kind { - ANNOTATIONS; + ANNOTATIONS } /** @@ -131,16 +170,18 @@ * @param other The metadata with which to combine this one. * @return The combined metadata. */ - public Element combine(Element other); + public Entry combine(Entry other); } /** * A type metadata object holding type annotations. */ - public static class Annotations implements Element { - private final List annos; + public static class Annotations implements Entry { + private List annos; + + public static final List TO_BE_SET = List.nil(); - public Annotations(final List annos) { + public Annotations(List annos) { this.annos = annos; } @@ -154,18 +195,16 @@ } @Override - public Annotations combine(final Element other) { - // Temporary: we should append the lists, but that won't - // work with type annotations today. Instead, we replace - // the list. - return new Annotations(((Annotations) other).annos); + public Annotations combine(Entry other) { + Assert.check(annos == TO_BE_SET); + annos = ((Annotations)other).annos; + return this; } @Override public Kind kind() { return Kind.ANNOTATIONS; } @Override - public String toString() { return "ANNOTATIONS { " + annos + " }"; } + public String toString() { return "ANNOTATIONS [ " + annos + " ]"; } } - }