// Copyright (c) 2006 Dustin Sallings <dustin@spy.net>
package net.spy.factory;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.spy.SpyObject;
/**
* Locate relevant CacheKeys for a given class.
*/
public class CacheKeyFinder extends SpyObject {
private static CacheKeyFinder instance=null;
private final ConcurrentMap<Class<?>, Map<CacheKey, Accessor<?>>> memo=
new ConcurrentHashMap<Class<?>, Map<CacheKey, Accessor<?>>>();
protected CacheKeyFinder() {
super();
}
/**
* Get the singleton CacheKeyFinder instance.
*/
public static synchronized CacheKeyFinder getInstance() {
if(instance == null) {
instance=new CacheKeyFinder();
}
return instance;
}
/**
* Set the singleton CacheKeyFinder instance.
*/
public static void setInstance(CacheKeyFinder to) {
instance=to;
}
/**
* Get the cache keys for the given class.
*
* @param c the class to search
* @return the cache keys for the given class
*/
public Map<CacheKey, Accessor<?>> getCacheKeys(Class<?> c) {
Map<CacheKey, Accessor<?>> rv=memo.get(c);
if(rv == null) {
synchronized(c) {
rv=memo.get(c);
if(rv == null) {
rv=new HashMap<CacheKey, Accessor<?>>();
lookupCacheKeys(rv, c);
}
}
}
return rv;
}
private void lookupCacheKeys(Map<CacheKey, Accessor<?>> rv, Class<?> c) {
// Get the recursion out of the way first
Class<?> sup=c.getSuperclass();
if(sup != null) {
lookupCacheKeys(rv, sup);
}
for(Class<?> i : c.getInterfaces()) {
lookupCacheKeys(rv, i);
}
// Do the real work
for(Method m : c.getDeclaredMethods()) {
CacheKey ck=m.getAnnotation(CacheKey.class);
if(ck != null) {
rv.put(ck, new MethodAccessor(m));
}
}
for(Field f : c.getDeclaredFields()) {
CacheKey ck=f.getAnnotation(CacheKey.class);
if(ck != null) {
rv.put(ck, new FieldAccessor(f));
}
}
}
/**
* Class to access an object from within another object.
*/
public static abstract class Accessor<T extends AccessibleObject> {
protected final T ao;
protected Accessor(T o) {
ao=o;
}
/**
* Get the value from the given object.
*
* @param o the object
* @return the value
* @throws Exception if an exception is thrown while accessing
*/
public final Object get(Object o) throws Exception {
boolean accessible=ao.isAccessible();
if(!accessible) {
ao.setAccessible(true);
}
Object rv=realGet(o);
if(!accessible) {
ao.setAccessible(false);
}
return rv;
}
protected abstract Object realGet(Object o) throws Exception;
}
private static class MethodAccessor extends Accessor<Method> {
public MethodAccessor(Method m) {
super(m);
}
@Override
protected Object realGet(Object o) throws Exception {
return ao.invoke(o, new Object[0]);
}
}
private static class FieldAccessor extends Accessor<Field> {
public FieldAccessor(Field f) {
super(f);
}
@Override
public Object realGet(Object o) throws Exception {
return ao.get(o);
}
}
}