/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.generator.as3.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class ValidatableBean {
private final Class<?> type;
private final String metaAnnotationName;
private final List<String> specialAnnotationNames;
private final Map<String, String> nameConversions;
public ValidatableBean(Class<?> type, String metaAnnotationName, List<String> specialAnnotationNames, Map<String, String> nameConversions) {
this.type = type;
this.metaAnnotationName = metaAnnotationName;
this.specialAnnotationNames = specialAnnotationNames;
this.nameConversions = nameConversions;
}
public void buildConstraints(Map<String, JavaProperty> properties, Map<JavaProperty, List<JavaConstraint>> constraints) {
Class<? extends Annotation> metaAnnotationClass = loadMetaAnnotationClass(type, metaAnnotationName);
if (metaAnnotationClass == null)
return;
// Collect validation annotations
for (JavaProperty property : properties.values()) {
List<JavaConstraint> javaConstraints = new ArrayList<JavaConstraint>();
List<Annotation> constraintAnnotations = new ArrayList<Annotation>();
for (Annotation annotation : property.getDeclaredAnnotations()) {
Class<? extends Annotation> annotationClass = annotation.annotationType();
if (annotationClass.isAnnotationPresent(metaAnnotationClass) || specialAnnotationNames.contains(annotationClass.getName()))
constraintAnnotations.add(annotation);
else {
// (Spec 2.2) "...the bean validation provider treats regular annotations
// (annotations not annotated by @Constraint) whose value element has a
// return type of an array of constraint annotations in a special way.
// Each element in the value array are processed by the Bean Validation
// implementation as regular constraint annotations."
Method value = null;
try {
value = annotationClass.getMethod("value");
}
catch (NoSuchMethodException e) {
}
if (value != null && value.getReturnType().isArray() &&
value.getReturnType().getComponentType().isAnnotation() &&
value.getReturnType().getComponentType().isAnnotationPresent(metaAnnotationClass)) {
try {
Annotation[] annotationList = (Annotation[])value.invoke(annotation);
constraintAnnotations.addAll(Arrays.asList(annotationList));
}
catch (Exception e) {
// should never happen...
}
}
}
}
for (Annotation constraint : constraintAnnotations) {
List<String[]> attributes = new ArrayList<String[]>();
for (Method attribute : constraint.annotationType().getDeclaredMethods()) {
if (Modifier.isPublic(attribute.getModifiers()) &&
!Modifier.isStatic(attribute.getModifiers()) &&
attribute.getParameterTypes().length == 0) {
Object value = null;
try {
value = attribute.invoke(constraint);
}
catch (Exception e) {
continue;
}
if (value != null && (!value.getClass().isArray() || Array.getLength(value) > 0))
attributes.add(new String[]{attribute.getName(), escape(value), attribute.getReturnType().getName()});
}
}
String constraintName = constraint.annotationType().getName();
if (nameConversions.containsKey(constraintName))
constraintName = nameConversions.get(constraintName);
String packageName = constraintName.indexOf(".") > 0 ? constraintName.substring(0, constraintName.lastIndexOf(".")) : "";
constraintName = constraintName.indexOf(".") > 0 ? constraintName.substring(constraintName.lastIndexOf(".")+1) : constraintName;
if (nameConversions.containsKey(packageName))
packageName = nameConversions.get(packageName);
javaConstraints.add(new JavaConstraint(packageName, constraintName, attributes));
}
if (!javaConstraints.isEmpty())
constraints.put(property, javaConstraints);
}
}
@SuppressWarnings("unchecked")
private static Class<? extends Annotation> loadMetaAnnotationClass(Class<?> type, String metaAnnotationName) {
try {
return (Class<? extends Annotation>)type.getClassLoader().loadClass(metaAnnotationName);
}
catch (Exception e) {
return null;
}
}
private static String escape(Object value) {
if (value.getClass().isArray()) {
StringBuilder sb = new StringBuilder();
final int length = Array.getLength(value);
boolean first = true;
for (int i = 0; i < length; i++) {
Object item = Array.get(value, i);
if (item == null)
continue;
if (first)
first = false;
else
sb.append(", ");
sb.append(escape(item, true));
}
return sb.toString();
}
return escape(value, false);
}
private static String escape(Object value, boolean array) {
if (value instanceof Class<?>)
return ((Class<?>)value).getName();
if (value.getClass().isEnum())
return ((Enum<?>)value).name();
value = value.toString().replace("&", "&").replace("\"", """);
if (array)
value = ((String)value).replace(",", ",,");
return (String)value;
}
}