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    }