package nl.ipo.cds.attributemapping.operations.discover;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import nl.ipo.cds.attributemapping.operations.PropertyBeanDescription;
import nl.ipo.cds.attributemapping.operations.PropertyBeanFieldDescription;
import org.springframework.beans.BeanUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
public class PropertyBeanIntrospector {
private final Set<String> baseNames = new HashSet<String> ();
private final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource ();
private final ConcurrentHashMap<Class<?>, BeanDescription> beanDescriptions = new ConcurrentHashMap<Class<?>, BeanDescription> ();
public PropertyBeanDescription getDescriptorForBeanClass (final Class<?> beanClass) {
if (beanClass == null) {
throw new IllegalArgumentException ("beanClass cannot be null");
}
final BeanDescription beanDescription = beanDescriptions.get (beanClass);
if (beanDescription == null) {
updateMessageSource (beanClass);
final BeanDescription newBeanDescription = new BeanDescription (beanClass, messageSource);
final BeanDescription existingBeanDescription = beanDescriptions.putIfAbsent (beanClass, newBeanDescription);
if (existingBeanDescription != null) {
return existingBeanDescription;
}
return newBeanDescription;
}
return beanDescription;
}
private synchronized void updateMessageSource (final Class<?> beanClass) {
final String baseName = String.format ("%s.messages", beanClass.getPackage ().getName ());
if (baseNames.add (baseName) || baseNames.add (beanClass.getCanonicalName ())) {
messageSource.setBasenames (baseNames.toArray (new String[baseNames.size ()]));
}
}
private final static class BeanDescription implements PropertyBeanDescription {
private final Class<?> beanClass;
private final MessageSource messageSource;
private final List<PropertyBeanFieldDescription> fieldDescriptions;
public BeanDescription (final Class<?> beanClass, final MessageSource messageSource) {
this.beanClass = beanClass;
this.messageSource = messageSource;
this.fieldDescriptions = this.introspect ();
}
public MessageSource getMessageSource () {
return messageSource;
}
@Override
public Class<?> getBeanClass () {
return beanClass;
}
@Override
public Collection<PropertyBeanFieldDescription> getFieldDescriptions() {
return fieldDescriptions;
}
private List<PropertyBeanFieldDescription> introspect () {
final List<PropertyBeanFieldDescription> result = new ArrayList<PropertyBeanFieldDescription> ();
for (final PropertyDescriptor pd: BeanUtils.getPropertyDescriptors (beanClass)) {
if (pd.getReadMethod () == null || pd.getWriteMethod () == null) {
continue;
}
final Class<?> cls = pd.getPropertyType ();
if (!String.class.equals (cls) && !Boolean.TYPE.equals (cls) && !Integer.TYPE.equals (cls)) {
continue;
}
result.add (new FieldDescription (this, pd));
}
return result;
}
}
private final static class FieldDescription implements PropertyBeanFieldDescription {
private final BeanDescription beanDescription;
private final PropertyDescriptor propertyDescriptor;
public FieldDescription (final BeanDescription beanDescription, final PropertyDescriptor propertyDescriptor) {
this.beanDescription = beanDescription;
this.propertyDescriptor = propertyDescriptor;
}
@Override
public String getName () {
return propertyDescriptor.getName ();
}
@Override
public String getDescription (final Locale locale) {
return beanDescription.getMessageSource ().getMessage (
getMessageKey (), null, getName (), locale);
}
@Override
public Type getType () {
return propertyDescriptor.getReadMethod ().getGenericReturnType ();
}
private String getMessageKey () {
return String.format (
"%s.%s.description",
beanDescription.getBeanClass ().getCanonicalName (),
propertyDescriptor.getName ()
);
}
}
}