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