net.sf.javagimmicks.util.ComparableWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gimmicks Show documentation
Show all versions of gimmicks Show documentation
Utility classes, APIs and tools for Java
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