001 /******************************************************************************* 002 * Portions created by Sebastian Thomschke are copyright (c) 2005-2011 Sebastian 003 * Thomschke. 004 * 005 * All Rights Reserved. This program and the accompanying materials 006 * are made available under the terms of the Eclipse Public License v1.0 007 * which accompanies this distribution, and is available at 008 * http://www.eclipse.org/legal/epl-v10.html 009 * 010 * Contributors: 011 * Sebastian Thomschke - initial implementation. 012 *******************************************************************************/ 013 package net.sf.oval.configuration.annotation; 014 015 import static net.sf.oval.Validator.*; 016 017 import java.lang.annotation.Annotation; 018 import java.lang.reflect.Constructor; 019 import java.lang.reflect.Field; 020 import java.lang.reflect.Method; 021 import java.util.Collection; 022 import java.util.List; 023 024 import javax.validation.Valid; 025 import javax.validation.constraints.AssertFalse; 026 import javax.validation.constraints.AssertTrue; 027 import javax.validation.constraints.DecimalMax; 028 import javax.validation.constraints.DecimalMin; 029 import javax.validation.constraints.Digits; 030 import javax.validation.constraints.Future; 031 import javax.validation.constraints.Max; 032 import javax.validation.constraints.Min; 033 import javax.validation.constraints.NotNull; 034 import javax.validation.constraints.Null; 035 import javax.validation.constraints.Past; 036 import javax.validation.constraints.Pattern; 037 import javax.validation.constraints.Pattern.Flag; 038 import javax.validation.constraints.Size; 039 040 import net.sf.oval.Check; 041 import net.sf.oval.collection.CollectionFactory; 042 import net.sf.oval.configuration.Configurer; 043 import net.sf.oval.configuration.pojo.elements.ClassConfiguration; 044 import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration; 045 import net.sf.oval.configuration.pojo.elements.ConstructorConfiguration; 046 import net.sf.oval.configuration.pojo.elements.FieldConfiguration; 047 import net.sf.oval.configuration.pojo.elements.MethodConfiguration; 048 import net.sf.oval.configuration.pojo.elements.MethodReturnValueConfiguration; 049 import net.sf.oval.configuration.pojo.elements.ParameterConfiguration; 050 import net.sf.oval.constraint.AssertFalseCheck; 051 import net.sf.oval.constraint.AssertNullCheck; 052 import net.sf.oval.constraint.AssertTrueCheck; 053 import net.sf.oval.constraint.AssertValidCheck; 054 import net.sf.oval.constraint.DigitsCheck; 055 import net.sf.oval.constraint.FutureCheck; 056 import net.sf.oval.constraint.MatchPatternCheck; 057 import net.sf.oval.constraint.MaxCheck; 058 import net.sf.oval.constraint.MinCheck; 059 import net.sf.oval.constraint.NotNullCheck; 060 import net.sf.oval.constraint.PastCheck; 061 import net.sf.oval.constraint.SizeCheck; 062 import net.sf.oval.guard.Guarded; 063 import net.sf.oval.internal.Log; 064 import net.sf.oval.internal.util.ReflectionUtils; 065 066 /** 067 * Constraints configurer that interprets the JSR303 built-in Java Bean Validation annotations: 068 * <ul> 069 * <li>javax.validation.constraints.AssertFalse => net.sf.oval.constraint.AssertFalseCheck 070 * <li>javax.validation.constraints.AssertTrue => net.sf.oval.constraint.AssertTrueCheck 071 * <li>javax.validation.constraints.DecimalMax => net.sf.oval.constraint.MaxCheck 072 * <li>javax.validation.constraints.DecimalMin => net.sf.oval.constraint.MinCheck 073 * <li>javax.validation.constraints.Digits => net.sf.oval.constraint.DigitsCheck 074 * <li>javax.validation.constraints.Future => net.sf.oval.constraint.FutureCheck 075 * <li>javax.validation.constraints.Max => net.sf.oval.constraint.MaxCheck 076 * <li>javax.validation.constraints.Min => net.sf.oval.constraint.MinCheck 077 * <li>javax.validation.constraints.NotNull => net.sf.oval.constraint.NotNullCheck 078 * <li>javax.validation.constraints.Null => net.sf.oval.constraint.AssertNullCheck 079 * <li>javax.validation.constraints.Past => net.sf.oval.constraint.PastCheck 080 * <li>javax.validation.constraints.Pattern => net.sf.oval.constraint.PatternCheck 081 * <li>javax.validation.constraints.Size => net.sf.oval.constraint.SizeCheck 082 * <li>javax.validation.Valid => net.sf.oval.constraint.AssertValidCheck 083 * </ul> 084 * @author Sebastian Thomschke 085 */ 086 public class BeanValidationAnnotationsConfigurer implements Configurer 087 { 088 private static final Log LOG = Log.getLog(BeanValidationAnnotationsConfigurer.class); 089 090 private List<ParameterConfiguration> _createParameterConfiguration(final Annotation[][] paramAnnotations, 091 final Class< ? >[] parameterTypes) 092 { 093 final CollectionFactory cf = getCollectionFactory(); 094 095 final List<ParameterConfiguration> paramCfg = cf.createList(paramAnnotations.length); 096 097 List<Check> paramChecks = cf.createList(2); 098 099 // loop over all parameters of the current constructor 100 for (int i = 0; i < paramAnnotations.length; i++) 101 { 102 // loop over all annotations of the current constructor parameter 103 for (final Annotation annotation : paramAnnotations[i]) 104 initializeChecks(annotation, paramChecks); 105 106 final ParameterConfiguration pc = new ParameterConfiguration(); 107 paramCfg.add(pc); 108 pc.type = parameterTypes[i]; 109 if (paramChecks.size() > 0) 110 { 111 pc.checks = paramChecks; 112 paramChecks = cf.createList(2); // create a new list for the next parameter having checks 113 } 114 } 115 return paramCfg; 116 } 117 118 protected void configureConstructorParameterChecks(final ClassConfiguration classCfg) 119 { 120 final CollectionFactory cf = getCollectionFactory(); 121 122 for (final Constructor< ? > ctor : classCfg.type.getDeclaredConstructors()) 123 { 124 final List<ParameterConfiguration> paramCfg = _createParameterConfiguration(ctor.getParameterAnnotations(), 125 ctor.getParameterTypes()); 126 127 if (paramCfg.size() > 0) 128 { 129 if (classCfg.constructorConfigurations == null) classCfg.constructorConfigurations = cf.createSet(2); 130 131 final ConstructorConfiguration cc = new ConstructorConfiguration(); 132 cc.parameterConfigurations = paramCfg; 133 cc.postCheckInvariants = false; 134 classCfg.constructorConfigurations.add(cc); 135 } 136 } 137 } 138 139 protected void configureFieldChecks(final ClassConfiguration classCfg) 140 { 141 final CollectionFactory cf = getCollectionFactory(); 142 143 List<Check> checks = cf.createList(2); 144 145 for (final Field field : classCfg.type.getDeclaredFields()) 146 { 147 // loop over all annotations of the current field 148 for (final Annotation annotation : field.getAnnotations()) 149 initializeChecks(annotation, checks); 150 151 if (checks.size() > 0) 152 { 153 if (classCfg.fieldConfigurations == null) classCfg.fieldConfigurations = cf.createSet(2); 154 155 final FieldConfiguration fc = new FieldConfiguration(); 156 fc.name = field.getName(); 157 fc.checks = checks; 158 classCfg.fieldConfigurations.add(fc); 159 checks = cf.createList(2); // create a new list for the next field with checks 160 } 161 } 162 } 163 164 /** 165 * configure method return value and parameter checks 166 */ 167 protected void configureMethodChecks(final ClassConfiguration classCfg) 168 { 169 final CollectionFactory cf = getCollectionFactory(); 170 171 List<Check> returnValueChecks = cf.createList(2); 172 173 for (final Method method : classCfg.type.getDeclaredMethods()) 174 { 175 // loop over all annotations 176 for (final Annotation annotation : ReflectionUtils.getAnnotations(method, 177 Boolean.TRUE.equals(classCfg.inspectInterfaces))) 178 initializeChecks(annotation, returnValueChecks); 179 180 /* 181 * determine parameter checks 182 */ 183 final List<ParameterConfiguration> paramCfg = _createParameterConfiguration( 184 ReflectionUtils.getParameterAnnotations(method, Boolean.TRUE.equals(classCfg.inspectInterfaces)), 185 method.getParameterTypes()); 186 187 // check if anything has been configured for this method at all 188 if (paramCfg.size() > 0 || returnValueChecks.size() > 0) 189 { 190 if (classCfg.methodConfigurations == null) classCfg.methodConfigurations = cf.createSet(2); 191 192 final MethodConfiguration mc = new MethodConfiguration(); 193 mc.name = method.getName(); 194 mc.parameterConfigurations = paramCfg; 195 mc.isInvariant = ReflectionUtils.isGetter(method); 196 if (returnValueChecks.size() > 0) 197 { 198 mc.returnValueConfiguration = new MethodReturnValueConfiguration(); 199 mc.returnValueConfiguration.checks = returnValueChecks; 200 returnValueChecks = cf.createList(2); // create a new list for the next method having return value checks 201 } 202 classCfg.methodConfigurations.add(mc); 203 } 204 } 205 } 206 207 /** 208 * {@inheritDoc} 209 */ 210 public ClassConfiguration getClassConfiguration(final Class< ? > clazz) 211 { 212 final ClassConfiguration classCfg = new ClassConfiguration(); 213 classCfg.type = clazz; 214 215 final Guarded guarded = clazz.getAnnotation(Guarded.class); 216 217 if (guarded == null) 218 { 219 classCfg.applyFieldConstraintsToConstructors = false; 220 classCfg.applyFieldConstraintsToSetters = false; 221 classCfg.assertParametersNotNull = false; 222 classCfg.checkInvariants = false; 223 classCfg.inspectInterfaces = false; 224 } 225 else 226 { 227 classCfg.applyFieldConstraintsToConstructors = guarded.applyFieldConstraintsToConstructors(); 228 classCfg.applyFieldConstraintsToSetters = guarded.applyFieldConstraintsToSetters(); 229 classCfg.assertParametersNotNull = guarded.assertParametersNotNull(); 230 classCfg.checkInvariants = guarded.checkInvariants(); 231 classCfg.inspectInterfaces = guarded.inspectInterfaces(); 232 } 233 234 configureFieldChecks(classCfg); 235 configureConstructorParameterChecks(classCfg); 236 configureMethodChecks(classCfg); 237 238 return classCfg; 239 } 240 241 /** 242 * {@inheritDoc} 243 */ 244 public ConstraintSetConfiguration getConstraintSetConfiguration(final String constraintSetId) 245 { 246 return null; 247 } 248 249 protected void initializeChecks(final Annotation annotation, final Collection<Check> checks) 250 { 251 assert annotation != null; 252 assert checks != null; 253 254 Class< ? >[] groups = null; 255 Check check = null; 256 if (annotation instanceof NotNull) 257 { 258 groups = ((NotNull) annotation).groups(); 259 check = new NotNullCheck(); 260 } 261 else if (annotation instanceof Null) 262 { 263 groups = ((Null) annotation).groups(); 264 check = new AssertNullCheck(); 265 } 266 else if (annotation instanceof Valid) 267 check = new AssertValidCheck(); 268 else if (annotation instanceof AssertTrue) 269 { 270 groups = ((AssertTrue) annotation).groups(); 271 check = new AssertTrueCheck(); 272 } 273 else if (annotation instanceof AssertFalse) 274 { 275 groups = ((AssertFalse) annotation).groups(); 276 check = new AssertFalseCheck(); 277 } 278 else if (annotation instanceof DecimalMax) 279 { 280 groups = ((DecimalMax) annotation).groups(); 281 final MaxCheck maxCheck = new MaxCheck(); 282 maxCheck.setMax(Double.parseDouble(((DecimalMax) annotation).value())); 283 check = maxCheck; 284 } 285 else if (annotation instanceof DecimalMin) 286 { 287 groups = ((DecimalMin) annotation).groups(); 288 final MinCheck minCheck = new MinCheck(); 289 minCheck.setMin(Double.parseDouble(((DecimalMin) annotation).value())); 290 check = minCheck; 291 } 292 else if (annotation instanceof Max) 293 { 294 groups = ((Max) annotation).groups(); 295 final MaxCheck maxCheck = new MaxCheck(); 296 maxCheck.setMax(((Max) annotation).value()); 297 check = maxCheck; 298 } 299 else if (annotation instanceof Min) 300 { 301 groups = ((Min) annotation).groups(); 302 final MinCheck minCheck = new MinCheck(); 303 minCheck.setMin(((Min) annotation).value()); 304 check = minCheck; 305 } 306 else if (annotation instanceof Future) 307 { 308 groups = ((Future) annotation).groups(); 309 check = new FutureCheck(); 310 } 311 else if (annotation instanceof Past) 312 { 313 groups = ((Past) annotation).groups(); 314 check = new PastCheck(); 315 } 316 else if (annotation instanceof Pattern) 317 { 318 groups = ((Pattern) annotation).groups(); 319 final MatchPatternCheck matchPatternCheck = new MatchPatternCheck(); 320 int iflag = 0; 321 for (final Flag flag : ((Pattern) annotation).flags()) 322 iflag = iflag | flag.getValue(); 323 matchPatternCheck.setPattern(((Pattern) annotation).regexp(), iflag); 324 check = matchPatternCheck; 325 } 326 else if (annotation instanceof Digits) 327 { 328 groups = ((Digits) annotation).groups(); 329 final DigitsCheck digitsCheck = new DigitsCheck(); 330 digitsCheck.setMaxFraction(((Digits) annotation).fraction()); 331 digitsCheck.setMaxInteger(((Digits) annotation).integer()); 332 check = digitsCheck; 333 } 334 else if (annotation instanceof Size) 335 { 336 groups = ((Size) annotation).groups(); 337 final SizeCheck sizeCheck = new SizeCheck(); 338 sizeCheck.setMax(((Size) annotation).max()); 339 sizeCheck.setMin(((Size) annotation).min()); 340 check = sizeCheck; 341 } 342 343 if (check != null) 344 { 345 final Method getMessage = ReflectionUtils.getMethod(annotation.getClass(), "message", (Class< ? >[]) null); 346 if (getMessage != null) 347 { 348 final String message = ReflectionUtils.invokeMethod(getMessage, annotation, (Object[]) null); 349 if (message != null && !message.startsWith("{javax.validation.constraints.")) 350 check.setMessage(message); 351 } 352 353 if (groups != null && groups.length > 0) 354 { 355 final String[] profiles = new String[groups.length]; 356 for (int i = 0, l = groups.length; i < l; i++) 357 profiles[i] = groups[i].getName(); 358 check.setProfiles(profiles); 359 } 360 checks.add(check); 361 return; 362 } 363 364 Annotation[] list = null; 365 if (annotation instanceof AssertFalse.List) 366 list = ((AssertFalse.List) annotation).value(); 367 else if (annotation instanceof AssertTrue.List) 368 list = ((AssertTrue.List) annotation).value(); 369 else if (annotation instanceof DecimalMax.List) 370 list = ((DecimalMax.List) annotation).value(); 371 else if (annotation instanceof DecimalMin.List) 372 list = ((DecimalMin.List) annotation).value(); 373 else if (annotation instanceof Digits.List) 374 list = ((Digits.List) annotation).value(); 375 else if (annotation instanceof Future.List) 376 list = ((Future.List) annotation).value(); 377 else if (annotation instanceof Max.List) 378 list = ((Max.List) annotation).value(); 379 else if (annotation instanceof Min.List) 380 list = ((Min.List) annotation).value(); 381 else if (annotation instanceof NotNull.List) 382 list = ((NotNull.List) annotation).value(); 383 else if (annotation instanceof Null.List) 384 list = ((Null.List) annotation).value(); 385 else if (annotation instanceof Past.List) 386 list = ((Past.List) annotation).value(); 387 else if (annotation instanceof Pattern.List) 388 list = ((Pattern.List) annotation).value(); 389 else if (annotation instanceof Size.List) list = ((Size.List) annotation).value(); 390 391 if (list != null) 392 for (final Annotation anno : list) 393 initializeChecks(anno, checks); 394 else 395 { 396 LOG.warn("Ignoring unsupported JSR303 constraint annotation {1}", annotation); 397 return; 398 } 399 } 400 }