Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ public Optional<String> getPattern() {
@Override
public Number getMin() {

return getAnnotationAttribute(Size.class, "min", Number.class) //
return sizeAsNumericBound("min") //
.or(() -> Optional.ofNullable(RANGE_ANNOTATION)
.flatMap(it -> getAnnotationAttribute(it, "min", Number.class))) //
.or(() -> getAnnotationAttribute(Min.class, "value", Number.class)) //
Expand All @@ -657,7 +657,7 @@ public Number getMin() {
@Override
public Number getMax() {

return getAnnotationAttribute(Size.class, "max", Number.class) //
return sizeAsNumericBound("max") //
.or(() -> Optional.ofNullable(RANGE_ANNOTATION)
.flatMap(it -> getAnnotationAttribute(it, "max", Number.class))) //
.or(() -> getAnnotationAttribute(Max.class, "value", Number.class)) //
Expand All @@ -672,8 +672,10 @@ public Number getMax() {
@Nullable
@Override
public Long getMinLength() {
return LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "min", Integer.class)) //
.map(Integer::longValue) //

return sizeAsLengthBound("min") //
.or(() -> LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "min", Integer.class)) //
.map(Integer::longValue)) //
.orElse(null);
}

Expand All @@ -684,8 +686,10 @@ public Long getMinLength() {
@Nullable
@Override
public Long getMaxLength() {
return LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "max", Integer.class)) //
.map(Integer::longValue) //

return sizeAsLengthBound("max") //
.or(() -> LENGTH_ANNOTATION.flatMap(it -> getAnnotationAttribute(it, "max", Integer.class)) //
.map(Integer::longValue)) //
.orElse(null);
}

Expand Down Expand Up @@ -737,6 +741,12 @@ private Optional<Number> parsePropertyAnnotationValue(Class<? extends Annotation
return TYPE_MAP.entrySet().stream() //
.flatMap(it -> {

// @Size constrains element/character count. For CharSequence properties that maps to a text
// input with minLength/maxLength rather than a numeric range control (GH-2531).
if (Size.class.equals(it.getKey()) && isCharSequenceProperty()) {
return Stream.empty();
}

MergedAnnotation<? extends Annotation> annotation = property.getAnnotation(it.getKey());

return annotation.isPresent() ? Stream.of(it.getValue()) : Stream.empty();
Expand All @@ -745,6 +755,36 @@ private Optional<Number> parsePropertyAnnotationValue(Class<? extends Annotation
.orElse(null);
}

/**
* {@link Size} is a character/element count constraint. Only surface it as numeric {@code min}/{@code max} when the
* property is not a {@link CharSequence} (GH-2531).
*/
private Optional<Number> sizeAsNumericBound(String attribute) {

if (isCharSequenceProperty()) {
return Optional.empty();
}

return getAnnotationAttribute(Size.class, attribute, Number.class);
}

/**
* Map {@link Size} to {@code minLength}/{@code maxLength} for {@link CharSequence} properties (GH-2531).
*/
private Optional<Long> sizeAsLengthBound(String attribute) {

if (!isCharSequenceProperty()) {
return Optional.empty();
}

return getAnnotationAttribute(Size.class, attribute, Number.class) //
.map(Number::longValue);
}

private boolean isCharSequenceProperty() {
return CharSequence.class.isAssignableFrom(property.getType().resolve(Object.class));
}

private <T> Optional<T> getAnnotationAttribute(Class<? extends Annotation> annotation, String attribute,
Class<T> type) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,20 @@ void exposesMinAndMaxFromJsr303AtSizeAnnotation() {
});
}

@Test // #2531
void mapsSizeOnCharSequenceToMinAndMaxLength() {

InputPayloadMetadata metadata = PropertyUtils.getExposedProperties(Jsr303SamplePayload.class);

assertThat(getProperty(metadata, "named")).hasValueSatisfying(it -> {
assertThat(it.getInputType()).isEqualTo(HtmlInputType.TEXT_VALUE);
assertThat(it.getMinLength()).isEqualTo(0L);
assertThat(it.getMaxLength()).isEqualTo(256L);
assertThat(it.getMin()).isNull();
assertThat(it.getMax()).isNull();
});
}

@Data
@AllArgsConstructor
@JsonIgnoreProperties({ "ignoreThisProperty" })
Expand Down Expand Up @@ -285,6 +299,7 @@ static class Jsr303SamplePayload {
@NotBlank @Pattern(regexp = "\\w") String nonBlankPattern;
TypeAnnotated annotated;
@Size(min = 41, max = 4711) int sized;
@Size(max = 256) String named;
}

@Pattern(regexp = "regex")
Expand Down