/[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 - (show 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 package Message::DOM::Node;
2 use strict;
3 our $VERSION=do{my @r=(q$Revision: 1.9 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4 push our @ISA, 'Message::IF::Node';
5 require Scalar::Util;
6 require Message::DOM::DOMException;
7
8 ## 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 ## 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 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 sub ___report_error ($$) {
78 $_[1]->throw;
79 } # ___report_error
80
81 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 manakai_read_only => 1,
91 }->{$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 sub $method_name (\$;\$) {
105 if (\@_ > 1) {
106 \${\$_[0]}->{$method_name} = ''.\$_[1];
107 }
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 ## |Node| attributes
119
120 ## NOTE: Overridden by |Element|.
121 sub attributes () { undef }
122
123 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
131 sub child_nodes ($) {
132 ## NOTE: Overridden by |CharacterData|, |ElementTypeDefinition|,
133 ## |Notation|, and |ProcessingInstruction|.
134 require Message::DOM::NodeList;
135 return bless \\($_[0]), 'Message::DOM::NodeList::ChildNodeList';
136 } # child_nodes
137
138 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 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 sub local_name { undef }
166
167 sub manakai_local_name { undef }
168
169 sub namespace_uri { undef }
170
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
186 ## NOTE: Overridden by subclasses.
187 sub node_name () { undef }
188
189 ## NOTE: Overridden by subclasses.
190 sub node_type () { }
191
192 ## NOTE: Overridden by |Attr|, |AttributeDefinition|,
193 ## |CharacterData|, and |ProcessingInstruction|.
194 sub node_value () { undef }
195
196 sub owner_document ($);
197
198 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 sub parent_node ($);
212
213 ## NOTE: Overridden by |Element| and |Attr|.
214 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 sub manakai_read_only ($);
232
233 sub text_content ($;$) {
234 ## 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 if (${$$self->{owner_document} or $self}->{strict_error_checking} and
245 $$self->{manakai_read_only}) {
246 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 } # text_content
281
282 ## |Node| methods
283
284 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 my $er_copy_asis
299 = $cfg->get_parameter
300 (q<http://suika.fam.cx/www/2006/dom-config/clone-entity-reference-subtree>);
301
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 $clone->is_element_content_whitespace (1)
334 if $node->is_element_content_whitespace;
335 } 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 ## TODO:
593 sub is_same_node ($$) {
594 return $_[0] eq $_[1];
595 } # is_same_node
596
597 ## TODO:
598 sub is_equal_node ($$) {
599 return $_[0]->node_name eq $_[1]->node_name &&
600 $_[0]->node_value eq $_[1]->node_value;
601 } # 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 my $parent_list = ${$$new_child->{parent_node}}->{child_nodes};
608 for (0..$#$parent_list) {
609 if ($parent_list->[$_] eq $new_child) {
610 splice @$parent_list, $_, 1;
611 last;
612 }
613 }
614 }
615 push @{$$self->{child_nodes}}, $new_child;
616 $$new_child->{parent_node} = $self;
617 Scalar::Util::weaken ($$new_child->{parent_node});
618 ## TODO:
619 $$new_child->{owner_document} = $self if $self->node_type == DOCUMENT_NODE;
620 return $new_child;
621 } # append_child
622
623 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 ## 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 my $parent_list = ${$$new_child->{parent_node}}->{child_nodes};
652 for (0..$#$parent_list) {
653 if ($parent_list->[$_] eq $new_child) {
654 splice @$parent_list, $_, 1;
655 last;
656 }
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 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 ## 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 last;
892 }
893 }
894 delete $$old_child->{parent_node};
895 return $old_child;
896 } # remove_child
897
898 sub manakai_set_read_only ($;$$) {
899 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 } # manakai_set_read_only
930
931 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 package Message::IF::Node;
958
959 =head1 LICENSE
960
961 Copyright 2007 Wakaba <w@suika.fam.cx>
962
963 This program is free software; you can redistribute it and/or
964 modify it under the same terms as Perl itself.
965
966 =cut
967
968 1;
969 ## $Date: 2007/07/07 07:36:58 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24