jrubyfx-fxmlloader 0.2-java → 0.3-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -1,5 +1,17 @@
1
- == FXMLLoader
1
+ # JRubyFX-FxmlLoader
2
2
 
3
- Use with JRubyFX.
3
+ JRubyFX-FxmlLoader is the JIT-enabled FXML parser for the JRubyFX project based
4
+ on the official OpenJDK 8 FXML parser. FxmlLoader strives to be a nearly drop-in
5
+ replacement FxmlLoader, only differing in JRuby compatibility.
4
6
 
5
- rake gem to build
7
+ At this point,
8
+ most code is directly copied from OpenJDK and converted to ruby via regexes and
9
+ as such is far from idiomatic ruby. The JIT compiler is also a work in progress.
10
+
11
+ If you would like to help clean up the code please do but be careful about breaking
12
+ FXML. Any 100% standard Java FXML document should work *identically* when run as a JRuby FXML document and
13
+ vice-versa with the exception of JRuby support (like ruby classes & controllers).
14
+
15
+ Use with JRubyFX!
16
+
17
+ rake gem to build. Note that half of this project is java and must be compiled.
data/Rakefile CHANGED
@@ -45,7 +45,7 @@ task :gem => :jar
45
45
 
46
46
  spec = Gem::Specification.new do |s|
47
47
  s.name = 'jrubyfx-fxmlloader'
48
- s.version = '0.2'
48
+ s.version = '0.3'
49
49
  s.platform = 'java'
50
50
  s.has_rdoc = true
51
51
  s.extra_rdoc_files = ['README', 'LICENSE']
Binary file
@@ -49,16 +49,19 @@ class Element
49
49
  collection = false;
50
50
  end
51
51
  end
52
- dputs callz + "Is it a collection? #{collection.inspect} <= #{@value.java_class}"
53
52
  return collection;
54
53
  end
55
54
 
56
- def add(element)
57
- dputs callz + "about to add #{element} to #{value.to_java}"
55
+ def add(element, prop_name=nil, rputs_elt=nil)
58
56
  # If value is a list, add element to it; otherwise, get the value
59
57
  # of the default property, which is assumed to be a list and add
60
58
  # to that (coerce to the appropriate type)
61
59
  if (@value.kind_of?(Enumerable) || @value.java_kind_of?(java.util.List))
60
+ if prop_name == nil
61
+ rputs value, "add(#{rget(element)||element.inspect})"
62
+ else
63
+ rputs @parent.value, "get#{prop_name[0].upcase}#{prop_name[1..-1]}.add(#{rputs_elt || rget(element)||element.inspect})"
64
+ end
62
65
  value.to_java
63
66
  else
64
67
  type = value.java_class
@@ -69,10 +72,11 @@ class Element
69
72
  list = getProperties[defaultPropertyName]
70
73
 
71
74
  # Coerce the element to the list item type
72
- if (!Map.java_class.assignable_from?(type))
75
+ if (!java.util.Map.java_class.assignable_from?(type))
73
76
  listType = @valueAdapter.getGenericType(defaultPropertyName);
74
77
  element = RubyWrapperBeanAdapter.coerce(element, RubyWrapperBeanAdapter.getListItemType(listType));
75
78
  end
79
+ rputs @value, "get#{defaultPropertyName[0].upcase}#{defaultPropertyName[1..-1]}.add(#{rget(element)||element.inspect})"
76
80
  list = list.to_java if list.class == Java::JavaObject
77
81
  list
78
82
  end.add(element)
@@ -94,38 +98,17 @@ class Element
94
98
  end
95
99
 
96
100
  def updateValue(value)
97
- dputs callz + "Updating value from #{@value} to #{value.class}"
98
101
  @value = value;
99
102
  @valueAdapter = nil;
100
103
  end
101
104
 
102
105
  def isTyped()
103
- dputs callz + "checking if typed"
104
- dp @value.class
105
- dp @value
106
- q = !(@value.java_kind_of? Java::java.util.Map or @value.is_a? Hash );
107
- dputs callz + "type result: #{q.inspect}"
108
- q
106
+ !(@value.java_kind_of? Java::java.util.Map or @value.is_a? Hash )
109
107
  end
110
108
 
111
109
  def getValueAdapter()
112
110
  if (@valueAdapter == nil)
113
- dputs callz + "trying to get itqq"
114
- dprint callz
115
- dprint @value
116
- dputs @value.class
117
- dputs caller
118
- if @value.class.to_s == "Java::JavafxFxml::ObjectBuilder"
119
- dputs caller
120
- end
121
- if @value.is_a? java.lang.Object
122
- dputs callz + "trying to call wrapper"
123
- @valueAdapter = RubyWrapperBeanAdapter.new(@value);
124
- else
125
- dputs callz + "trying to call ruby object wrapper"
126
- @valueAdapter = RubyObjectWrapperBeanAdapter.new(@value)
127
- end
128
- dputs callz + "got"
111
+ @valueAdapter = RubyWrapperBeanAdapter.for(@value)
129
112
  end
130
113
  return @valueAdapter;
131
114
  end
@@ -172,7 +155,6 @@ class Element
172
155
  if (@loadListener != nil)
173
156
  @loadListener.readEventHandlerAttribute(localName, value);
174
157
  end
175
- dputs callz + "found eventHandler prefix #{prefix}, #{localName}, #{value}"
176
158
  eventHandlerAttributes <<(Attribute.new(localName, nil, value));
177
159
  else
178
160
  i = localName.rindex('.');
@@ -183,15 +165,12 @@ class Element
183
165
  @loadListener.readPropertyAttribute(localName, nil, value);
184
166
  end
185
167
 
186
- dputs callz + "found property attrib #{prefix}, #{localName}, #{value}"
187
168
  instancePropertyAttributes << (Attribute.new(localName, nil, value));
188
169
  else
189
170
  # The attribute represents a static property
190
171
  name = localName[(i + 1)..-1];
191
172
  sourceType = parentLoader.getType(localName[0, i]);
192
173
 
193
- dputs callz + "found static property #{prefix}, #{localName}, #{value}"
194
- dputs callz + "and its #{sourceType}, #{staticLoad}"
195
174
  if (sourceType != nil)
196
175
  if (@loadListener != nil)
197
176
  @loadListener.readPropertyAttribute(name, sourceType, value);
@@ -217,7 +196,6 @@ class Element
217
196
  def processPropertyAttribute(attribute)
218
197
  value = attribute.value;
219
198
  if (isBindingExpression(value))
220
- dputs callz + "is a binding !"
221
199
  # Resolve the expression
222
200
 
223
201
  if (attribute.sourceType != nil)
@@ -235,38 +213,28 @@ class Element
235
213
  end
236
214
 
237
215
  value = value[2..-2] # TODO: BINDING_EXPRESSION_PREFIX == ${
238
- #value.length() - 1];
216
+ #value.length() - 1];
239
217
  # TODO: this only works for 7, not 8
240
- dputs "getting expression value of '#{value}' with #{parentLoader.namespace}"
241
218
  expression = Expression.valueOf(value);
242
- dputs callz + "got the expression as '#{expression}'"
243
- dp expression
244
219
  # Create the binding
245
220
  targetAdapter = RubyWrapperBeanAdapter.new(@value);
246
- dputs callz + "target adapter is #{targetAdapter} from #{value}"
247
221
  propertyModel = targetAdapter.getPropertyModel(attribute.name).to_java
248
222
  type = targetAdapter.getType(attribute.name);
249
- dputs callz + "prop model is #{propertyModel.inspect} and type is #{type.inspect}"
250
223
  if (propertyModel.is_a? Property)
251
- dputs callz + "checking out value using #{parentLoader.namespace}"
224
+ rputs @value, "#{attribute.name}Property.bind(RRExpressionValue.new(__local_namespace, Java::org.jruby.jfx8.Expression.valueOf(#{value.inspect}), Java::#{type.name.gsub(/[\$\.]/, "::")}.java_class))"
252
225
  #expression.value_property.addListener(JRExpressionTargetMapping.new(expression, getProperties(), Expression.split(value)));
253
226
  ( propertyModel).bind(RRExpressionValue.new(parentLoader.namespace, expression, type));
254
- dputs callz + "bound!"
255
227
  end
256
228
  elsif (isBidirectionalBindingExpression(value))
257
229
  raise UnsupportedOperationException.new("This feature is not currently enabled.");
258
230
  else
259
- dputs callz + "processing 3 for #{attribute.sourceType}, #{attribute.name}, #{value}"
260
231
  processValue3(attribute.sourceType, attribute.name, value);
261
232
  end
262
233
  end
263
234
 
264
235
  def isBindingExpression(aValue)
265
- dputs callz + "checking if '#{aValue}' is a binding prefix..."
266
236
  # TODO: BINDING_EXPRESSION_PREFIX == ${
267
- q = aValue.start_with?("${") && aValue.end_with?(FXL::BINDING_EXPRESSION_SUFFIX);
268
- dputs callz + "it is? #{q.inspect}"
269
- q
237
+ aValue.start_with?("${") && aValue.end_with?(FXL::BINDING_EXPRESSION_SUFFIX);
270
238
  end
271
239
 
272
240
  def isBidirectionalBindingExpression(aValue)
@@ -279,7 +247,6 @@ class Element
279
247
  processed = false;
280
248
  #process list or array first
281
249
  if (sourceType == nil && isTyped())
282
- dputs callz + "getting value adptr"
283
250
  lvalueAdapter = getValueAdapter();
284
251
  type = lvalueAdapter.getType(propertyName);
285
252
 
@@ -288,9 +255,8 @@ class Element
288
255
  dp sourceType, propertyName, aValue
289
256
  dp lvalueAdapter
290
257
  dp caller
291
- raise PropertyNotFoundException.new("Property \"" + propertyName + "\" does not exist" + " or is read-only.");
258
+ raise("Property \"" + propertyName + "\" does not exist" + " or is read-only.");
292
259
  end
293
- dputs "checking assignable"
294
260
  if (List.java_class.assignable_from?(type) && lvalueAdapter.read_only?(propertyName))
295
261
  populateListFromString(lvalueAdapter, propertyName, aValue);
296
262
  processed = true;
@@ -299,11 +265,8 @@ class Element
299
265
  processed = true;
300
266
  end
301
267
  end
302
- dputs callz + "276"
303
268
  if (!processed)
304
- dputs callz + "Must appky it"
305
269
  applyProperty(propertyName, sourceType, resolvePrefixedValue(aValue));
306
- dputs callz + "280"
307
270
  processed = true;
308
271
  end
309
272
  return processed;
@@ -336,7 +299,11 @@ class Element
336
299
  return aValue;
337
300
  else
338
301
  begin
339
- return (aValue[0] == '/') ? classLoader.getResource(aValue[1..-1]).to_s : URL.new(parentLoader.location, aValue).to_s
302
+ if $JRUBYFX_AOT_COMPILING
303
+ return RelativeFXMLString.new(aValue, URL.new(parentLoader.location, aValue).to_s)
304
+ else
305
+ return (aValue[0] == '/') ? classLoader.getResource(aValue[1..-1]).to_s : URL.new(parentLoader.location, aValue).to_s
306
+ end
340
307
  rescue MalformedURLException => e
341
308
  dp e
342
309
  dputs "#{parentLoader.location} + /+ #{aValue}"
@@ -375,10 +342,8 @@ class Element
375
342
  # The attribute value is nil
376
343
  return nil;
377
344
  end
378
- dputs callz + "Getting expression '#{aValue}'"
379
345
  # remove all nils, them add one in at the end so [0] returns nil if empty
380
346
  q = (KeyPath.parse(aValue).map{|i|parentLoader.namespace[i]} - [nil] + [nil])[0]
381
- dputs callz + "Parsed Expression! #{q};;;;#{q.inspect}"
382
347
  return q
383
348
  end
384
349
  return aValue;
@@ -482,7 +447,6 @@ class Element
482
447
  if (attrValue.length() == 0)
483
448
  raise LoadException.new("Missing expression reference.");
484
449
  end
485
- dputs callz + "exprValue!' #{attrValue}'"
486
450
  expression = Expression.get(@namespace, KeyPath.parse(attrValue));
487
451
  if (expression.is_a? EventHandler)
488
452
  eventHandler = expression;
@@ -490,12 +454,12 @@ class Element
490
454
 
491
455
  end
492
456
  if (eventHandler == nil)
493
- if (attrValue.length() == 0 || @scriptEngine == nil)
457
+ if (attrValue.length() == 0 || parentLoader.scriptEngine == nil)
494
458
  raise LoadException.new("Error resolving " + attribute.name + "='" + attribute.value +
495
459
  "', either the event handler is not in the Namespace or there is an error in the script.");
496
460
  end
497
461
 
498
- eventHandler = ScriptEventHandler.new(attrValue, @scriptEngine);
462
+ eventHandler = ScriptEventHandler.new(attrValue, parentLoader.scriptEngine);
499
463
  end
500
464
  # Add the handler
501
465
  if (eventHandler != nil)
@@ -546,6 +510,7 @@ end
546
510
 
547
511
  class EventHandlerWrapper
548
512
  include EventHandler
513
+ attr_reader :funcName
549
514
  def initialize(ctrl, funcName)
550
515
  @ctrl = ctrl
551
516
  @funcName = funcName
@@ -558,7 +523,51 @@ class EventHandlerWrapper
558
523
  @ctrl.send(@funcName, eventArgs)
559
524
  end
560
525
  else
561
- dputs "Warning: method #{@funcName} was not found on controller #{@ctrl}"
526
+ puts "Warning: method #{@funcName} was not found on controller #{@ctrl}"
527
+ end
528
+ end
529
+ end
530
+
531
+ class ScriptEventHandler
532
+ include EventHandler
533
+ attr_reader :script, :scriptEngine
534
+ def initialize(script, scriptEngine)
535
+ @script = script;
536
+ @scriptEngine = scriptEngine;
537
+ end
538
+
539
+ def handle(event)
540
+ # Don't pollute the page namespace with values defined in the script
541
+ engineBindings = @scriptEngine.getBindings(ScriptContext::ENGINE_SCOPE);
542
+ localBindings = @scriptEngine.createBindings();
543
+ localBindings.put(FXL::EVENT_KEY, event);
544
+ @scriptEngine.setBindings(localBindings, ScriptContext::ENGINE_SCOPE);
545
+
546
+ # Execute the script
547
+ begin
548
+ @scriptEngine.eval(@script);
549
+ rescue ScriptException => exception
550
+ raise exception
562
551
  end
552
+
553
+ # Restore the original bindings
554
+ @scriptEngine.setBindings(engineBindings, ScriptContext::ENGINE_SCOPE);
563
555
  end
564
- end
556
+ end
557
+
558
+ class RelativeFXMLString < String
559
+ alias :super_inspect :inspect
560
+ def initialize(str, rel)
561
+ super(rel)
562
+ @rel = str
563
+ end
564
+ def inspect
565
+ "java.net.URL.new(__local_namespace['location'], #{@rel.inspect}).to_s"
566
+ end
567
+ def to_s
568
+ super
569
+ end
570
+ def class()
571
+ String
572
+ end
573
+ end
@@ -0,0 +1,158 @@
1
+ # * Copyright (c) 2013 Patrick Plenefisch
2
+ # * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3
+ # *
4
+ # * This code is free software; you can redistribute it and/or modify it
5
+ # * under the terms of the GNU General Public License version 2 only, as
6
+ # * published by the Free Software Foundation. Oracle designates this
7
+ # * particular file as subject to the "Classpath" exception as provided
8
+ # * by Oracle in the LICENSE file that accompanied this code.
9
+ # *
10
+ # * This code is distributed in the hope that it will be useful, but WITHOUT
11
+ # * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
+ # * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
+ # * version 2 for more details (a copy is included in the LICENSE file that
14
+ # * accompanied this code).
15
+ # *
16
+ # * You should have received a copy of the GNU General Public License version
17
+ # * 2 along with this work; if not, write to the Free Software Foundation,
18
+ # * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ # *
20
+
21
+ require 'digest/sha1'
22
+ require 'fileutils'
23
+
24
+ def javafx
25
+ Java::javafx
26
+ end
27
+
28
+ Infinity = 1.0/0.0
29
+
30
+ class FxmlJitInfo
31
+ include JRubyFX
32
+ def self.hash(file)
33
+ Digest::SHA1.hexdigest(File.read file)
34
+ end
35
+ def self.load_aot(file, cache, validate = true)
36
+ if validate
37
+ hash = hash(file)
38
+ f_hash = File.open(cache, "r", &:readline).strip
39
+ return false if "# #{hash} encoding: utf-8" != f_hash
40
+ end
41
+ hash = Digest::SHA1.hexdigest(File.basename file)
42
+ require cache
43
+ JRubyFX::GeneratedAssets.const_get("AOT#{hash}").new rescue nil
44
+ end
45
+ # TODO: store jit settings in here instead of $RB_* variables
46
+ attr_accessor :file_name, :raw_code, :jit_settings
47
+ def initialize(file_name, jit_settings=1, outfile=nil, cache_dir=nil,validate = true, opts = nil)
48
+ @file_name = file_name
49
+ @no_write = (opts && opts[:no_write]) || false
50
+ if @file_name.start_with? "file:"
51
+ @file_name = @file_name.gsub(/^file\:/, '')
52
+ elsif @file_name.start_with? "jar:"
53
+ @no_write = true
54
+ elsif @file_name.start_with? "compoundjar:"
55
+ @no_write = true
56
+ end
57
+ @jit_settings = jit_settings
58
+ @run_count = 0
59
+ @opts = opts
60
+ @outfile = if @outfile
61
+ outfile
62
+ else
63
+ cache_dir = cache_dir || File.join(File.dirname(@file_name), ".jrubyfx_cache")
64
+ FileUtils.mkpath(cache_dir) unless File.directory?(cache_dir) or @no_write
65
+ @f_hash = Digest::SHA1.hexdigest(File.basename @file_name)
66
+ File.join(cache_dir, "#{@f_hash}.rb")
67
+ end
68
+ if File.exist?(@outfile) && !(opts && opts[:force])
69
+ @compiled = self.class.load_aot(@file_name, @outfile, validate)
70
+ if @compiled
71
+ dputs "got #{file_name} from cache"
72
+ end
73
+ end
74
+ end
75
+ def hash
76
+ FxmlJitInfo.hash(@file_name)
77
+ end
78
+ def should_jit?
79
+ return false if @jit_settings == :no_jit || compiled?
80
+ return true if (@run_count += 1) >= @jit_settings
81
+ end
82
+ def compiled?
83
+ !!@compiled
84
+ end
85
+ def decompile
86
+ @compiled = nil
87
+ end
88
+ def __build_via_jit(__local_fxml_controller, __local_namespace, __local_jruby_ext)
89
+ @compiled.__build_via_jit(__local_fxml_controller, __local_namespace, __local_jruby_ext)
90
+ end
91
+ def compile(code=@raw_code)
92
+ @raw_code = code
93
+ # TODO: begin rescue end
94
+ full_code = <<METHOD_DEF
95
+ def __build_via_jit(__local_fxml_controller, __local_namespace, __local_jruby_ext)
96
+ __local_fx_id_setter = lambda do |name, __i|
97
+ __local_namespace[name] = __i
98
+ __local_fxml_controller.instance_variable_set(("@\#{name}").to_sym, __i)
99
+ end
100
+ #{code}
101
+ end
102
+ METHOD_DEF
103
+ ;#)
104
+ unless @no_write
105
+ begin
106
+ jit_aot_cache(full_code)
107
+ rescue
108
+ p $!
109
+ jit_no_cache(full_code)
110
+ end
111
+ else
112
+ jit_no_cache(full_code)
113
+ end
114
+ if @opts && @opts[:compiled_hook]
115
+ @opts[:compiled_hook].call(@outfile)
116
+ end
117
+
118
+ end
119
+
120
+ def jit_no_cache(full_code)
121
+ dputs "JIT only, no aot for #{@file_name}"
122
+ self.instance_eval full_code
123
+ @compiled = true
124
+ end
125
+
126
+ def jit_aot_cache(full_code)
127
+ File.open(@outfile, "w") do |f|
128
+ hash = hash()
129
+ f << <<AOT
130
+ # #{hash} encoding: utf-8
131
+ # @@ 1
132
+
133
+ ########################### DO NOT MODIFY THIS FILE ###########################
134
+ # This file was automatically generated by JRubyFX-fxmlloader on #
135
+ # #{Time.now} for #{@file_name}
136
+ ########################### DO NOT MODIFY THIS FILE ###########################
137
+
138
+ module JRubyFX
139
+ module GeneratedAssets
140
+ class AOT#{@f_hash}
141
+ include JRubyFX
142
+ #{full_code}
143
+ def hash
144
+ #{hash.inspect}
145
+ end
146
+ def compiled?
147
+ true
148
+ end
149
+ end
150
+ end
151
+ end
152
+ AOT
153
+ end
154
+ puts "saved #{@outfile} to cache"
155
+ require @outfile
156
+ @compiled = JRubyFX::GeneratedAssets.const_get("AOT#{@f_hash}").new rescue nil
157
+ end
158
+ end