diff options
author | Tom Hennen <tom.hennen@gmail.com> | 2013-09-02 19:35:07 -0400 |
---|---|---|
committer | Tom Hennen <tom.hennen@gmail.com> | 2013-09-02 19:35:07 -0400 |
commit | d538e3899c65992870e1866b1d27fc7cd5385ce7 (patch) | |
tree | cad2c9b377566f99e7eef34ff70c1f03774de1cf /src/de/danoeh/antennapod/util/DuckType.java | |
parent | bd4c365c1147a214d20d33a6c2989b247452a732 (diff) | |
parent | 684590665ecc5b3d32bf1041f01ba3a47794a533 (diff) | |
download | AntennaPod-d538e3899c65992870e1866b1d27fc7cd5385ce7.zip |
Merge branch 'develop' of https://github.com/danieloeh/AntennaPod into move-to-top
Diffstat (limited to 'src/de/danoeh/antennapod/util/DuckType.java')
-rw-r--r-- | src/de/danoeh/antennapod/util/DuckType.java | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/de/danoeh/antennapod/util/DuckType.java b/src/de/danoeh/antennapod/util/DuckType.java new file mode 100644 index 000000000..0dfc01508 --- /dev/null +++ b/src/de/danoeh/antennapod/util/DuckType.java @@ -0,0 +1,115 @@ +/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */ + +package de.danoeh.antennapod.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Allows "duck typing" or dynamic invocation based on method signature rather + * than type hierarchy. In other words, rather than checking whether something + * IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck. + * + * To use first use the coerce static method to indicate the object you want to + * do Duck Typing for, then specify an interface to the to method which you want + * to coerce the type to, e.g: + * + * public interface Foo { void aMethod(); } class Bar { ... public void + * aMethod() { ... } ... } Bar bar = ...; Foo foo = + * DuckType.coerce(bar).to(Foo.class); foo.aMethod(); + * + * + */ +public class DuckType { + + private final Object objectToCoerce; + + private DuckType(Object objectToCoerce) { + this.objectToCoerce = objectToCoerce; + } + + private class CoercedProxy implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method delegateMethod = findMethodBySignature(method); + assert delegateMethod != null; + return delegateMethod.invoke(DuckType.this.objectToCoerce, args); + } + } + + /** + * Specify the duck typed object to coerce. + * + * @param object + * the object to coerce + * @return + */ + public static DuckType coerce(Object object) { + return new DuckType(object); + } + + /** + * Coerce the Duck Typed object to the given interface providing it + * implements all the necessary methods. + * + * @param + * @param iface + * @return an instance of the given interface that wraps the duck typed + * class + * @throws ClassCastException + * if the object being coerced does not implement all the + * methods in the given interface. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public <T> T to(Class iface) { + assert iface.isInterface() : "cannot coerce object to a class, must be an interface"; + if (isA(iface)) { + return (T) iface.cast(objectToCoerce); + } + if (quacksLikeA(iface)) { + return generateProxy(iface); + } + throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface); + } + + @SuppressWarnings("rawtypes") + private boolean isA(Class iface) { + return objectToCoerce.getClass().isInstance(iface); + } + + /** + * Determine whether the duck typed object can be used with the given + * interface. + * + * @param Type + * of the interface to check. + * @param iface + * Interface class to check + * @return true if the object will support all the methods in the interface, + * false otherwise. + */ + @SuppressWarnings("rawtypes") + public boolean quacksLikeA(Class iface) { + for (Method method : iface.getMethods()) { + if (findMethodBySignature(method) == null) { + return false; + } + } + return true; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private <T> T generateProxy(Class iface) { + return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy()); + } + + private Method findMethodBySignature(Method method) { + try { + return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes()); + } catch (NoSuchMethodException e) { + return null; + } + } + +}
\ No newline at end of file |