swift_generator 0.1.2

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.idea/.name +1 -0
  4. data/.idea/.rakeTasks +7 -0
  5. data/.idea/compiler.xml +22 -0
  6. data/.idea/copyright/profiles_settings.xml +3 -0
  7. data/.idea/dictionaries/ckornher.xml +7 -0
  8. data/.idea/encodings.xml +4 -0
  9. data/.idea/misc.xml +4 -0
  10. data/.idea/modules.xml +8 -0
  11. data/.idea/runConfigurations/Install_the_Gem.xml +26 -0
  12. data/.idea/runConfigurations/Ribosome.xml +26 -0
  13. data/.idea/runConfigurations/genswift_help.xml +26 -0
  14. data/.idea/runConfigurations/test1.xml +30 -0
  15. data/.idea/scopes/scope_settings.xml +5 -0
  16. data/.idea/swift_generator.iml +126 -0
  17. data/.idea/uiDesigner.xml +124 -0
  18. data/.idea/vcs.xml +6 -0
  19. data/.idea/workspace.xml +1356 -0
  20. data/.rspec +2 -0
  21. data/.travis.yml +3 -0
  22. data/CODE_OF_CONDUCT.md +13 -0
  23. data/Gemfile +7 -0
  24. data/LICENSE.txt +21 -0
  25. data/README.md +39 -0
  26. data/Rakefile +1 -0
  27. data/Tools/compile_templates.bash +3 -0
  28. data/Tools/help.rna +340 -0
  29. data/Tools/ribosome.rb +589 -0
  30. data/bin/console +14 -0
  31. data/bin/genswift +34 -0
  32. data/bin/setup +7 -0
  33. data/lib/swift_generator.rb +23 -0
  34. data/lib/swift_generator/code_generation/POSOTest.rb +8 -0
  35. data/lib/swift_generator/code_generation/SwiftFileTemplate.dna +157 -0
  36. data/lib/swift_generator/code_generation/ToRemove/generate.bash +41 -0
  37. data/lib/swift_generator/code_generation/code_generation_common.rb +6 -0
  38. data/lib/swift_generator/code_generation/swift_class_generation.rb +1589 -0
  39. data/lib/swift_generator/code_generation/swift_file_template.rb +499 -0
  40. data/lib/swift_generator/code_generation/swift_types.rb +106 -0
  41. data/lib/swift_generator/code_generation/usi_metaclasses.rb +23 -0
  42. data/lib/swift_generator/specfile_parser.rb +116 -0
  43. data/lib/swift_generator/version.rb +3 -0
  44. data/swift_generator.gemspec +31 -0
  45. metadata +118 -0
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "swift_generator"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/genswift ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+ require 'swift_generator'
5
+
6
+ # assistant = Objc2swiftAssistant::Assistant.new
7
+ # assistant.runFromCommandLine
8
+
9
+ program :name, 'genswift'
10
+ program :version, '1.0.0'
11
+ program :description, 'Basic CLI interface to the swift_generator gem'
12
+
13
+ command :generate do |c|
14
+ c.syntax = 'genswift specfile'
15
+ c.description = 'Generate Swift files based upon a JSON specification file.'
16
+ c.option '--out STRING', String, 'The root of the generated Swift source. Overrides the setting in the spec file.'
17
+ c.action do |args, options|
18
+ options.default :out => nil
19
+
20
+ if args.size == 1
21
+ a = SwiftGenerator::Generator.new( Pathname.new(args[0]), options.out )
22
+ a.generate()
23
+ elsif args.size > 1
24
+ puts( "specfile must be specified" )
25
+ else
26
+ puts( "more than one argument provided to command" )
27
+ end
28
+ end
29
+ end
30
+
31
+ default_command :generate
32
+ # end
33
+
34
+
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ require "swift_generator/version"
2
+
3
+ require "swift_generator/specfile_parser"
4
+
5
+ module SwiftGenerator
6
+
7
+ class Generator
8
+
9
+ attr_accessor :spec_file_path
10
+ attr_accessor :out_dir_path
11
+
12
+ def initialize( spec_file_path, out_dir_str )
13
+ @spec_file_path = spec_file_path
14
+ @out_dir_path = out_dir_str # may be nil
15
+ end
16
+
17
+ def generate
18
+ parser = SpecfileParser.new( @spec_file_path )
19
+ parser.process_specfile( )
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'swift_class_generation'
2
+
3
+ gen = SwiftDefinitionSet.new( 'output' )
4
+
5
+ c1 = swift_class.new( gen, 'User', ['USIWireData'] )
6
+
7
+ puts c1
8
+ puts gen
@@ -0,0 +1,157 @@
1
+ $definition_file = nil
2
+
3
+ require 'fileutils'
4
+ require "swift_generator/version"
5
+ require "swift_generator/code_generation/swift_class_generation"
6
+
7
+
8
+ module SwiftGenerator
9
+
10
+ def write_files_for_definition_set( definition_set )
11
+ for _, f in definition_set.output_files
12
+ SwiftGenerator::writeGeneratedFile( f )
13
+ end
14
+ end
15
+
16
+
17
+ module_function :write_files_for_definition_set
18
+
19
+ # files = YAML::load(File.open(ARGV[0]))
20
+
21
+ # Templated Output Methods
22
+ def write_element( element )
23
+ if( element.kind_of? SwiftClass )
24
+ SwiftGenerator::write_class( element )
25
+ elsif( element.kind_of? SwiftEnum )
26
+ SwiftGenerator::write_enum( element )
27
+ end
28
+ end
29
+
30
+ module_function :write_element
31
+
32
+ def writeGeneratedFile( f )
33
+ return if ( f.is_user_file && File.exist?( f.file_path ))
34
+
35
+ ./!output( f.file_path )
36
+
37
+ .//
38
+ .// @{f.file_name}
39
+ .// @{f.company_name}
40
+ if $definition_file
41
+ .//
42
+ .// Generated from @{$definition_file} on @{Time.now.strftime("%d/%m/%Y %H:%M")}
43
+ end
44
+ if f.include_editing_warnings
45
+ if !f.is_user_file
46
+ .//
47
+ .// WARNING: This entire file is generated. DO NOT EDIT.
48
+ else
49
+ .//
50
+ .// This is a user-editable generated file. Delete or rename this file to have a new empty file generated
51
+ end
52
+ end
53
+ .//
54
+ .// Copyright (c) @{Time.now.strftime("")} @{f.company_name}, Inc. All rights reserved.
55
+ .//
56
+
57
+ for import_statement in f.import_statements.sort
58
+ .&{import_statement}
59
+ end
60
+
61
+ for element in f.elements
62
+ SwiftGenerator::write_element(element)
63
+ end
64
+ end
65
+
66
+ module_function :writeGeneratedFile
67
+
68
+
69
+ def write_enum( e )
70
+ .
71
+ .
72
+ .&{e.access_control_modifier}enum @{e.type_name}
73
+ if e.inheritance_list.count > 0
74
+ . /+ : @{e.inheritance_list.join( ", " )}
75
+ end
76
+ . /+ {
77
+
78
+ e.enum_cases.each_with_index do |enum_case, _|
79
+ enum_case.declaration_lines().each do |declLine|
80
+ . &{declLine}
81
+ end
82
+ end
83
+
84
+ e.properties.each_with_index do |prop, i|
85
+ .
86
+ prop.declaration_lines().each do |declLine|
87
+ . &{declLine}
88
+ end
89
+ end
90
+
91
+ SwiftGenerator::write_methods( e.methods )
92
+ . }
93
+ end
94
+
95
+ module_function :write_enum
96
+
97
+
98
+ def write_class( c )
99
+ .
100
+ .
101
+ .&{c.access_control_modifier}class @{c.type_name}
102
+ if c.inheritance_list.count > 0
103
+ . /+ : @{c.inheritance_list.join( ", " )}
104
+ end
105
+ . /+ {
106
+
107
+ SwiftGenerator::write_property_declarations( c.transient_properties, "Transient" )
108
+ SwiftGenerator::write_property_declarations( c.persistent_properties, "Persistent" )
109
+
110
+ SwiftGenerator::write_methods( c.initializers )
111
+ SwiftGenerator::write_methods( c.methods )
112
+ .}
113
+
114
+ end
115
+
116
+ module_function :write_class
117
+
118
+
119
+ def write_property_declarations( properties, property_type_label )
120
+ properties.each_with_index do |prop, i|
121
+ if i == 0
122
+ .
123
+ . // MARK: @{property_type_label} Properties
124
+ end
125
+ prop.declaration_lines().each do |declLine|
126
+ . &{declLine}
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+ module_function :write_property_declarations
133
+
134
+
135
+ def write_methods( methods )
136
+ methods.each_with_index do |m, i|
137
+ overrideString = m.override ? 'override ' : ''
138
+ .
139
+ if ! m.comment.nil?
140
+ . &{m.comment}
141
+ end
142
+ args = m.argStr.nil? || m.argStr.empty? ? m.argStr : ' ' + m.argStr + ' '
143
+ . &{overrideString}&{m.access_control_modifier}&{m.func_fragment} @{m.name}(&{args})
144
+ if ! (m.returns.nil? || m.returns.length == 0 )
145
+ . /+ -> @{m.returns}
146
+ end
147
+ . /+ {
148
+ m.bodyLines.each do |line|
149
+ . &{line}
150
+ end
151
+ . }
152
+ end
153
+ end
154
+
155
+ module_function :write_methods
156
+
157
+ end
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ cd "$(dirname "$0")"
3
+ pwd
4
+
5
+ SOURCE_FILES=../Useful/Shared/Models/Generated/*.swift
6
+ for f in $SOURCE_FILES
7
+ do
8
+ rm $f
9
+ done
10
+
11
+ TEST_FILES=../UsefulTests/Shared/Models/Generated/*.swift
12
+ for f in $TEST_FILES
13
+ do
14
+ rm $f
15
+ done
16
+
17
+ ./Tools/ribosome --rna PlainOldSwiftObjects.dna > PlainOldSwiftObjects.rna
18
+ ./PlainOldSwiftObjects.rna
19
+
20
+
21
+ for f in $SOURCE_FILES
22
+ do
23
+ echo " "
24
+ echo "--------------------------------------------------------------------------------"
25
+ echo " "
26
+ echo "Generated Source File: $f"
27
+ echo " "
28
+ # take action on each file. $f store current file name
29
+ cat $f
30
+ done
31
+
32
+ for f in $TEST_FILES
33
+ do
34
+ echo " "
35
+ echo "--------------------------------------------------------------------------------"
36
+ echo " "
37
+ echo "Generated Test File: $f"
38
+ echo " "
39
+ # take action on each file. $f store current file name
40
+ cat $f
41
+ done
@@ -0,0 +1,6 @@
1
+ $SwiftIntegralTypes = [:Int, :UInt, :Int32, :UInt32, :Int64, :UInt64]
2
+ $SwiftLegalEnumTypes = $SwiftIntegralTypes << :String
3
+
4
+ $SwiftLegalEnumTypeNames = $SwiftLegalEnumTypes.map{ |typeSymbol| typeSymbol.to_s }
5
+
6
+ $UNIMPLEMENTED = "SYSTEM ERROR: method missing"
@@ -0,0 +1,1589 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require_relative 'swift_types'
4
+ require_relative 'code_generation_common'
5
+ require_relative 'swift_types'
6
+
7
+ module SwiftGenerator
8
+
9
+ # A Swift File to be generated. This file contains one or more elements
10
+ # Elements can be classes (Future: Structs, Enums, Functions)
11
+ class SwiftFile
12
+ attr_accessor :file_name
13
+ attr_accessor :file_path
14
+ attr_accessor :elements
15
+ attr_accessor :import_statements
16
+ attr_accessor :is_user_file
17
+ attr_accessor :company_name
18
+ attr_accessor :include_editing_warnings
19
+
20
+ def initialize (name, root_path, is_user_file:false, company_name:"<My Entity>")
21
+ name += '.swift' unless name.end_with?( '.swift' )
22
+ @file_name = name
23
+ @file_path = File.join(root_path, @file_name)
24
+
25
+ puts( "--- SwiftFile name = #{name} root_path = #{root_path} file_path = #{@file_path}" )
26
+
27
+ @is_user_file = is_user_file
28
+ @elements = []
29
+ @import_statements = []
30
+ @include_editing_warnings = false
31
+ @company_name = company_name
32
+ end
33
+
34
+ def add_element(swift_class)
35
+ @elements << swift_class
36
+ end
37
+
38
+ # Called before all other generation-time methods.
39
+ # Give user-defined elements ( classes, etc. ) the opportunity to construct other related or required elements
40
+ def prepare_supporting_elements
41
+ @elements.each do |element|
42
+ element.prepare_supporting_elements
43
+ end
44
+ end
45
+
46
+ def prepare_for_generation()
47
+ @elements.each do |element|
48
+ element.prepare_for_generation
49
+ end
50
+ end
51
+
52
+ # @param [String] module_name
53
+ def add_import(module_name)
54
+ import_statement = "import #{module_name}"
55
+ return if @import_statements.include?(import_statement)
56
+ @import_statements << import_statement
57
+ end
58
+ end
59
+
60
+ # Base class for Struct, Enum and Class
61
+ class SwiftNonPrimitive
62
+ attr_accessor :definition_set
63
+ attr_accessor :type_name
64
+ attr_accessor :specified_type_name
65
+ attr_accessor :access_control_modifier
66
+ attr_accessor :inheritance_list # Raw parent list
67
+
68
+ attr_accessor :file_name
69
+ attr_accessor :source_file
70
+
71
+ attr_accessor :properties
72
+ attr_accessor :initializers
73
+ attr_accessor :methods
74
+
75
+ attr_accessor :class_characteristics
76
+ attr_accessor :is_user_editable
77
+ attr_accessor :is_test_element
78
+
79
+ def initialize( definition_set, specified_type_name, inheritance_list=[], type_name:nil,
80
+ file_name: nil, characteristics:[], is_user_editable: false, is_test_element: false )
81
+ @definition_set = definition_set
82
+ @specified_type_name = specified_type_name
83
+ @inheritance_list = inheritance_list
84
+
85
+ @type_name = type_name.nil? ? specified_type_name : type_name
86
+ @file_name = file_name
87
+ #@access_control_modifier = 'public '
88
+ @access_control_modifier = ''
89
+
90
+ @class_characteristics = [* characteristics]
91
+ @is_user_editable = is_user_editable
92
+ @is_test_element = is_test_element
93
+
94
+ @methods = []
95
+ @initializers = []
96
+ @properties = []
97
+ @post_super_initializations = {}
98
+
99
+ # This class will now be added to the definition set and its source file
100
+ # The source file will be created if needed.
101
+ @definition_set.add_element(self)
102
+
103
+ # Source file is now set
104
+ @source_file.add_import('Foundation')
105
+ end
106
+
107
+ def transient_properties()
108
+ @properties.select { |prop| !prop.is_persistent }
109
+ end
110
+
111
+ def persistent_properties()
112
+ @properties.select { |prop| prop.is_persistent }
113
+ end
114
+
115
+ def comparable_properties()
116
+ persistent_properties
117
+ end
118
+
119
+ def resolve_property_types()
120
+ @properties.each do |property|
121
+ property.resolve_type
122
+ end
123
+ end
124
+
125
+ def swift_type_symbol
126
+ return @type_name.to_sym
127
+ end
128
+
129
+ def make_property_type; raise $UNIMPLEMENTED; end
130
+ def prepare_supporting_elements; raise $UNIMPLEMENTED; end
131
+ def prepare_for_generation; raise $UNIMPLEMENTED; end
132
+ end
133
+
134
+ $default_swift_enum_characteristics = [:indexed, :make_test_support]
135
+
136
+ class SwiftEnum < SwiftNonPrimitive
137
+ attr_accessor :enum_flavor
138
+ attr_accessor :enum_raw_type
139
+ attr_accessor :is_integral_type
140
+
141
+ attr_accessor :enum_cases
142
+
143
+ def initialize( definition_set, type_name, inheritance_list=[], file_name: nil, characteristics:[] )
144
+ determine_raw_type( inheritance_list )
145
+ super( definition_set, type_name, inheritance_list=inheritance_list, file_name: file_name, characteristics:characteristics )
146
+
147
+ @is_integral_type = $SwiftIntegralTypes.include? @enum_raw_type
148
+ @enum_cases = []
149
+ end
150
+
151
+
152
+ def add_case( enum_case )
153
+ @enum_cases << enum_case
154
+ end
155
+
156
+ def make_property_type()
157
+ # Make a type for this class so that references to this class can be resolved
158
+ # Includes code for hashing this enum
159
+
160
+ test_value_lambda = lambda{|num| make_test_value(num) }
161
+ property_type = StringEnumPropertyType.new( self, @enum_raw_type.to_sym, test_value:test_value_lambda )
162
+ property_type.hashable_value_lambda = lambda{|var_name, is_optional|
163
+ if is_optional
164
+ return "#{var_name}?.toIndex()"
165
+ else
166
+ return "#{var_name}.toIndex()"
167
+ end
168
+ }
169
+
170
+ #TODO Fix this Horror
171
+ property_type.custom_unmarshaling = lambda{|var_name, unwrapped_var|
172
+ "#{var_name} = #{unmarshal_expression(unwrapped_var)}"
173
+ }
174
+
175
+ return self.swift_type_symbol, property_type
176
+ end
177
+
178
+ def determine_raw_type(inheritace_list)
179
+ return if inheritace_list.empty?
180
+ possible_raw_type = inheritace_list[0]
181
+ @enum_raw_type = possible_raw_type if $SwiftLegalEnumTypeNames.include? possible_raw_type
182
+ end
183
+
184
+
185
+ def prepare_supporting_elements()
186
+ end
187
+
188
+
189
+ def prepare_for_generation()
190
+ make_enumeration_properties
191
+ make_indexing_methods
192
+ # make_test_support() if characteristics.include? :make_test_support
193
+ end
194
+
195
+
196
+ def make_enumeration_properties()
197
+ # The count of all cases for convenience
198
+ p_count = SwiftProperty.new(self, 'caseCount', :Int, initialization_value:self.enum_cases.count )
199
+ p_count.property_qualifiers = 'static'
200
+
201
+ # An array of all cases in declaration order
202
+ case_list = @enum_cases.map{ |a_case| ".#{a_case.case_name}" }.join( ', ' )
203
+ p_all_cases = SwiftProperty.new(self, 'allCases', self.type_name.to_sym , collection_type: :array, initialization_value:"[#{case_list}]" )
204
+ p_all_cases.property_qualifiers = 'static'
205
+ end
206
+
207
+ def make_indexing_methods
208
+ # .toIndex() -> Int
209
+ to_index_m = SwiftMethod.new(self, "toIndex", '', 'Int', comment: '/// USI standard enum method to get the case count')
210
+ to_index_m << '// Support for indexing for hashing, etc.'
211
+
212
+ to_index_m << "switch( self ) {"
213
+ i=0
214
+ for a_case in self.enum_cases
215
+ to_index_m._i "case #{a_case.case_name} : return #{i}"
216
+ to_index_m._o ""
217
+ i += 1
218
+ end
219
+ to_index_m.ii "default : return 0"
220
+ to_index_m << "}"
221
+
222
+ # Not Yet Required
223
+ # static fromIndex( Int ) -> Enum
224
+ # from_index_m = SwiftMethod.new(self, "fromIndex", 'index:Int', type_name, comment: '/// USI standard enum method to get the case count')
225
+ # from_index_m.func_qualifiers = 'static'
226
+ # from_index_m << "return #{@type_name}.allCases[ index ]" #Note: this is not safe
227
+ end
228
+
229
+ # JSON marshaling support
230
+ def marshal_expression( name )
231
+ #Probably only works for String enums
232
+ return "#{name}.rawValue"
233
+ end
234
+
235
+ def unmarshal_expression( name )
236
+ #Probably only works for String enums
237
+ return "#{@type_name}( rawValue:#{name} )"
238
+ end
239
+
240
+ # Test Support
241
+ def make_test_value(index)
242
+ return "#{@type_name}.allCases[ #{index} % #{@type_name}.caseCount ]"
243
+ end
244
+ end
245
+
246
+ class SwiftEnumCase
247
+ attr_accessor :enum_def
248
+ attr_accessor :raw_value
249
+ attr_accessor :case_name
250
+
251
+ def initialize( enum_def, case_name, raw_value=nil )
252
+ @enum_def = enum_def
253
+ @raw_value = raw_value
254
+ @case_name = case_name
255
+
256
+ enum_def.add_case( self )
257
+ end
258
+
259
+ def declaration_lines()
260
+ raw_value_literal = @raw_value
261
+ case enum_def.enum_raw_type
262
+ when "String"
263
+ raw_value_literal = "\"#{@raw_value}\""
264
+ end
265
+
266
+ if raw_value.nil?
267
+ ["case #{@case_name}"]
268
+ else
269
+ ["case #{@case_name} = #{raw_value_literal}"]
270
+ end
271
+ end
272
+
273
+ end
274
+
275
+
276
+
277
+
278
+ # class SwiftNumericEnum
279
+ # attr_accessor :enum_flavor
280
+ # attr_accessor :enum_raw_type
281
+ # attr_accessor :enum_cases
282
+ #
283
+ # def initialize( definition_set, type_name, file_name: nil, characteristics:[] )
284
+ # super( definition_set, type_name, file_name: file_name, characteristics:characteristics )
285
+ # end
286
+ #
287
+ # def prepare_supporting_elements()
288
+ # super()
289
+ # end
290
+ #
291
+ # def prepare_for_generation()
292
+ # super()
293
+ # end
294
+ # end
295
+ #
296
+ # class SwiftStringEnum
297
+ # attr_accessor :enum_flavor
298
+ # attr_accessor :enum_raw_type
299
+ # attr_accessor :enum_cases
300
+ #
301
+ # def initialize( definition_set, type_name, file_name: nil, characteristics:[] )
302
+ # super( definition_set, type_name, file_name: file_name, characteristics:characteristics )
303
+ # end
304
+ #
305
+ # def prepare_supporting_elements()
306
+ # super()
307
+ # end
308
+ #
309
+ # def prepare_for_generation()
310
+ # super()
311
+ # end
312
+ # end
313
+
314
+
315
+ #
316
+ #
317
+ #
318
+ #
319
+ # @do_generate = true
320
+ #
321
+ # @properties = []
322
+ # @methods = []
323
+ # #@access_control_modifier = 'public '
324
+ # @access_control_modifier = ''
325
+ #
326
+ # # This class will now be added to the definition set and its source file
327
+ # # The source file will be created if needed.
328
+ #
329
+ # # Source file is now set
330
+ # @source_file.add_import('Foundation')
331
+ # @supporting_elements_created = false
332
+ # end
333
+ # end
334
+
335
+
336
+ # class SwiftStringEnum < SwiftEnum
337
+ #
338
+ # end
339
+
340
+
341
+ #change
342
+ $default_swift_class_characteristics = []
343
+ # $default_swift_class_characteristics = [:json_serializable, :comparable, :make_test_class, :auto_describing]
344
+
345
+ # A Swift Class to be generated.
346
+ class SwiftClass < SwiftNonPrimitive
347
+ attr_accessor :parent_class # The parent class.
348
+
349
+ # Initializations
350
+ attr_accessor :post_super_initializations
351
+
352
+ # Associated classes
353
+ attr_accessor :auto_test_class
354
+
355
+ # Convenience
356
+ attr_accessor :access_control_modifier
357
+ attr_accessor :do_generate
358
+ attr_accessor :supporting_elements_created
359
+ attr_accessor :test_object_method
360
+
361
+
362
+ # @param [SwiftDefinitionSet] definition_set
363
+ # @param [String] type_name
364
+ # @param [Array] inherited_from
365
+ # @param [Object] file_name
366
+ # @param [Object] characteristics
367
+ # @param [Object] is_test_class
368
+ def initialize (definition_set, specified_type_name, inheritance_list=[], file_name: nil,
369
+ characteristics:$default_swift_class_characteristics, is_test_element: false, is_user_editable: false)
370
+ # Generate the type name from the specified type name. Non-editable classes are prepended with "_"
371
+ prefix = characteristics.include?(:create_user_class) ? '_' : ''
372
+ type_name = prefix + specified_type_name
373
+
374
+ super( definition_set, specified_type_name, inheritance_list=inheritance_list, type_name:type_name, file_name:file_name, characteristics:characteristics, is_user_editable:is_user_editable, is_test_element: is_test_element )
375
+
376
+ @parent_class = nil
377
+
378
+ #change for known & legal combinations of characteristics
379
+ abort( "illegal class characteristics" ) if ! definition_set.characteristics_are_legal(@class_characteristics )
380
+
381
+ # prefix = @class_characteristics.include?(:create_user_class) ? '_' : ''
382
+ # @type_name = prefix + @specified_type_name
383
+
384
+ @do_generate = true
385
+
386
+ @supporting_elements_created = false
387
+ end
388
+
389
+ def post_super_init( values_for_properties )
390
+ @post_super_initializations.merge!( values_for_properties )
391
+ end
392
+
393
+ def add_simple_class_property( name, type, value:nil, mutability: :var, override:false)
394
+ # class variables not supported. Use class property instead.
395
+ p = SwiftProperty.new(self, name, type, mutability )
396
+ p.property_qualifiers = 'class'
397
+ p.property_qualifiers = "override #{p.property_qualifiers}" if override
398
+ p.getter_body = "return #{value}"
399
+ end
400
+
401
+ def make_property_type()
402
+ # Make a type for this class so that references to this class can be resolved
403
+ type_symbol = @type_name.to_sym
404
+
405
+ test_value_lambda = lambda{|num|
406
+ ensure_test_object_method
407
+ test_object_method_call(num)
408
+ }
409
+ property_type = SwiftObjectPropertyType.new( self, :NSDictionary, test_value:test_value_lambda )
410
+ property_type.hashable_value_lambda = lambda{|var_name, is_optional|
411
+ if is_optional
412
+ return "#{var_name}?.hashValue()"
413
+ else
414
+ return "#{var_name}.hashValue()"
415
+ end
416
+ }
417
+
418
+ #TODO Fix this Horror
419
+ property_type.custom_unmarshaling = lambda{|var_name, unwrapped_var| [
420
+ "#{var_name} = #{unmarshal_expression(unwrapped_var)}"
421
+ ]}
422
+
423
+ return self.swift_type_symbol, property_type
424
+ end
425
+
426
+
427
+ # JSON marshaling support
428
+ def insert_marshal_expression( m, unwrapped_var, destination )
429
+ #Probably only works for String enums
430
+ m << "let objectDictionary = NSMutableDictionary()"
431
+ m << "#{unwrapped_var}.marshalToJSON( objectDictionary )"
432
+ m << "#{destination} = objectDictionary"
433
+ end
434
+
435
+ def insert_unmarshal_expression( m, unwrapped_value, destination )
436
+ #Probably only works for String enums
437
+ m << "let temp = #{self.type_name}()"
438
+ m << "temp.unmarshalFromJSON( #{unwrapped_value} )"
439
+ # TODO: validate?
440
+ m << "#{destination} = temp"
441
+ end
442
+
443
+ # TODO: Future? unwrapping and success variable.
444
+ # def insert_unmarshal_expression( m, jsonElement, destination, success_boolean )
445
+ # #Probably only works for String enums
446
+ # m << "if let objectDictionary = #{jsonElement} as? NSDictionary {"
447
+ # m._i "let temp = #{self.type_name}()"
448
+ # m << "temp.unmarshalFromJSON( objectDictionary )"
449
+ # m << "#{destination} = temp"
450
+ # m._o "#{success_boolean} = true"
451
+ # m << "} else {"
452
+ # m.ii "#{success_boolean} = false"
453
+ # m << "}"
454
+ # end
455
+
456
+ # Called before all other generation-time methods.
457
+ # Construct other related or required elements
458
+ # May be called more than once
459
+ def prepare_supporting_elements()
460
+ return if @supporting_elements_created
461
+
462
+ create_user_classes if @class_characteristics.include?(:create_user_class)
463
+ create_test_classes if ! @is_test_element && @class_characteristics.include?(:make_test_class)
464
+
465
+ @supporting_elements_created = true
466
+ end
467
+
468
+
469
+ def resolve_inheritance()
470
+ return if @inheritance_list.empty?
471
+ @parent_class = @definition_set.elements_by_name[ @inheritance_list[0] ]
472
+ end
473
+
474
+
475
+ def prepare_for_generation()
476
+ create_init_methods
477
+ create_equality_methods if @class_characteristics.include?(:comparable)
478
+ prepare_marshaling_code if @class_characteristics.include?(:json_serializable)
479
+ create_description_methods if @class_characteristics.include?(:auto_describing)
480
+ create_copy_methods if @class_characteristics.include?(:json_serializable)
481
+ end
482
+
483
+
484
+ def create_init_methods()
485
+ # Only no-argument init methods & optional properties are currently supported
486
+
487
+ return if @post_super_initializations.empty?
488
+ init_m = SwiftInitializer.new(self, 'init', nil, override: true)
489
+ init_m << "super.init()"
490
+ keys = @post_super_initializations.keys.sort!
491
+ keys.each do |prop_name|
492
+ init_m << "#{prop_name} = #{@post_super_initializations[prop_name]}"
493
+ end
494
+ end
495
+
496
+
497
+ def create_equality_methods()
498
+ comparable_super = super_has_characteristics(:comparable)
499
+
500
+ #func isEqual(_ anObject: AnyObject!) -> Bool
501
+ is_equals_method = SwiftMethod.new(self, 'isEqual', 'other:AnyObject!', 'Bool', override: true)
502
+ is_equals_method << "if( other == nil ) { return false }"
503
+ other_typed_var = "other#{@type_name}"
504
+ if comparable_super
505
+ is_equals_method << "if( !super.isEqual( other )) { return false }"
506
+ end
507
+
508
+ is_equals_method << ""
509
+ is_equals_method << "if let #{other_typed_var} = other as? #{@type_name} {"
510
+
511
+ comparable_properties.each do |property|
512
+ other_name = "#{other_typed_var}.#{property.property_name}"
513
+
514
+ if property.property_type.nil?
515
+ puts( property.property_name )
516
+ end
517
+
518
+ if property.collection_type == :array
519
+ is_equals_method.ii "if( !optionalArraysEqual( #{property.property_name}, #{other_name} )) { return false }"
520
+ else
521
+ custom_test = property.property_type.custom_equality_test
522
+ if custom_test.nil?
523
+ is_equals_method.ii "if( self.#{property.property_name} != #{other_name} ) { return false }"
524
+ else
525
+ is_equals_method.ii "if( !#{custom_test.call(property.property_name, other_name)} ) { return false }"
526
+ end
527
+ end
528
+ end
529
+ is_equals_method << "" << "\treturn true"
530
+ is_equals_method << "} else {"
531
+ is_equals_method << "\treturn false"
532
+ is_equals_method << "}"
533
+
534
+ #- (NSUInteger)hash
535
+ hash_method = SwiftMethod.new(self, 'hashValue', '', 'Int', override: comparable_super)
536
+ hash_method << "var hasher = Hasher() // Hasher must be mutable" << ""
537
+
538
+ if comparable_super
539
+ hash_method << 'hasher.hashIn( super.hashValue() )'
540
+ end
541
+
542
+ comparable_properties.each do |property|
543
+ hash_element = lambda{ |var_name, is_optional| "hasher.hashIn( #{property.property_type.hashable_value(var_name, is_optional )} )"}
544
+
545
+ if( property.collection_type.nil?)
546
+ hash_method << hash_element.call(property.property_name, property.mutability_type.must_be_unwrapped)
547
+ elsif( property.collection_type == :array )
548
+ arrayName = "#{property.property_name}Array"
549
+ hash_method << "if let #{arrayName} = #{property.property_name} {"
550
+ hash_method._i "for element in #{arrayName} {"
551
+ hash_method.ii hash_element.call("element", false)
552
+ hash_method._o "}"
553
+ hash_method << "}"
554
+ else
555
+ hash_method << "ERROR: hashing of #{property.collection_type.to_s} collections not supported."
556
+ end
557
+ end
558
+ hash_method << "" << "return hasher.hash"
559
+ end
560
+
561
+ def prepare_marshaling_code()
562
+ super_does_json = super_has_characteristics(:json_serializable)
563
+
564
+ # Reader & Writer
565
+ reader = SwiftMethod.new(self, 'unmarshalFromJSON', 'jsonObject:NSDictionary', nil, override: super_does_json )
566
+ writer = SwiftMethod.new(self, 'marshalToJSON', 'jsonObject:NSMutableDictionary', nil, override: super_does_json )
567
+
568
+ if super_does_json
569
+ reader << "super.unmarshalFromJSON( jsonObject )"
570
+ writer << "super.marshalToJSON( jsonObject )"
571
+ end
572
+
573
+ writer << "" << "jsonObject[\"objectType\"] = \"#{@type_name}\""
574
+
575
+ persistent_properties.each do |prop|
576
+ prop.unmarshal_code(reader)
577
+ prop.marshal_code(writer)
578
+ end
579
+
580
+ # Object Reader
581
+ object_reader = SwiftMethod.new(self, "objectFromJSON", 'newObjData:NSDictionary', @specified_type_name, override: super_does_json )
582
+ object_reader.func_qualifiers = 'class'
583
+
584
+ object_reader << "let newObj = #{@specified_type_name}()"
585
+ object_reader << "newObj.unmarshalFromJSON( newObjData )"
586
+ object_reader << 'return newObj'
587
+
588
+ # Array Reader & Writer
589
+
590
+ # Array Reader
591
+ array_reader = SwiftMethod.new(self, "arrayFromJSON", 'json:NSArray', '[USIBaseModel]', override: super_does_json )
592
+ array_reader.func_qualifiers = 'class'
593
+
594
+ array_reader << "var newObjects = [#{@specified_type_name}]()"
595
+ array_reader << "for objEntry in json {"
596
+ array_reader << "\tlet newObj = #{@specified_type_name}()"
597
+
598
+ array_reader << "\tif let newObjData = objEntry as? NSDictionary {"
599
+ array_reader << "\t\tnewObj.unmarshalFromJSON( newObjData )"
600
+ array_reader << "\t\tnewObjects.append( newObj )"
601
+
602
+ array_reader << "\t}"
603
+ array_reader << '}'
604
+ array_reader << 'return newObjects'
605
+
606
+ # Array Writer
607
+ array_writer = SwiftMethod.new(self, "arrayToJSON", "array:[USIBaseModel]", 'NSMutableArray', override: super_does_json )
608
+ array_writer.func_qualifiers = 'class'
609
+
610
+ array_writer << 'var dataArray = NSMutableArray()'
611
+
612
+ array_writer << "for obj in array {"
613
+ array_writer << "\tlet objData = NSMutableDictionary()"
614
+ array_writer << "\tobj.marshalToJSON( objData )"
615
+ array_writer << "\tdataArray.addObject( objData )"
616
+ array_writer << "}"
617
+ array_writer << "return dataArray"
618
+
619
+ end
620
+
621
+ # Copy methods
622
+ def create_copy_methods()
623
+ copy_to_method_name = "copyTo#{@type_name}"
624
+
625
+ # The copy method - calls copyTo so that we can avoid duplicating copy code
626
+ copy_m = SwiftMethod.new(self, 'copy', '', 'AnyObject', override: true)
627
+ copy_m << "let theCopy = #{@type_name}()"
628
+ copy_m << "#{copy_to_method_name}( theCopy )"
629
+ copy_m << "return theCopy"
630
+
631
+ copy_to_m = SwiftMethod.new(self, copy_to_method_name, "other: #{@type_name}", nil, override:false)
632
+
633
+ if !@parent_class.nil?
634
+ copy_to_m << "super.copyTo#{@parent_class.type_name}( other )"
635
+ end
636
+ self.persistent_properties.each do |prop|
637
+ copy_to_m << "\tother.#{prop.property_name} = #{prop.property_name}"
638
+ end
639
+ end
640
+
641
+ # Description methods
642
+ def create_description_methods()
643
+
644
+ # description
645
+ dm = SwiftMethod.new(self, 'description', 'indent:String=" ", diagnostic:Bool=false', "NSString", override:!@parent_class.nil? )
646
+ dm << "let d = NSMutableString()"
647
+
648
+ if @parent_class.nil?
649
+ else
650
+ dm << "d.appendString( super.description(indent:indent, diagnostic:diagnostic) )"
651
+ end
652
+
653
+ dm << "if( diagnostic ) {"
654
+ dm.ii "d.appendString( \" properties of class #{@type_name} :\\n\")"
655
+ dm.ii "d.appendString( \"\\(indent)- none -\\n\")" if @properties.empty?
656
+ dm << "}"
657
+
658
+ self.persistent_properties.each do |prop|
659
+ if( prop.is_optional )
660
+ prop_value = prop.property_name
661
+ if prop.property_type.swift_kind == :enum
662
+ dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(prettyFormatEnum( #{prop_value} ))\\n\" )"
663
+ else
664
+ dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(prettyFormat( #{prop_value} ))\\n\" )"
665
+ end
666
+
667
+ else
668
+ dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(#{prop_value})\\n\" )"
669
+ end
670
+ end
671
+
672
+ dm << "return d"
673
+
674
+ # className
675
+ type_name_m = SwiftMethod.new(self, 'className', nil, 'String', override:!@parent_class.nil? )
676
+ type_name_m .func_qualifiers = 'class'
677
+ type_name_m << "return \"#{@type_name}\""
678
+ end
679
+
680
+ # User class generation
681
+ def create_user_classes
682
+ user_class_characteristics = @class_characteristics - [:make_test_class, :create_user_class]
683
+ @user_editable_class = SwiftClass.new(@definition_set, @specified_type_name, [@type_name], characteristics:user_class_characteristics, is_user_editable:true )
684
+ end
685
+
686
+ # Test class generation
687
+ def create_test_classes()
688
+ tc = @auto_test_class = SwiftUnitTestClass.new(@definition_set, self, 'AutoGenerated')
689
+ end
690
+
691
+ # Utility
692
+ def super_has_characteristics( *characteristics )
693
+ remaining_characteristics = characteristics
694
+ ancestor = @parent_class
695
+ until ancestor.nil? || remaining_characteristics.empty?
696
+ remaining_characteristics = (remaining_characteristics - ancestor.class_characteristics)
697
+ ancestor = ancestor.parent_class
698
+ end
699
+
700
+ return remaining_characteristics.empty?
701
+ end
702
+
703
+ def set_test_values( set_method, variable_name, indexing_number )
704
+ if ! @parent_class.nil?
705
+ indexing_number = @parent_class.set_test_values( set_method, variable_name, indexing_number )
706
+ end
707
+
708
+ comparable_properties.each do |prop|
709
+ set_method << "#{variable_name}.#{prop.property_name} = #{prop.make_test_value(indexing_number)}"
710
+ indexing_number += 1
711
+ end
712
+
713
+ return indexing_number
714
+ end
715
+
716
+ # Test Support ( Here for embedded objects )
717
+ def ensure_test_object_method
718
+ #NOTE: this method is in the global test support class
719
+ return if !@test_object_method.nil?
720
+
721
+ comment = "/// Create a test #{@type_name} object with varying values"
722
+ # e.g. func makeTestUser() -> User
723
+ obj_name = "test#{@type_name}"
724
+ m = SwiftMethod.new(@definition_set.test_support_class, "makeTest#{@type_name}", '_ index:Int = 0', "#{@type_name}", comment: comment)
725
+ m.func_qualifiers = 'class'
726
+
727
+ m << "let #{obj_name} = #{@type_name}()" << ""
728
+ prop_index = 1
729
+
730
+ set_test_values( m, obj_name, 1 )
731
+
732
+ m << "" << "return #{obj_name}"
733
+ @test_object_method = m
734
+ end
735
+
736
+ def test_object_method_call(index=0)
737
+ argStr = index == 0 ? '' : "#{index}"
738
+ "#{@definition_set.test_support_class.type_name}.makeTest#{@type_name}(#{argStr})"
739
+ end
740
+ end
741
+
742
+
743
+ class SwiftUnitTestClass < SwiftClass
744
+ attr_accessor :tested_class
745
+ attr_accessor :tested_class_name
746
+
747
+ # @param [SwiftClass] tested_class
748
+ # @param [String] class_purpose
749
+ def initialize(definition_set, tested_class, class_purpose)
750
+ @tested_class = tested_class
751
+ @tested_class_name = tested_class.type_name
752
+ class_name = tested_class.specified_type_name + class_purpose + "Test"
753
+ super(definition_set, class_name, ['XCTestCase'], file_name: class_name, is_test_element: true, characteristics:[] )
754
+ @source_file.add_import('XCTest')
755
+ end
756
+
757
+
758
+ def prepare_for_generation()
759
+ super()
760
+
761
+ generate_copy_test
762
+ generate_marshal_test
763
+ generate_json_round_trip_test
764
+ end
765
+
766
+ def generate_copy_test
767
+ ensure_test_object_method
768
+
769
+ comment = "/// Test copy() implementation. Requires isEqual()"
770
+ m = SwiftMethod.new(self, "testCopying", '', '', comment: comment)
771
+ m << "let original = #{test_object_method_call()}"
772
+ m << "let theCopy = original.copy() as #{@tested_class_name}"
773
+
774
+ m << "if( theCopy != original ) {"
775
+ m << " print(\"original\")"
776
+ m << " print(original.description())"
777
+ m << " print(\"theCopy\")"
778
+ m << " print(theCopy.description())"
779
+ m << "}"
780
+
781
+ m << "XCTAssertEqual( theCopy, original, \"copy does not match original\" )"
782
+ end
783
+
784
+
785
+ def generate_marshal_test
786
+ ensure_test_object_method
787
+
788
+ comment = "/// Test Marshaling to JSON-compatible dictionaries"
789
+ m = SwiftMethod.new(self, 'testMarshaling', '', nil, comment: comment)
790
+ m << "let original = #{test_object_method_call()}"
791
+
792
+ m << "" << "let jsonObject = NSMutableDictionary()"
793
+ m << "original.marshalToJSON( jsonObject )" << ""
794
+
795
+ m << "let theCopy = #{@tested_class_name}()"
796
+ m << "theCopy.unmarshalFromJSON( jsonObject )" << ""
797
+
798
+ m << "if( theCopy != original ) {"
799
+ m._i "print(\"original\")"
800
+ m << "print(original.description())"
801
+ m << "print(\"theCopy\")"
802
+ m._o "print(theCopy.description())"
803
+ m << "}"
804
+
805
+ m << "XCTAssertEqual( theCopy, original, \"unmarshalled copy does not match original\" )"
806
+
807
+ end
808
+
809
+
810
+ def generate_json_round_trip_test
811
+ ensure_test_object_method
812
+ comment = "/// Test JSON round trip for a single object"
813
+ m = SwiftMethod.new(self, 'testJSONRoundTrip', '', nil, comment: comment)
814
+ m << "let original = #{test_object_method_call()}"
815
+
816
+ m << "" << "let jsonObject = NSMutableDictionary()"
817
+ m << "original.marshalToJSON( jsonObject )" << ""
818
+
819
+ m << "var error: NSError? = nil"
820
+ m << "let jsonData = NSJSONSerialization.dataWithJSONObject(jsonObject, options: nil, error:&error) as NSData?"
821
+ m << "XCTAssertNotNil( jsonData, \"Could not serialize to NSData\" )"
822
+
823
+ m << "var deserializedJSON:AnyObject? = NSJSONSerialization.JSONObjectWithData(jsonData!, options: nil, error:&error)"
824
+ m << "XCTAssertNotNil( deserializedJSON, \"Could not serialize to NSData\" )"
825
+
826
+ m << "if let newJSONObject = deserializedJSON as? NSDictionary {"
827
+ m._i "let theCopy = #{@tested_class_name}()"
828
+ m << "theCopy.unmarshalFromJSON( newJSONObject )"
829
+
830
+ m << "if( theCopy != original ) {"
831
+ m._i "print(\"original\")"
832
+ m << "print(original.description())"
833
+ m << "print(\"theCopy\")"
834
+ m._o "print(theCopy.description())"
835
+ m << "}"
836
+
837
+ m._o "XCTAssertEqual( theCopy, original, \"unmarshalled object should be == to original\" )"
838
+ m << "} else {"
839
+ m.ii "XCTAssert( false, \"JSON did not deserialize to an NSDictionary\" )"
840
+ m << "}"
841
+ end
842
+
843
+ # Utility
844
+ def ensure_test_object_method
845
+ @tested_class.ensure_test_object_method
846
+ end
847
+
848
+
849
+ def test_object_method_call(index=0)
850
+ @tested_class.test_object_method_call( index )
851
+ end
852
+ end
853
+
854
+
855
+ class SwiftProperty
856
+ attr_accessor :swift_class
857
+ attr_accessor :property_name
858
+ attr_accessor :property_type_symbol
859
+ attr_accessor :property_type
860
+ attr_accessor :mutability_type
861
+ attr_accessor :property_qualifiers
862
+ attr_accessor :is_persistent
863
+ attr_accessor :collection_type
864
+ attr_accessor :required
865
+
866
+ attr_accessor :initialization_value
867
+ attr_accessor :getter_body
868
+ attr_accessor :setter_body
869
+ attr_accessor :rest_omit
870
+
871
+
872
+ def initialize(swift_class, property_name, property_type_symbol, mutability= :let, initialization_value:nil, collection_type: nil, required: true, rest_omit:nil )
873
+ @swift_class = swift_class
874
+ @property_name = property_name
875
+ @property_type_symbol = property_type_symbol
876
+ @property_type = nil
877
+ # @property_type = swift_class.definition_set.property_type_for_symbol(property_type)
878
+ @mutability_type = SwiftDefinitionSet.mutability_types[mutability]
879
+ @is_persistent = false
880
+ @collection_type = collection_type
881
+ @required = required
882
+
883
+ #@access_control_modifier = 'public '
884
+ @access_control_modifier = ''
885
+ @property_qualifiers = nil
886
+
887
+ @initialization_value = initialization_value
888
+ @getter_body = nil
889
+ @setter_body = nil
890
+ @rest_omit = rest_omit
891
+
892
+ swift_class.properties << self
893
+ end
894
+
895
+ def declaration_lines
896
+
897
+ qualifiers = []
898
+ qualifiers += [*@property_qualifiers] if !@property_qualifiers.nil?
899
+ qualifiers << @mutability_type.mutability
900
+
901
+ declaration = "#{qualifiers.join(' ')} #{@property_name} : #{full_type_specifier()}"
902
+
903
+ # Initial Value
904
+ initial_value = @initialization_value
905
+ if !initial_value.nil?
906
+ if( collection_type == :array )
907
+ if( @mutability_type.mutability_id == :optional )
908
+ # Initialize variable arrays to empty by default
909
+ initial_value = "[]"
910
+ end
911
+
912
+ end
913
+ end
914
+
915
+ if (!initial_value.nil?)
916
+ declaration += " = #{initial_value}"
917
+ end
918
+
919
+ # Computed Properties
920
+ if !( @getter_body.nil? && @setter_body.nil? )
921
+ declaration = [declaration + " {"]
922
+
923
+ if !@getter_body.nil?
924
+ declaration << "\tget {"
925
+ declaration.concat([*@getter_body].map { |line| "\t\t" + line })
926
+ declaration << "\t}"
927
+ end
928
+
929
+ if !@setter_body.nil?
930
+ declaration << "" unless @getter_body.nil?
931
+ declaration << "\tset {"
932
+ declaration.concat([*@setter_body].map { |line| "\t\t" + line })
933
+ declaration << "\t}"
934
+ end
935
+
936
+ declaration << "}"
937
+ end
938
+
939
+ return [*declaration]
940
+ end
941
+
942
+ def full_type_specifier
943
+ # Apply Collection
944
+ full_type_name = @property_type.swift_type_name
945
+ if @collection_type == :array
946
+ full_type_name = "[#{full_type_name}]"
947
+ end
948
+
949
+ "#{full_type_name}#{@mutability_type.declaration_wrapping}"
950
+ end
951
+
952
+
953
+ def resolve_type()
954
+ @property_type = @swift_class.definition_set.property_type_for_symbol(@property_type_symbol)
955
+ abort( "No property type found for #{@property_type_symbol.to_s}") if @property_type.nil?
956
+ end
957
+
958
+
959
+ def make_test_value(index)
960
+ if @collection_type.nil?
961
+ return @property_type.make_test_value(index)
962
+ else
963
+ return '[]'
964
+ end
965
+ end
966
+
967
+ def property_declared_type
968
+ @property_type.swift_type_name + @mutability_type.declaration_wrapping
969
+ end
970
+
971
+ #Utility
972
+ def is_array_of_nsobject
973
+ (@collection_type == :array) && (@property_type.swift_kind == :class)
974
+ end
975
+
976
+ def is_optional
977
+ return @mutability_type.mutability_id == :optional
978
+ end
979
+ end
980
+
981
+
982
+ class SwiftPersistentProperty < SwiftProperty
983
+ attr_accessor :json_key
984
+
985
+ def initialize(swift_class, property_name, property_type, mutability=:let, initialization_value=nil, collection_type: nil, json_key: nil, rest_omit:nil )
986
+ super(swift_class, property_name, property_type, mutability, initialization_value:initialization_value, collection_type: collection_type, rest_omit:rest_omit)
987
+ @is_persistent = true
988
+ @json_key = json_key.nil? ? property_name : json_key
989
+ end
990
+
991
+ def marshal_code(marshal_method)
992
+ if @collection_type.nil?
993
+ marshal_single_element(marshal_method)
994
+ else
995
+ case @collection_type
996
+
997
+ when :array
998
+ marshal_array(marshal_method)
999
+
1000
+ when :dictionaryByString
1001
+
1002
+ else
1003
+ puts "ERROR: Unknown collection_type: #{@collection_type.to_s}"
1004
+ end
1005
+ end
1006
+ end
1007
+
1008
+ def marshal_single_element(marshal_method)
1009
+ return if self.rest_omit == :omit
1010
+ omit_null = self.rest_omit == :omit_if_null
1011
+
1012
+ case @property_type.swift_kind
1013
+ when :primitive
1014
+ case @mutability_type.mutability_id
1015
+ when :optional
1016
+ marshal_template(marshal_method, omit_null){ |m, unwrapped_var, destination|
1017
+ m.<< "#{destination} = #{unwrapped_var}"
1018
+ }
1019
+ else
1020
+ marshal_method << "ERROR Non-optional properties not yet supported"
1021
+ end
1022
+
1023
+ when :enum
1024
+ case @mutability_type.mutability_id
1025
+ when :optional
1026
+ marshal_template(marshal_method, omit_null){ |m, unwrapped_var, destination|
1027
+ # TODO: Make this look like the class property method below
1028
+ m << "#{destination} = #{@property_type.enum.marshal_expression( unwrapped_var)}"
1029
+ }
1030
+ else
1031
+ marshal_method << "ERROR Non-optional properties not yet supported"
1032
+ end
1033
+
1034
+ when :class
1035
+ case @mutability_type.mutability_id
1036
+ when :optional
1037
+ marshal_template(marshal_method, omit_null){ |m, unwrapped_var, destination|
1038
+ @property_type.swift_class.insert_marshal_expression( m, unwrapped_var, destination)
1039
+ }
1040
+ else
1041
+ marshal_method << "ERROR Non-optional properties not yet supported"
1042
+ end
1043
+
1044
+ when :struct
1045
+ return 'Structs not yet supported'
1046
+
1047
+ else
1048
+ puts "ERROR: Unknown swift_kind: #{@property_type.swift_kind.to_s}"
1049
+ end
1050
+ end
1051
+
1052
+
1053
+ def marshal_array(marshal_method)
1054
+ return if self.rest_omit == :omit
1055
+ omit_null = self.rest_omit == :omit_if_null
1056
+
1057
+ case @property_type.swift_kind
1058
+ when :primitive
1059
+ if( @mutability_type.mutability_id != :optional )
1060
+ marshal_method << "ERROR Non-optional properties not yet supported"
1061
+ end
1062
+
1063
+ marshal_template(marshal_method, omit_null) { |m, unwrapped_var, destination|
1064
+ m << "var newArray = NSMutableArray()"
1065
+ m << "for element in #{unwrapped_var} {"
1066
+ m.ii "newArray.addObject( element as #{@property_type.serialized_json_type.to_s} )"
1067
+ m << "}"
1068
+ m << "#{destination} = newArray"
1069
+ }
1070
+
1071
+ when :class
1072
+ if( @mutability_type.mutability_id != :optional )
1073
+ marshal_method << "ERROR Non-optional properties not yet supported"
1074
+ end
1075
+
1076
+ marshal_template(marshal_method, omit_null) { |m, unwrapped_var, destination|
1077
+ m.ii "#{destination} = #{@property_type.swift_type_name}.arrayToJSON(#{unwrapped_var})"
1078
+ }
1079
+
1080
+ when :struct
1081
+ return 'Arrays of structs not yet supported'
1082
+
1083
+ when :enum
1084
+ if( @mutability_type.mutability_id != :optional )
1085
+ marshal_method << "ERROR Non-optional properties not yet supported"
1086
+ end
1087
+
1088
+ marshal_template(marshal_method, omit_null) { |m, unwrapped_var, destination|
1089
+ m << "var newArray = NSMutableArray()"
1090
+ m << "for element in #{unwrapped_var} {"
1091
+ m.ii "newArray.addObject( #{@property_type.enum.marshal_expression( "element" )} )"
1092
+ m << "}"
1093
+ m << "#{destination} = newArray"
1094
+ }
1095
+
1096
+ else
1097
+ puts "ERROR: Unknown swift_kind: #{@property_type.swift_kind.to_s}"
1098
+ end
1099
+ end
1100
+
1101
+
1102
+ def marshal_template(marshal_method, omit_null, &detail)
1103
+ m = marshal_method
1104
+ unwrapped_var = @property_name + "Val"
1105
+ destination = "jsonObject[ \"#{@json_key}\" ]"
1106
+ m << ""
1107
+ m << "if let #{unwrapped_var} = #{@property_name} {"
1108
+
1109
+ if self.property_type.custom_marshaling.nil?
1110
+ m.indent += 1
1111
+ detail.call( m, unwrapped_var, destination)
1112
+ m.indent -= 1
1113
+ else
1114
+ m.ii self.property_type.custom_marshaling.call( destination ,unwrapped_var)
1115
+ end
1116
+ m << "} else {"
1117
+ if( ! omit_null )
1118
+ m << "\t#{destination} = NSNull()"
1119
+ else
1120
+ m << "// Omit nil values"
1121
+ end
1122
+
1123
+ m << "}"
1124
+ end
1125
+
1126
+
1127
+ def unmarshal_code(unmarshal_method)
1128
+ if @collection_type.nil?
1129
+ unmarshal_single_element(unmarshal_method)
1130
+ else
1131
+ case @collection_type
1132
+ when :array
1133
+ unmarshal_array(unmarshal_method)
1134
+
1135
+ when :dictionaryByString
1136
+
1137
+ else
1138
+ puts "ERROR: Unknown collection_type: #{@collection_type.to_s}"
1139
+ end
1140
+ end
1141
+ end
1142
+
1143
+ def unmarshal_single_element(unmarshal_method)
1144
+ m = unmarshal_method
1145
+ case @property_type.swift_kind
1146
+ when :primitive, :enum
1147
+ case @mutability_type.mutability_id
1148
+ when :optional
1149
+ unwrapped_var = @property_name + "Val"
1150
+ m << ""
1151
+ m << "if let #{unwrapped_var} = jsonObject[ \"#{@json_key}\" ] as? #{@property_type.serialized_json_type} {"
1152
+
1153
+ if self.property_type.custom_unmarshaling.nil?
1154
+ m.ii "#{@property_name} = #{unwrapped_var} "
1155
+ else
1156
+ m.ii self.property_type.custom_unmarshaling.call( @property_name, unwrapped_var )
1157
+ end
1158
+
1159
+ m << "} else {"
1160
+ m.ii "#{@property_name} = nil"
1161
+ m << "}"
1162
+ else
1163
+ m << "ERROR Non-optional properties not yet supported"
1164
+ end
1165
+
1166
+ # if let aVal = jsonObject[ "a" ] as? Int {
1167
+ # a = aVal
1168
+ # } else {
1169
+ # a = nil
1170
+ # }
1171
+
1172
+ # if @property_type.auto_bridged
1173
+ # return "#{@property_name} = jsonObject[ \"#{@json_key}\" ]"
1174
+ # else
1175
+ return "#{@property_name} = jsonObject[ \"#{@json_key}\" ] as #{property_declared_type}"
1176
+ # end
1177
+
1178
+ when :class
1179
+ case @mutability_type.mutability_id
1180
+ when :optional
1181
+ unwrapped_var = @property_name + "Val"
1182
+ # TODO: check for success
1183
+ # m << "var success = false"
1184
+ m << "if let #{unwrapped_var} = jsonObject[ \"#{@json_key}\" ] as? #{@property_type.serialized_json_type} {"
1185
+ self.property_type.swift_class.insert_unmarshal_expression( m, unwrapped_var, @property_name )
1186
+ m << "} else {"
1187
+ m.ii "#{@property_name} = nil"
1188
+ m << "}"
1189
+ else
1190
+ m << "ERROR Non-optional properties not yet supported"
1191
+ end
1192
+
1193
+ when :struct
1194
+ return 'Structs not yet supported'
1195
+
1196
+ else
1197
+ puts "ERROR: Unknown swift_kind: #{@property_type.swift_kind.to_s}"
1198
+ end
1199
+ end
1200
+
1201
+ def unmarshal_array(unmarshal_method)
1202
+ m = unmarshal_method
1203
+ case @property_type.swift_kind
1204
+ when :primitive
1205
+ if( @mutability_type.mutability_id == :optional )
1206
+ unwrapped_var = @property_name + "Array"
1207
+ prop_type = @property_type.swift_type_name
1208
+ json_type = @property_type.serialized_json_type.to_s
1209
+ m << ""
1210
+ m << "if let #{unwrapped_var} = jsonObject[ \"#{@property_name}\" ] as? NSArray {"
1211
+ m._i "var newArray = [#{prop_type}]()"
1212
+ m << "for element in #{unwrapped_var} {"
1213
+ m._i "if let typedElement = element as? #{json_type} {"
1214
+ if @property_type.auto_bridged
1215
+ m.ii "newArray.append( typedElement )"
1216
+ else
1217
+ m._i "if let unmarshalledValue = typedElement as #{prop_type} {"
1218
+ m.ii "newArray.append( unmarshalledValue )"
1219
+ m << "} else {"
1220
+ m.ii "println( \"Error in converting \\(typedElement) to #{prop_type}\")"
1221
+ m._o "}"
1222
+ end
1223
+ m << "} else {"
1224
+ m.ii "println( \"Unexpected json value: \\(element) for #{prop_type}\")"
1225
+ m._o "}"
1226
+ m << "}"
1227
+ m._o "#{@property_name} = newArray"
1228
+ m << "} else {"
1229
+ m.ii "#{@property_name} = nil"
1230
+ m << "}"
1231
+ else
1232
+ m << "ERROR Non-optional properties not yet supported"
1233
+ end
1234
+ when :class
1235
+ if( @mutability_type.mutability_id == :optional )
1236
+ unwrapped_var = @property_name + "Array"
1237
+ prop_type = @property_type.swift_type_name
1238
+ m << ""
1239
+ m << "if let #{unwrapped_var} = jsonObject[ \"#{@property_name}\" ] as? NSArray {"
1240
+ m.ii "#{@property_name} = #{prop_type}.arrayFromJSON( #{unwrapped_var} ) as? [#{prop_type}]"
1241
+ m << "} else {"
1242
+ m.ii "#{@property_name} = nil"
1243
+ m << "}"
1244
+ else
1245
+ m << "ERROR Non-optional properties not yet supported"
1246
+ end
1247
+
1248
+ when :struct
1249
+ m << 'ERROR Arrays of structs not yet supported'
1250
+
1251
+ when :enum
1252
+ if( @mutability_type.mutability_id == :optional )
1253
+ unwrapped_var = @property_name + "Array"
1254
+ prop_type = @property_type.swift_type_name
1255
+ m << ""
1256
+ m << "if let #{unwrapped_var} = jsonObject[ \"#{@property_name}\" ] as? NSArray {"
1257
+ m._i "var newArray = [#{prop_type}]()"
1258
+ m << "for element in #{unwrapped_var} {"
1259
+ m._i "if let typedElement = element as? NSString {"
1260
+ m._i "if let enumValue = #{@property_type.enum.unmarshal_expression( "typedElement" )} {"
1261
+ m.ii "newArray.append( enumValue )"
1262
+ m << "} else {"
1263
+ m.ii "println( \"Error in converting \\(typedElement) to #{prop_type}\")"
1264
+ m._o "}"
1265
+ m << "} else {"
1266
+ m.ii "println( \"Unexpected json value: \\(element) for #{prop_type}\")"
1267
+ m._o "}"
1268
+ m << "}"
1269
+ m._o "#{@property_name} = newArray"
1270
+ m << "} else {"
1271
+ m.ii "#{@property_name} = nil"
1272
+ m << "}"
1273
+ else
1274
+ m << "ERROR Non-optional properties not yet supported"
1275
+ end
1276
+ else
1277
+ puts "ERROR: Unknown swift_kind: #{@property_type.swift_kind.to_s}"
1278
+ end
1279
+ end
1280
+
1281
+
1282
+ def all_collection_types
1283
+ [
1284
+ :array,
1285
+ :dictionaryByString
1286
+ ]
1287
+ end
1288
+ end
1289
+
1290
+ class SwiftMethodBase
1291
+ attr_accessor :name
1292
+ attr_accessor :argStr
1293
+ attr_accessor :override
1294
+ attr_accessor :returns
1295
+ attr_accessor :comment
1296
+
1297
+ attr_accessor :access_control_modifier
1298
+ attr_accessor :func_qualifiers
1299
+
1300
+ attr_accessor :indent
1301
+ attr_accessor :bodyLines
1302
+
1303
+
1304
+ def initialize (swift_element, name, argStr, returns, override: false, comment: nil)
1305
+ @name = name
1306
+ @argStr = argStr
1307
+ @returns = returns
1308
+ @override = override
1309
+ @comment = comment
1310
+
1311
+ @indent = 0
1312
+ @bodyLines = []
1313
+
1314
+ @access_control_modifier = ''
1315
+ end
1316
+
1317
+
1318
+ def << (*line_or_lines)
1319
+ line_or_lines = line_or_lines.flatten()
1320
+ line_or_lines.each do |line|
1321
+ new_line = ("\t" * @indent) + line
1322
+ @bodyLines << new_line
1323
+ end
1324
+ @bodyLines = @bodyLines.flatten()
1325
+ end
1326
+
1327
+ def _i (*line_or_lines)
1328
+ @indent += 1
1329
+ self << line_or_lines
1330
+ end
1331
+
1332
+ def _o (*line_or_lines)
1333
+ self << line_or_lines
1334
+ @indent -= 1
1335
+ end
1336
+
1337
+ def ii (*line_or_lines)
1338
+ @indent += 1
1339
+ self << line_or_lines
1340
+ @indent -= 1
1341
+ end
1342
+
1343
+ def func_fragment()
1344
+ return 'func' if func_qualifiers.nil?
1345
+ return [*func_qualifiers].join( ' ' ) + ' func'
1346
+ end
1347
+ end
1348
+
1349
+ class SwiftInitializer < SwiftMethodBase
1350
+ def initialize (swift_element, name, argStr, override: false, comment: nil)
1351
+ super(swift_element, name, argStr, nil, override:override, comment:comment)
1352
+ swift_element.initializers << self
1353
+ end
1354
+
1355
+ def func_fragment()
1356
+ return [*func_qualifiers].join( ' ' )
1357
+ end
1358
+ end
1359
+
1360
+
1361
+ class SwiftMethod < SwiftMethodBase
1362
+
1363
+ def initialize (swift_element, name, argStr, returns, override: false, comment: nil)
1364
+ super(swift_element, name, argStr, returns, override:override, comment:comment)
1365
+ swift_element.methods << self
1366
+ end
1367
+
1368
+ def func_fragment()
1369
+ return 'func' if func_qualifiers.nil?
1370
+ return [*func_qualifiers].join( ' ' ) + ' func'
1371
+ end
1372
+ end
1373
+
1374
+
1375
+ # Variable & Property Mutability
1376
+ class MutabilityType
1377
+ attr_accessor :mutability_id
1378
+ attr_accessor :mutability
1379
+ attr_accessor :declaration_wrapping
1380
+ attr_accessor :must_be_unwrapped
1381
+
1382
+ def initialize(mutability_id, mutability, declaration_wrapping, must_be_unwrapped: false)
1383
+ @mutability_id = mutability_id
1384
+ @mutability = mutability
1385
+ @declaration_wrapping = declaration_wrapping
1386
+ @must_be_unwrapped = must_be_unwrapped
1387
+ end
1388
+ end
1389
+
1390
+
1391
+ # The single coordinator object for a set of swift file generations
1392
+ class SwiftDefinitionSet
1393
+ attr_accessor :generated_root
1394
+ attr_accessor :generated_test_root
1395
+ attr_accessor :generated_user_root
1396
+
1397
+ attr_accessor :make_unknown_types
1398
+
1399
+ attr_accessor :all_class_characteristics
1400
+ attr_accessor :output_files
1401
+
1402
+ attr_accessor :elements_by_name # All non-primitives
1403
+ # attr_accessor :classes_by_name # Only classes
1404
+ # attr_accessor :enums_by_name # Only enums
1405
+
1406
+ attr_accessor :types_by_symbol
1407
+ attr_accessor :make_more_supporting_elements
1408
+
1409
+ attr_accessor :test_support_class
1410
+
1411
+ attr_accessor :company_name
1412
+
1413
+ def self.mutability_types
1414
+ {
1415
+ let: MutabilityType.new(:let, 'let', ''),
1416
+ var: MutabilityType.new(:var, 'var', ''),
1417
+ optional: MutabilityType.new(:optional, 'var', '?', must_be_unwrapped: true),
1418
+ unwrapped: MutabilityType.new(:unwrapped, 'var', '!')
1419
+ }
1420
+ end
1421
+
1422
+ def self.collection_types
1423
+ {
1424
+ list: "list",
1425
+ idList: "idList"
1426
+ }
1427
+ end
1428
+
1429
+ def initialize( generated_root:nil, generated_user_root:nil, generated_test_root:nil)
1430
+ @generated_root = File.expand_path(generated_root)
1431
+ @generated_test_root = File.expand_path(generated_test_root) unless generated_test_root.nil?
1432
+ @generated_user_root = File.expand_path(generated_user_root) unless generated_test_root.nil?
1433
+
1434
+ @make_unknown_types = false
1435
+
1436
+ @output_files = {}
1437
+ @elements_by_name = {}
1438
+ @make_more_supporting_elements = true
1439
+
1440
+ @all_class_characteristics = [
1441
+ :json_serializable,
1442
+ :comparable,
1443
+ :make_test_class,
1444
+ :create_user_class
1445
+ ]
1446
+
1447
+ @types_by_symbol = {}
1448
+
1449
+ initial_property_types.each do |propType|
1450
+ @types_by_symbol[propType.swift_type_symbol] = propType
1451
+ end
1452
+
1453
+ @company_name = "<defined in SwiftDefinitionSet.company_name>"
1454
+ end
1455
+
1456
+ def run_generation_sequence
1457
+ prepare_output_paths
1458
+ prepare_supporting_elements
1459
+ resolve_property_types
1460
+ resolve_inheritance
1461
+
1462
+ prepare_for_generation
1463
+ resolve_property_types
1464
+ end
1465
+
1466
+ def prepare_output_paths
1467
+ # Create / empty the generated output directories
1468
+ paths = []
1469
+ paths << @generated_root unless @generated_root.nil?
1470
+ paths << @generated_test_root unless @generated_test_root.nil?
1471
+ paths.each do |path|
1472
+ FileUtils.mkdir_p(path)
1473
+ Dir.foreach(path) do |entry|
1474
+ file_path = File.join(path, entry)
1475
+ if (File::ftype(file_path) == "file")
1476
+ puts "deleting " + file_path
1477
+ File.delete(file_path)
1478
+ end
1479
+ end
1480
+ end
1481
+ end
1482
+
1483
+ # Called first during code generation. Note that this method may lead to the creation of new files
1484
+ def prepare_supporting_elements()
1485
+ unless @generated_test_root.nil?
1486
+ @test_support_class = SwiftClass.new(self, 'AutoGeneratedTestSupport', [], characteristics:[], is_test_element: true ) unless
1487
+
1488
+ while @make_more_supporting_elements do
1489
+ @make_more_supporting_elements = false
1490
+ original_output_files = @output_files.dup
1491
+ original_output_files.each do |_, swiftFile|
1492
+ swiftFile.prepare_supporting_elements
1493
+ end
1494
+ end
1495
+ end
1496
+
1497
+ end
1498
+
1499
+ # Called during code generation.
1500
+ def resolve_inheritance()
1501
+ @elements_by_name.each do |_, element|
1502
+ element.resolve_inheritance if element.respond_to? :resolve_inheritance
1503
+ end
1504
+ end
1505
+
1506
+ def resolve_property_types()
1507
+ @elements_by_name.each do |_, element|
1508
+ element.resolve_property_types if element.respond_to? :resolve_property_types
1509
+ end
1510
+ end
1511
+
1512
+ def prepare_for_generation
1513
+ @output_files.each do |_, swiftFile|
1514
+ swiftFile.prepare_for_generation
1515
+ end
1516
+ end
1517
+
1518
+ def add_element(swift_element)
1519
+ file_name = swift_element.file_name || swift_element.type_name
1520
+ the_file = file_for_name( file_name, swift_element.is_test_element, swift_element.is_user_editable )
1521
+ the_file.add_element(swift_element)
1522
+ swift_element.source_file = the_file
1523
+
1524
+ # if swift_element.is_a? SwiftEnum
1525
+ # @enums_by_name[swift_element.type_name] = swift_element
1526
+ # elsif swift_element.is_a? SwiftClass
1527
+ # @elements_by_name[swift_element.type_name] = swift_element
1528
+ # end
1529
+
1530
+ @elements_by_name[swift_element.type_name] = swift_element
1531
+ @make_more_supporting_elements = true
1532
+
1533
+ # # Make a type for this class so that references to this class can be resolved
1534
+ # type_symbol = swift_element.type_name.to_sym
1535
+ # element_property_type = SwiftPropertyType.new( type_symbol, :NSDictionary, swift_kind: :class, test_value:nil )
1536
+ # @types_by_symbol[type_symbol] = element_property_type
1537
+
1538
+ (type_symbol, property_type) = swift_element.make_property_type
1539
+ @types_by_symbol[type_symbol] = property_type
1540
+ end
1541
+
1542
+ # def add_json_serializable_class(serializable_class)
1543
+ # @json_serializable_classes << serializable_class
1544
+ # end
1545
+
1546
+ def file_for_name(name, is_test_element, is_user_editable)
1547
+ if( is_test_element )
1548
+ abort( "User-modifiable test classes are not generated" ) if is_user_editable
1549
+ file_dir = @generated_test_root
1550
+ else
1551
+ if( is_user_editable )
1552
+ file_dir = @generated_user_root
1553
+ else
1554
+ file_dir = @generated_root
1555
+ end
1556
+ end
1557
+
1558
+ @output_files[name] ||= SwiftFile.new(name, file_dir, is_user_file:is_user_editable, company_name:@company_name)
1559
+ return @output_files[name]
1560
+ end
1561
+
1562
+ def property_type_for_symbol(symbol)
1563
+ resolved = @types_by_symbol[symbol]
1564
+ if resolved.nil?
1565
+ if @make_unknown_types
1566
+ resolved = make_unknown_type( symbol )
1567
+ else
1568
+ puts( "ERROR: Unknown symbol: #{symbol.to_s}")
1569
+ end
1570
+ end
1571
+
1572
+ resolved
1573
+ end
1574
+
1575
+ def make_unknown_type( symbol )
1576
+ property_type = SwiftPropertyType.new( symbol, :NSDictionary ) #TODO: NSDictionary? for unknown type?
1577
+ @types_by_symbol[ symbol ] = property_type
1578
+ property_type
1579
+ end
1580
+
1581
+ # General purpose
1582
+ def characteristics_are_legal( characteristics )
1583
+ unknown_characteristics = (characteristics - @all_class_characteristics)
1584
+ unknown_characteristics.empty?
1585
+ end
1586
+ end
1587
+
1588
+ end
1589
+