/[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 - (hide 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 wakaba 1.1 package Message::DOM::Node;
2     use strict;
3 wakaba 1.12 our $VERSION=do{my @r=(q$Revision: 1.11 $=~/\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     ## 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 wakaba 1.1 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 wakaba 1.6 sub ___report_error ($$) {
76     $_[1]->throw;
77     } # ___report_error
78    
79 wakaba 1.1 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 wakaba 1.4 manakai_read_only => 1,
89 wakaba 1.1 }->{$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 wakaba 1.2 sub $method_name (\$;\$) {
103 wakaba 1.1 if (\@_ > 1) {
104 wakaba 1.2 \${\$_[0]}->{$method_name} = ''.\$_[1];
105 wakaba 1.1 }
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 wakaba 1.6 ## |Node| attributes
117 wakaba 1.1
118 wakaba 1.6 ## NOTE: Overridden by |Element|.
119     sub attributes () { undef }
120 wakaba 1.2
121 wakaba 1.7 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 wakaba 1.3
129     sub child_nodes ($) {
130 wakaba 1.6 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
131     ## |Notation|, and |ProcessingInstruction|.
132 wakaba 1.4 require Message::DOM::NodeList;
133     return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
134 wakaba 1.3 } # child_nodes
135    
136 wakaba 1.8 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 wakaba 1.3 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 wakaba 1.6 sub local_name { undef }
164 wakaba 1.3
165 wakaba 1.6 sub manakai_local_name { undef }
166    
167     sub namespace_uri { undef }
168 wakaba 1.3
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 wakaba 1.2
184 wakaba 1.6 ## NOTE: Overridden by subclasses.
185     sub node_name () { undef }
186 wakaba 1.2
187 wakaba 1.6 ## NOTE: Overridden by subclasses.
188     sub node_type () { }
189 wakaba 1.2
190 wakaba 1.6 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
191     ## |CharacterData|, and |ProcessingInstruction|.
192     sub node_value () { undef }
193 wakaba 1.3
194     sub owner_document ($);
195    
196 wakaba 1.8 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 wakaba 1.3 sub parent_node ($);
210    
211 wakaba 1.6 ## NOTE: Overridden by |Element| and |Attr|.
212 wakaba 1.3 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 wakaba 1.4 sub manakai_read_only ($);
230    
231 wakaba 1.3 sub text_content ($;$) {
232 wakaba 1.6 ## 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 wakaba 1.7 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
243     $$self->{manakai_read_only}) {
244 wakaba 1.6 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 wakaba 1.3 } # text_content
279    
280 wakaba 1.7 ## |Node| methods
281    
282 wakaba 1.12 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 wakaba 1.8 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 wakaba 1.10 my $er_copy_asis
395     = $cfg->get_parameter
396     (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
397 wakaba 1.8
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 wakaba 1.10 $clone->is_element_content_whitespace (1)
430     if $node->is_element_content_whitespace;
431 wakaba 1.8 } 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 wakaba 1.11 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 wakaba 1.8 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 wakaba 1.12 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 wakaba 1.11 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 wakaba 1.4
842 wakaba 1.11 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 wakaba 1.1 } # is_equal_node
875    
876 wakaba 1.11 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 wakaba 1.7 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 wakaba 1.8 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 wakaba 1.1 sub remove_child ($$) {
1115     my ($self, $old_child) = @_;
1116 wakaba 1.12
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 wakaba 1.1 for (0..$#$parent_list) {
1127     if ($parent_list->[$_] eq $old_child) {
1128 wakaba 1.12 splice @$parent_list, $_, 1, ();
1129     delete $$old_child->{parent_node};
1130     return $old_child;
1131 wakaba 1.1 }
1132     }
1133 wakaba 1.12
1134     report Message::DOM::DOMException
1135     -object => $self,
1136     -type => 'NOT_FOUND_ERR',
1137     -subtype => 'NOT_CHILD_ERR';
1138 wakaba 1.1 } # remove_child
1139    
1140 wakaba 1.12 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 wakaba 1.4 sub manakai_set_read_only ($;$$) {
1270 wakaba 1.7 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 wakaba 1.4 } # manakai_set_read_only
1301    
1302 wakaba 1.11 # {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 wakaba 1.9 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 wakaba 1.11 eval q{
1323 wakaba 1.12 no warnings;
1324 wakaba 1.11 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 wakaba 1.9 }
1332     }
1333     };
1334     }
1335     } else {
1336     delete $v->{$key};
1337     }
1338     return $r;
1339     } # set_user_data
1340    
1341 wakaba 1.4 package Message::IF::Node;
1342    
1343     =head1 LICENSE
1344 wakaba 1.1
1345 wakaba 1.4 Copyright 2007 Wakaba <w@suika.fam.cx>
1346 wakaba 1.1
1347 wakaba 1.4 This program is free software; you can redistribute it and/or
1348     modify it under the same terms as Perl itself.
1349 wakaba 1.1
1350 wakaba 1.4 =cut
1351 wakaba 1.1
1352     1;
1353 wakaba 1.12 ## $Date: 2007/07/07 15:05:01 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24