001package org.granite.generator.as3.reflect; 002 003import java.lang.annotation.Annotation; 004import java.lang.reflect.Array; 005import java.lang.reflect.Method; 006import java.lang.reflect.Modifier; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.List; 010import java.util.Map; 011 012public class ValidatableBean { 013 014 private final Class<?> type; 015 private final String metaAnnotationName; 016 private final List<String> specialAnnotationNames; 017 private final Map<String, String> nameConversions; 018 019 public ValidatableBean(Class<?> type, String metaAnnotationName, List<String> specialAnnotationNames, Map<String, String> nameConversions) { 020 this.type = type; 021 this.metaAnnotationName = metaAnnotationName; 022 this.specialAnnotationNames = specialAnnotationNames; 023 this.nameConversions = nameConversions; 024 } 025 026 public void buildConstraints(Map<String, JavaProperty> properties, Map<JavaProperty, List<JavaConstraint>> constraints) { 027 Class<? extends Annotation> metaAnnotationClass = loadMetaAnnotationClass(type, metaAnnotationName); 028 if (metaAnnotationClass == null) 029 return; 030 031 // Collect validation annotations 032 for (JavaProperty property : properties.values()) { 033 List<JavaConstraint> javaConstraints = new ArrayList<JavaConstraint>(); 034 035 List<Annotation> constraintAnnotations = new ArrayList<Annotation>(); 036 for (Annotation annotation : property.getDeclaredAnnotations()) { 037 Class<? extends Annotation> annotationClass = annotation.annotationType(); 038 039 if (annotationClass.isAnnotationPresent(metaAnnotationClass) || specialAnnotationNames.contains(annotationClass.getName())) 040 constraintAnnotations.add(annotation); 041 else { 042 043 // (Spec 2.2) "...the bean validation provider treats regular annotations 044 // (annotations not annotated by @Constraint) whose value element has a 045 // return type of an array of constraint annotations in a special way. 046 // Each element in the value array are processed by the Bean Validation 047 // implementation as regular constraint annotations." 048 049 Method value = null; 050 try { 051 value = annotationClass.getMethod("value"); 052 } 053 catch (NoSuchMethodException e) { 054 } 055 056 if (value != null && value.getReturnType().isArray() && 057 value.getReturnType().getComponentType().isAnnotation() && 058 value.getReturnType().getComponentType().isAnnotationPresent(metaAnnotationClass)) { 059 060 try { 061 Annotation[] annotationList = (Annotation[])value.invoke(annotation); 062 constraintAnnotations.addAll(Arrays.asList(annotationList)); 063 } 064 catch (Exception e) { 065 // should never happen... 066 } 067 } 068 } 069 } 070 071 for (Annotation constraint : constraintAnnotations) { 072 List<String[]> attributes = new ArrayList<String[]>(); 073 074 for (Method attribute : constraint.annotationType().getDeclaredMethods()) { 075 if (Modifier.isPublic(attribute.getModifiers()) && 076 !Modifier.isStatic(attribute.getModifiers()) && 077 attribute.getParameterTypes().length == 0) { 078 079 Object value = null; 080 try { 081 value = attribute.invoke(constraint); 082 } 083 catch (Exception e) { 084 continue; 085 } 086 087 if (value != null && (!value.getClass().isArray() || Array.getLength(value) > 0)) 088 attributes.add(new String[]{attribute.getName(), escape(value), attribute.getReturnType().getName()}); 089 } 090 } 091 092 String constraintName = constraint.annotationType().getName(); 093 if (nameConversions.containsKey(constraintName)) 094 constraintName = nameConversions.get(constraintName); 095 String packageName = constraintName.indexOf(".") > 0 ? constraintName.substring(0, constraintName.lastIndexOf(".")) : ""; 096 constraintName = constraintName.indexOf(".") > 0 ? constraintName.substring(constraintName.lastIndexOf(".")+1) : constraintName; 097 if (nameConversions.containsKey(packageName)) 098 packageName = nameConversions.get(packageName); 099 100 javaConstraints.add(new JavaConstraint(packageName, constraintName, attributes)); 101 } 102 103 if (!javaConstraints.isEmpty()) 104 constraints.put(property, javaConstraints); 105 } 106 } 107 108 @SuppressWarnings("unchecked") 109 private static Class<? extends Annotation> loadMetaAnnotationClass(Class<?> type, String metaAnnotationName) { 110 try { 111 return (Class<? extends Annotation>)type.getClassLoader().loadClass(metaAnnotationName); 112 } 113 catch (Exception e) { 114 return null; 115 } 116 } 117 118 private static String escape(Object value) { 119 120 if (value.getClass().isArray()) { 121 StringBuilder sb = new StringBuilder(); 122 123 final int length = Array.getLength(value); 124 boolean first = true; 125 for (int i = 0; i < length; i++) { 126 Object item = Array.get(value, i); 127 if (item == null) 128 continue; 129 130 if (first) 131 first = false; 132 else 133 sb.append(", "); 134 135 sb.append(escape(item, true)); 136 } 137 138 return sb.toString(); 139 } 140 141 return escape(value, false); 142 } 143 144 private static String escape(Object value, boolean array) { 145 if (value instanceof Class<?>) 146 return ((Class<?>)value).getName(); 147 148 if (value.getClass().isEnum()) 149 return ((Enum<?>)value).name(); 150 151 value = value.toString().replace("&", "&").replace("\"", """); 152 if (array) 153 value = ((String)value).replace(",", ",,"); 154 return (String)value; 155 } 156}