1 /*
2  * Copyright (c) 2005, 2013, 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 
26 package com.sun.tools.javac.model;
27 
28 import java.util.Collections;
29 import java.util.EnumSet;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Set;
33 
34 import javax.lang.model.element.*;
35 import javax.lang.model.type.*;
36 
37 import com.sun.tools.javac.code.*;
38 import com.sun.tools.javac.code.Symbol.*;
39 import com.sun.tools.javac.util.*;
40 
41 /**
42  * Utility methods for operating on types.
43  *
44  * <p><b>This is NOT part of any supported API.
45  * If you write code that depends on this, you do so at your own
46  * risk.  This code and its internal interfaces are subject to change
47  * or deletion without notice.</b></p>
48  */
49 public class JavacTypes implements javax.lang.model.util.Types {
50 
51     private Symtab syms;
52     private Types types;
53 
instance(Context context)54     public static JavacTypes instance(Context context) {
55         JavacTypes instance = context.get(JavacTypes.class);
56         if (instance == null)
57             instance = new JavacTypes(context);
58         return instance;
59     }
60 
61     /**
62      * Public for use only by JavacProcessingEnvironment
63      */
JavacTypes(Context context)64     protected JavacTypes(Context context) {
65         setContext(context);
66     }
67 
68     /**
69      * Use a new context.  May be called from outside to update
70      * internal state for a new annotation-processing round.
71      */
setContext(Context context)72     public void setContext(Context context) {
73         context.put(JavacTypes.class, this);
74         syms = Symtab.instance(context);
75         types = Types.instance(context);
76     }
77 
asElement(TypeMirror t)78     public Element asElement(TypeMirror t) {
79         switch (t.getKind()) {
80             case DECLARED:
81             case INTERSECTION:
82             case ERROR:
83             case TYPEVAR:
84                 Type type = cast(Type.class, t);
85                 return type.asElement();
86             default:
87                 return null;
88         }
89     }
90 
isSameType(TypeMirror t1, TypeMirror t2)91     public boolean isSameType(TypeMirror t1, TypeMirror t2) {
92         return types.isSameType((Type) t1, (Type) t2);
93     }
94 
isSubtype(TypeMirror t1, TypeMirror t2)95     public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
96         validateTypeNotIn(t1, EXEC_OR_PKG);
97         validateTypeNotIn(t2, EXEC_OR_PKG);
98         return types.isSubtype((Type) t1, (Type) t2);
99     }
100 
isAssignable(TypeMirror t1, TypeMirror t2)101     public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
102         validateTypeNotIn(t1, EXEC_OR_PKG);
103         validateTypeNotIn(t2, EXEC_OR_PKG);
104         return types.isAssignable((Type) t1, (Type) t2);
105     }
106 
contains(TypeMirror t1, TypeMirror t2)107     public boolean contains(TypeMirror t1, TypeMirror t2) {
108         validateTypeNotIn(t1, EXEC_OR_PKG);
109         validateTypeNotIn(t2, EXEC_OR_PKG);
110         return types.containsType((Type) t1, (Type) t2);
111     }
112 
isSubsignature(ExecutableType m1, ExecutableType m2)113     public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
114         return types.isSubSignature((Type) m1, (Type) m2);
115     }
116 
directSupertypes(TypeMirror t)117     public List<Type> directSupertypes(TypeMirror t) {
118         validateTypeNotIn(t, EXEC_OR_PKG);
119         return types.directSupertypes((Type) t);
120     }
121 
erasure(TypeMirror t)122     public TypeMirror erasure(TypeMirror t) {
123         if (t.getKind() == TypeKind.PACKAGE)
124             throw new IllegalArgumentException(t.toString());
125         return types.erasure((Type) t);
126     }
127 
boxedClass(PrimitiveType p)128     public TypeElement boxedClass(PrimitiveType p) {
129         return types.boxedClass((Type) p);
130     }
131 
unboxedType(TypeMirror t)132     public PrimitiveType unboxedType(TypeMirror t) {
133         if (t.getKind() != TypeKind.DECLARED)
134             throw new IllegalArgumentException(t.toString());
135         Type unboxed = types.unboxedType((Type) t);
136         if (! unboxed.isPrimitive())    // only true primitives, not void
137             throw new IllegalArgumentException(t.toString());
138         return (PrimitiveType)unboxed;
139     }
140 
capture(TypeMirror t)141     public TypeMirror capture(TypeMirror t) {
142         validateTypeNotIn(t, EXEC_OR_PKG);
143         return types.capture((Type) t);
144     }
145 
getPrimitiveType(TypeKind kind)146     public PrimitiveType getPrimitiveType(TypeKind kind) {
147         switch (kind) {
148         case BOOLEAN:   return syms.booleanType;
149         case BYTE:      return syms.byteType;
150         case SHORT:     return syms.shortType;
151         case INT:       return syms.intType;
152         case LONG:      return syms.longType;
153         case CHAR:      return syms.charType;
154         case FLOAT:     return syms.floatType;
155         case DOUBLE:    return syms.doubleType;
156         default:
157             throw new IllegalArgumentException("Not a primitive type: " + kind);
158         }
159     }
160 
getNullType()161     public NullType getNullType() {
162         return (NullType) syms.botType;
163     }
164 
getNoType(TypeKind kind)165     public NoType getNoType(TypeKind kind) {
166         switch (kind) {
167         case VOID:      return syms.voidType;
168         case NONE:      return Type.noType;
169         default:
170             throw new IllegalArgumentException(kind.toString());
171         }
172     }
173 
getArrayType(TypeMirror componentType)174     public ArrayType getArrayType(TypeMirror componentType) {
175         switch (componentType.getKind()) {
176         case VOID:
177         case EXECUTABLE:
178         case WILDCARD:  // heh!
179         case PACKAGE:
180             throw new IllegalArgumentException(componentType.toString());
181         }
182         return new Type.ArrayType((Type) componentType, syms.arrayClass);
183     }
184 
getWildcardType(TypeMirror extendsBound, TypeMirror superBound)185     public WildcardType getWildcardType(TypeMirror extendsBound,
186                                         TypeMirror superBound) {
187         BoundKind bkind;
188         Type bound;
189         if (extendsBound == null && superBound == null) {
190             bkind = BoundKind.UNBOUND;
191             bound = syms.objectType;
192         } else if (superBound == null) {
193             bkind = BoundKind.EXTENDS;
194             bound = (Type) extendsBound;
195         } else if (extendsBound == null) {
196             bkind = BoundKind.SUPER;
197             bound = (Type) superBound;
198         } else {
199             throw new IllegalArgumentException(
200                     "Extends and super bounds cannot both be provided");
201         }
202         switch (bound.getKind()) {
203         case ARRAY:
204         case DECLARED:
205         case ERROR:
206         case TYPEVAR:
207             return new Type.WildcardType(bound, bkind, syms.boundClass);
208         default:
209             throw new IllegalArgumentException(bound.toString());
210         }
211     }
212 
getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs)213     public DeclaredType getDeclaredType(TypeElement typeElem,
214                                         TypeMirror... typeArgs) {
215         ClassSymbol sym = (ClassSymbol) typeElem;
216 
217         if (typeArgs.length == 0)
218             return (DeclaredType) sym.erasure(types);
219         if (sym.type.getEnclosingType().isParameterized())
220             throw new IllegalArgumentException(sym.toString());
221 
222         return getDeclaredType0(sym.type.getEnclosingType(), sym, typeArgs);
223     }
224 
getDeclaredType(DeclaredType enclosing, TypeElement typeElem, TypeMirror... typeArgs)225     public DeclaredType getDeclaredType(DeclaredType enclosing,
226                                         TypeElement typeElem,
227                                         TypeMirror... typeArgs) {
228         if (enclosing == null)
229             return getDeclaredType(typeElem, typeArgs);
230 
231         ClassSymbol sym = (ClassSymbol) typeElem;
232         Type outer = (Type) enclosing;
233 
234         if (outer.tsym != sym.owner.enclClass())
235             throw new IllegalArgumentException(enclosing.toString());
236         if (!outer.isParameterized())
237             return getDeclaredType(typeElem, typeArgs);
238 
239         return getDeclaredType0(outer, sym, typeArgs);
240     }
241     // where
getDeclaredType0(Type outer, ClassSymbol sym, TypeMirror... typeArgs)242         private DeclaredType getDeclaredType0(Type outer,
243                                               ClassSymbol sym,
244                                               TypeMirror... typeArgs) {
245             if (typeArgs.length != sym.type.getTypeArguments().length())
246                 throw new IllegalArgumentException(
247                 "Incorrect number of type arguments");
248 
249             ListBuffer<Type> targs = new ListBuffer<Type>();
250             for (TypeMirror t : typeArgs) {
251                 if (!(t instanceof ReferenceType || t instanceof WildcardType))
252                     throw new IllegalArgumentException(t.toString());
253                 targs.append((Type) t);
254             }
255             // TODO: Would like a way to check that type args match formals.
256 
257             return (DeclaredType) new Type.ClassType(outer, targs.toList(), sym);
258         }
259 
260     /**
261      * Returns the type of an element when that element is viewed as
262      * a member of, or otherwise directly contained by, a given type.
263      * For example,
264      * when viewed as a member of the parameterized type {@code Set<String>},
265      * the {@code Set.add} method is an {@code ExecutableType}
266      * whose parameter is of type {@code String}.
267      *
268      * @param containing  the containing type
269      * @param element     the element
270      * @return the type of the element as viewed from the containing type
271      * @throws IllegalArgumentException if the element is not a valid one
272      *          for the given type
273      */
asMemberOf(DeclaredType containing, Element element)274     public TypeMirror asMemberOf(DeclaredType containing, Element element) {
275         Type site = (Type)containing;
276         Symbol sym = (Symbol)element;
277         if (types.asSuper(site, sym.getEnclosingElement()) == null)
278             throw new IllegalArgumentException(sym + "@" + site);
279         return types.memberType(site, sym);
280     }
281 
282 
283     private static final Set<TypeKind> EXEC_OR_PKG =
284             EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE);
285 
286     /**
287      * Throws an IllegalArgumentException if a type's kind is one of a set.
288      */
validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds)289     private void validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds) {
290         if (invalidKinds.contains(t.getKind()))
291             throw new IllegalArgumentException(t.toString());
292     }
293 
294     /**
295      * Returns an object cast to the specified type.
296      * @throws NullPointerException if the object is {@code null}
297      * @throws IllegalArgumentException if the object is of the wrong type
298      */
cast(Class<T> clazz, Object o)299     private static <T> T cast(Class<T> clazz, Object o) {
300         if (! clazz.isInstance(o))
301             throw new IllegalArgumentException(o.toString());
302         return clazz.cast(o);
303     }
304 
getOverriddenMethods(Element elem)305     public Set<MethodSymbol> getOverriddenMethods(Element elem) {
306         if (elem.getKind() != ElementKind.METHOD
307                 || elem.getModifiers().contains(Modifier.STATIC)
308                 || elem.getModifiers().contains(Modifier.PRIVATE))
309             return Collections.emptySet();
310 
311         if (!(elem instanceof MethodSymbol))
312             throw new IllegalArgumentException();
313 
314         MethodSymbol m = (MethodSymbol) elem;
315         ClassSymbol origin = (ClassSymbol) m.owner;
316 
317         Set<MethodSymbol> results = new LinkedHashSet<MethodSymbol>();
318         for (Type t : types.closure(origin.type)) {
319             if (t != origin.type) {
320                 ClassSymbol c = (ClassSymbol) t.tsym;
321                 for (Scope.Entry e = c.members().lookup(m.name); e.scope != null; e = e.next()) {
322                     if (e.sym.kind == Kinds.MTH && m.overrides(e.sym, origin, types, true)) {
323                         results.add((MethodSymbol) e.sym);
324                     }
325                 }
326             }
327         }
328 
329         return results;
330     }
331 }
332