org.hibernate.annotations.common.annotationfactory.AnnotationProxy Maven / Gradle / Ivy
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.annotations.common.annotationfactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* A concrete implementation of Annotation
that pretends it is a
* "real" source code annotation. It's also an InvocationHandler
.
*
* When you create an AnnotationProxy
, you must initialize it
* with an AnnotationDescriptor
.
* The adapter checks that the provided elements are the same elements defined
* in the annotation interface. However, it does not check that their
* values are the right type. If you omit an element, the adapter will use the
* default value for that element from the annotation interface, if it exists.
* If no default exists, it will throw an exception.
*
* Warning: this class does not implement hashCode()
and
* equals()
- it just uses the ones it inherits from Object
.
* This means that an AnnotationProxy
does not follow the
* recommendations of the Annotation
javadoc about these two
* methods. That's why you should never mix AnnotationProxies
* with "real" annotations. For example, don't put them into the same
* Collection
.
*
* @author Paolo Perrotta
* @author Davide Marchignoli
* @see java.lang.annotation.Annotation
*/
public final class AnnotationProxy implements Annotation, InvocationHandler {
private final Class extends Annotation> annotationType;
//FIXME it's probably better to use String as a key rather than Method
// to speed up and avoid any fancy permsize/GC issue
// I'd better check the litterature on the subject
private final Map values;
public AnnotationProxy(AnnotationDescriptor descriptor) {
this.annotationType = descriptor.type();
this.values = getAnnotationValues( annotationType, descriptor );
}
private static Map getAnnotationValues(
Class extends Annotation> annotationType,
AnnotationDescriptor descriptor) {
Map result = new HashMap();
int processedValuesFromDescriptor = 0;
for ( Method m : annotationType.getDeclaredMethods() ) {
if ( descriptor.containsElement( m.getName() ) ) {
result.put( m, descriptor.valueOf( m.getName() ) );
processedValuesFromDescriptor++;
}
else if ( m.getDefaultValue() != null ) {
result.put( m, m.getDefaultValue() );
}
else {
throw new IllegalArgumentException( "No value provided for " + m.getName() );
}
}
if ( processedValuesFromDescriptor != descriptor.numberOfElements() ) {
throw new RuntimeException( "Trying to instanciate " + annotationType + " with unknown elements" );
}
return toSmallMap( result );
}
static Map toSmallMap(Map map) {
switch ( map.size() ) {
case 0:
return Collections.emptyMap();
case 1:
Map.Entry entry = map.entrySet().iterator().next();
return Collections.singletonMap( entry.getKey(), entry.getValue() );
default:
return map;
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( values.containsKey( method ) ) {
return values.get( method );
}
return method.invoke( this, args );
}
public Class extends Annotation> annotationType() {
return annotationType;
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append( '@' ).append( annotationType().getName() ).append( '(' );
for ( Method m : getRegisteredMethodsInAlphabeticalOrder() ) {
result.append( m.getName() ).append( '=' ).append( values.get( m ) ).append( ", " );
}
// remove last separator:
if ( values.size() > 0 ) {
result.delete( result.length() - 2, result.length() );
result.append( ")" );
}
else {
result.delete( result.length() - 1, result.length() );
}
return result.toString();
}
private SortedSet getRegisteredMethodsInAlphabeticalOrder() {
SortedSet result = new TreeSet(
new Comparator() {
public int compare(Method o1, Method o2) {
return o1.getName().compareTo( o2.getName() );
}
}
);
result.addAll( values.keySet() );
return result;
}
}