1
7 package org.hibernate.validator.internal.metadata.aggregated;
8
9 import java.lang.invoke.MethodHandles;
10 import java.lang.reflect.Type;
11 import java.util.ArrayDeque;
12 import java.util.Collections;
13 import java.util.Deque;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Objects;
19 import java.util.Set;
20 import java.util.stream.Collectors;
21
22 import javax.validation.ElementKind;
23
24 import org.hibernate.validator.internal.engine.ConstraintCreationContext;
25 import org.hibernate.validator.internal.metadata.core.MetaConstraint;
26 import org.hibernate.validator.internal.metadata.core.MetaConstraints;
27 import org.hibernate.validator.internal.metadata.descriptor.PropertyDescriptorImpl;
28 import org.hibernate.validator.internal.metadata.facets.Cascadable;
29 import org.hibernate.validator.internal.metadata.location.ConstraintLocation;
30 import org.hibernate.validator.internal.metadata.location.GetterConstraintLocation;
31 import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation;
32 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement;
33 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind;
34 import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable;
35 import org.hibernate.validator.internal.metadata.raw.ConstrainedField;
36 import org.hibernate.validator.internal.properties.Getter;
37 import org.hibernate.validator.internal.properties.Property;
38 import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter;
39 import org.hibernate.validator.internal.util.CollectionHelper;
40 import org.hibernate.validator.internal.util.logging.Log;
41 import org.hibernate.validator.internal.util.logging.LoggerFactory;
42 import org.hibernate.validator.internal.util.stereotypes.Immutable;
43
44
60 public class PropertyMetaData extends AbstractConstraintMetaData {
61
62 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
63
64 @Immutable
65 private final Set<Cascadable> cascadables;
66
67 private PropertyMetaData(String propertyName,
68 Type type,
69 Set<MetaConstraint<?>> constraints,
70 Set<MetaConstraint<?>> containerElementsConstraints,
71 Set<Cascadable> cascadables) {
72 super(
73 propertyName,
74 type,
75 constraints,
76 containerElementsConstraints,
77 !cascadables.isEmpty(),
78 !cascadables.isEmpty() || !constraints.isEmpty() || !containerElementsConstraints.isEmpty()
79 );
80
81 this.cascadables = CollectionHelper.toImmutableSet( cascadables );
82 }
83
84 @Override
85 public PropertyDescriptorImpl asDescriptor(boolean defaultGroupSequenceRedefined, List<Class<?>> defaultGroupSequence) {
86
87
88 CascadingMetaData firstCascadingMetaData = cascadables.isEmpty() ? null : cascadables.iterator().next().getCascadingMetaData();
89
90 return new PropertyDescriptorImpl(
91 getType(),
92 getName(),
93 asDescriptors( getDirectConstraints() ),
94 asContainerElementTypeDescriptors( getContainerElementsConstraints(), firstCascadingMetaData, defaultGroupSequenceRedefined, defaultGroupSequence ),
95 firstCascadingMetaData != null ? firstCascadingMetaData.isCascading() : false,
96 defaultGroupSequenceRedefined,
97 defaultGroupSequence,
98 firstCascadingMetaData != null ? firstCascadingMetaData.getGroupConversionDescriptors() : Collections.emptySet()
99 );
100 }
101
102
114 public Set<Cascadable> getCascadables() {
115 return cascadables;
116 }
117
118 @Override
119 public String toString() {
120 return "PropertyMetaData [type=" + getType() + ", propertyName=" + getName() + "]]";
121 }
122
123 @Override
124 public ElementKind getKind() {
125 return ElementKind.PROPERTY;
126 }
127
128 @Override
129 public int hashCode() {
130 return super.hashCode();
131 }
132
133 @Override
134 public boolean equals(Object obj) {
135 if ( this == obj ) {
136 return true;
137 }
138 if ( !super.equals( obj ) ) {
139 return false;
140 }
141 if ( getClass() != obj.getClass() ) {
142 return false;
143 }
144 return true;
145 }
146
147 public static class Builder extends MetaDataBuilder {
148
149 private static final EnumSet<ConstrainedElementKind> SUPPORTED_ELEMENT_KINDS = EnumSet.of(
150 ConstrainedElementKind.FIELD,
151 ConstrainedElementKind.GETTER
152 );
153
154 private final String propertyName;
155 private final Map<Property, Cascadable.Builder> cascadableBuilders = new HashMap<>();
156 private final Type propertyType;
157
158 public Builder(Class<?> beanClass, ConstrainedField constrainedProperty, ConstraintCreationContext constraintCreationContext) {
159 super( beanClass, constraintCreationContext );
160
161 this.propertyName = constrainedProperty.getField().getName();
162 this.propertyType = constrainedProperty.getField().getType();
163 add( constrainedProperty );
164 }
165
166 public Builder(Class<?> beanClass, ConstrainedExecutable constrainedMethod, ConstraintCreationContext constraintCreationContext) {
167 super( beanClass, constraintCreationContext );
168
169 this.propertyName = constrainedMethod.getCallable().as( Property.class ).getPropertyName();
170 this.propertyType = constrainedMethod.getCallable().getType();
171 add( constrainedMethod );
172 }
173
174 @Override
175 public boolean accepts(ConstrainedElement constrainedElement) {
176 if ( !SUPPORTED_ELEMENT_KINDS.contains( constrainedElement.getKind() ) ) {
177 return false;
178 }
179
180 return Objects.equals( getPropertyName( constrainedElement ), propertyName );
181 }
182
183 @Override
184 public final void add(ConstrainedElement constrainedElement) {
185 super.add( constrainedElement );
186
187 if ( constrainedElement.getCascadingMetaDataBuilder().isMarkedForCascadingOnAnnotatedObjectOrContainerElements() ||
188 constrainedElement.getCascadingMetaDataBuilder().hasGroupConversionsOnAnnotatedObjectOrContainerElements() ) {
189
190 Property property = getConstrainableFromConstrainedElement( constrainedElement );
191
192 Cascadable.Builder builder = cascadableBuilders.get( property );
193 if ( builder == null ) {
194 builder = AbstractPropertyCascadable.AbstractBuilder.builder(
195 constraintCreationContext.getValueExtractorManager(), property,
196 constrainedElement.getCascadingMetaDataBuilder() );
197 cascadableBuilders.put( property, builder );
198 }
199 else {
200 builder.mergeCascadingMetaData( constrainedElement.getCascadingMetaDataBuilder() );
201 }
202 }
203 }
204
205 private Property getConstrainableFromConstrainedElement(ConstrainedElement constrainedElement) {
206 switch ( constrainedElement.getKind() ) {
207 case FIELD:
208 if ( constrainedElement instanceof ConstrainedField ) {
209 return ( (ConstrainedField) constrainedElement ).getField();
210 }
211 else {
212 throw LOG.getUnexpectedConstraintElementType( ConstrainedField.class, constrainedElement.getClass() );
213 }
214 case GETTER:
215 if ( constrainedElement instanceof ConstrainedExecutable ) {
216 return ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Getter.class );
217 }
218 else {
219 throw LOG.getUnexpectedConstraintElementType( ConstrainedExecutable.class, constrainedElement.getClass() );
220 }
221 default:
222 throw LOG.getUnsupportedConstraintElementType( constrainedElement.getKind() );
223 }
224 }
225
226 @Override
227 protected Set<MetaConstraint<?>> adaptConstraints(ConstrainedElement constrainedElement, Set<MetaConstraint<?>> constraints) {
228 if ( constraints.isEmpty() || constrainedElement.getKind() != ConstrainedElementKind.GETTER ) {
229 return constraints;
230 }
231
232 ConstraintLocation getterConstraintLocation = ConstraintLocation
233 .forGetter( ( (ConstrainedExecutable) constrainedElement ).getCallable().as( JavaBeanGetter.class ) );
234
235
236 return constraints.stream()
237 .map( c -> withGetterLocation( getterConstraintLocation, c ) )
238 .collect( Collectors.toSet() );
239 }
240
241 private MetaConstraint<?> withGetterLocation(ConstraintLocation getterConstraintLocation, MetaConstraint<?> constraint) {
242 ConstraintLocation converted = null;
243
244
245 if ( !( constraint.getLocation() instanceof TypeArgumentConstraintLocation ) ) {
246
247 if ( constraint.getLocation() instanceof GetterConstraintLocation ) {
248 converted = constraint.getLocation();
249 }
250 else {
251 converted = getterConstraintLocation;
252 }
253 }
254 else {
255 Deque<ConstraintLocation> locationStack = new ArrayDeque<>();
256
257
258 ConstraintLocation current = constraint.getLocation();
259 do {
260 locationStack.addFirst( current );
261 if ( current instanceof TypeArgumentConstraintLocation ) {
262 current = ( (TypeArgumentConstraintLocation) current ).getDelegate();
263 }
264 else {
265 current = null;
266 }
267 }
268 while ( current != null );
269
270
271 for ( ConstraintLocation location : locationStack ) {
272 if ( !(location instanceof TypeArgumentConstraintLocation) ) {
273
274 if ( location instanceof GetterConstraintLocation ) {
275 converted = location;
276 }
277 else {
278 converted = getterConstraintLocation;
279 }
280 }
281 else {
282 converted = ConstraintLocation.forTypeArgument(
283 converted,
284 ( (TypeArgumentConstraintLocation) location ).getTypeParameter(),
285 location.getTypeForValidatorResolution()
286 );
287 }
288 }
289 }
290
291 return MetaConstraints.create( constraintCreationContext.getTypeResolutionHelper(),
292 constraintCreationContext.getValueExtractorManager(),
293 constraintCreationContext.getConstraintValidatorManager(), constraint.getDescriptor(), converted );
294 }
295
296 private String getPropertyName(ConstrainedElement constrainedElement) {
297 if ( constrainedElement.getKind() == ConstrainedElementKind.FIELD ) {
298 return ( (ConstrainedField) constrainedElement ).getField().getPropertyName();
299 }
300 else if ( constrainedElement.getKind() == ConstrainedElementKind.GETTER ) {
301 return ( (ConstrainedExecutable) constrainedElement ).getCallable().as( Property.class ).getPropertyName();
302 }
303
304 return null;
305 }
306
307 @Override
308 public PropertyMetaData build() {
309 Set<Cascadable> cascadables = cascadableBuilders.values()
310 .stream()
311 .map( b -> b.build() )
312 .collect( Collectors.toSet() );
313
314 return new PropertyMetaData(
315 propertyName,
316 propertyType,
317 adaptOriginsAndImplicitGroups( getDirectConstraints() ),
318 adaptOriginsAndImplicitGroups( getContainerElementConstraints() ),
319 cascadables
320 );
321 }
322 }
323 }
324