package org.tynamo.descriptor.decorators;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Predicate;
import org.apache.tapestry5.ioc.ObjectLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tynamo.descriptor.*;
import org.tynamo.descriptor.annotation.handlers.DescriptorAnnotationHandler;
import org.tynamo.descriptor.annotation.handlers.HandledBy;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* This class uses the Tynamo's annotations on a given class or property to modify its
* descriptor
*/
public class TynamoDecorator implements DescriptorDecorator
{
private static final Logger LOGGER = LoggerFactory.getLogger(TynamoDecorator.class);
private ObjectLocator locator;
public TynamoDecorator(ObjectLocator locator)
{
this.locator = locator;
}
public TynamoClassDescriptor decorate(TynamoClassDescriptor descriptor)
{
Annotation[] classAnnotations = descriptor.getBeanType().getAnnotations();
TynamoClassDescriptor decoratedDescriptor = (TynamoClassDescriptor) descriptor.clone();
decorateFromAnnotations(decoratedDescriptor, classAnnotations);
decoratedDescriptor.setPropertyDescriptors(decoratePropertyDescriptors(descriptor));
decoratedDescriptor.setMethodDescriptors(decorateMethodDescriptors(descriptor));
return decoratedDescriptor;
}
List<TynamoPropertyDescriptor> decoratePropertyDescriptors(TynamoClassDescriptor descriptor)
{
List<TynamoPropertyDescriptor> decoratedPropertyDescriptors = new ArrayList<TynamoPropertyDescriptor>();
for (TynamoPropertyDescriptor propertyDescriptor : descriptor.getPropertyDescriptors())
{
TynamoPropertyDescriptor clonedDescriptor = (TynamoPropertyDescriptor) propertyDescriptor.clone();
decoratePropertyDescriptor(clonedDescriptor);
// recursively decorate components
if (clonedDescriptor.isEmbedded())
{
EmbeddedDescriptor embeddedDescriptor = (EmbeddedDescriptor) clonedDescriptor;
embeddedDescriptor.setEmbeddedClassDescriptor(decorate(embeddedDescriptor.getEmbeddedClassDescriptor()));
}
decoratedPropertyDescriptors.add(clonedDescriptor);
}
return decoratedPropertyDescriptors;
}
List<IMethodDescriptor> decorateMethodDescriptors(TynamoClassDescriptor descriptor)
{
List<IMethodDescriptor> decoratedMethodDescriptors = new ArrayList<IMethodDescriptor>();
for (IMethodDescriptor methodDescriptor : descriptor.getMethodDescriptors())
{
IMethodDescriptor clonedDescriptor = (IMethodDescriptor) methodDescriptor.clone();
decorateMethodDescriptor(clonedDescriptor);
decoratedMethodDescriptors.add(clonedDescriptor);
}
return decoratedMethodDescriptors;
}
void decoratePropertyDescriptor(final TynamoPropertyDescriptor descriptor)
{
decorateFromDeclaredField(descriptor);
decorateFromReadMethod(descriptor);
}
void decorateFromReadMethod(final TynamoPropertyDescriptor tynamoPropertyDescriptor)
{
try
{
PropertyDescriptor[] descriptors =
Introspector.getBeanInfo(tynamoPropertyDescriptor.getBeanType()).getPropertyDescriptors();
PropertyDescriptor beanPropDescriptor = F.flow(descriptors).filter(new Predicate<PropertyDescriptor>()
{
public boolean accept(PropertyDescriptor descriptor)
{
return descriptor.getName().equals(tynamoPropertyDescriptor.getName());
}
}).first();
Method readMethod = beanPropDescriptor.getReadMethod();
if (readMethod != null)
{
decorateFromAnnotations(tynamoPropertyDescriptor, readMethod.getAnnotations());
} else {
LOGGER.error("There is no readMethod for: " + tynamoPropertyDescriptor.getBeanType().getSimpleName() + "." + tynamoPropertyDescriptor.getName());
}
}
catch (IntrospectionException ex)
{
LOGGER.error("Could not decorate from readMethod: " + tynamoPropertyDescriptor.getBeanType().getSimpleName() + "." + tynamoPropertyDescriptor.getName());
}
}
void decorateFromDeclaredField(TynamoPropertyDescriptor tynamoPropertyDescriptor)
{
try
{
Field propertyField = tynamoPropertyDescriptor.getBeanType().getDeclaredField(tynamoPropertyDescriptor.getName());
decorateFromAnnotations(tynamoPropertyDescriptor, propertyField.getAnnotations());
} catch (NoSuchFieldException ex)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("NoSuchFieldException: " + tynamoPropertyDescriptor.getBeanType().getSimpleName() + "." + tynamoPropertyDescriptor.getName());
}
}
}
void decorateMethodDescriptor(IMethodDescriptor methodDescriptor)
{
try
{
decorateFromAnnotations(methodDescriptor, methodDescriptor.getMethod().getAnnotations());
} catch (NoSuchMethodException nsme)
{
LOGGER.warn(ExceptionUtils.getRootCauseMessage(nsme));
}
}
void decorateFromAnnotations(Descriptor descriptor, Annotation[] annotations)
{
for (Annotation annotation : annotations)
{
// If the annotation type itself has a @HandledBy annotation, it's one of ours
HandledBy handledBy = annotation.annotationType().getAnnotation(HandledBy.class);
if (handledBy != null)
{
try
{
String serviceId = handledBy.value();
DescriptorAnnotationHandler handler = locator.getService(serviceId, DescriptorAnnotationHandler.class);
handler.decorateFromAnnotation(annotation, descriptor);
}
catch (Exception ex)
{
LOGGER.error("error decorating: " + descriptor.toString() + " with " + annotation.toString(), ExceptionUtils.getRootCauseMessage(ex));
}
}
}
}
}