/* * This code is distributed under The GNU Lesser General Public License (LGPLv3) * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html * * Copyright Denis Pavlov 2009 * Web: http://www.genericdtoassembler.org * SVN: https://svn.code.sf.net/p/geda-genericdto/code/trunk/ * SVN (mirror): http://geda-genericdto.googlecode.com/svn/trunk/ */ package com.inspiresoftware.lib.dto.geda.assembler; import com.inspiresoftware.lib.dto.geda.exception.GeDARuntimeException; import com.inspiresoftware.lib.dto.geda.exception.InspectionBindingNotFoundException; import com.inspiresoftware.lib.dto.geda.exception.InspectionPropertyNotFoundException; import com.inspiresoftware.lib.dto.geda.exception.InspectionScanningException; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Assembles Pipe. * * @author Denis Pavlov * @since 1.0.0 * */ @SuppressWarnings("unchecked") final class PropertyInspector { private PropertyInspector() { // prevent instantiation } /** * @param descriptor the descriptor whose get method's return type will be inspected to retrieve * all descriptors for it. * @return all descriptors for specified class with deep inheritance on interfaces. * @throws InspectionScanningException if fails to get bean info from a class */ public static PropertyDescriptor[] getPropertyDescriptorsForClassReturnedByGet(final PropertyDescriptor descriptor) throws InspectionScanningException { final Method entityFieldRead = descriptor.getReadMethod(); final Class returnType = getClassForType(entityFieldRead.getGenericReturnType()); return getPropertyDescriptorsForClass(returnType); } /** * @param clazz class to inspect. * @return all descriptors for specified class with deep inheritance on interfaces. * @throws InspectionScanningException if fails to get bean info from a class */ public static PropertyDescriptor[] getPropertyDescriptorsForClass(final Class clazz) throws InspectionScanningException { try { PropertyDescriptor[] basic = Introspector.getBeanInfo(clazz, Introspector.USE_ALL_BEANINFO).getPropertyDescriptors(); if (clazz.isInterface()) { final Type[] extendedInterfaces = (Type[]) clazz.getGenericInterfaces(); if (extendedInterfaces != null && extendedInterfaces.length > 0) { final ArrayList<PropertyDescriptor> descs = new ArrayList<PropertyDescriptor>(); for (Type extendedInterface : extendedInterfaces) { if (extendedInterface instanceof Class) { addToList(descs, getPropertyDescriptorsForClass((Class) extendedInterface)); } else if (extendedInterface instanceof ParameterizedType) { addToList(descs, getPropertyDescriptorsForClass( (Class) ((ParameterizedType) extendedInterface).getRawType())); } } addToList(descs, basic); return descs.toArray(new PropertyDescriptor[descs.size()]); } } return basic; } catch (IntrospectionException itex) { throw new InspectionScanningException(clazz.getCanonicalName(), itex); } } private static void addToList(final List<PropertyDescriptor> list, final PropertyDescriptor[] descs) { if (descs == null || descs.length == 0) { return; } list.addAll(Arrays.asList(descs)); } /** * @param dtoClass the DTO * @param entityClass the Entity * @param dtoFieldName the DTO field * @param binding the Entity field binding * @param entityPropertyDescriptors all Entity property descriptors * @return property descriptor for Entity field. * @throws InspectionBindingNotFoundException thrown when unable to find descriptor for field. */ public static PropertyDescriptor getEntityPropertyDescriptorForField( final Class dtoClass, final Class entityClass, final String dtoFieldName, final String binding, final PropertyDescriptor[] entityPropertyDescriptors) throws InspectionBindingNotFoundException { PropertyDescriptor candidateRead = null; PropertyDescriptor candidateWrite = null; for (PropertyDescriptor current : entityPropertyDescriptors) { if (current.getName().equals(binding)) { if (current.getReadMethod() != null && current.getWriteMethod() != null) { // this is full get/set - best match return current; } else if (current.getReadMethod() != null) { // has a get - good enough for readOnly candidateRead = current; } else if (current.getWriteMethod() != null) { // has a get - save for later if we have a read candidate candidateWrite = current; } } } if (candidateRead != null) { if (candidateWrite != null) { try { return new PropertyDescriptor(candidateRead.getName(), candidateRead.getReadMethod(), candidateWrite.getWriteMethod()); } catch (IntrospectionException iexp) { throw new GeDARuntimeException( "Unable to combine get and set for [" + entityClass + "#" + binding + "] from different interfaces into single property descriptor" ); } } return candidateRead; } throw new InspectionBindingNotFoundException( dtoClass.getCanonicalName(), dtoFieldName, entityClass.getCanonicalName(), binding); } /** * @param dtoClass the DTO * @param dtoFieldName the DTO field * @param dtoPropertyDescriptors all DTO property descriptors * @return property descriptor for DTO field. * @throws InspectionPropertyNotFoundException thrown when unable to find descriptor for field. */ public static PropertyDescriptor getDtoPropertyDescriptorForField( final Class dtoClass, final String dtoFieldName, final PropertyDescriptor[] dtoPropertyDescriptors) throws InspectionPropertyNotFoundException { for (PropertyDescriptor current : dtoPropertyDescriptors) { if (current.getName().equals(dtoFieldName)) { return current; } } throw new InspectionPropertyNotFoundException(dtoClass.getCanonicalName(), dtoFieldName); } /** * Determine the actual class for this type. * * @param type generic type * @return class for this generic type * * @throws GeDARuntimeException if generics tree is too complex */ public static Class getClassForType(Type type) throws GeDARuntimeException { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { final Object rawType = ((ParameterizedType) type).getRawType(); if (rawType instanceof Class) { return (Class) rawType; // typed generic (e.g. MyClass<T> getMyClass()) } } else if (type instanceof TypeVariable) { final Object[] bounds = ((TypeVariable) type).getBounds(); if (bounds != null && bounds.length > 0 && bounds[0] instanceof Class) { return (Class) bounds[0]; // variable generics (e.g. T getMyClass()) } } throw new GeDARuntimeException("Unable to determine correct class for type: " + type); } }