lib/xcodeproj/project/object/helpers/groupable_helper.rb
module Xcodeproj class Project module Object class GroupableHelper class << self # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [PBXGroup, PBXProject] The parent of the object. # def parent(object) referrers = object.referrers.uniq if referrers.count > 1 referrers = referrers.grep(PBXGroup) end if referrers.count == 0 raise '[Xcodeproj] Consistency issue: no parent ' \ "for object `#{object.display_name}`: "\ "`#{object.referrers.join('`, `')}`" elsif referrers.count > 1 raise '[Xcodeproj] Consistency issue: unexpected multiple parents ' \ "for object `#{object.display_name}`: "\ "#{object.referrers}" end referrers.first end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [Array<PBXGroup, PBXProject>] The parents of the object. # def parents(object) if main_group?(object) [] else parent = parent(object) parents(parent).push(parent) end end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [String] A representation of the group hierarchy. # def hierarchy_path(object) unless main_group?(object) parent = parent(object) parent = parent.hierarchy_path if parent.respond_to?(:hierarchy_path) "#{parent}/#{object.display_name}" end end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [Bool] Wether the object is the main group of the project. # def main_group?(object) object.equal?(object.project.main_group) end # Moves the object to a new parent. # # @param [PBXGroup, PBXFileReference] object # The object to move. # # @param [PBXGroup] new_parent # The new parent. # # @return [void] # def move(object, new_parent) unless object raise "[Xcodeproj] Attempt to move nil object to `#{new_parent}`." end unless new_parent raise "[Xcodeproj] Attempt to move object `#{object}` to nil parent." end if new_parent.equal?(object) raise "[Xcodeproj] Attempt to move object `#{object}` to itself." end if parents(new_parent).include?(object) raise "[Xcodeproj] Attempt to move object `#{object}` to a child object `#{new_parent}`." end object.parent.children.delete(object) new_parent << object end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [Pathname] The absolute path of the object resolving the # source tree. # def real_path(object) source_tree = source_tree_real_path(object) path = object.path || ''.freeze if source_tree source_tree + path else Pathname(path) end end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [Pathname] The path of the object without resolving the # source tree. # def full_path(object) folder = case object.source_tree when '<group>' object_parent = parent(object) if object_parent.isa == 'PBXProject'.freeze nil else full_path(object_parent) end when 'SOURCE_ROOT' nil when '<absolute>' Pathname.new('/'.freeze) else Pathname.new("${#{object.source_tree}}") end folder ||= Pathname.new('') if object.path folder + object.path else folder end end # @param [PBXGroup, PBXFileReference] object # The object to analyze. # # @return [Pathname] The absolute path of the source tree of the # object. # def source_tree_real_path(object) case object.source_tree when '<group>' object_parent = parent(object) if object_parent.isa == 'PBXProject'.freeze object.project.project_dir else real_path(object_parent) end when 'SOURCE_ROOT' object.project.project_dir when '<absolute>' nil else Pathname.new("${#{object.source_tree}}") end end # @return [Hash{Symbol => String}] The source tree values by they # symbol representation. # SOURCE_TREES_BY_KEY = { :absolute => '<absolute>', :group => '<group>', :project => 'SOURCE_ROOT', :built_products => 'BUILT_PRODUCTS_DIR', :developer_dir => 'DEVELOPER_DIR', :sdk_root => 'SDKROOT', }.freeze # Sets the source tree of the given object. # # @param [Symbol, String] source_tree # The source tree, either a string or a key for # {SOURCE_TREES_BY_KEY}. # # @return [void] # def set_source_tree(object, source_tree) source_tree = normalize_source_tree(source_tree) object.source_tree = source_tree end # Sets the path of the given object according to the provided source # tree key. The path is converted to relative according to the real # path of the source tree for group and project source trees, if both # paths are relative or absolute. Otherwise the path is set as # provided. # # @param [PBXGroup, PBXFileReference] object # The object whose path needs to be set. # # @param [#to_s] path # The path. # # @param [Symbol, String] source_tree # The source tree, either a string or a key for # {SOURCE_TREES_BY_KEY}. # # @return [void] # def set_path_with_source_tree(object, path, source_tree) path = Pathname.new(path) source_tree = normalize_source_tree(source_tree) object.source_tree = source_tree if source_tree == SOURCE_TREES_BY_KEY[:absolute] unless path.absolute? raise '[Xcodeproj] Attempt to set a relative path with an ' \ "absolute source tree: `#{path}`" end object.path = path.to_s elsif source_tree == SOURCE_TREES_BY_KEY[:group] || source_tree == SOURCE_TREES_BY_KEY[:project] source_tree_real_path = GroupableHelper.source_tree_real_path(object) if source_tree_real_path && source_tree_real_path.absolute? == path.absolute? relative_path = path.relative_path_from(source_tree_real_path) object.path = relative_path.to_s else object.path = path.to_s end else object.path = path.to_s end end private # @group Helpers #-------------------------------------------------------------------# # Converts the given source tree to its string value. # # @param [Symbol, String] source_tree # The source tree, either a string or a key for # {SOURCE_TREES_BY_KEY}. # # @return [String] the string value of the source tree. # def normalize_source_tree(source_tree) if source_tree.is_a?(Symbol) source_tree = SOURCE_TREES_BY_KEY[source_tree] end unless SOURCE_TREES_BY_KEY.values.include?(source_tree) raise "[Xcodeproj] Unrecognized source tree option `#{source_tree}`" end source_tree end #-------------------------------------------------------------------# end end end end end