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

SwiftCategory, SwiftProtocol, SwiftUnitTestClass

Instance Attribute Summary collapse

Attributes inherited from SwiftNonPrimitive

#access_control_modifiers, #class_characteristics, #definition_set, #file_name, #inheritance_list, #initializers, #is_test_element, #is_user_editable, #methods, #properties, #source_file, #specified_type_name, #top_inner_comment_block, #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)


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 372

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



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

def access_control_modifier
  @access_control_modifier
end

#auto_test_classObject

Associated classes



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

def auto_test_class
  @auto_test_class
end

#do_generateObject

Returns the value of attribute do_generate.



361
362
363
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 361

def do_generate
  @do_generate
end

#parent_classObject

The parent class.



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

def parent_class
  @parent_class
end

#post_super_initializationsObject

Initializations



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

def post_super_initializations
  @post_super_initializations
end

#supporting_elements_createdObject

Returns the value of attribute supporting_elements_created.



362
363
364
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 362

def supporting_elements_created
  @supporting_elements_created
end

#test_object_methodObject

Returns the value of attribute test_object_method.



363
364
365
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 363

def test_object_method
  @test_object_method
end

Instance Method Details

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



397
398
399
400
401
402
403
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 397

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



626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 626

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



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
679
680
681
682
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 646

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



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
560
561
562
563
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 501

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



488
489
490
491
492
493
494
495
496
497
498
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 488

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



691
692
693
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 691

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

#create_user_classesObject

User class generation



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

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 )



721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 721

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



432
433
434
435
436
437
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 432

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



439
440
441
442
443
444
445
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 439

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



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

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



393
394
395
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 393

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

#prepare_for_generationObject



479
480
481
482
483
484
485
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 479

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



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
620
621
622
623
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 565

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



463
464
465
466
467
468
469
470
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 463

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



473
474
475
476
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 473

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



707
708
709
710
711
712
713
714
715
716
717
718
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 707

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



696
697
698
699
700
701
702
703
704
705
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 696

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



740
741
742
743
# File 'lib/swift_generator/code_generation/swift_class_generation.rb', line 740

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