Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Node.pm @ 104:196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
Minor and major fixes almost for everything.
A 'Source' property of the ValidationErrors generated from a NodeSet or a NodeList is subject to change in the future.
| author | wizard |
|---|---|
| date | Tue, 11 May 2010 02:42:59 +0400 |
| parents | cf3b6ef2be22 |
| 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 |
