/[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.17 - (show annotations) (download)
Mon Oct 8 07:17:18 2007 UTC (18 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.16: +11 -2 lines
++ manakai/lib/Message/DOM/ChangeLog	8 Oct 2007 07:16:56 -0000
2007-10-08  Wakaba  <wakaba@suika.fam.cx>

	* Event.pm, EventTarget.pm, EventTargetNode.pm,
	EventException.pm: Implemented (but not tested!).

	* DOMException.pm (MALFORMED_EVENT_TYPE_ERR,
	EVENT_INTERFACE_NOT_SUPPORTED_ERR, EXTERNAL_EVENT_ERR): New
	error subtypes.

	* DOMImplementation.pm ($HasFeature): The |Event| feature,
	version |3.0|, is added.

	* Document.pm, Node.pm: Event attributes and
	methods are implemented.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24