All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.venky.reflection.Reflector Maven / Gradle / Ivy

There is a newer version: 1.15
Show newest version
package com.venky.reflection;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;

public class Reflector {
    protected final Class reflectedClass;
    protected final Class upperBoundClass;
    protected final List allMethods ;
    protected final List> classHierarchy ;
    protected final List> classForest ;
    protected Reflector(Class reflectedClass,Class upperBoundClass){
    	assert(upperBoundClass != null);
    	assert(reflectedClass != null);
    	assert(upperBoundClass.isAssignableFrom(reflectedClass));

    	this.reflectedClass = reflectedClass ;
    	this.upperBoundClass = upperBoundClass;
    	this.allMethods = new ArrayList(reflectedClass.getMethods().length);
    	this.classHierarchy = new ArrayList>();
    	this.classForest = new ArrayList>();
    	
        Class rClass = reflectedClass;
        List> interfaces = new ArrayList>();
    	do {
    		loadMethods(rClass);
			classHierarchy.add(rClass);
			classForest.add(rClass);

    		@SuppressWarnings("unchecked")
			Class parentClass = (Class)getParentClass(rClass,upperBoundClass);

    		for (Class i : rClass.getInterfaces()){
    			if (i == parentClass){
    				continue;
    			}
        		interfaces.clear(); //Instead of new ArrayList each time.
    			loadAllInterfaces(i, interfaces);
        		for (Class c : interfaces){
        			loadMethods(c);
        		}
        		classForest.addAll(interfaces);
    		}
    		
            rClass = parentClass;
        }while(rClass != null );
    	
    }
    
    
    private void loadMethods(Class clazz){
    	int index = 0;
    	if (clazz == upperBoundClass){
    		index = allMethods.size();
    	}
    	for(Method m:getDeclaredMethods(clazz)){
        	List methodsForSignature = getMethodsForSignature(getMethodSignature(m));
        	if (methodsForSignature.isEmpty()){
    			allMethods.add(index,m);
        		index++;
        	}
        	methodsForSignature.add(m);
		}
    }
    
    
    public List> getClassHierarchy(){ 
    	return classHierarchy;
    }
    
    public List> getClassForest(){
    	return classForest;
    }

    
    public boolean isAnnotationPresent(Class annotationClass){
    	return getAnnotation(annotationClass) != null; 
    }
    public  T getAnnotation(Class annotationClass){
    	T annotation = null;
    	for (Class clazz:getClassForest()){
    		annotation = clazz.getAnnotation(annotationClass);
    		if (annotation != null){
    			break;
    		}
    	}
    	return annotation;
    }

    public boolean isAnnotationPresent(Method method, Class annotationClass){
    	return getAnnotation(method,annotationClass) != null;
    }
    
    public  T getAnnotation(Method method,Class annotationClass){
    	T annotation = null;
    	List methods = getMethodsForSignature(getMethodSignature(method)); 
    	for (int i = 0 ; annotation == null && i < methods.size() ; i ++){
    		Method m = methods.get(i);
    		annotation = m.getAnnotation(annotationClass);
    	}
    	return annotation;
    }
    
    private MethodSignatureCache signatureCache = new MethodSignatureCache(); 
    public String getMethodSignature(Method method){
    	return signatureCache.get(method);
    }

    private Map> methodsWithSameSignature = new HashMap>();
    protected List getMethodsForSignature(String signature){
    	List methods = methodsWithSameSignature.get(signature);
    	if (methods == null){
    		methods = new ArrayList();
    		methodsWithSameSignature.put(signature, methods);
    	}
    	return methods;
    }

    protected List getDeclaredMethods(Class forClass){ 
        List methods = new ArrayList(); 
        methods.addAll(Arrays.asList(forClass.getDeclaredMethods()));
        try {
        	ClassLoader cl = forClass.getClassLoader();
        	if (cl != null){
	            ClassReader reader = new ClassReader(cl.getResourceAsStream(forClass.getName().replace('.', '/')+ ".class"));
	            ClassVisitorImpl mv = new ClassVisitorImpl();
	            reader.accept(mv, 0);
	
	            final Map mSeq = mv.getMethodSequenceMap();
	            Collections.sort(methods,new Comparator(){
	                public int compare(Method o1, Method o2) {
	                    return mSeq.get(o1.getName()).compareTo(mSeq.get(o2.getName()));
	                }
	            });
        	}
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        
        return methods;
    }
    public Class getParentClass(){
    	return getParentClass(reflectedClass);
    }
    public Class getParentClass(Class clazz){
    	int i = classHierarchy.indexOf(clazz);
    	if (i  >= 0 && i < classHierarchy.size() - 1){
    		return classHierarchy.get(i+1); 
    	}
    	return null;
    }

    private static class ClassVisitorImpl implements ClassVisitor {
        private Map methodSequenceMap = new HashMap();
        
        public Map getMethodSequenceMap() {
            return methodSequenceMap;
        }

        public void visit(int version, int access, String name,
                String signature, String superName, String[] interfaces) {
            
            
        }

        public void visitSource(String source, String debug) {
            
            
        }

        public void visitOuterClass(String owner, String name, String desc) {
            
            
        }

        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            
            return null;
        }

        public void visitAttribute(Attribute attr) {
            
            
        }

        public void visitInnerClass(String name, String outerName,
                String innerName, int access) {
            
            
        }

        public FieldVisitor visitField(int access, String name, String desc,
                String signature, Object value) {
            
            return null;
        }

        public MethodVisitor visitMethod(int access, String name, String desc,
                String signature, String[] exceptions) {
            
            methodSequenceMap.put(name,methodSequenceMap.size());
            return null;
        }

        public void visitEnd() {
            
            
        }
    }
    
    public final List getMethods(MethodMatcher matcher){
        List methods = new ArrayList();
        for (Method method:allMethods){
            if (matcher.matches(method)){
                methods.add(method);
            }
        }
        return methods;
    }
    

    public static interface MethodMatcher {
        public boolean matches(Method method);
    }

    private static void loadAllInterfaces(Class clazz,List> interfaces){
    	if (clazz.isInterface()){
    		interfaces.add(clazz);
    	}
    	
    	for (Class infcClass: clazz.getInterfaces()){
    		loadAllInterfaces(infcClass, interfaces);
    	}
    }
    
    public static Class getParentClass(Class clazz,Class aSuperInfcOrClass){
    	
    	Class c = clazz;
    	if (aSuperInfcOrClass != null && !aSuperInfcOrClass.isAssignableFrom(c)){
    		throw new RuntimeException(c.getName() + " is not a "+ aSuperInfcOrClass.getName());
    	}
    	if (c.isInterface()){
        	List> interfaces =  new ArrayList>();
        	for (Class infc: c.getInterfaces()){
        		if (aSuperInfcOrClass == null || aSuperInfcOrClass.isAssignableFrom(infc)){
        			interfaces.add(infc);
        		}
        		if (interfaces.size() > 1){
        			break;
        		}
        	}
        	if (interfaces.isEmpty()){
        		return null;
        	}else if (interfaces.size() > 1){
        		throw new RuntimeException ("multiple interfaces implement " + aSuperInfcOrClass.getName());
        	}else {
        		return interfaces.get(0);
        	}    		
    	}else {
    		c = c.getSuperclass();
        	if (c != null && (aSuperInfcOrClass == null || aSuperInfcOrClass.isAssignableFrom(c))){
        		return c;
        	}else {
        		return null;
        	}
    	}
    }
}