/[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.14 - (hide annotations) (download)
Sat Jul 14 09:19:11 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.13: +56 -2 lines
++ manakai/t/ChangeLog	14 Jul 2007 09:19:01 -0000
	* DOM-Node.t: Test data for new constants and attributes
	are added.

	* DOM-TypeInfo.t: Tests for constants are added.

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

++ manakai/lib/Message/DOM/ChangeLog	14 Jul 2007 09:17:51 -0000
	* AttributeDefinition.pm (node_value): Implemented.
	(create_attribute_definition): Implemented.

	* DOMConfiguration.pm (%{}, TIEHASH,
	get_parameter, set_parameter, can_set_parameter,
	EXISTS, DELETE, parameter_names, FETCH, STORE,
	FIRSTKEY, LASTKEY): Implemented.

	* DOMDocument.pm (____new): Set |error-handler| default.
	(get_elements_by_tag_name, get_elements_by_tag_name_ns): Implemented.

	* DOMElement.pm (get_elements_by_tag_name, get_elements_by_tag_name_ns):
	Implemented.

	* DOMException.pm: Error types for |DOMConfiguration|
	are added.

	* DOMStringList.pm (Message::DOM::DOMStringList::StaticList): New
	class.

	* DocumentType.pm (get_element_type_definition_node,
	get_general_entity_node, get_notation_node,
	set_element_type_definition_node, set_general_entity_node,
	set_notation_node, create_document_type_definition): Implemented.

	* ElementTypeDefinition.pm (get_attribute_definition_node,
	set_attribute_definition_node, create_element_type_definition):
	Implemented.

	* Entity.pm (create_general_entity): Implemented.

	* Node.pm: Constants in |OperationType| definition
	group are added.
	(manakai_language): Implemented.

	* NodeList.pm (Message::DOM::NodeList::GetElementsList): New
	class.

	* Notation.pm (create_notation): Implemented.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24