Class: SwiftGenerator::SwiftClass

Inherits:
SwiftNonPrimitive show all
Defined in:
lib/swift_generator/code_generation/swift_class_generation.rb

Overview

A Swift Class to be generated.

Direct Known Subclasses

SwiftUnitTestClass

Instance Attribute Summary collapse

Attributes inherited from SwiftNonPrimitive

#class_characteristics, #definition_set, #file_name, #inheritance_list, #initializers, #is_test_element, #is_user_editable, #methods, #properties, #source_file, #specified_type_name, #type_name

Instance Method Summary collapse

Methods inherited from SwiftNonPrimitive

#comparable_properties, #persistent_properties, #resolve_property_types, #swift_type_symbol, #transient_properties

Constructor Details

#initialize(definition_set, specified_type_name, inheritance_list = [], file_name: nil, characteristics: $default_swift_class_characteristics, is_test_element: false, is_user_editable: false) ⇒ SwiftClass

Returns a new instance of SwiftClass.

Parameters:

  • definition_set (SwiftDefinitionSet)
  • type_name (String)
  • inherited_from (Array)
  • file_name (Object) (defaults to: nil)
  • characteristics (Object) (defaults to: $default_swift_class_characteristics)
  • is_test_class (Object)


368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 368

def initialize (definition_set, specified_type_name, inheritance_list=[], file_name: nil,
				characteristics:$default_swift_class_characteristics, is_test_element: false, is_user_editable: false)
	# Generate the type name from the specified type name. Non-editable classes are prepended with "_"
	prefix = characteristics.include?(:create_user_class) ? '_' : ''
	type_name = prefix + specified_type_name

	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 )

	@parent_class = nil

	#change for known & legal combinations of characteristics
	abort( "illegal class characteristics" ) if ! definition_set.characteristics_are_legal(@class_characteristics )

	# prefix = @class_characteristics.include?(:create_user_class) ? '_' : ''
	# @type_name = prefix + @specified_type_name

	@do_generate = true

	@supporting_elements_created = false
end

Instance Attribute Details

#access_control_modifierObject

Convenience



356
357
358
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 356

def access_control_modifier
  @access_control_modifier
end

#auto_test_classObject

Associated classes



353
354
355
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 353

def auto_test_class
  @auto_test_class
end

#do_generateObject

Returns the value of attribute do_generate.



357
358
359
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 357

def do_generate
  @do_generate
end

#parent_classObject

The parent class.



347
348
349
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 347

def parent_class
  @parent_class
end

#post_super_initializationsObject

Initializations



350
351
352
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 350

def post_super_initializations
  @post_super_initializations
end

#supporting_elements_createdObject

Returns the value of attribute supporting_elements_created.



358
359
360
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 358

def supporting_elements_created
  @supporting_elements_created
end

#test_object_methodObject

Returns the value of attribute test_object_method.



359
360
361
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 359

def test_object_method
  @test_object_method
end

Instance Method Details

#add_simple_class_property(name, type, value: nil, mutability: :var, override: false) ⇒ Object



393
394
395
396
397
398
399
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 393

def add_simple_class_property( name, type, value:nil, mutability: :var, override:false)
	# class variables not supported. Use class property instead.
	p = SwiftProperty.new(self, name, type, mutability )
	p.property_qualifiers = 'class'
	p.property_qualifiers = "override #{p.property_qualifiers}" if override
	p.getter_body = "return #{value}"
end

#create_copy_methodsObject

Copy methods



622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 622

def create_copy_methods()
	copy_to_method_name = "copyTo#{@type_name}"

	# The copy method - calls copyTo so that we can avoid duplicating copy code
	copy_m = SwiftMethod.new(self, 'copy', '', 'AnyObject', override: true)
	copy_m << "let theCopy = #{@type_name}()"
	copy_m << "#{copy_to_method_name}( theCopy )"
	copy_m << "return theCopy"

	copy_to_m = SwiftMethod.new(self, copy_to_method_name, "other: #{@type_name}", nil, override:false)

	if !@parent_class.nil?
		copy_to_m << "super.copyTo#{@parent_class.type_name}( other )"
	end
	self.persistent_properties.each do |prop|
		copy_to_m << "\tother.#{prop.property_name} = #{prop.property_name}"
	end
end

#create_description_methodsObject

Description methods



642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 642

def create_description_methods()

	# description
	dm = SwiftMethod.new(self, 'description', 'indent:String="        ", diagnostic:Bool=false', "NSString", override:!@parent_class.nil? )
	dm << "let d = NSMutableString()"

	if @parent_class.nil?
	else
		dm << "d.appendString( super.description(indent:indent, diagnostic:diagnostic) )"
	end

	dm << "if( diagnostic ) {"
	dm.ii	"d.appendString( \"    properties of class #{@type_name} :\\n\")"
	dm.ii	"d.appendString( \"\\(indent)- none -\\n\")" if @properties.empty?
	dm << "}"

	self.persistent_properties.each do |prop|
		if( prop.is_optional )
			prop_value = prop.property_name
			if prop.property_type.swift_kind == :enum
				dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(prettyFormatEnum( #{prop_value} ))\\n\" )"
			else
				dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(prettyFormat( #{prop_value} ))\\n\" )"
			end

		else
			dm << "d.appendString( \"\\(indent)#{prop.property_name} = \\(#{prop_value})\\n\" )"
		end
	end

	dm << "return d"

	# className
	type_name_m = SwiftMethod.new(self, 'className', nil, 'String', override:!@parent_class.nil? )
	type_name_m .func_qualifiers = 'class'
	type_name_m << "return \"#{@type_name}\""
end

#create_equality_methodsObject



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 497

def create_equality_methods()
	comparable_super = super_has_characteristics(:comparable)

	#func isEqual(_ anObject: AnyObject!) -> Bool
	is_equals_method = SwiftMethod.new(self, 'isEqual', 'other:AnyObject!', 'Bool', override: true)
	is_equals_method << "if( other == nil ) { return false }"
	other_typed_var = "other#{@type_name}"
	if comparable_super
		is_equals_method << "if( !super.isEqual( other )) { return false }"
	end

	is_equals_method << ""
	is_equals_method << "if let #{other_typed_var} = other as? #{@type_name} {"

	comparable_properties.each do |property|
		other_name = "#{other_typed_var}.#{property.property_name}"

		if property.property_type.nil?
			puts( property.property_name )
		end

		if property.collection_type == :array
			is_equals_method.ii "if( !optionalArraysEqual( #{property.property_name}, #{other_name} )) { return false }"
		else
			custom_test = property.property_type.custom_equality_test
			if custom_test.nil?
				is_equals_method.ii "if( self.#{property.property_name} != #{other_name} ) { return false }"
			else
				is_equals_method.ii "if( !#{custom_test.call(property.property_name, other_name)} ) { return false }"
			end
		end
	end
	is_equals_method << "" << "\treturn true"
	is_equals_method << "} else {"
	is_equals_method << "\treturn false"
	is_equals_method << "}"

	#- (NSUInteger)hash
	hash_method = SwiftMethod.new(self, 'hashValue', '', 'Int', override: comparable_super)
	hash_method << "var hasher = Hasher() // Hasher must be mutable" << ""

	if comparable_super
		hash_method << 'hasher.hashIn( super.hashValue() )'
	end

	comparable_properties.each do |property|
		hash_element = lambda{ |var_name, is_optional| "hasher.hashIn( #{property.property_type.hashable_value(var_name, is_optional )} )"}

		if( property.collection_type.nil?)
			hash_method <<  hash_element.call(property.property_name, property.mutability_type.must_be_unwrapped)
		elsif( property.collection_type == :array )
			arrayName = "#{property.property_name}Array"
			hash_method << "if let #{arrayName} = #{property.property_name} {"
			hash_method._i "for element in #{arrayName} {"
			hash_method.ii  hash_element.call("element", false)
			hash_method._o "}"
			hash_method << "}"
		else
			hash_method << "ERROR: hashing of #{property.collection_type.to_s} collections not supported."
		end
	end
	hash_method << "" << "return hasher.hash"
end

#create_init_methodsObject



484
485
486
487
488
489
490
491
492
493
494
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 484

def create_init_methods()
# Only no-argument init methods & optional properties are currently supported

return if @post_super_initializations.empty?
init_m = SwiftInitializer.new(self, 'init', nil, override: true)
init_m << "super.init()"
keys = @post_super_initializations.keys.sort!
keys.each do |prop_name|
		init_m << "#{prop_name} = #{@post_super_initializations[prop_name]}"
	end
end

#create_test_classesObject

Test class generation



687
688
689
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 687

def create_test_classes()
	tc = @auto_test_class = SwiftUnitTestClass.new(@definition_set, self, 'AutoGenerated')
end

#create_user_classesObject

User class generation



681
682
683
684
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 681

def create_user_classes
user_class_characteristics = @class_characteristics - [:make_test_class, :create_user_class]
@user_editable_class = SwiftClass.new(@definition_set, @specified_type_name, [@type_name], characteristics:user_class_characteristics, is_user_editable:true )
end

#ensure_test_object_methodObject

Test Support ( Here for embedded objects )



717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 717

def ensure_test_object_method
	#NOTE: this method is in the global test support class
	return if !@test_object_method.nil?

	comment = "/// Create a test #{@type_name} object with varying values"
	# e.g. func makeTestUser() -> User
	obj_name = "test#{@type_name}"
	m = SwiftMethod.new(@definition_set.test_support_class, "makeTest#{@type_name}", '_ index:Int = 0', "#{@type_name}", comment: comment)
	m.func_qualifiers = 'class'

	m << "let #{obj_name} = #{@type_name}()" << ""
	prop_index = 1

	set_test_values( m, obj_name, 1 )

	m << "" << "return #{obj_name}"
	@test_object_method = m
end

#insert_marshal_expression(m, unwrapped_var, destination) ⇒ Object

JSON marshaling support



428
429
430
431
432
433
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 428

def insert_marshal_expression( m, unwrapped_var, destination )
	#Probably only works for String enums
	m << "let objectDictionary = NSMutableDictionary()"
	m << "#{unwrapped_var}.marshalToJSON( objectDictionary )"
	m << "#{destination} = objectDictionary"
end

#insert_unmarshal_expression(m, unwrapped_value, destination) ⇒ Object



435
436
437
438
439
440
441
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 435

def insert_unmarshal_expression( m, unwrapped_value, destination )
	#Probably only works for String enums
	m << "let temp = #{self.type_name}()"
	m << "temp.unmarshalFromJSON( #{unwrapped_value} )"
	# TODO: validate?
	m << "#{destination} = temp"
end

#make_property_typeObject



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 401

def make_property_type()
	# Make a type for this class so that references to this class can be resolved
	type_symbol = @type_name.to_sym

	test_value_lambda = lambda{|num|
		ensure_test_object_method
		test_object_method_call(num)
	}
	property_type = SwiftObjectPropertyType.new( self, :NSDictionary, test_value:test_value_lambda )
	property_type.hashable_value_lambda = lambda{|var_name, is_optional|
		if is_optional
			return "#{var_name}?.hashValue()"
		else
			return "#{var_name}.hashValue()"
		end
	}

	#TODO Fix this Horror
	property_type.custom_unmarshaling = lambda{|var_name, unwrapped_var| [
		"#{var_name} = #{unmarshal_expression(unwrapped_var)}"
	]}

	return self.swift_type_symbol, property_type
end

#post_super_init(values_for_properties) ⇒ Object



389
390
391
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 389

def post_super_init( values_for_properties )
	@post_super_initializations.merge!( values_for_properties )
end

#prepare_for_generationObject



475
476
477
478
479
480
481
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 475

def prepare_for_generation()
	create_init_methods
	create_equality_methods if @class_characteristics.include?(:comparable)
	prepare_marshaling_code if @class_characteristics.include?(:json_serializable)
	create_description_methods  if @class_characteristics.include?(:auto_describing)
	create_copy_methods if @class_characteristics.include?(:json_serializable)
end

#prepare_marshaling_codeObject



561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 561

def prepare_marshaling_code()
super_does_json = super_has_characteristics(:json_serializable)

# Reader & Writer
reader = SwiftMethod.new(self, 'unmarshalFromJSON', 'jsonObject:NSDictionary', nil, override: super_does_json )
writer = SwiftMethod.new(self, 'marshalToJSON', 'jsonObject:NSMutableDictionary', nil, override: super_does_json )

if super_does_json
	reader << "super.unmarshalFromJSON( jsonObject )"
	writer << "super.marshalToJSON( jsonObject )"
end

writer << "" << "jsonObject[\"objectType\"] = \"#{@type_name}\""

persistent_properties.each do |prop|
	prop.unmarshal_code(reader)
	prop.marshal_code(writer)
end

# Object Reader
object_reader = SwiftMethod.new(self, "objectFromJSON", 'newObjData:NSDictionary', @specified_type_name, override: super_does_json )
object_reader.func_qualifiers = 'class'

object_reader << "let newObj = #{@specified_type_name}()"
object_reader << "newObj.unmarshalFromJSON( newObjData )"
object_reader << 'return newObj'

# Array Reader & Writer

# Array Reader
array_reader = SwiftMethod.new(self, "arrayFromJSON", 'json:NSArray', '[USIBaseModel]', override: super_does_json )
array_reader.func_qualifiers = 'class'

array_reader << "var newObjects = [#{@specified_type_name}]()"
array_reader << "for objEntry in json {"
array_reader << "\tlet newObj = #{@specified_type_name}()"

array_reader << "\tif let newObjData = objEntry as? NSDictionary {"
array_reader << "\t\tnewObj.unmarshalFromJSON( newObjData )"
array_reader << "\t\tnewObjects.append( newObj )"

array_reader << "\t}"
array_reader << '}'
array_reader << 'return newObjects'

# Array Writer
array_writer = SwiftMethod.new(self, "arrayToJSON", "array:[USIBaseModel]", 'NSMutableArray', override: super_does_json )
array_writer.func_qualifiers = 'class'

array_writer << 'var dataArray = NSMutableArray()'

array_writer << "for obj in array {"
array_writer << "\tlet objData = NSMutableDictionary()"
array_writer << "\tobj.marshalToJSON( objData )"
array_writer << "\tdataArray.addObject( objData )"
array_writer << "}"
array_writer << "return dataArray"

end

#prepare_supporting_elementsObject

Called before all other generation-time methods. Construct other related or required elements May be called more than once



459
460
461
462
463
464
465
466
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 459

def prepare_supporting_elements()
	return if @supporting_elements_created

	create_user_classes if @class_characteristics.include?(:create_user_class)
	create_test_classes if ! @is_test_element && @class_characteristics.include?(:make_test_class)

	@supporting_elements_created = true
end

#resolve_inheritanceObject



469
470
471
472
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 469

def resolve_inheritance()
	return if @inheritance_list.empty?
	@parent_class = @definition_set.elements_by_name[ @inheritance_list[0] ]
end

#set_test_values(set_method, variable_name, indexing_number) ⇒ Object



703
704
705
706
707
708
709
710
711
712
713
714
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 703

def set_test_values( set_method, variable_name, indexing_number )
	if ! @parent_class.nil?
		indexing_number = @parent_class.set_test_values( set_method, variable_name, indexing_number )
	end

	comparable_properties.each do |prop|
		set_method << "#{variable_name}.#{prop.property_name} = #{prop.make_test_value(indexing_number)}"
		indexing_number += 1
	end

	return indexing_number
end

#super_has_characteristics(*characteristics) ⇒ Object

Utility



692
693
694
695
696
697
698
699
700
701
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 692

def super_has_characteristics( *characteristics )
	remaining_characteristics = characteristics
	ancestor = @parent_class
	until  ancestor.nil? || remaining_characteristics.empty?
		remaining_characteristics = (remaining_characteristics - ancestor.class_characteristics)
		ancestor = ancestor.parent_class
	end

	return remaining_characteristics.empty?
end

#test_object_method_call(index = 0) ⇒ Object



736
737
738
739
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 736

def test_object_method_call(index=0)
	argStr = index == 0 ? '' : "#{index}"
	"#{@definition_set.test_support_class.type_name}.makeTest#{@type_name}(#{argStr})"
end