jrubyfx-fxmlloader 0.2-java → 0.3-java

Sign up to get free protection for your applications and to get access to all the features.
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