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