package org.nocket.component.form.beans;
import gengui.util.SevereGUIException;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Resolves properties of the specified entity class.
*
* Usage:
*
* <pre>
* ...
* final Map<String, BeanInfoPropertyDescriptor> entityProperties =
* new BeanInfoResolver(my.example.package.MyPojo.class).resolveProperties()
* ...
* </pre>
*
* It works recursively, with respect to cycle references.
*
* @author blaz02
*/
public class BeanInfoResolver {
final private static Logger log = LoggerFactory.getLogger(BeanInfoResolver.class);
final private static List<String> IGNORE = Arrays.asList("java.lang", "java.util.Date", "java.math");
private Class<?> entityClass;
private Set<Class<?>> alredyResolved;
/**
* Constructor.
*
* @param entityClass
* Class which has to be resolved.
*/
public BeanInfoResolver(Class<?> entityClass) {
this(entityClass, new HashSet<Class<?>>());
}
/**
* Friendly constructor for recursive resolving.
*
* @param entityClass
* @param alreadyResolved
*/
BeanInfoResolver(Class<?> entityClass, Set<Class<?>> alreadyResolved) {
this.entityClass = entityClass;
this.alredyResolved = alreadyResolved;
}
/**
* Performs property resolving of the entity class.
*
* @return Map with the all information about class properties. Key is a
* string with a full class name and a property name i.e.:
* "my.example.package.MyPojo.propertyName".
*/
public HashMap<String, BeanInfoPropertyDescriptor> resolveProperties() {
if (log.isDebugEnabled()) {
log.debug("Analyzing: " + entityClass.getCanonicalName());
}
alredyResolved.add(entityClass);
HashMap<String, BeanInfoPropertyDescriptor> entityProperties = new HashMap<String, BeanInfoPropertyDescriptor>();
BeanInfo entityBeanInfo;
try {
entityBeanInfo = Introspector.getBeanInfo(entityClass);
} catch (IntrospectionException ex) {
throw new SevereGUIException(ex);
}
for (PropertyDescriptor p : entityBeanInfo.getPropertyDescriptors()) {
entityProperties.put(getPropertyPath(p), new BeanInfoPropertyDescriptor(entityClass, p));
// Decide if a property should be resolved recursive
if (doRecursive(p.getPropertyType()) && !isClassAlreadyResolved(p.getPropertyType()))
entityProperties.putAll(new BeanInfoResolver(p.getPropertyType(), this.alredyResolved)
.resolveProperties());
}
return entityProperties;
}
private String getPropertyPath(PropertyDescriptor p) {
return entityClass.getName() + "." + p.getName();
}
private boolean doRecursive(Class<?> entityClass) {
if (entityClass == null || entityClass.isEnum())
return false;
String canonicalName = entityClass.getCanonicalName();
for (String s : IGNORE) {
if (canonicalName.startsWith(s))
return false;
}
return true;
}
/**
* @param entityClass
* Class name
*
* @return True if class has been resolved already. This is important to
* avoid cycle references.
*/
private boolean isClassAlreadyResolved(Class<?> entityClass) {
return alredyResolved.contains(entityClass);
}
}