ruboto 0.8.1 → 0.9.0.rc.0

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.
@@ -1,462 +0,0 @@
1
- ######################################################
2
- #
3
- # generate.rb (by Scott Moyer)
4
- #
5
- # Uses the dexmaker project
6
- # (http://code.google.com/p/dexmaker/)
7
- # to generate Ruboto callbacks.
8
- #
9
- ######################################################
10
-
11
- require 'ruboto/base'
12
- require 'fileutils'
13
-
14
- ######################################################
15
- #
16
- # Expand the functionality of TypeId
17
- #
18
-
19
- java_import 'com.google.dexmaker.TypeId'
20
- class TypeId
21
- @@convert_hash = {
22
- nil => VOID,
23
- "java.lang.String" => STRING,
24
- "java.lang.Object" => OBJECT
25
- }
26
-
27
- @@corresponding_class = {
28
- INT => get("Ljava/lang/Integer;"),
29
- FLOAT => get("Ljava/lang/Float;"),
30
- DOUBLE => get("Ljava/lang/Double;"),
31
- BYTE => get("Ljava/lang/Byte;"),
32
- BOOLEAN => get("Ljava/lang/Boolean;"),
33
- CHAR => get("Ljava/lang/Char;"),
34
- SHORT => get("Ljava/lang/Short;"),
35
- LONG => get("Ljava/lang/Long;")
36
- }
37
-
38
- @@conversion_method = {}
39
-
40
- %w(int float double byte boolean char short long).each do |i|
41
- @@convert_hash[i] = const_get(i.upcase)
42
- @@conversion_method[const_get(i.upcase)] = "#{i}Value"
43
- end
44
-
45
- def self.convert_type(type)
46
- rv = @@convert_hash[type]
47
- unless rv
48
- rv = type.split("[")
49
- unless rv[-1].length == 1
50
- rv[-1] = rv[-1].gsub('.', '/')
51
- unless rv[-1] =~ /^L.*;$/
52
- rv[-1] = "L#{rv[-1]};"
53
- end
54
- end
55
- rv = get(rv.join("["))
56
- end
57
- rv
58
- end
59
-
60
- def corresponding_class
61
- @@corresponding_class[self]
62
- end
63
-
64
- def conversion_method
65
- @@conversion_method[self]
66
- end
67
-
68
- def primitive?
69
- @@corresponding_class.key? self
70
- end
71
- end
72
-
73
- ######################################################
74
- #
75
- # Helper methods for class generation
76
- #
77
-
78
- java_import 'com.google.dexmaker.Code'
79
- class Code
80
- def call_super(class_id, method_name, return_value, *parameters)
81
- method_id = class_id.getMethod(return_value ? return_value.type : TypeId::VOID,
82
- method_name, *(parameters.map{|i| i.type}))
83
- invokeSuper(method_id, return_value, getThis(class_id), *parameters)
84
- end
85
- end
86
-
87
- def create_constructor(dex_maker, super_class, class_id, *parameters)
88
- constructor_id = class_id.getConstructor(*parameters)
89
- dex_maker.declare(constructor_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
90
- parameter_array = []
91
- parameters.each_with_index{|param, i| parameter_array << getParameter(i, param)}
92
-
93
- invokeDirect(super_class.getConstructor(*parameters), nil, getThis(class_id), *parameter_array)
94
- returnVoid
95
- end
96
- end
97
-
98
- ######################################################
99
- #
100
- # Generate a new classes for an interface or class
101
- # Takes a hash
102
- # :use_cache (default true) or regenerate
103
- # :reload (default false) or use existing constant
104
- # :dex_file - where to put the generated jar
105
- # -- for single classes default to use package as path
106
- # -- for multiple classes default to classes.jar
107
- # The remaining key/value pairs represent source => new_class
108
- # -- source is either a string or the actual class/interface
109
- # -- new_class is a string "package.class"
110
- #
111
-
112
- def ruboto_generate(options)
113
- use_cache = options.key?(:use_cache) ? options.delete(:use_cache) : true
114
- reload = options.key?(:reload) ? options.delete(:reload) : false
115
- dex_file = options.delete(:dex_file)
116
-
117
- #
118
- # Already loaded? Just check the first one.
119
- #
120
-
121
- class_name = options.values[0].split('.')[-1]
122
- return Object.const_get(class_name) if Object.const_defined?(class_name) and not reload
123
-
124
- #
125
- # Set up directory
126
- #
127
-
128
- base_dir = "#{$activity.files_dir.absolute_path}/dx"
129
- if dex_file
130
- components = dex_file.split('/')
131
- components.unshift(base_dir) unless components[0] == ""
132
- dex_dir = components[0..-2].join('/')
133
- dex_file = components[-1]
134
- elsif options.size == 1
135
- dex_dir = "#{base_dir}/#{options.values[0].split('.')[0..-2].join('/')}"
136
- dex_file = "#{options.values[0].split('.')[-1]}.jar"
137
- else
138
- dex_dir = base_dir
139
- dex_file = "classes.jar"
140
- end
141
- FileUtils.mkpath dex_dir unless File.exists?(dex_dir)
142
- jar_file = java.io.File.new("#{dex_dir}/#{dex_file}")
143
- puts "Exists: #{jar_file}" if File.exists?(jar_file.to_s)
144
-
145
- #
146
- # Already generated?
147
- #
148
-
149
- if use_cache
150
- rv = ruboto_load_class(jar_file.path, *options.values)
151
- return rv if rv
152
- end
153
-
154
- if File.exists? jar_file.path
155
- File.delete jar_file.path
156
- File.delete jar_file.path.gsub(/\.jar$/, ".dex")
157
- end
158
-
159
- puts "Generating: #{jar_file.path}"
160
-
161
- dex_maker = com.google.dexmaker.DexMaker.new
162
- options.each{|k, v| ruboto_generate_class(dex_maker, k, v)}
163
-
164
- #
165
- # Generate and save
166
- #
167
-
168
- dex = dex_maker.generate
169
- jar_file.createNewFile
170
- jarOut = java.util.jar.JarOutputStream.new(java.io.FileOutputStream.new(jar_file))
171
- jarOut.putNextEntry(java.util.jar.JarEntry.new("classes.dex"))
172
- jarOut.write(dex)
173
- jarOut.closeEntry
174
- jarOut.close
175
-
176
- return ruboto_load_class(jar_file.path, *options.values)
177
- end
178
-
179
- ######################################################
180
- #
181
- # Open a dex jar and load the class(es)
182
- #
183
-
184
- def ruboto_load_class(file_name, *package_class_names)
185
- return nil unless File.exists? file_name
186
-
187
- loader = Java::dalvik.system.DexClassLoader.new(file_name, file_name.split('/')[0..-2].join('/'), nil,
188
- com.google.dexmaker.DexMaker.java_class.class_loader)
189
-
190
- runtime = org.jruby.Ruby.getGlobalRuntime
191
-
192
- rv = []
193
- package_class_names.each do |i|
194
- tmp = org.jruby.javasupport.Java.getProxyClass(runtime,
195
- org.jruby.javasupport.JavaClass.get(runtime, loader.loadClass(i)))
196
- Object.const_set(i.split('.')[-1], tmp)
197
- ruboto_import tmp
198
- rv << tmp
199
- end
200
-
201
- rv.length == 1 ? rv[0] : rv
202
- end
203
-
204
- ######################################################
205
- #
206
- # Does the hard work of generating one class
207
- #
208
-
209
- def ruboto_generate_class(dex_maker, interface_or_class_name, package_class_name)
210
- #
211
- # Basic set up
212
- #
213
-
214
- if interface_or_class_name.is_a?(String)
215
- interface_or_class = eval("Java::#{interface_or_class_name.gsub('$', '::')}")
216
- else
217
- interface_or_class = interface_or_class_name
218
- interface_or_class_name = interface_or_class.java_class.name
219
- end
220
- interface_or_class_id = TypeId.convert_type(interface_or_class_name)
221
- interface = interface_or_class.java_class.interface?
222
- class_type_id = TypeId.convert_type(package_class_name)
223
- parameters = [class_type_id, "#{package_class_name.split('.')[-1]}.generated", java.lang.reflect.Modifier::PUBLIC,
224
- interface ? TypeId::OBJECT : interface_or_class_id]
225
- parameters << TypeId.convert_type(interface_or_class_name) if interface
226
- dex_maker.declare(*parameters)
227
-
228
- #
229
- # Create callbacks field
230
- #
231
-
232
- callbackProcs_field = class_type_id.getField(TypeId.get("[Ljava/lang/Object;"), "callbackProcs")
233
- dex_maker.declare(callbackProcs_field, java.lang.reflect.Modifier::PRIVATE, nil)
234
-
235
- #
236
- # Build constructor and create callbacks array
237
- #
238
-
239
- if interface
240
- create_constructor dex_maker, TypeId::OBJECT, class_type_id
241
- else
242
- interface_or_class.java_class.constructors.each do |c|
243
- parameter_type_array = c.parameter_types.map{|p| TypeId.convert_type(p.name)}
244
- create_constructor dex_maker, interface_or_class_id, class_type_id, *parameter_type_array
245
- end
246
- end
247
-
248
- #
249
- # Build a list of methods
250
- #
251
-
252
- methods = []
253
- if interface
254
- methods = interface_or_class.java_class.declared_instance_methods
255
- else
256
- method_hash, klass = {}, interface_or_class
257
- while klass != nil
258
- klass.java_class.declared_instance_methods.each do |i|
259
- if (i.name[0..1] == "on" or (i.modifiers & java.lang.reflect.Modifier::ABSTRACT) != 0) and
260
- (i.modifiers & java.lang.reflect.Modifier::FINAL) == 0 and not method_hash[i.name]
261
- method_hash[i.name] = i
262
- methods << i
263
- end
264
- end
265
- klass = klass == java.lang.Object ? nil : klass.superclass
266
- end
267
- end
268
-
269
- #
270
- # Build setCallbackProc method
271
- #
272
-
273
- method_id = class_type_id.getMethod(TypeId::VOID, "setCallbackProc", TypeId::INT, TypeId::OBJECT)
274
- dex_maker.declare(method_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
275
- index = getParameter(0, TypeId::INT)
276
- block = getParameter(1, TypeId::OBJECT)
277
- array = newLocal(TypeId.get("[Ljava/lang/Object;"))
278
- size = newLocal(TypeId::INT)
279
- null = newLocal(TypeId::OBJECT)
280
-
281
- array_exists = com.google.dexmaker.Label.new
282
-
283
- # Does the calback proc array exist yet?
284
- iget(callbackProcs_field, array, getThis(class_type_id))
285
- loadConstant(null, nil)
286
- compare(com.google.dexmaker.Comparison::NE, array_exists, array, null)
287
-
288
- # Create the array the first time
289
- loadConstant(size, methods.length)
290
- newArray(array, size)
291
- iput(callbackProcs_field, getThis(class_type_id), array)
292
-
293
- mark(array_exists)
294
- aput(array, index, block)
295
-
296
- returnVoid
297
- end
298
-
299
- #
300
- # Loop through and build a constant and method for each method in the interface
301
- #
302
-
303
- methods.each_with_index do |m, count|
304
- # Define the constant
305
- constant_name = "CB_" + m.name.gsub(/[A-Z]/, '_\0').upcase.gsub(/^ON_/, "")
306
- const = class_type_id.getField(TypeId::INT, constant_name)
307
- dex_maker.declare(const,
308
- java.lang.reflect.Modifier::PUBLIC | java.lang.reflect.Modifier::STATIC | java.lang.reflect.Modifier::FINAL,
309
- count.to_java(:int))
310
-
311
- # Build the method
312
- parameter_type_array = m.parameter_types.map{|j| TypeId.convert_type(j.name)}
313
- method_id = class_type_id.getMethod(TypeId.convert_type(m.return_type ? m.return_type.name : nil), m.name, *parameter_type_array)
314
- dex_maker.declare(method_id, java.lang.reflect.Modifier::PUBLIC).instance_eval do
315
- parameter_array = []
316
- parameter_type_array.each_with_index{|j, k| parameter_array << getParameter(k, j)}
317
-
318
- # Callback procs array
319
- index = newLocal(TypeId::INT)
320
- array = newLocal(TypeId.get("[Ljava/lang/Object;"))
321
- block = newLocal(TypeId::OBJECT)
322
-
323
- if parameter_array.length > 1
324
- # Call arguments array
325
- p_arr = newLocal(TypeId.get("[Ljava/lang/Object;"))
326
- p_size = newLocal(TypeId::INT)
327
- p_index = newLocal(TypeId::INT)
328
- end
329
-
330
- # Holds nil for comparison
331
- null = newLocal(TypeId::OBJECT)
332
-
333
- # Holds method name
334
- call_string = newLocal(TypeId::STRING)
335
-
336
- # Locals for possible return
337
- ret = retObject = nil
338
- if m.return_type
339
- ret = newLocal(TypeId.get(m.return_type))
340
- retObject = newLocal(TypeId::OBJECT)
341
- retClass = newLocal(TypeId.convert_type("java.lang.Class"))
342
- end
343
-
344
- # Create a local to help convert primitives
345
- tmp_locals = {}
346
- parameter_type_array.each do |p|
347
- tmp_locals[p] = newLocal(p.corresponding_class) if p.primitive?
348
- end
349
- tmp_locals[ret.type] = newLocal(ret.type.corresponding_class) if ret and ret.type.primitive? and tmp_locals[ret.type] == nil
350
-
351
- no_block = com.google.dexmaker.Label.new
352
- done = com.google.dexmaker.Label.new
353
-
354
- # Does the calback proc array exist yet?
355
- iget(callbackProcs_field, array, getThis(class_type_id))
356
- loadConstant(null, nil)
357
- compare(com.google.dexmaker.Comparison::EQ, no_block, array, null)
358
-
359
- # Do we have a callback proc?
360
- loadConstant(index, count)
361
- aget(block, array, index)
362
- compare(com.google.dexmaker.Comparison::EQ, no_block, block, null)
363
-
364
- call_super(class_type_id, m.name, ret, *parameter_array) unless interface
365
-
366
- # Prepare to call Script to call the method
367
- script_class_type_id = TypeId.convert_type("org.ruboto.JRubyAdapter")
368
- loadConstant(call_string, "call")
369
- parameter_types = [ret ? TypeId::OBJECT : TypeId::VOID, "runRubyMethod", TypeId::OBJECT, TypeId::STRING]
370
- method_parameters = [retObject, block, call_string]
371
-
372
- # Set up for different arity
373
- if parameter_array.length == 1
374
- parameter_types << TypeId::OBJECT
375
- # Cast ?
376
- p = parameter_array[0]
377
- # Need to convert primitives to add to array
378
- if p.type.primitive?
379
- newInstance(tmp_locals[p.type], p.type.corresponding_class.getConstructor(p.type), p)
380
- method_parameters << tmp_locals[p.type]
381
- else
382
- method_parameters << p
383
- end
384
- elsif parameter_array.length > 1
385
- # Create and populate an array for method parameters
386
- loadConstant(p_size, parameter_type_array.length.to_java(:int))
387
- newArray(p_arr, p_size)
388
- parameter_array.each_with_index do |p, i|
389
- loadConstant(p_index, i)
390
-
391
- # Need to convert primitives to add to array
392
- if p.type.primitive?
393
- newInstance(tmp_locals[p.type], p.type.corresponding_class.getConstructor(p.type), p)
394
- aput(p_arr, p_index, tmp_locals[p.type])
395
- else
396
- aput(p_arr, p_index, p)
397
- end
398
- end
399
-
400
- parameter_types << TypeId.get("[Ljava/lang/Object;")
401
- method_parameters << p_arr
402
- end
403
-
404
- # Add return class to the call
405
- if ret
406
- parameter_types << TypeId.convert_type("java.lang.Class")
407
- loadConstant(retClass, java.lang.Boolean.java_class)
408
- method_parameters << retClass
409
- end
410
-
411
- # Make the call
412
- method_parameters = [script_class_type_id.getMethod(*parameter_types)] + method_parameters
413
- invokeStatic(*method_parameters)
414
-
415
- # Cast the return
416
- if ret and ret.type.primitive?
417
- cast(tmp_locals[ret.type], retObject)
418
- invokeVirtual(tmp_locals[ret.type].type.getMethod(ret.type, ret.type.conversion_method), ret, tmp_locals[ret.type])
419
- elsif ret
420
- # May need to just copy if type is OBJECT
421
- cast(ret, retObject)
422
- end
423
-
424
- jump(done)
425
- mark(no_block)
426
-
427
- call_super(class_type_id, m.name, ret, *parameter_array) unless interface
428
-
429
- mark(done)
430
- ret ? returnValue(ret) : returnVoid
431
- end
432
- end
433
- end
434
-
435
- ######################################################
436
- #
437
- # Clear all generated dex jars
438
- #
439
-
440
- def ruboto_clear_dex_cache
441
- FileUtils.remove_dir "#{$activity.files_dir.absolute_path}/dx"
442
- end
443
-
444
- ######################################################
445
- #
446
- # Generate classes and configure any widgets
447
- #
448
-
449
- def ruboto_generate_widget(options)
450
- ruboto_generate_widgets(options)
451
- end
452
-
453
- def ruboto_generate_widgets(options)
454
- rv = ruboto_generate(options)
455
- if rv.is_a?(Array)
456
- rv.each{|i| ruboto_import_widget i if i < android.view.View}
457
- else
458
- ruboto_import_widget rv
459
- end
460
- rv
461
- end
462
-
@@ -1,35 +0,0 @@
1
- require 'ruboto/activity'
2
- require 'ruboto/widget'
3
- require 'ruboto/generate'
4
-
5
- ruboto_import_widgets :LinearLayout, :ListView, :TextView
6
-
7
- ruboto_generate("android.widget.ArrayAdapter" => $package_name + ".MyArrayAdapter")
8
-
9
- class GenerateActivity
10
- def on_create(bundle)
11
- super
12
- setTitle File.basename(__FILE__).chomp('_activity.rb').split('_').map { |s| "#{s[0..0].upcase}#{s[1..-1]}" }.join(' ')
13
-
14
- adapter = MyArrayAdapter.new(self, android.R.layout.simple_list_item_1 , AndroidIds::text1, ['Record one', 'Record two'])
15
- adapter.initialize_ruboto_callbacks do
16
- def get_view(position, convert_view, parent)
17
- puts "IN get_view!!!"
18
- @inflater ||= context.getSystemService(Context::LAYOUT_INFLATER_SERVICE)
19
- row = convert_view ? convert_view : @inflater.inflate(mResource, nil)
20
- row.findViewById(mFieldId).text = get_item(position)
21
- row
22
- rescue Exception
23
- puts "Exception getting list item view: #$!"
24
- puts $!.backtrace.join("\n")
25
- convert_view
26
- end
27
- end
28
-
29
- self.content_view =
30
- linear_layout :orientation => LinearLayout::VERTICAL do
31
- @text_view_margins = text_view :text => 'What hath Matz wrought?', :id => 42
32
- @list_view = list_view :adapter => adapter, :id => 43
33
- end
34
- end
35
- end