Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Node.pm @ 34:a8086f85a571
Dom Builder
author | Sergey |
---|---|
date | Mon, 16 Nov 2009 18:39:25 +0300 |
parents | dd4d72600c69 |
children | 1828103371d0 |
rev | line source |
---|---|
0 | 1 package IMPL::DOM::Node; |
2 use strict; | |
3 use warnings; | |
4 | |
11 | 5 use base qw(IMPL::Object); |
0 | 6 |
4 | 7 use IMPL::Object::List; |
0 | 8 use IMPL::Class::Property; |
9 use IMPL::Class::Property::Direct; | |
10 use Scalar::Util qw(weaken); | |
11 | |
1 | 12 use IMPL::Exception; |
13 | |
0 | 14 BEGIN { |
4 | 15 public _direct property nodeName => prop_get | owner_set; |
16 public _direct property isComplex => { get => \&_getIsComplex } ; | |
17 public _direct property nodeValue => prop_all; | |
18 public _direct property childNodes => { get => \&_getChildNodes }; | |
19 public _direct property parentNode => prop_get ; | |
18 | 20 public _direct property rootNode => { get => \&_getRootNode}; |
21 private _direct property _propertyMap => prop_all ; | |
0 | 22 } |
23 | |
24 sub CTOR { | |
11 | 25 my ($this,%args) = @_; |
0 | 26 |
34 | 27 $this->nodeName(delete $args{nodeName}) or die new IMPL::InvalidArgumentException("A name is required"); |
28 $this->nodeValue(delete $args{nodeValue}); | |
29 | |
30 $this->{$_propertyMap} = \%args; | |
0 | 31 } |
32 | |
33 sub insertNode { | |
34 my ($this,$node,$pos) = @_; | |
4 | 35 |
36 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
37 | |
38 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
39 | |
40 $this->childNodes->InsertAt($pos,$node); | |
41 | |
42 $node->_setParent( $this ); | |
43 | |
44 return $node; | |
45 } | |
46 | |
30 | 47 sub appendChild { |
16
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
48 my ($this,$node) = @_; |
14 | 49 |
50 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
51 | |
52 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
53 | |
18 | 54 my $children = $this->childNodes; |
55 $children->Append($node); | |
14 | 56 |
57 $node->_setParent( $this ); | |
58 | |
59 return $node; | |
60 } | |
61 | |
30 | 62 sub appendNode { |
63 goto &appendChild; | |
64 } | |
65 | |
16
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
66 sub appendRange { |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
67 my ($this,@range) = @_; |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
68 |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
69 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if grep $_ == $this, @range; |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
70 |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
71 foreach my $node (@range) { |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
72 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
73 $node->_setParent( $this ); |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
74 } |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
75 |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
76 $this->childNodes->Append(@range); |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
77 |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
78 return $this; |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
79 } |
75d55f4ee263
Окончательная концепция описания схем и построения DOM документов
Sergey
parents:
14
diff
changeset
|
80 |
4 | 81 sub _getChildNodes { |
82 my ($this) = @_; | |
83 | |
84 $this->{$childNodes} = new IMPL::Object::List() unless $this->{$childNodes}; | |
18 | 85 return $this->{$childNodes}; |
0 | 86 } |
87 | |
88 sub removeNode { | |
89 my ($this,$node) = @_; | |
4 | 90 |
91 if ($this == $node->{$parentNode}) { | |
92 $this->childNodes->RemoveItem($node); | |
18 | 93 $node->_setParent(undef); |
94 return $node; | |
4 | 95 } else { |
96 die new IMPL::InvalidOperationException("The specified node isn't belong to this node"); | |
97 } | |
0 | 98 } |
99 | |
7 | 100 sub replaceNodeAt { |
101 my ($this,$index,$node) = @_; | |
102 | |
103 my $nodeOld = $this->childNodes->[$index]; | |
104 | |
105 die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; | |
106 | |
107 # unlink node from previous parent | |
108 $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); | |
109 | |
110 # replace (or set) old node | |
111 $this->childNodes->[$index] = $node; | |
112 | |
18 | 113 # set new parent |
7 | 114 $node->_setParent( $this ); |
115 | |
116 # unlink old node if we have one | |
18 | 117 $nodeOld->_setParent(undef) if $nodeOld; |
7 | 118 |
119 # return old node | |
120 return $nodeOld; | |
121 } | |
122 | |
0 | 123 sub removeAt { |
124 my ($this,$pos) = @_; | |
4 | 125 |
126 if ( my $node = $this->childNodes->RemoveAt($pos) ) { | |
18 | 127 $node->_setParent(undef); |
128 return $node; | |
129 } else { | |
130 return undef; | |
131 } | |
132 } | |
133 | |
134 sub removeLast { | |
135 my ($this) = @_; | |
136 | |
137 if ( my $node = $this->{$childNodes} ? $this->{$childNodes}->RemoveLast() : undef) { | |
138 $node->_setParent(undef); | |
4 | 139 return $node; |
140 } else { | |
141 return undef; | |
142 } | |
0 | 143 } |
144 | |
18 | 145 sub removeSelected { |
146 my ($this,$query) = @_; | |
147 | |
148 my @newSet; | |
149 my @result; | |
150 | |
151 if (ref $query eq 'CODE') { | |
152 &$query($_) ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes}; | |
153 } elsif (defined $query) { | |
154 $_->nodeName eq $query ? push @result, $_ : push @newSet, $_ foreach @{$this->childNodes}; | |
155 } else { | |
156 my $children = $this->childNodes; | |
157 $_->_setParent(undef) foreach @$children; | |
158 delete $this->{$childNodes}; | |
159 return wantarray ? @$children : $children; | |
160 } | |
161 | |
162 $_->_setParent(undef) foreach @result; | |
163 | |
164 $this->{$childNodes} = @newSet ? bless \@newSet ,'IMPL::Object::List' : undef; | |
165 | |
166 return wantarray ? @result : \@result; | |
167 } | |
168 | |
0 | 169 sub selectNodes { |
14 | 170 my ($this,$query) = @_; |
18 | 171 |
14 | 172 my @result; |
4 | 173 |
14 | 174 if (ref $query eq 'CODE') { |
175 @result = grep &$query($_), @{$this->childNodes}; | |
24 | 176 } elsif (ref $query eq 'ARRAY' ) { |
177 my %keys = map (($_,1),@$query); | |
178 @result = grep $keys{$_->nodeName}, @{$this->childNodes}; | |
18 | 179 } elsif (defined $query) { |
180 @result = grep $_->nodeName eq $query, @{$this->childNodes}; | |
14 | 181 } else { |
18 | 182 if (wantarray) { |
183 return @{$this->childNodes}; | |
184 } else { | |
185 @result = $this->childNodes; | |
186 return \@result; | |
187 } | |
14 | 188 } |
4 | 189 |
190 return wantarray ? @result : \@result; | |
0 | 191 } |
192 | |
7 | 193 sub firstChild { |
194 @_ >=2 ? $_[0]->replaceNodeAt(0,$_[1]) : $_[0]->childNodes->[0]; | |
195 } | |
196 | |
4 | 197 sub _getIsComplex { |
198 $_[0]->childNodes->Count ? 1 : 0; | |
199 } | |
200 | |
18 | 201 sub _getRootNode { |
202 $_[0]->{$rootNode} || $_[0]; | |
203 } | |
204 | |
205 sub _updateRootRefs { | |
206 my ($this) = @_; | |
207 | |
208 if ( my $newRoot = $this->{$parentNode} ? $this->{$parentNode}->rootNode : undef) { | |
209 if ($this->{$rootNode} ? $this->{$rootNode} != $newRoot : 1 ) { | |
210 $this->{$rootNode} = $newRoot; | |
211 weaken($this->{$rootNode}); | |
212 if ($this->{$childNodes}) { | |
213 $_->_updateRootRefs foreach @{$this->{$childNodes}}; | |
214 } | |
215 } | |
216 } elsif($this->{$rootNode}) { | |
217 delete $this->{$rootNode}; | |
218 if ($this->{$childNodes}) { | |
219 $_->_updateRootRefs foreach @{$this->{$childNodes}}; | |
220 } | |
221 } | |
222 } | |
223 | |
4 | 224 sub _setParent { |
11 | 225 my ($this,$node) = @_; |
4 | 226 |
18 | 227 |
228 if (($node || 0) != ($this->{$parentNode} || 0)) { | |
229 if ($node) { | |
230 $this->{$parentNode} = $node; | |
231 # prevent from creating cyclicreferences | |
232 weaken($this->{$parentNode}); | |
233 } else { | |
234 delete $this->{$parentNode}; | |
235 } | |
236 $this->_updateRootRefs; | |
237 } | |
0 | 238 } |
239 | |
240 sub text { | |
241 my ($this) = @_; | |
4 | 242 |
18 | 243 join '', $this->nodeValue || '', map $_->nodeValue || '', @{$this->childNodes}; |
0 | 244 } |
245 | |
21 | 246 sub nodeProperty { |
0 | 247 my $this = shift; |
248 my $name = shift; | |
249 | |
250 if (@_) { | |
251 # set | |
6 | 252 return $this->{$_propertyMap}{$name} = shift; |
0 | 253 } else { |
6 | 254 return $this->{$_propertyMap}{$name}; |
0 | 255 } |
256 } | |
257 | |
24 | 258 sub qname { |
259 $_[0]->{$nodeName}; | |
260 } | |
261 | |
262 sub path { | |
263 my ($this) = @_; | |
264 | |
265 if ($this->parentNode) { | |
266 return $this->parentNode->path.'.'.$this->qname; | |
267 } else { | |
268 return $this->qname; | |
269 } | |
270 } | |
271 | |
0 | 272 1; |