StringProperty.java
package net.hostsharing.hsadminng.hs.validation;
import lombok.AccessLevel;
import lombok.Setter;
import net.hostsharing.hsadminng.mapper.Array;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.Arrays.stream;
@Setter
public class StringProperty<P extends StringProperty<P>> extends ValidatableProperty<P, String> {
protected static final String[] KEY_ORDER = Array.join(
ValidatableProperty.KEY_ORDER_HEAD,
Array.of("matchesRegEx", "matchesRegExDescription",
"notMatchesRegEx", "notMatchesRegExDescription",
"minLength", "maxLength",
"provided"),
ValidatableProperty.KEY_ORDER_TAIL,
Array.of("undisclosed"));
private String[] provided;
private Pattern[] matchesRegEx;
private String matchesRegExDescription;
private Pattern[] notMatchesRegEx;
private String notMatchesRegExDescription;
@Setter(AccessLevel.PRIVATE)
private Consumer<String> describedAsConsumer;
private Integer minLength;
private Integer maxLength;
private boolean undisclosed;
protected StringProperty(final String propertyName) {
super(String.class, propertyName, KEY_ORDER);
}
protected StringProperty(final String propertyName, final String[] keyOrder) {
super(String.class, propertyName, keyOrder);
}
public static StringProperty<?> stringProperty(final String propertyName) {
return new StringProperty<>(propertyName);
}
public P minLength(final int minLength) {
this.minLength = minLength;
return self();
}
public Integer minLength() {
return this.minLength;
}
public P maxLength(final int maxLength) {
this.maxLength = maxLength;
return self();
}
public Integer maxLength() {
return this.maxLength;
}
public P matchesRegEx(final String... regExPattern) {
this.matchesRegEx = stream(regExPattern).map(Pattern::compile).toArray(Pattern[]::new);
this.describedAsConsumer = violationMessage -> matchesRegExDescription = violationMessage;
return self();
}
public P notMatchesRegEx(final String... regExPattern) {
this.notMatchesRegEx = stream(regExPattern).map(Pattern::compile).toArray(Pattern[]::new);
this.describedAsConsumer = violationMessage -> notMatchesRegExDescription = violationMessage;
return self();
}
public P describedAs(final String violationMessage) {
describedAsConsumer.accept(violationMessage);
describedAsConsumer = null;
return self();
}
/// predefined values, similar to fixed values in a combobox
public P provided(final String firstProvidedValue, final String... moreProvidedValues) {
this.provided = ArrayUtils.addAll(new String[]{firstProvidedValue}, moreProvidedValues);
return self();
}
public String[] provided() {
return this.provided;
}
/**
* The property value is not disclosed in error messages.
*
* @return this;
*/
public P undisclosed() {
this.undisclosed = true;
return self();
}
@Override
protected void validate(final List<String> result, final String propValue, final PropertiesProvider propProvider) {
super.validate(result, propValue, propProvider);
validateMinLength(result, propValue);
validateMaxLength(result, propValue);
validateMatchesRegEx(result, propValue);
validateNotMatchesRegEx(result, propValue);
}
@Override
protected String display(final String propValue) {
return undisclosed
? "provided value"
: propValue != null
? ("'" + propValue + "'")
: null;
}
@Override
protected String simpleTypeName() {
return "string";
}
private void validateMinLength(final List<String> result, final String propValue) {
if (minLength != null && propValue.length()<minLength) {
result.add(propertyName + "' length is expected to be at min " + minLength + " but length of " + display(propValue) + " is " + propValue.length());
}
}
private void validateMaxLength(final List<String> result, final String propValue) {
if (maxLength != null && propValue.length()>maxLength) {
result.add(propertyName + "' length is expected to be at max " + maxLength + " but length of " + display(propValue) + " is " + propValue.length());
}
}
private void validateMatchesRegEx(final List<String> result, final String propValue) {
if (matchesRegEx != null &&
stream(matchesRegEx).map(p -> p.matcher(propValue)).noneMatch(Matcher::matches)) {
if (matchesRegExDescription != null) {
result.add(propertyName + "' = " + display(propValue) + " " + matchesRegExDescription);
} else if (matchesRegEx.length>1) {
result.add(propertyName + "' is expected to match any of " + Arrays.toString(matchesRegEx) +
" but " + display(propValue) + " does not match any");
} else {
result.add(propertyName + "' is expected to match " + Arrays.toString(matchesRegEx) + " but " + display(
propValue) +
" does not match");
}
}
}
private void validateNotMatchesRegEx(final List<String> result, final String propValue) {
if (notMatchesRegEx != null &&
stream(notMatchesRegEx).map(p -> p.matcher(propValue)).anyMatch(Matcher::matches)) {
if (notMatchesRegExDescription != null) {
result.add(propertyName + "' = " + display(propValue) + " " + notMatchesRegExDescription);
} else if (notMatchesRegEx.length>1) {
result.add(propertyName + "' is expected not to match any of " + Arrays.toString(notMatchesRegEx) +
" but " + display(propValue) + " does match at least one");
} else {
result.add(propertyName + "' is expected not to match " + Arrays.toString(notMatchesRegEx) +
" but " + display(propValue) + " does match");
}
}
}
}