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

net.sf.javagimmicks.util.ComparableWrapper Maven / Gradle / Ivy

There is a newer version: 0.99-alpha1
Show newest version
package net.sf.javagimmicks.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Comparator;

/**
 * This class adds {@link Comparable} abilities to a given non-
 * {@link Comparable} object by decorating it with a dynamic proxy.
 * 

* As usual for dynamic proxies, the decorated object must implement an * interface which must also be provided with the construction methods. *

* It is also recommended to write an own extension interface to the decorated * one which additionally extends {@link Comparable}. See the following example: * *

 * public interface StringWrapper
 * {
 *    String get();
 * }
 * 
 * public interface StringWrapperComparable extends StringWrapper, Comparable<StringWrapperComparable>
 * {}
 * 
* * Then you can use {@link ComparableWrapper} to create dynamic proxies for the * {@link Comparable} extension interface: * *
 * final ComparableWrapper<StringWrapper, StringWrapperComparable> wrapper =
 *       ComparableWrapper.create(StringWrapper.class, StringWrapperComparable.class, COMPARATOR);
 * 
 * final StringWrapperComparable w1 = wrapper.wrap(new StringWrapperImpl("1"));
 * final StringWrapperComparable w2 = wrapper.wrap(new StringWrapperImpl("2"));
 * final StringWrapperComparable w3 = wrapper.wrap(new StringWrapperImpl("3"));
 * 
* *

* However, if you do not provide such an interface, {@link ComparableWrapper} * can also create proxies which will implement {@link Comparable} in addition * to the provided delegate interface, but then you have to take care about * casting to {@link Comparable} by your own: * *

 * final ComparableWrapper<StringWrapper, StringWrapper> wrapper =
 *       ComparableWrapper.create(StringWrapper.class, COMPARATOR);
 * 
 * final StringWrapper w1 = wrapper.wrap(new StringWrapperImpl("1"));
 * final StringWrapper w2 = wrapper.wrap(new StringWrapperImpl("2"));
 * final StringWrapper w3 = wrapper.wrap(new StringWrapperImpl("3"));
 * 
 * assertTrue(((Comparable<StringWrapper>) w1).compareTo(w2) < 0);
 * 
* * @param * the (non-{@link Comparable}) type of objects to wrap * @param * the type of proxies to create */ public class ComparableWrapper { private static final Method COMPARE_TO_METHOD; static { Method method = null; try { method = Comparable.class.getMethod("compareTo", new Class[] { Object.class }); } catch (final Exception ignore) { // Cannot occur } COMPARE_TO_METHOD = method; } private final Class _proxyClass; private final Constructor _proxyConstructor; private final Class _nonComparableType; private final Comparator _comparator; /** * Creates a new {@link ComparableWrapper} instance which can be reused to * create {@link Comparable} proxies for any delegate objects implementing * the provided non-{@link Comparable} interface. * * @param classLoader * the {@link ClassLoader} to use for creating proxy instances * @param nonComparableType * an interface type that delegate objects to wrap must implement * @param comparableType * the proxy interface that must extend {@link Comparable} and the * non-{@link Comparable} delegate interface * @param comparator * the {@link Comparator} to use for creating proxies * @param * the (non-{@link Comparable}) type of objects to wrap * @param * the ({@link Comparable}) type of proxies to create * @return the resulting {@link ComparableWrapper} * @throws IllegalArgumentException * if {@code comparableType} does not extend * {@code nonComparableType} or it does not follow dynamic proxy * prerequisites (see * {@link Proxy#getProxyClass(ClassLoader, Class...)}) */ @SuppressWarnings("unchecked") public static > ComparableWrapper create(final ClassLoader classLoader, final Class nonComparableType, final Class comparableType, final Comparator comparator) { if (!nonComparableType.isAssignableFrom(comparableType)) { throw new IllegalArgumentException(String.format( "Comparable class '%1$s' must extend the given non-comparable type '%2$s'!", comparableType, nonComparableType)); } final Class proxyClass = (Class) Proxy.getProxyClass(classLoader, comparableType); return createInternal(nonComparableType, proxyClass, comparator); } /** * Creates a new {@link ComparableWrapper} instance which can be reused to * create {@link Comparable} proxies for any delegate objects implementing * the provided non-{@link Comparable} interface. * * @param nonComparableType * an interface type that delegate objects to wrap must implement * @param comparableType * the proxy interface that must extend {@link Comparable} and the * non-{@link Comparable} delegate interface * @param comparator * the {@link Comparator} to use for creating proxies * @param * the (non-{@link Comparable}) type of objects to wrap * @param * the ({@link Comparable}) type of proxies to create * @return the resulting {@link ComparableWrapper} * @throws IllegalArgumentException * if {@code comparableType} does not extend * {@code nonComparableType} or it does not follow dynamic proxy * prerequisites (see * {@link Proxy#getProxyClass(ClassLoader, Class...)}) */ public static > ComparableWrapper create(final Class nonComparableType, final Class comparableType, final Comparator comparator) { return create(comparableType.getClassLoader(), nonComparableType, comparableType, comparator); } /** * Creates a new {@link ComparableWrapper} instance which can be reused to * create {@link Comparable} proxies for any delegate objects implementing * the provided non-{@link Comparable} interface - returned proxies will * always implement the {@link Comparable} interface in addition to the * provided one. * * @param classLoader * the {@link ClassLoader} to use for creating proxy instances * @param nonComparableType * an interface type that delegate objects to wrap must implement * @param comparator * the {@link Comparator} to use for creating proxies * @param * the (non-{@link Comparable}) type of objects to wrap * @return the resulting {@link ComparableWrapper} * @throws IllegalArgumentException * if {@code comparableType} does not follow dynamic proxy * prerequisites (see * {@link Proxy#getProxyClass(ClassLoader, Class...)}) */ @SuppressWarnings("unchecked") public static ComparableWrapper create(final ClassLoader classLoader, final Class nonComparableType, final Comparator comparator) { return createInternal(nonComparableType, (Class) Proxy.getProxyClass(classLoader, new Class[] { nonComparableType, Comparable.class }), comparator); } /** * Creates a new {@link ComparableWrapper} instance which can be reused to * create {@link Comparable} proxies for any delegate objects implementing * the provided non-{@link Comparable} interface - returned proxies will * always implement the {@link Comparable} interface in addition to the * provided one. * * @param nonComparableType * an interface type that delegate objects to wrap must implement * @param comparator * the {@link Comparator} to use for creating proxies * @param * the (non-{@link Comparable}) type of objects to wrap * @return the resulting {@link ComparableWrapper} * @throws IllegalArgumentException * if {@code comparableType} does not follow dynamic proxy * prerequisites (see * {@link Proxy#getProxyClass(ClassLoader, Class...)}) */ public static ComparableWrapper create(final Class nonComparableType, final Comparator comparator) { return create(nonComparableType.getClassLoader(), nonComparableType, comparator); } private static ComparableWrapper createInternal(final Class nonComparableType, final Class proxyClass, final Comparator comparator) { final Constructor proxyConstructor = getConstructor(proxyClass); return new ComparableWrapper(nonComparableType, proxyClass, proxyConstructor, comparator); } private ComparableWrapper(final Class nonComparableType, final Class proxyClass, final Constructor proxyConstructor, final Comparator comparator) { _proxyClass = proxyClass; _proxyConstructor = proxyConstructor; _nonComparableType = nonComparableType; _comparator = comparator; } /** * Returns the {@link Class} that created proxy instances will have. * * @return the {@link Class} that created proxy instances will have */ public Class getProxyClass() { return _proxyClass; } /** * Returns the {@link Comparator} that is internally used for comparing * within the created proxies. * * @return the {@link Comparator} that is internally used for comparing * within the created proxies */ public Comparator getComparator() { return _comparator; } /** * Decorates a given {@code T} instance with a proxy of type {@code C}. * * @param delegate * the delegate object to decorate * @return the resulting proxy of type {@code C} */ public C wrap(final T delegate) { if (delegate == null) { throw new IllegalArgumentException("Delegate is null!"); } else if (!_nonComparableType.isAssignableFrom(delegate.getClass())) { throw new IllegalArgumentException( String.format( "Delegate object of type '%1$s' does not extend the specified non-comparable interface '%2$s'!", delegate.getClass(), _nonComparableType)); } final ComparableInvocationHandler invocationHandler = new ComparableInvocationHandler(delegate); try { return _proxyConstructor.newInstance(invocationHandler); } catch (final Exception ignore) { return null; } } private static Constructor getConstructor(final Class proxyClass) { try { return proxyClass.getConstructor(InvocationHandler.class); } catch (final Exception ex) { return null; } } protected class ComparableInvocationHandler implements InvocationHandler { protected final T _delegate; public ComparableInvocationHandler(final T delegate) { _delegate = delegate; } @SuppressWarnings("unchecked") @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { if (COMPARE_TO_METHOD.equals(method)) { return _comparator.compare(_delegate, (T) args[0]); } else { return method.invoke(_delegate, args); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy