blob: 3a0d10646e0e1d96da6518f25d56a8ce17130402 [file] [log] [blame]
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.code;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.List;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;
/**
* TypeMetadata is essentially an immutable {@code EnumMap<Entry.Kind, <? extends Entry>>}
*
* 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();
private final EnumMap<Entry.Kind, Entry> contents;
/**
* Create a new empty TypeMetadata map.
*/
private TypeMetadata() {
contents = new EnumMap<>(Entry.Kind.class);
}
/**
* 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);
}
/**
* 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();
}
/**
* 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);
TypeMetadata out = new TypeMetadata(this);
Entry.Kind key = elem.kind();
if (contents.containsKey(key)) {
out.add(key, this.contents.get(key).combine(elem));
} else {
out.add(key, elem);
}
return out;
}
/**
* 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<Entry.Kind> keys = new HashSet<>(contents.keySet());
keys.addAll(other.contents.keySet());
for(Entry.Kind key : keys) {
if (contents.containsKey(key)) {
if (other.contents.containsKey(key)) {
out.add(key, contents.get(key).combine(other.contents.get(key)));
} else {
out.add(key, contents.get(key));
}
} else if (other.contents.containsKey(key)) {
out.add(key, other.contents.get(key));
}
}
return out;
}
/**
* 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 (this == EMPTY || contents.get(kind) == null)
return this;
TypeMetadata out = new TypeMetadata(this);
out.contents.remove(kind);
return out.contents.isEmpty() ? EMPTY : out;
}
public Entry get(Entry.Kind kind) {
return contents.get(kind);
}
private void add(Entry.Kind kind, Entry elem) {
contents.put(kind, elem);
}
public interface Entry {
public enum Kind {
ANNOTATIONS
}
/**
* Get the kind of metadata this object represents
*/
public Kind kind();
/**
* Combine this type metadata with another metadata of the
* same kind.
*
* @param other The metadata with which to combine this one.
* @return The combined metadata.
*/
public Entry combine(Entry other);
}
/**
* A type metadata object holding type annotations.
*/
public static class Annotations implements Entry {
private List<Attribute.TypeCompound> annos;
public static final List<Attribute.TypeCompound> TO_BE_SET = List.nil();
public Annotations(List<Attribute.TypeCompound> annos) {
this.annos = annos;
}
/**
* Get the type annotations contained in this metadata.
*
* @return The annotations.
*/
public List<Attribute.TypeCompound> getAnnotations() {
return annos;
}
@Override
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 + " ]"; }
}
}