001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bstefanescu 016 */ 017package org.nuxeo.ecm.webengine.forms.validation; 018 019import java.lang.reflect.Method; 020import java.lang.reflect.Modifier; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026 027import org.nuxeo.ecm.webengine.forms.validation.annotations.Enumeration; 028import org.nuxeo.ecm.webengine.forms.validation.annotations.Length; 029import org.nuxeo.ecm.webengine.forms.validation.annotations.NotNull; 030import org.nuxeo.ecm.webengine.forms.validation.annotations.Range; 031import org.nuxeo.ecm.webengine.forms.validation.annotations.Regex; 032import org.nuxeo.ecm.webengine.forms.validation.annotations.Required; 033 034/** 035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 036 */ 037public class FormDescriptor { 038 039 protected FormValidator validator; 040 041 protected Map<String, Field> fields = new HashMap<String, Field>(); 042 043 protected HashSet<String> requiredFields = new HashSet<String>(); 044 045 public FormDescriptor(Class<?> type) throws ReflectiveOperationException { 046 Method[] methods = type.getMethods(); // get all inherited public methods 047 int mod = type.getModifiers(); 048 if (!Modifier.isInterface(mod)) { 049 throw new IllegalArgumentException("Form type is not an interface"); 050 } 051 for (Method m : methods) { 052 String name = m.getName(); 053 if (!name.startsWith("get")) { 054 continue; 055 } 056 // get the field name 057 name = getFieldName(name, name.length()); 058 Field field = new Field(m, name); 059 if (field.required) { 060 requiredFields.add(field.name); 061 } 062 fields.put(name, field); 063 } 064 org.nuxeo.ecm.webengine.forms.validation.annotations.FormValidator fv = type.getAnnotation(org.nuxeo.ecm.webengine.forms.validation.annotations.FormValidator.class); 065 if (fv != null) { 066 validator = fv.value().newInstance(); 067 } 068 } 069 070 static class Field { 071 CompositeValidator validator; 072 073 String name; 074 075 Method m; 076 077 boolean isArray; 078 079 boolean required; 080 081 boolean notnull; 082 083 String defaultValue; 084 085 TypeConvertor<?> convertor; 086 087 Field(Method m, String name) throws ReflectiveOperationException { 088 validator = new CompositeValidator(); 089 // not null 090 NotNull nn = m.getAnnotation(NotNull.class); 091 if (nn != null) { 092 String dv = nn.value(); 093 if (dv.length() > 0) { 094 defaultValue = dv; 095 } else { 096 notnull = true; 097 } 098 } 099 // required 100 required = m.isAnnotationPresent(Required.class); 101 // enum 102 Enumeration aenum = m.getAnnotation(Enumeration.class); 103 if (aenum != null) { 104 validator.add(new EnumerationValidator(aenum.value())); 105 } 106 // regex 107 Regex regex = m.getAnnotation(Regex.class); 108 if (regex != null) { 109 validator.add(new RegexValidator(regex.value())); 110 } 111 // length 112 Length length = m.getAnnotation(Length.class); 113 if (length != null) { 114 if (length.value() > -1) { 115 validator.add(new ExactLengthValidator(length.value())); 116 } else { 117 validator.add(new LengthValidator(length.min(), length.max())); 118 } 119 } 120 // range 121 Range range = m.getAnnotation(Range.class); 122 if (range != null) { 123 validator.add(new RangeValidator(range.min(), range.max(), range.negate())); 124 } 125 // custom validator 126 org.nuxeo.ecm.webengine.forms.validation.annotations.FieldValidator custom = m.getAnnotation(org.nuxeo.ecm.webengine.forms.validation.annotations.FieldValidator.class); 127 if (custom != null) { 128 validator.add((FieldValidator) custom.value().newInstance()); 129 } 130 // type convertor 131 Class<?> rtype = m.getReturnType(); 132 isArray = rtype.isArray(); 133 if (isArray) { 134 rtype = rtype.getComponentType(); 135 } 136 convertor = TypeConvertor.getConvertor(rtype); 137 138 this.m = m; 139 this.name = name; 140 } 141 142 Object validate(String value) throws ValidationException { 143 if (value == null || value.length() == 0) { 144 value = null; // "" empty strings are treated as null values 145 if (notnull) { 146 throw new ValidationException(); 147 } else if (defaultValue != null) { 148 value = defaultValue; 149 } 150 } 151 Object obj = value; 152 if (convertor != null) { 153 obj = convertor.convert(value); 154 } 155 if (validator != null) { 156 validator.validate(value, obj); 157 } 158 return obj; 159 } 160 161 Object[] validateArray(String[] values) throws ValidationException { 162 List<Object> result = new ArrayList<Object>(); 163 for (String value : values) { 164 result.add(validate(value)); 165 } 166 if (convertor != null) { 167 return result.toArray(convertor.newArray(values.length)); 168 } else { 169 return result.toArray(new String[values.length]); 170 } 171 } 172 } 173 174 static String getFieldName(String key, int len) { 175 if (len == 4) { 176 return "" + Character.toLowerCase(key.charAt(3)); 177 } else { 178 return Character.toLowerCase(key.charAt(3)) + key.substring(4); 179 } 180 } 181 182}