/[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.10 - (hide annotations) (download)
Sat Jul 7 11:11:34 2007 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.9: +7 -5 lines
++ manakai/t/ChangeLog	7 Jul 2007 11:11:27 -0000
	* DOM-Element.t: New tests for |create_element|
	and |create_element_ns| are added.

	* DOM-EntityReference.t: New tests for |create_entity_reference|
	are added.

	* DOM-Node.t: Test data for |is_element_content_whitespace|
	are added.

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

++ manakai/lib/Message/DOM/ChangeLog	7 Jul 2007 11:10:33 -0000
	* CDATASection.pm (is_element_content_whitespace): New.

	* DOMElement.pm (has_attribute): Alpha version.
	(create_element, create_element_ns): Implemented.

	* DocumentType.pm (get_general_entity_node): Alpha version.

	* EntityReference.pm (create_entity_reference): Implemented.

	* ProcessingInstruction.pm (create_processing_instruction): Implemented.

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

1 wakaba 1.1 package Message::DOM::Node;
2     use strict;
3 wakaba 1.10 our $VERSION=do{my @r=(q$Revision: 1.9 $=~/\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     '==' => sub {
28     return 0 unless UNIVERSAL::isa ($_[0], 'Message::IF::Node');
29     ## TODO: implement is_equal_node
30     return $_[0]->is_equal_node ($_[1]);
31     },
32     '!=' => sub {
33     return not ($_[0] == $_[1]);
34     },
35     fallback => 1;
36    
37 wakaba 1.2 ## The |Node| interface - constants
38    
39     ## Definition group NodeType
40    
41     ## NOTE: Numeric codes up to 200 are reserved by W3C [DOM1SE, DOM2, DOM3].
42    
43     sub ELEMENT_NODE () { 1 }
44     sub ATTRIBUTE_NODE () { 2 }
45     sub TEXT_NODE () { 3 }
46     sub CDATA_SECTION_NODE () { 4 }
47     sub ENTITY_REFERENCE_NODE () { 5 }
48     sub ENTITY_NODE () { 6 }
49     sub PROCESSING_INSTRUCTION_NODE () { 7 }
50     sub COMMENT_NODE () { 8 }
51     sub DOCUMENT_NODE () { 9 }
52     sub DOCUMENT_TYPE_NODE () { 10 }
53     sub DOCUMENT_FRAGMENT_NODE () { 11 }
54     sub NOTATION_NODE () { 12 }
55     sub ELEMENT_TYPE_DEFINITION_NODE () { 81001 }
56     sub ATTRIBUTE_DEFINITION_NODE () { 81002 }
57    
58     ## Definition group DocumentPosition
59    
60     ## Spec:
61     ## <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#DocumentPosition>
62    
63     sub DOCUMENT_POSITION_DISCONNECTED () { 0x01 }
64     sub DOCUMENT_POSITION_PRECEDING () { 0x02 }
65     sub DOCUMENT_POSITION_FOLLOWING () { 0x04 }
66     sub DOCUMENT_POSITION_CONTAINS () { 0x08 }
67     sub DOCUMENT_POSITION_CONTAINED_BY () { 0x10 }
68     sub DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC () { 0x20 }
69    
70 wakaba 1.1 sub ____new ($$) {
71     my $self = bless \({}), shift;
72     $$self->{owner_document} = shift;
73     Scalar::Util::weaken ($$self->{owner_document});
74     return $self;
75     } # ____new
76    
77 wakaba 1.6 sub ___report_error ($$) {
78     $_[1]->throw;
79     } # ___report_error
80    
81 wakaba 1.1 sub AUTOLOAD {
82     my $method_name = our $AUTOLOAD;
83     $method_name =~ s/.*:://;
84     return if $method_name eq 'DESTROY';
85    
86     if ({
87     ## Read-only attributes (trivial accessors)
88     owner_document => 1,
89     parent_node => 1,
90 wakaba 1.4 manakai_read_only => 1,
91 wakaba 1.1 }->{$method_name}) {
92     no strict 'refs';
93     eval qq{
94     sub $method_name (\$) {
95     return \${\$_[0]}->{$method_name};
96     }
97     };
98     goto &{ $AUTOLOAD };
99     } elsif ({
100     ## Read-write attributes (DOMString, trivial accessors)
101     }->{$method_name}) {
102     no strict 'refs';
103     eval qq{
104 wakaba 1.2 sub $method_name (\$;\$) {
105 wakaba 1.1 if (\@_ > 1) {
106 wakaba 1.2 \${\$_[0]}->{$method_name} = ''.\$_[1];
107 wakaba 1.1 }
108     return \${\$_[0]}->{$method_name};
109     }
110     };
111     goto &{ $AUTOLOAD };
112     } else {
113     require Carp;
114     Carp::croak (qq<Can't locate method "$AUTOLOAD">);
115     }
116     } # AUTOLOAD
117    
118 wakaba 1.6 ## |Node| attributes
119 wakaba 1.1
120 wakaba 1.6 ## NOTE: Overridden by |Element|.
121     sub attributes () { undef }
122 wakaba 1.2
123 wakaba 1.7 sub base_uri ($) {
124     ## NOTE: Overridden by |Attr|, |CharacterData|, |Document|, |DocumentType|,
125     ## |Element|, |EntityReference|, and |ProcessingInstruction|.
126    
127     local $Error::Depth = $Error::Depth + 1;
128     return $_[0]->owner_document->base_uri;
129     } # base_uri
130 wakaba 1.3
131     sub child_nodes ($) {
132 wakaba 1.6 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
133     ## |Notation|, and |ProcessingInstruction|.
134 wakaba 1.4 require Message::DOM::NodeList;
135     return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
136 wakaba 1.3 } # child_nodes
137    
138 wakaba 1.8 sub manakai_expanded_uri ($) {
139     my $self = shift;
140     local $Error::Depth = $Error::Depth + 1;
141     my $ln = $self->local_name;
142     if (defined $ln) {
143     my $nsuri = $self->namespace_uri;
144     if (defined $nsuri) {
145     return $nsuri . $ln;
146     } else {
147     return $ln;
148     }
149     } else {
150     return undef;
151     }
152     } # manakai_expanded_uri
153    
154 wakaba 1.3 sub first_child ($) {
155     my $self = shift;
156     return $$self->{child_nodes} ? $$self->{child_nodes}->[0] : undef;
157     } # first_child
158    
159     sub last_child ($) {
160     my $self = shift;
161     return $$self->{child_nodes} && $$self->{child_nodes}->[0]
162     ? $$self->{child_nodes}->[-1] : undef;
163     } # last_child
164    
165 wakaba 1.6 sub local_name { undef }
166 wakaba 1.3
167 wakaba 1.6 sub manakai_local_name { undef }
168    
169     sub namespace_uri { undef }
170 wakaba 1.3
171     sub next_sibling ($) {
172     my $self = shift;
173     my $parent = $$self->{parent_node};
174     return undef unless defined $parent;
175     my $has_self;
176     for (@{$parent->child_nodes}) {
177     if ($_ eq $self) {
178     $has_self = 1;
179     } elsif ($has_self) {
180     return $_;
181     }
182     }
183     return undef;
184     } # next_sibling
185 wakaba 1.2
186 wakaba 1.6 ## NOTE: Overridden by subclasses.
187     sub node_name () { undef }
188 wakaba 1.2
189 wakaba 1.6 ## NOTE: Overridden by subclasses.
190     sub node_type () { }
191 wakaba 1.2
192 wakaba 1.6 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
193     ## |CharacterData|, and |ProcessingInstruction|.
194     sub node_value () { undef }
195 wakaba 1.3
196     sub owner_document ($);
197    
198 wakaba 1.8 sub manakai_parent_element ($) {
199     my $self = shift;
200     my $parent = $$self->{parent_node};
201     while (defined $parent) {
202     if ($parent->node_type == ELEMENT_NODE) {
203     return $parent;
204     } else {
205     $parent = $$parent->{parent_node};
206     }
207     }
208     return undef;
209     } # manakai_parent_element
210    
211 wakaba 1.3 sub parent_node ($);
212    
213 wakaba 1.6 ## NOTE: Overridden by |Element| and |Attr|.
214 wakaba 1.3 sub prefix ($;$) { undef }
215    
216     sub previous_sibling ($) {
217     my $self = shift;
218     my $parent = $$self->{parent_node};
219     return undef unless defined $parent;
220     my $prev;
221     for (@{$parent->child_nodes}) {
222     if ($_ eq $self) {
223     return $prev;
224     } else {
225     $prev = $_;
226     }
227     }
228     return undef;
229     } # previous_sibling
230    
231 wakaba 1.4 sub manakai_read_only ($);
232    
233 wakaba 1.3 sub text_content ($;$) {
234 wakaba 1.6 ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
235     ## |DocumentFragment|, and |AttributeDefinition|. In addition,
236     ## |Document|'s |text_content| might call this attribute.
237    
238     ## NOTE: Overridden by |Document|, |DocumentType|, |Notation|,
239     ## |CharacterData|, |ProcessingInstruction|, and |ElementTypeDefinition|.
240    
241     my $self = $_[0];
242    
243     if (@_ > 1) {
244 wakaba 1.7 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
245     $$self->{manakai_read_only}) {
246 wakaba 1.6 report Message::DOM::DOMException
247     -object => $self,
248     -type => 'NO_MODIFICATION_ALLOWED_ERR',
249     -subtype => 'READ_ONLY_NODE_ERR';
250     }
251    
252     local $Error::Depth = $Error::Depth + 1;
253     @{$self->child_nodes} = ();
254     if (defined $_[1] and length $_[1]) {
255     ## NOTE: |DocumentType| don't use this code.
256     my $text = ($$self->{owner_document} || $self)->create_text_node ($_[1]);
257     $self->append_child ($text);
258     }
259     }
260    
261     if (defined wantarray) {
262     local $Error::Depth = $Error::Depth + 1;
263     my $r = '';
264     my @node = @{$self->child_nodes};
265     while (@node) {
266     my $child = shift @node;
267     my $child_nt = $child->node_type;
268     if ($child_nt == TEXT_NODE or $child_nt == CDATA_SECTION_NODE) {
269     $r .= $child->node_value unless $child->is_element_content_whitespace;
270     } elsif ($child_nt == COMMENT_NODE or
271     $child_nt == PROCESSING_INSTRUCTION_NODE or
272     $child_nt == DOCUMENT_TYPE_NODE) {
273     #
274     } else {
275     unshift @node, @{$child->child_nodes};
276     }
277     }
278     return $r;
279     }
280 wakaba 1.3 } # text_content
281    
282 wakaba 1.7 ## |Node| methods
283    
284 wakaba 1.8 sub clone_node ($;$) {
285     my ($self, $deep) = @_;
286    
287     ## ISSUE: Need definitions for the cloning operation
288     ## for ElementTypeDefinition, and AttributeDefinition nodes,
289     ## as well as new attributes introduced in DOM XML Document Type Definition
290     ## module.
291     ## ISSUE: Define if default attributes and attributedefinition are inconsistent
292    
293     local $Error::Depth = $Error::Depth + 1;
294     my $od = $self->owner_document;
295     my $strict_check = $od->strict_error_checking;
296     $od->strict_error_checking (0);
297     my $cfg = $od->dom_config;
298 wakaba 1.10 my $er_copy_asis
299     = $cfg->get_parameter
300     (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
301 wakaba 1.8
302     my $r;
303     my @udh;
304     my @node = ([$self]);
305     while (@node) {
306     my ($node, $parent) = @{shift @node};
307     my $nt = $node->node_type;
308     my $clone;
309     if ($nt == ELEMENT_NODE) {
310     $clone = $od->create_element_ns
311     ($node->namespace_uri, [$node->prefix, $node->local_name]);
312     if ($parent) {
313     $parent->append_child ($clone);
314     } else {
315     $r = $clone;
316     }
317     my $attrs = $node->attributes;
318     my $attrsMax = @$attrs - 1;
319     for my $i (0..$attrsMax) {
320     my $attr = $attrs->[$i];
321     push @node, [$attr, $clone] if $attr->specified;
322     }
323     if ($deep) {
324     push @node, map {[$_, $clone]} @{$node->child_nodes};
325     }
326     } elsif ($nt == TEXT_NODE) {
327     $clone = $od->create_text_node ($node->data);
328     if ($parent) {
329     $parent->append_child ($clone);
330     } else {
331     $r = $clone;
332     }
333 wakaba 1.10 $clone->is_element_content_whitespace (1)
334     if $node->is_element_content_whitespace;
335 wakaba 1.8 } elsif ($nt == ATTRIBUTE_NODE) {
336     $clone = $od->create_attribute_ns
337     ($node->namespace_uri, [$node->prefix, $node->local_name]);
338     if ($parent) {
339     $parent->set_attribute_node_ns ($clone);
340     } else {
341     $r = $clone;
342     }
343     $clone->specified (1);
344     push @node, map {[$_, $clone]} @{$node->child_nodes};
345     } elsif ($nt == COMMENT_NODE) {
346     $clone = $od->create_comment ($node->data);
347     if ($parent) {
348     $parent->append_child ($clone);
349     } else {
350     $r = $clone;
351     }
352     } elsif ($nt == CDATA_SECTION_NODE) {
353     $clone = $od->create_cdata_section ($node->data);
354     if ($parent) {
355     $parent->append_child ($clone);
356     } else {
357     $r = $clone;
358     }
359     } elsif ($nt == PROCESSING_INSTRUCTION_NODE) {
360     $clone = $od->create_processing_instruction
361     ($node->target, $node->data);
362     if ($parent) {
363     $parent->append_child ($clone);
364     } else {
365     $r = $clone;
366     }
367     } elsif ($nt == ENTITY_REFERENCE_NODE) {
368     $clone = $od->create_entity_reference ($node->node_name);
369     if ($er_copy_asis) {
370     $clone->manakai_set_read_only (0);
371     $clone->text_content (0);
372     for (@{$node->child_nodes}) {
373     $clone->append_child ($_->clone_node (1));
374     }
375     $clone->manakai_expanded ($node->manakai_expanded);
376     $clone->manakai_set_read_only (1, 1);
377     } # copy asis
378     if ($parent) {
379     $parent->append_child ($clone);
380     } else {
381     $r = $clone;
382     }
383     } elsif ($nt == DOCUMENT_FRAGMENT_NODE) {
384     $clone = $od->create_document_fragment;
385     $r = $clone;
386     push @node, map {[$_, $clone]} @{$node->child_nodes};
387     } elsif ($nt == DOCUMENT_NODE) {
388     $od->strict_error_checking ($strict_check);
389     report Message::DOM::DOMException
390     -object => $self,
391     -type => 'NOT_SUPPORTED_ERR',
392     -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
393     } elsif ($nt == DOCUMENT_TYPE_NODE) {
394     $od->strict_error_checking ($strict_check);
395     report Message::DOM::DOMException
396     -object => $self,
397     -type => 'NOT_SUPPORTED_ERR',
398     -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
399     } elsif ($nt == ENTITY_NODE) {
400     $od->strict_error_checking ($strict_check);
401     report Message::DOM::DOMException
402     -object => $self,
403     -type => 'NOT_SUPPORTED_ERR',
404     -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
405     } elsif ($nt == NOTATION_NODE) {
406     $od->strict_error_checking ($strict_check);
407     report Message::DOM::DOMException
408     -object => $self,
409     -type => 'NOT_SUPPORTED_ERR',
410     -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
411     } else {
412     $od->strict_error_checking ($strict_check);
413     report Message::DOM::DOMException
414     -object => $self,
415     -type => 'NOT_SUPPORTED_ERR',
416     -subtype => 'CLONE_NODE_NOT_SUPPORTED_ERR';
417     }
418    
419     my $udhs = $$self->{user_data};
420     push @udh, [$node => $clone, $udhs] if $udhs and %$udhs;
421     } # @node
422     $od->strict_error_checking (1) if $strict_check;
423    
424     ## Calling user data handlers if any
425     for my $sd (@udh) {
426     my $src = $sd->[0];
427     my $src_ud = $sd->[2];
428     for my $key (keys %{$src_ud}) {
429     my $dh = $src_ud->{$key}->[1];
430     if ($dh) { ## NODE_CLONED
431     $dh->handle (1, $key, $src_ud->{$key}->[0], $src, $sd->[1]);
432     ## ISSUE: |handler| method? CODE?
433     }
434     }
435     }
436    
437     return $r;
438     } # clone_node
439    
440     sub compare_document_position ($$) {
441     ## ISSUE: There are implementation specifics
442     ## (see what Gecko does if it implement this method...)
443    
444     ## ISSUE: Maybe we should overload <=> or cmp
445    
446     ## TODO: Too long method name! Too long constant names!
447     ## Too many thing to be done by a method!
448     ## Maybe we should import simpler method implemented by IE.
449    
450     ## ISSUE: Need documentation for ElementTypeDefinition and AttributeDefinition
451     ## concerns
452    
453     my @acontainer = ($_[0]);
454     my @bcontainer = ($_[1]);
455     F: {
456     A: while (1) {
457     if ($acontainer[-1] eq $bcontainer[-1]) {
458     last F;
459     } else {
460     my $ap;
461     my $atype = $acontainer[-1]->node_type;
462     if ($atype == ATTRIBUTE_NODE) {
463     $ap = $acontainer[-1]->owner_element;
464     } elsif ($atype == ENTITY_NODE or $atype == NOTATION_NODE or
465     $atype == ELEMENT_TYPE_DEFINITION_NODE) {
466     $ap = $acontainer[-1]->owner_document_type_definition;
467     } elsif ($atype == ATTRIBUTE_DEFINITION_NODE) {
468     $ap = $acontainer[-1]->owner_element_type_definition;
469     } else {
470     $ap = $acontainer[-1]->parent_node;
471     }
472     if (defined $ap) {
473     push @acontainer, $ap;
474     } else {
475     last A;
476     }
477     }
478     } # A
479    
480     B: while (1) {
481     if ($acontainer[-1] eq $bcontainer[-1]) {
482     last F;
483     } else {
484     my $bp;
485     my $btype = $bcontainer[-1]->node_type;
486     if ($btype == ATTRIBUTE_NODE) {
487     $bp = $bcontainer[-1]->owner_element;
488     } elsif ($btype == ENTITY_NODE or $btype == NOTATION_NODE or
489     $btype == ELEMENT_TYPE_DEFINITION_NODE) {
490     $bp = $bcontainer[-1]->owner_document_type_definition;
491     } elsif ($btype == ATTRIBUTE_DEFINITION_NODE) {
492     $bp = $bcontainer[-1]->owner_element_type_definition;
493     } else {
494     $bp = $bcontainer[-1]->parent_node;
495     }
496     if (defined $bp) {
497     push @bcontainer, $bp;
498     } else {
499     last B;
500     }
501     }
502     } # B
503    
504     ## Disconnected
505     if ($bcontainer[-1]->isa ('Message::IF::Node')) {
506     ## ISSUE: Document this in manakai's DOM Perl Binding?
507     return DOCUMENT_POSITION_DISCONNECTED
508     | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
509     | ((${$acontainer[-1]} cmp ${$bcontainer[-1]}) > 0
510     ? DOCUMENT_POSITION_FOLLOWING
511     : DOCUMENT_POSITION_PRECEDING);
512     } else {
513     ## TODO: Is there test cases for this?
514     return DOCUMENT_POSITION_DISCONNECTED
515     | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
516     | DOCUMENT_POSITION_FOLLOWING;
517     }
518     } # F
519    
520     ## Common container found
521     if (@acontainer >= 2) {
522     if (@bcontainer >= 2) {
523     my $acnt = $acontainer[-2]->node_type;
524     my $bcnt = $bcontainer[-2]->node_type;
525     if ($acnt == ATTRIBUTE_NODE or
526     $acnt == NOTATION_NODE or
527     $acnt == ELEMENT_TYPE_DEFINITION_NODE or
528     $acnt == ATTRIBUTE_DEFINITION_NODE) {
529     if ($acnt == $bcnt) {
530     return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
531     | (($acontainer[-2]->node_name cmp
532     $bcontainer[-2]->node_name) > 0
533     ? DOCUMENT_POSITION_FOLLOWING
534     : DOCUMENT_POSITION_PRECEDING);
535     } elsif ($bcnt == ATTRIBUTE_NODE or
536     $bcnt == NOTATION_NODE or
537     $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
538     $bcnt == ATTRIBUTE_DEFINITION_NODE) {
539     return (($acnt < $bcnt)
540     ? DOCUMENT_POSITION_FOLLOWING
541     : DOCUMENT_POSITION_PRECEDING);
542     } else {
543     ## A: Non-child and B: child
544     return DOCUMENT_POSITION_FOLLOWING;
545     }
546     } elsif ($bcnt == ATTRIBUTE_NODE or
547     $bcnt == NOTATION_NODE or
548     $bcnt == ELEMENT_TYPE_DEFINITION_NODE or
549     $bcnt == ATTRIBUTE_DEFINITION_NODE) {
550     ## A: Child and B: non-child
551     return DOCUMENT_POSITION_PRECEDING;
552     } else {
553     ## A and B are both children
554     for my $cn (@{$acontainer[-1]->child_nodes}) {
555     if ($cn eq $acontainer[-2]) {
556     return DOCUMENT_POSITION_FOLLOWING;
557     } elsif ($cn eq $bcontainer[-2]) {
558     return DOCUMENT_POSITION_PRECEDING;
559     }
560     }
561     die "compare_document_position: Something wrong (1)";
562     }
563     } else {
564     ## B contains A
565     return DOCUMENT_POSITION_CONTAINS
566     | DOCUMENT_POSITION_PRECEDING;
567     }
568     } else {
569     if (@bcontainer >= 2) {
570     ## A contains B
571     return DOCUMENT_POSITION_CONTAINED_BY
572     | DOCUMENT_POSITION_FOLLOWING;
573     } else {
574     ## A eq B
575     return 0;
576     }
577     }
578     die "compare_document_position: Something wrong (2)";
579     } # compare_document_position
580    
581     sub has_attributes ($) {
582     for (values %{${$_[0]}->{attributes} or {}}) {
583     return 1 if keys %$_;
584     }
585     return 0;
586     } # has_attributes
587    
588     sub has_child_nodes ($) {
589     return (@{${$_[0]}->{child_nodes} or []} > 0);
590     } # has_child_nodes
591    
592 wakaba 1.4 ## TODO:
593     sub is_same_node ($$) {
594     return $_[0] eq $_[1];
595     } # is_same_node
596    
597     ## TODO:
598 wakaba 1.1 sub is_equal_node ($$) {
599 wakaba 1.4 return $_[0]->node_name eq $_[1]->node_name &&
600     $_[0]->node_value eq $_[1]->node_value;
601 wakaba 1.1 } # is_equal_node
602    
603     ## NOTE: Only applied to Elements and Documents
604     sub append_child ($$) {
605     my ($self, $new_child) = @_;
606     if (defined $$new_child->{parent_node}) {
607 wakaba 1.5 my $parent_list = ${$$new_child->{parent_node}}->{child_nodes};
608 wakaba 1.1 for (0..$#$parent_list) {
609     if ($parent_list->[$_] eq $new_child) {
610     splice @$parent_list, $_, 1;
611 wakaba 1.7 last;
612 wakaba 1.1 }
613     }
614     }
615     push @{$$self->{child_nodes}}, $new_child;
616     $$new_child->{parent_node} = $self;
617     Scalar::Util::weaken ($$new_child->{parent_node});
618 wakaba 1.7 ## TODO:
619     $$new_child->{owner_document} = $self if $self->node_type == DOCUMENT_NODE;
620 wakaba 1.1 return $new_child;
621     } # append_child
622    
623 wakaba 1.7 sub manakai_append_text ($$) {
624     ## NOTE: For |Element|, |Attr|, |Entity|, |EntityReference|,
625     ## |DocumentFragment|, and |AttributeDefinition|. In addition,
626     ## |Document|'s |text_content| might call this attribute.
627    
628     ## NOTE: Overridden by |Document|, |DocumentType|, |CharacterData|,
629     ## |ElementTypeDefinition|, |Notation|, and |ProcessingInstruction|.
630    
631     my $self = $_[0];
632     local $Error::Depth = $Error::Depth + 1;
633     if (@{$$self->{child_nodes}} and
634     $$self->{child_nodes}->[-1]->node_type == TEXT_NODE) {
635     $$self->{child_nodes}->[-1]->manakai_append_text ($_[1]);
636     } else {
637     my $text = ($$self->{owner_document} or $self)->create_text_node ($_[1]);
638     $self->append_child ($text);
639     }
640     } # manakai_append_text
641    
642     sub get_feature {
643     ## TODO:
644     return $_[0];
645     }
646    
647 wakaba 1.1 ## NOTE: Only applied to Elements and Documents
648     sub insert_before ($$;$) {
649     my ($self, $new_child, $ref_child) = @_;
650     if (defined $$new_child->{parent_node}) {
651 wakaba 1.5 my $parent_list = ${$$new_child->{parent_node}}->{child_nodes};
652 wakaba 1.1 for (0..$#$parent_list) {
653     if ($parent_list->[$_] eq $new_child) {
654     splice @$parent_list, $_, 1;
655 wakaba 1.7 last;
656 wakaba 1.1 }
657     }
658     }
659     my $i = @{$$self->{child_nodes}};
660     if (defined $ref_child) {
661     for (0..$#{$$self->{child_nodes}}) {
662     if ($$self->{child_nodes}->[$_] eq $ref_child) {
663     $i = $_;
664     last;
665     }
666     }
667     }
668     splice @{$$self->{child_nodes}}, $i, 0, $new_child;
669     $$new_child->{parent_node} = $self;
670     Scalar::Util::weaken ($$new_child->{parent_node});
671     return $new_child;
672     } # insert_before
673    
674 wakaba 1.8 sub is_default_namespace ($$) {
675     ## TODO: Document that ElementTypeDefinition and AttributeDefinition
676     ## are same as DocumentType
677    
678     local $Error::Depth = $Error::Depth + 1;
679     my $namespace_uri = defined $_[1] ? $_[1] : '';
680     my $nt = $_[0]->node_type;
681     if ($nt == ELEMENT_NODE) {
682     my $el = $_[0];
683     EL: {
684     unless (defined $el->prefix) {
685     my $elns = $el->namespace_uri;
686     if ($namespace_uri ne '' and defined $elns) {
687     return $namespace_uri eq $elns;
688     } else {
689     return not ($namespace_uri eq '' or defined $elns);
690     }
691     }
692     my $xmlns = $el->get_attribute_ns
693     ('http://www.w3.org/2000/xmlns/', 'xmlns');
694     if (defined $xmlns) {
695     if ($namespace_uri ne '') {
696     return ($namespace_uri eq $xmlns);
697     } else {
698     return ($xmlns eq '');
699     }
700     }
701     $el = $el->manakai_parent_element;
702     redo EL if defined $el;
703     return 0;
704     } # EL;
705     } else {
706     my $el = $nt == DOCUMENT_NODE
707     ? $_[0]->document_element
708     : $nt == ATTRIBUTE_NODE
709     ? $_[0]->owner_element
710     : $_[0]->manakai_parent_element;
711     if (defined $el) {
712     return $el->is_default_namespace ($_[1]);
713     } else {
714     return 0;
715     }
716     }
717     } # is_default_namespace
718    
719     sub lookup_namespace_uri ($$) {
720     ## TODO: Need definition for ElementTypeDefinition and AttributeDefinition
721    
722     my ($self, $prefix) = @_;
723     $prefix = undef if defined $prefix and $prefix eq '';
724     ## NOTE: Implementation dependent.
725     ## TODO: Check what Gecko does.
726     local $Error::Depth = $Error::Depth + 1;
727     my $nt = $self->node_type;
728     if ($nt == ELEMENT_NODE) {
729     my $el = $self;
730     EL: {
731     my $elns = $el->namespace_uri;
732     if (defined $elns) {
733     my $elpfx = $el->prefix;
734     if ((not defined $prefix and not defined $elpfx) or
735     (defined $prefix and defined $elpfx and $prefix eq $elpfx)) {
736     return $elns;
737     }
738     }
739     AT: for my $attr (@{$el->attributes}) {
740     my $attrns = $attr->namespace_uri;
741     next AT if not defined $attrns or
742     $attrns ne 'http://www.w3.org/2000/xmlns/';
743     my $attrpfx = $attr->prefix;
744     if (not defined $prefix) {
745     my $attrln = $attr->local_name;
746     if ($attrln eq 'xmlns') {
747     my $attrval = $attr->value;
748     return length $attrval ? $attrval : undef;
749     }
750     } elsif (defined $prefix and
751     defined $attrpfx and $attrpfx eq 'xmlns') {
752     my $attrln = $attr->local_name;
753     if ($attrln eq $prefix) {
754     my $attrval = $attr->value;
755     return length $attrval ? $attrval : undef;
756     }
757     }
758     } # AT
759     $el = $el->manakai_parent_element;
760     redo EL if defined $el;
761     return undef;
762     } # EL;
763     } else {
764     my $el = $nt == DOCUMENT_NODE
765     ? $self->document_element
766     : $nt == ATTRIBUTE_NODE
767     ? $self->owner_element
768     : $self->manakai_parent_element;
769     if (defined $el) {
770     return $el->lookup_namespace_uri ($prefix);
771     } else {
772     return undef;
773     }
774     }
775     } # lookup_namespace_uri
776    
777     sub lookup_prefix ($$) {
778     ## ISSUE: Document ElementTypeDefinition and AttributeDefinition
779     ## behavior (i.e. same as DocumentType)
780    
781     my $namespace_uri = defined $_[1] ? $_[1] : '';
782     if ($namespace_uri eq '') {
783     return undef;
784     }
785    
786     local $Error::Depth = $Error::Depth + 1;
787     my $nt = $_[0]->node_type;
788     if ($nt == ELEMENT_NODE) {
789     my $el = $_[0];
790     EL: {
791     my $elns = $el->namespace_uri;
792     if (defined $elns and $elns eq $namespace_uri) {
793     my $elpfx = $el->prefix;
794     if (defined $elpfx) {
795     my $oeluri = $_[0]->lookup_namespace_uri ($elpfx);
796     if (defined $oeluri and $oeluri eq $namespace_uri) {
797     return $elpfx;
798     }
799     }
800     }
801     AT: for my $attr (@{$el->attributes}) {
802     my $attrpfx = $attr->prefix;
803     next AT if not defined $attrpfx or $attrpfx ne 'xmlns';
804     my $attrns = $attr->namespace_uri;
805     next AT if not defined $attrns or
806     $attrns ne 'http://www.w3.org/2000/xmlns/';
807     next AT unless $attr->value eq $namespace_uri;
808     my $attrln = $attr->local_name;
809     my $oeluri = $el->lookup_namespace_uri ($attrln);
810     next AT unless defined $oeluri;
811     if ($oeluri eq $namespace_uri) {
812     return $attrln;
813     }
814     }
815     $el = $el->manakai_parent_element;
816     redo EL if defined $el;
817     return undef;
818     } # EL
819     } else {
820     my $el = $nt == DOCUMENT_NODE
821     ? $_[0]->document_element
822     : $nt == ATTRIBUTE_NODE
823     ? $_[0]->owner_element
824     : $_[0]->manakai_parent_element;
825     if (defined $el) {
826     return $el->lookup_prefix ($_[1]);
827     } else {
828     return undef;
829     }
830     }
831     } # lookup_prefix
832    
833     sub normalize ($) {
834     my $self = shift;
835     my $ptext;
836     local $Error::Depth = $Error::Depth + 1;
837    
838     ## Children
839     my @remove;
840     for my $cn (@{$self->child_nodes}) {
841     if ($cn->node_type == TEXT_NODE) {
842     my $nv = $cn->node_value;
843     if (length $nv) {
844     if (defined $ptext) {
845     $ptext->manakai_append_text ($nv);
846     $ptext->is_element_content_whitespace (1)
847     if $cn->is_element_content_whitespace and
848     $ptext->is_element_content_whitespace;
849     push @remove, $cn;
850     } else {
851     $ptext = $cn;
852     }
853     } else {
854     push @remove, $cn;
855     }
856     } else {
857     $cn->normalize;
858     undef $ptext;
859     }
860     }
861     $self->remove_child ($_) for @remove;
862    
863     my $nt = $self->node_type;
864     if ($nt == ELEMENT_NODE) {
865     ## Attributes
866     $_->normalize for @{$self->attributes};
867     } elsif ($nt == DOCUMENT_TYPE_NODE) {
868     ## ISSUE: Document these explicitly in DOM XML Document Type Definitions spec
869     ## Element type definitions
870     $_->normalize for @{$self->element_types};
871     ## General entities
872     $_->normalize for @{$self->general_entities};
873     } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
874     ## Attribute definitions
875     $_->normalize for @{$self->attribute_definitions};
876     }
877     ## TODO: normalize-characters
878    
879     ## TODO: In this implementation, if a modification raises a
880     ## |NO_MODIFICATION_ALLOWED_ERR|, then any modification before it
881     ## is not reverted.
882     } # normalize
883    
884 wakaba 1.1 ## NOTE: Only applied to Elements and Documents
885     sub remove_child ($$) {
886     my ($self, $old_child) = @_;
887     my $parent_list = $$self->{child_nodes};
888     for (0..$#$parent_list) {
889     if ($parent_list->[$_] eq $old_child) {
890     splice @$parent_list, $_, 1;
891 wakaba 1.7 last;
892 wakaba 1.1 }
893     }
894     delete $$old_child->{parent_node};
895     return $old_child;
896     } # remove_child
897    
898 wakaba 1.4 sub manakai_set_read_only ($;$$) {
899 wakaba 1.7 my $value = 1 if $_[1];
900     if ($_[2]) {
901     my @target = ($_[0]);
902     while (@target) {
903     my $target = shift @target;
904     if ($value) {
905     $$target->{manakai_read_only} = 1;
906     } else {
907     delete $$target->{manakai_read_only};
908     }
909     push @target, @{$target->child_nodes};
910    
911     my $nt = $target->node_type;
912     if ($nt == ELEMENT_NODE) {
913     push @target, @{$target->attributes};
914     } elsif ($nt == ELEMENT_TYPE_DEFINITION_NODE) {
915     push @target, @{$target->attribute_definitions};
916     } elsif ($nt == DOCUMENT_TYPE_NODE) {
917     push @target, @{$target->element_types};
918     push @target, @{$target->general_entities};
919     push @target, @{$target->notations};
920     }
921     }
922     } else { # not deep
923     if ($value) {
924     ${$_[0]}->{manakai_read_only} = 1;
925     } else {
926     delete ${$_[0]}->{manakai_read_only};
927     }
928     }
929 wakaba 1.4 } # manakai_set_read_only
930    
931 wakaba 1.9 sub set_user_data ($$$;$) {
932     my ($self, $key, $data, $handler) = @_;
933    
934     my $v = ($$self->{user_data} ||= {});
935     my $r = $v->{$key}->[0];
936    
937     if (defined $data) {
938     $v->{$key} = [$data, $handler];
939    
940     if (defined $handler) {
941     $$self->{manakai_onunload} = sub {
942     my $node = $_[0];
943     my $uds = $$node->{user_data};
944     for my $key (keys %$uds) {
945     if (defined $uds->{$key}->[1]) {
946     $uds->{$key}->[1]->(3, $key, $uds->{$key}->[0]); # NODE_DELETED
947     }
948     }
949     };
950     }
951     } else {
952     delete $v->{$key};
953     }
954     return $r;
955     } # set_user_data
956    
957 wakaba 1.4 package Message::IF::Node;
958    
959     =head1 LICENSE
960 wakaba 1.1
961 wakaba 1.4 Copyright 2007 Wakaba <w@suika.fam.cx>
962 wakaba 1.1
963 wakaba 1.4 This program is free software; you can redistribute it and/or
964     modify it under the same terms as Perl itself.
965 wakaba 1.1
966 wakaba 1.4 =cut
967 wakaba 1.1
968     1;
969 wakaba 1.10 ## $Date: 2007/07/07 07:36:58 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24