IntegerProperty cannot be converted to Observable - javafx

Callback<IntegerProperty, Observable[]> extractor =
(IntegerProperty p) -> { return new Observable[]{p}; };
I gets error: IntegerProperty cannot be converted to Observable.
What wrong?

Related

How to bind a List<String> to a ObservableList<String>?

I have an object Bean containing a List<String>. I would like to "bind" this list to an ObservableList so when an item is added to or removed from the original list, the ObservableList is updated (which then triggers the listeners that monitor the ObservableList).
I found this question whose answer shows how to wrap a simple String into a JavaFX StringProperty using JavaBeanStringPropertyBuilder.
I tried to do the same thing but replacing the String with a List<String> as shown below:
public class Bean {
private final List<String> nameList;
private final PropertyChangeSupport propertySupport ;
public Bean() {
this.nameList = new ArrayList<>();
this.propertySupport = new PropertyChangeSupport(this);
}
public List<String> getNameList() {
return nameList;
}
public void setNameList(List<String> nameList)
{
List<String> oldList = new ArrayList<>(this.nameList);
this.nameList.clear();
this.nameList.addAll(nameList);
propertySupport.firePropertyChange("nameList", oldList, this.nameList);
}
public void addName(String name) {
List<String> oldList = new ArrayList<>(this.nameList);
this.nameList.add(name);
propertySupport.firePropertyChange("nameList", oldList, this.nameList);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
}
}
Bean bean = new Bean();
JavaBeanObjectProperty<List<String>> listProperty = null;
try
{
listProperty = JavaBeanObjectPropertyBuilder.create().bean(bean).name("nameList").build();
} catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
}
listProperty.addListener((ObservableValue<? extends List<String>> obs, List<String> oldName, List<String> newName) ->
{
System.out.println("List changed");
});
bean.setNameList(Arrays.asList("George", "James"));
But the listener is not triggered after calling setNameList() and I don't know what I'm missing.
Could you help me please?
A change listener registered with a Property<T> will only be notified if the value of the property actually changes. That is, the set(T newValue) method is implemented something like this:
public void set(T newValue) {
T oldValue = this.get();
if (! oldValue.equals(newValue)) {
// notify change listeners...
}
}
The JavaBeanObjectPropertyBuilder is going to create a JavaBeanObjectProperty<List<String>> (an implementation of Property<List<String>>) and set its value to the result of calling bean.getNameList(). I.e. the value held internally by listProperty is a reference to bean.nameList.
The JavaBeanObjectProperty also registers a listener via the call to bean.addPropertyChangeListener(...). When
propertySupport.firePropertyChange("nameList", oldList, this.nameList);
is invoked, the internal listener in JavaBeanObjectProperty will set its own value to the new value fired by the property change support; i.e. it will call
set(bean.nameList);
However, since this is just a reference to the current value of the property, no change listener registered with listProperty will be notified (basically, no change has occurred).
To clarify, if it helps: the content of the List<String> returned by listProperty.get() will change when you call
this.nameList.clear();
and
this.nameList.addAll(nameList);
in the bean (because the listProperty references bean.nameList), but the actual list reference itself has not changed.
You can test this with, e.g.
Bean bean = new Bean();
JavaBeanObjectProperty<List<String>> listProperty = null;
try
{
listProperty = JavaBeanObjectPropertyBuilder.create().bean(bean).name("nameList").build();
} catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
}
listProperty.addListener((ObservableValue<? extends List<String>> obs, List<String> oldName, List<String> newName) ->
{
System.out.println("List changed");
});
List<String> oldList = listProperty.get();
bean.setNameList(Arrays.asList("George", "James"));
List<String> newList = listProperty.get();
System.out.println(oldList);
System.out.println(newList);
System.out.println(oldList == newList);
The best fix is simply to use an ObservableList in your Bean class:
public class Bean {
private final ObservableList<String> nameList;
public Bean() {
this.nameList = FXCollections.observableArrayList<>();
}
public ObservableList<String> getNameList() {
return nameList;
}
public void addName(String name) {
this.nameList.add(name);
}
}
Note you don't lose the functionality provided by setNameList(...); you can do
bean.getNameList().setAll(...);
if you want to set the entire content of the list. If you want the same API, you can use a ListProperty instead of the ObservableList.
The test code you have then becomes
Bean bean = new Bean();
bean.getNameList().addListener((ListChangeListener.Change<? extends String> change) ->
{
System.out.println("List changed");
});
bean.getNameList().setAll("George", "James");
As stated in the comments in the question, I don't really understand having any restriction preventing the use of ObservableList in the model; indeed this is exactly the use case for which ObservableList (along with the properties and bindings API) was designed.
There is no adapter designed for use with observable lists in the same way as there are Java Bean adapters for simple properties. Thus if you really wanted to avoid use of ObservableList in your model class (which, again, doesn't really make sense to me), you would have to implement your own listener notification for the Bean:
public class Bean {
private final List<String> nameList ;
private final List<Consumer<String>> nameAddedListeners ;
private final List<Consumer<List<String>>> nameListReplacedListeners ;
public Bean() {
this.nameList = new ArrayList<>();
this.nameAddedListeners = new ArrayList<>();
this.nameListReplacedListeners = new ArrayList<>();
}
public List<String> getNameList() {
return nameList ;
}
public void setNameList(List<String> newNames) {
this.nameList.setAll(newNames);
nameListReplacedListeners.forEach(listener -> listener.accept(newNames));
}
public void addName(String name) {
this.nameList.add(name);
nameAddedListeners.forEach(listener -> listener.accept(name));
}
public void addNameListReplacedListener(Consumer<List<String>> listener) {
nameListReplacedListeners.add(listener);
}
public void addNameAddedListener(Consumer<String> listener) {
nameAddedListeners.add(listener);
}
}
Now you could do
Bean bean = new Bean();
bean.addNameListReplacedListener(list -> System.out.println("Names changed"));
bean.setNameList(List.of("George", "James"));
or you could effectively create an adapter:
Bean bean = new Bean();
ObservableList<String> names = FXCollections.observableArrayList(bean.getNameList());
bean.addNameAddedListener(names::add);
bean.addNameListReplacedListener(names::setAll);
etc.

java.util.LinkedHashMap cannot be cast to class GenericEvent - objectmapper typeReference

The messages are getting consumed from kafka topic using json deserializer(spring commons). The generic messages structure as below.
GenericEvent:
{
"id": "10000",
"payload": {
"id": 100
"attribute1": "hi",
"attribute2": "hello"
},
"type": {
"id" : 1,
"name" : "A"
}
}
Different types has different payload and the structure of the payload also will be varied. So i would like to process the payload based on the type.
My respective POJO is as below, and total 3 different payloads and respective payload pojos has been created.
GenericEvent {
private int id;
private T payload:
private Type type;
}
Right now i am using the below code to convert
JsonNode jsonNode = objectMapper.readTree("messagefromKafka);
GenericEvent genericEvent = objectMapper.convertValue(jsonNode, new TypeReference<GenericEvent>() {});
But the code is throwing java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class GenericEvent .
Can someone help on this issue?
EDIT:
//Generic Object i have provided already
//Payload Object - applicable for different types - A, B, C, D
public class Payload {
private int id;
private String name;
private String address;
private String typeAAttribute1; //applicable for type A attribute
private String typeAAttribute2; //applicable for type A attribute
private String typeBAtribute1; //applicable for type B attribute
private String typeABAtribute2; //applicable for type A,B attibute
private String typeCtribute1; //applicable for type C attibute
private String typeABCAtribute1;//applicable for type A,B,C attibute
}
Kafka consumer config:
---------------------
import org.springframework.kafka.support.serializer.JsonDeserializer;
#Bean
public ConcurrentKafkaListenerContainerFactory<Object, Object> reprocessListenerContainerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
props.put(ConsumerConfig.ALLOW_AUTO_CREATE_TOPICS_CONFIG, false);
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapservers);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, latest);
props.put(ConsumerConfig.GROUP_ID_CONFIG, "testgroupid");
props.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "300000");
ConcurrentKafkaListenerContainerFactory<Object, Object> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(props));
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
factory.setRecordFilterStrategy(
(consumerRecord) -> {
try {
JsonNode jsonNode = objectMapper.readTree(consumerRecord.value().toString());
GenericEvent genericEvent = objectMapper.convertValue(jsonNode, new TypeReference<GenericEvent>() {});
log.info(
"Retrieved the record {} from the partition {} with offset {}",
consumerRecord.value(),
consumerRecord.partition(),
consumerRecord.offset());
//Process type A and B events
if (genericEvent.getType().equalIgnoreCase("A") || genericEvent.getType().equalIgnoreCase("B"))) {
return false;
}
return true;
} catch (Exception ex) {
log.error("Error occured:{}", ex.getStackTrace());
return true;
}
});
return factory;
}
//Listener
#KafkaListener(id = "MYREPROCESS-ID", topics = "reprocess-test",
containerFactory = "reprocessListenerContainerFactory",
autoStartup = "true")
public void onMessage(ConsumerRecord<String, String> consumerRecord, Acknowledgment acknowledgment) {
JsonNode jsonNode = objectMapper.readTree("messagefromKafka);
GenericEvent genericEvent = objectMapper.convertValue(jsonNode, new TypeReference<GenericEvent>() {});
//I should identify the respective payload during runtime
Payload payload = genericEvent.getPayload();
if (genericEvent.getType().equalsIgnoreCase("A") {
processPayload(payload);
} else {
processPayload(payload);
}
}
Something is odd. Since you are using the Spring JsonDeserializer, you have to tell it what to convert to; properties are documented here https://docs.spring.io/spring-kafka/docs/current/reference/html/#serdes-json-config).
In that case, you would get ConsumerRecord<?, GenericEvent>.
If you want to receive ConsumerRecord<String, String> and do the conversion yourself, you should use StringDeserializer s instead.

JavaFX TreeTableView - Prevent editing of unavailable cells

I have a particular TreeTableView that displays a hierarchical tree of mixed types. These types do not necessarily have overlapping columns and as such the columns for some rows will be empty. As an example, consider the following classes:
public class Person {
private final StringProperty nameProperty;
private final StringProperty surnameProperty;
public Person() {
this.nameProperty = new SimpleStringProperty();
this.surnameProperty = new SimpleStringProperty();
}
public StringProperty nameProperty() {
return this.nameProperty;
}
public void setName(String value) {
this.nameProperty.set(value);
}
public String getName() {
return this.nameProperty.get();
}
public StringProperty surnameProperty() {
return this.surnameProperty;
}
public void setSurname(String value) {
this.surnameProperty.set(value);
}
public String getSurname() {
return this.surnameProperty.get();
}
}
public class Dog {
private final StringProperty nameProperty;
private final IntegerProperty ageProperty;
private final StringProperty breedProperty;
public Dog() {
this.nameProperty = new SimpleStringProperty();
this.ageProperty = new SimpleIntegerProperty();
this.breedProperty = new SimpleStringProperty();
}
public StringProperty nameProperty() {
return this.nameProperty;
}
public void setName(String value) {
this.nameProperty.set(value);
}
public String getName() {
return this.nameProperty.get();
}
public IntegerProperty ageProperty() {
return this.ageProperty;
}
public void setAge(int value) {
this.ageProperty.setValue(value);
}
public int getAge() {
return this.ageProperty.get();
}
public StringProperty breedProperty() {
return this.breedProperty;
}
public void setBreed(String breed) {
this.breedProperty.set(breed);
}
public String getBreed() {
return this.breedProperty.get();
}
}
If I construct the TreeTableView as follows:
TreeTableView<Object> treeTableView = new TreeTableView<>();
treeTableView.setEditable(true);
List<TreeTableColumn<Object, ?>> columns = treeTableView.getColumns();
TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
nameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
columns.add(nameColumn);
TreeTableColumn<Object, String> surnameColumn = new TreeTableColumn<>("Surname");
surnameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
surnameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("surname"));
columns.add(surnameColumn);
TreeTableColumn<Object, Integer> ageColumn = new TreeTableColumn<>("Age");
ageColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn(new IntegerStringConverter()));
ageColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("age"));
columns.add(ageColumn);
TreeTableColumn<Object, String> breedColumn = new TreeTableColumn<>("Breed");
breedColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
breedColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("breed"));
columns.add(breedColumn);
TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);
List<TreeItem<Object>> rootChildren = rootItem.getChildren();
Person john = new Person();
john.setName("John");
john.setSurname("Denver");
TreeItem<Object> johnTreeItem = new TreeItem<>(john);
rootChildren.add(johnTreeItem);
List<TreeItem<Object>> johnChildren = johnTreeItem.getChildren();
Dog charlie = new Dog();
charlie.setName("Charlie");
charlie.setAge(4);
charlie.setBreed("Labrador");
TreeItem<Object> charlieTreeItem = new TreeItem<>(charlie);
johnChildren.add(charlieTreeItem);
Dog daisy = new Dog();
daisy.setName("Daisy");
daisy.setAge(7);
daisy.setBreed("Bulldog");
TreeItem<Object> daisyTreeItem = new TreeItem<>(daisy);
johnChildren.add(daisyTreeItem);
I will get a TreeTableView that looks like:
The Age and Breed columns are empty for the TreeItems that contains Person objects. However, nothing stops me from editing Age or Breed cell for the top-most Person row. Setting a value in one of those cells doesn't change the Person object, but the value still hangs around there like it is committed.
Is there any way to prevent this from happening? I know that I could check for nulls in a custom TreeTableCell subclass and prevent the editing from kicking off in the startEdit() method. However, there are circumstances where a null-value is valid and preventing editing by checking nulls is not a feasible solution for all situations. Also, creating a custom TreeTableCell subclass for every datatype and corresponding columns is painful. It would have been nice if TreeItemPropertyValueFactory could provide for a way to abort the edit when no value is present for a particular cell.
Ok, I scraped together something by looking at the TreeItemPropertyValueFactory class itself for inspiration. This gives me the desired functionality, although I'm not sure if it is 100% correct or what the implications are of using it.
It basically comes down to installing a new cell-factory that checks if the cell-value-factory is of type TreeItemPropertyValueFactory. If it is the case, a new cell-factory is installed that delegates to the original but adds listeners for the table-row and tree-item properties. When the TreeItem changes, we get the row-data and see if we can access the desired property (via a PropertyReference that is cached for performance). If we can't (and we get the two exceptions) we assume that the property cannot be accessed and we set the cell's editable-property to false.
public <S, T> void disableUnavailableCells(TreeTableColumn<S, T> treeTableColumn) {
Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> cellFactory = treeTableColumn.getCellFactory();
Callback<CellDataFeatures<S, T>, ObservableValue<T>> cellValueFactory = treeTableColumn.getCellValueFactory();
if (cellValueFactory instanceof TreeItemPropertyValueFactory) {
TreeItemPropertyValueFactory<S, T> valueFactory = (TreeItemPropertyValueFactory<S, T>)cellValueFactory;
String property = valueFactory.getProperty();
Map<Class<?>, PropertyReference<T>> propertyRefCache = new HashMap<>();
treeTableColumn.setCellFactory(column -> {
TreeTableCell<S, T> cell = cellFactory.call(column);
cell.tableRowProperty().addListener((o1, oldRow, newRow) -> {
if (newRow != null) {
newRow.treeItemProperty().addListener((o2, oldTreeItem, newTreeItem) -> {
if (newTreeItem != null) {
S rowData = newTreeItem.getValue();
if (rowData != null) {
Class<?> rowType = rowData.getClass();
PropertyReference<T> reference = propertyRefCache.get(rowType);
if (reference == null) {
reference = new PropertyReference<>(rowType, property);
propertyRefCache.put(rowType, reference);
}
try {
reference.getProperty(rowData);
} catch (IllegalStateException e1) {
try {
reference.get(rowData);
} catch (IllegalStateException e2) {
cell.setEditable(false);
}
}
}
}
});
}
});
return cell;
});
}
}
For the example listed in the question, you can call it after you created all your columns as:
...
columns.forEach(this::disableUnavailableCells);
TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);
...
You'll see that cells for the Age and Breed columns are now uneditable for Person entries whereas cells for the Surname column is now uneditable for Dog entries, which is what we want. Cells for the common Name column is editable for all entries as this is a common property among Person and Dog objects.

Not able to merge to Map<String, Long>

I am trying to merge several Map into a single map, for doing this I wrote this piece of code.
public static Map<String, Long> mergeMaps(List<Map<String, Long>> maps){
Map<String, Long> mergedMap = new TreeMap<>();
maps.forEach( map -> {
//Map<String, Long> mx = new TreeMap<>(map);
map.forEach((key, value) -> mergedMap.merge(key, value, Long::sum));
});
logger.info("Merge map successful");
return mergedMap;
}
On doing this the code is throwing an error :
org.springframework.batch.core.step.AbstractStep java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[?:1.8.0_201-1-ojdkbuild]
lambda$mergeMaps$1
I have this pojo
public class MappingDto {
public MappingDto(){
map1 = new LinkedHashMap<>();
map2 = new LinkedHashMap<>();
map3 = new LinkedHashMap<>();
}
private Map<String, Long> map1;
private Map<String, Long> map2;
private Map<String, Long> map3;
getters()/Setters()
toString()
}
I populate this pojo maps with the below codes.
MappingDto.setMap1(Arrays.asList(doc.getDescription().split("\\s+")).stream().collect(Collectors.groupingBy(Function.identity(),LinkedHashMap::new,Collectors.counting())));
after generating this I have to merge this map with the some Stored in File, To read the Map I have the below method.
public static <T> T readJsonFile(String filePath, T t) throws JAXBException, IOException, IllegalAccessException, InstantiationException {
ObjectMapper mapper = new ObjectMapper();
if(!Utils.checkIfFileExists(filePath)){
Utils.createANewFile(filePath);
return t;
}
return mapper.readValue(new File(filePath), new TypeReference<T>(){});
}
After getting this maps I pass it to the
mergeMaps(Arrays.asList(MappingDto.getMap1(),storedData));
and
List<Map<String, Long>> dataList = new LinkedList<>();
dataList.add(MappingDto.getMap1());
dataList.add(MappingDto.getMap2());
dataList.add(MappingDto.getMap3());
mergeMaps(dataList);
I went through this code several time, But not able to figure what exactly is causing this issue.
One is reading the Stored JSON map
public static <T> T readJsonFile(String filePath, Class<T> t) throws JAXBException, IOException, IllegalAccessException, InstantiationException {
ObjectMapper mapper = new ObjectMapper();
if(!Utils.checkIfFileExists(filePath)){
Utils.createANewFile(filePath);
return t.newInstance();
}
return mapper.readValue(new File(filePath), new TypeReference<T>(){});
}
I tried this and several other variations but no results.
public static Map<String, Long> mergeMaps(List<Map<String, Long>> maps){
Map<String, Long> mergedMap = new TreeMap<>();
maps.forEach( map -> {
//Map<String, Long> mx = new TreeMap<>(map);
map.forEach((key, value) -> mergedMap.merge(key, Long.valueOf(value), Long::sum));
});
logger.info("Merge map successful");
return mergedMap;
}
Can you please help me understand what is in Integer and why is being cast to Long
Thanks in advance.

Notify . change of ObservableProperties

I have a class with following properties :
public class Member {
private LongProperty version;
private LongProperty id;
private StringProperty name;
private StringProperty surname;
private StringProperty bornname;
private StringProperty photo;
private ObjectProperty<Age> age;
private ObjectProperty<Sex> sex;
}
The problem is, when I add Change Listener on this object :
{
private ObjectProperty<Member> member;
public void addMemberChangeListener() {
this.member.addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
(...)
}
}
}
The change listener is not notified when I change value of properties. There is other way to notify listener about changes properties that implement standard Observer pattern ?
If you want to add a single listener that is notified when any of the properties belonging to an instance of Member change, you can do something like the following:
Member member = new Member();
ChangeListener<Object> listener = (obs, oldValue, newValue) ->
System.out.println(obs + " changed from " + oldValue + " to " + newValue);
Stream.of(member.versionProperty(), member.idProperty(), member.nameProperty() /*, ... */)
.forEach(p -> p.addListener(listener));
Note that the concrete property classes contain a constructor that takes an owning bean and a name of the property, so if you want to support this kind of use, you might want to use those constructors:
public class Member {
private final LongProperty version = new SimpleLongProperty(this, "version");
private final StringProperty name = new SimpleStringProperty(this, "name");
// ...
}
and then you can leverage this in the change listener:
ChangeListener<Object> listener = (obs, oldValue, newValue) ->
System.out.println(((Property<?>)obs).getName() + " changed from " + oldValue + " to " + newValue);
Just a comment: in my experience, needing this is rare (at best). You will usually (always?) find it more convenient to register a different listener with each property, as typically the action to take when a property is changed depends on which property is changed. In other cases, there is usually a more elegant solution anyway.
The properties listener is triggered when it changes value memberProperty.setValue(otherMember), not when you modify some property of its value memberProperty.getValue().setId(0)
Try to use
PropertyChangeSupport.
Example:
public class Member
{
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private long id;
private String name;
public void addChangeListener(PropertyChangeListener listener)
{
this.pcs.addPropertyChangeListener(listener);
}
public void removeChangeListener(PropertyChangeListener listener)
{
this.pcs.removePropertyChangeListener(listener);
}
public void setId(long newId)
{
long oldId = this.id;
this.id = newId;
pcs.firePropertyChange("id", oldId, newId);
}
public void setName(String newName)
{
String oldName = this.name;
this.name = newName;
pcs.firePropertyChange("name", oldName, newName);
}
}
Use:
Member member = new Member();
member.addChangeListener((evt) ->
{
System.out.println("Changed "
+ evt.getPropertyName()
+ ": " + evt.getOldValue()
+ " -> " + evt.getNewValue());
});
member.setId(15);
member.setName("Maria");
member.setName("Ana");
Output:
Changed id: 0 -> 15
Changed name: null -> Maria
Changed name: Maria -> Ana

Resources