The Bean Validation functionality works for my validation requirements.
I have an idea to implement the isReadOnly functionality is a generic-yet-fit-your-architecture approach by extending the Bean Validation slightly. I involve adding a IsReadonly interface and IsReadonly annotation. It would look like this:
@MyEntityValidator
public class MyEntity {
@Min(value=1, message="Min value is 1")
@Max(value=100, message="Max value is 100")
@IsReadOnly(name="Field1")
public int getField1() {
}
@Min(value=1, message="Min value is 1")
@Max(value=100, message="Max value is 100")
@IsReadOnly(name="Field2")
public int getField2() {
}
}
public class MyEntityValidator implements ConstraintValidator<MyAnnotation, MyEntity>, IsReadOnly {
public void initialize(MyAnnotation annotation) {
}
public boolean isValid(MyEntity entity, ConstraintValidatorContext context) {
if (entity.getField1() > entity.getField2()) {
return false;
}
return true;
}
public boolean isReadOnly(MyEntity entity, String fieldName) {
if("Field1".equals(fieldName)) {
return entity.getField2() > entity.getField1();
}
else if("Field2".equals(fieldName)) {
return String.equals(entity.getField1(), "VALUE");
}
}
}
Your logic in the screens would be changed to not only look for the Readonly metadata attribute but also use the IsReadOnly attribute to determine if a field is enabled or not. I am spit balling here so the idea is not completely flushed out.
As far as handling property changes, I am not sure if you are familiar with XAML and Dependency Properties. I am not sure if there is a similar standard in Java to decouple the Entity and UI in a way that allows the UI to be notified when related properties are changed. Its a fundamental part of LightSwitch that allows the developer to write code to handle the validation/readonly/changed patterns without knowing the actual implementation of a screen.
It could be implemented like:
@MyEntityValidator
public class MyEntity {
@Min(value=1, message="Min value is 1")
@Max(value=100, message="Max value is 100")
@IsReadOnly(name="Field1")
public int getField1() {
}
@Min(value=1, message="Min value is 1")
@Max(value=100, message="Max value is 100")
@IsReadOnly(name="Field2")
public int getField2() {
}
@Changed(name="Field3")
public String getField3() {
}
}
public class MyEntityValidator implements ConstraintValidator<MyAnnotation, MyEntity>, IsReadOnly, Changed {
public void initialize(MyAnnotation annotation) {
}
public boolean isValid(MyEntity entity, ConstraintValidatorContext context) {
if (entity.getField1() > entity.getField2()) {
return false;
}
return true;
}
public boolean isReadOnly(MyEntity entity, String fieldName) {
if("Field3".equals(fieldName)) {
// If Field3 == CUSTOMER then make Field2 read only.
return String.equals(entity.getField3(), "CUSTOMER");
}
}
public void changed(MyEntity entity, String fieldName) {
if("Field3".equals(fieldName)) {
// Field2 will always be 100 if Field1 is a CUSTOMER
if(entity.getField3().equals("CUSTOMER")) {
entity.setField2(100);
}
}
}
}
The changed method would be called when a screen changes the subscribed property. I am not sure how you would implement the Dependency Property functionality to update the UI with changes.
The goals: Code encapsulated in one class and capabilities could be added as required. If the entity is used on multiple screens or in a REST api (there are issues here) then the behavior will be consistent.
This is a naive example of a pattern I have found eliminates a lot of boilerplate code.