4 # Copyright (C) 2002-2005 Freescale Semiconductor, Inc.
5 # Distributed under terms of the GNU General Public License (GPL).
7 # This package contains routines for Document Object Model (DOM) objects.
8 # A DOM object is the prest equivalent of a doctree object.
11 # _`DOM`: Recursive hash reference with following
13 # ``tag``: The name of the tag of the DOM object
14 # ``attr``: Reference to hash of attribute/value pairs
15 # ``content``: Reference to array of DOM objects
16 # ``text``: Contains the literal text for #PCDATA
17 # ``internal``: Reference to hash of internal attribute/value pairs
18 # ``source``: Optionally contains the source
19 # ``lineno``: Optionally contains the line number
20 # ``lit``: Optionally contains the literal text
21 # ``val``: The value returned by the DOM's handler (added
22 # during traversal of the writer's handlers)
25 # ``%DOM::PARENT``: hash whose keys are DOM references and whose values are
26 # a reference to the DOM object of the parent.
27 # Should only be accessed indirectly through the
28 # ``DOM::parent`` method.
34 # Creates a new DOM object.
35 # Arguments: (optional) tag, (optional) list of attribute/value pairs
38 my ($class, $tag, %attr) = @_;
41 $dom->{tag} = $tag if defined $tag;
42 $dom->{attr} = {%attr} if %attr;
48 # Creates a new DOM object that is a "#PCDATA" type.
52 my ($class, $text) = @_;
54 return bless {tag=>'#PCDATA', text=>$text, content=>[] };
58 # Appends to the contents of a DOM object.
59 # Arguments: DOM objects to append
60 # Returns: The new number of contents
62 my ($dom, @doms) = @_;
64 @PARENT{@doms} = ($dom) x @doms;
65 # grep do {$_->{parent} = $dom}, @doms;
66 push @{$dom->{content}}, @doms;
70 # Returns the content objects the DOM object has
72 # Returns: Array of content DOM objects
74 my ($dom, @doms) = @_;
76 return @{$dom->{content}};
80 # Returns the index of a child in the contents (-1 if it does not occur).
81 # Arguments: child DOM object
82 # Returns: index number
84 my ($dom, $child) = @_;
86 for ($i=0; $i<@{$dom->{content}}; $i++) {
87 return $i if $dom->{content}[$i] == $child;
93 # Returns the last DOM in the contents of a DOM.
95 # Returns: last DOM object (or undefined)
100 if (@{$dom->{content}}) {
101 $last = $dom->{content}[-1];
107 # Returns the next DOM in the logical structure of the tree. If the
108 # given DOM is the last in a section or list, this routine may have to
109 # go up in the tree to find the next object.
110 # Arguments: optional regular expression for tags to ignore
111 # Returns: next DOM or undef
113 my ($dom, $ignore) = @_;
115 # my $parent = $dom->{parent};
116 my $parent = $dom->parent();
117 my $indx = $parent->index($dom) + 1;
118 my $cur_parent = $parent;
119 while (defined $cur_parent) {
120 while ($indx < $cur_parent->num_contents()) {
121 my $tag = $cur_parent->{content}[$indx]{tag};
122 if (defined $ignore && $tag =~ /^(?:$ignore)$/) {
123 # It's a skippable tag
127 return $cur_parent->{content}[$indx];
129 my $new_parent = $cur_parent->parent();
130 return unless defined $new_parent;
131 $indx = $new_parent->index($cur_parent) + 1;
132 $cur_parent = $new_parent;
138 # Returns the number of content objects the DOM object has
140 # Returns: Number of elements
142 my ($dom, @doms) = @_;
144 return 0+@{$dom->{content}};
148 # Returns the parent DOM of an instance.
150 # Returns: The DOM object's parent
154 return $PARENT{$dom};
158 # Puts the arguments at the beginning of the contents of a DOM object.
159 # Arguments: DOM objects to prepend
160 # Returns: The new number of objects
162 my ($dom, @doms) = @_;
164 @PARENT{@doms} = ($dom) x @doms;
165 # grep do {$_->{parent} = $dom}, @doms;
166 unshift (@{$dom->{content}}, @doms);
170 # Goes through a DOM object recursively calling a subroutine on every
171 # element. It can do either preorder, postorder or bothorder traversal
172 # (defaults to postorder). Unlike Reshape, it does not modify the
173 # children of the nodes it visits.
174 # Arguments: callback routine, optional 'pre'/'post'/'both',
175 # optional additional arguments to be propagated
176 # Returns: Stop recursion flag
177 # Callback routine arguments: target DOM, 'pre'/'post',
178 # optional additional arguments
179 # Callback routine returns: non-zero in 'pre' mode to avoid further recursion.
181 my($dom, $sub, $when, @args) = @_;
183 $when = 'post' unless defined $when;
185 if ($when =~ /^(pre|both)$/) {
186 $stop = eval { &{$sub}($dom, 'pre', @args) };
187 die "Error: $sub: $@" if $@;
191 my @contents = @{$dom->{content}};
193 for ($i=0; $i<@contents; $i++) {
194 my $content = $contents[$i];
195 $content->Recurse($sub, $when, @args);
198 if ($when ne 'pre') {
199 eval { &{$sub}($dom, 'post', @args) };
200 die "Error: $sub: $@" if $@;
205 # Replaces the contents of a DOM object with a new set of objects.
206 # Arguments: DOM objects to replace
209 my ($dom, @doms) = @_;
211 @PARENT{@doms} = ($dom) x @doms;
212 # grep do {$_->{parent} = $dom}, @doms;
213 @{$dom->{content}} = @doms;
218 # Goes through a DOM object recursively calling a subroutine on every
219 # element. It can do either preorder, postorder or bothorder traversal
220 # (defaults to postorder).
221 # Arguments: callback routine, optional 'pre'/'post'/'both',
222 # optional additional arguments to be propagated
223 # Returns: Reference to new set objects to replace the current object
224 # Callback routine arguments: target DOM, 'pre'/'post',
225 # optional additional arguments
226 # Callback routine returns: whatever list of DOM objects are to be
227 # substituted for the current node (this
228 # list is returned on the 'post' call if
229 # 'both' is selected).
231 my($dom, $sub, $when, @args) = @_;
233 $when = 'post' unless defined $when;
235 if ($when =~ /^(pre|both)$/) {
236 @newdom = eval { &{$sub}($dom, 'pre', @args) };
237 die "Error: $sub: $@" if $@;
240 my @contents = @{$dom->{content}};
243 for ($i=0; $i<@contents; $i++) {
244 my $content = $contents[$i];
245 my @new_contents = grep(defined $_,
246 $content->Reshape($sub, $when, @args));
247 $dom->splice($replace, 1, @new_contents);
248 $replace += @new_contents;
251 if ($when ne 'pre') {
252 @newdom = eval { &{$sub}($dom, 'post', @args) };
253 die "Error: $sub: $@" if $@;
260 # Splices objects into the contents of a DOM object.
261 # Arguments: start index, number to replace, list of DOM objects to splice
262 # Returns: Array of removed objects
264 my ($dom, $index, $n, @doms) = @_;
266 @PARENT{@doms} = ($dom) x @doms;
267 # grep do {$_->{parent} = $dom}, @doms;
268 return splice(@{$dom->{content}}, $index, $n, @doms);