lib/xcodeproj/project/object_dictionary.rb
module Xcodeproj class Project # This class represents relationships to other objects stored in a # Dictionary. # # It works in conjunction with the {AbstractObject} class to ensure that # the project is not serialized with unreachable objects by updating the # with reference count on modifications. # # @note This class is a stub currently only being used by # {PBXProject#project_references}. It doesn't perform type cheeking # and the keys of the dictionary are in camel-case. To provide full # support as the other classes the dictionary should be able to # # Give the following attribute: # # has_many_references_by_keys :project_references, { # :project_ref => PBXFileReference, # :product_group => PBXGroup # } # # This should be possible: # # #=> Note the API: # root_object.project_references.project_ref = file # # #=> This should raise: # root_object.project_references.product_group = file # # generate setters and getters from the specification hash. # # Also the interface is a dirty hybrid between the # {AbstractObjectAttribute} and the {ObjectList}. # # @note Concerning the mutations methods it is safe to call only those # which are overridden to inform objects reference count. Ideally all # the hash methods should be covered, but this is not done yet. # Moreover it is a moving target because the methods of array # usually are implemented in C # # @todo Cover all the mutations methods of the {Hash} class. # class ObjectDictionary < Hash # {Xcodeproj} clients are not expected to create instances of # {ObjectDictionary}, it is always initialized empty and automatically by # the synthesized methods generated by {AbstractObject.has_many}. # def initialize(attribute, owner) @attribute = attribute @owner = owner end # @return [Array<Class>] The attribute that generated the list. # attr_reader :attribute # @return [Array<Class>] The object that owns the list. # attr_reader :owner #------------------------------------------------------------------------# # @!group Notification enabled methods # TODO: the overridden methods are incomplete. # Associates an object to the given key and updates its references count. # # @param [String] key # the key # # @param [AbstractObject] object # the object to add to the dictionary. # # @return [void] # def []=(key, object) if object perform_additions_operations(object) else perform_deletion_operations(self[key]) end super end # Removes the given key from the dictionary and informs the object that # is not longer referenced by the owner. # # @param [String] key # the key # # @return [void] # def delete(key) object = self[key] perform_deletion_operations(object) super end #------------------------------------------------------------------------# # @!group AbstractObject # The plist representation of the dictionary where the objects are # replaced by their UUIDs. # # @return [Hash<String => String>] # def to_plist result = {} each { |key, obj| result[key] = obj.uuid } result end # Returns a cascade representation of the object without UUIDs. # # @return [Hash<String => String>] # def to_tree_hash result = {} each { |key, obj| result[key] = obj.to_tree_hash } result end # Removes all the references to a given object. # # @return [void] # def remove_reference(object) each { |key, obj| self[key] = nil if obj == object } end #------------------------------------------------------------------------# # @!group ObjectList # Informs the objects contained in the dictionary that another object is # referencing them. # # @return [void] # def add_referrer(referrer) values.each { |obj| obj.add_referrer(referrer) } end # Informs the objects contained in the dictionary that another object # stopped referencing them. # # @return [void] # def remove_referrer(referrer) values.each { |obj| obj.remove_referrer(referrer) } end #------------------------------------------------------------------------# # @!group Notification Methods private # Informs an object that it was added to the dictionary. In practice it # adds the owner of the list as referrer to the objects. It also # validates the value. # # @return [void] # def perform_additions_operations(objects) objects = [objects] unless objects.is_a?(Array) objects.each do |obj| obj.add_referrer(owner) attribute.validate_value(obj) end end # Informs an object that it was removed from to the dictionary, so it can # remove it from its referrers and take the appropriate actions. # # @return [void] # def perform_deletion_operations(objects) objects = [objects] unless objects.is_a?(Array) objects.each do |obj| obj.remove_referrer(owner) end end end end end