fn-fx / fn-fx

A Functional API around JavaFX / OpenJFX.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug with KeyFrame constructors

kxygk opened this issue · comments

The constructors of KeyFrame are not being initialized correctly and we just get the default/empty constructor. It should actually have multiple constructor values (:time and :on-finished) https://docs.oracle.com/javase/8/javafx/api/javafx/animation/KeyFrame.html

I've narrowed down the issue a bit. Comparing something that works

 (fn-fx.util.reflect-utils/get-value-ctors javafx.scene.image.Image) 

output:

({:method #object[java.lang.reflect.Method 0x8876855 "public static javafx.scene.image.Image javafx.scene.image.Image.impl_fromPlatformImage(java.lang.Object)"], :is-ctor? false, :method-name impl_fromPlatformImage, :prop-names (image), :prop-types [java.lang.Object], :prop-names-kw [:image], :prop-names-sym [image]} {:method #object[java.lang.reflect.Constructor 0x49507ef5 "public javafx.scene.image.Image(java.io.InputStream,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.io.InputStream double double boolean boolean], :prop-names-kw [:is :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [is requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x70df4b07 "public javafx.scene.image.Image(java.io.InputStream)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is), :prop-types [java.io.InputStream], :prop-names-kw [:is], :prop-names-sym [is]} {:method #object[java.lang.reflect.Constructor 0x18cdfc45 "public javafx.scene.image.Image(java.lang.String)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url), :prop-types [java.lang.String], :prop-names-kw [:url], :prop-names-sym [url]} {:method #object[java.lang.reflect.Constructor 0x2b3bfac9 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.lang.String double double boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [url requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x61568e8d "public javafx.scene.image.Image(java.lang.String,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url backgroundLoading), :prop-types [java.lang.String boolean], :prop-names-kw [:url :background-loading], :prop-names-sym [url background-loading]} {:method #object[java.lang.reflect.Constructor 0x6bdb66d3 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth backgroundLoading), :prop-types [java.lang.String double double boolean boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth :background-loading], :prop-names-sym [url requested-width requested-height preserve-ratio smooth background-loading]})

To KeyFrame which is broken

(fn-fx.util.reflect-utils/get-value-ctors javafx.animation.KeyFrame) 

output:

({:method #object[java.lang.reflect.Constructor 0x28f769b2 "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x64f9ddf1 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x5002e3ee "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x35c9f65a "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x26e0a3a8 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,java.util.Collection)"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler java.util.Collection], :prop-names-kw [], :prop-names-sym []})

It's really hard to see with GitHub's formatting, but there a semi-colon ";" that shows up near the beginning of the KeyFrame output It's very bizarre and makes most of the output effectively commented out.

Running:

(:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))

Does give us something where we can see the constructor arguments are there

#object[java.lang.reflect.Constructor 0x67dc18da "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"]

Past that, I'm not sure. Something is not quite parsing in the values correctly. I haven't been able to figure out the rest of the internals of get-value-ctors b/c the code is a bit beyond me. Any help would be greatly appreciated

Okay, I figured out where the issue is. In fn-fx.util.reflect-utils/get-arg-names the part that gets the params is having issues

0:

(.getType (nth  (.getParameters (:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))) 0 ))

will return

javafx.util.Duration

1:

(.getType (nth  (.getParameters (:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))) 0 ))

will return

javafx.event.EventHandler

2:

(.getType (nth  (.getParameters (:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))) 0 ))

will return

[Ljavafx.animation.KeyValue;

and notice how this has a [L in the front and a trailing semi-colon!
According to here this is an encoding to say that It's an array of KeyValues

The next part I don't really get.. the file method-arg-info.edn has the mappings from JavaFX classes to signatures. If you look through this monster file you can fine a part that says

["KeyFrame" "KeyFrame" ["Duration" "EventHandler<ActionEvent>" "KeyValue"]] ["time" "onFinished" "values"]`

I have no idea where this files comes from, but this gives no indication that KeyValue is an array.. My guess is that there is no support for array arguments? Maybe someone can confirm?

At this point the easiest patch would be to have a check that if the param is an array, it will get its type and use that instead.

Alright, upon further study.. in method-arg-info.edn we are reading in a huge map and the ["KeyFrame" "KeyFrame" ["Duration" "EventHandler<ActionEvent>" "KeyValue"]] part is the key and the ["time" "onFinished" "values"] is the value

The code get the function signature and tries to use it as a key to get the values which have be determined ahead of time and cached in a this file.

The line

(map #(-> % .getType .getName fn-fx.util.reflect-utils/unqualified-name) (.getParameters (:method (first (fn-fx.util.reflect-utils/get-ctors javafx.animation.KeyFrame)))))

returns

("Duration" "EventHandler" "KeyValue;")

Which I guess has two problems. The KeyValue; with the comma.. and the EvenHandler without the <ActionEvent> extra type info. The comma seems to disappear after it's all put together into a map in [class-name method-name params]
and we get ["KeyFrame" "KeyFrame" ("Duration" "EventHandler" "KeyValue")] which doesn't work as a key!

But ["KeyFrame" "KeyFrame" ["Duration" "EventHandler<ActionEvent>" "KeyValue"]]) does..

Backpedaling a bit, we need to look at what we get when we actually ask the system for the signature..
Running for instance (first (.getConstructors javafx.animation.KeyFrame)) we get

#object[java.lang.reflect.Constructor 0x12c1dc7c "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"]

Which has nothing about the <ActionEvent>. So my guess is that the real issue is with the method-arg-info.edn file which is generated by method_arg_indexer.clj

However I can't quite figure out where the issue is in that file..

I see this project has come back from the dead. I'm available to help debug this..

@geokon-gh fantastic! Would you mind checking out the latest source, and reproducing your issue and seeing if it still occurs, and if so whether the symptoms are the same?

We hope to have automatic SNAPSHOT builds deployed to Clojars soon (see issue #43 and PR that addresses it), but that fix is still pending as of right now.

Okay, well..

I'm rather new to Clojure so I'm probably not doing this in the most intelligent way (if you @pmonks could give me some pointers that'd be great haha)

What I tried to do is clone the repo, open up the project.clj and jack in. This for some reason doesn't load all the namespaces, so then I ran cider-load-all-files (there must be a better way to do that..)

Then again I ran

(fn-fx.util.reflect-utils/get-value-ctors javafx.scene.image.Image) 

and got a properly formatted output

({:method #object[java.lang.reflect.Method 0x2be10b14 "public static javafx.scene.image.Image javafx.scene.image.Image.impl_fromPlatformImage(java.lang.Object)"], :is-ctor? false, :method-name impl_fromPlatformImage, :prop-names (image), :prop-types [java.lang.Object], :prop-names-kw [:image], :prop-names-sym [image]} {:method #object[java.lang.reflect.Constructor 0x43ce88bd "public javafx.scene.image.Image(java.io.InputStream,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.io.InputStream double double boolean boolean], :prop-names-kw [:is :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [is requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x53b3a6ba "public javafx.scene.image.Image(java.io.InputStream)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (is), :prop-types [java.io.InputStream], :prop-names-kw [:is], :prop-names-sym [is]} {:method #object[java.lang.reflect.Constructor 0x125984f0 "public javafx.scene.image.Image(java.lang.String)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url), :prop-types [java.lang.String], :prop-names-kw [:url], :prop-names-sym [url]} {:method #object[java.lang.reflect.Constructor 0x31439404 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth), :prop-types [java.lang.String double double boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth], :prop-names-sym [url requested-width requested-height preserve-ratio smooth]} {:method #object[java.lang.reflect.Constructor 0x6ff69f1 "public javafx.scene.image.Image(java.lang.String,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url backgroundLoading), :prop-types [java.lang.String boolean], :prop-names-kw [:url :background-loading], :prop-names-sym [url background-loading]} {:method #object[java.lang.reflect.Constructor 0x48b789e2 "public javafx.scene.image.Image(java.lang.String,double,double,boolean,boolean,boolean)"], :is-ctor? true, :method-name javafx.scene.image.Image, :prop-names (url requestedWidth requestedHeight preserveRatio smooth backgroundLoading), :prop-types [java.lang.String double double boolean boolean boolean], :prop-names-kw [:url :requested-width :requested-height :preserve-ratio :smooth :background-loading], :prop-names-sym [url requested-width requested-height preserve-ratio smooth background-loading]})

Then I again ran

(fn-fx.util.reflect-utils/get-value-ctors javafx.animation.KeyFrame)

and again I get an improperly formatted output (you can just see very obviously by the highlighting in the REPL):

({:method #object[java.lang.reflect.Constructor 0x1b76ab09 "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x6d22353a "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0xc1f4536 "public javafx.animation.KeyFrame(javafx.util.Duration,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x7af44995 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,javafx.animation.KeyValue[])"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler [Ljavafx.animation.KeyValue;], :prop-names-kw [], :prop-names-sym []} {:method #object[java.lang.reflect.Constructor 0x54f49b21 "public javafx.animation.KeyFrame(javafx.util.Duration,java.lang.String,javafx.event.EventHandler,java.util.Collection)"], :is-ctor? true, :method-name javafx.animation.KeyFrame, :prop-names (), :prop-types [javafx.util.Duration java.lang.String javafx.event.EventHandler java.util.Collection], :prop-names-kw [], :prop-names-sym []})

Again there is this stray comma near the beginning where it reads [Ljavafx.animation.KeyValue;]

Btw, the issue you linked has been closed. So can I just change :dependencies [[halgari/fn-fx "0.4.0"] ... to :dependencies [[halgari/fn-fx "0.4.0-SNAPSHOT"] ... ?

Or should it be [pauld/fn-fx "0.5.0-SNAPSHOT"] ?? (that's what I'm seeing on Clojars)

Sorry for the very beginner-level questions.. I'm not very experienced with Clojure. And this KeyFrame issue was actually a bit of a show stopper for the project I was trying to do a few months back.

Thanks for all the renewed work on fn-fx. It's looking great :))

Yeah sorry for the poor developer experience. Now that we have the 0.5.0-SNAPSHOT out (i.e. the fix for issue #43) it should have improved - you should be able to just start a REPL and reproduce the issue there.

To start a lein-try REPL:

$ lein try fn-fx/fn-fx-openjfx11 "0.5.0-SNAPSHOT"   # If you're running JDK 11
$ lein try fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"      # If you're running on JDK 7u6-10

Or, if you'd rather not use lein-try, you could create a new folder and put a project.clj file in it, something like this:

(defproject geokon-gh/my-fn-fx-project "0.1.0-SNAPSHOT"
  :description      "My fn-fx project"
  :min-lein-version "2.8.1"
  :dependencies     [[org.clojure/clojure "1.9.0"]
                     [fn-fx/fn-fx-openjfx11 "0.5.0-SNAPSHOT"]    ; If you're running JDK 11
                     [fn-fx/fn-fx-javafx "0.5.0-SNAPSHOT"]       ; If you're running JDK 7u6-10
])

Then start a vanilla REPL:

$ lein repl
nREPL server started on port 55554 on host 127.0.0.1 - nrepl://127.0.0.1:55554
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
OpenJDK 64-Bit Server VM 11.0.1+13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> 

Either way, once you're in a REPL you should be able to get started on reproducing your issue via standard fn-fx API calls:

user=> (require '[fn-fx.fx-dom :as dom])
nil
user=> (require '[fn-fx.controls :as ui])
nil
user=> ; Start reproing here

I wish I could help you more with the fn-fx specific parts here, but I'm a complete n00b with the library and JavaFX myself. In fact my selfish motivation in helping to resurrect the project is to learn it! 😉

thanks for those tips @pmonks! Always a bit hard to find the right workflow for things like this.

My first 2 posts kinda illustrate as far as I can understand the picture - but I hit a bit of wall after that. Hopefully you can more sense of things

Okay, now that I have things working again, maybe I can take a stab at this again :)

Have you been able to regenerate the method-arg-info.edn file @pmonks ? And.. have there not been any changes to the JavaFX API between Java 8 and 11? B/c I'm seeing just one method-arg-info.edn