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