generic subclass return values for ValidatableProperty and subclasses

This commit is contained in:
Michael Hoennig 2024-06-27 11:37:51 +02:00
parent e9f48f0a19
commit 593e62fa32
11 changed files with 62 additions and 56 deletions

View File

@ -16,7 +16,7 @@ import static java.util.Optional.ofNullable;
public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingItemEntity> {
public HsBookingItemEntityValidator(final ValidatableProperty<?>... properties) {
public HsBookingItemEntityValidator(final ValidatableProperty<?, ?>... properties) {
super(properties);
}
@ -54,7 +54,7 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte
// TODO.refa: convert into generic shape like multi-options validator
private static String validateMaxTotalValue(
final HsBookingItemEntity bookingItem,
final ValidatableProperty<?> propDef) {
final ValidatableProperty<?, ?> propDef) {
final var propName = propDef.propertyName();
final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse("");
final var totalValue = ofNullable(bookingItem.getSubBookingItems()).orElse(emptyList())

View File

@ -24,7 +24,7 @@ import static java.util.Optional.ofNullable;
public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
static final ValidatableProperty<?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?>[0];
static final ValidatableProperty<?, ?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?, ?>[0];
private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation;
private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation;
@ -36,7 +36,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
@NotNull final ParentAsset parentAssetValidation,
@NotNull final AssignedToAsset assignedToAssetValidation,
@NotNull final AlarmContact alarmContactValidation,
final ValidatableProperty<?>... properties) {
final ValidatableProperty<?, ?>... properties) {
super(properties);
this.bookingItemValidation = bookingItemValidation;
this.parentAssetValidation = parentAssetValidation;
@ -105,7 +105,7 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
// TODO.test: check, if there are any hosting assets which need this validation at all
private String validateMaxTotalValue(
final HsHostingAssetEntity hostingAsset,
final ValidatableProperty<?> propDef) {
final ValidatableProperty<?, ?> propDef) {
final var propName = propDef.propertyName();
final var propUnit = ofNullable(propDef.unit()).map(u -> " " + u).orElse("");
final var totalValue = ofNullable(hostingAsset.getSubHostingAssets()).orElse(emptyList())

View File

@ -30,7 +30,7 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
.withDefault("/bin/false"),
stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir),
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(),
passwordProperty("password").minLength(8).maxLength(40).writeOnly());
passwordProperty("password").minLength(8).maxLength(40).hashedUsing("SHA-512").writeOnly());
}
@Override

View File

@ -9,7 +9,7 @@ import java.util.Map;
import java.util.Objects;
@Setter
public class BooleanProperty extends ValidatableProperty<Boolean> {
public class BooleanProperty extends ValidatableProperty<BooleanProperty, Boolean> {
private static final String[] KEY_ORDER = Array.join(ValidatableProperty.KEY_ORDER_HEAD, ValidatableProperty.KEY_ORDER_TAIL);
@ -23,7 +23,7 @@ public class BooleanProperty extends ValidatableProperty<Boolean> {
return new BooleanProperty(propertyName);
}
public ValidatableProperty<Boolean> falseIf(final String refPropertyName, final String refPropertyValue) {
public BooleanProperty falseIf(final String refPropertyName, final String refPropertyValue) {
this.falseIf = new AbstractMap.SimpleImmutableEntry<>(refPropertyName, refPropertyValue);
return this;
}

View File

@ -9,7 +9,7 @@ import java.util.List;
import static java.util.Arrays.stream;
@Setter
public class EnumerationProperty extends ValidatableProperty<String> {
public class EnumerationProperty extends ValidatableProperty<EnumerationProperty, String> {
private static final String[] KEY_ORDER = Array.join(
ValidatableProperty.KEY_ORDER_HEAD,
@ -26,12 +26,12 @@ public class EnumerationProperty extends ValidatableProperty<String> {
return new EnumerationProperty(propertyName);
}
public ValidatableProperty<String> values(final String... values) {
public EnumerationProperty values(final String... values) {
this.values = values;
return this;
}
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
public void deferredInit(final ValidatableProperty<?, ?>[] allProperties) {
if (hasDeferredInit()) {
if (this.values != null) {
throw new IllegalStateException("property " + this + " already has values");
@ -40,8 +40,8 @@ public class EnumerationProperty extends ValidatableProperty<String> {
}
}
public ValidatableProperty<String> valuesFromProperties(final String propertyNamePrefix) {
this.setDeferredInit( (ValidatableProperty<?>[] allProperties) -> stream(allProperties)
public EnumerationProperty valuesFromProperties(final String propertyNamePrefix) {
this.setDeferredInit( (ValidatableProperty<?, ?>[] allProperties) -> stream(allProperties)
.map(ValidatableProperty::propertyName)
.filter(name -> name.startsWith(propertyNamePrefix))
.map(name -> name.substring(propertyNamePrefix.length()))

View File

@ -14,9 +14,9 @@ import static java.util.Collections.emptyList;
// TODO.refa: rename to HsEntityProcessor, also subclasses
public abstract class HsEntityValidator<E extends PropertiesProvider> {
public final ValidatableProperty<?>[] propertyValidators;
public final ValidatableProperty<?, ?>[] propertyValidators;
public HsEntityValidator(final ValidatableProperty<?>... validators) {
public HsEntityValidator(final ValidatableProperty<?, ?>... validators) {
propertyValidators = validators;
stream(propertyValidators).forEach(p -> p.deferredInit(propertyValidators));
}
@ -68,7 +68,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
.orElse(emptyList()));
}
protected static Integer getIntegerValueWithDefault0(final ValidatableProperty<?> prop, final Map<String, Object> propValues) {
protected static Integer getIntegerValueWithDefault0(final ValidatableProperty<?, ?> prop, final Map<String, Object> propValues) {
final var value = prop.getValue(propValues);
if (value instanceof Integer) {
return (Integer) value;

View File

@ -7,7 +7,7 @@ import org.apache.commons.lang3.Validate;
import java.util.List;
@Setter
public class IntegerProperty extends ValidatableProperty<Integer> {
public class IntegerProperty extends ValidatableProperty<IntegerProperty, Integer> {
private final static String[] KEY_ORDER = Array.join(
ValidatableProperty.KEY_ORDER_HEAD,
@ -30,7 +30,7 @@ public class IntegerProperty extends ValidatableProperty<Integer> {
}
@Override
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
public void deferredInit(final ValidatableProperty<?, ?>[] allProperties) {
Validate.isTrue(min == null || minFrom == null, "min and minFrom are exclusive, but both are given");
Validate.isTrue(max == null || maxFrom == null, "max and maxFrom are exclusive, but both are given");
}

View File

@ -6,7 +6,7 @@ import java.util.List;
import java.util.stream.Stream;
@Setter
public class PasswordProperty extends StringProperty {
public class PasswordProperty extends StringProperty<PasswordProperty> {
private PasswordProperty(final String propertyName) {
super(propertyName);
@ -23,7 +23,9 @@ public class PasswordProperty extends StringProperty {
validatePassword(result, propValue);
}
// TODO.impl: only a SHA512 hash should be stored in the database, not the password itself
public PasswordProperty hashedUsing(final String hashAlgoritm) {
return self();
}
@Override
protected String simpleTypeName() {
@ -60,6 +62,5 @@ public class PasswordProperty extends StringProperty {
if (containsColon) {
result.add(propertyName + "' must not contain colon (':')");
}
}
}

View File

@ -8,7 +8,7 @@ import java.util.regex.Pattern;
@Setter
public class StringProperty extends ValidatableProperty<String> {
public class StringProperty<P extends StringProperty<P>> extends ValidatableProperty<P, String> {
private static final String[] KEY_ORDER = Array.join(
ValidatableProperty.KEY_ORDER_HEAD,
@ -24,23 +24,23 @@ public class StringProperty extends ValidatableProperty<String> {
super(String.class, propertyName, KEY_ORDER);
}
public static StringProperty stringProperty(final String propertyName) {
return new StringProperty(propertyName);
public static StringProperty<?> stringProperty(final String propertyName) {
return new StringProperty<>(propertyName);
}
public StringProperty minLength(final int minLength) {
public P minLength(final int minLength) {
this.minLength = minLength;
return this;
return self();
}
public StringProperty maxLength(final int maxLength) {
public P maxLength(final int maxLength) {
this.maxLength = maxLength;
return this;
return self();
}
public StringProperty matchesRegEx(final String regExPattern) {
public P matchesRegEx(final String regExPattern) {
this.matchesRegEx = Pattern.compile(regExPattern);
return this;
return self();
}
/**
@ -48,9 +48,9 @@ public class StringProperty extends ValidatableProperty<String> {
*
* @return this;
*/
public StringProperty undisclosed() {
public P undisclosed() {
this.undisclosed = true;
return this;
return self();
}
@Override

View File

@ -25,7 +25,7 @@ import static java.util.Optional.ofNullable;
@Getter
@RequiredArgsConstructor
public abstract class ValidatableProperty<T> {
public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T> {
protected static final String[] KEY_ORDER_HEAD = Array.of("propertyName");
protected static final String[] KEY_ORDER_TAIL = Array.of("required", "defaultValue", "readOnly", "writeOnly", "computed", "isTotalsValidator", "thresholdPercentage");
@ -51,7 +51,7 @@ public abstract class ValidatableProperty<T> {
@Accessors(makeFinal = true, chain = true, fluent = false)
private boolean writeOnly;
private Function<ValidatableProperty<?>[], T[]> deferredInit;
private Function<ValidatableProperty<?, ?>[], T[]> deferredInit;
private boolean isTotalsValidator = false;
@JsonIgnore
@ -59,11 +59,16 @@ public abstract class ValidatableProperty<T> {
private Integer thresholdPercentage; // TODO.impl: move to IntegerProperty
public final P self() {
//noinspection unchecked
return (P) this;
}
public String unit() {
return null;
}
protected void setDeferredInit(final Function<ValidatableProperty<?>[], T[]> function) {
protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]> function) {
this.deferredInit = function;
}
@ -71,47 +76,47 @@ public abstract class ValidatableProperty<T> {
return deferredInit != null;
}
public T[] doDeferredInit(final ValidatableProperty<?>[] allProperties) {
public T[] doDeferredInit(final ValidatableProperty<?, ?>[] allProperties) {
return deferredInit.apply(allProperties);
}
public ValidatableProperty<T> writeOnly() {
public P writeOnly() {
this.writeOnly = true;
optional();
return this;
return self();
}
public ValidatableProperty<T> readOnly() {
public P readOnly() {
this.readOnly = true;
optional();
return this;
return self();
}
public ValidatableProperty<T> required() {
public P required() {
required = TRUE;
return this;
return self();
}
public ValidatableProperty<T> optional() {
public ValidatableProperty<P, T> optional() {
required = FALSE;
return this;
}
public ValidatableProperty<T> withDefault(final T value) {
public P withDefault(final T value) {
defaultValue = value;
required = FALSE;
return this;
return self();
}
public void deferredInit(final ValidatableProperty<?>[] allProperties) {
public void deferredInit(final ValidatableProperty<?, ?>[] allProperties) {
}
public ValidatableProperty<T> asTotalLimit() {
public P asTotalLimit() {
isTotalsValidator = true;
return this;
return self();
}
public ValidatableProperty<T> asTotalLimitFor(final String propertyName, final String propertyValue) {
public P asTotalLimitFor(final String propertyName, final String propertyValue) {
if (asTotalLimitValidators == null) {
asTotalLimitValidators = new ArrayList<>();
}
@ -132,7 +137,7 @@ public abstract class ValidatableProperty<T> {
return emptyList();
};
asTotalLimitValidators.add((final HsBookingItemEntity entity) -> validator.apply(entity, (IntegerProperty)this, 1));
return this;
return self();
}
public String propertyName() {
@ -147,7 +152,7 @@ public abstract class ValidatableProperty<T> {
return thresholdPercentage;
}
public ValidatableProperty<T> eachComprising(final int factor, final TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> validator) {
public ValidatableProperty<P, T> eachComprising(final int factor, final TriFunction<HsBookingItemEntity, IntegerProperty, Integer, List<String>> validator) {
if (asTotalLimitValidators == null) {
asTotalLimitValidators = new ArrayList<>();
}
@ -155,9 +160,9 @@ public abstract class ValidatableProperty<T> {
return this;
}
public ValidatableProperty<?> withThreshold(final Integer percentage) {
public P withThreshold(final Integer percentage) {
this.thresholdPercentage = percentage;
return this;
return self();
}
public final List<String> validate(final PropertiesProvider propsProvider) {
@ -250,10 +255,10 @@ public abstract class ValidatableProperty<T> {
.toList();
}
public ValidatableProperty<T> computedBy(final Function<PropertiesProvider, T> compute) {
public P computedBy(final Function<PropertiesProvider, T> compute) {
this.computedBy = compute;
this.computed = true;
return this;
return self();
}
public <E extends PropertiesProvider> T compute(final E entity) {

View File

@ -12,7 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class PasswordPropertyUnitTest {
private final ValidatableProperty<String> passwordProp = passwordProperty("password").minLength(8).maxLength(40).writeOnly();
private final ValidatableProperty<PasswordProperty, String> passwordProp = passwordProperty("password").minLength(8).maxLength(40).writeOnly();
private final List<String> violations = new ArrayList<>();
@ParameterizedTest