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