sfxcode / sapphire-core

Scala - JavaFX Application Framework (CDI, Binding, Expressions)
https://sfxcode.github.io/sapphire-javafx
Apache License 2.0
8 stars 1 forks source link

bindBidirectional #17

Closed ASchmidt84 closed 4 years ago

ASchmidt84 commented 5 years ago

Hello Tom,

I have now a combobox with locale. I defined a converter for this combo to display the Countryname. This works fine. Now I have an adapter for an external object. I set this to the adapter as current element. Field name is countryIsoCode. Now I want convert the iso code "de" to locale Germany. I set up an converter

bindings.add("countryCombo","headquarter.countryIsoCode")
adapter.addConverter[Locale](
      "countryCombo",
      StringConverter[Locale](e ⇒ {
        println("ARG")
        Try(Locale.forLanguageTag(e)).getOrElse(Locale.CHINA)
      }, i ⇒ i.getCountry )
    )
adapter.addBindings(bindings)

I get now an error NPE. So I looked at FXBeanAdapter:58 whichs bindBidirectional the bean.

protected def bindBidirectional[S](bean: FXBean[T], property: Property[S], beanKey: String) {
    val observable = bean.getProperty(beanKey)
    observable match {
      case beanProperty: Property[_] => property match {
        case stringProperty: StringProperty => bindBidirectionalFromStringProperty(stringProperty, observable, beanKey)
        case p: Property[S] => bindBidirectionalFromProperty[S](p, beanProperty.asInstanceOf[Property[S]])
      }
      case _ =>
    }
  }

I think

Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.Locale (java.lang.String and java.util.Locale are in module java.base of loader 'bootstrap')
    at scalafx.util.StringConverter$$anon$3.toString(StringConverter.scala:82)
    at scalafx.util.StringConverter$$anon$1.toString(StringConverter.scala:48)
    at javafx.scene.control.skin.ComboBoxListViewSkin.updateDisplayText(ComboBoxListViewSkin.java:458)
    at javafx.scene.control.skin.ComboBoxListViewSkin.updateDisplayNode(ComboBoxListViewSkin.java:357)
    at javafx.scene.control.skin.ComboBoxListViewSkin.getDisplayNode(ComboBoxListViewSkin.java:266)
    at javafx.scene.control.skin.ComboBoxBaseSkin.updateDisplayArea(ComboBoxBaseSkin.java:289)
    at javafx.scene.control.skin.ComboBoxBaseSkin.lambda$new$7(ComboBoxBaseSkin.java:133)
    at java.base/java.util.function.Consumer.lambda$andThen$0(Consumer.java:65)
    at com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:106)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at javafx.beans.property.ObjectProperty.setValue(ObjectProperty.java:72)
    at com.sun.javafx.binding.BidirectionalBinding.bind(BidirectionalBinding.java:64)
    at javafx.beans.binding.Bindings.bindBidirectional(Bindings.java:868)
    at javafx.beans.property.ObjectProperty.bindBidirectional(ObjectProperty.java:80)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.bindBidirectionalFromProperty(FXBeanAdapter.scala:137)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.bindBidirectional(FXBeanAdapter.scala:114)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.$anonfun$bindAll$1(FXBeanAdapter.scala:58)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.$anonfun$bindAll$1$adapted(FXBeanAdapter.scala:58)
    at scala.collection.Iterator.foreach(Iterator.scala:941)
    at scala.collection.Iterator.foreach$(Iterator.scala:941)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1429)
    at scala.collection.IterableLike.foreach(IterableLike.scala:74)
    at scala.collection.IterableLike.foreach$(IterableLike.scala:73)
    at scala.collection.AbstractIterable.foreach(Iterable.scala:56)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.bindAll(FXBeanAdapter.scala:58)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.updateBean(FXBeanAdapter.scala:44)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.$anonfun$new$1(FXBeanAdapter.scala:21)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:106)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at javafx.beans.property.ObjectProperty.setValue(ObjectProperty.java:72)
    at com.sfxcode.sapphire.core.value.FXBeanAdapter.set(FXBeanAdapter.scala:34)
    at controller.operatorView.view.ManufactorOverviewController.$anonfun$didGainVisibility$11(ManufactorOverviewController.scala:117)
    at controller.operatorView.view.ManufactorOverviewController.$anonfun$didGainVisibility$11$adapted(ManufactorOverviewController.scala:108)
    at scalafx.beans.value.ObservableValue$$anon$1.changed(ObservableValue.scala:116)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:105)
    at javafx.scene.control.MultipleSelectionModelBase.lambda$new$0(MultipleSelectionModelBase.java:67)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ReadOnlyIntegerPropertyBase.fireValueChangedEvent(ReadOnlyIntegerPropertyBase.java:72)
    at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:102)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:114)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:148)
    at javafx.scene.control.SelectionModel.setSelectedIndex(SelectionModel.java:69)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.updateSelectedIndex(TableView.java:3008)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2511)
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.clearAndSelect(TableView.java:2441)
    at javafx.scene.control.TableView$TableViewSelectionModel.clearAndSelect(TableView.java:1965)
    at com.sun.javafx.scene.control.behavior.TableCellBehaviorBase.simpleSelect(TableCellBehaviorBase.java:213)
    at com.sun.javafx.scene.control.behavior.TableCellBehaviorBase.doSelect(TableCellBehaviorBase.java:146)
    at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(CellBehaviorBase.java:176)
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3862)
    at javafx.scene.Scene.processMouseEvent(Scene.java:1849)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2590)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:411)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at com.sun.glass.ui.View.notifyMouse(View.java:942)
    at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
    at java.base/java.lang.Thread.run(Thread.java:835)
ASchmidt84 commented 5 years ago

I tried a few things.

val k = StringConverter[Locale](e ⇒ Locale.CANADA, e ⇒ e.getCountry)
this.converterFactory.converterMap.put("countryIsoCodeConverter",k)
println("KLASS: " + k.getClass ) //class scalafx.util.StringConverter$$anon$2
if (property.isDefined && property.get.isInstanceOf[StringProperty])
      println(property.get.isInstanceOf[StringProperty])
    else
      println("NOPE!") //This is shown
println( converterFactory.getConverterByName("countryIsoCodeConverter") ) //javafx.util.converter.DefaultStringConverter@13a8bd28

But the problem is that the guessPropertyForNode is

Some(ObjectProperty [bean: ComboBox[id=countryCombo, styleClass=combo-box-base combo-box], name: value, value: null])

So I will never have the possibilty to set the combo item with an binding.

ASchmidt84 commented 5 years ago

I try to get an objectproperty of FXBean to set it all manual. But their is always a simplestringproperty.

def getObjectProperty[S <: Any](key: String): ObjectProperty[S] = getProperty(key).asInstanceOf[ObjectProperty[S]]

def getProperty(key: String): Property[_] = {
....
value match {
                case i: Integer => result = new SimpleIntegerProperty(bean, info.name, i)
                case l: Long => result = new SimpleLongProperty(bean, info.name, l)
                case f: Float => result = new SimpleFloatProperty(bean, info.name, f)
                case d: Double => result = new SimpleDoubleProperty(bean, info.name, d)
                case b: Boolean => result = new SimpleBooleanProperty(bean, info.name, b)
                case ld: LocalDate => result = new SimpleObjectProperty(bean, info.name, ld)
                case _ => result = createSimpleStringPropertyForObject(value, info.name)
              }
....

}
sfxcode commented 5 years ago

I try to create an object property in case of unprocessed AnyRef values, maybe that will work. Please send me a simple usecase forme to try it out.

A workaround is always set combobox value on setBean programatically and add a listener to the combobox to update bean value on change.