implement common validators for constraints

This commit is contained in:
Albert Shift 2015-06-09 12:48:55 -07:00
parent 46a476a34f
commit 9919fd9e25
14 changed files with 638 additions and 33 deletions

View file

@ -124,6 +124,13 @@
<version>${guava.version}</version>
</dependency>
<!-- Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- TESTS -->
<dependency>

View file

@ -21,6 +21,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import com.noorq.casser.mapping.validator.AlphabetValidator;
import com.noorq.casser.mapping.validator.EmailValidator;
import com.noorq.casser.mapping.validator.LengthValidator;
import com.noorq.casser.mapping.validator.LowerCaseValidator;
import com.noorq.casser.mapping.validator.MaxLengthValidator;
import com.noorq.casser.mapping.validator.MinLengthValidator;
import com.noorq.casser.mapping.validator.NotEmptyValidator;
import com.noorq.casser.mapping.validator.NotNullValidator;
import com.noorq.casser.mapping.validator.NumberValidator;
import com.noorq.casser.mapping.validator.PatternValidator;
import com.noorq.casser.mapping.validator.UpperCaseValidator;
/**
* Constraint annotations are using for data integrity mostly for @java.lang.String types.
* The place of the annotation is the particular method in model interface.
@ -52,6 +66,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = NotNullValidator.class)
public @interface NotNull {
}
@ -61,7 +76,7 @@ public final class Constraints {
*
* Also checks for the null and it is more strict annotation then @NotNull
*
* Can be used only for @java.lang.CharSequence
* Can be used for @java.lang.CharSequence, @ByteBuffer and any array
*
* It does not check on selects and data retrieval operations
*
@ -70,14 +85,15 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = NotEmptyValidator.class)
public @interface NotEmpty {
}
/**
* Email annotation is using to check that value has a valid email before storing it
*
* Can be used only for @java.lang.CharSequence
* Can be used only for @CharSequence
*
* It does not check on selects and data retrieval operations
*
@ -86,6 +102,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = EmailValidator.class)
public @interface Email {
}
@ -102,6 +119,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = NumberValidator.class)
public @interface Number {
}
@ -119,6 +137,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = AlphabetValidator.class)
public @interface Alphabet {
/**
@ -134,7 +153,7 @@ public final class Constraints {
/**
* Length annotation is using to ensure that value has exact length before storing it
*
* Can be used for @java.lang.CharSequence, @ByteBuffer and byte[]
* Can be used for @java.lang.CharSequence, @ByteBuffer and any array
*
* It does not have effect on selects and data retrieval operations
*
@ -143,6 +162,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = LengthValidator.class)
public @interface Length {
int value();
@ -161,6 +181,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = MaxLengthValidator.class)
public @interface MaxLength {
int value();
@ -179,6 +200,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = MinLengthValidator.class)
public @interface MinLength {
int value();
@ -197,6 +219,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = LowerCaseValidator.class)
public @interface LowerCase {
}
@ -213,6 +236,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = UpperCaseValidator.class)
public @interface UpperCase {
}
@ -229,6 +253,7 @@ public final class Constraints {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Constraint(validatedBy = PatternValidator.class)
public @interface Pattern {
/**
@ -239,39 +264,13 @@ public final class Constraints {
String value();
}
/**
* Custom annotation is using special implementation to check value before storing it
*
* Applicable to use in any @java.lang.Object
*
* It does not have effect on selects and data retrieval operations
*
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Custom {
/**
* Defines value that will be passed to the checker
* Regex flags composition
*
* @return value in the string form, can be anything that checker implemented in className understands
* @return Java regex flags
*/
String value();
/**
* Defines class name of the custom implementation of the checker.
* Class must implement special interface for this and be thread-safe and do not relay that it will be a singleton.
*
*
* @return className of the custom implementation
*/
String className();
int flags();
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import java.util.Arrays;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.Alphabet;
public final class AlphabetValidator implements ConstraintValidator<Alphabet, CharSequence> {
char[] alphabet;
@Override
public void initialize(Alphabet constraintAnnotation) {
alphabet = constraintAnnotation.value().toCharArray();
Arrays.sort(alphabet);
}
@Override
public boolean isValid(CharSequence value,
ConstraintValidatorContext context) {
if (value == null) {
return true;
}
final int len = value.length();
for (int i = 0; i != len; ++i) {
char ch = value.charAt(i);
if (Arrays.binarySearch(alphabet, ch) < 0) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import java.net.IDN;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.Email;
public final class EmailValidator implements ConstraintValidator<Email, CharSequence> {
static final String ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~-]";
static final String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)*";
static final String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
static final String PATTERN =
"^" + ATOM + "+(\\." + ATOM + "+)*@"
+ DOMAIN
+ "|"
+ IP_DOMAIN
+ ")$";
private static final Pattern pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE);
@Override
public void initialize(Email constraintAnnotation) {
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
String asciiString = IDN.toASCII( value.toString() );
return pattern.matcher(asciiString).matches();
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.Length;
public final class LengthValidator implements ConstraintValidator<Length, Object>, SizeConstraint {
int length;
@Override
public void initialize(Length constraintAnnotation) {
this.length = constraintAnnotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
int[] size = getSize(value);
if (size == null || size.length == 0) {
return true;
}
return size[0] == length;
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.LowerCase;
public final class LowerCaseValidator implements ConstraintValidator<LowerCase, CharSequence> {
@Override
public void initialize(LowerCase constraintAnnotation) {
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
final int len = value.length();
for (int i = 0; i != len; ++i) {
char c = value.charAt(i);
if (c <= 0x7F) {
if (isUpperCaseLetter(c)) {
return false;
}
}
if (c != Character.toLowerCase(c)) {
return false;
}
}
return true;
}
private static boolean isUpperCaseLetter(char ch) {
return ch >= 'A' && ch <= 'Z';
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.MaxLength;
public final class MaxLengthValidator implements ConstraintValidator<MaxLength, Object>, SizeConstraint {
int maxLength;
@Override
public void initialize(MaxLength constraintAnnotation) {
this.maxLength = constraintAnnotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
int[] size = getSize(value);
if (size == null || size.length == 0) {
return true;
}
return size[0] <= maxLength;
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.MinLength;
public final class MinLengthValidator implements ConstraintValidator<MinLength, Object>, SizeConstraint {
int minLength;
@Override
public void initialize(MinLength constraintAnnotation) {
this.minLength = constraintAnnotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
int[] size = getSize(value);
if (size == null || size.length == 0) {
return true;
}
return size[0] >= minLength;
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints;
import com.noorq.casser.mapping.annotation.Constraints.NotEmpty;
public final class NotEmptyValidator implements ConstraintValidator<Constraints.NotEmpty, Object>, SizeConstraint {
@Override
public void initialize(NotEmpty constraintAnnotation) {
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
int[] size = getSize(value);
if (size == null) {
return false;
}
if (size.length == 0) {
return true;
}
return size[0] > 0;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints;
import com.noorq.casser.mapping.annotation.Constraints.NotNull;
public final class NotNullValidator implements ConstraintValidator<Constraints.NotNull, Object> {
@Override
public void initialize(NotNull constraintAnnotation) {
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return value != null;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.Number;
public class NumberValidator implements ConstraintValidator<Number, CharSequence>{
@Override
public void initialize(Number constraintAnnotation) {
}
@Override
public boolean isValid(CharSequence value,
ConstraintValidatorContext context) {
if (value == null) {
return true;
}
final int len = value.length();
for (int i = 0; i != len; ++i) {
char ch = value.charAt(i);
if (!isNumber(ch)) {
return false;
}
}
return true;
}
private static boolean isNumber(char ch) {
return ch >= '0' && ch <= '9';
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints;
public final class PatternValidator implements ConstraintValidator<Constraints.Pattern, CharSequence> {
private Pattern pattern;
@Override
public void initialize(Constraints.Pattern constraintAnnotation) {
pattern = Pattern.compile(constraintAnnotation.value(), constraintAnnotation.flags());
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return pattern.matcher(value).matches();
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
public interface SizeConstraint {
static final int[] EMPTY = new int[0];
default int[] getSize(Object value) {
if (value == null) {
return null;
}
if (value.getClass().isArray()) {
return new int[] { Array.getLength(value) };
}
if (value instanceof CharSequence) {
CharSequence seq = (CharSequence) value;
return new int[] { seq.length() };
}
if (value instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer) value;
return new int[] { bb.position() };
}
if (value instanceof Collection) {
Collection<?> col = (Collection<?>) value;
return new int[] { col.size() };
}
if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
return new int[] { map.size() };
}
return EMPTY;
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 Noorq, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.noorq.casser.mapping.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.noorq.casser.mapping.annotation.Constraints.UpperCase;
public final class UpperCaseValidator implements ConstraintValidator<UpperCase, CharSequence> {
@Override
public void initialize(UpperCase constraintAnnotation) {
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
final int len = value.length();
for (int i = 0; i != len; ++i) {
char c = value.charAt(i);
if (c <= 0x7F) {
if (isLowerCaseLetter(c)) {
return false;
}
}
if (c != Character.toUpperCase(c)) {
return false;
}
}
return true;
}
private static boolean isLowerCaseLetter(char ch) {
return ch >= 'a' && ch <= 'z';
}
}