aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.internal.jxc.model.nav; aoqi@0: aoqi@0: import com.sun.source.tree.CompilationUnitTree; aoqi@0: import com.sun.source.util.TreePath; aoqi@0: import com.sun.source.util.Trees; aoqi@0: import com.sun.xml.internal.bind.v2.model.nav.Navigator; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.Location; aefimov@835: import java.lang.annotation.Annotation; aefimov@835: import java.util.ArrayList; aefimov@835: import java.util.Collection; aefimov@835: import java.util.HashMap; aefimov@835: import java.util.List; aefimov@835: import java.util.Map; aoqi@0: import javax.annotation.processing.ProcessingEnvironment; aoqi@0: import javax.lang.model.element.AnnotationMirror; aoqi@0: import javax.lang.model.element.Element; aoqi@0: import javax.lang.model.element.ElementKind; aoqi@0: import javax.lang.model.element.ExecutableElement; aoqi@0: import javax.lang.model.element.Modifier; aoqi@0: import javax.lang.model.element.TypeElement; aoqi@0: import javax.lang.model.element.TypeParameterElement; aoqi@0: import javax.lang.model.element.VariableElement; aoqi@0: import javax.lang.model.type.ArrayType; aoqi@0: import javax.lang.model.type.DeclaredType; aoqi@0: import javax.lang.model.type.PrimitiveType; aoqi@0: import javax.lang.model.type.TypeKind; aoqi@0: import javax.lang.model.type.TypeMirror; aoqi@0: import javax.lang.model.type.TypeVariable; aoqi@0: import javax.lang.model.type.TypeVisitor; aoqi@0: import javax.lang.model.type.WildcardType; aoqi@0: import javax.lang.model.util.ElementFilter; aoqi@0: import javax.lang.model.util.Elements; aoqi@0: import javax.lang.model.util.SimpleTypeVisitor6; aoqi@0: import javax.lang.model.util.Types; aoqi@0: aoqi@0: /** aoqi@0: * {@link Navigator} implementation for annotation processing. aoqi@0: * TODO: check the spec on how generics are supposed to be handled aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi (kk@kohsuke.org) aoqi@0: */ aoqi@0: public final class ApNavigator implements Navigator { aoqi@0: aoqi@0: private final ProcessingEnvironment env; aoqi@0: aoqi@0: private final PrimitiveType primitiveByte; aoqi@0: aoqi@0: public ApNavigator(ProcessingEnvironment env) { aoqi@0: this.env = env; aoqi@0: this.primitiveByte = env.getTypeUtils().getPrimitiveType(TypeKind.BYTE); aoqi@0: } aoqi@0: aoqi@0: public TypeElement getSuperClass(TypeElement typeElement) { aoqi@0: if (typeElement.getKind().equals(ElementKind.CLASS)) { aoqi@0: TypeMirror sup = typeElement.getSuperclass(); aoqi@0: if (!sup.getKind().equals(TypeKind.NONE)) aoqi@0: return (TypeElement) ((DeclaredType) sup).asElement(); aoqi@0: else aoqi@0: return null; aoqi@0: } aoqi@0: return env.getElementUtils().getTypeElement(Object.class.getName()); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getBaseClass(TypeMirror type, TypeElement sup) { aoqi@0: return baseClassFinder.visit(type, sup); aoqi@0: } aoqi@0: aoqi@0: public String getClassName(TypeElement t) { aoqi@0: return t.getQualifiedName().toString(); aoqi@0: } aoqi@0: aoqi@0: public String getTypeName(TypeMirror typeMirror) { aoqi@0: return typeMirror.toString(); aoqi@0: } aoqi@0: aoqi@0: public String getClassShortName(TypeElement t) { aoqi@0: return t.getSimpleName().toString(); aoqi@0: } aoqi@0: aoqi@0: public Collection getDeclaredFields(TypeElement typeElement) { aoqi@0: return ElementFilter.fieldsIn(typeElement.getEnclosedElements()); aoqi@0: } aoqi@0: aoqi@0: public VariableElement getDeclaredField(TypeElement clazz, String fieldName) { aoqi@0: for (VariableElement fd : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { aoqi@0: if (fd.getSimpleName().toString().equals(fieldName)) aoqi@0: return fd; aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public Collection getDeclaredMethods(TypeElement typeElement) { aoqi@0: return ElementFilter.methodsIn(typeElement.getEnclosedElements()); aoqi@0: } aoqi@0: aoqi@0: public TypeElement getDeclaringClassForField(VariableElement f) { aoqi@0: return (TypeElement) f.getEnclosingElement(); aoqi@0: } aoqi@0: aoqi@0: public TypeElement getDeclaringClassForMethod(ExecutableElement m) { aoqi@0: return (TypeElement) m.getEnclosingElement(); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getFieldType(VariableElement f) { aoqi@0: return f.asType(); aoqi@0: } aoqi@0: aoqi@0: public String getFieldName(VariableElement f) { aoqi@0: return f.getSimpleName().toString(); aoqi@0: } aoqi@0: aoqi@0: public String getMethodName(ExecutableElement m) { aoqi@0: return m.getSimpleName().toString(); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getReturnType(ExecutableElement m) { aoqi@0: return m.getReturnType(); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror[] getMethodParameters(ExecutableElement m) { aoqi@0: Collection ps = m.getParameters(); aoqi@0: TypeMirror[] r = new TypeMirror[ps.size()]; aoqi@0: int i=0; aoqi@0: for (VariableElement p : ps) aoqi@0: r[i++] = p.asType(); aoqi@0: return r; aoqi@0: } aoqi@0: aoqi@0: public boolean isStaticMethod(ExecutableElement m) { aoqi@0: return hasModifier(m, Modifier.STATIC); aoqi@0: } aoqi@0: aoqi@0: public boolean isFinalMethod(ExecutableElement m) { aoqi@0: return hasModifier(m, Modifier.FINAL); aoqi@0: } aoqi@0: aoqi@0: private boolean hasModifier(Element d, Modifier mod) { aoqi@0: return d.getModifiers().contains(mod); aoqi@0: } aoqi@0: aoqi@0: public boolean isSubClassOf(TypeMirror sub, TypeMirror sup) { aoqi@0: if(sup==DUMMY) aoqi@0: // see ref(). if the sub type is known to Annotation Processing, aoqi@0: // its base class must be known. Thus if the sup is DUMMY, aoqi@0: // it cannot possibly be the super type. aoqi@0: return false; aoqi@0: return env.getTypeUtils().isSubtype(sub,sup); aoqi@0: } aoqi@0: aoqi@0: private String getSourceClassName(Class clazz) { aoqi@0: Class d = clazz.getDeclaringClass(); aoqi@0: if(d==null) aoqi@0: return clazz.getName(); aoqi@0: else { aoqi@0: String shortName = clazz.getName().substring(d.getName().length()+1/*for $*/); aoqi@0: return getSourceClassName(d)+'.'+shortName; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public TypeMirror ref(Class c) { aoqi@0: if(c.isArray()) aoqi@0: return env.getTypeUtils().getArrayType( ref(c.getComponentType()) ); aoqi@0: if(c.isPrimitive()) aoqi@0: return getPrimitive(c); aoqi@0: TypeElement t = env.getElementUtils().getTypeElement(getSourceClassName(c)); aoqi@0: // Annotation Processing only operates on a set of classes used in the compilation, aoqi@0: // and it won't recognize additional classes (even if they are visible from javac) aoqi@0: // and return null. aoqi@0: // aoqi@0: // this is causing a problem where we check if a type is collection. aoqi@0: // so until the problem is fixed in Annotation Processing, work around the issue aoqi@0: // by returning a dummy token aoqi@0: // TODO: check if this is still valid aoqi@0: if(t==null) aoqi@0: return DUMMY; aoqi@0: return env.getTypeUtils().getDeclaredType(t); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror use(TypeElement t) { aoqi@0: assert t != null; aoqi@0: return env.getTypeUtils().getDeclaredType(t); aoqi@0: } aoqi@0: aoqi@0: public TypeElement asDecl(TypeMirror m) { aoqi@0: m = env.getTypeUtils().erasure(m); aoqi@0: if (m.getKind().equals(TypeKind.DECLARED)) { aoqi@0: DeclaredType d = (DeclaredType) m; aoqi@0: return (TypeElement) d.asElement(); aoqi@0: } else aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public TypeElement asDecl(Class c) { aoqi@0: return env.getElementUtils().getTypeElement(getSourceClassName(c)); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror erasure(TypeMirror t) { aoqi@0: Types tu = env.getTypeUtils(); aoqi@0: t = tu.erasure(t); aoqi@0: if (t.getKind().equals(TypeKind.DECLARED)) { aoqi@0: DeclaredType dt = (DeclaredType)t; aoqi@0: if (!dt.getTypeArguments().isEmpty()) aoqi@0: return tu.getDeclaredType((TypeElement) dt.asElement()); aoqi@0: } aoqi@0: return t; aoqi@0: } aoqi@0: aoqi@0: public boolean isAbstract(TypeElement clazz) { aoqi@0: return hasModifier(clazz,Modifier.ABSTRACT); aoqi@0: } aoqi@0: aoqi@0: public boolean isFinal(TypeElement clazz) { aoqi@0: return hasModifier(clazz, Modifier.FINAL); aoqi@0: } aoqi@0: aoqi@0: public VariableElement[] getEnumConstants(TypeElement clazz) { aoqi@0: List elements = env.getElementUtils().getAllMembers(clazz); aefimov@835: Collection constants = new ArrayList(); aoqi@0: for (Element element : elements) { aoqi@0: if (element.getKind().equals(ElementKind.ENUM_CONSTANT)) { aoqi@0: constants.add((VariableElement) element); aoqi@0: } aoqi@0: } aoqi@0: return constants.toArray(new VariableElement[constants.size()]); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getVoidType() { aoqi@0: return env.getTypeUtils().getNoType(TypeKind.VOID); aoqi@0: } aoqi@0: aoqi@0: public String getPackageName(TypeElement clazz) { aoqi@0: return env.getElementUtils().getPackageOf(clazz).getQualifiedName().toString(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public TypeElement loadObjectFactory(TypeElement referencePoint, String packageName) { aoqi@0: return env.getElementUtils().getTypeElement(packageName + ".ObjectFactory"); aoqi@0: } aoqi@0: aoqi@0: public boolean isBridgeMethod(ExecutableElement method) { aoqi@0: return method.getModifiers().contains(Modifier.VOLATILE); aoqi@0: } aoqi@0: aoqi@0: public boolean isOverriding(ExecutableElement method, TypeElement base) { aoqi@0: Elements elements = env.getElementUtils(); aoqi@0: aoqi@0: while (true) { aoqi@0: for (ExecutableElement m : ElementFilter.methodsIn(elements.getAllMembers(base))) { aoqi@0: if (elements.overrides(method, m, base)) aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: if (base.getSuperclass().getKind().equals(TypeKind.NONE)) aoqi@0: return false; aoqi@0: base = (TypeElement) env.getTypeUtils().asElement(base.getSuperclass()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public boolean isInterface(TypeElement clazz) { aoqi@0: return clazz.getKind().isInterface(); aoqi@0: } aoqi@0: aoqi@0: public boolean isTransient(VariableElement f) { aoqi@0: return f.getModifiers().contains(Modifier.TRANSIENT); aoqi@0: } aoqi@0: aoqi@0: public boolean isInnerClass(TypeElement clazz) { aoqi@0: return clazz.getEnclosingElement() != null && !clazz.getModifiers().contains(Modifier.STATIC); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isSameType(TypeMirror t1, TypeMirror t2) { aoqi@0: return env.getTypeUtils().isSameType(t1, t2); aoqi@0: } aoqi@0: aoqi@0: public boolean isArray(TypeMirror type) { aoqi@0: return type != null && type.getKind().equals(TypeKind.ARRAY); aoqi@0: } aoqi@0: aoqi@0: public boolean isArrayButNotByteArray(TypeMirror t) { aoqi@0: if(!isArray(t)) aoqi@0: return false; aoqi@0: aoqi@0: ArrayType at = (ArrayType) t; aoqi@0: TypeMirror ct = at.getComponentType(); aoqi@0: aoqi@0: return !ct.equals(primitiveByte); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getComponentType(TypeMirror t) { aoqi@0: if (isArray(t)) { aoqi@0: ArrayType at = (ArrayType) t; aoqi@0: return at.getComponentType(); aoqi@0: } aoqi@0: aoqi@0: throw new IllegalArgumentException(); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getTypeArgument(TypeMirror typeMirror, int i) { aoqi@0: if (typeMirror != null && typeMirror.getKind().equals(TypeKind.DECLARED)) { aoqi@0: DeclaredType declaredType = (DeclaredType) typeMirror; aoqi@0: TypeMirror[] args = declaredType.getTypeArguments().toArray(new TypeMirror[declaredType.getTypeArguments().size()]); aoqi@0: return args[i]; aoqi@0: } else throw new IllegalArgumentException(); aoqi@0: } aoqi@0: aoqi@0: public boolean isParameterizedType(TypeMirror typeMirror) { aoqi@0: if (typeMirror != null && typeMirror.getKind().equals(TypeKind.DECLARED)) { aoqi@0: DeclaredType d = (DeclaredType) typeMirror; aoqi@0: return !d.getTypeArguments().isEmpty(); aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public boolean isPrimitive(TypeMirror t) { aoqi@0: return t.getKind().isPrimitive(); aoqi@0: } aoqi@0: aoqi@0: private static final Map primitives = new HashMap(); aoqi@0: aoqi@0: static { aoqi@0: primitives.put(Integer.TYPE, TypeKind.INT); aoqi@0: primitives.put(Byte.TYPE, TypeKind.BYTE); aoqi@0: primitives.put(Float.TYPE, TypeKind.FLOAT); aoqi@0: primitives.put(Boolean.TYPE, TypeKind.BOOLEAN); aoqi@0: primitives.put(Short.TYPE, TypeKind.SHORT); aoqi@0: primitives.put(Long.TYPE, TypeKind.LONG); aoqi@0: primitives.put(Double.TYPE, TypeKind.DOUBLE); aoqi@0: primitives.put(Character.TYPE, TypeKind.CHAR); aoqi@0: } aoqi@0: aoqi@0: public TypeMirror getPrimitive(Class primitiveType) { aoqi@0: assert primitiveType.isPrimitive(); aoqi@0: if(primitiveType==void.class) aoqi@0: return getVoidType(); aoqi@0: return env.getTypeUtils().getPrimitiveType(primitives.get(primitiveType)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * see {@link #ref(Class)}. aoqi@0: */ aoqi@0: private static final TypeMirror DUMMY = new TypeMirror() { aoqi@0: @Override aoqi@0: public R accept(TypeVisitor v, P p) { aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public TypeKind getKind() { aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: aoqi@0: // @Override aoqi@0: public List getAnnotationMirrors() { aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: aoqi@0: // @Override aoqi@0: public A getAnnotation(Class annotationType) { aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: aoqi@0: // @Override aoqi@0: public A[] getAnnotationsByType(Class annotationType) { aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: public Location getClassLocation(TypeElement typeElement) { aoqi@0: Trees trees = Trees.instance(env); aoqi@0: return getLocation(typeElement.getQualifiedName().toString(), trees.getPath(typeElement)); aoqi@0: } aoqi@0: aoqi@0: public Location getFieldLocation(VariableElement variableElement) { aoqi@0: return getLocation(variableElement); aoqi@0: } aoqi@0: aoqi@0: public Location getMethodLocation(ExecutableElement executableElement) { aoqi@0: return getLocation(executableElement); aoqi@0: } aoqi@0: aoqi@0: public boolean hasDefaultConstructor(TypeElement t) { aoqi@0: if (t == null || !t.getKind().equals(ElementKind.CLASS)) aoqi@0: return false; aoqi@0: aoqi@0: for (ExecutableElement init : ElementFilter.constructorsIn(env.getElementUtils().getAllMembers(t))) { aoqi@0: if (init.getParameters().isEmpty()) aoqi@0: return true; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public boolean isStaticField(VariableElement f) { aoqi@0: return hasModifier(f,Modifier.STATIC); aoqi@0: } aoqi@0: aoqi@0: public boolean isPublicMethod(ExecutableElement m) { aoqi@0: return hasModifier(m,Modifier.PUBLIC); aoqi@0: } aoqi@0: aoqi@0: public boolean isPublicField(VariableElement f) { aoqi@0: return hasModifier(f,Modifier.PUBLIC); aoqi@0: } aoqi@0: aoqi@0: public boolean isEnum(TypeElement t) { aoqi@0: return t != null && t.getKind().equals(ElementKind.ENUM); aoqi@0: } aoqi@0: aoqi@0: private Location getLocation(Element element) { aoqi@0: Trees trees = Trees.instance(env); aoqi@0: return getLocation( aoqi@0: ((TypeElement) element.getEnclosingElement()).getQualifiedName() + "." + element.getSimpleName(), aoqi@0: trees.getPath(element) aoqi@0: ); aoqi@0: } aoqi@0: aoqi@0: private Location getLocation(final String name, final TreePath treePath) { aoqi@0: return new Location() { aoqi@0: public String toString() { aoqi@0: if (treePath == null) aoqi@0: return name + " (Unknown Source)"; aoqi@0: // just like stack trace, we just print the file name and aoqi@0: // not the whole path. The idea is that the package name should aoqi@0: // provide enough clue on which directory it lives. aoqi@0: CompilationUnitTree compilationUnit = treePath.getCompilationUnit(); aoqi@0: Trees trees = Trees.instance(env); aoqi@0: long startPosition = trees.getSourcePositions().getStartPosition(compilationUnit, treePath.getLeaf()); aoqi@0: return name + "(" + aoqi@0: compilationUnit.getSourceFile().getName() + ":" + compilationUnit.getLineMap().getLineNumber(startPosition) + aoqi@0: ")"; aoqi@0: } aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Implements {@link #getBaseClass}. aoqi@0: */ aoqi@0: private final SimpleTypeVisitor6 baseClassFinder = new SimpleTypeVisitor6() { aoqi@0: @Override aoqi@0: public TypeMirror visitDeclared(DeclaredType t, TypeElement sup) { aoqi@0: if (t.asElement().equals(sup)) aoqi@0: return t; aoqi@0: aoqi@0: for (TypeMirror i : env.getTypeUtils().directSupertypes(t)) { aoqi@0: TypeMirror r = visitDeclared((DeclaredType) i, sup); aoqi@0: if (r != null) aoqi@0: return r; aoqi@0: } aoqi@0: aoqi@0: // otherwise recursively apply super class and base types aoqi@0: TypeMirror superclass = ((TypeElement) t.asElement()).getSuperclass(); aoqi@0: if (!superclass.getKind().equals(TypeKind.NONE)) { aoqi@0: TypeMirror r = visitDeclared((DeclaredType) superclass, sup); aoqi@0: if (r != null) aoqi@0: return r; aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public TypeMirror visitTypeVariable(TypeVariable t, TypeElement typeElement) { aoqi@0: // we are checking if T (declared as T extends A&B&C) is assignable to sup. aoqi@0: // so apply bounds recursively. aoqi@0: for (TypeMirror typeMirror : ((TypeParameterElement) t.asElement()).getBounds()) { aoqi@0: TypeMirror m = visit(typeMirror, typeElement); aoqi@0: if (m != null) aoqi@0: return m; aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public TypeMirror visitArray(ArrayType t, TypeElement typeElement) { aoqi@0: // we are checking if t=T[] is assignable to sup. aoqi@0: // the only case this is allowed is sup=Object, aoqi@0: // and Object isn't parameterized. aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public TypeMirror visitWildcard(WildcardType t, TypeElement typeElement) { aoqi@0: // we are checking if T (= ? extends A&B&C) is assignable to sup. aoqi@0: // so apply bounds recursively. aoqi@0: return visit(t.getExtendsBound(), typeElement); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected TypeMirror defaultAction(TypeMirror e, TypeElement typeElement) { aoqi@0: return e; aoqi@0: } aoqi@0: }; aoqi@0: }