package org.jboss.loom.spi.ann; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.jboss.loom.ex.MigrationException; /** * Metadata for JAXB classes; to be used for reporting and ModelNode creation. * @author Ondrej Zizka, ozizka at redhat.com */ @Retention( RetentionPolicy.RUNTIME ) @Target({ElementType.FIELD, ElementType.METHOD }) public @interface Property { public String name() default ""; public String expr() default ""; public String label() default ""; public String style() default ""; /** * Annotated field or method will be skipped when setting a ModelNode or creating a report. */ @Retention( RetentionPolicy.RUNTIME ) @Target({ElementType.FIELD, ElementType.METHOD }) public static @interface Skip {} /** * Determines how the properties will be treated in the given class. */ @Retention( RetentionPolicy.RUNTIME ) @Target({ElementType.TYPE}) public static @interface Access { public enum Type { PUBLIC, FIELD, ANNOTATED } public Type value() default Type.FIELD; } public static class Utils { public static Map<String, Object> describeBean( Object bean ) throws MigrationException{ Map<String, Object> map = new LinkedHashMap(); describeBean( bean, bean.getClass(), map ); return map; } public static void describeBean( Object bean, Class cls, Map<String, Object> map ) throws MigrationException{ List<Exception> problems = new LinkedList(); // Introspector? No... that only relies on getters... /*try { BeanInfo beanInfo = Introspector.getBeanInfo( bean.getClass() ); beanInfo.getPropertyDescriptors(); } catch( IntrospectionException ex ){ throw new MigrationException("Failed describing bean " + bean.getClass() + ":\n " + ex.getMessage(), ex); }*/ // General options Access classAnn = bean.getClass().getAnnotation( Property.Access.class ); Access.Type access = ( classAnn == null ) ? Access.Type.FIELD : classAnn.value(); // Fields for( Field field : bean.getClass().getFields() ) { final Property ann = field.getAnnotation( Property.class ); if( access == Access.Type.ANNOTATED && null == ann ) continue; if( access == Access.Type.PUBLIC && ! Modifier.isPublic( field.getModifiers() ) ) continue; String propName = (ann.name() != null && ! ann.name().isEmpty()) ? ann.name() : convertFieldToPropName( field.getName() ); if( map.containsKey( propName )) continue; try { map.put( propName, field.get( bean ) ); } catch( IllegalArgumentException | IllegalAccessException ex ){ Exception ex2 = new MigrationException("Failed describing bean " + bean.getClass() + ", field "+field.getName()+":\n " + ex.getMessage(), ex); problems.add( ex2 ); } } // Methods Method[] methods = bean.getClass().getMethods(); for( Method method : methods ) { if( method.getParameterTypes().length != 0 ) continue; final Property ann = method.getAnnotation( Property.class ); if( access == Access.Type.ANNOTATED && null == ann ) continue; if( access == Access.Type.PUBLIC && ! Modifier.isPublic( method.getModifiers() ) ) continue; // Only use getters which return String. /*boolean get = false; String name = method.getName(); if( name.startsWith("get") ) get = true; if( ! (get || name.startsWith("is")) ) continue; //if( ! method.getReturnType().equals( String.class ) ) continue; /* // Remove "get" or "is" name = name.substring( get ? 3 : 2 ); // Uncapitalize, unless it's getDLQJNDIName. if( name.length() > 1 && ! Character.isUpperCase( name.charAt(2) ) ) name = StringUtils.uncapitalize( name ); */ String propName = (ann.name() != null && ! ann.name().isEmpty()) ? ann.name() : convertMethodToPropName( method.getName() ); if( map.containsKey( propName )) continue; try { map.put( propName, method.invoke(bean)); } catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) { Exception ex2 = new MigrationException("Failed describing bean " + bean.getClass() + ", method "+method.getName()+":\n " + ex.getMessage(), ex); problems.add( ex2 ); } } // Skip Object and primitive types. if( cls.getSuperclass() == null || cls.getSuperclass().getSuperclass() == null ) return; describeBean( bean, cls.getSuperclass(), map ); }// describeBean(); /** * Converts "foo-bar" to "getFooBar()". * @see convertMethodToPropName(). */ public static String convertPropToMethodName( String propName ){ StringBuilder sb = new StringBuilder("get"); /*String[] parts = StringUtils.split( propName, "-"); for( String part : parts) { sb.append( StringUtils.capitalize( part ) ); }*/ boolean capNext = true; for( int i = 0; i < propName.length(); i++ ) { char ch = propName.charAt(i); if( Character.isLetter( ch )){ sb.append( capNext ? Character.toUpperCase( ch ) : ch ); capNext = false; } else capNext = true; } return sb.toString(); } /** * Converts "getFooBar()" to "foo-bar". * @see convertPropToMethodName(). */ public static String convertMethodToPropName( String methodName ){ // Remove get/set/is prefix methodName = StringUtils.removeStart( methodName, "get"); methodName = StringUtils.removeStart( methodName, "set"); methodName = StringUtils.removeStart( methodName, "is"); return convertFieldToPropName( methodName ); } public static String convertFieldToPropName( String fieldName ){ StringBuilder sb = new StringBuilder(); // Convert. Allows lowercase as the first char. for( int i = 0; i < fieldName.length(); i++ ) { char ch = fieldName.charAt(i); if( Character.isUpperCase( ch ) ) sb.append('-').append( Character.toLowerCase( ch ) ); else sb.append( ch ); } if( sb.charAt(0) == '-' ) sb.deleteCharAt(0); return sb.toString(); } }// Utils }// class