jrubyfx 1.2.0-java → 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,134 @@
1
+ require 'java'
2
+ require 'jruby/core_ext'
3
+
4
+ module JRubyFX::FxmlHelper
5
+ include_package 'javax.xml.stream'
6
+ include_package 'javafx.fxml'
7
+ import 'java.nio.charset.Charset'
8
+
9
+ # fxmlloader transformer, modifies clazz to be compatible with the fxml file
10
+ def self.transform(clazz, fxml_url)
11
+ packages = []
12
+ classes = {}
13
+ attribs = []
14
+
15
+ begin
16
+
17
+ # Read in the stream and set everything up
18
+
19
+ inputStream = fxml_url.open_stream
20
+ xmlInputFactory = XMLInputFactory.newFactory
21
+ xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true)
22
+
23
+ # Some stream readers incorrectly report an empty string as the prefix
24
+ # for the default namespace; correct this as needed
25
+ inputStreamReader = java.io.InputStreamReader.new(inputStream, Charset.forName("UTF-8"))
26
+ xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStreamReader)
27
+
28
+ # set up a quick map so we can easily look up java return values with jruby values for the xml events
29
+ dm = Hash[XMLStreamConstants.constants.map {|x| [XMLStreamConstants.const_get(x), x] }]
30
+
31
+ lastType = nil
32
+ while xmlStreamReader.hasNext
33
+ event = xmlStreamReader.next
34
+
35
+ case dm[event]
36
+ when :PROCESSING_INSTRUCTION
37
+ # PI's needs to be added to our package list
38
+ if xmlStreamReader.pi_target.strip == FXMLLoader::IMPORT_PROCESSING_INSTRUCTION
39
+ name = xmlStreamReader.pi_data.strip
40
+
41
+ # importing package vs class
42
+ if name.end_with? ".*"
43
+ packages << name[0...-2] # strip the .* and add to package list
44
+ else
45
+ i = name.index('.') # find the package/class split, should probably be backwards, but eh
46
+ i = name.index('.', i + 1) while i && i < name.length && name[i + 1] == name[i + 1].downcase
47
+
48
+ if i.nil? or i+1 >= name.length
49
+ raise LoadException.new(NameError.new(name))#TODO: rubyize the stack trace TODO: test
50
+ end
51
+
52
+ class_name = name[(i + 1)..-1]
53
+ pkg_name = "#{name[0...i]}.#{class_name.gsub('.', '$')}"
54
+
55
+ # now that we have the class name, look it up and add to the list, or if failure, assume ruby
56
+ classes[class_name] = begin
57
+ JavaUtilities.get_proxy_class(pkg_name).java_class.to_java
58
+ rescue NameError => ex # probably ruby class
59
+ begin
60
+ pkg_name.constantize_by(".")
61
+ rescue
62
+ raise LoadException.new(NameError.new(pkg_name)) # nope, not our issue anymore TODO: rubyize?
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ when :START_ELEMENT
69
+ # search start elements for ID's that we need to inject on init
70
+ lastType = xmlStreamReader.local_name
71
+
72
+ # search all atttribues for id and events
73
+ xmlStreamReader.attribute_count.times do |i|
74
+ prefix = xmlStreamReader.get_attribute_prefix(i)
75
+ localName = xmlStreamReader.get_attribute_local_name(i)
76
+ value = xmlStreamReader.get_attribute_value(i)
77
+
78
+ # if it is an id, save the id and annotate it as injectable by JavaFX. Default to object since ruby land doesn't care...
79
+ if localName == "id" and prefix == FXMLLoader::FX_NAMESPACE_PREFIX
80
+ #puts "GOT ID! #{lastType} #{value}"
81
+ attribs << value
82
+
83
+ # add the field to the controller
84
+ clazz.instance_eval do
85
+ # Note: we could detect the type, but Ruby doesn't care, and neither does JavaFX's FXMLLoader
86
+ java_field "@javafx.fxml.FXML java.lang.Object #{value}", instance_variable: true
87
+ end
88
+ # otherwise, if it is an event, add a forwarding call
89
+ elsif localName.start_with? "on" and value.start_with? "#"
90
+ mname = value[1..-1] # strip hash
91
+ aname = "jrubyfx_aliased_#{mname}"
92
+
93
+ # TODO: use java proxy rewrites
94
+ # add the method to the controller by aliasing the old method, and replacing it with our own fxml-annotated forwarding call
95
+ clazz.instance_eval do
96
+ alias_method aname.to_sym, mname.to_sym if method_defined? mname.to_sym
97
+ java_signature "@javafx.fxml.FXML void #{mname}(javafx.event.Event)"
98
+ define_method(mname) do |e|
99
+ if respond_to? aname
100
+ if method(aname).arity == 0
101
+ send(aname)
102
+ else
103
+ send(aname, e)
104
+ end
105
+ else
106
+ puts "Warning: method #{mname} was not found on controller #{self}"
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ end
113
+ end
114
+ end
115
+ # poorly dispose of stream reader
116
+ xmlStreamReader = nil
117
+
118
+ # have to jump through hoops to set the classwide list
119
+ class << clazz
120
+ define_method :__jruby_set_insts, &(lambda {|list|
121
+ @__jrubyfx_fxml_ids = list
122
+ })
123
+ define_method :__jruby_get_insts, &(lambda {
124
+ @__jrubyfx_fxml_ids
125
+ })
126
+ end
127
+ clazz.__jruby_set_insts(attribs)
128
+
129
+ clazz.become_java!
130
+ rescue XMLStreamException => exception
131
+ raise (exception)
132
+ end
133
+ end
134
+ end