JavaFX TableView with simple xml model - javafx

For configuration I use simple xml. I also use this model for TableView. My problem is using of boolean. TableView needs BooleanProperty but simple xml cannot access to this object, obviously. How can I combine this without write big code?
Model
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private BooleanProperty active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
this.active = new SimpleBooleanProperty(active);
}
public boolean isActive() {
return active.getValue();
}
public void setActive(boolean active) {
this.active.set(active);
}
CellFactory
modulActiveColumn.setCellValueFactory(new PropertyValueFactory<>("active"));
modulActiveColumn.setCellFactory(CheckBoxTableCell.forTableColumn(modulActiveColumn));
modulActiveColumn.setOnEditCommit((EventHandler<CellEditEvent>) t -> {
((ScriptData) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setActive((boolean) t.getNewValue());
}

My problem is using of boolean. TableView needs BooleanProperty
You're wrong. In fact the TableView never gains access to the BooleanProperty object stored in the active field of it's items.
PropertyValueFactory uses reflection to
Access a property object by invoking a method with the constructor parameter concatenated with "Property". (This method would be called activeProperty() in your case).
If the above doesn't work it wraps the value returned by a the getter for the property in a ObservableValue. (The name of the getter in this case is getActive() or isActive).
In your case the cellValueFactory does something similar to the following factory
modulActiveColumn.setCellValueFactory(cellData -> new SimpleBooleanProperty(cellData.getValue().isActive()));
Using a boolean field to store the data achieves exactly the same result in your case. The drawback of this approach is that programatic updates of the property do not trigger an update of the TableView and the edits need to be handled manually.
#Root(name="scriptdata")
#Order(elements={"title", "active"})
public class ScriptData {
#Element (required=true)
private String title;
#Element (required=false)
private boolean active;
/**
*
* #param title
* #param active
*/
public ScriptData() {
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}

Related

Current datetime shouldnt pass the validation using #Past annotation

I need the #Past to error when the field is set to now. I realize that the now value on the field, and the now value used when the validator is comparing would be slightly different, thus the need to set the tolerance in hibernate validator.
Problem is that i can not get this to work. Here is the junit:
#Test
public void testHibernateValidator_withPast_withTodayDate() {
// populates with 'now'
MyFormWithPast form = new MyFormWithPast();
form.setDt(OffsetDateTime.now(Clock.systemUTC()));
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.clockProvider(() -> Clock.systemUTC())
// adds tolerance so that when comparing, the form dt and 'now' is considered equal,
// therefore dt is not a past datetime
.temporalValidationTolerance(Duration.ofMinutes(1))
.buildValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<MyFormWithPast>> errors = validator.validate(form);
// needs to fail, since 'now' shouldn't be considered 'past'
assertFalse("now shoudnt be considered as Past", errors.isEmpty());
}
public static class MyFormWithPast {
#Past
private OffsetDateTime dt;
public void setDt(OffsetDateTime dt) {
this.dt = dt;
}
public OffsetDateTime getDt() {
return dt;
}
}
I expect the validation to fail when i put in 'now' in the field, as 'now' shouldnt be considered as 'past'. What did i miss ?
The temporal validation tolerance was designed to be more lenient, not stricter. You want it to be stricter.
I think you will need your own constraints to deal with what you want to do.
Just want to share my current solution, adding a default of 1 minute forward tolerance so that inputted 'now' is not considered a 'past'.
The annotation:
/**
* Validates that the date is of the past, with forward tolerance of 1 minute,
* to offset the time to create a 'now' instance to compare to.
* The usage is when user selects 'today' in the UI, we dont want it to be considered as 'Past'
* https://stackoverflow.com/questions/60341963/current-datetime-shouldnt-pass-the-validation-using-past-annotation
* Annotation is applicable to {#link OffsetDateTime}.
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy=StrictPastValidator.class)
public #interface StrictPast {
public static final String MESSAGE = "{constraints.StrictPast.message}";
/**
* #return The error message template.
*/
String message() default MESSAGE;
/**
* #return The groups the constraint belongs to.
*/
Class<?>[] groups() default { };
/**
* #return The payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
}
The validator:
public class StrictPastValidator implements ConstraintValidator<StrictPast, Object> {
#Override
public void initialize(StrictPast annotation) {
}
#Override
public boolean isValid(Object input, ConstraintValidatorContext ignored) {
if (input == null) {
return true;
} else if (input instanceof OffsetDateTime) {
return isValidOffsetDateTime((OffsetDateTime) input);
}
throw new IllegalStateException("StrictPastValidator is not applicable to the field type " + input.getClass().getName());
}
private boolean isValidOffsetDateTime(OffsetDateTime input) {
OffsetDateTime plusSecondsDt = input.plusSeconds(Duration.ofMinutes(1).getSeconds());
return plusSecondsDt.isBefore(OffsetDateTime.now(Clock.systemUTC()));
}
}

Corda node driver does not pick up custom serializer

I'm writing a cordapp where I need to use state classes that require a custom serializer. I have got one defined according to the documentation, but it does not seem to be picked up when I'm trying to run contract tests via corda-node-driver (exactly as per corda-helloworld template).
Here is the class definition (suitably obfuscated):
public class BlahBlahSerializer implements SerializationCustomSerializer<BlahBlah, BlahBlahProxy> {
public IOUState fromProxy(BlahBlahProxy proxy) {
/* impl */
}
public BlahBlahProxy toProxy(BlahBlah state) {
/* impl */
}
}
}
The .class file for the above is in the cordap jar created by the driver, and so are the BlahBlahProxy.class, contract and state classes. All these classes are is in the same package as the state and contract classes themselves.
I have enabled DEBUG logging for corda and I can see a lot of lines like this one
[Test worker] CachingCustomSerializerRegistry - action="Using custom serializer", class=java.security.PublicKey, declaredType=java.security.PublicKey
but there isn't a line for my class.
I wonder if there is anything else I need to do to enable custom serializer to be picked up?
I think #fowlerr is relatively correct may have been an issue with this in the past.
Take a look at the latest version of Corda (4.5) and see if you can get a custom serializer work there. Hopefully, you won't have issues with something like this :
/**
* The class lacks a public constructor that takes parameters it can associate
* with its properties and is thus not serializable by the CORDA serialization
* framework.
*/
class Example {
private int a;
private int b;
public int getA() { return a; }
public int getB() { return b; }
public Example(List<int> l) {
this.a = l.get(0);
this.b = l.get(1);
}
}
/**
* This is the class that will Proxy instances of Example within the serializer
*/
public class ExampleProxy {
/**
* These properties will be serialized into the byte stream, this is where we choose how to
* represent instances of the object we're proxying. In this example, which is somewhat
* contrived, this choice is obvious. In your own classes / 3rd party libraries, however, this
* may require more thought.
*/
private int proxiedA;
private int proxiedB;
/**
* The proxy class itself must be serializable by the framework, it must thus have a constructor that
* can be mapped to the properties of the class via getter methods.
*/
public int getProxiedA() { return proxiedA; }
public int getProxiedB() { return proxiedB; }
public ExampleProxy(int proxiedA, int proxiedB) {
this.proxiedA = proxiedA;
this.proxiedB = proxiedB;
}
}
/**
* Finally this is the custom serializer that will automatically loaded into the serialization
* framework when the CorDapp Jar is scanned at runtime.
*/
public class ExampleSerializer implements SerializationCustomSerializer<Example, ExampleProxy> {
/**
* Given an instance of the Example class, create an instance of the proxying object ExampleProxy.
*
* Essentially convert Example -> ExampleProxy
*/
public ExampleProxy toProxy(Example obj) {
return new ExampleProxy(obj.getA(), obj.getB());
}
/**
* Conversely, given an instance of the proxy object, revert that back to an instance of the
* type being proxied.
*
* Essentially convert ExampleProxy -> Example
*/
public Example fromProxy(ExampleProxy proxy) {
List<int> l = new ArrayList<int>(2);
l.add(proxy.getProxiedA());
l.add(proxy.getProxiedB());
return new Example(l);
}
}
source from docs: https://docs.corda.net/docs/corda-os/4.4/cordapp-custom-serializers.html#writing-a-custom-serializer

When observablelist generate update change event?

I tried different collections under different conditions but all changes that I was able to receive were Permutation, Add, Removed and Replace changes.
In what conditions does update change emerge? What base class, what stored class and what operations is needed to produce such event?
To generate an update event, you must create an ObservableList with an extractor.
The extractor is a function mapping each element in the list to an array of Observables. If any of those Observables change (while the element is still in the list), then the list will receive an update event.
For example, given a Person class:
public class Person {
private final StringProperty name = new SimpleStringProperty();
public Person(String name) {
nameProperty().set(name);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
}
if you create an observable list as
ObservableList<Person> people = FXCollections.observableArrayList(person ->
new Observable[] {person.nameProperty()} );
and register a listener
people.addListener((Change<? extends Person> change) -> {
while (change.next()) {
if (change.wasAdded()) {
System.out.println("Add");
}
if (change.wasUpdated()) {
System.out.println("Update");
}
}
});
Then the following will show an update event:
Person person = new Person("Jacob Smith");
people.add(person);
person.setName("Isabella Johnson");

Spring MVC, Binding, multi-select options following validation

Thanks in advance for any help.
I have a form that is being validated with JSR 303. After validation fails, the controller returns the form, shows validation errors, and renders the form with the original values. This works fine with all types of form elements except the mutli-select element.
The command object:
public class TaskOrder implements Serializable {
private static final long serialVersionUID = 1L;
...
#XmlTransient
#ManyToMany
#<OtherJPAAnnotations...>
private List<Contractor> subcontractors;
...
}
Contractor class:
public class Contractor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#<OtherJPAAnnotations...>
private Integer id;
#<OtherJPAAnnotations...>
private String name;
}
Controller:
#RequestMapping(value="processingPath", method=RequestMethod.POST)
public String createNewTaskOrder(#Valid #ModelAttribute TaskOrder taskOrder,
BindingResult result,
Model model) {
...
if (!result.hasErrors()) {
//No binding errors, lots of processing...
else {
model.addAllAttributes(result.getModel());
model.addAttribute(taskOrder);
model.addAttribute("subs", myDAOInstance.getSubs());
return this.setupNewTaskOrder(model);
}
}
#RequestMapping("getFormPath")
public String setupNewTaskOrder(Model model) {
if (!model.containsAttribute("taskOrder")) {
TaskOrder taskOrder = new TaskOrder();
taskOrder.setId(0);
model.addAttribute(taskOrder);
}
return "_n/admin/taskOrder/new";
}
The form:
<form:form commandName="taskOrder" action="processPath">
...
<form:select path="subcontractors">
<form:options items="${subs}" itemValue="id" itemLabel="name"/>
</form:select>
...
</form>
When I open an existing "TaskOrder" with the same form, the values are selected in the "subcontractors" multi-select.
However, trying to add a new "TaskOrder", when it returns from validation the values aren't selected. I know that the selected values are being attached to the model and returned, but just not being selected in the select element.
Thanks for any help!
Thanks for the help. Biju...you were correct!
For those who may stumble across this issue, I added the "EqualsUtil" class described here to my project:
http://www.javapractices.com/topic/TopicAction.do?Id=17
Then added the following method to my Contractor class:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Contractor)) return false;
Contractor c = (Contractor) o;
//Here I ignore the other properties since "id" and "name" are what
//I'm primarily concerned with...
return EqualsUtil.areEqual(this.name, c.name) &&
EqualsUtil.areEqual(this.id, c.id);
}

Grails bind request parameters to enum

My Grails application has a large number of enums that look like this:
public enum Rating {
BEST("be"), GOOD("go"), AVERAGE("av"), BAD("ba"), WORST("wo")
final String id
private RateType(String id) {
this.id = id
}
static public RateType getEnumFromId(String value) {
values().find {it.id == value }
}
}
If I have a command object such as this:
class MyCommand {
Rating rating
}
I would like to (for example) automatically convert a request parameter with value "wo" to Rating.WORST.
The procedure for defining custom converters is described here (in the context of converting Strings to Dates). Although this procedure works fine, I don't want to have to create a class implementing PropertyEditorSupport for each of my enums. Is there a better alternative?
I found a solution I'm pretty happy with.
Step 1: Create an implementation of PropertyEditorSupport to convert text to/from the relevant Enum
public class EnumEditor extends PropertyEditorSupport {
private Class<? extends Enum<?>> clazz
public EnumEditor(Class<? extends Enum<?>> clazz) {
this.clazz = clazz
}
public String getAsText() {
return value?.id
}
public void setAsText(String text) {
value = clazz.getEnumFromId(text)
}
}
Step 2: Define a class that registers EnumEditor as a converter for the various enum classes. To change the list of enum classes that are bindable by id, just modify BINDABLE_ENUMS
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
private static final String REQUIRED_METHOD_NAME = 'getEnumFromId'
// Add any enums that you want to bind to by ID into this list
private static final BINDABLE_ENUMS = [Rating, SomeOtherEnum, SomeOtherEnum2]
public void registerCustomEditors(PropertyEditorRegistry registry) {
BINDABLE_ENUMS.each {enumClass ->
registerEnum(registry, enumClass)
}
}
/**
* Register an enum to be bound by ID from a request parameter
* #param registry Registry of types eligible for data binding
* #param enumClass Class of the enum
*/
private registerEnum(PropertyEditorRegistry registry, Class<? extends Enum<?>> enumClass) {
boolean hasRequiredMethod = enumClass.metaClass.methods.any {MetaMethod method ->
method.isStatic() && method.name == REQUIRED_METHOD_NAME && method.parameterTypes.size() == 1
}
if (!hasRequiredMethod) {
throw new MissingMethodException(REQUIRED_METHOD_NAME, enumClass, [String].toArray())
}
registry.registerCustomEditor(enumClass, new EnumEditor(enumClass))
}
}
Step 3: Make Spring aware of the registry above by defining the following Spring bean in grails-app/conf/spring/resources.grooovy
customPropertyEditorRegistrar(CustomPropertyEditorRegistrar)
So the default Databinding binds on the Enum name and not a separately defined property of the Enum. You can either create your own PropertyEditor as you have mentioned or do a work-around similar to this:
class MyCommand {
String ratingId
Rating getRating() {
return Rating.getEnumFromId(this.ratingId)
}
static constraints = {
ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
}
}

Resources