package com.redhat.ceylon.model.loader.impl.reflect.mirror;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.redhat.ceylon.model.loader.AbstractModelLoader;
import com.redhat.ceylon.model.loader.ModelResolutionException;
import com.redhat.ceylon.model.loader.mirror.AnnotationMirror;
import com.redhat.ceylon.model.loader.mirror.ClassMirror;
import com.redhat.ceylon.model.loader.mirror.FieldMirror;
import com.redhat.ceylon.model.loader.mirror.MethodMirror;
import com.redhat.ceylon.model.loader.mirror.PackageMirror;
import com.redhat.ceylon.model.loader.mirror.TypeMirror;
import com.redhat.ceylon.model.loader.mirror.TypeParameterMirror;
import com.redhat.ceylon.model.typechecker.model.Module;
public class ReflectionClass implements ClassMirror {
public final Class<?> klass;
private ArrayList<FieldMirror> fields;
private ArrayList<MethodMirror> methods;
private ArrayList<TypeMirror> interfaces;
private List<TypeParameterMirror> typeParameters;
private ReflectionPackage pkg;
private boolean superClassSet;
private ReflectionType superClass;
private boolean enclosingClassSet;
private ClassMirror enclosingClass;
private boolean enclosingMethodSet;
private MethodMirror enclosingMethod;
private LinkedList<ClassMirror> innerClasses;
private String cacheKey;
private Map<String, AnnotationMirror> annotations;
public ReflectionClass(Class<?> klass) {
this.klass = klass;
}
@Override
public AnnotationMirror getAnnotation(String type) {
return getAnnotations().get(type);
}
private boolean isAnnotationPresent(String type) {
return getAnnotations().containsKey(type);
}
private Map<String, AnnotationMirror> getAnnotations() {
// profiling revealed we need to cache this
if(annotations == null){
annotations = ReflectionUtils.getAnnotations(klass);
}
return annotations;
}
@Override
public boolean isPublic() {
return Modifier.isPublic(klass.getModifiers());
}
@Override
public boolean isProtected() {
return Modifier.isProtected(klass.getModifiers());
}
@Override
public boolean isDefaultAccess() {
return !Modifier.isPrivate(klass.getModifiers())
&& !Modifier.isPublic(klass.getModifiers())
&& !Modifier.isProtected(klass.getModifiers());
}
@Override
public String getQualifiedName() {
return klass.getName();
}
@Override
public String getFlatName() {
// FIXME: is this really the same?
return klass.getName();
}
@Override
public String getName() {
return klass.getSimpleName();
}
@Override
public PackageMirror getPackage() {
if(pkg != null)
return pkg;
pkg = new ReflectionPackage(klass);
return pkg;
}
@Override
public boolean isInterface() {
return klass.isInterface();
}
@Override
public boolean isAnnotationType() {
return klass.isAnnotation();
}
@Override
public boolean isAbstract() {
return Modifier.isAbstract(klass.getModifiers());
}
@Override
public boolean isStatic() {
return Modifier.isStatic(klass.getModifiers());
}
@Override
public boolean isFinal() {
return Modifier.isFinal(klass.getModifiers());
}
@Override
public List<MethodMirror> getDirectMethods() {
if(methods != null)
return methods;
Method[] directMethods;
Constructor<?>[] directConstructors;
try{
directMethods = klass.getDeclaredMethods();
directConstructors = klass.getDeclaredConstructors();
}catch(NoClassDefFoundError x){
throw new ModelResolutionException("Failed to load methods in "+getQualifiedName(), x);
}
methods = new ArrayList<MethodMirror>(directMethods.length + directConstructors.length);
for(Method directMethod : directMethods){
// Note: unlike JavacClass and JDTClass we return private members, because the runtime manager
// depends on them
if(!directMethod.isSynthetic() && !directMethod.isBridge())
methods.add(new ReflectionMethod(this, directMethod));
}
for(Constructor<?> directConstructor : directConstructors){
// Note: unlike JavacClass and JDTClass we return private members, because the runtime manager
// depends on them
if(!directConstructor.isSynthetic())
methods.add(new ReflectionMethod(this, directConstructor));
}
return methods;
}
@Override
public List<FieldMirror> getDirectFields() {
if(fields != null)
return fields;
Field[] directFields = klass.getDeclaredFields();
fields = new ArrayList<FieldMirror>(directFields.length);
for(Field field : directFields)
if(!field.isSynthetic())
fields.add(new ReflectionField(field));
return fields;
}
@Override
public TypeMirror getSuperclass() {
if(superClassSet)
return superClass;
Type sup = klass.getGenericSuperclass();
if(sup != null)
superClass = new ReflectionType(sup);
superClassSet = true;
return superClass;
}
@Override
public List<TypeMirror> getInterfaces() {
if(interfaces != null)
return interfaces;
Type[] javaInterfaces = klass.getGenericInterfaces();
interfaces = new ArrayList<TypeMirror>(javaInterfaces.length);
for(Type javaInterface : javaInterfaces)
interfaces.add(new ReflectionType(javaInterface));
return interfaces;
}
@Override
public List<TypeParameterMirror> getTypeParameters() {
if(typeParameters != null)
return typeParameters;
typeParameters = ReflectionUtils.getTypeParameters(klass);
return typeParameters;
}
@Override
public boolean isCeylonToplevelAttribute() {
return !isInnerClass() && isAnnotationPresent(AbstractModelLoader.CEYLON_ATTRIBUTE_ANNOTATION);
}
@Override
public boolean isCeylonToplevelObject() {
return !isInnerClass() && isAnnotationPresent(AbstractModelLoader.CEYLON_OBJECT_ANNOTATION);
}
@Override
public boolean isCeylonToplevelMethod() {
return !isInnerClass() && isAnnotationPresent(AbstractModelLoader.CEYLON_METHOD_ANNOTATION);
}
@Override
public boolean isLoadedFromSource() {
return false;
}
@Override
public String toString() {
return "[ReflectionClass: "+klass.toString()+"]";
}
@Override
public boolean isInnerClass() {
return klass.isMemberClass() || isAnnotationPresent(AbstractModelLoader.CEYLON_CONTAINER_ANNOTATION);
}
@Override
public boolean isLocalClass() {
return klass.isLocalClass() || isAnnotationPresent(AbstractModelLoader.CEYLON_LOCAL_DECLARATION_ANNOTATION);
}
@Override
public List<ClassMirror> getDirectInnerClasses() {
if(innerClasses == null){
innerClasses = new LinkedList<ClassMirror>();
for(Class<?> innerClass : klass.getDeclaredClasses()){
innerClasses.add(new ReflectionClass(innerClass));
}
}
return innerClasses;
}
@Override
public boolean isAnonymous() {
return klass.isAnonymousClass();
}
@Override
public boolean isJavaSource() {
return false;
}
@Override
public ClassMirror getEnclosingClass() {
if(enclosingClassSet)
return enclosingClass;
Class<?> encl = klass.getEnclosingClass();
if(encl != null)
enclosingClass = new ReflectionClass(encl);
enclosingClassSet = true;
return enclosingClass;
}
@Override
public MethodMirror getEnclosingMethod() {
if(enclosingMethodSet)
return enclosingMethod;
Member encl = klass.getEnclosingMethod();
if(encl == null)
encl = klass.getEnclosingConstructor();
if(encl != null){
Class<?> declaringClass = encl.getDeclaringClass();
ReflectionClass declaringClassMirror = new ReflectionClass(declaringClass);
enclosingMethod = new ReflectionMethod(declaringClassMirror, encl);
}
enclosingMethodSet = true;
return enclosingMethod;
}
@Override
public boolean isEnum() {
return klass.isEnum();
}
@Override
public String getCacheKey(Module module) {
if(cacheKey == null){
String className = getQualifiedName();
cacheKey = AbstractModelLoader.getCacheKeyByModule(module, className);
}
return cacheKey;
}
}