1
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
60 public final class BeanMetaDataImpl<T> implements BeanMetaData<T> {
61
62 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
63
64
67 private static final List<Class<?>> DEFAULT_GROUP_SEQUENCE = Collections.<Class<?>>singletonList( Default.class );
68
69
72 private final boolean hasConstraints;
73
74 private final ValidationOrderGenerator validationOrderGenerator;
75
76
79 private final Class<T> beanClass;
80
81
84 @Immutable
85 private final Set<MetaConstraint<?>> allMetaConstraints;
86
87
90 @Immutable
91 private final Set<MetaConstraint<?>> directMetaConstraints;
92
93
103 @Immutable
104 private final Map<Signature, ExecutableMetaData> executableMetaDataMap;
105
106
110 @Immutable
111 private final Set<Signature> unconstrainedExecutables;
112
113
116 @Immutable
117 private final Map<String, PropertyMetaData> propertyMetaDataMap;
118
119
122 @Immutable
123 private final Set<Cascadable> cascadedProperties;
124
125
128 @Immutable
129 private final List<Class<?>> defaultGroupSequence;
130
131
137 private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;
138
139 private final ValidationOrder validationOrder;
140
141
145 @Immutable
146 private final List<Class<? super T>> classHierarchyWithoutInterfaces;
147
148
152 private final boolean defaultGroupSequenceRedefined;
153
154
157 private final List<Class<?>> resolvedDefaultGroupSequence;
158
159
162 private volatile BeanDescriptor beanDescriptor;
163
164
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
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
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
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
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
567 private static class DefaultGroupSequenceContext<T> {
568 List<Class<?>> defaultGroupSequence;
569 DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;
570 ValidationOrder validationOrder;
571 }
572 }
573