package org.springframework.roo.classpath.details; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; /** * Provides utility methods for querying JavaBeans. * * @author Ben Alex * @since 1.1.1 */ public final class BeanInfoUtils { /** * Returns the accessor name for the given field. * * @param field the field to determine the accessor name * @return the accessor method name */ public static JavaSymbolName getAccessorMethodName(final FieldMetadata field) { Validate.notNull(field, "Field required"); return getAccessorMethodName(field.getFieldName(), field.getFieldType()); } /** * Returns the accessor name for the given field name and field type. * * @param fieldName the field name used to determine the accessor name * @param fieldType the field type * @return the accessor method name */ public static JavaSymbolName getAccessorMethodName(final JavaSymbolName fieldName, final JavaType fieldType) { Validate.notNull(fieldName, "Field name required"); Validate.notNull(fieldType, "Field type required"); final String capitalizedFieldName = StringUtils.capitalize(fieldName.getSymbolName()); return fieldType.equals(JavaType.BOOLEAN_PRIMITIVE) ? new JavaSymbolName("is" + capitalizedFieldName) : new JavaSymbolName("get" + capitalizedFieldName); } /** * Attempts to locate the field which is represented by the presented java * bean method. * <p> * Not every JavaBean getter or setter actually backs to a field with an * identical name. In such cases, null will be returned. * * @param memberDetails the member holders to scan (required) * @param method the method name (required) * @return the field if found, or null if it could not be found */ public static FieldMetadata getFieldForJavaBeanMethod(final MemberDetails memberDetails, final MethodMetadata method) { Validate.notNull(memberDetails, "Member details required"); Validate.notNull(method, "Method is required"); final JavaSymbolName propertyName = getPropertyNameForJavaBeanMethod(method); return getFieldForPropertyName(memberDetails, propertyName); } /** * Attempts to locate the field which is represented by the presented * property name. * <p> * Not every JavaBean getter or setter actually backs to a field with an * identical name. In such cases, null will be returned. * * @param memberDetails the member holders to scan (required) * @param propertyName the property name (required) * @return the field if found, or null if it could not be found */ public static FieldMetadata getFieldForPropertyName(final MemberDetails memberDetails, final JavaSymbolName propertyName) { Validate.notNull(memberDetails, "Member details required"); Validate.notNull(propertyName, "Property name required"); for (final MemberHoldingTypeDetails holder : memberDetails.getDetails()) { FieldMetadata result = holder.getDeclaredField(propertyName); if (result != null) { return result; } // To get here means we couldn't find the property using the exact // same case; // try to scan with a lowercase first character (see ROO-203) result = holder.getDeclaredField(new JavaSymbolName(StringUtils.uncapitalize(propertyName .getSymbolName()))); if (result != null) { return result; } } return null; } /** * Returns the mutator name for the given field. * * @param field the field used to determine the accessor name * @return the mutator method name */ public static JavaSymbolName getMutatorMethodName(final FieldMetadata field) { Validate.notNull(field, "Field metadata required"); return getMutatorMethodName(field.getFieldName()); } /** * Returns the mutator name for the given field name. * * @param fieldName the field name used to determine the accessor name * @return the mutator method name */ public static JavaSymbolName getMutatorMethodName(final JavaSymbolName fieldName) { Validate.notNull(fieldName, "Field name required"); return new JavaSymbolName("set" + StringUtils.capitalize(fieldName.getSymbolName())); } /** * Obtains the property name for the specified JavaBean accessor or mutator * method. This is determined by discarding the first 2 or 3 letters of the * method name (depending whether it is a "get", "set" or "is" method). * There is no special searching back to the actual field name. * * @param method to search (required, and must be a "get", "set" or "is" * method) * @return the name of the property (never returned null) */ public static JavaSymbolName getPropertyNameForJavaBeanMethod(final MethodMetadata method) { Validate.notNull(method, "Method is required"); final String name = method.getMethodName().getSymbolName(); if (name.startsWith("set") || name.startsWith("get")) { return new JavaSymbolName(name.substring(3)); } if (name.startsWith("is")) { return new JavaSymbolName(name.substring(2)); } throw new IllegalStateException("Method name '" + name + "' does not observe JavaBean method naming conventions"); } /** * Attempts to locate an accessor and a mutator method for a given field. * <p> * Not every JavaBean getter or setter actually backs to a field with an * identical name. In such cases, false will be returned. * * @param field the member holders to scan (required) * @param memberDetails the member details to scan * @return true if an accessor and a mutator are present, or false otherwise */ public static boolean hasAccessorAndMutator(final FieldMetadata field, final MemberDetails memberDetails) { Validate.notNull(field, "Field required"); Validate.notNull(memberDetails, "Member details required"); if (memberDetails.getMethod(getAccessorMethodName(field), new ArrayList<JavaType>()) != null && memberDetails .getMethod(getMutatorMethodName(field), Arrays.asList(field.getFieldType())) != null) { return true; } return false; } /** * Indicates if the presented method compiles with the JavaBean conventions * around accessor methods (public, "set" or "is", 0 args etc). * * @param method to evaluate (required) * @return true if the presented method is an accessor, otherwise false */ public static boolean isAccessorMethod(final MethodMetadata method) { Validate.notNull(method, "Method is required"); return (method.getMethodName().getSymbolName().startsWith("get") || method.getMethodName() .getSymbolName().startsWith("is")) && method.getParameterTypes().isEmpty() && Modifier.isPublic(method.getModifier()); } /** * Determines whether the presented entity is a test class or not. * * @param entity the type to test * @return true if the entity is likely not a test class, otherwise false */ public static boolean isEntityReasonablyNamed(final JavaType entity) { Validate.notNull(entity, "Entity required"); return !entity.getSimpleTypeName().startsWith("Test") && !entity.getSimpleTypeName().endsWith("TestCase") && !entity.getSimpleTypeName().endsWith("Test"); } /** * Indicates if the presented method compiles with the JavaBean conventions * around mutator methods (public, "set", 1 arg etc). * * @param method to evaluate (required) * @return true if the presented method is a mutator, otherwise false */ public static boolean isMutatorMethod(final MethodMetadata method) { Validate.notNull(method, "Method is required"); return method.getMethodName().getSymbolName().startsWith("set") && method.getParameterTypes().size() == 1 && Modifier.isPublic(method.getModifier()); } /** * Constructor is private to prevent instantiation * * @since 1.2.0 */ private BeanInfoUtils() {} }