/[suikacvs]/messaging/manakai/lib/Message/DOM/Node.pm
Suika

Contents of /messaging/manakai/lib/Message/DOM/Node.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (show annotations) (download)
Sat Jul 14 09:19:11 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.13: +56 -2 lines
++ manakai/t/ChangeLog	14 Jul 2007 09:19:01 -0000
	* DOM-Node.t: Test data for new constants and attributes
	are added.

	* DOM-TypeInfo.t: Tests for constants are added.

2007-07-14  Wakaba  <wakaba@suika.fam.cx>

++ manakai/lib/Message/DOM/ChangeLog	14 Jul 2007 09:17:51 -0000
	* AttributeDefinition.pm (node_value): Implemented.
	(create_attribute_definition): Implemented.

	* DOMConfiguration.pm (%{}, TIEHASH,
	get_parameter, set_parameter, can_set_parameter,
	EXISTS, DELETE, parameter_names, FETCH, STORE,
	FIRSTKEY, LASTKEY): Implemented.

	* DOMDocument.pm (____new): Set |error-handler| default.
	(get_elements_by_tag_name, get_elements_by_tag_name_ns): Implemented.

	* DOMElement.pm (get_elements_by_tag_name, get_elements_by_tag_name_ns):
	Implemented.

	* DOMException.pm: Error types for |DOMConfiguration|
	are added.

	* DOMStringList.pm (Message::DOM::DOMStringList::StaticList): New
	class.

	* DocumentType.pm (get_element_type_definition_node,
	get_general_entity_node, get_notation_node,
	set_element_type_definition_node, set_general_entity_node,
	set_notation_node, create_document_type_definition): Implemented.

	* ElementTypeDefinition.pm (get_attribute_definition_node,
	set_attribute_definition_node, create_element_type_definition):
	Implemented.

	* Entity.pm (create_general_entity): Implemented.

	* Node.pm: Constants in |OperationType| definition
	group are added.
	(manakai_language): Implemented.

	* NodeList.pm (Message::DOM::NodeList::GetElementsList): New
	class.

	* Notation.pm (create_notation): Implemented.

2007-07-14  Wakaba  <wakaba@suika.fam.cx>

1 package Message::DOM::Node;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.13 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 push our @ISA, 'Message::IF::Node';
5 require Scalar::Util;
6 require Message::DOM::DOMException;
7
8 ## NOTE:
9 ## Node
10 ## + Attr (2)
11 ## + AttributeDefinition (81002)
12 ## + CharacterData
13 ## + Comment (8)
14 ## + Text (3)
15 ## + CDATASection (4)
16 ## + Document (9)
17 ## + DocumentFragment (11)
18 ## + DocumentType (10)
19 ## + Element (1)
20 ## + ElementTypeDefinition (81001)
21 ## + Entity (6)
22 ## + EntityReference (5)
23 ## + Notation (12)
24 ## + ProcessingInstruction (7)
25
26 use overload
27 '==' => 'is_equal_node',
28 '!=' => sub {
29 return not ($_[0] == $_[1]);
30 },
31 #eq => sub { $_[0] eq $_[1] }, ## is_same_node
32 #ne => sub { $_[0] ne $_[1] }, ## not is_same_node
33 fallback => 1;
34
35 ## The |Node| interface - constants
36
37 ## Definition group NodeType
38
39 ## NOTE: Numeric codes up to 200 are reserved by W3C [DOM1SE, DOM2, DOM3].
40
41 sub ELEMENT_NODE () { 1 }
42 sub ATTRIBUTE_NODE () { 2 }
43 sub TEXT_NODE () { 3 }
44 sub CDATA_SECTION_NODE () { 4 }
45 sub ENTITY_REFERENCE_NODE () { 5 }
46 sub ENTITY_NODE () { 6 }
47 sub PROCESSING_INSTRUCTION_NODE () { 7 }
48 sub COMMENT_NODE () { 8 }
49 sub DOCUMENT_NODE () { 9 }
50 sub DOCUMENT_TYPE_NODE () { 10 }
51 sub DOCUMENT_FRAGMENT_NODE () { 11 }
52 sub NOTATION_NODE () { 12 }
53 sub ELEMENT_TYPE_DEFINITION_NODE () { 81001 }
54 sub ATTRIBUTE_DEFINITION_NODE () { 81002 }
55
56 ## Definition group DocumentPosition
57
58 sub DOCUMENT_POSITION_DISCONNECTED () { 0x01 }
59 sub DOCUMENT_POSITION_PRECEDING () { 0x02 }
60 sub DOCUMENT_POSITION_FOLLOWING () { 0x04 }
61 sub DOCUMENT_POSITION_CONTAINS () { 0x08 }
62 sub DOCUMENT_POSITION_CONTAINED_BY () { 0x10 }
63 sub DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC () { 0x20 }
64
65 ## TODO: Define |UserDataHandler| Perl bindig
66 ## OperationType
67 sub NODE_CLONED () { 1 }
68 sub NODE_IMPORTED () { 2 }
69 sub NODE_DELETED () { 3 }
70 sub NODE_RENAMED () { 4 }
71 sub NODE_ADOPTED () { 5 }
72
73 sub ____new ($$) {
74 my $self = bless \({}), shift;
75 $$self->{owner_document} = shift;
76 Scalar::Util::weaken ($$self->{owner_document});
77 return $self;
78 } # ____new
79
80 sub ___report_error ($$) {
81 $_[1]->throw;
82 } # ___report_error
83
84 sub AUTOLOAD {
85 my $method_name = our $AUTOLOAD;
86 $method_name =~ s/.*:://;
87 return if $method_name eq 'DESTROY';
88
89 if ({
90 ## Read-only attributes (trivial accessors)
91 owner_document => 1,
92 parent_node => 1,
93 manakai_read_only => 1,
94 }->{$method_name}) {
95 no strict 'refs';
96 eval qq{
97 sub $method_name (\$) {
98 return \${\$_[0]}->{$method_name};
99 }
100 };
101 goto &{ $AUTOLOAD };
102 } else {
103 require Carp;
104 Carp::croak (qq<Can't locate method "$AUTOLOAD">);
105 }
106 } # AUTOLOAD
107
108 ## |Node| attributes
109
110 ## NOTE: Overridden by |Element|.
111 sub attributes () { undef }
112
113 sub base_uri ($) {
114 ## NOTE: Overridden by |Attr|, |CharacterData|, |Document|, |DocumentType|,
115 ## |Element|, |EntityReference|, and |ProcessingInstruction|.
116
117 local $Error::Depth = $Error::Depth + 1;
118 return $_[0]->owner_document->base_uri;
119 } # base_uri
120
121 sub child_nodes ($) {
122 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
123 ## |Notation|, and |ProcessingInstruction|.
124 require Message::DOM::NodeList;
125 return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
126 } # child_nodes
127
128 sub manakai_expanded_uri ($) {
129 my $self = shift;
130 local $Error::Depth = $Error::Depth + 1;
131 my $ln = $self->local_name;
132 if (defined $ln) {
133 my $nsuri = $self->namespace_uri;
134 if (defined $nsuri) {
135 return $nsuri . $ln;
136 } else {
137 return $ln;
138 }
139 } else {
140 return undef;
141 }
142 } # manakai_expanded_uri
143
144 sub first_child ($) {
145 my $self = shift;
146 return $$self->{child_nodes} ? $$self->{child_nodes}->[0] : undef;
147 } # first_child
148
149 sub manakai_language ($;$) {
150 my $self = $_[0];
151
152 if (@_ > 1) {
153 local $Error::Depth = $Error::Depth + 1;
154 if ($self->node_type == 1) { # ELEMENT_NODE
155 if (defined $_[1]) {
156 if ($self->has_attribute_ns (undef, 'xml:lang')) {
157 $self->set_attribute_ns (undef, [undef, 'xml:lang'] => $_[1]);
158 # or exception
159 } else {
160 $self->set_attribute_ns
161 (q<http://www.w3.org/XML/1998/namespace>, 'xml:lang', $_[1]);
162 }
163 } else {
164 $self->remove_attribute_ns
165 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
166 $self->remove_attribute_ns (undef, 'xml:lang');
167 }
168 }
169
170 return undef unless defined wantarray;
171 }
172
173 my $target = $self;
174 while (defined $target) {
175 if ($target->node_Type == 1) { # ELEMENT_NODE
176 my $r = $target->get_attribute_ns
177 (q<http://www.w3.org/XML/1998/namespace>, 'lang');
178 return $r if defined $r;
179
180 $r = $target->get_attribute_ns (undef, 'xml:lang');
181 return $r if defined $r;
182 }
183
184 $target = $target->parent_node;
185 }
186
187 ## TODO: from ownerDocument
188 ## TODO: from upper-level protocol
189
190 ## TODO: Documentation
191
192 return '';
193 } # manakai_language
194
195 sub last_child ($) {
196 my $self = shift;
197 return $$self->{child_nodes} && $$self->{child_nodes}->[0]
198 ? $$self->{child_nodes}->[-1] : undef;
199 } # last_child
200
201 sub local_name { undef }
202
203 sub manakai_local_name { undef }
204
205 sub namespace_uri { undef }
206
207 sub next_sibling ($) {
208 my $self = shift;
209 my $parent = $$self->{parent_node};
210 return undef unless defined $parent;
211 my $has_self;
212 for (@{$parent->child_nodes}) {
213 if ($_ eq $self) {
214 $has_self = 1;
215 } elsif ($has_self) {
216 return $_;
217 }
218 }
219 return undef;
220 } # next_sibling
221
222 ## NOTE: Overridden by subclasses.
223 sub node_name () { undef }
224
225 ## NOTE: Overridden by subclasses.
226 sub node_type () { }
227
228 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
229 ## |CharacterData|, and |ProcessingInstruction|.
230 sub node_value () { undef }
231
232 sub owner_document ($);
233
234 sub manakai_parent_element ($) {
235 my $self = shift;
236 my $parent = $$self->{parent_node};
237 while (defined $parent) {
238 if ($parent->node_type == ELEMENT_NODE) {
239 return $parent;
240 } else {
241 $parent = $$parent->{parent_node};
242 }
243 }
244 return undef;
245 } # manakai_parent_element
246
247 sub parent_node ($);
248
249 ## NOTE: Overridden by |Element| and |Attr|.
250 sub prefix ($;$) { undef }
251
252 sub previous_sibling ($) {
253 my $self = shift;
254 my $parent = $$self->{parent_node};
255 return undef unless defined $parent;
256 my $prev;
257 for (@{$parent->child_nodes}) {
258 if ($_ eq $self) {
259 return $prev;
260 } else {
261 $prev = $_;
262 }
263 }
264 return undef;
265 } # previous_sibling
266
267 sub manakai_read_only ($);
268
269 sub text_content ($;$) {
270 ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
271 ## |DocumentFragment|, and |AttributeDefinition|. In addition,
272 ## |Document|'s |text_content| might call this attribute.
273
274 ## NOTE: Overridden by |Document|, |DocumentType|, |Notation|,
275 ## |CharacterData|, |ProcessingInstruction|, and |ElementTypeDefinition|.
276
277 my $self = $_[0];
278
279 if (@_ > 1) {
280 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
281 $$self->{manakai_read_only}) {
282 report Message::DOM::DOMException
283 -object => $self,
284 -type => 'NO_MODIFICATION_ALLOWED_ERR',
285 -subtype => 'READ_ONLY_NODE_ERR';
286 }
287
288 local $Error::Depth = $Error::Depth + 1;
289 @{$self->child_nodes} = ();
290 if (defined $_[1] and length $_[1]) {
291 ## NOTE: |DocumentType| don't use this code.
292 my $text = ($$self->{owner_document} || $self)->create_text_node ($_[1]);
293 $self->append_child ($text);
294 }
295 }
296
297 if (defined wantarray) {
298 local $Error::Depth = $Error::Depth + 1;
299 my $r = '';
300 my @node = @{$self->child_nodes};
301 while (@node) {
302 my $child = shift @node;
303 my $child_nt = $child->node_type;
304 if ($child_nt == TEXT_NODE or $child_nt == CDATA_SECTION_NODE) {
305 $r .= $child->node_value unless $child->is_element_content_whitespace;
306 } elsif ($child_nt == COMMENT_NODE or
307 $child_nt == PROCESSING_INSTRUCTION_NODE or
308 $child_nt == DOCUMENT_TYPE_NODE) {
309 #
310 } else {
311 unshift @node, @{$child->child_nodes};
312 }
313 }
314 return $r;
315 }
316 } # text_content
317
318 ## |Node| methods
319
320 sub append_child ($$) {
321 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
322 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
323 ## |Notation|, |ProcessingInstruction| |ElementTypeDefinition|,
324 ## and |DocumentType| define their own implementations.
325 my $self = $_[0];
326
327 ## NOTE: Depends on $self->node_type:
328 my $self_od = $$self->{owner_document};
329
330 ## -- Node Type check
331 my @new_child;
332 my $new_child_parent;
333 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
334 push @new_child, @{$_[1]->child_nodes};
335 $new_child_parent = $_[1];
336 } else {
337 @new_child = ($_[1]);
338 $new_child_parent = $_[1]->parent_node;
339 }
340
341 ## NOTE: Depends on $self->node_type:
342 if ($$self_od->{strict_error_checking}) {
343 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
344 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
345 report Message::DOM::DOMException
346 -object => $self,
347 -type => 'WRONG_DOCUMENT_ERR',
348 -subtype => 'EXTERNAL_OBJECT_ERR';
349 }
350
351 if ($$self->{manakai_read_only} or
352 (@new_child and defined $new_child_parent and
353 $$new_child_parent->{manakai_read_only})) {
354 report Message::DOM::DOMException
355 -object => $self,
356 -type => 'NO_MODIFICATION_ALLOWED_ERR',
357 -subtype => 'READ_ONLY_NODE_ERR';
358 }
359
360 ## NOTE: |Document| has children order check here.
361
362 for my $cn (@new_child) {
363 unless ({
364 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
365 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
366 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
367 }->{$cn->node_type}) {
368 report Message::DOM::DOMException
369 -object => $self,
370 -type => 'HIERARCHY_REQUEST_ERR',
371 -subtype => 'CHILD_NODE_TYPE_ERR';
372 }
373 }
374
375 my $anode = $self;
376 while (defined $anode) {
377 if ($anode eq $_[1]) {
378 report Message::DOM::DOMException
379 -object => $self,
380 -type => 'HIERARCHY_REQUEST_ERR',
381 -subtype => 'ANCESTOR_NODE_ERR';
382 }
383 $anode = $$anode->{parent_node};
384 }
385 }
386
387 ## NOTE: "Insert at" code only in insert_before and replace_child
388
389 ## -- Removes from parent
390 if ($new_child_parent) {
391 if (@new_child == 1) {
392 my $v = $$new_child_parent->{child_nodes};
393 RP: for my $i (0..$#$v) {
394 if ($v->[$i] eq $new_child[0]) {
395 splice @$v, $i, 1, ();
396 last RP;
397 }
398 } # RP
399 } else {
400 @{$$new_child_parent->{child_nodes}} = ();
401 }
402 }
403
404 ## -- Rewrite the |parentNode| properties
405 for my $nc (@new_child) {
406 $$nc->{parent_node} = $self;
407 Scalar::Util::weaken ($$nc->{parent_node});
408 }
409
410 ## NOTE: Depends on method:
411 push @{$$self->{child_nodes}}, @new_child;
412
413 ## NOTE: Setting |owner_document| in |Document|.
414
415 return $_[1];
416 } # apepnd_child
417
418 sub clone_node ($;$) {
419 my ($self, $deep) = @_;
420
421 ## ISSUE: Need definitions for the cloning operation
422 ## for ElementTypeDefinition, and AttributeDefinition nodes,
423 ## as well as new attributes introduced in DOM XML Document Type Definition
424 ## module.
425 ## ISSUE: Define if default attributes and attributedefinition are inconsistent
426
427 local $Error::Depth = $Error::Depth + 1;
428 my $od = $self->owner_document;
429 my $strict_check = $od->strict_error_checking;
430 $od->strict_error_checking (0);
431 my $cfg = $od->dom_config;
432 my $er_copy_asis
433 = $cfg->get_parameter
434 (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
435
436 my $r;
437 my @udh;
438 my @node = ([$self]);
439 while (@node) {
440 my ($node, $parent) = @{shift @node};
441 my $nt = $node->node_type;
442 my $clone;
443 if ($nt == ELEMENT_NODE) {
444 $clone = $od->create_element_ns
445 ($node->namespace_uri, [$node->prefix, $node->local_name]);
446 if ($parent) {
447 $parent->append_child ($clone);
448 } else {
449 $r = $clone;
450 }
451 my $attrs = $node->attributes;
452 my $attrsMax = @$attrs - 1;
453 for my $i (0..$attrsMax) {
454 my $attr = $attrs->[$i];
455 push @node, [$attr, $clone] if $attr->specified;
456 }
457 if ($deep) {
458 push @node, map {[$_, $clone]} @{$node->child_nodes};
459 }
460 } elsif ($nt == TEXT_NODE) {
461 $clone = $od->create_text_node ($node->data);
462 if ($parent) {
463 $parent->append_child ($clone);
464 } else {
465 $r = $clone;
466 }
467 $clone->is_element_content_whitespace (1)
468 if $node->is_element_content_whitespace;
469 } elsif ($nt == ATTRIBUTE_NODE) {
470 $clone = $od->create_attribute_ns
471 ($node->namespace_uri, [$node->prefix, $node->local_name]);
472 if ($parent) {
473 $parent->set_attribute_node_ns ($clone);
474 } else {
475 $r = $clone;
476 }
477 $clone->specified (1);
478 push @node, map {[$_, $clone]} @{$node->child_nodes};
479 } elsif ($nt == COMMENT_NODE) {
480 $clone = $od->create_comment ($node->data);
481 if ($parent) {
482 $parent->append_child ($clone);
483 } else {
484 $r = $clone;
485 }
486 } elsif ($nt == CDATA_SECTION_NODE) {
487 $clone = $od->create_cdata_section ($node->data);
488 if ($parent) {
489 $parent->append_child ($clone);
490 } else {
491 $r = $clone;
492 }
493 } elsif ($nt == PROCESSING_INSTRUCTION_NODE) {
494 $clone = $od->create_processing_instruction
495 ($node->target, $node->data);
496 if ($parent) {
497 $parent->append_child ($clone);
498 } else {
499 $r = $clone;
500 }
501 } elsif ($nt == ENTITY_REFERENCE_NODE) {
502 $clone = $od->create_entity_reference ($node->node_name);
503 if ($er_copy_asis) {
504 $clone->manakai_set_read_only (0);
505 $clone->text_content (0);
506 for (@{$node->child_nodes}) {
507 $clone->append_child ($_->clone_node (1));
508 }
509 $clone->manakai_expanded ($node->manakai_expanded);
510 $clone->manakai_set_read_only (1, 1);
511 } # copy asis
512 if ($parent) {
513 $parent->append_child ($clone);
514 } else {
515 $r = $clone;
516 }
517 } elsif ($nt == DOCUMENT_FRAGMENT_NODE) {
518 $clone = $od->create_document_fragment;
519 $r = $clone;
520 push @node, map {[$_, $clone]} @{$node->child_nodes};
521 } elsif ($nt == DOCUMENT_NODE) {
522 $od->strict_error_checking ($strict_check);
523 report Message::DOM::DOMException
524 -object => $self,
525 -type => 'NOT_SUPPORTED_ERR',
526 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
527 } elsif ($nt == DOCUMENT_TYPE_NODE) {
528 $od->strict_error_checking ($strict_check);
529 report Message::DOM::DOMException
530 -object => $self,
531 -type => 'NOT_SUPPORTED_ERR',
532 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
533 } elsif ($nt == ENTITY_NODE) {
534 $od->strict_error_checking ($strict_check);
535 report Message::DOM::DOMException
536 -object => $self,
537 -type => 'NOT_SUPPORTED_ERR',
538 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
539 } elsif ($nt == NOTATION_NODE) {
540 $od->strict_error_checking ($strict_check);
541 report Message::DOM::DOMException
542 -object => $self,
543 -type => 'NOT_SUPPORTED_ERR',
544 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
545 } else {
546 $od->strict_error_checking ($strict_check);
547 report Message::DOM::DOMException
548 -object => $self,
549 -type => 'NOT_SUPPORTED_ERR',
550 -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
551 }
552
553 my $udhs = $$self->{user_data};
554 push @udh, [$node => $clone, $udhs] if $udhs and %$udhs;
555 } # @node
556 $od->strict_error_checking (1) if $strict_check;
557
558 ## Calling user data handlers if any
559 for my $sd (@udh) {
560 my $src = $sd->[0];
561 my $src_ud = $sd->[2];
562 for my $key (keys %{$src_ud}) {
563 my $dh = $src_ud->{$key}->[1];
564 if ($dh) { ## NODE_CLONED
565 $dh->handle (1, $key, $src_ud->{$key}->[0], $src, $sd->[1]);
566 ## ISSUE: |handler| method? CODE?
567 }
568 }
569 }
570
571 return $r;
572 } # clone_node
573
574 sub compare_document_position ($$) {
575 ## ISSUE: There are implementation specifics
576 ## (see what Gecko does if it implement this method...)
577
578 ## ISSUE: Maybe we should overload <=> or cmp
579
580 ## TODO: Too long method name! Too long constant names!
581 ## Too many thing to be done by a method!
582 ## Maybe we should import simpler method implemented by IE.
583
584 ## ISSUE: Need documentation for ElementTypeDefinition and AttributeDefinition
585 ## concerns
586
587 my @acontainer = ($_[0]);
588 my @bcontainer = ($_[1]);
589 F: {
590 A: while (1) {
591 if ($acontainer[-1] eq $bcontainer[-1]) {
592 last F;
593 } else {
594 my $ap;
595 my $atype = $acontainer[-1]->node_type;
596 if ($atype == ATTRIBUTE_NODE) {
597 $ap = $acontainer[-1]->owner_element;
598 } elsif ($atype == ENTITY_NODE or $atype == NOTATION_NODE or
599 $atype == ELEMENT_TYPE_DEFINITION_NODE) {
600 $ap = $acontainer[-1]->owner_document_type_definition;
601 } elsif ($atype == ATTRIBUTE_DEFINITION_NODE) {
602 $ap = $acontainer[-1]->owner_element_type_definition;
603 } else {
604 $ap = $acontainer[-1]->parent_node;
605 }
606 if (defined $ap) {
607 push @acontainer, $ap;
608 } else {
609 last A;
610 }
611 }
612 } # A
613
614 B: while (1) {
615 if ($acontainer[-1] eq $bcontainer[-1]) {
616 last F;
617 } else {
618 my $bp;
619 my $btype = $bcontainer[-1]->node_type;
620 if ($btype == ATTRIBUTE_NODE) {
621 $bp = $bcontainer[-1]->owner_element;
622 } elsif ($btype == ENTITY_NODE or $btype == NOTATION_NODE or
623 $btype == ELEMENT_TYPE_DEFINITION_NODE) {
624 $bp = $bcontainer[-1]->owner_document_type_definition;
625 } elsif ($btype == ATTRIBUTE_DEFINITION_NODE) {
626 $bp = $bcontainer[-1]->owner_element_type_definition;
627 } else {
628 $bp = $bcontainer[-1]->parent_node;
629 }
630 if (defined $bp) {
631 push @bcontainer, $bp;
632 } else {
633 last B;
634 }
635 }
636 } # B
637
638 ## Disconnected
639 if ($bcontainer[-1]->isa ('Message::IF::Node')) {
640 ## ISSUE: Document this in manakai's DOM Perl Binding?
641 return DOCUMENT_POSITION_DISCONNECTED
642 | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
643 | ((${$acontainer[-1]} cmp ${$bcontainer[-1]}) > 0
644 ? DOCUMENT_POSITION_FOLLOWING
645 : DOCUMENT_POSITION_PRECEDING);
646 } else {
647 ## TODO: Is there test cases for this?
648 return DOCUMENT_POSITION_DISCONNECTED
649 | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
650 | DOCUMENT_POSITION_FOLLOWING;
651 }
652 } # F
653
654 ## Common container found
655 if (@acontainer >= 2) {
656 if (@bcontainer >= 2) {
657 my $acnt = $acontainer[-2]->node_type;
658 my $bcnt = $bcontainer[-2]->node_type;
659 if ($acnt == ATTRIBUTE_NODE or
660 $acnt == NOTATION_NODE or
661 $acnt == ELEMENT_TYPE_DEFINITION_NODE or
662 $acnt == ATTRIBUTE_DEFINITION_NODE) {
663 if ($acnt == $bcnt) {
664 return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
665 | (($acontainer[-2]->node_name cmp
666 $bcontainer[-2]->node_name) > 0
667 ? DOCUMENT_POSITION_FOLLOWING
668 : DOCUMENT_POSITION_PRECEDING);
669 } elsif ($bcnt == ATTRIBUTE_NODE or
670 $bcnt == NOTATION_NODE or
671 $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
672 $bcnt == ATTRIBUTE_DEFINITION_NODE) {
673 return (($acnt < $bcnt)
674 ? DOCUMENT_POSITION_FOLLOWING
675 : DOCUMENT_POSITION_PRECEDING);
676 } else {
677 ## A: Non-child and B: child
678 return DOCUMENT_POSITION_FOLLOWING;
679 }
680 } elsif ($bcnt == ATTRIBUTE_NODE or
681 $bcnt == NOTATION_NODE or
682 $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
683 $bcnt == ATTRIBUTE_DEFINITION_NODE) {
684 ## A: Child and B: non-child
685 return DOCUMENT_POSITION_PRECEDING;
686 } else {
687 ## A and B are both children
688 for my $cn (@{$acontainer[-1]->child_nodes}) {
689 if ($cn eq $acontainer[-2]) {
690 return DOCUMENT_POSITION_FOLLOWING;
691 } elsif ($cn eq $bcontainer[-2]) {
692 return DOCUMENT_POSITION_PRECEDING;
693 }
694 }
695 die "compare_document_position: Something wrong (1)";
696 }
697 } else {
698 ## B contains A
699 return DOCUMENT_POSITION_CONTAINS
700 | DOCUMENT_POSITION_PRECEDING;
701 }
702 } else {
703 if (@bcontainer >= 2) {
704 ## A contains B
705 return DOCUMENT_POSITION_CONTAINED_BY
706 | DOCUMENT_POSITION_FOLLOWING;
707 } else {
708 ## A eq B
709 return 0;
710 }
711 }
712 die "compare_document_position: Something wrong (2)";
713 } # compare_document_position
714
715 sub get_feature ($$;$) {
716 my $feature = lc $_[1]; ## TODO: |lc|?
717 $feature =~ s/^\+//;
718 my $version = defined $_[2] ? $_[2] : '';
719 if ($Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version}) {
720 return $_[0];
721 } else {
722 return undef;
723 }
724 } # get_feature
725
726 sub get_user_data ($$) {
727 if (${$_[0]}->{user_data}->{$_[1]}) {
728 return ${$_[0]}->{user_data}->{$_[1]}->[0];
729 } else {
730 return undef;
731 }
732 } # get_user_data
733
734 sub has_attributes ($) {
735 for (values %{${$_[0]}->{attributes} or {}}) {
736 return 1 if keys %$_;
737 }
738 return 0;
739 } # has_attributes
740
741 sub has_child_nodes ($) {
742 return (@{${$_[0]}->{child_nodes} or []} > 0);
743 } # has_child_nodes
744
745 sub insert_before ($$) {
746 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
747 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
748 ## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|,
749 ## and |DocumentType| define their own implementations.
750 my $self = $_[0];
751
752 ## NOTE: Depends on $self->node_type:
753 my $self_od = $$self->{owner_document};
754
755 ## -- Node Type check
756 my @new_child;
757 my $new_child_parent;
758 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
759 push @new_child, @{$_[1]->child_nodes};
760 $new_child_parent = $_[1];
761 } else {
762 @new_child = ($_[1]);
763 $new_child_parent = $_[1]->parent_node;
764 }
765
766 ## NOTE: Depends on $self->node_type:
767 if ($$self_od->{strict_error_checking}) {
768 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
769 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
770 report Message::DOM::DOMException
771 -object => $self,
772 -type => 'WRONG_DOCUMENT_ERR',
773 -subtype => 'EXTERNAL_OBJECT_ERR';
774 }
775
776 if ($$self->{manakai_read_only} or
777 (@new_child and defined $new_child_parent and
778 $$new_child_parent->{manakai_read_only})) {
779 report Message::DOM::DOMException
780 -object => $self,
781 -type => 'NO_MODIFICATION_ALLOWED_ERR',
782 -subtype => 'READ_ONLY_NODE_ERR';
783 }
784
785 ## NOTE: |Document| has children order check here.
786
787 for my $cn (@new_child) {
788 unless ({
789 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
790 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
791 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
792 }->{$cn->node_type}) {
793 report Message::DOM::DOMException
794 -object => $self,
795 -type => 'HIERARCHY_REQUEST_ERR',
796 -subtype => 'CHILD_NODE_TYPE_ERR';
797 }
798 }
799
800 my $anode = $self;
801 while (defined $anode) {
802 if ($anode eq $_[1]) {
803 report Message::DOM::DOMException
804 -object => $self,
805 -type => 'HIERARCHY_REQUEST_ERR',
806 -subtype => 'ANCESTOR_NODE_ERR';
807 }
808 $anode = $$anode->{parent_node};
809 }
810 }
811
812 ## -- Insert at... ## NOTE: Only in insert_before and replace_child
813 my $index = -1; # last
814 if (defined $_[2]) {
815 ## error if $_[1] eq $_[2];
816
817 my $cns = $self->child_nodes;
818 my $cnsl = @$cns;
819 C: {
820 $index = 0;
821 for my $i (0..($cnsl-1)) {
822 my $cn = $cns->[$i];
823 if ($cn eq $_[2]) {
824 $index += $i;
825 last C;
826 } elsif ($cn eq $_[1]) {
827 $index = -1; # offset
828 }
829 }
830
831 report Message::DOM::DOMException
832 -object => $self,
833 -type => 'NOT_FOUND_ERR',
834 -subtype => 'NOT_CHILD_ERR';
835 } # C
836 }
837 ## NOTE: "else" only in replace_child
838
839 ## -- Removes from parent
840 if ($new_child_parent) {
841 if (@new_child == 1) {
842 my $v = $$new_child_parent->{child_nodes};
843 RP: for my $i (0..$#$v) {
844 if ($v->[$i] eq $new_child[0]) {
845 splice @$v, $i, 1, ();
846 last RP;
847 }
848 } # RP
849 } else {
850 @{$$new_child_parent->{child_nodes}} = ();
851 }
852 }
853
854 ## -- Rewrite the |parentNode| properties
855 for my $nc (@new_child) {
856 $$nc->{parent_node} = $self;
857 Scalar::Util::weaken ($$nc->{parent_node});
858 }
859
860 ## NOTE: Depends on method:
861 if ($index == -1) {
862 push @{$$self->{child_nodes}}, @new_child;
863 } else {
864 splice @{$$self->{child_nodes}}, $index, 0, @new_child;
865 }
866
867 ## NOTE: Setting |owner_document| in |Document|.
868
869 return $_[1];
870 } # insert_before
871
872 sub is_equal_node ($$) {
873 local $Error::Depth = $Error::Depth + 1;
874
875 return 0 unless UNIVERSAL::isa ($_[1], 'Message::IF::Node');
876
877 my $nt = $_[0]->node_type;
878 return 0 unless $nt == $_[1]->node_type;
879
880 my @str_attr = qw/node_name local_name namespace_uri
881 prefix node_value/;
882 push @str_attr, qw/public_id system_id internal_subset/
883 if $nt == DOCUMENT_TYPE_NODE;
884 for my $attr_name (@str_attr) {
885 my $v1 = $_[0]->can ($attr_name) ? $_[0]->$attr_name : undef;
886 my $v2 = $_[1]->can ($attr_name) ? $_[1]->$attr_name : undef;
887 if (defined $v1 and defined $v2) {
888 return 0 unless ''.$v1 eq ''.$v2;
889 } elsif (defined $v1 or defined $v2) {
890 return 0;
891 }
892 }
893
894 my @num_eq_attr = qw/child_nodes attributes/;
895 push @num_eq_attr, qw/entities notations element_types/
896 if $nt == DOCUMENT_TYPE_NODE;
897 push @num_eq_attr, qw/attribute_definitions/
898 if $nt == ELEMENT_TYPE_DEFINITION_NODE;
899 push @num_eq_attr, qw/declared_type default_type allowed_tokens/
900 if $nt == ATTRIBUTE_DEFINITION_NODE;
901 for my $attr_name (@num_eq_attr) {
902 my $v1 = $_[0]->can ($attr_name) ? $_[0]->$attr_name : undef;
903 my $v2 = $_[1]->can ($attr_name) ? $_[1]->$attr_name : undef;
904 if (defined $v1 and defined $v2) {
905 return 0 unless $v1 == $v2;
906 } elsif (defined $v1 or defined $v2) {
907 return 0;
908 }
909 }
910
911 return 1;
912 } # is_equal_node
913
914 sub is_same_node ($$) { $_[0] eq $_[1] }
915
916 sub is_supported ($$;$) {
917 my $feature = lc $_[1]; ## TODO: |lc|?
918 my $plus = ($feature =~ s/^\+//);
919 my $version = defined $_[2] ? $_[2] : '';
920 return $Message::DOM::DOMImplementation::HasFeature->{$feature}->{$version};
921 } # is_supported;
922
923 sub manakai_append_text ($$) {
924 ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
925 ## |DocumentFragment|, and |AttributeDefinition|. In addition,
926 ## |Document|'s |text_content| might call this attribute.
927
928 ## NOTE: Overridden by |Document|, |DocumentType|, |CharacterData|,
929 ## |ElementTypeDefinition|, |Notation|, and |ProcessingInstruction|.
930
931 my $self = $_[0];
932 local $Error::Depth = $Error::Depth + 1;
933 if (@{$$self->{child_nodes}} and
934 $$self->{child_nodes}->[-1]->node_type == TEXT_NODE) {
935 $$self->{child_nodes}->[-1]->manakai_append_text ($_[1]);
936 } else {
937 my $text = ($$self->{owner_document} or $self)->create_text_node ($_[1]);
938 $self->append_child ($text);
939 }
940 } # manakai_append_text
941
942 sub is_default_namespace ($$) {
943 ## TODO: Document that ElementTypeDefinition and AttributeDefinition
944 ## are same as DocumentType
945
946 local $Error::Depth = $Error::Depth + 1;
947 my $namespace_uri = defined $_[1] ? $_[1] : '';
948 my $nt = $_[0]->node_type;
949 if ($nt == ELEMENT_NODE) {
950 my $el = $_[0];
951 EL: {
952 unless (defined $el->prefix) {
953 my $elns = $el->namespace_uri;
954 if ($namespace_uri ne '' and defined $elns) {
955 return $namespace_uri eq $elns;
956 } else {
957 return not ($namespace_uri eq '' or defined $elns);
958 }
959 }
960 my $xmlns = $el->get_attribute_ns
961 ('http://www.w3.org/2000/xmlns/', 'xmlns');
962 if (defined $xmlns) {
963 if ($namespace_uri ne '') {
964 return ($namespace_uri eq $xmlns);
965 } else {
966 return ($xmlns eq '');
967 }
968 }
969 $el = $el->manakai_parent_element;
970 redo EL if defined $el;
971 return 0;
972 } # EL;
973 } else {
974 my $el = $nt == DOCUMENT_NODE
975 ? $_[0]->document_element
976 : $nt == ATTRIBUTE_NODE
977 ? $_[0]->owner_element
978 : $_[0]->manakai_parent_element;
979 if (defined $el) {
980 return $el->is_default_namespace ($_[1]);
981 } else {
982 return 0;
983 }
984 }
985 } # is_default_namespace
986
987 sub lookup_namespace_uri ($$) {
988 ## TODO: Need definition for ElementTypeDefinition and AttributeDefinition
989
990 my ($self, $prefix) = @_;
991 $prefix = undef if defined $prefix and $prefix eq '';
992 ## NOTE: Implementation dependent.
993 ## TODO: Check what Gecko does.
994 local $Error::Depth = $Error::Depth + 1;
995 my $nt = $self->node_type;
996 if ($nt == ELEMENT_NODE) {
997 my $el = $self;
998 EL: {
999 my $elns = $el->namespace_uri;
1000 if (defined $elns) {
1001 my $elpfx = $el->prefix;
1002 if ((not defined $prefix and not defined $elpfx) or
1003 (defined $prefix and defined $elpfx and $prefix eq $elpfx)) {
1004 return $elns;
1005 }
1006 }
1007 AT: for my $attr (@{$el->attributes}) {
1008 my $attrns = $attr->namespace_uri;
1009 next AT if not defined $attrns or
1010 $attrns ne 'http://www.w3.org/2000/xmlns/';
1011 my $attrpfx = $attr->prefix;
1012 if (not defined $prefix) {
1013 my $attrln = $attr->local_name;
1014 if ($attrln eq 'xmlns') {
1015 my $attrval = $attr->value;
1016 return length $attrval ? $attrval : undef;
1017 }
1018 } elsif (defined $prefix and
1019 defined $attrpfx and $attrpfx eq 'xmlns') {
1020 my $attrln = $attr->local_name;
1021 if ($attrln eq $prefix) {
1022 my $attrval = $attr->value;
1023 return length $attrval ? $attrval : undef;
1024 }
1025 }
1026 } # AT
1027 $el = $el->manakai_parent_element;
1028 redo EL if defined $el;
1029 return undef;
1030 } # EL;
1031 } else {
1032 my $el = $nt == DOCUMENT_NODE
1033 ? $self->document_element
1034 : $nt == ATTRIBUTE_NODE
1035 ? $self->owner_element
1036 : $self->manakai_parent_element;
1037 if (defined $el) {
1038 return $el->lookup_namespace_uri ($prefix);
1039 } else {
1040 return undef;
1041 }
1042 }
1043 } # lookup_namespace_uri
1044
1045 sub lookup_prefix ($$) {
1046 ## ISSUE: Document ElementTypeDefinition and AttributeDefinition
1047 ## behavior (i.e. same as DocumentType)
1048
1049 my $namespace_uri = defined $_[1] ? $_[1] : '';
1050 if ($namespace_uri eq '') {
1051 return undef;
1052 }
1053
1054 local $Error::Depth = $Error::Depth + 1;
1055 my $nt = $_[0]->node_type;
1056 if ($nt == ELEMENT_NODE) {
1057 my $el = $_[0];
1058 EL: {
1059 my $elns = $el->namespace_uri;
1060 if (defined $elns and $elns eq $namespace_uri) {
1061 my $elpfx = $el->prefix;
1062 if (defined $elpfx) {
1063 my $oeluri = $_[0]->lookup_namespace_uri ($elpfx);
1064 if (defined $oeluri and $oeluri eq $namespace_uri) {
1065 return $elpfx;
1066 }
1067 }
1068 }
1069 AT: for my $attr (@{$el->attributes}) {
1070 my $attrpfx = $attr->prefix;
1071 next AT if not defined $attrpfx or $attrpfx ne 'xmlns';
1072 my $attrns = $attr->namespace_uri;
1073 next AT if not defined $attrns or
1074 $attrns ne 'http://www.w3.org/2000/xmlns/';
1075 next AT unless $attr->value eq $namespace_uri;
1076 my $attrln = $attr->local_name;
1077 my $oeluri = $el->lookup_namespace_uri ($attrln);
1078 next AT unless defined $oeluri;
1079 if ($oeluri eq $namespace_uri) {
1080 return $attrln;
1081 }
1082 }
1083 $el = $el->manakai_parent_element;
1084 redo EL if defined $el;
1085 return undef;
1086 } # EL
1087 } else {
1088 my $el = $nt == DOCUMENT_NODE
1089 ? $_[0]->document_element
1090 : $nt == ATTRIBUTE_NODE
1091 ? $_[0]->owner_element
1092 : $_[0]->manakai_parent_element;
1093 if (defined $el) {
1094 return $el->lookup_prefix ($_[1]);
1095 } else {
1096 return undef;
1097 }
1098 }
1099 } # lookup_prefix
1100
1101 sub normalize ($) {
1102 my $self = shift;
1103 my $ptext;
1104 local $Error::Depth = $Error::Depth + 1;
1105
1106 ## Children
1107 my @remove;
1108 for my $cn (@{$self->child_nodes}) {
1109 if ($cn->node_type == TEXT_NODE) {
1110 my $nv = $cn->node_value;
1111 if (length $nv) {
1112 if (defined $ptext) {
1113 $ptext->manakai_append_text ($nv);
1114 $ptext->is_element_content_whitespace (1)
1115 if $cn->is_element_content_whitespace and
1116 $ptext->is_element_content_whitespace;
1117 push @remove, $cn;
1118 } else {
1119 $ptext = $cn;
1120 }
1121 } else {
1122 push @remove, $cn;
1123 }
1124 } else {
1125 $cn->normalize;
1126 undef $ptext;
1127 }
1128 }
1129 $self->remove_child ($_) for @remove;
1130
1131 my $nt = $self->node_type;
1132 if ($nt == ELEMENT_NODE) {
1133 ## Attributes
1134 $_->normalize for @{$self->attributes};
1135 } elsif ($nt == DOCUMENT_TYPE_NODE) {
1136 ## ISSUE: Document these explicitly in DOM XML Document Type Definitions spec
1137 ## Element type definitions
1138 $_->normalize for @{$self->element_types};
1139 ## General entities
1140 $_->normalize for @{$self->general_entities};
1141 } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
1142 ## Attribute definitions
1143 $_->normalize for @{$self->attribute_definitions};
1144 }
1145 ## TODO: normalize-characters
1146
1147 ## TODO: In this implementation, if a modification raises a
1148 ## |NO_MODIFICATION_ALLOWED_ERR|, then any modification before it
1149 ## is not reverted.
1150 } # normalize
1151
1152 sub remove_child ($$) {
1153 my ($self, $old_child) = @_;
1154
1155 if ($$self->{manakai_read_only} and
1156 ${$$self->{owner_document} or $self}->{strict_error_checking}) {
1157 report Message::DOM::DOMException
1158 -object => $self,
1159 -type => 'NO_MODIFICATION_ALLOWED_ERR',
1160 -subtype => 'READ_ONLY_NODE_ERR';
1161 }
1162
1163 my $parent_list = $$self->{child_nodes} || [];
1164 for (0..$#$parent_list) {
1165 if ($parent_list->[$_] eq $old_child) {
1166 splice @$parent_list, $_, 1, ();
1167 delete $$old_child->{parent_node};
1168 return $old_child;
1169 }
1170 }
1171
1172 report Message::DOM::DOMException
1173 -object => $self,
1174 -type => 'NOT_FOUND_ERR',
1175 -subtype => 'NOT_CHILD_ERR';
1176 } # remove_child
1177
1178 sub replace_child ($$) {
1179 ## NOTE: |Element|, |Entity|, |DocumentFragment|, |EntityReference|.
1180 ## NOTE: |Document|, |Attr|, |CharacterData|, |AttributeDefinition|,
1181 ## |Notation|, |ProcessingInstruction|, |ElementTypeDefinition|,
1182 ## and |DocumentType| define their own implementations.
1183 my $self = $_[0];
1184
1185 ## NOTE: Depends on $self->node_type:
1186 my $self_od = $$self->{owner_document};
1187
1188 ## -- Node Type check
1189 my @new_child;
1190 my $new_child_parent;
1191 if ($_[1]->node_type == DOCUMENT_FRAGMENT_NODE) {
1192 push @new_child, @{$_[1]->child_nodes};
1193 $new_child_parent = $_[1];
1194 } else {
1195 @new_child = ($_[1]);
1196 $new_child_parent = $_[1]->parent_node;
1197 }
1198
1199 ## NOTE: Depends on $self->node_type:
1200 if ($$self_od->{strict_error_checking}) {
1201 my $child_od = $_[1]->owner_document || $_[1]; # might be DocumentType
1202 if ($self_od ne $child_od and $child_od->node_type != DOCUMENT_TYPE_NODE) {
1203 report Message::DOM::DOMException
1204 -object => $self,
1205 -type => 'WRONG_DOCUMENT_ERR',
1206 -subtype => 'EXTERNAL_OBJECT_ERR';
1207 }
1208
1209 if ($$self->{manakai_read_only} or
1210 (@new_child and defined $new_child_parent and
1211 $$new_child_parent->{manakai_read_only})) {
1212 report Message::DOM::DOMException
1213 -object => $self,
1214 -type => 'NO_MODIFICATION_ALLOWED_ERR',
1215 -subtype => 'READ_ONLY_NODE_ERR';
1216 }
1217
1218 ## NOTE: |Document| has children order check here.
1219
1220 for my $cn (@new_child) {
1221 unless ({
1222 TEXT_NODE, 1, ENTITY_REFERENCE_NODE, 1,
1223 ELEMENT_NODE, 1, CDATA_SECTION_NODE, 1,
1224 PROCESSING_INSTRUCTION_NODE, 1, COMMENT_NODE, 1,
1225 }->{$cn->node_type}) {
1226 report Message::DOM::DOMException
1227 -object => $self,
1228 -type => 'HIERARCHY_REQUEST_ERR',
1229 -subtype => 'CHILD_NODE_TYPE_ERR';
1230 }
1231 }
1232
1233 my $anode = $self;
1234 while (defined $anode) {
1235 if ($anode eq $_[1]) {
1236 report Message::DOM::DOMException
1237 -object => $self,
1238 -type => 'HIERARCHY_REQUEST_ERR',
1239 -subtype => 'ANCESTOR_NODE_ERR';
1240 }
1241 $anode = $$anode->{parent_node};
1242 }
1243 }
1244
1245 ## -- Insert at... ## NOTE: Only in insertBefore and replaceChild
1246 my $index = -1; # last
1247 if (defined $_[2]) {
1248 ## error if $_[1] eq $_[2];
1249
1250 my $cns = $self->child_nodes;
1251 my $cnsl = @$cns;
1252 C: {
1253 $index = 0;
1254 for my $i (0..($cnsl-1)) {
1255 my $cn = $cns->[$i];
1256 if ($cn eq $_[2]) {
1257 $index += $i;
1258 last C;
1259 } elsif ($cn eq $_[1]) {
1260 $index = -1; # offset
1261 }
1262 }
1263
1264 report Message::DOM::DOMException
1265 -object => $self,
1266 -type => 'NOT_FOUND_ERR',
1267 -subtype => 'NOT_CHILD_ERR';
1268 } # C
1269 } else {
1270 ## NOTE: Only in replaceChild
1271 report Message::DOM::DOMException
1272 -object => $self,
1273 -type => 'NOT_FOUND_ERR',
1274 -subtype => 'NOT_CHILD_ERR';
1275 }
1276
1277 ## -- Removes from parent
1278 if ($new_child_parent) {
1279 if (@new_child == 1) {
1280 my $v = $$new_child_parent->{child_nodes};
1281 RP: for my $i (0..$#$v) {
1282 if ($v->[$i] eq $new_child[0]) {
1283 splice @$v, $i, 1, ();
1284 last RP;
1285 }
1286 } # RP
1287 } else {
1288 @{$$new_child_parent->{child_nodes}} = ();
1289 }
1290 }
1291
1292 ## -- Rewrite the |parentNode| properties
1293 for my $nc (@new_child) {
1294 $$nc->{parent_node} = $self;
1295 Scalar::Util::weaken ($$nc->{parent_node});
1296 }
1297
1298 ## NOTE: Depends on method:
1299 splice @{$$self->{child_nodes}}, $index, 1, @new_child;
1300 delete ${$_[2]}->{parent_node};
1301
1302 ## NOTE: Setting |owner_document| in |Document|.
1303
1304 return $_[2];
1305 } # replace_child
1306
1307 sub manakai_set_read_only ($;$$) {
1308 my $value = 1 if $_[1];
1309 if ($_[2]) {
1310 my @target = ($_[0]);
1311 while (@target) {
1312 my $target = shift @target;
1313 if ($value) {
1314 $$target->{manakai_read_only} = 1;
1315 } else {
1316 delete $$target->{manakai_read_only};
1317 }
1318 push @target, @{$target->child_nodes};
1319
1320 my $nt = $target->node_type;
1321 if ($nt == ELEMENT_NODE) {
1322 push @target, @{$target->attributes};
1323 } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
1324 push @target, @{$target->attribute_definitions};
1325 } elsif ($nt == DOCUMENT_TYPE_NODE) {
1326 push @target, @{$target->element_types};
1327 push @target, @{$target->general_entities};
1328 push @target, @{$target->notations};
1329 }
1330 }
1331 } else { # not deep
1332 if ($value) {
1333 ${$_[0]}->{manakai_read_only} = 1;
1334 } else {
1335 delete ${$_[0]}->{manakai_read_only};
1336 }
1337 }
1338 } # manakai_set_read_only
1339
1340 # {NOTE:: Perl application developers are advised to be careful
1341 # to include direct or indirect references to the node
1342 # itself as user data or in user data handlers.
1343 # They would result in memory leak problems unless
1344 # the circular references are removed later.
1345 #
1346 # It would be a good practive to eusure that every user data
1347 # registered to a node is later unregistered by setting
1348 # <DOM::null> as a data for the same key.
1349 #
1350 sub set_user_data ($$$;$) {
1351 my ($self, $key, $data, $handler) = @_;
1352
1353 my $v = ($$self->{user_data} ||= {});
1354 my $r = $v->{$key}->[0];
1355
1356 if (defined $data) {
1357 $v->{$key} = [$data, $handler];
1358
1359 if (defined $handler) {
1360 eval q{
1361 no warnings;
1362 sub DESTROY {
1363 my $uds = ${$_[0]}->{user_data};
1364 for my $key (keys %$uds) {
1365 if (defined $uds->{$key}->[1]) {
1366 local $Error::Depth = $Error::Depth + 1;
1367 $uds->{$key}->[1]->(3, $key, $uds->{$key}->[0]); # NODE_DELETED
1368 }
1369 }
1370 }
1371 };
1372 }
1373 } else {
1374 delete $v->{$key};
1375 }
1376 return $r;
1377 } # set_user_data
1378
1379 package Message::IF::Node;
1380
1381 =head1 LICENSE
1382
1383 Copyright 2007 Wakaba <w@suika.fam.cx>
1384
1385 This program is free software; you can redistribute it and/or
1386 modify it under the same terms as Perl itself.
1387
1388 =cut
1389
1390 1;
1391 ## $Date: 2007/07/08 13:04:37 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24