blob: 836f191f7ba5563d2f32bb2232d4ece334e0a677 [file] [log] [blame]
/*
* Copyright 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.internal.jxc.model.nav;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sun.istack.internal.tools.APTTypeVisitor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.ConstructorDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.EnumConstantDeclaration;
import com.sun.mirror.declaration.EnumDeclaration;
import com.sun.mirror.declaration.FieldDeclaration;
import com.sun.mirror.declaration.InterfaceDeclaration;
import com.sun.mirror.declaration.MemberDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.declaration.ParameterDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.type.ArrayType;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.DeclaredType;
import com.sun.mirror.type.InterfaceType;
import com.sun.mirror.type.PrimitiveType;
import com.sun.mirror.type.ReferenceType;
import com.sun.mirror.type.TypeMirror;
import com.sun.mirror.type.TypeVariable;
import com.sun.mirror.type.VoidType;
import com.sun.mirror.type.WildcardType;
import com.sun.mirror.util.Declarations;
import com.sun.mirror.util.SourcePosition;
import com.sun.mirror.util.TypeVisitor;
import com.sun.mirror.util.Types;
import com.sun.xml.internal.bind.v2.model.nav.Navigator;
import com.sun.xml.internal.bind.v2.runtime.Location;
/**
* {@link Navigator} implementation for APT.
*
* TODO: check the spec on how generics are supposed to be handled
*
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public class APTNavigator implements Navigator<TypeMirror,TypeDeclaration,FieldDeclaration,MethodDeclaration> {
private final AnnotationProcessorEnvironment env;
private final PrimitiveType primitiveByte;
public APTNavigator(AnnotationProcessorEnvironment env) {
this.env = env;
this.primitiveByte = env.getTypeUtils().getPrimitiveType(PrimitiveType.Kind.BYTE);
}
public TypeDeclaration getSuperClass(TypeDeclaration t) {
if (t instanceof ClassDeclaration) {
ClassDeclaration c = (ClassDeclaration) t;
ClassType sup = c.getSuperclass();
if(sup!=null)
return sup.getDeclaration();
else
return null;
}
return env.getTypeDeclaration(Object.class.getName());
}
public TypeMirror getBaseClass(TypeMirror type, TypeDeclaration sup) {
return baseClassFinder.apply(type,sup);
}
public String getClassName(TypeDeclaration t) {
return t.getQualifiedName();
}
public String getTypeName(TypeMirror typeMirror) {
return typeMirror.toString();
}
public String getClassShortName(TypeDeclaration t) {
return t.getSimpleName();
}
public Collection<FieldDeclaration> getDeclaredFields(TypeDeclaration c) {
List<FieldDeclaration> l = new ArrayList<FieldDeclaration>(c.getFields());
return sort(l);
}
public FieldDeclaration getDeclaredField(TypeDeclaration clazz, String fieldName) {
for( FieldDeclaration fd : clazz.getFields() ) {
if(fd.getSimpleName().equals(fieldName))
return fd;
}
return null;
}
public Collection<MethodDeclaration> getDeclaredMethods(TypeDeclaration c) {
List<MethodDeclaration> l = new ArrayList<MethodDeclaration>(c.getMethods());
return sort(l);
}
private <A extends Declaration> List<A> sort(List<A> l) {
if(l.isEmpty()) return l;
// APT supports the operation mode where it creates Declarations from
// a class file, in which case the source position is not available
// use that as a key to sort them correctly. This isn't "correct" in
// the sense that it relies on undocumented behavior of APT where
// it returns declarations in the reverse order, but this makes things work.
SourcePosition pos = l.get(0).getPosition();
if(pos!=null)
Collections.sort(l,SOURCE_POS_COMPARATOR);
else
Collections.reverse(l);
return l;
}
public ClassDeclaration getDeclaringClassForField(FieldDeclaration f) {
return (ClassDeclaration)f.getDeclaringType();
}
public ClassDeclaration getDeclaringClassForMethod(MethodDeclaration m) {
return (ClassDeclaration)m.getDeclaringType();
}
public TypeMirror getFieldType(FieldDeclaration f) {
return f.getType();
}
public String getFieldName(FieldDeclaration f) {
return f.getSimpleName();
}
public String getMethodName(MethodDeclaration m) {
return m.getSimpleName();
}
public TypeMirror getReturnType(MethodDeclaration m) {
return m.getReturnType();
}
public TypeMirror[] getMethodParameters(MethodDeclaration m) {
Collection<ParameterDeclaration> ps = m.getParameters();
TypeMirror[] r = new TypeMirror[ps.size()];
int i=0;
for( ParameterDeclaration p : ps )
r[i++] = p.getType();
return r;
}
public boolean isStaticMethod(MethodDeclaration m) {
return hasModifier(m, Modifier.STATIC);
}
private boolean hasModifier(Declaration d, Modifier mod) {
return d.getModifiers().contains(mod);
}
public boolean isSubClassOf(TypeMirror sub, TypeMirror sup) {
if(sup==DUMMY)
// see ref(). if the sub type is known to APT,
// its base class must be known. Thus if the sup is DUMMY,
// it cannot possibly be the super type.
return false;
return env.getTypeUtils().isSubtype(sub,sup);
}
private String getSourceClassName(Class clazz) {
Class<?> d = clazz.getDeclaringClass();
if(d==null)
return clazz.getName();
else {
String shortName = clazz.getName().substring(d.getName().length()+1/*for $*/);
return getSourceClassName(d)+'.'+shortName;
}
}
public TypeMirror ref(Class c) {
if(c.isArray())
return env.getTypeUtils().getArrayType( ref(c.getComponentType()) );
if(c.isPrimitive())
return getPrimitive(c);
TypeDeclaration t = env.getTypeDeclaration(getSourceClassName(c));
// APT only operates on a set of classes used in the compilation,
// and it won't recognize additional classes (even if they are visible from javac)
// and return null.
//
// this is causing a problem where we check if a type is collection.
// so until the problem is fixed in APT, work around the issue
// by returning a dummy token
if(t==null)
return DUMMY;
return env.getTypeUtils().getDeclaredType(t);
}
public TypeMirror use(TypeDeclaration t) {
assert t!=null;
return env.getTypeUtils().getDeclaredType(t);
}
public TypeDeclaration asDecl(TypeMirror m) {
m = env.getTypeUtils().getErasure(m);
if (m instanceof DeclaredType) {
DeclaredType d = (DeclaredType) m;
return d.getDeclaration();
} else
return null;
}
public TypeDeclaration asDecl(Class c) {
return env.getTypeDeclaration(getSourceClassName(c));
}
public <T> TypeMirror erasure(TypeMirror t) {
Types tu = env.getTypeUtils();
t = tu.getErasure(t);
if(t instanceof DeclaredType) {
DeclaredType dt = (DeclaredType)t;
if(!dt.getActualTypeArguments().isEmpty())
return tu.getDeclaredType(dt.getDeclaration());
}
return t;
}
public boolean isAbstract(TypeDeclaration clazz) {
return hasModifier(clazz,Modifier.ABSTRACT);
}
public boolean isFinal(TypeDeclaration clazz) {
return hasModifier(clazz,Modifier.FINAL);
}
public FieldDeclaration[] getEnumConstants(TypeDeclaration clazz) {
EnumDeclaration ed = (EnumDeclaration) clazz;
Collection<EnumConstantDeclaration> constants = ed.getEnumConstants();
return constants.toArray(new EnumConstantDeclaration[constants.size()]);
}
public TypeMirror getVoidType() {
return env.getTypeUtils().getVoidType();
}
public String getPackageName(TypeDeclaration clazz) {
return clazz.getPackage().getQualifiedName();
}
public TypeDeclaration findClass(String className, TypeDeclaration referencePoint) {
return env.getTypeDeclaration(className);
}
public boolean isBridgeMethod(MethodDeclaration method) {
return method.getModifiers().contains(Modifier.VOLATILE);
}
public boolean isOverriding(MethodDeclaration method) {
TypeDeclaration declaringType = method.getDeclaringType();
if(!(declaringType instanceof ClassDeclaration))
return false; // act defensively. this might be because we are recovering from errors
ClassDeclaration sc = (ClassDeclaration) declaringType;
Declarations declUtil = env.getDeclarationUtils();
while(sc.getSuperclass()!=null) {
sc = sc.getSuperclass().getDeclaration();
for (MethodDeclaration m : sc.getMethods()) {
if(declUtil.overrides(method,m))
return true;
}
}
return false;
}
public boolean isInterface(TypeDeclaration clazz) {
return clazz instanceof InterfaceDeclaration;
}
public boolean isTransient(FieldDeclaration f) {
return f.getModifiers().contains(Modifier.TRANSIENT);
}
public boolean isArray(TypeMirror t) {
return t instanceof ArrayType;
}
public boolean isArrayButNotByteArray(TypeMirror t) {
if(!isArray(t))
return false;
ArrayType at = (ArrayType) t;
TypeMirror ct = at.getComponentType();
return !ct.equals(primitiveByte);
}
public TypeMirror getComponentType(TypeMirror t) {
if (t instanceof ArrayType) {
ArrayType at = (ArrayType) t;
return at.getComponentType();
}
throw new IllegalArgumentException();
}
public TypeMirror getTypeArgument(TypeMirror typeMirror, int i) {
if (typeMirror instanceof DeclaredType){
DeclaredType d = (DeclaredType)typeMirror;
TypeMirror[] args = d.getActualTypeArguments().toArray(new TypeMirror[0]);
return args[i];
} else throw new IllegalArgumentException();
}
public boolean isParameterizedType(TypeMirror t) {
if (t instanceof DeclaredType) {
DeclaredType d = (DeclaredType) t;
return !d.getActualTypeArguments().isEmpty();
}
return false;
}
public boolean isPrimitive(TypeMirror t) {
return t instanceof PrimitiveType;
}
private static final Map<Class,PrimitiveType.Kind> primitives = new HashMap<Class,PrimitiveType.Kind>();
static {
primitives.put(Integer.TYPE, PrimitiveType.Kind.INT);
primitives.put(Byte.TYPE, PrimitiveType.Kind.BYTE);
primitives.put(Float.TYPE, PrimitiveType.Kind.FLOAT);
primitives.put(Boolean.TYPE, PrimitiveType.Kind.BOOLEAN);
primitives.put(Short.TYPE, PrimitiveType.Kind.SHORT);
primitives.put(Long.TYPE, PrimitiveType.Kind.LONG);
primitives.put(Double.TYPE, PrimitiveType.Kind.DOUBLE);
primitives.put(Character.TYPE, PrimitiveType.Kind.CHAR);
}
public TypeMirror getPrimitive(Class primitiveType) {
assert primitiveType.isPrimitive();
if(primitiveType==void.class)
return getVoidType();
return env.getTypeUtils().getPrimitiveType(primitives.get(primitiveType));
}
/**
* see {@link #ref(Class)}.
*/
private static final TypeMirror DUMMY = new TypeMirror() {
public void accept(TypeVisitor v) {
throw new IllegalStateException();
}
};
/**
* Implements {@link #getBaseClass}.
*/
private final APTTypeVisitor<TypeMirror,TypeDeclaration> baseClassFinder = new APTTypeVisitor<TypeMirror,TypeDeclaration>(){
public TypeMirror onClassType(ClassType type, TypeDeclaration sup) {
TypeMirror r = onDeclaredType(type,sup);
if(r!=null) return r;
// otherwise recursively apply super class and base types
if(type.getSuperclass()!=null) {
r = onClassType(type.getSuperclass(),sup);
if(r!=null) return r;
}
return null;
}
protected TypeMirror onPrimitiveType(PrimitiveType type, TypeDeclaration param) {
return type;
}
protected TypeMirror onVoidType(VoidType type, TypeDeclaration param) {
return type;
}
public TypeMirror onInterfaceType(InterfaceType type, TypeDeclaration sup) {
return onDeclaredType(type,sup);
}
private TypeMirror onDeclaredType(DeclaredType t, TypeDeclaration sup) {
// t = sup<...>
if(t.getDeclaration().equals(sup))
return t;
for(InterfaceType i : t.getSuperinterfaces()) {
TypeMirror r = onInterfaceType(i,sup);
if(r!=null) return r;
}
return null;
}
public TypeMirror onTypeVariable(TypeVariable t, TypeDeclaration sup) {
// we are checking if T (declared as T extends A&B&C) is assignable to sup.
// so apply bounds recursively.
for( ReferenceType r : t.getDeclaration().getBounds() ) {
TypeMirror m = apply(r,sup);
if(m!=null) return m;
}
return null;
}
public TypeMirror onArrayType(ArrayType type, TypeDeclaration sup) {
// we are checking if t=T[] is assignable to sup.
// the only case this is allowed is sup=Object,
// and Object isn't parameterized.
return null;
}
public TypeMirror onWildcard(WildcardType type, TypeDeclaration sup) {
// we are checking if T (= ? extends A&B&C) is assignable to sup.
// so apply bounds recursively.
for( ReferenceType r : type.getLowerBounds() ) {
TypeMirror m = apply(r,sup);
if(m!=null) return m;
}
return null;
}
} ;
public Location getClassLocation(TypeDeclaration decl) {
return getLocation(decl.getQualifiedName(),decl.getPosition());
}
public Location getFieldLocation(FieldDeclaration decl) {
return getLocation(decl);
}
public Location getMethodLocation(MethodDeclaration decl) {
return getLocation(decl);
}
public boolean hasDefaultConstructor(TypeDeclaration t) {
if(!(t instanceof ClassDeclaration))
return false;
ClassDeclaration c = (ClassDeclaration) t;
for( ConstructorDeclaration init : c.getConstructors() ) {
if(init.getParameters().isEmpty())
return true;
}
return false;
}
public boolean isStaticField(FieldDeclaration f) {
return hasModifier(f,Modifier.STATIC);
}
public boolean isPublicMethod(MethodDeclaration m) {
return hasModifier(m,Modifier.PUBLIC);
}
public boolean isPublicField(FieldDeclaration f) {
return hasModifier(f,Modifier.PUBLIC);
}
public boolean isEnum(TypeDeclaration t) {
return t instanceof EnumDeclaration;
}
private Location getLocation(MemberDeclaration decl) {
return getLocation(decl.getDeclaringType().getQualifiedName()+'.'+decl.getSimpleName(),decl.getPosition());
}
private Location getLocation(final String name, final SourcePosition sp) {
return new Location() {
public String toString() {
if(sp==null)
return name+" (Unknown Source)";
// just like stack trace, we just print the file name and
// not the whole path. The idea is that the pakage name should
// provide enough clue on which directory it lives.
return name+'('+sp.file().getName()+':'+sp.line()+')';
}
};
}
/**
* Comparator that uses the source position
*/
private static final Comparator<Declaration> SOURCE_POS_COMPARATOR = new Comparator<Declaration>() {
public int compare(Declaration d1, Declaration d2) {
if (d1 == d2)
return 0;
SourcePosition p1 = d1.getPosition();
SourcePosition p2 = d2.getPosition();
if (p1 == null) {
return (p2 == null) ? 0 : 1;
} else {
if (p2 == null)
return -1;
int fileComp = p1.file().compareTo(p2.file());
if (fileComp == 0) {
long diff = (long) p1.line() - (long) p2.line();
if (diff == 0) {
diff = Long.signum((long) p1.column() - (long) p2.column());
if (diff != 0)
return (int) diff;
else {
// declarations may be two
// compiler-generated members with the
// same source position
return (Long.signum((long) System.identityHashCode(d1) -
(long) System.identityHashCode(d2)));
}
} else
return (diff < 0) ? -1 : 1;
} else
return fileComp;
}
}
};
}