UnknownMagic.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.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.metanotion.util.reflect.ExtensionWalker;
import net.metanotion.util.reflect.GetFieldList;
/** Use reflection to generate a suitable implementation of Unknown.lookupInterface(...).
This class provides a way to simply and controllable provide an object with an implementation of
Unknown. It uses reflection to examine the class of an object, and will "provide" an implementation of
a given interface based on the following heuristics:
<ol>
<li>Any interface implemented by the object(by enumerating the interfaces it implements)</li>
<li>Any instance variable annotated with {@link net.metanotion.util.Service} will be exanimed for the
interfaces implemented by the type associated with that instance variable.</li>
<li>Any instance variable annotated with {@link net.metanotion.util.Extends} will have these rules applied
recursively to it.</li>
</ol>
In essence, those two annotations provide a sort of runtime "inheritance/delegation" pattern that this class uses
to determine if the "component" implements an interface.
*/
public final class UnknownMagic {
private static final Logger logger = LoggerFactory.getLogger(UnknownMagic.class);
private final class Visitor implements ExtensionWalker.ClassVisitor {
@Override public void visit(final Field[] stack, final Class current) {
if(stack.length == 0) { recurseInterfaces(stack, current); }
for(final Field f: current.getFields()) {
f.setAccessible(true);
if(f.getAnnotation(Service.class) != null) {
final Class type = f.getType();
final Field[] f1 = Arrays.copyOf(stack, stack.length + 1);
f1[stack.length] = f;
map.put(type, new GetFieldList(f1));
recurseInterfaces(f1, type);
}
}
}
private void recurseInterfaces(final Field[] prev, final Class current) {
for(final Class c: current.getInterfaces()) {
logger.debug(" implementing " + c.getName());
if(!(map.containsKey(c))) {
map.put(c, (prev.length == 0) ? IdentityDictionary.INSTANCE : new GetFieldList(prev));
}
recurseInterfaces(prev, c);
}
}
}
private final Map<Class,Dictionary> map = new HashMap<>();
/** Create an implementation of Unkonwn.lookupInterface(...) for the class provided to the constructor.
@param klazz The class to introspect on.
*/
public UnknownMagic(final Class klazz) {
logger.debug("Processing " + klazz.getName());
ExtensionWalker.visit(new Visitor(), klazz);
}
/** To use this implementation of lookupInterface(...) you could implement Unknown. Then:<br />
<code>public <I> I lookupInterface(Class<I> theInterface) {<br />
return instanceOfUnknownMagic.lookupInterface(theInterface, this);<br />
}</code>
@param <I> The type of the interface to return.
@param theInterface The service requested from the component.
@param instance The component that will provide an implementation of the service.
@return An instance of an object that implements theInterface.
*/
public <I> I lookupInterface(final Class<I> theInterface, final Object instance) {
if(theInterface == Unknown.class) { return (I) instance; }
if(theInterface == Object.class) { return (I) instance; }
final Dictionary g = map.get(theInterface);
if(g==null) { throw new RuntimeException("no suitable proxy."); }
return (I) g.get(instance);
}
}