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