1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package org.hibernate.validator.internal.metadata.aggregated;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
10 import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
11
12 import java.lang.invoke.MethodHandles;
13 import java.lang.reflect.Executable;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.stream.Collectors;
23
24 import javax.validation.ElementKind;
25 import javax.validation.groups.Default;
26 import javax.validation.metadata.BeanDescriptor;
27 import javax.validation.metadata.ConstructorDescriptor;
28 import javax.validation.metadata.PropertyDescriptor;
29
30 import org.hibernate.validator.internal.engine.groups.Sequence;
31 import org.hibernate.validator.internal.engine.groups.ValidationOrder;
32 import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
33 import org.hibernate.validator.internal.metadata.core.MetaConstraint;
34 import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl;
35 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
36 import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl;
37 import org.hibernate.validator.internal.metadata.facets.Cascadable;
38 import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
39 import org.hibernate.validator.internal.properties.Signature;
40 import org.hibernate.validator.internal.util.CollectionHelper;
41 import org.hibernate.validator.internal.util.ExecutableHelper;
42 import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
43 import org.hibernate.validator.internal.util.classhierarchy.Filters;
44 import org.hibernate.validator.internal.util.logging.Log;
45 import org.hibernate.validator.internal.util.logging.LoggerFactory;
46 import org.hibernate.validator.internal.util.stereotypes.Immutable;
47 import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
48
49 /**
50  * This class encapsulates all meta data needed for validation. Implementations of {@code Validator} interface can
51  * instantiate an instance of this class and delegate the metadata extraction to it.
52  *
53  * @author Hardy Ferentschik
54  * @author Gunnar Morling
55  * @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
56  * @author Chris Beckey &lt;cbeckey@paypal.com&gt;
57  * @author Guillaume Smet
58  * @author Marko Bekhta
59  */

60 public final class BeanMetaDataImpl<T> implements BeanMetaData<T> {
61
62     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
63
64     /**
65      * Represents the "sequence" of just Default.class.
66      */

67     private static final List<Class<?>> DEFAULT_GROUP_SEQUENCE = Collections.<Class<?>>singletonList( Default.class );
68
69     /**
70      * Whether there are any constraints or cascades at all.
71      */

72     private final boolean hasConstraints;
73
74     private final ValidationOrderGenerator validationOrderGenerator;
75
76     /**
77      * The root bean class for this meta data.
78      */

79     private final Class<T> beanClass;
80
81     /**
82      * Set of all constraints for this bean type (defined on any implemented interfaces or super types)
83      */

84     @Immutable
85     private final Set<MetaConstraint<?>> allMetaConstraints;
86
87     /**
88      * Set of all constraints which are directly defined on the bean or any of the directly implemented interfaces
89      */

90     @Immutable
91     private final Set<MetaConstraint<?>> directMetaConstraints;
92
93     /**
94      * Contains constrained related meta data for all the constrained methods and constructors of the type represented
95      * by this bean meta data. Keyed by executable, values are an aggregated view on each executable together with all
96      * the executables from the inheritance hierarchy with the same signature.
97      * <p>
98      * An entry will be stored once under the signature of the represented method and all the methods it overrides
99      * (there will only be more than one entry in case of generics in the parameters, e.g. in case of a super-type
100      * method {@code foo(T)} and an overriding sub-type method {@code foo(String)} two entries for the same executable
101      * meta-data will be stored).
102      */

103     @Immutable
104     private final Map<Signature, ExecutableMetaData> executableMetaDataMap;
105
106     /**
107      * The set of unconstrained executables of the bean. It contains all the relevant signatures, following the same
108      * rules as {@code executableMetaDataMap}.
109      */

110     @Immutable
111     private final Set<Signature> unconstrainedExecutables;
112
113     /**
114      * Property meta data keyed against the property name
115      */

116     @Immutable
117     private final Map<String, PropertyMetaData> propertyMetaDataMap;
118
119     /**
120      * The cascaded properties of this bean.
121      */

122     @Immutable
123     private final Set<Cascadable> cascadedProperties;
124
125     /**
126      * The default groups sequence for this bean class.
127      */

128     @Immutable
129     private final List<Class<?>> defaultGroupSequence;
130
131     /**
132      * The default group sequence provider.
133      *
134      * @see org.hibernate.validator.group.GroupSequenceProvider
135      * @see DefaultGroupSequenceProvider
136      */

137     private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;
138
139     private final ValidationOrder validationOrder;
140
141     /**
142      * The class hierarchy for this class starting with the class itself going up the inheritance chain. Interfaces
143      * are not included.
144      */

145     @Immutable
146     private final List<Class<? super T>> classHierarchyWithoutInterfaces;
147
148     /**
149      * {code trueif the default group sequence is redefined, either via a group sequence redefinition or a group
150      * sequence provider.
151      */

152     private final boolean defaultGroupSequenceRedefined;
153
154     /**
155      * The resolved default group sequence.
156      */

157     private final List<Class<?>> resolvedDefaultGroupSequence;
158
159     /**
160      * The bean descriptor for this bean. Lazily created.
161      */

162     private volatile BeanDescriptor beanDescriptor;
163
164     /**
165      * Creates a new {@link BeanMetaDataImpl}
166      *
167      * @param beanClass The Java type represented by this meta data object.
168      * @param defaultGroupSequence The default group sequence.
169      * @param defaultGroupSequenceProvider The default group sequence provider if set.
170      * @param constraintMetaDataSet All constraint meta data relating to the represented type.
171      */

172     public BeanMetaDataImpl(Class<T> beanClass,
173                             List<Class<?>> defaultGroupSequence,
174                             DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider,
175                             Set<ConstraintMetaData> constraintMetaDataSet,
176                             ValidationOrderGenerator validationOrderGenerator) {
177
178         this.validationOrderGenerator = validationOrderGenerator;
179         this.beanClass = beanClass;
180         this.propertyMetaDataMap = newHashMap();
181
182         Set<PropertyMetaData> propertyMetaDataSet = newHashSet();
183
184         Set<ExecutableMetaData> executableMetaDataSet = newHashSet();
185         Set<Signature> tmpUnconstrainedExecutables = newHashSet();
186
187         boolean hasConstraints = false;
188         Set<MetaConstraint<?>> allMetaConstraints = newHashSet();
189
190         for ( ConstraintMetaData constraintMetaData : constraintMetaDataSet ) {
191             boolean elementHasConstraints = constraintMetaData.isCascading() || constraintMetaData.isConstrained();
192             hasConstraints |= elementHasConstraints;
193
194             if ( constraintMetaData.getKind() == ElementKind.PROPERTY ) {
195                 propertyMetaDataSet.add( (PropertyMetaData) constraintMetaData );
196             }
197             else if ( constraintMetaData.getKind() == ElementKind.BEAN ) {
198                 allMetaConstraints.addAll( ( (ClassMetaData) constraintMetaData ).getAllConstraints() );
199             }
200             else {
201                 ExecutableMetaData executableMetaData = (ExecutableMetaData) constraintMetaData;
202                 if ( elementHasConstraints ) {
203                     executableMetaDataSet.add( executableMetaData );
204                 }
205                 else {
206                     tmpUnconstrainedExecutables.addAll( executableMetaData.getSignatures() );
207                 }
208             }
209         }
210
211         Set<Cascadable> cascadedProperties = newHashSet();
212
213         for ( PropertyMetaData propertyMetaData : propertyMetaDataSet ) {
214             propertyMetaDataMap.put( propertyMetaData.getName(), propertyMetaData );
215             cascadedProperties.addAll( propertyMetaData.getCascadables() );
216             allMetaConstraints.addAll( propertyMetaData.getAllConstraints() );
217         }
218
219         this.hasConstraints = hasConstraints;
220         this.cascadedProperties = CollectionHelper.toImmutableSet( cascadedProperties );
221         this.allMetaConstraints = CollectionHelper.toImmutableSet( allMetaConstraints );
222
223         this.classHierarchyWithoutInterfaces = CollectionHelper.toImmutableList( ClassHierarchyHelper.getHierarchy(
224                 beanClass,
225                 Filters.excludeInterfaces()
226         ) );
227
228         DefaultGroupSequenceContext<? super T> defaultGroupContext = getDefaultGroupSequenceData( beanClass, defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator );
229         this.defaultGroupSequenceProvider = defaultGroupContext.defaultGroupSequenceProvider;
230         this.defaultGroupSequence = CollectionHelper.toImmutableList( defaultGroupContext.defaultGroupSequence );
231         this.validationOrder = defaultGroupContext.validationOrder;
232
233         this.directMetaConstraints = getDirectConstraints();
234
235         this.executableMetaDataMap = CollectionHelper.toImmutableMap( bySignature( executableMetaDataSet ) );
236         this.unconstrainedExecutables = CollectionHelper.toImmutableSet( tmpUnconstrainedExecutables );
237
238         // We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata
239         this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider();
240         this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null );
241     }
242
243     @Override
244     public Class<T> getBeanClass() {
245         return beanClass;
246     }
247
248     @Override
249     public boolean hasConstraints() {
250         return hasConstraints;
251     }
252
253     @Override
254     public BeanDescriptor getBeanDescriptor() {
255         BeanDescriptor beanDescriptor = this.beanDescriptor;
256
257         if ( beanDescriptor == null ) {
258             synchronized (this) {
259                 beanDescriptor = this.beanDescriptor;
260
261                 if ( beanDescriptor == null ) {
262                     beanDescriptor = createBeanDescriptor( beanClass, allMetaConstraints, propertyMetaDataMap, executableMetaDataMap,
263                             defaultGroupSequenceRedefined, resolvedDefaultGroupSequence );
264
265                     this.beanDescriptor = beanDescriptor;
266                 }
267             }
268         }
269
270         return beanDescriptor;
271     }
272
273     @Override
274     public Set<Cascadable> getCascadables() {
275         return cascadedProperties;
276     }
277
278     @Override
279     public boolean hasCascadables() {
280         return !cascadedProperties.isEmpty();
281     }
282
283     @Override
284     public PropertyMetaData getMetaDataFor(String propertyName) {
285         PropertyMetaData propertyMetaData = propertyMetaDataMap.get( propertyName );
286
287         if ( propertyMetaData == null ) {
288             throw LOG.getPropertyNotDefinedByValidatedTypeException( beanClass, propertyName );
289         }
290
291         return propertyMetaData;
292     }
293
294     @Override
295     public Set<MetaConstraint<?>> getMetaConstraints() {
296         return allMetaConstraints;
297     }
298
299     @Override
300     public Set<MetaConstraint<?>> getDirectMetaConstraints() {
301         return directMetaConstraints;
302     }
303
304     @Override
305     public Optional<ExecutableMetaData> getMetaDataFor(Executable executable) {
306         Signature signature = ExecutableHelper.getSignature( executable );
307
308         if ( unconstrainedExecutables.contains( signature ) ) {
309             return Optional.empty();
310         }
311
312         ExecutableMetaData executableMetaData = executableMetaDataMap.get( signature );
313
314         if ( executableMetaData == null ) {
315             // there is no executable metadata - specified object and method do not match
316             throw LOG.getMethodOrConstructorNotDefinedByValidatedTypeException(
317                     beanClass,
318                     executable
319             );
320         }
321
322         return Optional.of( executableMetaData );
323     }
324
325     @Override
326     public List<Class<?>> getDefaultGroupSequence(T beanState) {
327         if ( hasDefaultGroupSequenceProvider() ) {
328             List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );
329             return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence );
330         }
331
332         return defaultGroupSequence;
333     }
334
335     @Override
336     public Iterator<Sequence> getDefaultValidationSequence(T beanState) {
337         if ( hasDefaultGroupSequenceProvider() ) {
338             List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );
339             return validationOrderGenerator.getDefaultValidationOrder(
340                     beanClass,
341                     getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence )
342             )
343                     .getSequenceIterator();
344         }
345         else {
346             return validationOrder.getSequenceIterator();
347         }
348     }
349
350     @Override
351     public boolean isDefaultGroupSequenceRedefined() {
352         return defaultGroupSequenceRedefined;
353     }
354
355     @Override
356     public List<Class<? super T>> getClassHierarchy() {
357         return classHierarchyWithoutInterfaces;
358     }
359
360     private static BeanDescriptor createBeanDescriptor(Class<?> beanClass, Set<MetaConstraint<?>> allMetaConstraints,
361             Map<String, PropertyMetaData> propertyMetaDataMap, Map<Signature, ExecutableMetaData> executableMetaDataMap,
362             boolean defaultGroupSequenceRedefined,
363             List<Class<?>> resolvedDefaultGroupSequence) {
364         Map<String, PropertyDescriptor> propertyDescriptors = getConstrainedPropertiesAsDescriptors(
365                 propertyMetaDataMap,
366                 defaultGroupSequenceRedefined,
367                 resolvedDefaultGroupSequence
368         );
369
370         Map<Signature, ExecutableDescriptorImpl> methodsDescriptors = getConstrainedMethodsAsDescriptors(
371                 executableMetaDataMap,
372                 defaultGroupSequenceRedefined,
373                 resolvedDefaultGroupSequence
374         );
375
376         Map<Signature, ConstructorDescriptor> constructorsDescriptors = getConstrainedConstructorsAsDescriptors(
377                 executableMetaDataMap,
378                 defaultGroupSequenceRedefined,
379                 resolvedDefaultGroupSequence
380         );
381
382         return new BeanDescriptorImpl(
383                 beanClass,
384                 getClassLevelConstraintsAsDescriptors( allMetaConstraints ),
385                 propertyDescriptors,
386                 methodsDescriptors,
387                 constructorsDescriptors,
388                 defaultGroupSequenceRedefined,
389                 resolvedDefaultGroupSequence
390         );
391     }
392
393     private static Set<ConstraintDescriptorImpl<?>> getClassLevelConstraintsAsDescriptors(Set<MetaConstraint<?>> constraints) {
394         return constraints.stream()
395                 .filter( c -> c.getConstraintLocationKind() == ConstraintLocationKind.TYPE )
396                 .map( MetaConstraint::getDescriptor )
397                 .collect( Collectors.toSet() );
398     }
399
400     private static Map<String, PropertyDescriptor> getConstrainedPropertiesAsDescriptors(Map<String, PropertyMetaData> propertyMetaDataMap,
401             boolean defaultGroupSequenceIsRedefined, List<Class<?>> resolvedDefaultGroupSequence) {
402         Map<String, PropertyDescriptor> theValue = newHashMap();
403
404         for ( Entry<String, PropertyMetaData> entry : propertyMetaDataMap.entrySet() ) {
405             if ( entry.getValue().isConstrained() && entry.getValue().getName() != null ) {
406                 theValue.put(
407                         entry.getKey(),
408                         entry.getValue().asDescriptor(
409                                 defaultGroupSequenceIsRedefined,
410                                 resolvedDefaultGroupSequence
411                         )
412                 );
413             }
414         }
415
416         return theValue;
417     }
418
419     private static Map<Signature, ExecutableDescriptorImpl> getConstrainedMethodsAsDescriptors(
420             Map<Signature, ExecutableMetaData> executableMetaDataMap,
421             boolean defaultGroupSequenceIsRedefined, List<Class<?>> resolvedDefaultGroupSequence) {
422         Map<Signature, ExecutableDescriptorImpl> constrainedMethodDescriptors = newHashMap();
423
424         for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) {
425             if ( executableMetaData.getKind() == ElementKind.METHOD
426                     && executableMetaData.isConstrained() ) {
427                 ExecutableDescriptorImpl descriptor = executableMetaData.asDescriptor(
428                         defaultGroupSequenceIsRedefined,
429                         resolvedDefaultGroupSequence
430                 );
431
432                 for ( Signature signature : executableMetaData.getSignatures() ) {
433                     constrainedMethodDescriptors.put( signature, descriptor );
434                 }
435             }
436         }
437
438         return constrainedMethodDescriptors;
439     }
440
441     private static Map<Signature, ConstructorDescriptor> getConstrainedConstructorsAsDescriptors(Map<Signature, ExecutableMetaData> executableMetaDataMap,
442             boolean defaultGroupSequenceIsRedefined, List<Class<?>> resolvedDefaultGroupSequence) {
443         Map<Signature, ConstructorDescriptor> constrainedMethodDescriptors = newHashMap();
444
445         for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) {
446             if ( executableMetaData.getKind() == ElementKind.CONSTRUCTOR && executableMetaData.isConstrained() ) {
447                 constrainedMethodDescriptors.put(
448                         // constructors never override, so there will be exactly one identifier
449                         executableMetaData.getSignatures().iterator().next(),
450                         executableMetaData.asDescriptor(
451                                 defaultGroupSequenceIsRedefined,
452                                 resolvedDefaultGroupSequence
453                         )
454                 );
455             }
456         }
457
458         return constrainedMethodDescriptors;
459     }
460
461     private static <T> DefaultGroupSequenceContext<T> getDefaultGroupSequenceData(Class<?> beanClass, List<Class<?>> defaultGroupSequence, DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) {
462         if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) {
463             throw LOG.getInvalidDefaultGroupSequenceDefinitionException();
464         }
465
466         DefaultGroupSequenceContext<T> context = new DefaultGroupSequenceContext<>();
467
468         if ( defaultGroupSequenceProvider != null ) {
469             context.defaultGroupSequenceProvider = defaultGroupSequenceProvider;
470             context.defaultGroupSequence = Collections.emptyList();
471             context.validationOrder = null;
472         }
473         else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) {
474             context.defaultGroupSequence = getValidDefaultGroupSequence( beanClass, defaultGroupSequence );
475             context.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, context.defaultGroupSequence );
476         }
477         else {
478             context.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE;
479             context.validationOrder = ValidationOrder.DEFAULT_SEQUENCE;
480         }
481
482         return context;
483     }
484
485     private Set<MetaConstraint<?>> getDirectConstraints() {
486         Set<MetaConstraint<?>> constraints = newHashSet();
487
488         Set<Class<?>> classAndInterfaces = newHashSet();
489         classAndInterfaces.add( beanClass );
490         classAndInterfaces.addAll( ClassHierarchyHelper.getDirectlyImplementedInterfaces( beanClass ) );
491
492         for ( Class<?> clazz : classAndInterfaces ) {
493             for ( MetaConstraint<?> metaConstraint : allMetaConstraints ) {
494                 if ( metaConstraint.getLocation().getDeclaringClass().equals( clazz ) ) {
495                     constraints.add( metaConstraint );
496                 }
497             }
498         }
499
500         return CollectionHelper.toImmutableSet( constraints );
501     }
502
503     /**
504      * Builds up the method meta data for this type; each meta-data entry will be stored under the signature of the
505      * represented method and all the methods it overrides.
506      */

507     private Map<Signature, ExecutableMetaData> bySignature(Set<ExecutableMetaData> executables) {
508         Map<Signature, ExecutableMetaData> theValue = newHashMap();
509
510         for ( ExecutableMetaData executableMetaData : executables ) {
511             for ( Signature signature : executableMetaData.getSignatures() ) {
512                 theValue.put( signature, executableMetaData );
513             }
514         }
515
516         return theValue;
517     }
518
519     private static List<Class<?>> getValidDefaultGroupSequence(Class<?> beanClass, List<Class<?>> groupSequence) {
520         List<Class<?>> validDefaultGroupSequence = new ArrayList<>();
521
522         boolean groupSequenceContainsDefault = false;
523         if ( groupSequence != null ) {
524             for ( Class<?> group : groupSequence ) {
525                 if ( group.getName().equals( beanClass.getName() ) ) {
526                     validDefaultGroupSequence.add( Default.class );
527                     groupSequenceContainsDefault = true;
528                 }
529                 else if ( group.getName().equals( Default.class.getName() ) ) {
530                     throw LOG.getNoDefaultGroupInGroupSequenceException();
531                 }
532                 else {
533                     validDefaultGroupSequence.add( group );
534                 }
535             }
536         }
537         if ( !groupSequenceContainsDefault ) {
538             throw LOG.getBeanClassMustBePartOfRedefinedDefaultGroupSequenceException( beanClass );
539         }
540         if ( LOG.isTraceEnabled() ) {
541             LOG.tracef(
542                     "Members of the default group sequence for bean %s are: %s.",
543                     beanClass.getName(),
544                     validDefaultGroupSequence
545             );
546         }
547
548         return validDefaultGroupSequence;
549     }
550
551     private boolean hasDefaultGroupSequenceProvider() {
552         return defaultGroupSequenceProvider != null;
553     }
554
555     @Override
556     public String toString() {
557         return "BeanMetaDataImpl"
558                 + "{beanClass=" + beanClass.getSimpleName()
559                 + ", constraintCount=" + getMetaConstraints().size()
560                 + ", cascadedPropertiesCount=" + cascadedProperties.size()
561                 + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}';
562     }
563
564     /**
565      * Tuple for returning default group sequence, provider and validation order at once.
566      */

567     private static class DefaultGroupSequenceContext<T> {
568         List<Class<?>> defaultGroupSequence;
569         DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;
570         ValidationOrder validationOrder;
571     }
572 }
573