1
7 package org.hibernate.validator.internal.metadata.aggregated;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
10 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
11 import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
12
13 import java.lang.reflect.Type;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20
21 import javax.validation.ElementKind;
22 import javax.validation.metadata.ParameterDescriptor;
23
24 import org.hibernate.validator.internal.engine.ConstraintCreationContext;
25 import org.hibernate.validator.internal.engine.MethodValidationConfiguration;
26 import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule;
27 import org.hibernate.validator.internal.metadata.core.MetaConstraint;
28 import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl;
29 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
30 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind;
31 import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
32 import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter;
33 import org.hibernate.validator.internal.properties.Callable;
34 import org.hibernate.validator.internal.properties.Signature;
35 import org.hibernate.validator.internal.util.CollectionHelper;
36 import org.hibernate.validator.internal.util.ExecutableHelper;
37 import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
38 import org.hibernate.validator.internal.util.stereotypes.Immutable;
39
40
57 public class ExecutableMetaData extends AbstractConstraintMetaData {
58
59 private final Class<?>[] parameterTypes;
60
61 @Immutable
62 private final List<ParameterMetaData> parameterMetaDataList;
63
64 private final ValidatableParametersMetaData validatableParametersMetaData;
65
66 @Immutable
67 private final Set<MetaConstraint<?>> crossParameterConstraints;
68
69 private final boolean isGetter;
70
71
76 private final Set<Signature> signatures;
77
78 private final ReturnValueMetaData returnValueMetaData;
79 private final ElementKind kind;
80
81 private ExecutableMetaData(
82 String name,
83 Type returnType,
84 Class<?>[] parameterTypes,
85 ElementKind kind,
86 Set<Signature> signatures,
87 Set<MetaConstraint<?>> returnValueConstraints,
88 Set<MetaConstraint<?>> returnValueContainerElementConstraints,
89 List<ParameterMetaData> parameterMetaDataList,
90 Set<MetaConstraint<?>> crossParameterConstraints,
91 CascadingMetaData cascadingMetaData,
92 boolean isConstrained,
93 boolean isGetter) {
94 super(
95 name,
96 returnType,
97 returnValueConstraints,
98 returnValueContainerElementConstraints,
99 cascadingMetaData.isMarkedForCascadingOnAnnotatedObjectOrContainerElements(),
100 isConstrained
101 );
102
103 this.parameterTypes = parameterTypes;
104 this.parameterMetaDataList = CollectionHelper.toImmutableList( parameterMetaDataList );
105 this.validatableParametersMetaData = new ValidatableParametersMetaData( parameterMetaDataList );
106 this.crossParameterConstraints = CollectionHelper.toImmutableSet( crossParameterConstraints );
107 this.signatures = signatures;
108 this.returnValueMetaData = new ReturnValueMetaData(
109 returnType,
110 returnValueConstraints,
111 returnValueContainerElementConstraints,
112 cascadingMetaData
113 );
114 this.isGetter = isGetter;
115 this.kind = kind;
116 }
117
118
125 public ParameterMetaData getParameterMetaData(int parameterIndex) {
126 return parameterMetaDataList.get( parameterIndex );
127 }
128
129 public Class<?>[] getParameterTypes() {
130 return parameterTypes;
131 }
132
133
141 public Set<Signature> getSignatures() {
142 return signatures;
143 }
144
145
153 public Set<MetaConstraint<?>> getCrossParameterConstraints() {
154 return crossParameterConstraints;
155 }
156
157 public ValidatableParametersMetaData getValidatableParametersMetaData() {
158 return validatableParametersMetaData;
159 }
160
161 public ReturnValueMetaData getReturnValueMetaData() {
162 return returnValueMetaData;
163 }
164
165 @Override
166 public ExecutableDescriptorImpl asDescriptor(boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) {
167 return new ExecutableDescriptorImpl(
168 getType(),
169 getName(),
170 asDescriptors( getCrossParameterConstraints() ),
171 returnValueMetaData.asDescriptor(
172 defaultGroupSequenceRedefined,
173 defaultGroupSequence
174 ),
175 parametersAsDescriptors( defaultGroupSequenceRedefined, defaultGroupSequence ),
176 defaultGroupSequenceRedefined,
177 isGetter,
178 defaultGroupSequence
179 );
180 }
181
182 private List<ParameterDescriptor> parametersAsDescriptors(boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) {
183 List<ParameterDescriptor> parameterDescriptorList = newArrayList();
184
185 for ( ParameterMetaData parameterMetaData : parameterMetaDataList ) {
186 parameterDescriptorList.add(
187 parameterMetaData.asDescriptor(
188 defaultGroupSequenceRedefined,
189 defaultGroupSequence
190 )
191 );
192 }
193
194 return parameterDescriptorList;
195 }
196
197 @Override
198 public ElementKind getKind() {
199 return kind;
200 }
201
202 @Override
203 public String toString() {
204 StringBuilder parameterBuilder = new StringBuilder();
205
206 for ( Class<?> oneParameterType : getParameterTypes() ) {
207 parameterBuilder.append( oneParameterType.getSimpleName() );
208 parameterBuilder.append( ", " );
209 }
210
211 String parameters =
212 parameterBuilder.length() > 0 ?
213 parameterBuilder.substring( 0, parameterBuilder.length() - 2 ) :
214 parameterBuilder.toString();
215
216 return "ExecutableMetaData [executable=" + getType() + " " + getName() + "(" + parameters + "), isCascading=" + isCascading() + ", isConstrained="
217 + isConstrained() + "]";
218 }
219
220 @Override
221 public int hashCode() {
222 final int prime = 31;
223 int result = super.hashCode();
224 result = prime * result + Arrays.hashCode( parameterTypes );
225 return result;
226 }
227
228 @Override
229 public boolean equals(Object obj) {
230 if ( this == obj ) {
231 return true;
232 }
233 if ( !super.equals( obj ) ) {
234 return false;
235 }
236 if ( getClass() != obj.getClass() ) {
237 return false;
238 }
239 ExecutableMetaData other = (ExecutableMetaData) obj;
240 if ( !Arrays.equals( parameterTypes, other.parameterTypes ) ) {
241 return false;
242 }
243 return true;
244 }
245
246
252 public static class Builder extends MetaDataBuilder {
253 private final Set<Signature> signatures = newHashSet();
254
255
258 private final ConstrainedElementKind kind;
259 private final Set<ConstrainedExecutable> constrainedExecutables = newHashSet();
260 private Callable callable;
261 private final Set<MetaConstraint<?>> crossParameterConstraints = newHashSet();
262 private final Set<MethodConfigurationRule> rules;
263 private boolean isConstrained = false;
264 private CascadingMetaDataBuilder cascadingMetaDataBuilder;
265
266
272 private final Map<Class<?>, ConstrainedExecutable> executablesByDeclaringType = newHashMap();
273
274 private final ExecutableHelper executableHelper;
275
276 private final ExecutableParameterNameProvider parameterNameProvider;
277
278 public Builder(
279 Class<?> beanClass,
280 ConstrainedExecutable constrainedExecutable,
281 ConstraintCreationContext constraintCreationContext,
282 ExecutableHelper executableHelper,
283 ExecutableParameterNameProvider parameterNameProvider,
284 MethodValidationConfiguration methodValidationConfiguration) {
285 super( beanClass, constraintCreationContext );
286
287 this.executableHelper = executableHelper;
288 this.parameterNameProvider = parameterNameProvider;
289 this.kind = constrainedExecutable.getKind();
290 this.callable = constrainedExecutable.getCallable();
291 this.rules = methodValidationConfiguration.getConfiguredRuleSet();
292
293 add( constrainedExecutable );
294 }
295
296 @Override
297 public boolean accepts(ConstrainedElement constrainedElement) {
298 if ( kind != constrainedElement.getKind() ) {
299 return false;
300 }
301
302 Callable candidate = ( (ConstrainedExecutable) constrainedElement ).getCallable();
303
304
305
306 return isResolvedToSameMethodInHierarchy( callable, candidate );
307 }
308
309 private boolean isResolvedToSameMethodInHierarchy(Callable first, Callable other) {
310 if ( isConstructor( first ) || isConstructor( other ) ) {
311 return first.equals( other );
312 }
313
314 return first.isResolvedToSameMethodInHierarchy( executableHelper, getBeanClass(), other );
315 }
316
317 private boolean overrides(Callable first, Callable other) {
318 if ( isConstructor( first ) || isConstructor( other ) ) {
319 return false;
320 }
321
322 return executableHelper.overrides( first, other );
323 }
324
325 private boolean isConstructor(Callable callable) {
326 return callable.getConstrainedElementKind() == ConstrainedElementKind.CONSTRUCTOR;
327 }
328
329 @Override
330 public final void add(ConstrainedElement constrainedElement) {
331 super.add( constrainedElement );
332 ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement;
333
334 signatures.add( constrainedExecutable.getCallable().getSignature() );
335
336 constrainedExecutables.add( constrainedExecutable );
337 isConstrained = isConstrained || constrainedExecutable.isConstrained();
338 crossParameterConstraints.addAll( constrainedExecutable.getCrossParameterConstraints() );
339 if ( cascadingMetaDataBuilder == null ) {
340 cascadingMetaDataBuilder = constrainedExecutable.getCascadingMetaDataBuilder();
341 }
342 else {
343 cascadingMetaDataBuilder = cascadingMetaDataBuilder.merge( constrainedExecutable.getCascadingMetaDataBuilder() );
344 }
345
346 addToExecutablesByDeclaringType( constrainedExecutable );
347
348
349
350 if ( callable != null && overrides(
351 constrainedExecutable.getCallable(),
352 callable
353 ) ) {
354 callable = constrainedExecutable.getCallable();
355 }
356 }
357
358
364 private void addToExecutablesByDeclaringType(ConstrainedExecutable executable) {
365 Class<?> beanClass = executable.getCallable().getDeclaringClass();
366 ConstrainedExecutable mergedExecutable = executablesByDeclaringType.get( beanClass );
367
368 if ( mergedExecutable != null ) {
369 mergedExecutable = mergedExecutable.merge( executable );
370 }
371 else {
372 mergedExecutable = executable;
373 }
374
375 executablesByDeclaringType.put( beanClass, mergedExecutable );
376 }
377
378 @Override
379 public ExecutableMetaData build() {
380 assertCorrectnessOfConfiguration();
381
382 return new ExecutableMetaData(
383 callable.getName(),
384 callable.getType(),
385 callable.getParameterTypes(),
386 kind == ConstrainedElementKind.CONSTRUCTOR ? ElementKind.CONSTRUCTOR : ElementKind.METHOD,
387 kind == ConstrainedElementKind.CONSTRUCTOR ? Collections.singleton( callable.getSignature() ) :
388 CollectionHelper.toImmutableSet( signatures ),
389 adaptOriginsAndImplicitGroups( getDirectConstraints() ),
390 adaptOriginsAndImplicitGroups( getContainerElementConstraints() ),
391 findParameterMetaData(),
392 adaptOriginsAndImplicitGroups( crossParameterConstraints ),
393 cascadingMetaDataBuilder.build( constraintCreationContext.getValueExtractorManager(), callable ),
394 isConstrained,
395 kind == ConstrainedElementKind.GETTER
396 );
397 }
398
399
406 private List<ParameterMetaData> findParameterMetaData() {
407 List<ParameterMetaData.Builder> parameterBuilders = null;
408
409 for ( ConstrainedExecutable oneExecutable : constrainedExecutables ) {
410 if ( parameterBuilders == null ) {
411 parameterBuilders = newArrayList();
412
413 for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) {
414 parameterBuilders.add(
415 new ParameterMetaData.Builder(
416 callable.getDeclaringClass(),
417 oneParameter,
418 constraintCreationContext,
419 parameterNameProvider
420 )
421 );
422 }
423 }
424 else {
425 int i = 0;
426 for ( ConstrainedParameter oneParameter : oneExecutable.getAllParameterMetaData() ) {
427 parameterBuilders.get( i ).add( oneParameter );
428 i++;
429 }
430 }
431 }
432
433 List<ParameterMetaData> parameterMetaDatas = newArrayList();
434
435 for ( ParameterMetaData.Builder oneBuilder : parameterBuilders ) {
436 parameterMetaDatas.add( oneBuilder.build() );
437 }
438
439 return parameterMetaDatas;
440 }
441
442
458 private void assertCorrectnessOfConfiguration() {
459 for ( Entry<Class<?>, ConstrainedExecutable> entry : executablesByDeclaringType.entrySet() ) {
460 for ( Entry<Class<?>, ConstrainedExecutable> otherEntry : executablesByDeclaringType.entrySet() ) {
461 for ( MethodConfigurationRule rule : rules ) {
462 rule.apply( entry.getValue(), otherEntry.getValue() );
463 }
464 }
465 }
466 }
467 }
468 }
469