TypeMetadata.java revision 2877:62e285806e83
1/* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights 3 * reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE 4 * HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27package com.sun.tools.javac.code; 28 29import com.sun.tools.javac.util.Assert; 30import com.sun.tools.javac.util.List; 31import java.util.EnumMap; 32import java.util.HashSet; 33import java.util.Set; 34 35/** 36 * TypeMetadata is essentially an immutable {@code EnumMap<Entry.Kind, <? extends Entry>>} 37 * 38 * A metadata class represented by a subtype of Entry can express a property on a Type instance. 39 * Thers should be at most one instance of an Entry per Entry.Kind on any given Type instance. 40 * 41 * Metadata classes of a specific kind are responsible for how they combine themselvs. 42 * 43 * @implNote {@code Entry:combine} need not be commutative. 44 */ 45public class TypeMetadata { 46 public static final TypeMetadata EMPTY = new TypeMetadata(); 47 48 private final EnumMap<Entry.Kind, Entry> contents; 49 50 /** 51 * Create a new empty TypeMetadata map. 52 */ 53 private TypeMetadata() { 54 contents = new EnumMap<>(Entry.Kind.class); 55 } 56 57 /** 58 * Create a new TypeMetadata map containing the Entry {@code elem}. 59 * 60 * @param elem the sole contents of this map 61 */ 62 public TypeMetadata(Entry elem) { 63 this(); 64 Assert.checkNonNull(elem); 65 contents.put(elem.kind(), elem); 66 } 67 68 /** 69 * Creates a copy of TypeMetadata {@code other} with a shallow copy the other's metadata contents. 70 * 71 * @param other the TypeMetadata to copy contents from. 72 */ 73 public TypeMetadata(TypeMetadata other) { 74 Assert.checkNonNull(other); 75 contents = other.contents.clone(); 76 } 77 78 /** 79 * Return a copy of this TypeMetadata with the metadata entry for {@code elem.kind()} combined 80 * with {@code elem}. 81 * 82 * @param elem the new value 83 * @return a new TypeMetadata updated with {@code Entry elem} 84 */ 85 public TypeMetadata combine(Entry elem) { 86 Assert.checkNonNull(elem); 87 88 TypeMetadata out = new TypeMetadata(this); 89 Entry.Kind key = elem.kind(); 90 if (contents.containsKey(key)) { 91 out.add(key, this.contents.get(key).combine(elem)); 92 } else { 93 out.add(key, elem); 94 } 95 return out; 96 } 97 98 /** 99 * Return a copy of this TypeMetadata with the metadata entry for all kinds from {@code other} 100 * combined with the same kind from this. 101 * 102 * @param other the TypeMetadata to combine with this 103 * @return a new TypeMetadata updated with all entries from {@code other} 104 */ 105 public TypeMetadata combineAll(TypeMetadata other) { 106 Assert.checkNonNull(other); 107 108 TypeMetadata out = new TypeMetadata(); 109 Set<Entry.Kind> keys = new HashSet<>(contents.keySet()); 110 keys.addAll(other.contents.keySet()); 111 112 for(Entry.Kind key : keys) { 113 if (contents.containsKey(key)) { 114 if (other.contents.containsKey(key)) { 115 out.add(key, contents.get(key).combine(other.contents.get(key))); 116 } else { 117 out.add(key, contents.get(key)); 118 } 119 } else if (other.contents.containsKey(key)) { 120 out.add(key, other.contents.get(key)); 121 } 122 } 123 return out; 124 } 125 126 /** 127 * Return a TypeMetadata with the metadata entry for {@code kind} removed. 128 * 129 * This may be the same instance or a new TypeMetadata. 130 * 131 * @param kind the {@code Kind} to remove metadata for 132 * @return a new TypeMetadata without {@code Kind kind} 133 */ 134 public TypeMetadata without(Entry.Kind kind) { 135 if (this == EMPTY || contents.get(kind) == null) 136 return this; 137 138 TypeMetadata out = new TypeMetadata(this); 139 out.contents.remove(kind); 140 return out.contents.isEmpty() ? EMPTY : out; 141 } 142 143 public Entry get(Entry.Kind kind) { 144 return contents.get(kind); 145 } 146 147 private void add(Entry.Kind kind, Entry elem) { 148 contents.put(kind, elem); 149 } 150 151 public interface Entry { 152 153 public enum Kind { 154 ANNOTATIONS 155 } 156 157 /** 158 * Get the kind of metadata this object represents 159 */ 160 public Kind kind(); 161 162 /** 163 * Combine this type metadata with another metadata of the 164 * same kind. 165 * 166 * @param other The metadata with which to combine this one. 167 * @return The combined metadata. 168 */ 169 public Entry combine(Entry other); 170 } 171 172 /** 173 * A type metadata object holding type annotations. 174 */ 175 public static class Annotations implements Entry { 176 private List<Attribute.TypeCompound> annos; 177 178 public static final List<Attribute.TypeCompound> TO_BE_SET = List.nil(); 179 180 public Annotations(List<Attribute.TypeCompound> annos) { 181 this.annos = annos; 182 } 183 184 /** 185 * Get the type annotations contained in this metadata. 186 * 187 * @return The annotations. 188 */ 189 public List<Attribute.TypeCompound> getAnnotations() { 190 return annos; 191 } 192 193 @Override 194 public Annotations combine(Entry other) { 195 Assert.check(annos == TO_BE_SET); 196 annos = ((Annotations)other).annos; 197 return this; 198 } 199 200 @Override 201 public Kind kind() { return Kind.ANNOTATIONS; } 202 203 @Override 204 public String toString() { return "ANNOTATIONS [ " + annos + " ]"; } 205 } 206} 207