1require "rexml/child"
2
3module REXML
4  # A parent has children, and has methods for accessing them.  The Parent
5  # class is never encountered except as the superclass for some other
6  # object.
7  class Parent < Child
8    include Enumerable
9
10    # Constructor
11    # @param parent if supplied, will be set as the parent of this object
12    def initialize parent=nil
13      super(parent)
14      @children = []
15    end
16
17    def add( object )
18      #puts "PARENT GOTS #{size} CHILDREN"
19      object.parent = self
20      @children << object
21      #puts "PARENT NOW GOTS #{size} CHILDREN"
22      object
23    end
24
25    alias :push :add
26    alias :<< :push
27
28    def unshift( object )
29      object.parent = self
30      @children.unshift object
31    end
32
33    def delete( object )
34      found = false
35      @children.delete_if {|c| c.equal?(object) and found = true }
36      object.parent = nil if found
37      found ? object : nil
38    end
39
40    def each(&block)
41      @children.each(&block)
42    end
43
44    def delete_if( &block )
45      @children.delete_if(&block)
46    end
47
48    def delete_at( index )
49      @children.delete_at index
50    end
51
52    def each_index( &block )
53      @children.each_index(&block)
54    end
55
56    # Fetches a child at a given index
57    # @param index the Integer index of the child to fetch
58    def []( index )
59      @children[index]
60    end
61
62    alias :each_child :each
63
64
65
66    # Set an index entry.  See Array.[]=
67    # @param index the index of the element to set
68    # @param opt either the object to set, or an Integer length
69    # @param child if opt is an Integer, this is the child to set
70    # @return the parent (self)
71    def []=( *args )
72      args[-1].parent = self
73      @children[*args[0..-2]] = args[-1]
74    end
75
76    # Inserts an child before another child
77    # @param child1 this is either an xpath or an Element.  If an Element,
78    # child2 will be inserted before child1 in the child list of the parent.
79    # If an xpath, child2 will be inserted before the first child to match
80    # the xpath.
81    # @param child2 the child to insert
82    # @return the parent (self)
83    def insert_before( child1, child2 )
84      if child1.kind_of? String
85        child1 = XPath.first( self, child1 )
86        child1.parent.insert_before child1, child2
87      else
88        ind = index(child1)
89        child2.parent.delete(child2) if child2.parent
90        @children[ind,0] = child2
91        child2.parent = self
92      end
93      self
94    end
95
96    # Inserts an child after another child
97    # @param child1 this is either an xpath or an Element.  If an Element,
98    # child2 will be inserted after child1 in the child list of the parent.
99    # If an xpath, child2 will be inserted after the first child to match
100    # the xpath.
101    # @param child2 the child to insert
102    # @return the parent (self)
103    def insert_after( child1, child2 )
104      if child1.kind_of? String
105        child1 = XPath.first( self, child1 )
106        child1.parent.insert_after child1, child2
107      else
108        ind = index(child1)+1
109        child2.parent.delete(child2) if child2.parent
110        @children[ind,0] = child2
111        child2.parent = self
112      end
113      self
114    end
115
116    def to_a
117      @children.dup
118    end
119
120    # Fetches the index of a given child
121    # @param child the child to get the index of
122    # @return the index of the child, or nil if the object is not a child
123    # of this parent.
124    def index( child )
125      count = -1
126      @children.find { |i| count += 1 ; i.hash == child.hash }
127      count
128    end
129
130    # @return the number of children of this parent
131    def size
132      @children.size
133    end
134
135    alias :length :size
136
137    # Replaces one child with another, making sure the nodelist is correct
138    # @param to_replace the child to replace (must be a Child)
139    # @param replacement the child to insert into the nodelist (must be a
140    # Child)
141    def replace_child( to_replace, replacement )
142      @children.map! {|c| c.equal?( to_replace ) ? replacement : c }
143      to_replace.parent = nil
144      replacement.parent = self
145    end
146
147    # Deeply clones this object.  This creates a complete duplicate of this
148    # Parent, including all descendants.
149    def deep_clone
150      cl = clone()
151      each do |child|
152        if child.kind_of? Parent
153          cl << child.deep_clone
154        else
155          cl << child.clone
156        end
157      end
158      cl
159    end
160
161    alias :children :to_a
162
163    def parent?
164      true
165    end
166  end
167end
168