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 @@ -316,6 +316,7 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NORMALIZED, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NULL_OR_NOT_BLANK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NULL_OR_NOT_EMPTY, TYPES_SUPPORTED_BY_SIZE_AND_NOT_EMPTY_ANNOTATIONS );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.STARTS_WITH, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.SCRIPT_ASSERT, Object.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.UNIQUE_ELEMENTS, Collection.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.URL, CharSequence.class );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public static class HibernateValidatorTypes {
public static final String NOT_BLANK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotBlank";
public static final String NOT_EMPTY = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotEmpty";
public static final String SCRIPT_ASSERT = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ScriptAssert";
public static final String STARTS_WITH = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".StartsWith";
public static final String UNIQUE_ELEMENTS = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".UniqueElements";
public static final String URL = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".URL";
public static final String DURATION_MIN = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".time.DurationMin";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.hibernate.validator.ap.testmodel.ModelWithNormalizedConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithNullOrNotBlankConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithNullOrNotEmptyConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithStartsWithConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithUUIDConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithUniqueElementsConstraints;
import org.hibernate.validator.ap.testmodel.ModelWithoutConstraints;
Expand Down Expand Up @@ -798,6 +799,25 @@ public void nullOrNotEmptyConstraints() {
);
}

@Test
@TestForIssue(jiraKey = "HV-2245")
public void startsWithConstraints() {
File[] sourceFiles = new File[] {
compilerHelper.getSourceFile( ModelWithStartsWithConstraints.class )
};

boolean compilationResult =
compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, false, true, sourceFiles );

assertFalse( compilationResult );
assertThatDiagnosticsMatch(
diagnostics,
new DiagnosticExpectation( Kind.ERROR, 15 ),
new DiagnosticExpectation( Kind.ERROR, 18 ),
new DiagnosticExpectation( Kind.ERROR, 21 )
);
}

@Test
@TestForIssue(jiraKey = "HV-1867")
public void uuidConstraints() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.validator.ap.testmodel;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.hibernate.validator.constraints.StartsWith;

public class ModelWithStartsWithConstraints {

@StartsWith("foo")
public Collection<String> collection;

@StartsWith("foo")
public List<String> list;

@StartsWith("foo")
public Set<String> set;

@StartsWith("foo")
public String string;
}
4 changes: 4 additions & 0 deletions documentation/src/main/asciidoc/reference/_ch02.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,10 @@ The default is `ANY`, which means both IPv4 and IPv6 addresses are considered va
Supported data types::: Any type
Hibernate metadata impact::: None

`@StartsWith(value=, ignoreCase=)`:: Validates that the annotated character sequence starts with at least one of the specified prefixes. When `ignoreCase` is set to `true`, the comparison is case-insensitive.
Supported data types::: `CharSequence`
Hibernate metadata impact::: None

`@UniqueElements`:: Checks that the annotated collection only contains unique elements. The equality is determined using the `equals()` method. The default message does not include the list of duplicate elements but you can include it by overriding the message and using the `{duplicates}` message parameter. The list of duplicate elements is also included in the dynamic payload of the constraint violation.
Supported data types::: `Collection`
Hibernate metadata impact::: None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/

package org.hibernate.validator.cfg.defs;

import org.hibernate.validator.Incubating;
import org.hibernate.validator.cfg.ConstraintDef;
import org.hibernate.validator.constraints.StartsWith;

/**
* A {@link StartsWith} constraint definition.
* @author Koen Aers
* @since 9.2
*/
@Incubating
public class StartsWithDef extends ConstraintDef<StartsWithDef, StartsWith> {

public StartsWithDef() {
super( StartsWith.class );
}

public StartsWithDef value(String... value) {
addParameter( "value", value );
return this;
}

public StartsWithDef ignoreCase(boolean ignoreCase) {
addParameter( "ignoreCase", ignoreCase );
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.validator.constraints;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import org.hibernate.validator.Incubating;
import org.hibernate.validator.constraints.StartsWith.List;

/**
* Validates that the annotated character sequence starts with the specified prefix(es).
* <p>
* When multiple values are specified, at least one must match (i.e., OR semantics).
* <p>
* When {@code ignoreCase} is set to {@code true}, the comparison is case-insensitive.
* {@code null} values are considered valid.
*
* @author Koen Aers
* @since 9.2
*/
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Incubating
public @interface StartsWith {

/**
* @return the prefixes of which at least one must match the start of the annotated character sequence.
*/
String[] value();

/**
* @return whether to perform case-insensitive matching.
* When {@code false} (default), matching is case-sensitive.
* When {@code true}, both the input and the prefixes are compared
* using {@link java.util.Locale#ROOT} lowercasing.
*/
boolean ignoreCase() default false;

String message() default "{org.hibernate.validator.constraints.StartsWith.message}";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

/**
* Defines several {@code @StartsWith} annotations on the same element.
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface List {
StartsWith[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.validator.internal.constraintvalidators.hv;

import java.util.Locale;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import org.hibernate.validator.constraints.StartsWith;

/**
* Checks that the character sequence starts with at least one of the specified prefixes.
*
* @author Koen Aers
*/
public class StartsWithValidator implements ConstraintValidator<StartsWith, CharSequence> {

private String[] values;
private boolean ignoreCase;

@Override
public void initialize(StartsWith parameters) {
this.ignoreCase = parameters.ignoreCase();

String[] rawValues = parameters.value();
if ( ignoreCase ) {
this.values = new String[rawValues.length];
for ( int i = 0; i < rawValues.length; i++ ) {
this.values[i] = rawValues[i].toLowerCase( Locale.ROOT );
}
}
else {
this.values = rawValues;
}
}

@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext constraintValidatorContext) {
if ( value == null ) {
return true;
}
String str = ignoreCase ? value.toString().toLowerCase( Locale.ROOT ) : value.toString();
for ( String prefix : values ) {
if ( str.startsWith( prefix ) ) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ enum BuiltinConstraint {
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_RANGE( "org.hibernate.validator.constraints.Range",
Arrays.asList( JAKARTA_VALIDATION_CONSTRAINTS_MIN, JAKARTA_VALIDATION_CONSTRAINTS_MAX ) ),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_SCRIPT_ASSERT( "org.hibernate.validator.constraints.ScriptAssert" ),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_STARTS_WITH( "org.hibernate.validator.constraints.StartsWith" ),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_URL( "org.hibernate.validator.constraints.URL",
Arrays.asList( JAKARTA_VALIDATION_CONSTRAINTS_PATTERN ) ),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_UNIQUE_ELEMENTS( "org.hibernate.validator.constraints.UniqueElements" ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_RANGE;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_RU_INN;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_SCRIPT_ASSERT;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_STARTS_WITH;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_TIME_DURATION_MAX;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_TIME_DURATION_MIN;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_UNIQUE_ELEMENTS;
Expand Down Expand Up @@ -130,6 +131,7 @@
import org.hibernate.validator.constraints.Port;
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.constraints.StartsWith;
import org.hibernate.validator.constraints.URL;
import org.hibernate.validator.constraints.UUID;
import org.hibernate.validator.constraints.UniqueElements;
Expand Down Expand Up @@ -378,6 +380,7 @@
import org.hibernate.validator.internal.constraintvalidators.hv.PortValidatorForNumber;
import org.hibernate.validator.internal.constraintvalidators.hv.PortValidatorForShort;
import org.hibernate.validator.internal.constraintvalidators.hv.ScriptAssertValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.StartsWithValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.URLValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.UUIDValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.UniqueElementsValidator;
Expand Down Expand Up @@ -897,6 +900,9 @@ protected Map<Class<? extends Annotation>, List<? extends ConstraintValidatorDes
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_SCRIPT_ASSERT ) ) {
putBuiltinConstraint( tmpConstraints, ScriptAssert.class, ScriptAssertValidator.class );
}
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_STARTS_WITH ) ) {
putBuiltinConstraint( tmpConstraints, StartsWith.class, StartsWithValidator.class );
}
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_BR_TITULO_ELEITORAL ) ) {
putBuiltinConstraint( tmpConstraints, TituloEleitoral.class );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ org.hibernate.validator.constraints.ParametersScriptAssert.message = script exp
org.hibernate.validator.constraints.Port.message = must be a valid port number
org.hibernate.validator.constraints.Range.message = must be between {min} and {max}
org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.StartsWith.message = must start with {value}
org.hibernate.validator.constraints.UniqueElements.message = must only contain unique elements
org.hibernate.validator.constraints.URL.message = must be a valid URL
org.hibernate.validator.constraints.UUID.message = must be a valid UUID
Expand Down
Loading
Loading