/[suikacvs]/messaging/manakai/lib/Message/DOM/Node.pm
Suika

Contents of /messaging/manakai/lib/Message/DOM/Node.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

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

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

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24