Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Node.pm @ 106:83e356614c1e
DOM Builder now is a navigator like SimpleBuilder
PostToDOM transformation
author | wizard |
---|---|
date | Wed, 12 May 2010 17:52:12 +0400 |
parents | 196bf443b5e1 |
children | c6fb6964de4c |
rev | line source |
---|---|
49 | 1 package IMPL::DOM::Node; |
2 use strict; | |
3 use warnings; | |
4 | |
5 use base qw(IMPL::Object); | |
6 | |
7 use IMPL::Object::List; | |
8 use IMPL::Class::Property; | |
9 use IMPL::Class::Property::Direct; | |
10 use Scalar::Util qw(weaken); | |
11 | |
12 use IMPL::Exception; | |
13 | |
14 BEGIN { | |
15 public _direct property nodeName => prop_get; | |
16 public _direct property document => prop_get; | |
17 public _direct property isComplex => { get => \&_getIsComplex } ; | |
18 public _direct property nodeValue => prop_all; | |
102 | 19 public _direct property childNodes => { get => \&_getChildNodes }; # prop_list |
49 | 20 public _direct property parentNode => prop_get ; |
21 private _direct property _propertyMap => prop_all ; | |
22 } | |
23 | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
24 our %Axes = ( |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
25 parent => \&selectParent, |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
26 siblings => \&selectSiblings, |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
27 child => \&childNodes, |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
28 document => \&selectDocument |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
29 ); |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
30 |
49 | 31 sub CTOR { |
32 my ($this,%args) = @_; | |
33 | |
34 $this->{$nodeName} = delete $args{nodeName} or die new IMPL::InvalidArgumentException("A name is required"); | |
35 $this->{$nodeValue} = delete $args{nodeValue} if exists $args{nodeValue}; | |
36 if ( exists $args{document} ) { | |
37 $this->{$document} = delete $args{document}; | |
38 weaken($this->{$document}); | |
39 } | |
40 | |
41 $this->{$_propertyMap} = \%args; | |
42 } | |
43 | |
44 sub insertNode { | |
45 my ($this,$node,$pos) = @_; | |
46 | |
47 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
48 | |
49 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
50 | |
51 $this->childNodes->InsertAt($pos,$node); | |
52 | |
53 $node->_setParent( $this ); | |
54 | |
55 return $node; | |
56 } | |
57 | |
58 sub appendChild { | |
59 my ($this,$node) = @_; | |
60 | |
61 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
62 | |
63 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
64 | |
65 my $children = $this->childNodes; | |
66 $children->Append($node); | |
67 | |
68 $node->_setParent( $this ); | |
69 | |
70 return $node; | |
71 } | |
72 | |
73 sub appendNode { | |
74 goto &appendChild; | |
75 } | |
76 | |
77 sub appendRange { | |
78 my ($this,@range) = @_; | |
79 | |
80 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if grep $_ == $this, @range; | |
81 | |
82 foreach my $node (@range) { | |
83 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
84 $node->_setParent( $this ); | |
85 } | |
86 | |
87 $this->childNodes->Append(@range); | |
88 | |
89 return $this; | |
90 } | |
91 | |
92 sub _getChildNodes { | |
93 my ($this) = @_; | |
94 | |
95 $this->{$childNodes} = new IMPL::Object::List() unless $this->{$childNodes}; | |
102 | 96 return wantarray ? @{ $this->{$childNodes} } : $this->{$childNodes}; |
49 | 97 } |
98 | |
99 sub removeNode { | |
100 my ($this,$node) = @_; | |
101 | |
102 if ($this == $node->{$parentNode}) { | |
103 $this->childNodes->RemoveItem($node); | |
104 $node->_setParent(undef); | |
105 return $node; | |
106 } else { | |
107 die new IMPL::InvalidOperationException("The specified node isn't belong to this node"); | |
108 } | |
109 } | |
110 | |
111 sub replaceNodeAt { | |
112 my ($this,$index,$node) = @_; | |
113 | |
114 my $nodeOld = $this->childNodes->[$index]; | |
115 | |
116 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
117 | |
118 # unlink node from previous parent | |
119 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
120 | |
121 # replace (or set) old node | |
122 $this->childNodes->[$index] = $node; | |
123 | |
124 # set new parent | |
125 $node->_setParent( $this ); | |
126 | |
127 # unlink old node if we have one | |
128 $nodeOld->_setParent(undef) if $nodeOld; | |
129 | |
130 # return old node | |
131 return $nodeOld; | |
132 } | |
133 | |
134 sub removeAt { | |
135 my ($this,$pos) = @_; | |
136 | |
137 if ( my $node = $this->childNodes->RemoveAt($pos) ) { | |
138 $node->_setParent(undef); | |
139 return $node; | |
140 } else { | |
141 return undef; | |
142 } | |
143 } | |
144 | |
145 sub removeLast { | |
146 my ($this) = @_; | |
147 | |
148 if ( my $node = $this->{$childNodes} ? $this->{$childNodes}->RemoveLast() : undef) { | |
149 $node->_setParent(undef); | |
150 return $node; | |
151 } else { | |
152 return undef; | |
153 } | |
154 } | |
155 | |
156 sub removeSelected { | |
157 my ($this,$query) = @_; | |
158 | |
159 my @newSet; | |
160 my @result; | |
161 | |
162 if (ref $query eq 'CODE') { | |
163 &$query($_) ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes}; | |
164 } elsif (defined $query) { | |
165 $_->nodeName eq $query ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes}; | |
166 } else { | |
167 my $children = $this->childNodes; | |
168 $_->_setParent(undef) foreach @$children; | |
169 delete $this->{$childNodes}; | |
170 return wantarray ? @$children : $children; | |
171 } | |
172 | |
173 $_->_setParent(undef) foreach @result; | |
174 | |
175 $this->{$childNodes} = @newSet ? bless \@newSet ,'IMPL::Object::List' : undef; | |
176 | |
177 return wantarray ? @result : \@result; | |
178 } | |
179 | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
180 sub resolveAxis { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
181 my ($this,$axis) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
182 return $Axes{$axis}->($this) |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
183 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
184 |
49 | 185 sub selectNodes { |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
186 my ($this,$query,$axis) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
187 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
188 $axis ||= 'child'; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
189 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
190 die new IMPL::InvalidOperationException('Unknown axis',$axis) unless exists $Axes{$axis}; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
191 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
192 my $nodes = $this->resolveAxis($axis); |
49 | 193 |
194 my @result; | |
195 | |
196 if (ref $query eq 'CODE') { | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
197 @result = grep &$query($_), @{$nodes}; |
49 | 198 } elsif (ref $query eq 'ARRAY' ) { |
199 my %keys = map (($_,1),@$query); | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
200 @result = grep $keys{$_->nodeName}, @{$nodes}; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
201 } elsif (ref $query eq 'HASH') { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
202 while( my ($axis,$filter) = each %$query ) { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
203 push @result, $this->selectNodes($filter,$axis); |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
204 } |
49 | 205 } elsif (defined $query) { |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
206 @result = grep $_->nodeName eq $query, @{$nodes}; |
49 | 207 } else { |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
208 return wantarray ? @{$nodes} : $nodes; |
49 | 209 } |
210 | |
211 return wantarray ? @result : \@result; | |
212 } | |
213 | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
214 sub selectPath { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
215 my ($this,$path) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
216 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
217 my @set = ($this); |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
218 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
219 while (my $query = shift @$path) { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
220 @set = map $_->selectNodes($query), @set; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
221 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
222 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
223 return wantarray ? @set : \@set; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
224 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
225 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
226 sub selectParent { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
227 my ($this) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
228 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
229 if ($this->parentNode) { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
230 return wantarray ? $this->parentNode : [$this->parentNode]; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
231 } else { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
232 return wantarray ? () : []; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
233 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
234 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
235 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
236 sub selectSiblings { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
237 my ($this) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
238 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
239 if ($this->parentNode) { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
240 return $this->parentNode->selectNodes( sub { $_ != $this } ); |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
241 } else { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
242 return wantarray ? () : []; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
243 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
244 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
245 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
246 sub selectDocument { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
247 my ($this) = @_; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
248 |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
249 if ($this->document) { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
250 return wantarray ? $this->document : [$this->document]; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
251 } else { |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
252 return wantarray ? () : []; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
253 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
254 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
102
diff
changeset
|
255 |
49 | 256 sub firstChild { |
257 @_ >=2 ? $_[0]->replaceNodeAt(0,$_[1]) : $_[0]->childNodes->[0]; | |
258 } | |
259 | |
260 sub _getIsComplex { | |
261 $_[0]->childNodes->Count ? 1 : 0; | |
262 } | |
263 | |
264 sub _updateDocRefs { | |
265 my ($this) = @_; | |
266 | |
267 # this method is called by the parent node on his children, so we need no to check parent | |
268 $this->{$document} = $this->{$parentNode}->document; | |
269 | |
270 # prevent cyclic | |
271 weaken($this->{$document}) if $this->{$document}; | |
272 | |
273 $_->_updateDocRefs foreach @{$this->{$childNodes}}; | |
274 } | |
275 | |
276 sub _setParent { | |
277 my ($this,$node) = @_; | |
278 | |
279 | |
280 if (($node || 0) != ($this->{$parentNode} || 0)) { | |
281 my $newOwner; | |
282 if ($node) { | |
283 $this->{$parentNode} = $node; | |
284 $newOwner = $node->document || 0; | |
285 | |
286 # prevent from creating cyclicreferences | |
287 weaken($this->{$parentNode}); | |
288 | |
289 } else { | |
290 delete $this->{$parentNode}; | |
75 | 291 |
292 #keep document | |
293 $newOwner = $this->{$document}; | |
49 | 294 } |
295 | |
296 if (($this->{$document}||0) != $newOwner) { | |
297 $this->{$document} = $newOwner; | |
298 weaken($this->{$document}) if $newOwner; | |
299 $_->_updateDocRefs foreach @{$this->childNodes}; | |
300 } | |
301 } | |
302 } | |
303 | |
304 sub text { | |
305 my ($this) = @_; | |
306 | |
307 join ('', $this->nodeValue || '', map ($_->text || '', @{$this->childNodes})); | |
308 } | |
309 | |
310 sub nodeProperty { | |
311 my $this = shift; | |
312 my $name = shift; | |
313 | |
314 if (@_) { | |
315 # set | |
316 return $this->{$_propertyMap}{$name} = shift; | |
317 } else { | |
318 return $this->{$_propertyMap}{$name}; | |
319 } | |
320 } | |
321 | |
322 sub qname { | |
323 $_[0]->{$nodeName}; | |
324 } | |
325 | |
326 sub path { | |
327 my ($this) = @_; | |
328 | |
329 if ($this->parentNode) { | |
330 return $this->parentNode->path.'.'.$this->qname; | |
331 } else { | |
332 return $this->qname; | |
333 } | |
334 } | |
335 | |
336 1; | |
75 | 337 |
338 __END__ | |
339 | |
340 =pod | |
341 | |
342 =head1 NAME | |
343 | |
344 C<IMPL::DOM::Node> Элемент DOM модели | |
345 | |
346 =head1 DESCRIPTION | |
347 | |
348 Базовый узел DOM модели. От него можно наследовать другие элементы DOM модели. | |
349 | |
350 =head1 MEMBERS | |
351 | |
352 =head2 PROPERTIES | |
353 | |
354 =over | |
355 | |
356 =item C<[get] nodeName> | |
357 | |
358 Имя узла. Задается при создании. | |
359 | |
360 =item C<[get] document> | |
361 | |
362 Документ к которому принадлежит узел. Задается при поздании узла. | |
363 | |
364 =item C<[get] isComplex> | |
365 | |
366 Определяет является ли узел сложным (тоесть есть ли дети). | |
367 | |
368 C<true> - есть, C<false> - нет. | |
369 | |
370 =item C<[get,set] nodeValue> | |
371 | |
372 Значение узла, обычно простой скаляр, но ничто не мешает туда | |
373 устанавливать любое значение. | |
374 | |
375 =item C<[get,list] childNodes> | |
376 | |
377 Список детей, является списокм C<IMPL::Object::List>. | |
378 | |
379 =item C<[get] parentNode> | |
380 | |
381 Ссылка на родительский элемент, если таковой имеется. | |
382 | |
383 =head2 METHODS | |
384 | |
385 =cut |