divine 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +30 -0
  2. data/README.md +57 -0
  3. data/Rakefile +45 -18
  4. data/lib/divine.rb +3 -3
  5. data/lib/divine/code_generators/code_generator.rb +112 -14
  6. data/lib/divine/code_generators/java.rb +144 -80
  7. data/lib/divine/code_generators/javascript.rb +192 -104
  8. data/lib/divine/code_generators/ruby.rb +133 -60
  9. data/lib/divine/dsl.rb +70 -12
  10. data/lib/divine/version.rb +1 -1
  11. data/test/basic_complex_test/js_test/js_testBasic.js +1 -1
  12. data/test/basic_complex_test/js_test/js_testComplex.js +1 -1
  13. data/test/basic_complex_test/ruby_test/ruby_test.rb +10 -10
  14. data/test/binaryTree_test/js_test/js_test.js +1 -1
  15. data/test/binaryTree_test/ruby_test/ruby_test.rb +4 -4
  16. data/test/complex_test/js_test/js_test.js +3 -2
  17. data/test/ipv6_test/java_test/JavaTest.java +4 -6
  18. data/test/ipv6_test/js_test/js_test.js +1 -1
  19. data/test/signed_float_test/ruby_test/ruby_test.rb +36 -0
  20. data/test/signed_float_test/signed_float_test.rb +14 -0
  21. data/test/signed_int_test/java_test/JavaTest.java +83 -0
  22. data/test/signed_int_test/js_test/js_test.js +55 -0
  23. data/test/signed_int_test/ruby_test/ruby_test.rb +37 -0
  24. data/test/signed_int_test/signed_int_test.rb +15 -0
  25. data/test/unify_test/unify_test.rb +24 -13
  26. metadata +14 -38
  27. data/test/basic_complex_test/java_test/test_babel.java +0 -368
  28. data/test/basic_complex_test/js_test/test_babel.js +0 -523
  29. data/test/basic_complex_test/ruby_test/test_babel.rb +0 -368
  30. data/test/binaryTree_test/java_test/test_binaryTree.java +0 -301
  31. data/test/binaryTree_test/js_test/test_binaryTree.js +0 -447
  32. data/test/binaryTree_test/ruby_test/test_binaryTree.rb +0 -303
  33. data/test/complex_test/java_test/test_complex.java +0 -331
  34. data/test/complex_test/js_test/test_complex.js +0 -478
  35. data/test/complex_test/ruby_test/test_complex.rb +0 -330
  36. data/test/ipv6_test/java_test/junit.jar +0 -0
  37. data/test/ipv6_test/java_test/test_ipv6.java +0 -270
  38. data/test/ipv6_test/js_test/test_ipv6.js +0 -409
  39. data/test/ipv6_test/ruby_test/test_ipv6.rb +0 -267
  40. data/test/unify_test/java_test/test_unify.java +0 -171
  41. data/test/unify_test/js_test/js_test.js +0 -56
  42. data/test/unify_test/js_test/test_unify.js +0 -326
  43. data/test/unify_test/ruby_test/ruby_test.rb +0 -35
  44. data/test/unify_test/ruby_test/test_unify.rb +0 -187
data/.gitignore CHANGED
@@ -15,3 +15,33 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.class
19
+ test/basic_complex_test/java_test/bin.babel
20
+ test/basic_complex_test/js_test/bin.babel.js
21
+ test/basic_complex_test/ruby_test/bin.babel.rb
22
+ test/binaryTree_test/java_test/bin.babel
23
+ test/binaryTree_test/js_test/bin.babel.js
24
+ test/binaryTree_test/ruby_test/bin.babel.rb
25
+ test/complex_test/java_test/bin.babel
26
+ test/complex_test/js_test/bin.babel.js
27
+ test/complex_test/ruby_test/bin.babel.rb
28
+ test/ipv6_test/java_test/bin.babel
29
+ test/ipv6_test/js_test/bin.babel.js
30
+ test/ipv6_test/ruby_test/bin.babel.rb
31
+ test/basic_complex_test/ruby_test/test_babel.rb
32
+ test/binaryTree_test/ruby_test/test_binaryTree.rb
33
+ test/complex_test/ruby_test/test_complex.rb
34
+ test/ipv6_test/ruby_test/test_ipv6.rb
35
+ test/signed_int_test/ruby_test/test_signed_int.rb
36
+ test/signed_int_test/js_test/bin.babel.js
37
+ test/signed_int_test/ruby_test/bin.babel.rb
38
+ test/basic_complex_test/java_test/test_babel.java
39
+ test/binaryTree_test/java_test/test_binaryTree.java
40
+ test/complex_test/java_test/test_complex.java
41
+ test/ipv6_test/java_test/test_ipv6.java
42
+ test/signed_int_test/java_test/test_signed_int.java
43
+ test/basic_complex_test/js_test/test_babel.js
44
+ test/binaryTree_test/js_test/test_binaryTree.js
45
+ test/complex_test/js_test/test_complex.js
46
+ test/ipv6_test/js_test/test_ipv6.js
47
+ test/signed_int_test/js_test/test_signed_int.js
data/README.md CHANGED
@@ -94,6 +94,63 @@ c2.deserialize(new BabelDataReader(ca));
94
94
  console.log(c2);
95
95
  ```
96
96
 
97
+ ### Versioning
98
+ ```ruby
99
+ require 'divine'
100
+
101
+ struct 'Foobar' do # Default version is '1'
102
+ int8 :foo
103
+ end
104
+
105
+
106
+ struct 'Foobar', version: 2 do
107
+ int8 :foo
108
+ int8 :bar # We added a new field named 'bar' in version 2 of Foobar
109
+ end
110
+
111
+ Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
112
+ Divine::CodeGenerator.new.generate(:java, file: 'test_babel.java')
113
+ ```
114
+
115
+ There are some basic rules regarding versioning of structs
116
+
117
+ * A versioned struct defines all fields you want to serialize/deserialize
118
+ * You can delete and add fields as you whish between versions
119
+ * You are not allowed to change type of a defined variable between versions
120
+ * You cannot have a bigger version number than 255
121
+ * The class that represents the struct also defines a 'struct_version' that keeps the current version of the struct
122
+
123
+
124
+ ### Freezing
125
+ When starting to use generated code in production, the defined structs cannot be changed without breaking everything (unless you really know what you're doing).
126
+ To prevent that you change a struct by accident, you can 'freeze' a struct. The easiest way to get started is to add a empty _freeze_ field to the struct, like below
127
+
128
+ ```ruby
129
+ require 'divine'
130
+
131
+ struct 'Foobar', freeze: '' do
132
+ int8 :foo
133
+ int8 :bar
134
+ end
135
+
136
+ Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
137
+ ```
138
+
139
+ The compiler will throw a runtime exception saying that the MD5 sum differs. Take the MD5 sum from this exception and put into the _freeze_ field as below
140
+
141
+ ```ruby
142
+ require 'divine'
143
+
144
+ struct 'Foobar', freeze: '3e59aa582d3137a2d0cdba174e5aa6b18beb649b' do
145
+ int8 :foo
146
+ int8 :bar
147
+ end
148
+
149
+ Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
150
+ ```
151
+
152
+ It is now not possible to alter _Foobar_ by accident. If you make a change to the struct, you will also need to provide a correct MD5 sum.
153
+
97
154
 
98
155
  ## Installation
99
156
 
data/Rakefile CHANGED
@@ -1,27 +1,48 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+
4
+ task :default => ["test:all"]
5
+
6
+
3
7
  namespace :test do
8
+ desc "Run all tests"
9
+ task :all => [:ruby, :js, :java, :unify] # Make sure that the unify test be the last one in the list.
4
10
 
5
11
  desc "Run Ruby code test suite"
6
- task :ruby => :environment do
7
- ruby "test/basic_complex_test/ruby_test/ruby_test.rb"
8
- ruby "test/binaryTree_test/ruby_test/ruby_test.rb"
12
+ task :ruby do
13
+ generate_source('ruby')
14
+ ruby "test/signed_int_test/ruby_test/ruby_test.rb"
9
15
  ruby "test/ipv6_test/ruby_test/ruby_test.rb"
10
16
  ruby "test/complex_test/ruby_test/ruby_test.rb"
17
+ ruby "test/binaryTree_test/ruby_test/ruby_test.rb"
18
+ ruby "test/basic_complex_test/ruby_test/ruby_test.rb"
19
+
20
+ system("find . -name 'test*.rb' | xargs rm") # Remove generated source code files
11
21
  end
12
22
 
13
23
 
14
24
  desc "Run JS code test suite"
15
- task :js => :environment do
16
- system("node test/basic_complex_test/js_test/js_testBasic.js")
17
- system("node test/basic_complex_test/js_test/js_testComplex.js")
18
- system("node test/binaryTree_test/js_test/js_test.js")
25
+ task :js do
26
+ generate_source('js')
27
+ system("node test/signed_int_test/js_test/js_test.js")
19
28
  system("node test/ipv6_test/js_test/js_test.js")
20
29
  system("node test/complex_test/js_test/js_test.js")
30
+ system("node test/binaryTree_test/js_test/js_test.js")
31
+ system("node test/basic_complex_test/js_test/js_testBasic.js")
32
+ system("node test/basic_complex_test/js_test/js_testComplex.js")
33
+
34
+ system("find . -name 'test*.js' | xargs rm") # Remove generated source code files
21
35
  end
22
36
 
23
37
  desc "Run java code test suite"
24
- task :java => :environment do
38
+ task :java do
39
+ generate_source('java')
40
+ #system("javac -cp test/java_lib/junit.jar: test/signed_float_test/java_test/*.java")
41
+ #system("java -cp test/signed_float_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
42
+
43
+ system("javac -cp test/java_lib/junit.jar: test/signed_int_test/java_test/*.java")
44
+ system("java -cp test/signed_int_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
45
+
25
46
  system("javac -cp test/java_lib/junit.jar: test/ipv6_test/java_test/*.java")
26
47
  system("java -cp test/ipv6_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
27
48
 
@@ -33,18 +54,24 @@ namespace :test do
33
54
 
34
55
  system("javac -cp test/java_lib/junit.jar: test/basic_complex_test/java_test/*.java")
35
56
  system("java -cp test/basic_complex_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
36
- end
37
-
38
57
 
39
- desc "Generate Source Code"
40
- task :environment do
41
- puts "Divine Version #{Divine::VERSION}"
42
- lang = Rake.application.top_level_tasks[0].match(/:(.*)/)[1]
58
+ system("find . -name 'test*.java' | xargs rm") # Remove generated source code files
59
+ system("find . -name '*.class' | xargs rm") # Remove generated .class files
60
+ end
43
61
 
44
- ruby "test/basic_complex_test/basic_complex_test.rb #{lang}"
45
- ruby "test/ipv6_test/ipv6_test.rb #{lang}"
46
- ruby "test/complex_test/complex_test.rb #{lang}"
47
- ruby "test/binaryTree_test/binaryTree_test.rb #{lang}"
62
+ desc "Unify test to compare the produced binary files. Prerequisite Other tests must be run first to generate bin files to be compared"
63
+ task :unify do
64
+ ruby "test/unify_test/unify_test.rb"
65
+ system("find . -name 'bin.babel*' | xargs rm") # Remove generated bin files
48
66
  end
49
67
  end
50
68
 
69
+
70
+ def generate_source(lang)
71
+ puts "Divine Version #{Divine::VERSION}"
72
+ ruby "test/signed_int_test/signed_int_test.rb #{lang}"
73
+ ruby "test/ipv6_test/ipv6_test.rb #{lang}"
74
+ ruby "test/complex_test/complex_test.rb #{lang}"
75
+ ruby "test/binaryTree_test/binaryTree_test.rb #{lang}"
76
+ ruby "test/basic_complex_test/basic_complex_test.rb #{lang}"
77
+ end
@@ -14,9 +14,9 @@ end
14
14
  #
15
15
  # Toplevel definition of struct'
16
16
  #
17
- def struct(name, version=1, &block)
18
- puts "struct #{name}"
19
- builder = Divine::StructBuilder.new(name, version) # Defined in divine/dsl.rb
17
+ def struct(name, properties=nil, &block)
18
+ #puts "struct #{name}"
19
+ builder = Divine::StructBuilder.new(name, properties) # Defined in divine/dsl.rb
20
20
  ::Docile.dsl_eval(builder, &block)
21
21
  builder
22
22
  end
@@ -1,6 +1,29 @@
1
+ require 'digest/sha1'
2
+ require 'fileutils'
3
+
4
+
1
5
  module Divine
2
6
  $language_generators = {}
3
7
 
8
+ class StructHandler
9
+ attr_reader :name, :latest_version, :structs
10
+
11
+ def initialize(structs)
12
+ @structs = structs
13
+ @name = structs.first.name
14
+ @latest_version = structs.last.version
15
+ @field_hash = structs.map(&:fields).flatten.group_by(&:name)
16
+ end
17
+
18
+ def field(name)
19
+ @field_hash[name]
20
+ end
21
+
22
+ def field_names
23
+ @field_hash.keys.sort
24
+ end
25
+ end
26
+
4
27
  class BabelHelperMethods
5
28
  def format_src(first_indent, following_indent, is, spc = " ")
6
29
  indent = "#{spc * first_indent}"
@@ -27,40 +50,115 @@ module Divine
27
50
  ss = str.map(&:to_s).join("_").split(/_/).flatten
28
51
  "#{ss.first.downcase}#{ss[1..-1].map(&:downcase).map(&:capitalize).join}"
29
52
  end
53
+
54
+ def get_header_comment_text
55
+ return [
56
+ "",
57
+ "-- DO NOT EDIT THIS FILE --", "",
58
+ "This file was generated by Divine #{Divine::VERSION} (#{Time.now.to_s})", "",
59
+ "-- DO NOT EDIT THIS FILE --", ""
60
+ ]
61
+ end
62
+
63
+ #
64
+ # Sanity check the different revisions of the struct
65
+ #
66
+ def sanity_check(ss)
67
+ vos = ss.sort do |x,y|
68
+ v1,v2 = [x,y].map(&:version)
69
+ v1 <=> v2
70
+ end
71
+
72
+ # Check version numbers
73
+ unless vos.map(&:version).uniq.size == ss.size
74
+ raise "Inconsistent version numbering for '#{ss.first.name}': #{vos.map(&:version).join(', ')}"
75
+ end
76
+
77
+ # Check that we don't have multiple variables with same name
78
+ vos.each do |s|
79
+ names = s.fields.group_by(&:name)
80
+ names.each_pair do |k, v|
81
+ raise "Multiple fields with same name '#{k}' is defined in '#{s.name}', version #{s.version}" unless v.size == 1
82
+ end
83
+ end
84
+
85
+ # Check types between versions
86
+ check_field_types(vos)
87
+
88
+ # Check for changed definitions
89
+ check_freezed_structs(vos)
90
+
91
+ # Return them ordered by version
92
+ vos
93
+ end
94
+
95
+ private
96
+ #
97
+ # Check types through all versions, we're not allowed to change the type of a variable between different versions
98
+ #
99
+ def check_field_types(ss)
100
+ types = {}
101
+ ss.each do |x|
102
+ x.fields.each do |f|
103
+ if types[f.name]
104
+ unless same_type(types[f.name], x, f.name)
105
+ raise "Cannot change the field type for struct #{x.name}.#{f.name} between version #{types[f.name].version} and #{x.version}"
106
+ end
107
+ else
108
+ types[f.name] = x
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ def same_type(t1, t2, field)
115
+ t1.get_field(field).referenced_types == t2.get_field(field).referenced_types
116
+ end
117
+
118
+ def check_freezed_structs(ss)
119
+ ss.each do |s|
120
+ if s.freezed?
121
+ sig = calculate_signature(s)
122
+ unless sig == s.freeze_signature
123
+ raise "Struct '#{s.name}', version #{s.version} has changed! Got signature '#{sig}', expected '#{s.freeze_signature}'"
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def calculate_signature(s)
130
+ str = s.fields.map do |f|
131
+ "#{f.name}:#{f.referenced_types.inspect}"
132
+ end.join(",")
133
+ Digest::SHA1.hexdigest("#{s.name}:#{s.version},#{str}")
134
+ end
30
135
  end
31
136
 
137
+
138
+
32
139
  class CodeGenerator
33
140
  def generate(target, opts)
34
141
  gen = $language_generators[target.to_sym]
35
- raise "Unknown taget language: #{target}" unless gen
142
+ raise "Unknown target language: #{target}" unless gen
36
143
  puts "Generating code for #{target}"
37
144
  src = gen.generate_code($all_structs, opts)
38
- target_dir = getTargetDir(opts[:target_dir])
145
+ target_dir = (opts[:target_dir] || ".") + "/"
39
146
 
40
147
  if opts[:package]
41
- path = target_dir + opts[:package]
42
- Dir.mkdir(path) unless File.exists?(path)
148
+ path = target_dir + opts[:package].gsub(/\./, "/")
149
+ FileUtils.mkdir_p(path) unless File.exists?(path)
43
150
  for cls in src
44
151
  file_name = path+"/"+cls[:file]
45
152
  writeFile(file_name, cls[:src])
46
153
  end
47
154
  elsif opts[:file]
155
+ FileUtils.mkdir_p(target_dir) unless File.exists?(target_dir)
48
156
  path = target_dir + opts[:file]
49
157
  writeFile(path, src[0][:src])
50
158
  else
51
159
  puts src
52
160
  end
53
161
  end
54
-
55
- def getTargetDir(dir)
56
- # check if the path is relative or absolute if exist
57
- if dir && File.directory?(dir)
58
- return dir + "/"
59
- elsif dir && File.directory?(Dir.pwd + "/" + dir)
60
- return Dir.pwd + dir + "/"
61
- end
62
- ""
63
- end
64
162
 
65
163
  def writeFile(path, content)
66
164
  File.open(path, 'w+') do |f|
@@ -1,6 +1,12 @@
1
1
  module Divine
2
2
 
3
3
  class JavaHelperMethods < BabelHelperMethods
4
+ def get_header_comment
5
+ get_header_comment_text.map do |s|
6
+ "// #{s}"
7
+ end.join("\n")
8
+ end
9
+
4
10
  def java_base_class_template_str
5
11
  <<EOS
6
12
  abstract class BabelBase <%= toplevel_class %> {
@@ -33,6 +39,14 @@ abstract class BabelBase <%= toplevel_class %> {
33
39
  return (data.read() << 24) | readInt24(data);
34
40
  }
35
41
 
42
+ protected int readSint32(ByteArrayInputStream data) {
43
+ return (data.read() << 24) | readInt24(data);
44
+ }
45
+
46
+ protected long readSint64(ByteArrayInputStream data) {
47
+ return (readInt32(data) << 32) | (readInt32(data) & 0xFFFFFFFFL);
48
+ }
49
+
36
50
  protected boolean readBool(ByteArrayInputStream data) {
37
51
  return readInt8(data) == 1;
38
52
  }
@@ -135,6 +149,26 @@ abstract class BabelBase <%= toplevel_class %> {
135
149
  writeInt24((int) (v & 0xFFFFFF), out);
136
150
  }
137
151
 
152
+ protected void writeSint32(int v, ByteArrayOutputStream out) {
153
+ if (v > Integer.MAX_VALUE) { // Max 2.147.483.647
154
+ raiseError("Too large sInt32 number: " + v + ", Max = " + Integer.MAX_VALUE);
155
+ }else if(v < Integer.MIN_VALUE){ // Min -2.147.483.648
156
+ raiseError("Too small sInt32 number: " + v + ", Min = " + Integer.MIN_VALUE);
157
+ }
158
+ writeInt8((int) ((v >> 24) & 0xFF), out);
159
+ writeInt24((int) (v & 0xFFFFFF), out);
160
+ }
161
+
162
+ protected void writeSint64(long v, ByteArrayOutputStream out) {
163
+ if (v > Long.MAX_VALUE) { // Max 9,223,372,036,854,775,807
164
+ raiseError("Too large sInt64 number: " + v + ", Max = " + Integer.MAX_VALUE);
165
+ }else if(v < Long.MIN_VALUE){ // Min -9,223,372,036,854,775,807
166
+ raiseError("Too small sInt64 number: " + v + ", Min = " + Integer.MIN_VALUE);
167
+ }
168
+ writeInt32(((v >> 32) & 0xFFFFFFFFL), out);
169
+ writeInt32( (v & 0xFFFFFFFFL) , out);
170
+ }
171
+
138
172
  protected void writeBool(boolean v, ByteArrayOutputStream out) {
139
173
  writeInt8(v ? 1 : 0, out);
140
174
  }
@@ -242,43 +276,90 @@ abstract class BabelBase <%= toplevel_class %> {
242
276
  EOS
243
277
  end
244
278
 
245
- def java_class_template_str
246
- <<EOS2
247
- class <%= c.name %> extends BabelBase {
248
- <% unless c.fields.empty? %>
249
- <% c.fields.each do |f| %>
250
- public <%= this.java_get_type_declaration(f) %> <%= f.name %> = <%= this.java_get_empty_declaration(f) %>;
251
- <% end %>
252
- <% end %>
253
-
254
- @Override
255
- void serializeInternal(ByteArrayOutputStream baos) throws IOException {
256
- <% c.simple_fields.each do |f| %>
257
- <%= this.camelize("write", f.type) %>(this.<%= f.name %>, baos);
258
- <% end %>
259
- <% c.complex_fields.each do |f| %>
260
- <%= this.java_serialize_complex f %>
261
- <% end %>
262
- }
279
+ def java_class_template(sh)
280
+ code = [
281
+ "class #{sh.name} extends BabelBase {",
282
+ :indent,
283
+ "",
284
+
285
+ # PROPERTIES
286
+ "public int struct_version = #{sh.latest_version};",
287
+ sh.field_names.map do |fn|
288
+ f = sh.field(fn).last
289
+ "public #{java_get_type_declaration(f)} #{fn} = #{java_get_empty_declaration(f)};"
290
+ end, "",
291
+
263
292
 
264
- @Override
265
- public void deserialize(ByteArrayInputStream bais) throws IOException {
266
- <% c.simple_fields.each do |f| %>
267
- this.<%= f.name %> = <%= this.camelize("read", f.type) %>(bais);
268
- <% end %>
269
- <% c.complex_fields.each do |f| %>
270
- <%= this.java_deserialize_complex f %>
271
- <% end %>
272
- }
273
- }
274
- EOS2
293
+ # SERiALIZE INTERNAL
294
+ "@Override",
295
+ "void serializeInternal(ByteArrayOutputStream baos) throws IOException {",
296
+ :indent,
297
+ "writeInt8(this.struct_version, baos);",
298
+ sh.structs.map do |s|
299
+ [
300
+ "if(this.struct_version == #{s.version}) {",
301
+ :indent,
302
+ s.simple_fields.map do |f|
303
+ "#{camelize("write", f.type)}(this.#{f.name}, baos);"
304
+ end,
305
+ s.complex_fields.map do |f|
306
+ [
307
+ "", "// Serialize #{f.type} '#{f.name}'",
308
+ java_serialize_internal("this.#{f.name}", f.referenced_types)
309
+ ]
310
+ end,
311
+ "return;",
312
+ :deindent,
313
+ "}", ""
314
+ ]
315
+ end, "",
316
+ "throw new UnsupportedOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
317
+ :deindent,
318
+ "}", "",
319
+
320
+
321
+ # DESERIALIZE
322
+ "@Override",
323
+ "public void deserialize(ByteArrayInputStream bais) throws IOException {",
324
+ :indent,
325
+ "this.struct_version = readInt8(bais);",
326
+ sh.structs.map do |s|
327
+ [
328
+ "if(this.struct_version == #{s.version}) {",
329
+ :indent,
330
+ s.simple_fields.map do |f|
331
+ "this.#{f.name} = #{camelize("read", f.type)}(bais);"
332
+ end,
333
+ s.complex_fields.map do |f|
334
+ [
335
+ "", "// Read #{f.type} '#{f.name}'",
336
+ java_deserialize_internal("this.#{f.name}", f.referenced_types)
337
+ ]
338
+ end,
339
+ "return;",
340
+ :deindent,
341
+ "}"
342
+ ]
343
+ end, "",
344
+ "throw new UnsupportedOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
345
+ :deindent,
346
+ "}", "",
347
+
348
+
349
+ # END OF CLASS
350
+ :deindent,
351
+ "}"
352
+ ]
353
+
354
+ format_src(3, 3, code)
275
355
  end
356
+
276
357
 
277
358
  def java_get_empty_declaration(types, is_reference_type = false)
278
359
  if types.respond_to? :referenced_types
279
360
  java_get_empty_declaration(types.referenced_types)
280
361
 
281
- elsif types.respond_to? :first
362
+ elsif types.respond_to?(:first) && types.size > 1
282
363
  case types.first
283
364
  when :list
284
365
  "new #{java_get_type_declaration(types, true)}()"
@@ -288,13 +369,16 @@ EOS2
288
369
  raise "Missing empty declaration for #{types}"
289
370
  end
290
371
 
372
+ elsif types.respond_to?(:first) && types.size == 1
373
+ java_get_empty_declaration(types.first, is_reference_type)
374
+
291
375
  else
292
376
  case types
293
377
  when :binary, :short_binary
294
378
  is_reference_type ? "new Byte[0]" : "new byte[0]"
295
- when :int8, :int16
379
+ when :int8, :int16, :sint32
296
380
  "0"
297
- when :int32
381
+ when :int32, :sint64
298
382
  "0L"
299
383
  when :string, :ip_number
300
384
  "\"\""
@@ -312,7 +396,7 @@ EOS2
312
396
  if types.respond_to? :referenced_types
313
397
  java_get_type_declaration(types.referenced_types, is_reference_type)
314
398
 
315
- elsif types.respond_to? :first
399
+ elsif types.respond_to?(:first) && types.size > 1
316
400
  case types.first
317
401
  when :list
318
402
  subtypes = java_get_type_declaration(types[1], true)
@@ -324,14 +408,17 @@ EOS2
324
408
  else
325
409
  raise "Missing serialization for #{types}"
326
410
  end
411
+
412
+ elsif types.respond_to?(:first) && types.size == 1
413
+ java_get_type_declaration(types.first, is_reference_type)
327
414
 
328
415
  else
329
416
  case types
330
417
  when :binary, :short_binary
331
418
  is_reference_type ? "Byte[]" : "byte[]"
332
- when :int8, :int16
419
+ when :int8, :int16, :sint32
333
420
  is_reference_type ? "Integer" : "int"
334
- when :int32
421
+ when :int32, :sint64
335
422
  is_reference_type ? "Long" : "long"
336
423
  when :string, :ip_number
337
424
  "String"
@@ -345,15 +432,6 @@ EOS2
345
432
  end
346
433
  end
347
434
 
348
- def java_serialize_complex(field)
349
- types = field.referenced_types
350
- as = [
351
- "// Serialize #{field.type} '#{field.name}'",
352
- java_serialize_internal(field.name, types)
353
- ]
354
- format_src(2, 1, as, "\t")
355
- end
356
-
357
435
  def java_serialize_internal(var, types)
358
436
  if types.respond_to? :first
359
437
  case types.first
@@ -398,15 +476,6 @@ EOS2
398
476
  end
399
477
  end
400
478
 
401
- def java_deserialize_complex(field)
402
- types = field.referenced_types
403
- as = [
404
- "// Deserialize #{field.type} '#{field.name}'",
405
- java_deserialize_internal("this.#{field.name}", types)
406
- ]
407
- format_src(2, 1, as, "\t")
408
- end
409
-
410
479
  def java_deserialize_internal(var, types)
411
480
  if types.respond_to? :first
412
481
  case types.first
@@ -465,16 +534,14 @@ EOS2
465
534
 
466
535
  class JavaGenerator < JavaHelperMethods
467
536
  def generate_code(structs, opts)
468
- pp opts
469
537
  $debug_java = true if opts[:debug]
470
538
  base_template = Erubis::Eruby.new(java_base_class_template_str)
471
- class_template = Erubis::Eruby.new(java_class_template_str)
472
539
  keys = structs.keys.sort
473
540
  src = keys.map do |k|
474
541
  ss = structs[k]
475
- # TODO: Should we merge different versions and deduce deprecated methods, warn for incompatible changes, etc?
476
- raise "Duplicate definitions of struct #{k}" if ss.size > 1
477
- class_template.result( c: ss.first, this: self )
542
+ # Check different aspects the the structs
543
+ vss = sanity_check(ss)
544
+ java_class_template(StructHandler.new(vss))
478
545
  end
479
546
 
480
547
  # User defined super class?
@@ -492,28 +559,25 @@ EOS2
492
559
  end
493
560
 
494
561
  def java_get_begin_module(opts)
495
- if opts[:package]
496
- [
497
- "package #{opts[:package]};\n",
498
- "import java.io.ByteArrayInputStream;",
499
- "import java.io.ByteArrayOutputStream;",
500
- "import java.io.IOException;",
501
- "import java.util.ArrayList;",
502
- "import java.util.HashMap;",
503
- "import java.util.regex.Pattern;",
504
- "import java.nio.charset.Charset;\n\n"
505
- ].join("\n")
506
- else
507
- [
508
- "import java.io.ByteArrayInputStream;",
509
- "import java.io.ByteArrayOutputStream;",
510
- "import java.io.IOException;",
511
- "import java.util.ArrayList;",
512
- "import java.util.HashMap;",
513
- "import java.util.regex.Pattern;",
514
- "import java.nio.charset.Charset;\n\n"
515
- ].join("\n")
516
- end
562
+ str = "#{get_header_comment}\n\n"
563
+ str << "package #{opts[:package]};" if opts[:package]
564
+ str << get_java_imports
565
+ str << "\n\n"
566
+ return str
567
+ end
568
+
569
+ def get_java_imports
570
+ [
571
+ "java.io.ByteArrayInputStream",
572
+ "java.io.ByteArrayOutputStream",
573
+ "java.io.IOException",
574
+ "java.util.ArrayList",
575
+ "java.util.HashMap",
576
+ "java.util.regex.Pattern",
577
+ "java.nio.charset.Charset"
578
+ ].map do |i|
579
+ "import #{i};"
580
+ end.join("\n")
517
581
  end
518
582
  end
519
583