divine 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in babelphish.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2012 Nils Franzen
2
+ Copyright (c) 2009-2010, Patrick Reiter Horn (Javascript UTF8 string encode/decode methods taken from the ProtoJS project)
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Divine
2
+
3
+ Divine provides a compact data interchange format for Ruby, Java and Javascript. Divine is similar to [Thrift](http://thrift.apache.org/) and [Protobuf](http://code.google.com/p/protobuf/), but I try to overcome of some of the shortcommings I think the other libraries have.
4
+
5
+
6
+ ## Example
7
+
8
+
9
+ ```ruby
10
+ require 'divine'
11
+
12
+ struct 'TestBasic' do
13
+ int8 :i8
14
+ int16 :i16
15
+ int32 :i32
16
+ string :str
17
+ ip_number :ip
18
+ binary :guid
19
+ end
20
+
21
+ struct 'Entry' do
22
+
23
+ end
24
+
25
+
26
+ struct(:TestComplex) {
27
+ list :list1, :int32
28
+ list :list2, :int8
29
+ map :map1, :int8, :int32
30
+ map(:map2, :string) {
31
+ list 'Entry'
32
+ }
33
+ }
34
+
35
+ Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb', module: 'BabelTest', parent_class: "Object")
36
+ Divine::CodeGenerator.new.generate(:javascript, file: 'test_babel.js')
37
+ ```
38
+
39
+ The resulting _test_babel.rb_ contains the generated source code for the defined structs. Below is an example on how to use the generated code in ruby
40
+
41
+ ```ruby
42
+ require_relative 'test_babel.rb'
43
+
44
+ t1 = BabelTest::TestBasic.new
45
+ t1.i8 = 0x0F
46
+ t1.i16 = 0X1234
47
+ t1.i32 = 0x567890AB
48
+ t1.str = "abc\n123\tåäö´+'\""
49
+ t1.ip = "192.168.0.1"
50
+ t1.guid = (0..255).to_a
51
+
52
+ p1 = BabelTest::TestComplex.new
53
+ p1.list1 = [0, 1, 255, 0x7FFFFFFF, 0x7FFFFFFF+1, 0xFFFFFFFE, 0xFFFFFFFF]
54
+ p1.list2 = [0,1, 15,16, 127,128, 254,255]
55
+ p1.map1[0] = 10
56
+ p1.map1[10] = 100
57
+ p1.map2["Hello"] = [BabelTest::Entry.new, BabelTest::Entry.new]
58
+
59
+
60
+ data1 = t1.serialize
61
+ data2 = p1.serialize
62
+ File.open("bin.babel", "w+b") do |f|
63
+ f.write(data1)
64
+ f.write(data2)
65
+ end
66
+
67
+ mem_buf = File.new('bin.babel').binmode
68
+ t2 = BabelTest::TestBasic.new
69
+ t2.deserialize mem_buf
70
+
71
+ p2 = BabelTest::TestComplex.new
72
+ p2.deserialize mem_buf
73
+ ```
74
+
75
+ And a javascript example that uses the generated file _test_babel.js_
76
+
77
+ ```javascript
78
+ var c1 = new TestComplex();
79
+ c1.list1 = [0, 1, 255, 0x7FFFFFFF, 0x7FFFFFFF+1, 0xFFFFFFFE, 0xFFFFFFFF];
80
+ c1.list2 = [0,1, 15,16, 127,128, 254,255];
81
+
82
+ c1.map1[0] = 10;
83
+ c1.map1[10] = 100;
84
+
85
+ c1.map2["FooBar"] = [new Entry(), new Entry()];
86
+
87
+
88
+ console.log("SERIALIZE");
89
+ var ca = c1.serialize();
90
+
91
+ console.log("DESERIALIZE");
92
+ var c2 = new TestComplex();
93
+ c2.deserialize(new BabelDataReader(ca));
94
+ console.log(c2);
95
+ ```
96
+
97
+
98
+ ## Installation
99
+
100
+ Add this line to your application's Gemfile:
101
+
102
+ gem 'divine'
103
+
104
+ And then execute:
105
+
106
+ $ bundle
107
+
108
+ Or install it yourself as:
109
+
110
+ $ gem install divine
111
+
112
+ ## Usage
113
+
114
+ TODO: Write usage instructions here
115
+
116
+ ## Contributing
117
+
118
+ 1. Fork it
119
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin my-new-feature`)
122
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/divine.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'divine/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "divine"
8
+ gem.version = Divine::VERSION
9
+ gem.authors = ["Nils Franzen"]
10
+ gem.email = ["nils@franzens.org"]
11
+ gem.description = %q{A simple data serialization generator for java, ruby and javascript}
12
+ gem.summary = %q{A simple data serialization generator for java, ruby and javascript}
13
+ gem.homepage = "https://github.com/franzen/babelphish"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency(%q<docile>, [">= 1.0.0"])
21
+ gem.add_dependency(%q<erubis>, [">= 2.7.0"])
22
+ end
data/lib/divine.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'erubis'
2
+ require 'docile'
3
+
4
+ require "divine/version"
5
+ require "divine/dsl"
6
+ require "divine/code_generators/code_generator"
7
+ require "divine/code_generators/ruby"
8
+ require "divine/code_generators/java"
9
+ require "divine/code_generators/javascript"
10
+
11
+ module Divine
12
+ end
13
+
14
+ #
15
+ # Toplevel definition of struct'
16
+ #
17
+ def struct(name, version=1, &block)
18
+ puts "struct #{name}"
19
+ builder = Divine::StructBuilder.new(name, version) # Defined in divine/dsl.rb
20
+ ::Docile.dsl_eval(builder, &block)
21
+ builder
22
+ end
@@ -0,0 +1,49 @@
1
+ module Divine
2
+ $language_generators = {}
3
+
4
+ class BabelHelperMethods
5
+ def format_src(first_indent, following_indent, is, spc = " ")
6
+ indent = "#{spc * first_indent}"
7
+ is.flatten.compact.map do |i|
8
+ case i
9
+ when :indent
10
+ indent << spc * following_indent
11
+ nil
12
+ when :deindent
13
+ indent = indent[0..-(following_indent+1)]
14
+ nil
15
+ else
16
+ "#{indent}#{i}"
17
+ end
18
+ end.compact.join("\n")
19
+ end
20
+
21
+ def get_fresh_variable_name
22
+ @vindex = (@vindex || 0xFF) + 1
23
+ return "var_#{@vindex.to_s(16)}"
24
+ end
25
+
26
+ def camelize(*str)
27
+ ss = str.map(&:to_s).join("_").split(/_/).flatten
28
+ "#{ss.first.downcase}#{ss[1..-1].map(&:downcase).map(&:capitalize).join}"
29
+ end
30
+ end
31
+
32
+ class CodeGenerator
33
+ def generate(target, opts)
34
+ gen = $language_generators[target.to_sym]
35
+ raise "Unknown taget language: #{target}" unless gen
36
+ puts "Generating code for #{target}"
37
+ src = gen.generate_code($all_structs, opts)
38
+ if opts[:file]
39
+ filename = opts[:file]
40
+ File.open(filename, 'w+') do |f|
41
+ puts "... writing #{filename}"
42
+ f.write(src)
43
+ end
44
+ else
45
+ puts src
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,414 @@
1
+ module Divine
2
+
3
+ class JavaHelperMethods < BabelHelperMethods
4
+ def java_base_class_template_str
5
+ <<EOS
6
+ abstract class BabelBase <%= toplevel_class %> {
7
+ private static final Charset UTF8 = Charset.forName("UTF-8");
8
+
9
+ public byte[] serialize() throws IOException {
10
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
11
+ serializeInternal(baos);
12
+ baos.close();
13
+ return baos.toByteArray();
14
+ }
15
+
16
+ abstract void serializeInternal(ByteArrayOutputStream baos) throws IOException;
17
+
18
+ abstract void deserialize(ByteArrayInputStream baos) throws IOException;
19
+
20
+ protected int readInt8(ByteArrayInputStream data) {
21
+ return data.read() & 0xff;
22
+ }
23
+
24
+ protected int readInt16(ByteArrayInputStream data) {
25
+ return (data.read() << 8) | readInt8(data);
26
+ }
27
+
28
+ protected int readInt24(ByteArrayInputStream data) {
29
+ return (data.read() << 16) | readInt16(data);
30
+ }
31
+
32
+ protected long readInt32(ByteArrayInputStream data) {
33
+ return (data.read() << 24) | readInt24(data);
34
+ }
35
+
36
+ protected boolean readBool(ByteArrayInputStream data) {
37
+ return readInt8(data) == 1;
38
+ }
39
+
40
+ protected String readString(ByteArrayInputStream data) throws IOException {
41
+ // Force utf8
42
+ return new String(readBytes(readInt16(data), data), UTF8);
43
+ }
44
+
45
+ private byte[] readBytes(int size, ByteArrayInputStream data) throws IOException {
46
+ byte[] bs = new byte[size];
47
+ data.read(bs);
48
+ return bs;
49
+ }
50
+
51
+ protected byte[] readBinary(ByteArrayInputStream data) throws IOException {
52
+ long c = readInt32(data);
53
+ if (c > Integer.MAX_VALUE) {
54
+ throw new IndexOutOfBoundsException("Binary data to big for java");
55
+ }
56
+ return readBytes((int) readInt32(data), data);
57
+ }
58
+
59
+ protected byte[] readShortBinary(ByteArrayInputStream data) throws IOException {
60
+ return readBytes(readInt8(data), data);
61
+ }
62
+
63
+ protected String readIpNumber(ByteArrayInputStream data) throws IOException {
64
+ byte[] ips = readShortBinary(data);
65
+ String ip = "";
66
+ for (byte b : ips) {
67
+ if (ip.length() > 0) {
68
+ ip += ".";
69
+ }
70
+ ip += (b & 0xFF);
71
+ }
72
+ return ip;
73
+ }
74
+
75
+ protected void writeInt8(int v, ByteArrayOutputStream out) {
76
+ if (v > 0xFF) { // Max 255
77
+ raiseError("Too large int8 number: " + v);
78
+ }
79
+ out.write(v);
80
+ }
81
+
82
+ protected void writeInt16(int v, ByteArrayOutputStream out) {
83
+ if (v > 0xFFFF) { // Max 65.535
84
+ raiseError("Too large int16 number: " + v);
85
+ }
86
+ writeInt8(v >> 8 & 0xFF, out);
87
+ writeInt8(v & 0xFF, out);
88
+ }
89
+
90
+ protected void writeInt24(int v, ByteArrayOutputStream out) {
91
+ if (v > 0xFFFFFF) { // Max 16.777.215
92
+ raiseError("Too large int24 number: " + v);
93
+ }
94
+ writeInt8(v >> 16 & 0xFF, out);
95
+ writeInt16(v & 0xFFFF, out);
96
+ }
97
+
98
+ protected void writeInt32(long v, ByteArrayOutputStream out) {
99
+ if (v > 0xFFFFFFFF) { // Max 4.294.967.295
100
+ raiseError("Too large int32 number: " + v);
101
+ }
102
+ writeInt8((int) ((v >> 24) & 0xFF), out);
103
+ writeInt24((int) (v & 0xFFFFFF), out);
104
+ }
105
+
106
+ protected void writeBool(boolean v, ByteArrayOutputStream out) {
107
+ writeInt8(v ? 1 : 0, out);
108
+ }
109
+
110
+ protected void writeString(String v, ByteArrayOutputStream out) throws IOException {
111
+ byte[] bs = v.getBytes(UTF8);
112
+ if (bs.length > 0xFFFF) {
113
+ raiseError("Too large string: " + bs.length + " bytes");
114
+ }
115
+ writeInt16(bs.length, out);
116
+ out.write(bs);
117
+ }
118
+
119
+ protected void writeBinary(byte[] v, ByteArrayOutputStream out) throws IOException {
120
+ if (v.length > 0xFFFFFFFF) {
121
+ raiseError("Too large binary: " + v.length + " bytes");
122
+ }
123
+ writeInt32(v.length, out);
124
+ out.write(v);
125
+ }
126
+
127
+ protected void writeShortBinary(byte[] v, ByteArrayOutputStream out) throws IOException {
128
+ if (v.length > 0xFF) {
129
+ raiseError("Too large short_binary: " + v.length + " bytes");
130
+ }
131
+ writeInt8(v.length, out);
132
+ out.write(v);
133
+ }
134
+
135
+ protected void writeIpNumber(String v, ByteArrayOutputStream out) throws IOException {
136
+ String[] bs = v.split(".");
137
+ byte[] ss = new byte[bs.length];
138
+ for (int i = 0; i < bs.length; i++) {
139
+ ss[i] = (byte) (Integer.parseInt(bs[i]) & 0xFF);
140
+ }
141
+ if (ss.length == 0 || ss.length == 4) {
142
+ writeShortBinary(ss, out);
143
+ } else {
144
+ raiseError("Unknown IP v4 number " + v); // Only IPv4 for now
145
+ }
146
+ }
147
+
148
+ protected void raiseError(String msg) {
149
+ throw new IllegalArgumentException("[" + this.getClass().getCanonicalName() + "] " + msg);
150
+ }
151
+ }
152
+ EOS
153
+ end
154
+
155
+ def java_class_template_str
156
+ <<EOS2
157
+ public class <%= c.name %> extends BabelBase {
158
+ <% unless c.fields.empty? %>
159
+ <% c.fields.each do |f| %>
160
+ public <%= this.java_get_type_declaration(f) %> <%= f.name %> = <%= this.java_get_empty_declaration(f) %>;
161
+ <% end %>
162
+ <% end %>
163
+
164
+ @Override
165
+ void serializeInternal(ByteArrayOutputStream baos) throws IOException {
166
+ <% c.simple_fields.each do |f| %>
167
+ <%= this.camelize("write", f.type) %>(this.<%= f.name %>, baos);
168
+ <% end %>
169
+ <% c.complex_fields.each do |f| %>
170
+ <%= this.java_serialize_complex f %>
171
+ <% end %>
172
+ }
173
+
174
+ @Override
175
+ void deserialize(ByteArrayInputStream bais) throws IOException {
176
+ <% c.simple_fields.each do |f| %>
177
+ this.<%= f.name %> = <%= this.camelize("read", f.type) %>(bais);
178
+ <% end %>
179
+ <% c.complex_fields.each do |f| %>
180
+ <%= this.java_deserialize_complex f %>
181
+ <% end %>
182
+ }
183
+ }
184
+ EOS2
185
+ end
186
+
187
+ def java_get_empty_declaration(types, is_reference_type = false)
188
+ if types.respond_to? :referenced_types
189
+ java_get_empty_declaration(types.referenced_types)
190
+
191
+ elsif types.respond_to? :first
192
+ case types.first
193
+ when :list
194
+ "new #{java_get_type_declaration(types, true)}()"
195
+ when :map
196
+ "new #{java_get_type_declaration(types, true)}()"
197
+ else
198
+ raise "Missing empty declaration for #{types}"
199
+ end
200
+
201
+ else
202
+ case types
203
+ when :binary, :short_binary
204
+ is_reference_type ? "new Byte[0]" : "new byte[0]"
205
+ when :int8, :int16
206
+ "0"
207
+ when :int32
208
+ "0L"
209
+ when :string, :ip_number
210
+ "\"\""
211
+ else
212
+ if $all_structs[types]
213
+ types
214
+ else
215
+ raise "Unkown field type #{types}"
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ def java_get_type_declaration(types, is_reference_type = false)
222
+ if types.respond_to? :referenced_types
223
+ java_get_type_declaration(types.referenced_types, is_reference_type)
224
+
225
+ elsif types.respond_to? :first
226
+ case types.first
227
+ when :list
228
+ subtypes = java_get_type_declaration(types[1], true)
229
+ return "ArrayList<#{subtypes}>"
230
+ when :map
231
+ key_type = java_get_type_declaration(types[1], true)
232
+ value_type = java_get_type_declaration(types[2], true)
233
+ return "HashMap<#{key_type}, #{value_type}>"
234
+ else
235
+ raise "Missing serialization for #{types}"
236
+ end
237
+
238
+ else
239
+ case types
240
+ when :binary, :short_binary
241
+ is_reference_type ? "Byte[]" : "byte[]"
242
+ when :int8, :int16
243
+ is_reference_type ? "Integer" : "int"
244
+ when :int32
245
+ is_reference_type ? "Long" : "long"
246
+ when :string, :ip_number
247
+ "String"
248
+ else
249
+ if $all_structs[types]
250
+ types
251
+ else
252
+ raise "Unkown field type #{types}"
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ def java_serialize_complex(field)
259
+ types = field.referenced_types
260
+ as = [
261
+ "// Serialize #{field.type} '#{field.name}'",
262
+ java_serialize_internal(field.name, types)
263
+ ]
264
+ format_src(2, 1, as, "\t")
265
+ end
266
+
267
+ def java_serialize_internal(var, types)
268
+ if types.respond_to? :first
269
+ case types.first
270
+ when :list
271
+ nv = get_fresh_variable_name
272
+ idx = get_fresh_variable_name
273
+ return [
274
+ "writeInt32(#{var}.size(), baos);",
275
+ "for(int #{idx}=0; #{idx}<#{var}.size(); #{idx}++) {",
276
+ :indent,
277
+ "#{java_get_type_declaration types[1]} #{nv} = #{var}.get(#{idx});",
278
+ java_serialize_internal(nv, types[1]),
279
+ :deindent,
280
+ "}"
281
+ ]
282
+ when :map
283
+ nv1 = get_fresh_variable_name
284
+ nv2 = get_fresh_variable_name
285
+ return [
286
+ "writeInt32(#{var}.size(), baos);",
287
+ "for(#{java_get_type_declaration types[1]} #{nv1} : #{var}.keySet()) {",
288
+ :indent,
289
+ "#{java_get_type_declaration types[2]} #{nv2} = #{var}.get(#{nv1});",
290
+ java_serialize_internal(nv1, types[1]),
291
+ java_serialize_internal(nv2, types[2]),
292
+ :deindent,
293
+ "}"
294
+ ]
295
+ else
296
+ raise "Missing serialization for #{var}"
297
+ end
298
+ else
299
+ if $all_structs[types]
300
+ "#{var}.serializeInternal(baos);"
301
+
302
+ elsif $available_types[types] && $available_types[types].ancestors.include?(SimpleDefinition)
303
+ "#{self.camelize "write", types}(#{var}, baos);"
304
+
305
+ else
306
+ raise "Missing code generation case #{types}"
307
+ end
308
+ end
309
+ end
310
+
311
+ def java_deserialize_complex(field)
312
+ types = field.referenced_types
313
+ as = [
314
+ "// Deserialize #{field.type} '#{field.name}'",
315
+ java_deserialize_internal("this.#{field.name}", types)
316
+ ]
317
+ format_src(2, 1, as, "\t")
318
+ end
319
+
320
+ def java_deserialize_internal(var, types)
321
+ if types.respond_to? :first
322
+ case types.first
323
+ when :list
324
+ count = get_fresh_variable_name
325
+ nv = get_fresh_variable_name
326
+ iter = get_fresh_variable_name
327
+ return [
328
+ "#{"#{java_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{java_get_empty_declaration(types)};",
329
+ "int #{count} = (int)this.readInt32(bais);",
330
+ "for(int #{iter}=0; #{iter}<#{count}; #{iter}++) {",
331
+ :indent,
332
+ java_deserialize_internal(nv, types[1]),
333
+ "#{var}.add(#{nv});",
334
+ :deindent,
335
+ "}"
336
+ ]
337
+ when :map
338
+ count = get_fresh_variable_name
339
+ nv1 = get_fresh_variable_name
340
+ nv2 = get_fresh_variable_name
341
+ iter = get_fresh_variable_name
342
+ return ["#{"#{java_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{java_get_empty_declaration(types)};",
343
+ "int #{count} = (int)readInt32(bais);",
344
+ "for(int #{iter}=0; #{iter}<#{count}; #{iter}++) {",
345
+ :indent,
346
+ java_deserialize_internal(nv1, types[1]),
347
+ java_deserialize_internal(nv2, types[2]),
348
+ "#{var}.put(#{nv1}, #{nv2});",
349
+ :deindent,
350
+ "}"
351
+ ]
352
+ else
353
+ raise "Missing serialization for #{var}"
354
+ end
355
+ else
356
+ # case types
357
+ # when :map
358
+ # "#{var} = #{java_get_empty_declaration(types)}"
359
+ # when :list
360
+ # "#{var} = #{java_get_empty_declaration(types)}"
361
+ # else
362
+ if $all_structs.key? types
363
+ [
364
+ "#{types} #{var} = new #{types}();",
365
+ "#{var}.deserialize(bais);"
366
+ ]
367
+ else
368
+ "#{"#{java_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{self.camelize("read", types)}(bais);"
369
+ end
370
+ # end
371
+ end
372
+ end
373
+ end
374
+
375
+
376
+ class JavaGenerator < JavaHelperMethods
377
+ def generate_code(structs, opts)
378
+ pp opts
379
+ $debug_java = true if opts[:debug]
380
+ base_template = Erubis::Eruby.new(java_base_class_template_str)
381
+ class_template = Erubis::Eruby.new(java_class_template_str)
382
+ keys = structs.keys.sort
383
+ src = keys.map do |k|
384
+ ss = structs[k]
385
+ # TODO: Should we merge different versions and deduce deprecated methods, warn for incompatible changes, etc?
386
+ raise "Duplicate definitions of struct #{k}" if ss.size > 1
387
+ class_template.result( c: ss.first, this: self )
388
+ end
389
+
390
+ # User defined super class?
391
+ toplevel = opts[:parent_class] || nil
392
+ toplevel = " extends #{toplevel}" if toplevel
393
+ return "#{java_get_begin_module(opts)}#{base_template.result({ toplevel_class: toplevel })}\n\n#{src.join("\n\n")}"
394
+ end
395
+
396
+ def java_get_begin_module(opts)
397
+ if opts[:package]
398
+ [
399
+ "package #{opts[:package]};\n",
400
+ "import java.io.ByteArrayInputStream;",
401
+ "import java.io.ByteArrayOutputStream;",
402
+ "import java.io.IOException;",
403
+ "import java.util.ArrayList;",
404
+ "import java.util.HashMap;",
405
+ "import java.nio.charset.Charset;\n\n"
406
+ ].join("\n")
407
+ else
408
+ nil
409
+ end
410
+ end
411
+ end
412
+
413
+ $language_generators[:java] = JavaGenerator.new
414
+ end