/**
* DataCleaner (community edition)
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.datacleaner.descriptors;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
/**
* This tricky class allows us to create new annotations dynamically. Java api
* doesn't provide anything like this, annotations can be created either via
* source code, or specific class can be defined. But to create a dynamic
* annotation of specific interface by reflection, that is not available in
* standard java api.
*
* @Since 21.9.15
*/
public class AnnotationProxy {
private static class InvHandler implements InvocationHandler {
Map<String, Object> properties;
Class<?> annotationClass;
public InvHandler(final Class<?> anClass, final Map<String, Object> properties) {
this.properties = properties;
this.annotationClass = anClass;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if ("hashCode".equals(method.getName())) {
return annotationClass.hashCode();
}
if ("toString".equals(method.getName())) {
return annotationClass.getName() + ": " + properties.toString();
}
if ("annotationType".equals(method.getName())) {
return annotationClass;
}
final Object result = properties.get(method.getName());
if (result != null) {
final Class<?> retType = method.getReturnType();
if (retType.isArray()) {
if (List.class.isAssignableFrom(result.getClass())) {
return ((List<?>) result).toArray(
(Object[]) Array.newInstance(retType.getComponentType(), ((List<?>) result).size()));
}
return result;
}
}
return result;
}
}
/**
* Factory method for annotations which is able to create an instance of any
* annotation class.
*
* @param anClass
* Annotation interface that should be instantiated
* @param properties
* map of values for the annotation. The name (key) of a property
* is interpreted as a name of a method from the annotation
* interface. The created annotation proxy then returns this
* value when this method is called.
* @param <A>
* @return An annotation instance providing given values by its interface
* methods.
*/
public static <A extends Annotation> A newAnnotation(final Class<A> anClass, final Map<String, Object> properties) {
final InvHandler handler = new InvHandler(anClass, properties);
final ClassLoader classLoader = AnnotationProxy.class.getClassLoader();
@SuppressWarnings("unchecked") final A proxy =
(A) Proxy.newProxyInstance(classLoader, new Class[] { anClass }, handler);
return proxy;
}
}