How to add validation to TableViewer? - java

I want to add some validation to the different cells in the JFace TableViewer.
What is the best way to do it ?
I tried to use Converter and Validator for example
// define a validator to check that only numbers are entered
IValidator validator = new IValidator() {
#Override
public IStatus validate(Object value) {
if (value instanceof Integer) {
if (value.toString().matches(".*\\d.*")) {
return ValidationStatus.ok();
}
}
return ValidationStatus.error(value.toString() +"is not a number");
}
};
// create UpdateValueStrategy and assign
// to the binding
UpdateValueStrategy strategy = new UpdateValueStrategy();
strategy.setBeforeSetValidator(validator);
Binding bindValue = ctx.bindValue(widgetValue, modelValue, strategy, null);
Does it the right way to do it for cells in TableViewer? (for example check String, int, Dates )

Related

Reuse artifacts from validation

Lets say I create a validator for NewUserRequestBean called #CheckUsernameAvailable.
The validator would perform something simple like
public boolean isValid(NewUserRequestBean request, ConstraintValidationContext context) {
String userName = request.getUserName();
User existingUser = userProviderService.getUser(userName);
if (existingUser != null) {
return false;
}
}
Is there a way to reuse the existingUser object, so as to do something like
// if (existingUser != null)
else if (existingUser.getEmailAddress() == request.getUserEmailAddress()) {
sendObjectToCaller(existingUser);
// or returnObjectToCaller(existingUser);
}
In case you are using Hibernate Validator you can take a look at dynamic payload. Your implementation of validator would look like:
#Override
public boolean isValid(NewUserRequestBean value, ConstraintValidatorContext context) {
// all the code you need
// make sure that you are working with Hibernate Validator contexts before unwrapping
if ( context instanceof HibernateConstraintValidatorContext ) {
context.unwrap( HibernateConstraintValidatorContext.class )
.withDynamicPayload( existingUser );
}
return validationResult;
}
and then you should be able to access this same payload from the constraint violation if one is raised:
Set<ConstraintViolation<NewUserRequestBean>> violations = // results of validation of your NewUserRequestBean...
// just get the violation and unwrap it to HibernateConstraintViolation.
// to stay on the safe side you should apply an instanceof check here as well before unwrapping
HibernateConstraintViolation<NewUserRequestBean> violation = violations.iterator().next()
.unwrap( HibernateConstraintViolation.class );
User existingUser = violation.getDynamicPayload( User.class );
For more info you can check the javadocs of these dynamic payload methods and also please have a look at this section in documentation on dynamic payloads

Inserting integer (not String) data into a JavaFX2 TableView

so I've got a table working properly and grabbing data from an ObservableList with the code here:
public void setMainTableData(ObservableList<FileMP3> list)
{
artistCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("artist"));
albumCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("album"));
titleCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("title"));
trackCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("track"));
yearCol.setCellValueFactory(new PropertyValueFactory<FileMP3, String>("year"));
mainTable.setItems(list);
}
These columns, however do not ALL contain string data - I need to able to insert an int, and potentially other types like Duration. The track and year entries are stored as integers, and there is a (not shown) entry called length. This is stored in my FileMP3 object as a Duration, and I don't see any obvious way to manipulate the data stored there before inserting it into the table. I'd like to be able to use Duration.getMillis() and then perform some math on that to get it into a displayable int format, but I want to keep it stored in the FileMP3 as Duration.
All the tutorials I've read on the topic all use the constructor as such:
new PropertyValueFactory<FileMP3, String>("genre")
All in all, I'd like to be able to insert something other than a String into the table.
You can just replace String with any (reference, not primitive) type. For example:
TableColumn<FileMP3, Integer> yearCol = new TableColumn<>("Year");
yearCol.setCellValueFatory(new PropertyValueFactory<FileMP3, Integer>("year"));
Similarly with Duration (instead of Integer).
By default, the value in the cell will be displayed by calling toString() on the value in the cell. If you want the value to be displayed differently, you can create a custom cell factory (different to a cell value factory):
TableColumn<FileMP3, Integer> durationCol = new TableColumn<>("Duration");
durationCol.setCellValueFactory(new PropertyValueFactory<FileMP3, Duration>("duration"));
durationCol.setCellFactory(new Callback<TableColumn<FileMP3, Duration>, TableCell<FileMP3, Duration>>() {
#Override
public TableCell<FileMP3, Duration> call(TableColumn<FileMP3, Duration> col) {
return new TableCell<FileMP3, Duration>() {
#Override
protected void updateItem(Duration duration, boolean empty) {
super.updateItem(duration, empty);
if (empty) {
setText(null);
} else {
setText(Double.toString(duration.toMillis());
}
}
};
}
});
You can provide a custom cell value factory:
duration.setCellValueFactory(new Callback<CellDataFeatures<FileMP3, Integer>, ObservableValue<Integer>>() {
#Override public ObservableValue<Integer> call(CellDataFeatures<FileMP3, Integer> c) {
return new SimpleIntegerProperty(c.getValue().getDurationAsInt()));
}
});

Set both interpolated and message template on ConstraintValidatorContext

I am in need to call validator.validateProperty() from my custom Validator and looking for a way to pass BOTH messageTemplate and interpolated message to ConstraintValidatorContext.
What i want to achieve is that if given property has a particular value than fire validation of another property.
MyCustomClassLevelValidator implements ConstraintValidator<Foo, Bar>{
#Autowired
private Validator validator
public boolean isValid(Bar bar,
ConstraintValidatorContext constraintValidatorContext){
if(bar.isSth()){
Set<ConstraintViolation<Bar>> somePropViolations = validator.validateProperty(bar, "someprop", Conditional.class);
for (ConstraintViolation<Bar> propertyViolation : somePropViolations) {
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext.buildConstraintViolationWithTemplate(propertyViolation.getMessageTemplate()).addNode(propertyViolation.getPropertyPath().toString())
.addConstraintViolation();
}
}
}
}
So the problem with my code is that when Bar is validated, constraint violations on "someprop" are not fully interpolated (constraint annotations attributes are not resolved)
class Bar{
...
#Digits(groups=Conditional.class, integer=4,fraction=0)
String someProp;
}
So when validating Bar like
Bar bar = new Bar();
bar.setSomeProp("99.9");
Set<ConstraintViolation<Bar>> constraintViolations = validator.validate(bar);
i see numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
instead of
numeric value out of bounds (<4 digits>.<0 digits> expected)
Is there any way i put BOTH message Template and message text (interpolated version) on constraintValidatorContext ?
I don't think it's a good idea to call back to Validator from within a constraint validator implementation, as this might easily cause endless loops when validating the same object again, which hosts the current object.
Depending on how you invoke validation, you could you simply pass the Conditional group to the validation call:
if(bar.isSth()) {
Set<ConstraintViolation<Bar>> constraintViolations =
validator.validate(bar, Conditional.class);
}
else {
Set<ConstraintViolation<Bar>> constraintViolations =
validator.validate(bar);
}

How to handle empty selection in a JFace bound combobox?

I am developing a search dialog in my eclipse-rcp application.
In the search dialog I have a combobox as follows:
comboImp = new CCombo(grpColSpet, SWT.BORDER | SWT.READ_ONLY);
comboImp.setBounds(556, 46, 184, 27);
comboImpViewer = new ComboViewer(comboImp);
comboImpViewer.setContentProvider(new ArrayContentProvider());
comboImpViewer.setInput(ImpContentProvider.getInstance().getImps());
comboImpViewer.setLabelProvider(new LabelProvider() {
#Override
public String getText(Object element) {
return ((Imp)element).getImpName();
}
});
Imp is a database entity, ManyToOne to the main entity which is searched, and ImpContentProvider is the model class which speaks to embedded sqlite database via jpa/hibernate.
This combobox is supposed to contain all instances of Imp, but to also let empty selection; it's value is bound to a service bean as follows:
IObservableValue comboImpSelectionObserveWidget =
ViewersObservables.observeSingleSelection(comboImpViewer);
IObservableValue filterByImpObserveValue =
BeansObservables.observeValue(searchPrep, "imp");
bindingContext.bindValue(comboImpSelectionObserveWidget, filterByImpObserveValue
, null, null);
As soon as the user clicks on the combo, a selection (first element) is made: I can see the call to a selectionlistener i added on the viewer. My question is:
after a selection has been made, how do I let the user change his mind and have an empty selection in the combobox? should I add a "fake" empty instance of Imp to the List returned by the ImpContentProvider? or should I implement an alternative to ArrayContentProvider?
and one additional related question is:
why calling deselectAll() and clearSelection() on the combo does NOT set a null value to the bound bean?
ComboViewer.setSelection(StructuredSelection.EMPTY) will fire selection event and set "imp" to null. Combo widget selection event is only triggered when manually selected from dropdown list i think.
Use Converter
Define empty selection obj, let us say EMPTYEnum ( display empty string in label provider)
You can define UpdateValueStrategy for target-to-model and set IConverter when you bind observables.
In the converter, you can convert EMPTYEnum to null.
IConverter:
fromType: Object.class
toType: Object.class
public Object convert(Object fromObject)
{
if(fromObject instanceof EMPTYEnum)
{
return null;
}
return fromObject;
}
make LabelProvider and handle null value
public String getText(Object element) {
if (element == null) {
return "Choose one";
}
return super.getText(element);
}
insert null value at index 0 and handle empty selection
combo.setInput(yourItems);
combo.insert(null, 0);
combo.getCCombo().select(0);
combo.addPostSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
if (combo.getCCombo().getSelectionIndex() == -1) {
combo.getCCombo().select(0);
}
}
});
bind ComboViewer ...

How do I check for null values in a Wicket Textfield?

I have a Wicket Textfield which contains an Integer value
currentValueTextField = new TextField<IntParameter>("valueText", new PropertyModel<IntParameter>(model, "value"));
I'm attaching a custom validator to this, as follows
currentValueTextField.add(new IntegerValidator());
The validator class is
class IntegerValidator extends AbstractValidator<IntParameter> {
private static final long serialVersionUID = 5899174401360212883L;
public IntegerValidator() {
}
#Override
public void onValidate(IValidatable<IntParameter> validatable) {
ValidationError error = new ValidationError();
if (model.getValue() == null) {
AttributeAppender redOutline = new AttributeAppender("style", new Model<String>("border-style:solid; border-color:#f86b5c; border-width: 3px"), ";");
currentValueTextField.add(redOutline);
currentValueTextField.getParent().getParent().add(redOutline);
validatable.error(error);
}
}
}
However if I type nothing in the textfield, my onValidate() method is not being called.
What is the recommended way to check for null values in this case?
I would also like to do range checking on the value entered.
just call
currentValueTextField.setRequired(true);
to mark the field as required and have Wicket handle null values on it's own. You can easily combine multiple validators per input field.
Any special error handling, like adding red borders or displaying of error messages can be implemented in the onError method of the form or by adding FeedbackBorders to the appropriate fields.
Override validateOnNullValue() that is false by default.
#Override
public boolean validateOnNullValue()
{
return true;
}
This is the description of validateOnNullValue() method:
Indicates whether or not to validate the value if it is null. It is usually desirable to skip validation if the value is null, unless we want to make sure
the value is in fact null (a rare use case). Validators that extend this and
wish to ensure the value is null should override this method and return
true.
currentValueTextField.setRequired(true);
Now you need to customise the error message. So subclass FeedbackPanel.
you can find more information in the following link
Add this class to your form or component
A better (and reusable) way to do this is to override the isEnabled(Component) method of the behavior:
public class HomePage extends WebPage {
private Integer value;
public HomePage() {
add(new FeedbackPanel("feedback"));
add(new Form("form", new CompoundPropertyModel(this))
.add(new TextField("value")
.setRequired(true)
.add(new ErrorDecorationBehavior()))
.add(new Button("submit") {
#Override
public void onSubmit() {
info(value.toString());
}
}));
}
}
class ErrorDecorationBehavior extends AttributeAppender {
public ErrorDecorationBehavior() {
super("style", true, Model.of("border-style:solid; border-color:#f86b5c; border-width: 3px"), ",");
}
#Override
public boolean isEnabled(Component component) {
return super.isEnabled(component) && component.hasErrorMessage();
}
}

Categories