I have a TableView that is sourced from an attribute in a legacy Java Bean of type java.util.Date. I wish to customize the formatting of the date String to HH:mm:ss
I'm looking for is a native JavaFX utility to create an ObservableValue wrapper taking a java.util.DateFormat or javafx.util.StringConverter
I've found Bindings.format() class which could be used to wrap the ObservableValue, however this only allows printf format patterns like %04d etc, not any custom date specific formatting.
The best I've come up with is to use Bindings.bindBidirectional(property, property, format) with a dummy StringProperty which the CellFactory returns. Can this be simplified? Could this cause a memory leak?
public class OldBeanTableView extends Application {
public class OldBean {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public static final String PROPERTY_NAME_FOO = "foo";
private Date foo = new Date();
public Date getFoo() {
return foo;
}
public void setFoo(Date foo) {
Date oldValue = this.foo;
this.foo = foo;
pcs.firePropertyChange(PROPERTY_NAME_FOO, oldValue, foo);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
private class LegacyValueFactory<T, F> implements Callback<CellDataFeatures<T, String>, ObservableValue<String>> {
private String propertyName;
private Format format;
public LegacyValueFactory(String propertyName, Format format) {
this.propertyName = propertyName;
this.format = format;
}
#Override
public ObservableValue<String> call(CellDataFeatures<T, String> param) {
try {
Property<String> formattedString = new SimpleStringProperty();
Property<F> original = JavaBeanObjectPropertyBuilder.create().name(propertyName).bean(param.getValue()).build();
Bindings.bindBidirectional(formattedString, original, format);
return formattedString;
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<OldBean> beans = FXCollections.observableArrayList();
beans.add(new OldBean());
TableView<OldBean> tableView = new TableView<>();
TableColumn<OldBean, String> column = new TableColumn<OldBeanTableView.OldBean, String>();
tableView.getColumns().add(column);
column.setCellValueFactory(new LegacyValueFactory<OldBean, String>("foo", new SimpleDateFormat("HH:mm:ss")));
tableView.setItems(beans);
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
() -> beans.get(0).setFoo(new Date(beans.get(0).getFoo().getTime() + 1000)), 0, 1, TimeUnit.SECONDS);
}
public static void main(String[] args) {
launch(args);
}
}
Based on James_D suggestion this can be done using a CellFactory instead of a CellValueFactory...
public class FormattedTableCell<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private Format format;
public FormattedTableCell(Format format) {
super();
this.format = format;
}
#Override
public TableCell<S, T> call(TableColumn<S, T> param) {
return new TableCell<S, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(format.format(item));
}
}
};
}
}
Usage
column.setCellFactory(new FormattedTableCell<OldBean, Date>(new SimpleDateFormat("HH:mm:ss")));
Related
I can use an extractor (Callback<E, Observable[]> extractor) to make a ListProperty fire change events if one of its elements changed one of its properties (update event).
Update Change Event in ObservableList
Is there an equivalent for ObjectProperty<>? I have an SimpleObjectProperty which I want to fire events when properties of it's value (another bean type) change (update change events).
Sample code:
public class TestBean {
public static <T extends TestBean> Callback<T, Observable[]> extractor() {
return (final T o) -> new Observable[] { o.testPropertyProperty() };
}
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() {
return this.testProperty;
}
public final String getTestProperty() {
return this.testPropertyProperty().get();
}
public final void setTestProperty(final String testProperty) {
this.testPropertyProperty().set(testProperty);
}
}
public class SomeType {
/**
* How can I listen for changes of TestBean#testProperty?
*/
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
}
I want to receive change events if the value of SomeType#property changes, but also, if SomeType#property#testProperty changes.
I cannot just listen for SomeType#property#testProperty, since I would not be notified when SomeType#property was changed (I would then listen on the wrong object for changes).
I want to receive change events if value of SomeType#property changes, but also, if SomeType#property#testProperty changes.
I cannot just listen for SomeType#property#testProperty, since I would not be notified, when SomeType#property was changed (I would then listen on the wrong object for changes).
This is a limitation of sorts of the current iteration of JavaFX. The built-in way is unreliable and you're better off using 3rd party libraries. See this answer for more information.
For you case, ReactFX can be utilized in a similar way:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.reactfx.value.Val;
import org.reactfx.value.Var;
class TestBean {
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() { return testProperty; }
public final String getTestProperty() { return testProperty.get(); }
public final void setTestProperty(String newTestProperty) { testProperty.set(newTestProperty); }
}
public class SomeType {
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
public final ObjectProperty<TestBean> propertyProperty() { return property; }
public final TestBean getProperty() { return property.get(); }
public final void setProperty(TestBean newProperty) { property.set(newProperty); }
public static void main(String[] args) {
SomeType someType = new SomeType();
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
chainedTestProperty.addListener((obs, oldVal, newVal) -> System.out.println(obs + " " + oldVal + "->" + newVal));
//Tests
someType.setProperty(new TestBean());
someType.getProperty().setTestProperty("s1");
TestBean bean2 = new TestBean();
bean2.setTestProperty("s2");
someType.setProperty(bean2);
someType.setProperty(new TestBean());
}
}
Output:
org.reactfx.value.FlatMappedVar#7aec35a null->s1
org.reactfx.value.FlatMappedVar#7aec35a s1->s2
org.reactfx.value.FlatMappedVar#7aec35a s2->null
The key line
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
is a sort of listener chaining. The first argument is a property (OvservableValue) of some type Type. The second argument is the "sub"-property of some other type Type2 inside Type, which is given as a function from Type to that property.
Now whenever any "links" in the chain change, you are notified. You can continue to listen to changes in sub-sub-... properties by continuously chaining ovservables this way.
I came up with the following:
public class ObservableValueProperty<T> extends SimpleObjectProperty<T> {
private InvalidationListener listener = null;
private final Callback<T, Observable[]> extractor;
public ObservableValueProperty() {
this(null);
}
public ObservableValueProperty(final Callback<T, Observable[]> extractor) {
this.extractor = extractor;
}
#Override
protected void fireValueChangedEvent() {
super.fireValueChangedEvent();
}
#Override
public void setValue(final T v) {
if (extractor != null) {
final T oldValue = super.get();
if (oldValue != null) {
for (final Observable o : extractor.call(oldValue)) {
o.removeListener(listener);
}
}
listener = o -> fireValueChangedEvent();
for (final Observable o : extractor.call(v)) {
o.addListener(listener);
}
}
super.setValue(v);
}
}
public class ObservableValuePropertyTest4 implements ChangeListener<Object> {
#BeforeClass
public static void setUpBeforeClass() throws Exception {
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
}
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
static class NestedBean {
StringProperty nestedProperty = new SimpleStringProperty("hans");
public static <T extends NestedBean> Callback<T, Observable[]> extractor() {
return (final T o) -> new Observable[] { o.nestedProperty };
}
#Override
public boolean equals(final Object obj) {
if (obj instanceof NestedBean) {
System.err.println(this.nestedProperty.get() + " " + ((NestedBean) obj).nestedProperty.get());
return Objects.equal(this.nestedProperty.get(), ((NestedBean) obj).nestedProperty.get());
}
return false;
}
}
private ObservableValueProperty<NestedBean> p;
private NestedBean nestedBean;
private String newNestedValue = null;
#Test
public void test01() {
p = new ObservableValueProperty<>(NestedBean.extractor());
nestedBean = new NestedBean();
p.setValue(nestedBean);
p.addListener(this);
nestedBean.nestedProperty.set("peter");
assertEquals("peter", newNestedValue);
}
#Override
public void changed(final ObservableValue<? extends Object> observable, final Object oldValue,
final Object newValue) {
System.err.println("Changed");
newNestedValue = nestedBean.nestedProperty.get();
}
}
Unfortunately, this does not fire any change events because of ExpressionHelper$SingleChange:
#Override
protected void fireValueChangedEvent() {
final T oldValue = currentValue;
currentValue = observable.getValue();
final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
if (changed) {
try {
listener.changed(observable, oldValue, currentValue);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
}
This checks for equality and only if not equal, notifies all listeners. When I trigger fireValueChangedEvent() the value has already changed, and new- and old values are equal, therefore no notification to listeners.
I had the same problem last week, and after many tries, I found a solution that seems to work as expected:
I created a new class called ObjectXProperty<E>, that has the same interface of an ObjectProperty<E>;
It has constructors that can accept a Callback<E,Observable[]>, our extractor function;
Inside the ObjectXProperty, I use a SimpleObjectProperty that deleguates all methods;
The magic trick lies in the set(E value) methods : I create an ObjectBinding that simply send back the value, but it uses the extractor function to decide when it's become invalidated!
This trick will not be applied if the bind method was used previously on the ObjectXProperty, to let the "real" binding do his job; it will work again if the unbind method is called;
Here's my new class ObjectXProperty<E> :
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.util.Callback;
/**
*
* #author Claude Bouchard - 2017
*/
public class ObjectXProperty<E> extends ObjectProperty<E> {
SimpleObjectProperty<E> p;
Callback<E, Observable[]> extractor;
boolean externalBound = false;
public ObjectXProperty(Callback<E, Observable[]> extractor) {
this.extractor = extractor;
}
public ObjectXProperty(E init, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty();
this.extractor = extractor;
set(init);
}
public ObjectXProperty(Object bean, String name, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty(bean, name);
this.extractor = extractor;
}
public ObjectXProperty(Object bean, String name, E init, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty(bean, name);
this.extractor = extractor;
set(init);
}
#Override
public void set(E value) {
if (!externalBound) {
if (value != null) {
p.bind(Bindings.createObjectBinding(() -> {
return value;
}, extractor.call(value)));
} else {
p.bind(Bindings.createObjectBinding(() -> {
return value;
}, new Observable[]{}));
}
} else {
p.set(value); //As expected, it will throw a java.lang.RuntimeException
}
}
#Override
public E get() {
return p.get();
}
#Override
public void addListener(ChangeListener<? super E> listener) {
p.addListener(listener);
}
#Override
public void removeListener(ChangeListener<? super E> listener) {
p.removeListener(listener);
}
#Override
public void addListener(InvalidationListener listener) {
p.addListener(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
p.removeListener(listener);
}
#Override
public Object getBean() {
return p.getBean();
}
#Override
public String getName() {
return p.getName();
}
#Override
public void bind(ObservableValue<? extends E> observable) {
p.bind(observable);
externalBound = true;
}
#Override
public void unbind() {
p.unbind();
externalBound = false;
set(get()); //to reactivate the extractor on the last value
}
#Override
public boolean isBound() {
return externalBound;
}
}
I think you need to add a listener to your object. This can be done simply. First of all you should write your class with a constructor and with getters this way:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class SomeType {
public ObjectProperty<TestProperty> property;
public SomeType(TestProperty testProperty) {
this.property = new SimpleObjectProperty<>(testProperty);
}
public TestProperty getProperty() {
return property.get();
}
public ObjectProperty<TestProperty> propertyProperty() {
return property;
}
}
Then anywhere you have an instance of SomeType you can chain the properties, so you get the property the property's testProperty() and then simply add a listener to it.
someType.getProperty().testProperty().addListener((observable, oldValue, newValue) -> {
// Do whatever you want if the its value changed.
// You can also use its old or new value.
});
We're integrating JavaFX onto a large legacy code base containing many "original" Java beans, i.e. the type implemented using java.beans.PropertyChangeSupport.
JavaFX does not support update of these style of beans, only initial value, as documented in javafx.scene.control.cell.PropertyValueFactory
If no method matching this pattern exists, there is fall-through
support for attempting to call get() or is() (that
is, getFirstName() or isFirstName() in the example above). If a method
matching this pattern exists, the value returned from this method is
wrapped in a ReadOnlyObjectWrapper and returned to the TableCell.
However, in this situation, this means that the TableCell will not be
able to observe the ObservableValue for changes (as is the case in the
first approach above).
Upgrading the beans to the property API is not an option as they live in a separate code base which we don't wish to add JavaFX dependencies on as it is still used by legacy Java 6 projects.
My question, how can I get a TableView to update when properties are changed without having to add/remove listeners onto all the individual beans in the table.
I was considering creating my own version of PropertyValueFactory which supports this, but I'd like to know if there are any other possible solutions.
I've produced two examples to illustrate this.
TableView using old-school beans
public class OldBeanTableView extends Application {
public class OldBean {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public static final String PROPERTY_NAME_FOO = "foo";
private int foo = 99;
public int getFoo() {
return foo;
}
public void setFoo(int foo) {
int oldValue = this.foo;
this.foo = foo;
pcs.firePropertyChange(PROPERTY_NAME_FOO, oldValue, foo);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<OldBean> beans = FXCollections.observableArrayList();
beans.add(new OldBean());
TableView<OldBean> tableView = new TableView<>();
TableColumn<OldBean, Integer> column = new TableColumn<OldBeanTableView.OldBean, Integer>();
tableView.getColumns().add(column);
column.setCellValueFactory(new PropertyValueFactory<>("foo"));
tableView.setItems(beans);
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> beans.get(0).setFoo(beans.get(0).getFoo() + 1), 0,
1, TimeUnit.SECONDS);
}
}
TableView using new beans
public class NewBeanTableView extends Application {
public class NewBean {
private IntegerProperty fooProperty = new SimpleIntegerProperty(0);
public int getFoo() {
return fooProperty.get();
}
public void setFoo(int foo) {
fooProperty.set(foo);
}
public IntegerProperty fooProperty() {
return fooProperty;
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<NewBean> beans = FXCollections.observableArrayList();
beans.add(new NewBean());
TableView<NewBean> tableView = new TableView<>();
TableColumn<NewBean, Integer> column = new TableColumn<NewBeanTableView.NewBean, Integer>();
tableView.getColumns().add(column);
column.setCellValueFactory(new PropertyValueFactory<>("foo"));
tableView.setItems(beans);
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> beans.get(0).setFoo(beans.get(0).getFoo() + 1), 0,
1, TimeUnit.SECONDS);
}
}
A very quick example for using JavaBeanProperty as valueFactory:
Callback<CellDataFeatures<OldBean, Integer>, ObservableValue<Integer>> valueFactory = cdf -> {
OldBean bean = cdf.getValue();
JavaBeanObjectProperty<Integer> wrappee;
try {
wrappee = JavaBeanObjectPropertyBuilder.create()
.name("foo").bean(bean).build();
return wrappee;
} catch (Exception e) {
e.printStackTrace();
}
return null;
};
column.setCellValueFactory(valueFactory);
Note that the bean must have methods add/removePropertyChangeListeners (which your real beans will have anyway :-) to work.
Extrapolating kleopatra's answer to the generic solution.
public class LegacyValueFactory<T, F> implements Callback<CellDataFeatures<T, F>, ObservableValue<F>> {
private String propertyName;
public LegacyValueFactory(String propertyName) {
this.propertyName = propertyName;
}
#Override
public ObservableValue<F> call(CellDataFeatures<T, F> param) {
try {
return JavaBeanObjectPropertyBuilder.create().name(propertyName).bean(param.getValue()).build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
Usage
column.setCellValueFactory(new LegacyValueFactory<OldBean, Integer>("foo"));
How to bind two Spinner controls into a TableView ? According to the sreenshot below, I would like to do something : colA = colB / 2 (and colB = colA x 2...) :
Here the snippet (deliberately simple) used to expose the problem :
TestApp.java
public class TestApp extends Application {
#Override
public void start(Stage stage) throws Exception {
final TableView<MyBean> tableView = new TableView<>();
final TableColumn<MyBean, Integer> colA = new TableColumn<>("Col A");
final TableColumn<MyBean, Integer> colB = new TableColumn<>("Col B");
colA.setCellFactory(col -> new SpinnerCell<MyBean, Integer>());
colA.setCellValueFactory(new PropertyValueFactory<MyBean, Integer>("valA"));
colB.setCellFactory(col -> new SpinnerCell<MyBean, Integer>());
colB.setCellValueFactory(new PropertyValueFactory<MyBean, Integer>("valB"));
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.setItems(FXCollections.observableArrayList(new MyBean(1, 2)));
tableView.getColumns().addAll(colA, colB);
stage.setScene(new Scene(new VBox(tableView), 500, 300));
stage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
SpinnerCell.java
public class SpinnerCell<S, T> extends TableCell<S, T> {
private Spinner<Integer> spinner;
private ObservableValue<T> ov;
public SpinnerCell() {
this.spinner = new Spinner<Integer>(0, 100, 1);
setAlignment(Pos.CENTER);
}
#Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(null);
setGraphic(this.spinner);
if(this.ov instanceof IntegerProperty) {
this.spinner.getValueFactory().valueProperty().unbindBidirectional(((IntegerProperty) this.ov).asObject());
}
this.ov = getTableColumn().getCellObservableValue(getIndex());
if(this.ov instanceof IntegerProperty) {
this.spinner.getValueFactory().valueProperty().bindBidirectional(((IntegerProperty) this.ov).asObject());
}
}
}
}
MyBean.java
public class MyBean {
private IntegerProperty valA, valB;
public MyBean(int valA, int valB) {
this.valA = new SimpleIntegerProperty(this, "valA", valA);
this.valB = new SimpleIntegerProperty(this, "valB", valB);
}
public IntegerProperty valAProperty() {
return this.valA;
}
public void setValA(int valA) {
this.valA.set(valA);
}
public int getValA() {
return valA.get();
}
public IntegerProperty valBProperty() {
return this.valB;
}
public void setValB(int valB) {
this.valB.set(valB);
}
public int getValB() {
return valB.get();
}
}
Here's an example of using the extended bidirectional binding support in MyBean:
public static class MyBean {
private IntegerProperty valA;
private IntegerProperty valB;
public MyBean(int valA) {
this.valA = new SimpleIntegerProperty(this, "valA", valA);
this.valB = new SimpleIntegerProperty(this, "valB", 0);
updateB(this.valA, null, this.valA.get());
BidirectionalBinding.<Number, Number>bindBidirectional(
this.valA, this.valB, this::updateB, this::updateA);
}
protected void updateB(ObservableValue<? extends Number> source, Number old, Number value) {
setValB(value.intValue() * 2);
}
protected void updateA(ObservableValue<? extends Number> source, Number old, Number value) {
setValA(value.intValue() / 2);
}
... // same as in OP's code
}
Plus in the SpinnerCell, bind to the bean property directly (vs. to its asObject wrapper) - there's a typing issue that I don't understand entirely [update, see below] (me and generics will never become friends ;-) which stands in the way of successful bidi-binding:
public static class SpinnerCell<S, T extends Number> extends TableCell<S, T> {
private Spinner<T> spinner;
private ObservableValue<T> ov;
public SpinnerCell() {
this(1);
}
public SpinnerCell(int step) {
this.spinner = new Spinner<>(0, 100, step);
setAlignment(Pos.CENTER);
}
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(null);
setGraphic(this.spinner);
if(this.ov instanceof Property) {
this.spinner.getValueFactory().valueProperty().unbindBidirectional(((Property) this.ov));
}
this.ov = getTableColumn().getCellObservableValue(getIndex());
if(this.ov instanceof Property) {
this.spinner.getValueFactory().valueProperty().bindBidirectional(((Property) this.ov));
}
}
}
}
Update (in understanding the problem with .asObject)
The problem is not the typing as such, but (struck again!) the weak listener registration in bidi-binding:
// spinner type
Spinner<Integer> spinner;
// value type (in valueFactory):
ObjectProperty<Integer> valueProperty;
// value type in bean:
IntegerProperty valXProperty;
// to be bindeable to spinner's value, needs to be wrapped
// into ObjectProperty<Integer>
// intuitively ... WRONG!
valueProperty.bindBidirectional(bean.valXProperty().asObject());
The wrapper created on the fly is a local reference which can (and is) garbage collected as soon as containing method is left ... As always with these weak listening contexts, none (? at least none that I'm aware of) of the alternatives is satisfying:
relax on typing of the Spinner: using Number (vs.Integer) doesn't require the wrapper because InterProperty instanceOf ObjectProperty<Number>
keep a strong reference to the wrapper somewhere
Try :
valA.bind(valB.divide(2));
I've faced with a requirement to deserialize fields that possibly can be transient using XStream 1.4.2. Despite of that, such fields may be annotated with both #XStreamAlias and #XStreamAsAttribute. Yes, I know, it sounds weird, and this is an indicator of bad design, but this is what I currently have. Since XStream offers a way to specify custom converter, I tried to extend com.thoughtworks.xstream.converters.reflection.ReflectionConverter in order to override the default way of omitting all transient fields trying to make XStream allow to deserialize them. However, I've fully stuck having two ideas to implement such a converter, but none of them works. So here is what I tried:
The 1st way doesn't work:
public final class TransientSimpleConverter extends ReflectionConverter {
private final Class<?> type;
private TransientSimpleConverter(Class<?> type, Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper, reflectionProvider);
this.type = type;
}
public static TransientSimpleConverter transientSimpleConverter(Class<?> type, XStream xStream) {
return new TransientSimpleConverter(type, xStream.getMapper(), xStream.getReflectionProvider());
}
#Override
protected boolean shouldUnmarshalTransientFields() {
return true;
}
#Override
public boolean canConvert(Class type) {
return this.type == type;
}
}
The 2nd way doesn't work either:
public final class TransientComplexConverter extends ReflectionConverter {
private final Class<?> type;
private TransientComplexConverter(Class<?> type, Mapper mapper, ReflectionProvider provider) {
super(mapper, provider);
this.type = type;
}
public static TransientComplexConverter transientComplexConverter(Class<?> type, Mapper mapper, Iterable<String> fieldNames) {
return new TransientComplexConverter(type, mapper, TransientHackReflectionProvider.transientHackReflectionProvider(type, fieldNames));
}
#Override
public boolean canConvert(Class type) {
return this.type == type;
}
private static final class TransientHackReflectionProvider extends PureJavaReflectionProvider {
private final Class<?> type;
private final Collection<Field> allowedFields;
private final Collection<String> allowedAliases;
private TransientHackReflectionProvider(Class<?> type, Collection<Field> allowedFields, Collection<String> allowedAliases) {
this.type = type;
this.allowedFields = allowedFields;
this.allowedAliases = allowedAliases;
}
public static TransientHackReflectionProvider transientHackReflectionProvider(final Class<?> type, Iterable<String> fieldNames) {
final Collection<Field> allowedFields = from(fieldNames).transform(new Function<String, Field>() {
#Override
public Field apply(String name) {
return field(type, name);
}
}).toList();
final Collection<String> allowedAliases = transform(allowedFields, new Function<Field, String>() {
#Override
public String apply(Field f) {
return f.getName();
}
});
return new TransientHackReflectionProvider(type, allowedFields, allowedAliases);
}
#Override
protected boolean fieldModifiersSupported(Field field) {
return allowedFields.contains(field) ? true : super.fieldModifiersSupported(field);
}
#Override
public boolean fieldDefinedInClass(String fieldName, Class type) {
return type == this.type && allowedAliases.contains(fieldName) ? true : super.fieldDefinedInClass(fieldName, type);
}
private static final Field field(Class<?> type, String name) {
try {
final Field field = type.getDeclaredField(name);
checkArgument(isTransient(field.getModifiers()), name + " is not transient");
checkArgument(field.getAnnotation(XStreamAsAttribute.class) != null, name + " must be annotated with XStreamAsAttribute");
checkArgument(field.getAnnotation(XStreamAlias.class) != null, name + " must be annotated with XStreamAlias");
return field;
} catch (final SecurityException ex) {
throw new RuntimeException(ex);
} catch (final NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
}
}
Any suggestions or ideas for a workaround? Thanks in advance.
I know this post is old, but maybe someone is still interested. My solution:
XStream xstream = new XStream(new MyPureJavaReflectionProvider());
class MyPureJavaReflectionProvider extends PureJavaReflectionProvider {
public MyPureJavaReflectionProvider() {
this(new FieldDictionary(new ImmutableFieldKeySorter()));
}
public MyPureJavaReflectionProvider(FieldDictionary fieldDictionary) {
super(fieldDictionary);
}
protected boolean fieldModifiersSupported(Field field) {
int modifiers = field.getModifiers();
return !Modifier.isStatic(modifiers);
}
public boolean fieldDefinedInClass(String fieldName, Class type) {
Field field = fieldDictionary.fieldOrNull(type, fieldName, null);
return field != null && fieldModifiersSupported(field);
}
}
I need some help, even my case is simple.
But i can't understand why the check box in the tableview is not geting the value.
I have get the example from the javafx ensemble
I have a class
public class ReservationObj {
private BooleanProperty tcheck;
private StringProperty tname;
private StringProperty tstatus;
private int tser;
public ReservationObj(boolean tcheck, String lname , String lBStatus, int serialinVector) {
this.tcheck = new SimpleBooleanProperty(tcheck);
this.tname = new SimpleStringProperty(lname);
this.tstatus = new SimpleStringProperty(lBStatus);
this.tser = serialinVector;
this.tcheck.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
System.out.println("The check Box is: " + t1);
}
});
}
public BooleanProperty getTcheck() {return tcheck;}
public String getTname() {return tname.getValue() ;}
public void setTname(String tname) {this.tname.set(tname);}
public String getTstatus() {return tstatus.getValue() ;}
public void setTstatus(String tstatus) {this.tstatus.set(tstatus);}
public int getTser() {return tser;}
public void setTser(int tser) { this.tser = tser;}
}
And also i have the.
public Parent createContent() {
final ObservableList<ReservationObj> ReservationList = FXCollections.observableArrayList(
new ReservationObj(true, "aaaaaaaa", "bbbbbbbbb", 1));
TableColumn RCheckCol = new TableColumn<ReservationObj, Boolean>();
RCheckCol.setCellValueFactory(new PropertyValueFactory("tcheck"));
RCheckCol.setText("aaa");
RCheckCol.setCellFactory(new Callback<TableColumn<ReservationObj, Boolean>, TableCell<ReservationObj, Boolean>>() {
public TableCell<ReservationObj, Boolean> call(TableColumn<ReservationObj, Boolean> p) {
return new CheckBoxTableCell<ReservationObj, Boolean>();
}
});
TableColumn RNameCol = new TableColumn();
RNameCol.setCellValueFactory(new PropertyValueFactory("tname"));
RNameCol.setText("bbbb");
TableColumn RAgeCol = new TableColumn();
RAgeCol.setCellValueFactory(new PropertyValueFactory("tstatus"));
RAgeCol.setText("cccc");
TableView AAView = new TableView();
AAView.setItems(ReservationList);
AAView.setEditable(true);
AAView.getColumns().addAll(RCheckCol,RNameCol,RAgeCol);
return AAView;
}
And when simple .
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
The columns of the tableview are getting the values except from the first “Checkbox”.
Also the listener in the checkbox is not working.
I really don’t understand what I have done wrong. Because I get the example from the ensemble.
Thanks for every idea and any solution.
Elias
Your ReservationObj class doesn’t respect JavaFX Properties convention in naming methods.
If you want to bind the RCheckCol with the tcheck BooleanProperty in :
RCheckCol.setCellValueFactory(new PropertyValueFactory("tcheck"));
You have to provide a tcheckProperty method in your model class:
public BooleanProperty tcheckProperty() {
return tcheck;
}
As an example of a valid JavaFX Bean:
public class Person {
private StringProperty name = new SimpleStringProperty("");
public Person(String name) {
this.name.setValue(name);
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public StringProperty nameProperty() {
return name;
}
}