/* * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.tools.javac.model; import java.util.List; import java.util.Set; import java.util.EnumSet; import javax.lang.model.element.*; import javax.lang.model.type.*; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.util.*; /** * Utility methods for operating on types. * * <p><b>This is NOT part of any API supported by Sun Microsystems. * If you write code that depends on this, you do so at your own * risk. This code and its internal interfaces are subject to change * or deletion without notice.</b></p> */ public class JavacTypes implements javax.lang.model.util.Types { private Symtab syms; private Types types; private static final Context.Key<JavacTypes> KEY = new Context.Key<JavacTypes>(); public static JavacTypes instance(Context context) { JavacTypes instance = context.get(KEY); if (instance == null) { instance = new JavacTypes(context); context.put(KEY, instance); } return instance; } /** * Public for use only by JavacProcessingEnvironment */ // TODO JavacTypes constructor should be protected public JavacTypes(Context context) { setContext(context); } /** * Use a new context. May be called from outside to update * internal state for a new annotation-processing round. * This instance is *not* then registered with the new context. */ public void setContext(Context context) { syms = Symtab.instance(context); types = Types.instance(context); } public Element asElement(TypeMirror t) { Type type = cast(Type.class, t); if (type.tag != TypeTags.CLASS && type.tag != TypeTags.TYPEVAR) return null; return type.asElement(); } public boolean isSameType(TypeMirror t1, TypeMirror t2) { return types.isSameType((Type) t1, (Type) t2); } public boolean isSubtype(TypeMirror t1, TypeMirror t2) { validateTypeNotIn(t1, EXEC_OR_PKG); validateTypeNotIn(t2, EXEC_OR_PKG); return types.isSubtype((Type) t1, (Type) t2); } public boolean isAssignable(TypeMirror t1, TypeMirror t2) { validateTypeNotIn(t1, EXEC_OR_PKG); validateTypeNotIn(t2, EXEC_OR_PKG); return types.isAssignable((Type) t1, (Type) t2); } public boolean contains(TypeMirror t1, TypeMirror t2) { validateTypeNotIn(t1, EXEC_OR_PKG); validateTypeNotIn(t2, EXEC_OR_PKG); return ((Type) t1).contains((Type) t2); } public boolean isSubsignature(ExecutableType m1, ExecutableType m2) { return types.isSubSignature((Type) m1, (Type) m2); } public List<Type> directSupertypes(TypeMirror t) { validateTypeNotIn(t, EXEC_OR_PKG); Type type = (Type) t; Type sup = types.supertype(type); return (sup == Type.noType || sup == type || sup == null) ? types.interfaces(type) : types.interfaces(type).prepend(sup); } public TypeMirror erasure(TypeMirror t) { if (t.getKind() == TypeKind.PACKAGE) throw new IllegalArgumentException(t.toString()); return types.erasure((Type) t); } public TypeElement boxedClass(PrimitiveType p) { return types.boxedClass((Type) p); } public PrimitiveType unboxedType(TypeMirror t) { if (t.getKind() != TypeKind.DECLARED) throw new IllegalArgumentException(t.toString()); Type unboxed = types.unboxedType((Type) t); if (! unboxed.isPrimitive()) // only true primitives, not void throw new IllegalArgumentException(t.toString()); return unboxed; } public TypeMirror capture(TypeMirror t) { validateTypeNotIn(t, EXEC_OR_PKG); return types.capture((Type) t); } public PrimitiveType getPrimitiveType(TypeKind kind) { switch (kind) { case BOOLEAN: return syms.booleanType; case BYTE: return syms.byteType; case SHORT: return syms.shortType; case INT: return syms.intType; case LONG: return syms.longType; case CHAR: return syms.charType; case FLOAT: return syms.floatType; case DOUBLE: return syms.doubleType; default: throw new IllegalArgumentException("Not a primitive type: " + kind); } } public NullType getNullType() { return (NullType) syms.botType; } public NoType getNoType(TypeKind kind) { switch (kind) { case VOID: return syms.voidType; case NONE: return Type.noType; default: throw new IllegalArgumentException(kind.toString()); } } public ArrayType getArrayType(TypeMirror componentType) { switch (componentType.getKind()) { case VOID: case EXECUTABLE: case WILDCARD: // heh! case PACKAGE: throw new IllegalArgumentException(componentType.toString()); } return new Type.ArrayType((Type) componentType, syms.arrayClass); } public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) { BoundKind bkind; Type bound; if (extendsBound == null && superBound == null) { bkind = BoundKind.UNBOUND; bound = syms.objectType; } else if (superBound == null) { bkind = BoundKind.EXTENDS; bound = (Type) extendsBound; } else if (extendsBound == null) { bkind = BoundKind.SUPER; bound = (Type) superBound; } else { throw new IllegalArgumentException( "Extends and super bounds cannot both be provided"); } switch (bound.getKind()) { case ARRAY: case DECLARED: case ERROR: case TYPEVAR: return new Type.WildcardType(bound, bkind, syms.boundClass); default: throw new IllegalArgumentException(bound.toString()); } } public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) { ClassSymbol sym = (ClassSymbol) typeElem; if (typeArgs.length == 0) return (DeclaredType) sym.erasure(types); if (sym.type.getEnclosingType().isParameterized()) throw new IllegalArgumentException(sym.toString()); return getDeclaredType0(sym.type.getEnclosingType(), sym, typeArgs); } public DeclaredType getDeclaredType(DeclaredType enclosing, TypeElement typeElem, TypeMirror... typeArgs) { if (enclosing == null) return getDeclaredType(typeElem, typeArgs); ClassSymbol sym = (ClassSymbol) typeElem; Type outer = (Type) enclosing; if (outer.tsym != sym.owner.enclClass()) throw new IllegalArgumentException(enclosing.toString()); if (!outer.isParameterized()) return getDeclaredType(typeElem, typeArgs); return getDeclaredType0(outer, sym, typeArgs); } // where private DeclaredType getDeclaredType0(Type outer, ClassSymbol sym, TypeMirror... typeArgs) { if (typeArgs.length != sym.type.getTypeArguments().length()) throw new IllegalArgumentException( "Incorrect number of type arguments"); ListBuffer<Type> targs = new ListBuffer<Type>(); for (TypeMirror t : typeArgs) { if (!(t instanceof ReferenceType || t instanceof WildcardType)) throw new IllegalArgumentException(t.toString()); targs.append((Type) t); } // TODO: Would like a way to check that type args match formals. return (DeclaredType) new Type.ClassType(outer, targs.toList(), sym); } /** * Returns the type of an element when that element is viewed as * a member of, or otherwise directly contained by, a given type. * For example, * when viewed as a member of the parameterized type {@code Set<String>}, * the {@code Set.add} method is an {@code ExecutableType} * whose parameter is of type {@code String}. * * @param containing the containing type * @param element the element * @return the type of the element as viewed from the containing type * @throws IllegalArgumentException if the element is not a valid one * for the given type */ public TypeMirror asMemberOf(DeclaredType containing, Element element) { Type site = (Type)containing; Symbol sym = (Symbol)element; if (types.asSuper(site, sym.getEnclosingElement()) == null) throw new IllegalArgumentException(sym + "@" + site); return types.memberType(site, sym); } private static final Set<TypeKind> EXEC_OR_PKG = EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE); /** * Throws an IllegalArgumentException if a type's kind is one of a set. */ private void validateTypeNotIn(TypeMirror t, Set<TypeKind> invalidKinds) { if (invalidKinds.contains(t.getKind())) throw new IllegalArgumentException(t.toString()); } /** * Returns an object cast to the specified type. * @throws NullPointerException if the object is {@code null} * @throws IllegalArgumentException if the object is of the wrong type */ private static <T> T cast(Class<T> clazz, Object o) { if (! clazz.isInstance(o)) throw new IllegalArgumentException(o.toString()); return clazz.cast(o); } }