<commit_before>require File.expand_path('../../utils/log', File.dirname(__FILE__))
require File.expand_path('../../utils/string_xcdatamodel', File.dirname(__FILE__))
require File.expand_path('../../utils/file_dbgenerator', File.dirname(__FILE__))
require File.expand_path('../../xcdatamodel/parser/relationship', File.dirname(__FILE__))
require File.expand_path('converter', File.dirname(__FILE__))
require File.expand_path('enum_generator', File.dirname(__FILE__))
require File.expand_path('templates', File.dirname(__FILE__))
require File.expand_path('object_mapper_generator', File.dirname(__FILE__))

module DBGenerator
  module Realm
    module Swift

      class Generator

        # INCLUDES #############################################################

        include DBGenerator::XCDataModel::Parser
        include Converter
        include EnumGenerator
        include Templates
        include ObjectMapperGenerator

        # PUBLIC METHODS #######################################################

        def initialize(path, xcdatamodel, json = false)
          puts "\n"
          Log::title('Swift Realm')
          xcdatamodel.entities.each do |_, entity|
            unless entity.abstract?
              Log::success("Generating entity #{entity.name}...")
              generate_class(path, entity, json)
              generate_object_mapper_categories(path, xcdatamodel) if json
            end
          end
        end

        private ################################################################

        def generate_class(path, entity, json)
          class_file = String.new
          entity.name = entity.name.delete_objc_prefix
          class_file << generate_header(entity)
          class_file << generate_attributes(entity.attributes, entity.relationships, json)
          class_file << generate_inverse_properties(entity)
          class_file << generate_primary_key(entity)
          class_file << generate_indexed_properties(entity)
          class_file << generate_ignored_properties(entity)
          class_file << '}' + "\n"
          File.write_file_with_name(path, SWIFT_FILE_TEMPLATE%[entity.name], class_file)
          generate_enums(path, entity.attributes)
        end

        def generate_header(entity)
          class_file = String.new
          class_file << GENERATED_MESSAGE + "\n\n"
          class_file << IMPORT_REALM + "\n\n"
          class_file << CLASS_COMMENT_TEMPLATE%[entity.comment] + "\n" unless entity.comment.empty?
          class_file << CLASS_TEMPLATE%[entity.name] + "\n\n"
          class_file << generate_constants(entity)
        end

        def generate_constants(entity)
          attribute_constants = String.new
          unless entity.attributes.empty?
            attribute_constants << '    ' + ENUM_STRING_DEF_TEMPLATE%['Attributes'] + "\n"
            entity.attributes.each do |_, attribute|
              unless attribute.realm_ignored? or attribute.read_only?
                attribute_constants << '        ' + ATTRIBUTE_COMMENT_TEMPLATE%[attribute.comment] + "\n" unless attribute.comment.empty?
                attribute_constants << '        ' + ENUM_STRING_CASE_TEMPLATE%[attribute.name.capitalize_first_letter, attribute.name] + "\n"
              end
            end
            attribute_constants << '    ' + '}'+ "\n\n"
          end
          relationship_constants = String.new
          if not entity.relationships.empty? and not entity.has_only_inverse?
            relationship_constants << '    ' + ENUM_STRING_DEF_TEMPLATE%['Relationships'] + "\n"
            entity.relationships.each do |_, relationship|
              relationship_constants << '        ' + ENUM_STRING_CASE_TEMPLATE%[relationship.name.capitalize_first_letter, relationship.name] + "\n" if not relationship.inverse?
            end
            relationship_constants << '    ' + '}'+ "\n\n"
          end
          attribute_constants + relationship_constants
        end

        def generate_attributes(attributes, relationships, json)
          # "NORMAL" ATTRIBUTES
          attributes_string = write_attributes(attributes, json)
          # "RELATIONSHIP" ATTRIBUTES
          relationships.each do |_, relationship|
            unless relationship.inverse?
              is_list = relationship.type == :to_many
              name = relationship.name
              type = relationship.inverse_type.delete_objc_prefix
              if is_list
                if json
                  attributes_string << '    ' + PROPERTY_LIST_VAR_TEMPLATE%[name, type] + "\n"
                else
                  attributes_string << '    ' + PROPERTY_LIST_TEMPLATE%[name, type] + "\n"
                end
              else
                attributes_string << '    ' + PROPERTY_OBJECT_TEMPLATE%[name, type] + "\n"
              end
            end
          end
          attributes_string + "\n"
        end

        def write_attributes(attributes, json)
          attributes_string = String.new
          attributes.each_with_index do |(_, attribute)|
            unless attribute.read_only?
              if attribute.enum?
                attributes_string << write_enum_attribute(attribute, json)
              else
                if attribute.optional?
                  attributes_string << write_optional_attribute(attribute, json) + "\n"
                else
                  default_value = convert_default(attribute.type, attribute.has_default? ? attribute.default : nil)
                  attributes_string << '    ' + PROPERTY_DEFAULT_TEMPLATE%[attribute.name, convert_type(attribute.type), default_value] + "\n"
                end
              end
            end
          end
          attributes_string
        end

        def write_optional_attribute(attribute, json)
          optional_string = String.new
          type = convert_type(attribute.type)
          if attribute.is_number? or attribute.is_bool?
            if json
              optional_string << '    ' + PROPERTY_OPTIONAL_NUMBER_VAR_TEMPLATE%[attribute.name, type]
            else
              optional_string << '    ' + PROPERTY_OPTIONAL_NUMBER_TEMPLATE%[attribute.name, type]
            end
          else
            optional_string << '    ' + PROPERTY_OPTIONAL_NON_NUMBER_TEMPLATE%[attribute.name, type]
          end
          optional_string
        end

        def write_enum_attribute(attribute, json)
          enum_string = String.new
          if json
            enum_string << '    ' + PROPERTY_ENUM_TEMPLATE%[attribute.name] + "\n\n"
          else
            enum_string << '    ' + PROPERTY_PRIVATE_ENUM_TEMPLATE%[attribute.name] + "\n\n"
          end
          enum_type = attribute.enum_type.delete_objc_prefix
          enum_name = attribute.name + 'Enum'

          if attribute.optional?
            enum_string << '    ' + PROPERTY_OPTIONAL_COMPUTED_TEMPLATE%[enum_name, enum_type] + "\n"
          else
            enum_string << '    ' + PROPERTY_COMPUTED_TEMPLATE%[enum_name, enum_type] + "\n" 
          end
          enum_string << '        ' + 'get {' + "\n"

          if attribute.optional?
            enum_string << '            ' + "if let #{attribute.name} = #{attribute.name}, enumValue = #{enum_type}(rawValue: #{attribute.name}) { return enumValue }" + "\n"
          else
            enum_string << '            ' + "if let enumValue = #{enum_type}(rawValue: #{attribute.name}) { return enumValue }" + "\n"
          end

          if attribute.optional?
          enum_string << '            ' + "return nil" + "\n"
        else 
          enum_string << '            ' + "return #{enum_type}.#{attribute.enum_values[attribute.default.to_i].delete_objc_prefix}" + "\n"
        end<commit_msg>Fix regression<commit_after>require File.expand_path('../../utils/log', File.dirname(__FILE__))
require File.expand_path('../../utils/string_xcdatamodel', File.dirname(__FILE__))
require File.expand_path('../../utils/file_dbgenerator', File.dirname(__FILE__))
require File.expand_path('../../xcdatamodel/parser/relationship', File.dirname(__FILE__))
require File.expand_path('converter', File.dirname(__FILE__))
require File.expand_path('enum_generator', File.dirname(__FILE__))
require File.expand_path('templates', File.dirname(__FILE__))
require File.expand_path('object_mapper_generator', File.dirname(__FILE__))

module DBGenerator
  module Realm
    module Swift

      class Generator

        # INCLUDES #############################################################

        include DBGenerator::XCDataModel::Parser
        include Converter
        include EnumGenerator
        include Templates
        include ObjectMapperGenerator

        # PUBLIC METHODS #######################################################

        def initialize(path, xcdatamodel, json = false)
          puts "\n"
          Log::title('Swift Realm')
          xcdatamodel.entities.each do |_, entity|
            unless entity.abstract?
              Log::success("Generating entity #{entity.name}...")
              generate_class(path, entity, json)
              generate_object_mapper_categories(path, xcdatamodel) if json
            end
          end
        end

        private ################################################################

        def generate_class(path, entity, json)
          class_file = String.new
          entity.name = entity.name.delete_objc_prefix
          class_file << generate_header(entity)
          class_file << generate_attributes(entity.attributes, entity.relationships, json)
          class_file << generate_inverse_properties(entity)
          class_file << generate_primary_key(entity)
          class_file << generate_indexed_properties(entity)
          class_file << generate_ignored_properties(entity)
          class_file << '}' + "\n"
          File.write_file_with_name(path, SWIFT_FILE_TEMPLATE%[entity.name], class_file)
          generate_enums(path, entity.attributes)
        end

        def generate_header(entity)
          class_file = String.new
          class_file << GENERATED_MESSAGE + "\n\n"
          class_file << IMPORT_REALM + "\n\n"
          class_file << CLASS_COMMENT_TEMPLATE%[entity.comment] + "\n" unless entity.comment.empty?
          class_file << CLASS_TEMPLATE%[entity.name] + "\n\n"
          class_file << generate_constants(entity)
        end

        def generate_constants(entity)
          attribute_constants = String.new
          unless entity.attributes.empty?
            attribute_constants << '    ' + ENUM_STRING_DEF_TEMPLATE%['Attributes'] + "\n"
            entity.attributes.each do |_, attribute|
              unless attribute.realm_ignored? or attribute.read_only?
                attribute_constants << '        ' + ATTRIBUTE_COMMENT_TEMPLATE%[attribute.comment] + "\n" unless attribute.comment.empty?
                attribute_constants << '        ' + ENUM_STRING_CASE_TEMPLATE%[attribute.name.capitalize_first_letter, attribute.name] + "\n"
              end
            end
            attribute_constants << '    ' + '}'+ "\n\n"
          end
          relationship_constants = String.new
          if not entity.relationships.empty? and not entity.has_only_inverse?
            relationship_constants << '    ' + ENUM_STRING_DEF_TEMPLATE%['Relationships'] + "\n"
            entity.relationships.each do |_, relationship|
              relationship_constants << '        ' + ENUM_STRING_CASE_TEMPLATE%[relationship.name.capitalize_first_letter, relationship.name] + "\n" if not relationship.inverse?
            end
            relationship_constants << '    ' + '}'+ "\n\n"
          end
          attribute_constants + relationship_constants
        end

        def generate_attributes(attributes, relationships, json)
          # "NORMAL" ATTRIBUTES
          attributes_string = write_attributes(attributes, json)
          # "RELATIONSHIP" ATTRIBUTES
          relationships.each do |_, relationship|
            unless relationship.inverse?
              is_list = relationship.type == :to_many
              name = relationship.name
              type = relationship.inverse_type.delete_objc_prefix
              if is_list
                if json
                  attributes_string << '    ' + PROPERTY_LIST_VAR_TEMPLATE%[name, type] + "\n"
                else
                  attributes_string << '    ' + PROPERTY_LIST_TEMPLATE%[name, type] + "\n"
                end
              else
                attributes_string << '    ' + PROPERTY_OBJECT_TEMPLATE%[name, type] + "\n"
              end
            end
          end
          attributes_string + "\n"
        end

        def write_attributes(attributes, json)
          attributes_string = String.new
          attributes.each_with_index do |(_, attribute)|
            unless attribute.read_only?
              if attribute.enum?
                attributes_string << write_enum_attribute(attribute, json)
              else
                if attribute.optional?
                  attributes_string << write_optional_attribute(attribute, json) + "\n"
                else
                  default_value = convert_default(attribute.type, attribute.has_default? ? attribute.default : nil)
                  attributes_string << '    ' + PROPERTY_DEFAULT_TEMPLATE%[attribute.name, convert_type(attribute.type), default_value] + "\n"
                end
              end
            end
          end
          attributes_string
        end

        def write_optional_attribute(attribute, json)
          optional_string = String.new
          type = convert_type(attribute.type)
          if attribute.is_number? or attribute.is_bool?
            if json
              optional_string << '    ' + PROPERTY_OPTIONAL_NUMBER_VAR_TEMPLATE%[attribute.name, type]
            else
              optional_string << '    ' + PROPERTY_OPTIONAL_NUMBER_TEMPLATE%[attribute.name, type]
            end
          else
            optional_string << '    ' + PROPERTY_OPTIONAL_NON_NUMBER_TEMPLATE%[attribute.name, type]
          end
          optional_string
        end

        def write_enum_attribute(attribute, json)
          enum_string = String.new
          if json
            enum_string << '    ' + PROPERTY_ENUM_TEMPLATE%[attribute.name] + "\n\n"
          else
            enum_string << '    ' + PROPERTY_PRIVATE_ENUM_TEMPLATE%[attribute.name] + "\n\n"
          end
          enum_type = attribute.enum_type.delete_objc_prefix
          enum_name = attribute.name + 'Enum'

          if attribute.optional?
            enum_string << '    ' + PROPERTY_OPTIONAL_COMPUTED_TEMPLATE%[enum_name, enum_type] + "\n"
          else
            enum_string << '    ' + PROPERTY_COMPUTED_TEMPLATE%[enum_name, enum_type] + "\n" 
          end
          enum_string << '        ' + 'get {' + "\n"

          enum_string << '            ' + "if let #{attribute.name} = #{attribute.name}, enumValue = #{enum_type}(rawValue: #{attribute.name}) { return enumValue }" + "\n"

          if attribute.optional?
          enum_string << '            ' + "return nil" + "\n"
        else 
          enum_string << '            ' + "return #{enum_type}.#{attribute.enum_values[attribute.default.to_i].delete_objc_prefix}" + "\n"
        end
{'fraction_non_alphanumeric': 0.11964238758874572, 'fraction_numerical': 0.0, 'mean_word_length': 2.177982034677251, 'pattern_counts': {'":': 0, '<': 149, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5923693', 'n_tokens_mistral': 4109, 'n_tokens_neox': 4019, 'n_words': 723}