Sources/Carte/ruby/2.3.0/gems/nanaimo-0.2.3/lib/nanaimo/writer/pbxproj.rb
module Nanaimo
  class Writer
    # Transforms native ruby objects or Plist objects into their ASCII Plist
    # string representation, formatted as Xcode writes Xcode projects.
    #
    class PBXProjWriter < Writer
      ISA = String.new('isa', '')
      private_constant :ISA

      def initialize(*)
        super
        @objects_section = false
      end

      private

      def write_dictionary(object)
        n = newlines
        @newlines = false if flat_dictionary?(object)
        return super(sort_dictionary(object)) unless @objects_section
        @objects_section = false
        write_dictionary_start
        value = value_for(object)
        objects_by_isa = value.group_by { |_k, v| isa_for(v) }
        objects_by_isa.each do |isa, kvs|
          write_newline
          output << "/* Begin #{isa} section */"
          write_newline
          sort_dictionary(kvs).each do |k, v|
            write_dictionary_key_value_pair(k, v)
          end
          output << "/* End #{isa} section */"
          write_newline
        end
        write_dictionary_end
      ensure
        @newlines = n
      end

      def write_dictionary_key_value_pair(k, v)
        @objects_section = true if value_for(k) == 'objects'
        super
      end

      def sort_dictionary(dictionary)
        hash = value_for(dictionary)
        hash.to_a.sort do |(k1, v1), (k2, v2)|
          v2_isa = isa_for(v2)
          v1_isa = v2_isa && isa_for(v1)
          comp = v1_isa <=> v2_isa
          next comp if !comp.zero? && v1_isa

          key1 = value_for(k1)
          key2 = value_for(k2)
          next -1 if key1 == 'isa'
          next 1 if key2 == 'isa'
          key1 <=> key2
        end
      end

      def isa_for(dictionary)
        dictionary = value_for(dictionary)
        return unless dictionary.is_a?(Hash)
        isa = dictionary.values_at('isa', ISA).map(&method(:value_for)).compact.first
        isa && value_for(isa)
      end

      def flat_dictionary?(dictionary)
        case isa_for(dictionary)
        when 'PBXBuildFile', 'PBXFileReference'
          true
        else
          false
        end
      end
    end
  end
end
