/[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.13 - (show annotations) (download)
Sun Jul 8 13:04:37 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.12: +2 -18 lines
++ manakai/t/ChangeLog	8 Jul 2007 13:03:58 -0000
	* DOM-Element.t: Tests for |attributes| are added.

	* DOM-Entity.t: Tests for |is_externally_declared|
	and |input_encoding| are added.

	* DOM-Node.t: Test data for |tag_name| and
	|get_feature| are added.

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

++ manakai/lib/Message/DOM/ChangeLog	8 Jul 2007 13:02:01 -0000
	* Attr.pm (value, node_value): Now it is defined
	as |text_content| itself.

	* AttributeDefinition.pm, ElementTypeDefinition.pm,
	Node.pm (AUTOLOAD): Unused block is removed.

	* CDATASection.pm, DocumentFragment.pm (AUTOLOAD): Removed.  Unused.

	* DocumentType.pm (internal_subset): Implemented.

	* Entity.pm (is_externally_declared, input_encoding,
	xml_version): Implemented.

	* ProcessingInstruction.pm (target, data): Implemented.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24