package com.owlike.genson;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import com.owlike.genson.reflect.TypeUtil;
/**
* Wrapper class must be extended by decorated converters that wrap other converters. This allows to
* access merged class information of wrapped converter and the converter itself. So instead of
* doing myObject.getClass().isAnnotationPresent(..) you will do myObject.isAnnotationPresent(..),
* where myObject is an instance of Wrapper. For example to check if a converter (or any another
* encapsulated converter and so on) has annotation @HandleNull you will do it that way:
* <p/>
* <pre>
* Wrapper.toAnnotatedElement(converter).isAnnotationPresent(HandleNull.class);
* </pre>
* <p/>
* In the future there may be other methods to access other kind of class information.
*
* @author eugen
*/
public abstract class Wrapper<T> implements AnnotatedElement {
private AnnotatedElement wrappedElement;
protected T wrapped;
protected Wrapper() {
}
protected Wrapper(T wrappedObject) {
if (wrappedObject == null)
throw new IllegalArgumentException("Null not allowed!");
decorate(wrappedObject);
}
public Annotation[] getAnnotations() {
return Operations.union(Annotation[].class, wrappedElement.getAnnotations(), getClass()
.getAnnotations());
}
public <A extends Annotation> A getAnnotation(Class<A> aClass) {
A ann = wrappedElement.getAnnotation(aClass);
return ann == null ? getClass().getAnnotation(aClass) : ann;
}
public Annotation[] getDeclaredAnnotations() {
return Operations.union(Annotation[].class, wrappedElement.getDeclaredAnnotations(),
getClass().getDeclaredAnnotations());
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return wrappedElement.isAnnotationPresent(annotationClass)
|| getClass().isAnnotationPresent(annotationClass);
}
// package visibility as a convenience for CircularClassReferenceConverter
protected void decorate(T object) {
if (wrappedElement != null)
throw new IllegalStateException("An object is already wrapped!");
if (object instanceof AnnotatedElement)
this.wrappedElement = (AnnotatedElement) object;
else
this.wrappedElement = object.getClass();
this.wrapped = object;
}
public T unwrap() {
return wrapped;
}
/**
* This method acts as an adapter to AnnotatedElement, use it when you need to work on a
* converter annotations. In fact "object" argument will usually be of type converter. If this
* class is a wrapper than it will cast it to annotatedElement (as Wrapper implements
* AnnotatedElement). Otherwise we will return the class of this object.
*
* @param object may be an instance of converter for example
* @return an annotatedElement that allows us to get annotations from this object and it's
* wrapped classes if it is a Wrapper.
*/
public static AnnotatedElement toAnnotatedElement(Object object) {
if (object == null)
return null;
if (isWrapped(object))
return (AnnotatedElement) object;
else
return object.getClass();
}
public static boolean isWrapped(Object object) {
return object instanceof Wrapper;
}
/**
* @return true if this object or its wrapped object (if the object extends Wrapper) is of type clazz.
*/
public static boolean isOfType(Object object, Class<?> clazz) {
return TypeUtil.match(object.getClass(), clazz, false) ||
(isWrapped(object) && Wrapper.isOfType(((Wrapper<?>) object).unwrap(), clazz));
}
}