TypeMetadata.java revision 3170:dc017a37aac5
1168404Spjd/*
2168404Spjd * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3168404Spjd * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4168404Spjd *
5168404Spjd * This code is free software; you can redistribute it and/or modify it
6168404Spjd * under the terms of the GNU General Public License version 2 only, as
7168404Spjd * published by the Free Software Foundation.  Oracle designates this
8168404Spjd * particular file as subject to the "Classpath" exception as provided
9168404Spjd * by Oracle in the LICENSE file that accompanied this code.
10168404Spjd *
11168404Spjd * This code is distributed in the hope that it will be useful, but WITHOUT
12168404Spjd * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13168404Spjd * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14168404Spjd * version 2 for more details (a copy is included in the LICENSE file that
15168404Spjd * accompanied this code).
16168404Spjd *
17168404Spjd * You should have received a copy of the GNU General Public License version
18168404Spjd * 2 along with this work; if not, write to the Free Software Foundation,
19168404Spjd * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20168404Spjd *
21168404Spjd * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22168404Spjd * or visit www.oracle.com if you need additional information or have any
23168404Spjd * questions.
24168404Spjd */
25168404Spjd
26168404Spjdpackage com.sun.tools.javac.code;
27168404Spjd
28168404Spjdimport com.sun.tools.javac.util.Assert;
29168404Spjdimport com.sun.tools.javac.util.List;
30168404Spjdimport java.util.EnumMap;
31168404Spjdimport java.util.HashSet;
32168404Spjdimport java.util.Set;
33168404Spjd
34168404Spjd/**
35168404Spjd * TypeMetadata is essentially an immutable {@code EnumMap<Entry.Kind, <? extends Entry>>}
36168404Spjd *
37168404Spjd * A metadata class represented by a subtype of Entry can express a property on a Type instance.
38168404Spjd * Thers should be at most one instance of an Entry per Entry.Kind on any given Type instance.
39168404Spjd *
40168404Spjd * Metadata classes of a specific kind are responsible for how they combine themselvs.
41168404Spjd *
42168404Spjd * @implNote {@code Entry:combine} need not be commutative.
43168404Spjd */
44168404Spjdpublic class TypeMetadata {
45168404Spjd    public static final TypeMetadata EMPTY = new TypeMetadata();
46168404Spjd
47168404Spjd    private final EnumMap<Entry.Kind, Entry> contents;
48168404Spjd
49168404Spjd    /**
50168404Spjd     * Create a new empty TypeMetadata map.
51168404Spjd     */
52168404Spjd    private TypeMetadata() {
53168404Spjd        contents = new EnumMap<>(Entry.Kind.class);
54168404Spjd    }
55168404Spjd
56168404Spjd    /**
57168404Spjd     * Create a new TypeMetadata map containing the Entry {@code elem}.
58168404Spjd     *
59168404Spjd     * @param elem the sole contents of this map
60168404Spjd     */
61168712Spjd    public TypeMetadata(Entry elem) {
62168712Spjd        this();
63168712Spjd        Assert.checkNonNull(elem);
64168712Spjd        contents.put(elem.kind(), elem);
65168712Spjd    }
66168712Spjd
67168404Spjd    /**
68168712Spjd     * Creates a copy of TypeMetadata {@code other} with a shallow copy the other's metadata contents.
69168404Spjd     *
70168404Spjd     * @param other the TypeMetadata to copy contents from.
71168404Spjd     */
72168404Spjd    public TypeMetadata(TypeMetadata other) {
73168404Spjd        Assert.checkNonNull(other);
74168404Spjd        contents = other.contents.clone();
75168404Spjd    }
76168404Spjd
77168404Spjd    /**
78168404Spjd     * Return a copy of this TypeMetadata with the metadata entry for {@code elem.kind()} combined
79168404Spjd     * with {@code elem}.
80168404Spjd     *
81168404Spjd     * @param elem the new value
82168404Spjd     * @return a new TypeMetadata updated with {@code Entry elem}
83168404Spjd     */
84168404Spjd    public TypeMetadata combine(Entry elem) {
85168404Spjd        Assert.checkNonNull(elem);
86168404Spjd
87168404Spjd        TypeMetadata out = new TypeMetadata(this);
88168404Spjd        Entry.Kind key = elem.kind();
89168404Spjd        if (contents.containsKey(key)) {
90168404Spjd            out.add(key, this.contents.get(key).combine(elem));
91168404Spjd        } else {
92168404Spjd            out.add(key, elem);
93168404Spjd        }
94168404Spjd        return out;
95168404Spjd    }
96168404Spjd
97168404Spjd    /**
98168404Spjd     * Return a copy of this TypeMetadata with the metadata entry for all kinds from {@code other}
99168404Spjd     * combined with the same kind from this.
100168404Spjd     *
101168404Spjd     * @param other the TypeMetadata to combine with this
102168404Spjd     * @return a new TypeMetadata updated with all entries from {@code other}
103168404Spjd     */
104168404Spjd    public TypeMetadata combineAll(TypeMetadata other) {
105168404Spjd        Assert.checkNonNull(other);
106168404Spjd
107168404Spjd        TypeMetadata out = new TypeMetadata();
108168404Spjd        Set<Entry.Kind> keys = new HashSet<>(contents.keySet());
109168404Spjd        keys.addAll(other.contents.keySet());
110168404Spjd
111168404Spjd        for(Entry.Kind key : keys) {
112168404Spjd            if (contents.containsKey(key)) {
113168404Spjd                if (other.contents.containsKey(key)) {
114168404Spjd                    out.add(key, contents.get(key).combine(other.contents.get(key)));
115168404Spjd                } else {
116168404Spjd                    out.add(key, contents.get(key));
117168404Spjd                }
118168404Spjd            } else if (other.contents.containsKey(key)) {
119168404Spjd                out.add(key, other.contents.get(key));
120168712Spjd            }
121168712Spjd        }
122168404Spjd        return out;
123168404Spjd    }
124168404Spjd
125168404Spjd    /**
126168404Spjd     * Return a TypeMetadata with the metadata entry for {@code kind} removed.
127168404Spjd     *
128168404Spjd     * This may be the same instance or a new TypeMetadata.
129168712Spjd     *
130168715Spjd     * @param kind the {@code Kind} to remove metadata for
131168404Spjd     * @return a new TypeMetadata without {@code Kind kind}
132168712Spjd     */
133168712Spjd    public TypeMetadata without(Entry.Kind kind) {
134168712Spjd        if (this == EMPTY || contents.get(kind) == null)
135168712Spjd            return this;
136168712Spjd
137168712Spjd        TypeMetadata out = new TypeMetadata(this);
138168404Spjd        out.contents.remove(kind);
139168404Spjd        return out.contents.isEmpty() ? EMPTY : out;
140168404Spjd    }
141168404Spjd
142168404Spjd    public Entry get(Entry.Kind kind) {
143168404Spjd        return contents.get(kind);
144168404Spjd    }
145168404Spjd
146168404Spjd    private void add(Entry.Kind kind, Entry elem) {
147168404Spjd        contents.put(kind, elem);
148168404Spjd    }
149168404Spjd
150168404Spjd    public interface Entry {
151168404Spjd
152168404Spjd        public enum Kind {
153168404Spjd            ANNOTATIONS
154168404Spjd        }
155168404Spjd
156168404Spjd        /**
157168404Spjd         * Get the kind of metadata this object represents
158168404Spjd         */
159168404Spjd        public Kind kind();
160168404Spjd
161168404Spjd        /**
162168404Spjd         * Combine this type metadata with another metadata of the
163168404Spjd         * same kind.
164168404Spjd         *
165168404Spjd         * @param other The metadata with which to combine this one.
166168404Spjd         * @return The combined metadata.
167168404Spjd         */
168168404Spjd        public Entry combine(Entry other);
169168404Spjd    }
170168404Spjd
171168404Spjd    /**
172168404Spjd     * A type metadata object holding type annotations.
173168404Spjd     */
174168404Spjd    public static class Annotations implements Entry {
175168404Spjd        private List<Attribute.TypeCompound> annos;
176168404Spjd
177168404Spjd        public static final List<Attribute.TypeCompound> TO_BE_SET = List.nil();
178168404Spjd
179168404Spjd        public Annotations(List<Attribute.TypeCompound> annos) {
180168404Spjd            this.annos = annos;
181168404Spjd        }
182168404Spjd
183168404Spjd        /**
184168404Spjd         * Get the type annotations contained in this metadata.
185168404Spjd         *
186168404Spjd         * @return The annotations.
187168404Spjd         */
188168404Spjd        public List<Attribute.TypeCompound> getAnnotations() {
189168404Spjd            return annos;
190168404Spjd        }
191168404Spjd
192168404Spjd        @Override
193168404Spjd        public Annotations combine(Entry other) {
194168404Spjd            Assert.check(annos == TO_BE_SET);
195168404Spjd            annos = ((Annotations)other).annos;
196168404Spjd            return this;
197168404Spjd        }
198168404Spjd
199168404Spjd        @Override
200168404Spjd        public Kind kind() { return Kind.ANNOTATIONS; }
201168404Spjd
202168404Spjd        @Override
203168404Spjd        public String toString() { return "ANNOTATIONS [ " + annos + " ]"; }
204168404Spjd    }
205168404Spjd}
206168404Spjd