/[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 - (hide 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 wakaba 1.1 package Message::DOM::Node;
2     use strict;
3 wakaba 1.13 our $VERSION=do{my @r=(q$Revision: 1.12 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 wakaba 1.1 push our @ISA, 'Message::IF::Node';
5     require Scalar::Util;
6 wakaba 1.6 require Message::DOM::DOMException;
7 wakaba 1.1
8 wakaba 1.4 ## 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 wakaba 1.11 '==' => 'is_equal_node',
28 wakaba 1.4 '!=' => sub {
29     return not ($_[0] == $_[1]);
30     },
31 wakaba 1.11 #eq => sub { $_[0] eq $_[1] }, ## is_same_node
32     #ne => sub { $_[0] ne $_[1] }, ## not is_same_node
33 wakaba 1.4 fallback => 1;
34    
35 wakaba 1.2 ## 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 wakaba 1.1 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 wakaba 1.6 sub ___report_error ($$) {
73     $_[1]->throw;
74     } # ___report_error
75    
76 wakaba 1.1 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 wakaba 1.4 manakai_read_only => 1,
86 wakaba 1.1 }->{$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 wakaba 1.6 ## |Node| attributes
101 wakaba 1.1
102 wakaba 1.6 ## NOTE: Overridden by |Element|.
103     sub attributes () { undef }
104 wakaba 1.2
105 wakaba 1.7 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 wakaba 1.3
113     sub child_nodes ($) {
114 wakaba 1.6 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
115     ## |Notation|, and |ProcessingInstruction|.
116 wakaba 1.4 require Message::DOM::NodeList;
117     return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
118 wakaba 1.3 } # child_nodes
119    
120 wakaba 1.8 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 wakaba 1.3 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 wakaba 1.6 sub local_name { undef }
148 wakaba 1.3
149 wakaba 1.6 sub manakai_local_name { undef }
150    
151     sub namespace_uri { undef }
152 wakaba 1.3
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 wakaba 1.2
168 wakaba 1.6 ## NOTE: Overridden by subclasses.
169     sub node_name () { undef }
170 wakaba 1.2
171 wakaba 1.6 ## NOTE: Overridden by subclasses.
172     sub node_type () { }
173 wakaba 1.2
174 wakaba 1.6 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
175     ## |CharacterData|, and |ProcessingInstruction|.
176     sub node_value () { undef }
177 wakaba 1.3
178     sub owner_document ($);
179    
180 wakaba 1.8 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 wakaba 1.3 sub parent_node ($);
194    
195 wakaba 1.6 ## NOTE: Overridden by |Element| and |Attr|.
196 wakaba 1.3 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 wakaba 1.4 sub manakai_read_only ($);
214    
215 wakaba 1.3 sub text_content ($;$) {
216 wakaba 1.6 ## 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 wakaba 1.7 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
227     $$self->{manakai_read_only}) {
228 wakaba 1.6 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 wakaba 1.3 } # text_content
263    
264 wakaba 1.7 ## |Node| methods
265    
266 wakaba 1.12 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 wakaba 1.8 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 wakaba 1.10 my $er_copy_asis
379     = $cfg->get_parameter
380     (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
381 wakaba 1.8
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 wakaba 1.10 $clone->is_element_content_whitespace (1)
414     if $node->is_element_content_whitespace;
415 wakaba 1.8 } 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 wakaba 1.11 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 wakaba 1.8 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 wakaba 1.12 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 wakaba 1.11 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 wakaba 1.4
826 wakaba 1.11 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 wakaba 1.1 } # is_equal_node
859    
860 wakaba 1.11 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 wakaba 1.7 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 wakaba 1.8 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 wakaba 1.1 sub remove_child ($$) {
1099     my ($self, $old_child) = @_;
1100 wakaba 1.12
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 wakaba 1.1 for (0..$#$parent_list) {
1111     if ($parent_list->[$_] eq $old_child) {
1112 wakaba 1.12 splice @$parent_list, $_, 1, ();
1113     delete $$old_child->{parent_node};
1114     return $old_child;
1115 wakaba 1.1 }
1116     }
1117 wakaba 1.12
1118     report Message::DOM::DOMException
1119     -object => $self,
1120     -type => 'NOT_FOUND_ERR',
1121     -subtype => 'NOT_CHILD_ERR';
1122 wakaba 1.1 } # remove_child
1123    
1124 wakaba 1.12 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 wakaba 1.4 sub manakai_set_read_only ($;$$) {
1254 wakaba 1.7 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 wakaba 1.4 } # manakai_set_read_only
1285    
1286 wakaba 1.11 # {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 wakaba 1.9 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 wakaba 1.11 eval q{
1307 wakaba 1.12 no warnings;
1308 wakaba 1.11 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 wakaba 1.9 }
1316     }
1317     };
1318     }
1319     } else {
1320     delete $v->{$key};
1321     }
1322     return $r;
1323     } # set_user_data
1324    
1325 wakaba 1.4 package Message::IF::Node;
1326    
1327     =head1 LICENSE
1328 wakaba 1.1
1329 wakaba 1.4 Copyright 2007 Wakaba <w@suika.fam.cx>
1330 wakaba 1.1
1331 wakaba 1.4 This program is free software; you can redistribute it and/or
1332     modify it under the same terms as Perl itself.
1333 wakaba 1.1
1334 wakaba 1.4 =cut
1335 wakaba 1.1
1336     1;
1337 wakaba 1.13 ## $Date: 2007/07/08 05:42:37 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24