DispatcherGenerator.java
/***************************************************************************
Copyright 2012 Emily Estes
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***************************************************************************/
package net.metanotion.util;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** Utility class to recursively generate or find a suitable Dispatcher implementation for an object or type. */
public final class DispatcherGenerator implements Dictionary<Object,Dispatcher> {
private static Dispatcher fieldValue(final Object fValue)
throws IllegalAccessException, InstantiationException, ClassNotFoundException {
if(fValue instanceof Dispatcher) {
/* This field IS a Dispatcher, so let's return that. */
return (Dispatcher) fValue;
} else if((fValue instanceof Class) && (Dispatcher.class.isAssignableFrom((Class) fValue))) {
return (Dispatcher) ((Class) fValue).newInstance();
} else if(fValue instanceof String) {
/* The field (hopefully) contains the name of a class that will give us a Dispatcher */
final Class cls = Class.forName((String) fValue);
if(Dispatcher.class.isAssignableFrom(cls)) {
return (Dispatcher) cls.newInstance();
}
}
return null;
}
private static Dispatcher fromField(final Class cls, final Object o) {
try {
final Field f = cls.getField("DISPATCHER");
f.setAccessible(true);
if(!(Modifier.isStatic(f.getModifiers()) ^ (o == null))) {
return fieldValue(f.get(o));
}
} catch (final InstantiationException | ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { }
return null;
}
private static Dispatcher fromMethod(final Class cls, final Object o) {
/* Let's check to see if there is a zero-parameter method called "dispatcher" that returns a Dispatcher instance. */
try {
final Method m = cls.getMethod("dispatcher", null);
if(!(Dispatcher.class.isAssignableFrom(m.getReturnType()))) { return null; }
if(!(Modifier.isStatic(m.getModifiers()) ^ (o == null))) {
m.setAccessible(true);
return (Dispatcher) m.invoke(o, new Object[0]);
}
} catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { }
return null;
}
private static Dispatcher fromClass(final Class cls) {
/* This class might be it's own dispatcher. */
if(Dispatcher.class.isAssignableFrom(cls)) {
try {
return (Dispatcher) cls.newInstance();
} catch (IllegalAccessException | InstantiationException e) { }
}
Dispatcher d = fromField(cls, null);
if(d != null) { return d; }
d = fromMethod(cls, null);
if(d != null) { return d; }
try {
return MapToListDispatcher.getDispatcher(cls);
} catch (final IOException e) {
return new ReflectionListDispatcher(cls);
}
}
private Dispatcher fromObject(final Object o, final Class cls) {
/* This object might actually be a dispatcher. */
if(o instanceof Dispatcher) { return (Dispatcher) o; }
/* Now let's look for a static field called "DISPATCHER" */
Dispatcher d = fromField(cls, null);
if(d != null) { return d; }
d = fromMethod(cls, o);
if(d != null) { return d; }
return this.get(cls);
}
// TO DO This needs to be a thread safe map preferably with weak references for GC purposes.
private final ConcurrentMap<Class,Dispatcher> cache = new ConcurrentHashMap<Class,Dispatcher>();
/** Do whatever it takes to derive a usable Dispatcher implementation from an object or class.
@param o The object instance or {@link java.lang.Class} we want to derive a dispatcher from. As a last ditch
effort, we use reflection to generate a dispatcher, but if you provide a (possibly static) field called
"DISPATCHER" whose value is a: <ul>
<li>{@link java.lang.Class} of a class that implements Dispatcher</li>
<li>An instance of a Dispatcher implementation.</li>
<li>A string that contains the NAME of a class that implemets Dispatcher
</ul>
that will be used first. Of you can provide a zero argument (possibly static) method called "dispatcher"
that returns an instance of a Dispatcher implementation we will use that. Finally, we try to use
reflection. We look for parameter names so we can use {@link net.metanotion.util.MapToListDispatcher},
first using the {@link net.metanotion.util.NamedParameterMapper}, and if that fails, we use the
{@link net.metanotion.util.ObjectParameterMapper} which uses byte code disassembly. Finally, if that fails
we just use a "list" dispatcher({@link net.metanotion.util.ReflectionListDispatcher}) only.
@return An implementation of a Dispatcher that works with o, or we will throw a RuntimeException to indicate we
couldn't do anything useful.
*/
@Override public Dispatcher get(final Object o) {
if(o instanceof Class) {
final Class cls = (Class) o;
/* Check the cache. */
Dispatcher d = cache.get(cls);
if(d != null) { return d; }
d = fromClass(cls);
cache.put(cls, d);
return d;
} else {
final Class cls = o.getClass();
/* Check the cache. */
final Dispatcher d = cache.get(cls);
if(d != null) { return d; }
return fromObject(o, cls);
}
}
}