Skip to content

Commit

Permalink
Feature #2 - New support for property identifier lookup and validatio…
Browse files Browse the repository at this point in the history
…n. This allows fields to reference a data object instead of just a property name as a string.
  • Loading branch information
Carl Dea committed Jun 17, 2024
1 parent 9c4fd4c commit f49cef9
Show file tree
Hide file tree
Showing 28 changed files with 1,811 additions and 217 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[Cognitive](https://github.com/carldea/cognitive/wiki)
# Cognitive
A light weight JavaFX (21) forms framework based on the MVVM UI architecture pattern.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
package org.carlfx.cognitive.validator;

import javafx.beans.property.ReadOnlyBooleanProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for boolean properties.
*/
public interface BooleanValidator extends Validator<ReadOnlyBooleanProperty> {
public interface BooleanValidator extends Validator<ReadOnlyBooleanProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
*/

package org.carlfx.cognitive.validator;

import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* A custom validator for any properties.
*/
public interface CustomValidator extends Validator<Void> {
public interface CustomValidator extends Validator<Void, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyDoubleProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for double properties.
*/
public interface DoubleValidator extends Validator<ReadOnlyDoubleProperty> {
public interface DoubleValidator extends Validator<ReadOnlyDoubleProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@


import javafx.beans.property.ReadOnlyFloatProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for float properties.
*/
public interface FloatValidator extends Validator<ReadOnlyFloatProperty> {
public interface FloatValidator extends Validator<ReadOnlyFloatProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyIntegerProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for integer properties.
*/
public interface IntegerValidator extends Validator<ReadOnlyIntegerProperty> {
public interface IntegerValidator extends Validator<ReadOnlyIntegerProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyListProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for List properties.
*/
public interface ListValidator extends Validator<ReadOnlyListProperty> {
public interface ListValidator extends Validator<ReadOnlyListProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyLongProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for long properties.
*/
public interface LongValidator extends Validator<ReadOnlyLongProperty> {
public interface LongValidator extends Validator<ReadOnlyLongProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyObjectProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for object properties.
*/
public interface ObjectValidator extends Validator<ReadOnlyObjectProperty> {
public interface ObjectValidator extends Validator<ReadOnlyObjectProperty, ViewModel> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@


import javafx.beans.property.ReadOnlySetProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for a set as a property.
*/
public interface SetValidator extends Validator<ReadOnlySetProperty> {
public interface SetValidator extends Validator<ReadOnlySetProperty, ViewModel> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@


import javafx.beans.property.ReadOnlyStringProperty;
import org.carlfx.cognitive.viewmodel.ViewModel;

/**
* Validator for String properties.
*/
public interface StringValidator extends Validator<ReadOnlyStringProperty> {
public interface StringValidator extends Validator<ReadOnlyStringProperty, ViewModel> {
}
170 changes: 170 additions & 0 deletions src/main/java/org/carlfx/cognitive/validator/ValidationManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package org.carlfx.cognitive.validator;

import javafx.beans.property.Property;
import org.carlfx.cognitive.viewmodel.ViewModel;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class ValidationManager {
private final Map<String, String> friendlyNameMap = new LinkedHashMap<>();
private Map<String, List<Validator<?, ViewModel>>> validatorsMap = new LinkedHashMap<>();

private List<ValidationMessage> validationMessages = new ArrayList<>();

/**
* Allows derived classes to create field (property) validators.
* @param name Property name
* @param friendlyName Friendly name of field
* @param validator Validator based on datatype
* @return ValidationViewModel itself.
*/
public Validator<?, ViewModel> createFieldValidator(String name, String friendlyName, Validator<?, ViewModel> validator) {
String alreadyName = friendlyNameMap.get(name);
if ((alreadyName != null && !alreadyName.isBlank() && (friendlyName == null || friendlyName.isBlank()))) {
// ignore the caller's friendly name as null or empty
} else {
friendlyNameMap.putIfAbsent(name, friendlyName);
}
validatorsMap.putIfAbsent(name, new ArrayList());
List<Validator<?, ViewModel>> validators = validatorsMap.get(name);
validators.add(validator);
return validator;
}

/**
* Allows derived classes to create global type validators. e.g. saving to database a duplicate record.
* @param errorCode an error code assigned to error message.
* @param name Property name
* @param errorMsg A string of the validation message
* @param validator Validator based on datatype
* @return ValidationViewModel itself.
* @param <T> Any class derived from a Validator
*/
public <T extends Validator> Validator createGlobalValidator(int errorCode, String name, String errorMsg, T validator) {
validatorsMap.putIfAbsent(name, new ArrayList());
List validators = validatorsMap.get(name);
validators.add(validator);
return validator;
}

/**
* Returns all validators based on property name
* @param name property name
* @return List of validators
*/
public List<Validator<?, ViewModel>> getValidators(String name) {
return validatorsMap.get(name);
}
/**
* Invalidate or clear error messages.
* @return itself
*/
public void invalidate() {
getValidationMessages().clear();
}
public List<ValidationMessage> validate(ViewModel viewModel) {
validationMessages.clear();
// with each property contain a validator eval and build validation messages.
validatorsMap.forEach( (name, validators) -> {
// each property evaluate validators
validationMessages.addAll(validators.stream().map(validator -> {
// call each validator with property and this vm
// 1. property?
// 2. list?
// 3. set?
// 4. custom global name
ValidationMessage validationMessage = switch (validator) {
case BooleanValidator boolValidator -> boolValidator.apply(viewModel.getProperty(name), viewModel);
case CustomValidator customValidator -> customValidator.apply(viewModel.getProperty(name), viewModel);
case DoubleValidator doubleValidator -> doubleValidator.apply(viewModel.getProperty(name), viewModel);
case FloatValidator floatValidator -> floatValidator.apply(viewModel.getProperty(name), viewModel);
case IntegerValidator intValidator -> intValidator.apply(viewModel.getProperty(name), viewModel);
case ListValidator listValidator -> listValidator.apply(viewModel.getProperty(name), viewModel);
case LongValidator longValidator -> longValidator.apply(viewModel.getProperty(name), viewModel);
case ObjectValidator objValidator -> objValidator.apply(viewModel.getProperty(name), viewModel);
case SetValidator setValidator -> setValidator.apply(viewModel.getProperty(name), viewModel);
case StringValidator stringValidator -> stringValidator.apply(viewModel.getProperty(name), viewModel);
default -> throw new IllegalStateException("Unexpected value: " + validator);
};
return validationMessage;
}).filter( vMsg -> vMsg != null).toList());
});
return validationMessages;
}
/**
* Returns all validation messages.
* @return A list of validation messages.
*/
public List<ValidationMessage> getValidationMessages() {
return validationMessages;
}
/**
* True is returned if there are error messages otherwise false.
* @return True is returned if there are error messages otherwise false.
*/
public boolean hasErrorMsgs() {
return hasMsgType(MessageType.ERROR);
}

/**
* True is returned if there are no error messages otherwise false.
* @return True is returned if there are no error messages otherwise false.
*/
public boolean hasNoErrorMsgs() {
return !hasErrorMsgs();
}

/**
* True is returned if there are warning messages otherwise false.
* @return True is returned if there are warning messages otherwise false.
*/
public boolean hasWarningMsgs() {
return hasMsgType(MessageType.WARN);
}

/**
* True is returned if there are no warning messages otherwise false.
* @return True is returned if there are no warning messages otherwise false.
*/
public boolean hasNoWarningMsgs() {
return !hasWarningMsgs();
}

/**
* True is returned if there are info messages otherwise false.
* @return True is returned if there are info messages otherwise false.
*/
public boolean hasInfoMsgs() {
return hasMsgType(MessageType.INFO);
}

/**
* True is returned if there are no info messages otherwise false.
* @return True is returned if there are no info messages otherwise false.
*/
public boolean hasNoInfoMsgs() {
return !hasInfoMsgs();
}

public boolean hasMsgType(MessageType type){
if (getValidationMessages() != null) {
return getValidationMessages().stream().filter(msg -> msg.messageType() == type).findAny().isPresent();
}
return false;
}
/**
* Returns friendly name of property name.
* @param propertName property name
* @return friendly name
*/
public String getFriendlyName(String propertName) {
return friendlyNameMap.get(propertName);
}

public Map<String, String> getFriendlyNameMap() {
return friendlyNameMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@



import org.carlfx.cognitive.viewmodel.ValidationViewModel;
import org.carlfx.cognitive.viewmodel.Validatable;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -76,14 +76,14 @@ public ValidationMessage(String propertyName, MessageType messageType, String me
/**
* Used in templates named substitution. Regex to find properties used in message such as ${myProp1}
*/
static Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{([a-zA-Z\\d\\_]+)\\}");
static Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{(.+)\\}");

/**
* Interpolates a string containing property values to substitute.
* @param vvmodel A validation view model
* @return A interpolated string
*/
public String interpolate(ValidationViewModel vvmodel) {
public String interpolate(Validatable vvmodel) {
Matcher matcher = PROPERTY_PATTERN.matcher(this.message);
List<String> props = new ArrayList<>();
String newMessage = this.message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
* created to allow a UI developer to display as feedback to the User.
* @param <T> Type T is a JavaFX Read-only Property class.
*/
public interface Validator<T> extends BiFunction<T, ViewModel, ValidationMessage> {
public interface Validator<T, U extends ViewModel> extends BiFunction<T, U, ValidationMessage> {
}
Loading

0 comments on commit f49cef9

Please sign in to comment.