/[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.15 - (show annotations) (download)
Sat Aug 25 08:41:00 2007 UTC (17 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.14: +21 -12 lines
++ manakai/lib/Message/DOM/ChangeLog	25 Aug 2007 08:40:23 -0000
	* Node.pm (manakai_language): Return the |manakai_language|
	of the |owner_document|, if any, as defined in the spec.

2007-08-25  Wakaba  <wakaba@suika.fam.cx>

++ manakai/t/ChangeLog	25 Aug 2007 07:45:25 -0000
2007-08-25  Wakaba  <wakaba@suika.fam.cx>

	* DOM-Node.t: New tests for |manakai_language| are
	added.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24