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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/compiler.xml +22 -0
- data/.idea/copyright/profiles_settings.xml +3 -0
- data/.idea/dictionaries/ckornher.xml +7 -0
- data/.idea/encodings.xml +4 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/runConfigurations/Install_the_Gem.xml +26 -0
- data/.idea/runConfigurations/Ribosome.xml +26 -0
- data/.idea/runConfigurations/genswift_help.xml +26 -0
- data/.idea/runConfigurations/test1.xml +30 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/swift_generator.iml +126 -0
- data/.idea/uiDesigner.xml +124 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +1356 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +1 -0
- data/Tools/compile_templates.bash +3 -0
- data/Tools/help.rna +340 -0
- data/Tools/ribosome.rb +589 -0
- data/bin/console +14 -0
- data/bin/genswift +34 -0
- data/bin/setup +7 -0
- data/lib/swift_generator.rb +23 -0
- data/lib/swift_generator/code_generation/POSOTest.rb +8 -0
- data/lib/swift_generator/code_generation/SwiftFileTemplate.dna +157 -0
- data/lib/swift_generator/code_generation/ToRemove/generate.bash +41 -0
- data/lib/swift_generator/code_generation/code_generation_common.rb +6 -0
- data/lib/swift_generator/code_generation/swift_class_generation.rb +1589 -0
- data/lib/swift_generator/code_generation/swift_file_template.rb +499 -0
- data/lib/swift_generator/code_generation/swift_types.rb +106 -0
- data/lib/swift_generator/code_generation/usi_metaclasses.rb +23 -0
- data/lib/swift_generator/specfile_parser.rb +116 -0
- data/lib/swift_generator/version.rb +3 -0
- data/swift_generator.gemspec +31 -0
- 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,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,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,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
|
+
|