/[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.12 - (show annotations) (download)
Sun Jul 8 05:42:37 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.11: +375 -55 lines
++ manakai/t/ChangeLog	8 Jul 2007 05:42:31 -0000
2007-07-08  Wakaba  <wakaba@suika.fam.cx>

	* DOM-Document.t, DOM-Node.t, DOM-NodeList.t: Some tests are modified so
	that no |WRONG_DOCUMENT_ERR| is raised.

	* DOM-Node.t: Tests for |remove_child| are added.

++ manakai/lib/Message/DOM/ChangeLog	8 Jul 2007 05:41:27 -0000
2007-07-08  Wakaba  <wakaba@suika.fam.cx>

	* Attr.pm, AttributeDefinition.pm, DOMCharacterData.pm,
	DOMDocument.pm, DocumentType.pm, ElementTypeDefinition.pm,
	Node.pm, Notation.pm, ProcessingInstruction.pm (append_child,
	insert_before, replace_child): Implemented.

	* DOMException.pm (HIERARCHY_REQUEST_ERR, NOT_FOUND_ERR): Implemented.

	* Node.pm (remove_child): Implemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24