File Coverage

blib/lib/XML/DOM.pm
Criterion Covered Total %
statement 13 18 72.2
branch 0 2 0.0
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 25 72.0


line stmt bran cond sub pod time code
1             ################################################################################
2             #
3             # Perl module: XML::DOM
4             #
5             # By Enno Derksen
6             #
7             ################################################################################
8             #
9             # To do:
10             #
11             # * optimize Attr if it only contains 1 Text node to hold the value
12             # * fix setDocType!
13             #
14             # * BUG: setOwnerDocument - does not process default attr values correctly,
15             # they still point to the old doc.
16             # * change Exception mechanism
17             # * maybe: more checking of sysId etc.
18             # * NoExpand mode (don't know what else is useful)
19             # * various odds and ends: see comments starting with "??"
20             # * normalize(1) could also expand CDataSections and EntityReferences
21             # * parse a DocumentFragment?
22             # * encoding support
23             #
24             ######################################################################
25              
26             ######################################################################
27             package XML::DOM;
28             ######################################################################
29              
30 21     21   27116 use strict;
  21         41  
  21         1025  
31              
32 21         2865 use vars qw( $VERSION @ISA @EXPORT
33             $IgnoreReadOnly $SafeMode $TagStyle
34             %DefaultEntities %DecodeDefaultEntity
35 21     21   109 );
  21         33  
36 21     21   110 use Carp;
  21         38  
  21         2283  
37 21     21   21177 use XML::RegExp;
  21         20590  
  21         2966  
38              
39             BEGIN
40             {
41 21     21   37118 require XML::Parser;
42 0           $VERSION = '1.44';
43              
44 0           my $needVersion = '2.28';
45 0 0         die "need at least XML::Parser version $needVersion (current=${XML::Parser::VERSION})"
46             unless $XML::Parser::VERSION >= $needVersion;
47              
48 0           @ISA = qw( Exporter );
49              
50             # Constants for XML::DOM Node types
51 0           @EXPORT = qw(
52             UNKNOWN_NODE
53             ELEMENT_NODE
54             ATTRIBUTE_NODE
55             TEXT_NODE
56             CDATA_SECTION_NODE
57             ENTITY_REFERENCE_NODE
58             ENTITY_NODE
59             PROCESSING_INSTRUCTION_NODE
60             COMMENT_NODE
61             DOCUMENT_NODE
62             DOCUMENT_TYPE_NODE
63             DOCUMENT_FRAGMENT_NODE
64             NOTATION_NODE
65             ELEMENT_DECL_NODE
66             ATT_DEF_NODE
67             XML_DECL_NODE
68             ATTLIST_DECL_NODE
69             );
70             }
71              
72             #---- Constant definitions
73              
74             # Node types
75              
76             sub UNKNOWN_NODE () { 0 } # not in the DOM Spec
77              
78             sub ELEMENT_NODE () { 1 }
79             sub ATTRIBUTE_NODE () { 2 }
80             sub TEXT_NODE () { 3 }
81             sub CDATA_SECTION_NODE () { 4 }
82             sub ENTITY_REFERENCE_NODE () { 5 }
83             sub ENTITY_NODE () { 6 }
84             sub PROCESSING_INSTRUCTION_NODE () { 7 }
85             sub COMMENT_NODE () { 8 }
86             sub DOCUMENT_NODE () { 9 }
87             sub DOCUMENT_TYPE_NODE () { 10}
88             sub DOCUMENT_FRAGMENT_NODE () { 11}
89             sub NOTATION_NODE () { 12}
90              
91             sub ELEMENT_DECL_NODE () { 13 } # not in the DOM Spec
92             sub ATT_DEF_NODE () { 14 } # not in the DOM Spec
93             sub XML_DECL_NODE () { 15 } # not in the DOM Spec
94             sub ATTLIST_DECL_NODE () { 16 } # not in the DOM Spec
95              
96             %DefaultEntities =
97             (
98             "quot" => '"',
99             "gt" => ">",
100             "lt" => "<",
101             "apos" => "'",
102             "amp" => "&"
103             );
104              
105             %DecodeDefaultEntity =
106             (
107             '"' => """,
108             ">" => ">",
109             "<" => "<",
110             "'" => "'",
111             "&" => "&"
112             );
113              
114             #
115             # If you don't want DOM warnings to use 'warn', override this method like this:
116             #
117             # { # start block scope
118             # local *XML::DOM::warning = \&my_warn;
119             # ... your code here ...
120             # } # end block scope (old XML::DOM::warning takes effect again)
121             #
122             sub warning # static
123             {
124             warn @_;
125             }
126              
127             #
128             # This method defines several things in the caller's package, so you can use named constants to
129             # access the array that holds the member data, i.e. $self->[_Data]. It assumes the caller's package
130             # defines a class that is implemented as a blessed array reference.
131             # Note that this is very similar to using 'use fields' and 'use base'.
132             #
133             # E.g. if $fields eq "Name Model", $parent eq "XML::DOM::Node" and
134             # XML::DOM::Node had "A B C" as fields and it was called from package "XML::DOM::ElementDecl",
135             # then this code would basically do the following:
136             #
137             # package XML::DOM::ElementDecl;
138             #
139             # sub _Name () { 3 } # Note that parent class had three fields
140             # sub _Model () { 4 }
141             #
142             # # Maps constant names (without '_') to constant (int) value
143             # %HFIELDS = ( %XML::DOM::Node::HFIELDS, Name => _Name, Model => _Model );
144             #
145             # # Define XML:DOM::ElementDecl as a subclass of XML::DOM::Node
146             # @ISA = qw{ XML::DOM::Node };
147             #
148             # # The following function names can be exported into the user's namespace.
149             # @EXPORT_OK = qw{ _Name _Model };
150             #
151             # # The following function names can be exported into the user's namespace
152             # # with: import XML::DOM::ElementDecl qw( :Fields );
153             # %EXPORT_TAGS = ( Fields => qw{ _Name _Model } );
154             #
155             sub def_fields # static
156             {
157             my ($fields, $parent) = @_;
158              
159             my ($pkg) = caller;
160              
161             no strict 'refs';
162              
163             my @f = split (/\s+/, $fields);
164             my $n = 0;
165              
166             my %hfields;
167             if (defined $parent)
168             {
169             my %pf = %{"$parent\::HFIELDS"};
170             %hfields = %pf;
171              
172             $n = scalar (keys %pf);
173             @{"$pkg\::ISA"} = ( $parent );
174             }
175              
176             my $i = $n;
177             for (@f)
178             {
179             eval "sub $pkg\::_$_ () { $i }";
180             $hfields{$_} = $i;
181             $i++;
182             }
183             %{"$pkg\::HFIELDS"} = %hfields;
184             @{"$pkg\::EXPORT_OK"} = map { "_$_" } @f;
185            
186             ${"$pkg\::EXPORT_TAGS"}{Fields} = [ map { "_$_" } @f ];
187             }
188              
189             # sub blesh
190             # {
191             # my $hashref = shift;
192             # my $class = shift;
193             # no strict 'refs';
194             # my $self = bless [\%{"$class\::FIELDS"}], $class;
195             # if (defined $hashref)
196             # {
197             # for (keys %$hashref)
198             # {
199             # $self->{$_} = $hashref->{$_};
200             # }
201             # }
202             # $self;
203             # }
204              
205             # sub blesh2
206             # {
207             # my $hashref = shift;
208             # my $class = shift;
209             # no strict 'refs';
210             # my $self = bless [\%{"$class\::FIELDS"}], $class;
211             # if (defined $hashref)
212             # {
213             # for (keys %$hashref)
214             # {
215             # eval { $self->{$_} = $hashref->{$_}; };
216             # croak "ERROR in field [$_] $@" if $@;
217             # }
218             # }
219             # $self;
220             #}
221              
222             #
223             # CDATA section may not contain "]]>"
224             #
225             sub encodeCDATA
226             {
227             my ($str) = shift;
228             $str =~ s/]]>/]]>/go;
229             $str;
230             }
231              
232             #
233             # PI may not contain "?>"
234             #
235             sub encodeProcessingInstruction
236             {
237             my ($str) = shift;
238             $str =~ s/\?>/?>/go;
239             $str;
240             }
241              
242             #
243             #?? Not sure if this is right - must prevent double minus somehow...
244             #
245             sub encodeComment
246             {
247             my ($str) = shift;
248             return undef unless defined $str;
249              
250             $str =~ s/--/--/go;
251             $str;
252             }
253              
254             #
255             # For debugging
256             #
257             sub toHex
258             {
259             my $str = shift;
260             my $len = length($str);
261             my @a = unpack ("C$len", $str);
262             my $s = "";
263             for (@a)
264             {
265             $s .= sprintf ("%02x", $_);
266             }
267             $s;
268             }
269              
270             #
271             # 2nd parameter $default: list of Default Entity characters that need to be
272             # converted (e.g. "&<" for conversion to "&" and "<" resp.)
273             #
274             sub encodeText
275             {
276             my ($str, $default) = @_;
277             return undef unless defined $str;
278              
279             if ($] >= 5.006) {
280             $str =~ s/([$default])|(]]>)/
281             defined ($1) ? $DecodeDefaultEntity{$1} : "]]>" /egs;
282             }
283             else {
284             $str =~ s/([\xC0-\xDF].|[\xE0-\xEF]..|[\xF0-\xFF]...)|([$default])|(]]>)/
285             defined($1) ? XmlUtf8Decode ($1) :
286             defined ($2) ? $DecodeDefaultEntity{$2} : "]]>" /egs;
287             }
288              
289             #?? could there be references that should not be expanded?
290             # e.g. should not replace &#nn; ¯ and &abc;
291             # $str =~ s/&(?!($ReName|#[0-9]+|#x[0-9a-fA-F]+);)/&/go;
292              
293             $str;
294             }
295              
296             #
297             # Used by AttDef - default value
298             #
299             sub encodeAttrValue
300             {
301             encodeText (shift, '"&<>');
302             }
303              
304             #
305             # Converts an integer (Unicode - ISO/IEC 10646) to a UTF-8 encoded character
306             # sequence.
307             # Used when converting e.g. { or Ͽ to a string value.
308             #
309             # Algorithm borrowed from expat/xmltok.c/XmlUtf8Encode()
310             #
311             # not checking for bad characters: < 0, x00-x08, x0B-x0C, x0E-x1F, xFFFE-xFFFF
312             #
313             sub XmlUtf8Encode
314             {
315             my $n = shift;
316             if ($n < 0x80)
317             {
318             return chr ($n);
319             }
320             elsif ($n < 0x800)
321             {
322             return pack ("CC", (($n >> 6) | 0xc0), (($n & 0x3f) | 0x80));
323             }
324             elsif ($n < 0x10000)
325             {
326             return pack ("CCC", (($n >> 12) | 0xe0), ((($n >> 6) & 0x3f) | 0x80),
327             (($n & 0x3f) | 0x80));
328             }
329             elsif ($n < 0x110000)
330             {
331             return pack ("CCCC", (($n >> 18) | 0xf0), ((($n >> 12) & 0x3f) | 0x80),
332             ((($n >> 6) & 0x3f) | 0x80), (($n & 0x3f) | 0x80));
333             }
334             croak "number is too large for Unicode [$n] in &XmlUtf8Encode";
335             }
336              
337             #
338             # Opposite of XmlUtf8Decode plus it adds prefix "&#" or "&#x" and suffix ";"
339             # The 2nd parameter ($hex) indicates whether the result is hex encoded or not.
340             #
341             sub XmlUtf8Decode
342             {
343             my ($str, $hex) = @_;
344             my $len = length ($str);
345             my $n;
346              
347             if ($len == 2)
348             {
349             my @n = unpack "C2", $str;
350             $n = (($n[0] & 0x3f) << 6) + ($n[1] & 0x3f);
351             }
352             elsif ($len == 3)
353             {
354             my @n = unpack "C3", $str;
355             $n = (($n[0] & 0x1f) << 12) + (($n[1] & 0x3f) << 6) +
356             ($n[2] & 0x3f);
357             }
358             elsif ($len == 4)
359             {
360             my @n = unpack "C4", $str;
361             $n = (($n[0] & 0x0f) << 18) + (($n[1] & 0x3f) << 12) +
362             (($n[2] & 0x3f) << 6) + ($n[3] & 0x3f);
363             }
364             elsif ($len == 1) # just to be complete...
365             {
366             $n = ord ($str);
367             }
368             else
369             {
370             croak "bad value [$str] for XmlUtf8Decode";
371             }
372             $hex ? sprintf ("&#x%x;", $n) : "&#$n;";
373             }
374              
375             $IgnoreReadOnly = 0;
376             $SafeMode = 1;
377              
378             sub getIgnoreReadOnly
379             {
380             $IgnoreReadOnly;
381             }
382              
383             #
384             # The global flag $IgnoreReadOnly is set to the specified value and the old
385             # value of $IgnoreReadOnly is returned.
386             #
387             # To temporarily disable read-only related exceptions (i.e. when parsing
388             # XML or temporarily), do the following:
389             #
390             # my $oldIgnore = XML::DOM::ignoreReadOnly (1);
391             # ... do whatever you want ...
392             # XML::DOM::ignoreReadOnly ($oldIgnore);
393             #
394             sub ignoreReadOnly
395             {
396             my $i = $IgnoreReadOnly;
397             $IgnoreReadOnly = $_[0];
398             return $i;
399             }
400              
401             #
402             # XML spec seems to break its own rules... (see ENTITY xmlpio)
403             #
404             sub forgiving_isValidName
405             {
406             use bytes; # XML::RegExp expressed in terms encoded UTF8
407             $_[0] =~ /^$XML::RegExp::Name$/o;
408             }
409              
410             #
411             # Don't allow names starting with xml (either case)
412             #
413             sub picky_isValidName
414             {
415             use bytes; # XML::RegExp expressed in terms encoded UTF8
416             $_[0] =~ /^$XML::RegExp::Name$/o and $_[0] !~ /^xml/i;
417             }
418              
419             # Be forgiving by default,
420             *isValidName = \&forgiving_isValidName;
421              
422             sub allowReservedNames # static
423             {
424             *isValidName = ($_[0] ? \&forgiving_isValidName : \&picky_isValidName);
425             }
426              
427             sub getAllowReservedNames # static
428             {
429             *isValidName == \&forgiving_isValidName;
430             }
431              
432             #
433             # Always compress empty tags by default
434             # This is used by Element::print.
435             #
436             $TagStyle = sub { 0 };
437              
438             sub setTagCompression
439             {
440             $TagStyle = shift;
441             }
442              
443             ######################################################################
444             package XML::DOM::PrintToFileHandle;
445             ######################################################################
446              
447             #
448             # Used by XML::DOM::Node::printToFileHandle
449             #
450              
451             sub new
452             {
453             my($class, $fn) = @_;
454             bless $fn, $class;
455             }
456              
457             sub print
458             {
459             my ($self, $str) = @_;
460             print $self $str;
461             }
462              
463             ######################################################################
464             package XML::DOM::PrintToString;
465             ######################################################################
466              
467             use vars qw{ $Singleton };
468              
469             #
470             # Used by XML::DOM::Node::toString to concatenate strings
471             #
472              
473             sub new
474             {
475             my($class) = @_;
476             my $str = "";
477             bless \$str, $class;
478             }
479              
480             sub print
481             {
482             my ($self, $str) = @_;
483             $$self .= $str;
484             }
485              
486             sub toString
487             {
488             my $self = shift;
489             $$self;
490             }
491              
492             sub reset
493             {
494             ${$_[0]} = "";
495             }
496              
497             $Singleton = new XML::DOM::PrintToString;
498              
499             ######################################################################
500             package XML::DOM::DOMImplementation;
501             ######################################################################
502            
503             $XML::DOM::DOMImplementation::Singleton =
504             bless \$XML::DOM::DOMImplementation::Singleton, 'XML::DOM::DOMImplementation';
505            
506             sub hasFeature
507             {
508             my ($self, $feature, $version) = @_;
509            
510             uc($feature) eq 'XML' and ($version eq '1.0' || $version eq '');
511             }
512              
513              
514             ######################################################################
515             package XML::XQL::Node; # forward declaration
516             ######################################################################
517              
518             ######################################################################
519             package XML::DOM::Node;
520             ######################################################################
521              
522             use vars qw( @NodeNames @EXPORT @ISA %HFIELDS @EXPORT_OK @EXPORT_TAGS );
523              
524             BEGIN
525             {
526             use XML::DOM::DOMException;
527             import Carp;
528              
529             require FileHandle;
530              
531             @ISA = qw( Exporter XML::XQL::Node );
532              
533             # NOTE: SortKey is used in XML::XQL::Node.
534             # UserData is reserved for users (Hang your data here!)
535             XML::DOM::def_fields ("C A Doc Parent ReadOnly UsedIn Hidden SortKey UserData");
536              
537             push (@EXPORT, qw(
538             UNKNOWN_NODE
539             ELEMENT_NODE
540             ATTRIBUTE_NODE
541             TEXT_NODE
542             CDATA_SECTION_NODE
543             ENTITY_REFERENCE_NODE
544             ENTITY_NODE
545             PROCESSING_INSTRUCTION_NODE
546             COMMENT_NODE
547             DOCUMENT_NODE
548             DOCUMENT_TYPE_NODE
549             DOCUMENT_FRAGMENT_NODE
550             NOTATION_NODE
551             ELEMENT_DECL_NODE
552             ATT_DEF_NODE
553             XML_DECL_NODE
554             ATTLIST_DECL_NODE
555             ));
556             }
557              
558             #---- Constant definitions
559              
560             # Node types
561              
562             sub UNKNOWN_NODE () {0;} # not in the DOM Spec
563              
564             sub ELEMENT_NODE () {1;}
565             sub ATTRIBUTE_NODE () {2;}
566             sub TEXT_NODE () {3;}
567             sub CDATA_SECTION_NODE () {4;}
568             sub ENTITY_REFERENCE_NODE () {5;}
569             sub ENTITY_NODE () {6;}
570             sub PROCESSING_INSTRUCTION_NODE () {7;}
571             sub COMMENT_NODE () {8;}
572             sub DOCUMENT_NODE () {9;}
573             sub DOCUMENT_TYPE_NODE () {10;}
574             sub DOCUMENT_FRAGMENT_NODE () {11;}
575             sub NOTATION_NODE () {12;}
576              
577             sub ELEMENT_DECL_NODE () {13;} # not in the DOM Spec
578             sub ATT_DEF_NODE () {14;} # not in the DOM Spec
579             sub XML_DECL_NODE () {15;} # not in the DOM Spec
580             sub ATTLIST_DECL_NODE () {16;} # not in the DOM Spec
581              
582             @NodeNames = (
583             "UNKNOWN_NODE", # not in the DOM Spec!
584              
585             "ELEMENT_NODE",
586             "ATTRIBUTE_NODE",
587             "TEXT_NODE",
588             "CDATA_SECTION_NODE",
589             "ENTITY_REFERENCE_NODE",
590             "ENTITY_NODE",
591             "PROCESSING_INSTRUCTION_NODE",
592             "COMMENT_NODE",
593             "DOCUMENT_NODE",
594             "DOCUMENT_TYPE_NODE",
595             "DOCUMENT_FRAGMENT_NODE",
596             "NOTATION_NODE",
597              
598             "ELEMENT_DECL_NODE",
599             "ATT_DEF_NODE",
600             "XML_DECL_NODE",
601             "ATTLIST_DECL_NODE"
602             );
603              
604             sub decoupleUsedIn
605             {
606             my $self = shift;
607             undef $self->[_UsedIn]; # was delete
608             }
609              
610             sub getParentNode
611             {
612             $_[0]->[_Parent];
613             }
614              
615             sub appendChild
616             {
617             my ($self, $node) = @_;
618              
619             # REC 7473
620             if ($XML::DOM::SafeMode)
621             {
622             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
623             "node is ReadOnly")
624             if $self->isReadOnly;
625             }
626              
627             my $doc = $self->[_Doc];
628              
629             if ($node->isDocumentFragmentNode)
630             {
631             if ($XML::DOM::SafeMode)
632             {
633             for my $n (@{$node->[_C]})
634             {
635             croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR,
636             "nodes belong to different documents")
637             if $doc != $n->[_Doc];
638            
639             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
640             "node is ancestor of parent node")
641             if $n->isAncestor ($self);
642            
643             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
644             "bad node type")
645             if $self->rejectChild ($n);
646             }
647             }
648              
649             my @list = @{$node->[_C]}; # don't try to compress this
650             for my $n (@list)
651             {
652             $n->setParentNode ($self);
653             }
654             push @{$self->[_C]}, @list;
655             }
656             else
657             {
658             if ($XML::DOM::SafeMode)
659             {
660             croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR,
661             "nodes belong to different documents")
662             if $doc != $node->[_Doc];
663            
664             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
665             "node is ancestor of parent node")
666             if $node->isAncestor ($self);
667            
668             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
669             "bad node type")
670             if $self->rejectChild ($node);
671             }
672             $node->setParentNode ($self);
673             push @{$self->[_C]}, $node;
674             }
675             $node;
676             }
677              
678             sub getChildNodes
679             {
680             # NOTE: if node can't have children, $self->[_C] is undef.
681             my $kids = $_[0]->[_C];
682              
683             # Return a list if called in list context.
684             wantarray ? (defined ($kids) ? @{ $kids } : ()) :
685             (defined ($kids) ? $kids : $XML::DOM::NodeList::EMPTY);
686             }
687              
688             sub hasChildNodes
689             {
690             my $kids = $_[0]->[_C];
691             defined ($kids) && @$kids > 0;
692             }
693              
694             # This method is overriden in Document
695             sub getOwnerDocument
696             {
697             $_[0]->[_Doc];
698             }
699              
700             sub getFirstChild
701             {
702             my $kids = $_[0]->[_C];
703             defined $kids ? $kids->[0] : undef;
704             }
705              
706             sub getLastChild
707             {
708             my $kids = $_[0]->[_C];
709             defined $kids ? $kids->[-1] : undef;
710             }
711              
712             sub getPreviousSibling
713             {
714             my $self = shift;
715              
716             my $pa = $self->[_Parent];
717             return undef unless $pa;
718             my $index = $pa->getChildIndex ($self);
719             return undef unless $index;
720              
721             $pa->getChildAtIndex ($index - 1);
722             }
723              
724             sub getNextSibling
725             {
726             my $self = shift;
727              
728             my $pa = $self->[_Parent];
729             return undef unless $pa;
730              
731             $pa->getChildAtIndex ($pa->getChildIndex ($self) + 1);
732             }
733              
734             sub insertBefore
735             {
736             my ($self, $node, $refNode) = @_;
737              
738             return $self->appendChild ($node) unless $refNode; # append at the end
739              
740             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
741             "node is ReadOnly")
742             if $self->isReadOnly;
743              
744             my @nodes = ($node);
745             @nodes = @{$node->[_C]}
746             if $node->getNodeType == DOCUMENT_FRAGMENT_NODE;
747              
748             my $doc = $self->[_Doc];
749              
750             for my $n (@nodes)
751             {
752             croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR,
753             "nodes belong to different documents")
754             if $doc != $n->[_Doc];
755            
756             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
757             "node is ancestor of parent node")
758             if $n->isAncestor ($self);
759              
760             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
761             "bad node type")
762             if $self->rejectChild ($n);
763             }
764             my $index = $self->getChildIndex ($refNode);
765              
766             croak new XML::DOM::DOMException (NOT_FOUND_ERR,
767             "reference node not found")
768             if $index == -1;
769              
770             for my $n (@nodes)
771             {
772             $n->setParentNode ($self);
773             }
774              
775             splice (@{$self->[_C]}, $index, 0, @nodes);
776             $node;
777             }
778              
779             sub replaceChild
780             {
781             my ($self, $node, $refNode) = @_;
782              
783             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
784             "node is ReadOnly")
785             if $self->isReadOnly;
786              
787             my @nodes = ($node);
788             @nodes = @{$node->[_C]}
789             if $node->getNodeType == DOCUMENT_FRAGMENT_NODE;
790              
791             for my $n (@nodes)
792             {
793             croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR,
794             "nodes belong to different documents")
795             if $self->[_Doc] != $n->[_Doc];
796              
797             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
798             "node is ancestor of parent node")
799             if $n->isAncestor ($self);
800              
801             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
802             "bad node type")
803             if $self->rejectChild ($n);
804             }
805              
806             my $index = $self->getChildIndex ($refNode);
807             croak new XML::DOM::DOMException (NOT_FOUND_ERR,
808             "reference node not found")
809             if $index == -1;
810              
811             for my $n (@nodes)
812             {
813             $n->setParentNode ($self);
814             }
815             splice (@{$self->[_C]}, $index, 1, @nodes);
816              
817             $refNode->removeChildHoodMemories;
818             $refNode;
819             }
820              
821             sub removeChild
822             {
823             my ($self, $node) = @_;
824              
825             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
826             "node is ReadOnly")
827             if $self->isReadOnly;
828              
829             my $index = $self->getChildIndex ($node);
830              
831             croak new XML::DOM::DOMException (NOT_FOUND_ERR,
832             "reference node not found")
833             if $index == -1;
834              
835             splice (@{$self->[_C]}, $index, 1, ());
836              
837             $node->removeChildHoodMemories;
838             $node;
839             }
840              
841             # Merge all subsequent Text nodes in this subtree
842             sub normalize
843             {
844             my ($self) = shift;
845             my $prev = undef; # previous Text node
846              
847             return unless defined $self->[_C];
848              
849             my @nodes = @{$self->[_C]};
850             my $i = 0;
851             my $n = @nodes;
852             while ($i < $n)
853             {
854             my $node = $self->getChildAtIndex($i);
855             my $type = $node->getNodeType;
856              
857             if (defined $prev)
858             {
859             # It should not merge CDATASections. Dom Spec says:
860             # Adjacent CDATASections nodes are not merged by use
861             # of the Element.normalize() method.
862             if ($type == TEXT_NODE)
863             {
864             $prev->appendData ($node->getData);
865             $self->removeChild ($node);
866             $i--;
867             $n--;
868             }
869             else
870             {
871             $prev = undef;
872             if ($type == ELEMENT_NODE)
873             {
874             $node->normalize;
875             if (defined $node->[_A])
876             {
877             for my $attr (@{$node->[_A]->getValues})
878             {
879             $attr->normalize;
880             }
881             }
882             }
883             }
884             }
885             else
886             {
887             if ($type == TEXT_NODE)
888             {
889             $prev = $node;
890             }
891             elsif ($type == ELEMENT_NODE)
892             {
893             $node->normalize;
894             if (defined $node->[_A])
895             {
896             for my $attr (@{$node->[_A]->getValues})
897             {
898             $attr->normalize;
899             }
900             }
901             }
902             }
903             $i++;
904             }
905             }
906              
907             #
908             # Return all Element nodes in the subtree that have the specified tagName.
909             # If tagName is "*", all Element nodes are returned.
910             # NOTE: the DOM Spec does not specify a 3rd or 4th parameter
911             #
912             sub getElementsByTagName
913             {
914             my ($self, $tagName, $recurse, $list) = @_;
915             $recurse = 1 unless defined $recurse;
916             $list = (wantarray ? [] : new XML::DOM::NodeList) unless defined $list;
917              
918             return unless defined $self->[_C];
919              
920             # preorder traversal: check parent node first
921             for my $kid (@{$self->[_C]})
922             {
923             if ($kid->isElementNode)
924             {
925             if ($tagName eq "*" || $tagName eq $kid->getTagName)
926             {
927             push @{$list}, $kid;
928             }
929             $kid->getElementsByTagName ($tagName, $recurse, $list) if $recurse;
930             }
931             }
932             wantarray ? @{ $list } : $list;
933             }
934              
935             sub getNodeValue
936             {
937             undef;
938             }
939              
940             sub setNodeValue
941             {
942             # no-op
943             }
944              
945             #
946             # Redefined by XML::DOM::Element
947             #
948             sub getAttributes
949             {
950             undef;
951             }
952              
953             #------------------------------------------------------------
954             # Extra method implementations
955              
956             sub setOwnerDocument
957             {
958             my ($self, $doc) = @_;
959             $self->[_Doc] = $doc;
960              
961             return unless defined $self->[_C];
962              
963             for my $kid (@{$self->[_C]})
964             {
965             $kid->setOwnerDocument ($doc);
966             }
967             }
968              
969             sub cloneChildren
970             {
971             my ($self, $node, $deep) = @_;
972             return unless $deep;
973            
974             return unless defined $self->[_C];
975              
976             local $XML::DOM::IgnoreReadOnly = 1;
977              
978             for my $kid (@{$node->[_C]})
979             {
980             my $newNode = $kid->cloneNode ($deep);
981             push @{$self->[_C]}, $newNode;
982             $newNode->setParentNode ($self);
983             }
984             }
985              
986             #
987             # For internal use only!
988             #
989             sub removeChildHoodMemories
990             {
991             my ($self) = @_;
992              
993             undef $self->[_Parent]; # was delete
994             }
995              
996             #
997             # Remove circular dependencies. The Node and its children should
998             # not be used afterwards.
999             #
1000             sub dispose
1001             {
1002             my $self = shift;
1003              
1004             $self->removeChildHoodMemories;
1005              
1006             if (defined $self->[_C])
1007             {
1008             $self->[_C]->dispose;
1009             undef $self->[_C]; # was delete
1010             }
1011             undef $self->[_Doc]; # was delete
1012             }
1013              
1014             #
1015             # For internal use only!
1016             #
1017             sub setParentNode
1018             {
1019             my ($self, $parent) = @_;
1020              
1021             # REC 7473
1022             my $oldParent = $self->[_Parent];
1023             if (defined $oldParent)
1024             {
1025             # remove from current parent
1026             my $index = $oldParent->getChildIndex ($self);
1027              
1028             # NOTE: we don't have to check if [_C] is defined,
1029             # because were removing a child here!
1030             splice (@{$oldParent->[_C]}, $index, 1, ());
1031              
1032             $self->removeChildHoodMemories;
1033             }
1034             $self->[_Parent] = $parent;
1035             }
1036              
1037             #
1038             # This function can return 3 values:
1039             # 1: always readOnly
1040             # 0: never readOnly
1041             # undef: depends on parent node
1042             #
1043             # Returns 1 for DocumentType, Notation, Entity, EntityReference, Attlist,
1044             # ElementDecl, AttDef.
1045             # The first 4 are readOnly according to the DOM Spec, the others are always
1046             # children of DocumentType. (Naturally, children of a readOnly node have to be
1047             # readOnly as well...)
1048             # These nodes are always readOnly regardless of who their ancestors are.
1049             # Other nodes, e.g. Comment, are readOnly only if their parent is readOnly,
1050             # which basically means that one of its ancestors has to be one of the
1051             # aforementioned node types.
1052             # Document and DocumentFragment return 0 for obvious reasons.
1053             # Attr, Element, CDATASection, Text return 0. The DOM spec says that they can
1054             # be children of an Entity, but I don't think that that's possible
1055             # with the current XML::Parser.
1056             # Attr uses a {ReadOnly} property, which is only set if it's part of a AttDef.
1057             # Always returns 0 if ignoreReadOnly is set.
1058             #
1059             sub isReadOnly
1060             {
1061             # default implementation for Nodes that are always readOnly
1062             ! $XML::DOM::IgnoreReadOnly;
1063             }
1064              
1065             sub rejectChild
1066             {
1067             1;
1068             }
1069              
1070             sub getNodeTypeName
1071             {
1072             $NodeNames[$_[0]->getNodeType];
1073             }
1074              
1075             sub getChildIndex
1076             {
1077             my ($self, $node) = @_;
1078             my $i = 0;
1079              
1080             return -1 unless defined $self->[_C];
1081              
1082             for my $kid (@{$self->[_C]})
1083             {
1084             return $i if $kid == $node;
1085             $i++;
1086             }
1087             -1;
1088             }
1089              
1090             sub getChildAtIndex
1091             {
1092             my $kids = $_[0]->[_C];
1093             defined ($kids) ? $kids->[$_[1]] : undef;
1094             }
1095              
1096             sub isAncestor
1097             {
1098             my ($self, $node) = @_;
1099              
1100             do
1101             {
1102             return 1 if $self == $node;
1103             $node = $node->[_Parent];
1104             }
1105             while (defined $node);
1106              
1107             0;
1108             }
1109              
1110             #
1111             # Added for optimization. Overriden in XML::DOM::Text
1112             #
1113             sub isTextNode
1114             {
1115             0;
1116             }
1117              
1118             #
1119             # Added for optimization. Overriden in XML::DOM::DocumentFragment
1120             #
1121             sub isDocumentFragmentNode
1122             {
1123             0;
1124             }
1125              
1126             #
1127             # Added for optimization. Overriden in XML::DOM::Element
1128             #
1129             sub isElementNode
1130             {
1131             0;
1132             }
1133              
1134             #
1135             # Add a Text node with the specified value or append the text to the
1136             # previous Node if it is a Text node.
1137             #
1138             sub addText
1139             {
1140             # REC 9456 (if it was called)
1141             my ($self, $str) = @_;
1142              
1143             my $node = ${$self->[_C]}[-1]; # $self->getLastChild
1144              
1145             if (defined ($node) && $node->isTextNode)
1146             {
1147             # REC 5475 (if it was called)
1148             $node->appendData ($str);
1149             }
1150             else
1151             {
1152             $node = $self->[_Doc]->createTextNode ($str);
1153             $self->appendChild ($node);
1154             }
1155             $node;
1156             }
1157              
1158             #
1159             # Add a CDATASection node with the specified value or append the text to the
1160             # previous Node if it is a CDATASection node.
1161             #
1162             sub addCDATA
1163             {
1164             my ($self, $str) = @_;
1165              
1166             my $node = ${$self->[_C]}[-1]; # $self->getLastChild
1167              
1168             if (defined ($node) && $node->getNodeType == CDATA_SECTION_NODE)
1169             {
1170             $node->appendData ($str);
1171             }
1172             else
1173             {
1174             $node = $self->[_Doc]->createCDATASection ($str);
1175             $self->appendChild ($node);
1176             }
1177             }
1178              
1179             sub removeChildNodes
1180             {
1181             my $self = shift;
1182              
1183             my $cref = $self->[_C];
1184             return unless defined $cref;
1185              
1186             my $kid;
1187             while ($kid = pop @{$cref})
1188             {
1189             undef $kid->[_Parent]; # was delete
1190             }
1191             }
1192              
1193             sub toString
1194             {
1195             my $self = shift;
1196             my $pr = $XML::DOM::PrintToString::Singleton;
1197             $pr->reset;
1198             $self->print ($pr);
1199             $pr->toString;
1200             }
1201              
1202             sub to_sax
1203             {
1204             my $self = shift;
1205             unshift @_, 'Handler' if (@_ == 1);
1206             my %h = @_;
1207              
1208             my $doch = exists ($h{DocumentHandler}) ? $h{DocumentHandler}
1209             : $h{Handler};
1210             my $dtdh = exists ($h{DTDHandler}) ? $h{DTDHandler}
1211             : $h{Handler};
1212             my $enth = exists ($h{EntityResolver}) ? $h{EntityResolver}
1213             : $h{Handler};
1214              
1215             $self->_to_sax ($doch, $dtdh, $enth);
1216             }
1217              
1218             sub printToFile
1219             {
1220             my ($self, $fileName) = @_;
1221             my $fh = new FileHandle ($fileName, "w") ||
1222             croak "printToFile - can't open output file $fileName";
1223            
1224             $self->print ($fh);
1225             $fh->close;
1226             }
1227              
1228             #
1229             # Use print to print to a FileHandle object (see printToFile code)
1230             #
1231             sub printToFileHandle
1232             {
1233             my ($self, $FH) = @_;
1234             my $pr = new XML::DOM::PrintToFileHandle ($FH);
1235             $self->print ($pr);
1236             }
1237              
1238             #
1239             # Used by AttDef::setDefault to convert unexpanded default attribute value
1240             #
1241             sub expandEntityRefs
1242             {
1243             my ($self, $str) = @_;
1244             my $doctype = $self->[_Doc]->getDoctype;
1245              
1246             use bytes; # XML::RegExp expressed in terms encoded UTF8
1247             $str =~ s/&($XML::RegExp::Name|(#([0-9]+)|#x([0-9a-fA-F]+)));/
1248             defined($2) ? XML::DOM::XmlUtf8Encode ($3 || hex ($4))
1249             : expandEntityRef ($1, $doctype)/ego;
1250             $str;
1251             }
1252              
1253             sub expandEntityRef
1254             {
1255             my ($entity, $doctype) = @_;
1256              
1257             my $expanded = $XML::DOM::DefaultEntities{$entity};
1258             return $expanded if defined $expanded;
1259              
1260             $expanded = $doctype->getEntity ($entity);
1261             return $expanded->getValue if (defined $expanded);
1262              
1263             #?? is this an error?
1264             croak "Could not expand entity reference of [$entity]\n";
1265             # return "&$entity;"; # entity not found
1266             }
1267              
1268             sub isHidden
1269             {
1270             $_[0]->[_Hidden];
1271             }
1272              
1273             ######################################################################
1274             package XML::DOM::Attr;
1275             ######################################################################
1276              
1277             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1278              
1279             BEGIN
1280             {
1281             import XML::DOM::Node qw( :DEFAULT :Fields );
1282             XML::DOM::def_fields ("Name Specified", "XML::DOM::Node");
1283             }
1284              
1285             use XML::DOM::DOMException;
1286             use Carp;
1287              
1288             sub new
1289             {
1290             my ($class, $doc, $name, $value, $specified) = @_;
1291              
1292             if ($XML::DOM::SafeMode)
1293             {
1294             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1295             "bad Attr name [$name]")
1296             unless XML::DOM::isValidName ($name);
1297             }
1298              
1299             my $self = bless [], $class;
1300              
1301             $self->[_Doc] = $doc;
1302             $self->[_C] = new XML::DOM::NodeList;
1303             $self->[_Name] = $name;
1304            
1305             if (defined $value)
1306             {
1307             $self->setValue ($value);
1308             $self->[_Specified] = (defined $specified) ? $specified : 1;
1309             }
1310             else
1311             {
1312             $self->[_Specified] = 0;
1313             }
1314             $self;
1315             }
1316              
1317             sub getNodeType
1318             {
1319             ATTRIBUTE_NODE;
1320             }
1321              
1322             sub isSpecified
1323             {
1324             $_[0]->[_Specified];
1325             }
1326              
1327             sub getName
1328             {
1329             $_[0]->[_Name];
1330             }
1331              
1332             sub getValue
1333             {
1334             my $self = shift;
1335             my $value = "";
1336              
1337             for my $kid (@{$self->[_C]})
1338             {
1339             $value .= $kid->getData if defined $kid->getData;
1340             }
1341             $value;
1342             }
1343              
1344             sub setValue
1345             {
1346             my ($self, $value) = @_;
1347              
1348             # REC 1147
1349             $self->removeChildNodes;
1350             $self->appendChild ($self->[_Doc]->createTextNode ($value));
1351             $self->[_Specified] = 1;
1352             }
1353              
1354             sub getNodeName
1355             {
1356             $_[0]->getName;
1357             }
1358              
1359             sub getNodeValue
1360             {
1361             $_[0]->getValue;
1362             }
1363              
1364             sub setNodeValue
1365             {
1366             $_[0]->setValue ($_[1]);
1367             }
1368              
1369             sub cloneNode
1370             {
1371             my ($self) = @_; # parameter deep is ignored
1372              
1373             my $node = $self->[_Doc]->createAttribute ($self->getName);
1374             $node->[_Specified] = $self->[_Specified];
1375             $node->[_ReadOnly] = 1 if $self->[_ReadOnly];
1376              
1377             $node->cloneChildren ($self, 1);
1378             $node;
1379             }
1380              
1381             #------------------------------------------------------------
1382             # Extra method implementations
1383             #
1384              
1385             sub isReadOnly
1386             {
1387             # ReadOnly property is set if it's part of a AttDef
1388             ! $XML::DOM::IgnoreReadOnly && defined ($_[0]->[_ReadOnly]);
1389             }
1390              
1391             sub print
1392             {
1393             my ($self, $FILE) = @_;
1394              
1395             my $name = $self->[_Name];
1396              
1397             $FILE->print ("$name=\"");
1398             for my $kid (@{$self->[_C]})
1399             {
1400             if ($kid->getNodeType == TEXT_NODE)
1401             {
1402             $FILE->print (XML::DOM::encodeAttrValue ($kid->getData));
1403             }
1404             else # ENTITY_REFERENCE_NODE
1405             {
1406             $kid->print ($FILE);
1407             }
1408             }
1409             $FILE->print ("\"");
1410             }
1411              
1412             sub rejectChild
1413             {
1414             my $t = $_[1]->getNodeType;
1415              
1416             $t != TEXT_NODE
1417             && $t != ENTITY_REFERENCE_NODE;
1418             }
1419              
1420             ######################################################################
1421             package XML::DOM::ProcessingInstruction;
1422             ######################################################################
1423              
1424             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1425             BEGIN
1426             {
1427             import XML::DOM::Node qw( :DEFAULT :Fields );
1428             XML::DOM::def_fields ("Target Data", "XML::DOM::Node");
1429             }
1430              
1431             use XML::DOM::DOMException;
1432             use Carp;
1433              
1434             sub new
1435             {
1436             my ($class, $doc, $target, $data, $hidden) = @_;
1437              
1438             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1439             "bad ProcessingInstruction Target [$target]")
1440             unless (XML::DOM::isValidName ($target) && $target !~ /^xml$/io);
1441              
1442             my $self = bless [], $class;
1443            
1444             $self->[_Doc] = $doc;
1445             $self->[_Target] = $target;
1446             $self->[_Data] = $data;
1447             $self->[_Hidden] = $hidden;
1448             $self;
1449             }
1450              
1451             sub getNodeType
1452             {
1453             PROCESSING_INSTRUCTION_NODE;
1454             }
1455              
1456             sub getTarget
1457             {
1458             $_[0]->[_Target];
1459             }
1460              
1461             sub getData
1462             {
1463             $_[0]->[_Data];
1464             }
1465              
1466             sub setData
1467             {
1468             my ($self, $data) = @_;
1469              
1470             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
1471             "node is ReadOnly")
1472             if $self->isReadOnly;
1473              
1474             $self->[_Data] = $data;
1475             }
1476              
1477             sub getNodeName
1478             {
1479             $_[0]->[_Target];
1480             }
1481              
1482             #
1483             # Same as getData
1484             #
1485             sub getNodeValue
1486             {
1487             $_[0]->[_Data];
1488             }
1489              
1490             sub setNodeValue
1491             {
1492             $_[0]->setData ($_[1]);
1493             }
1494              
1495             sub cloneNode
1496             {
1497             my $self = shift;
1498             $self->[_Doc]->createProcessingInstruction ($self->getTarget,
1499             $self->getData,
1500             $self->isHidden);
1501             }
1502              
1503             #------------------------------------------------------------
1504             # Extra method implementations
1505              
1506             sub isReadOnly
1507             {
1508             return 0 if $XML::DOM::IgnoreReadOnly;
1509              
1510             my $pa = $_[0]->[_Parent];
1511             defined ($pa) ? $pa->isReadOnly : 0;
1512             }
1513              
1514             sub print
1515             {
1516             my ($self, $FILE) = @_;
1517              
1518             $FILE->print ("
1519             $FILE->print ($self->[_Target]);
1520             $FILE->print (" ");
1521             $FILE->print (XML::DOM::encodeProcessingInstruction ($self->[_Data]));
1522             $FILE->print ("?>");
1523             }
1524              
1525             sub _to_sax {
1526             my ($self, $doch) = @_;
1527             $doch->processing_instruction({Target => $self->getTarget, Data => $self->getData});
1528             }
1529              
1530             ######################################################################
1531             package XML::DOM::Notation;
1532             ######################################################################
1533             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1534              
1535             BEGIN
1536             {
1537             import XML::DOM::Node qw( :DEFAULT :Fields );
1538             XML::DOM::def_fields ("Name Base SysId PubId", "XML::DOM::Node");
1539             }
1540              
1541             use XML::DOM::DOMException;
1542             use Carp;
1543              
1544             sub new
1545             {
1546             my ($class, $doc, $name, $base, $sysId, $pubId, $hidden) = @_;
1547              
1548             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1549             "bad Notation Name [$name]")
1550             unless XML::DOM::isValidName ($name);
1551              
1552             my $self = bless [], $class;
1553              
1554             $self->[_Doc] = $doc;
1555             $self->[_Name] = $name;
1556             $self->[_Base] = $base;
1557             $self->[_SysId] = $sysId;
1558             $self->[_PubId] = $pubId;
1559             $self->[_Hidden] = $hidden;
1560             $self;
1561             }
1562              
1563             sub getNodeType
1564             {
1565             NOTATION_NODE;
1566             }
1567              
1568             sub getPubId
1569             {
1570             $_[0]->[_PubId];
1571             }
1572              
1573             sub setPubId
1574             {
1575             $_[0]->[_PubId] = $_[1];
1576             }
1577              
1578             sub getSysId
1579             {
1580             $_[0]->[_SysId];
1581             }
1582              
1583             sub setSysId
1584             {
1585             $_[0]->[_SysId] = $_[1];
1586             }
1587              
1588             sub getName
1589             {
1590             $_[0]->[_Name];
1591             }
1592              
1593             sub setName
1594             {
1595             $_[0]->[_Name] = $_[1];
1596             }
1597              
1598             sub getBase
1599             {
1600             $_[0]->[_Base];
1601             }
1602              
1603             sub getNodeName
1604             {
1605             $_[0]->[_Name];
1606             }
1607              
1608             sub print
1609             {
1610             my ($self, $FILE) = @_;
1611              
1612             my $name = $self->[_Name];
1613             my $sysId = $self->[_SysId];
1614             my $pubId = $self->[_PubId];
1615              
1616             $FILE->print ("
1617              
1618             if (defined $pubId)
1619             {
1620             $FILE->print (" PUBLIC \"$pubId\"");
1621             }
1622             if (defined $sysId)
1623             {
1624             $FILE->print (" SYSTEM \"$sysId\"");
1625             }
1626             $FILE->print (">");
1627             }
1628              
1629             sub cloneNode
1630             {
1631             my ($self) = @_;
1632             $self->[_Doc]->createNotation ($self->[_Name], $self->[_Base],
1633             $self->[_SysId], $self->[_PubId],
1634             $self->[_Hidden]);
1635             }
1636              
1637             sub to_expat
1638             {
1639             my ($self, $iter) = @_;
1640             $iter->Notation ($self->getName, $self->getBase,
1641             $self->getSysId, $self->getPubId);
1642             }
1643              
1644             sub _to_sax
1645             {
1646             my ($self, $doch, $dtdh, $enth) = @_;
1647             $dtdh->notation_decl ( { Name => $self->getName,
1648             Base => $self->getBase,
1649             SystemId => $self->getSysId,
1650             PublicId => $self->getPubId });
1651             }
1652              
1653             ######################################################################
1654             package XML::DOM::Entity;
1655             ######################################################################
1656             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1657              
1658             BEGIN
1659             {
1660             import XML::DOM::Node qw( :DEFAULT :Fields );
1661             XML::DOM::def_fields ("NotationName Parameter Value Ndata SysId PubId", "XML::DOM::Node");
1662             }
1663              
1664             use XML::DOM::DOMException;
1665             use Carp;
1666              
1667             sub new
1668             {
1669             my ($class, $doc, $notationName, $value, $sysId, $pubId, $ndata, $isParam, $hidden) = @_;
1670              
1671             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1672             "bad Entity Name [$notationName]")
1673             unless XML::DOM::isValidName ($notationName);
1674              
1675             my $self = bless [], $class;
1676              
1677             $self->[_Doc] = $doc;
1678             $self->[_NotationName] = $notationName;
1679             $self->[_Parameter] = $isParam;
1680             $self->[_Value] = $value;
1681             $self->[_Ndata] = $ndata;
1682             $self->[_SysId] = $sysId;
1683             $self->[_PubId] = $pubId;
1684             $self->[_Hidden] = $hidden;
1685             $self;
1686             #?? maybe Value should be a Text node
1687             }
1688              
1689             sub getNodeType
1690             {
1691             ENTITY_NODE;
1692             }
1693              
1694             sub getPubId
1695             {
1696             $_[0]->[_PubId];
1697             }
1698              
1699             sub getSysId
1700             {
1701             $_[0]->[_SysId];
1702             }
1703              
1704             # Dom Spec says:
1705             # For unparsed entities, the name of the notation for the
1706             # entity. For parsed entities, this is null.
1707              
1708             #?? do we have unparsed entities?
1709             sub getNotationName
1710             {
1711             $_[0]->[_NotationName];
1712             }
1713              
1714             sub getNodeName
1715             {
1716             $_[0]->[_NotationName];
1717             }
1718              
1719             sub cloneNode
1720             {
1721             my $self = shift;
1722             $self->[_Doc]->createEntity ($self->[_NotationName], $self->[_Value],
1723             $self->[_SysId], $self->[_PubId],
1724             $self->[_Ndata], $self->[_Parameter], $self->[_Hidden]);
1725             }
1726              
1727             sub rejectChild
1728             {
1729             return 1;
1730             #?? if value is split over subnodes, recode this section
1731             # also add: C => new XML::DOM::NodeList,
1732              
1733             my $t = $_[1];
1734              
1735             return $t == TEXT_NODE
1736             || $t == ENTITY_REFERENCE_NODE
1737             || $t == PROCESSING_INSTRUCTION_NODE
1738             || $t == COMMENT_NODE
1739             || $t == CDATA_SECTION_NODE
1740             || $t == ELEMENT_NODE;
1741             }
1742              
1743             sub getValue
1744             {
1745             $_[0]->[_Value];
1746             }
1747              
1748             sub isParameterEntity
1749             {
1750             $_[0]->[_Parameter];
1751             }
1752              
1753             sub getNdata
1754             {
1755             $_[0]->[_Ndata];
1756             }
1757              
1758             sub print
1759             {
1760             my ($self, $FILE) = @_;
1761              
1762             my $name = $self->[_NotationName];
1763              
1764             my $par = $self->isParameterEntity ? "% " : "";
1765              
1766             $FILE->print ("
1767              
1768             my $value = $self->[_Value];
1769             my $sysId = $self->[_SysId];
1770             my $pubId = $self->[_PubId];
1771             my $ndata = $self->[_Ndata];
1772              
1773             if (defined $value)
1774             {
1775             #?? Not sure what to do if it contains both single and double quote
1776             $value = ($value =~ /\"/) ? "'$value'" : "\"$value\"";
1777             $FILE->print (" $value");
1778             }
1779             if (defined $pubId)
1780             {
1781             $FILE->print (" PUBLIC \"$pubId\"");
1782             }
1783             elsif (defined $sysId)
1784             {
1785             $FILE->print (" SYSTEM");
1786             }
1787              
1788             if (defined $sysId)
1789             {
1790             $FILE->print (" \"$sysId\"");
1791             }
1792             $FILE->print (" NDATA $ndata") if defined $ndata;
1793             $FILE->print (">");
1794             }
1795              
1796             sub to_expat
1797             {
1798             my ($self, $iter) = @_;
1799             my $name = ($self->isParameterEntity ? '%' : "") . $self->getNotationName;
1800             $iter->Entity ($name,
1801             $self->getValue, $self->getSysId, $self->getPubId,
1802             $self->getNdata);
1803             }
1804              
1805             sub _to_sax
1806             {
1807             my ($self, $doch, $dtdh, $enth) = @_;
1808             my $name = ($self->isParameterEntity ? '%' : "") . $self->getNotationName;
1809             $dtdh->entity_decl ( { Name => $name,
1810             Value => $self->getValue,
1811             SystemId => $self->getSysId,
1812             PublicId => $self->getPubId,
1813             Notation => $self->getNdata } );
1814             }
1815              
1816             ######################################################################
1817             package XML::DOM::EntityReference;
1818             ######################################################################
1819             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1820              
1821             BEGIN
1822             {
1823             import XML::DOM::Node qw( :DEFAULT :Fields );
1824             XML::DOM::def_fields ("EntityName Parameter NoExpand", "XML::DOM::Node");
1825             }
1826              
1827             use XML::DOM::DOMException;
1828             use Carp;
1829              
1830             sub new
1831             {
1832             my ($class, $doc, $name, $parameter, $noExpand) = @_;
1833              
1834             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1835             "bad Entity Name [$name] in EntityReference")
1836             unless XML::DOM::isValidName ($name);
1837              
1838             my $self = bless [], $class;
1839              
1840             $self->[_Doc] = $doc;
1841             $self->[_EntityName] = $name;
1842             $self->[_Parameter] = ($parameter || 0);
1843             $self->[_NoExpand] = ($noExpand || 0);
1844              
1845             $self;
1846             }
1847              
1848             sub getNodeType
1849             {
1850             ENTITY_REFERENCE_NODE;
1851             }
1852              
1853             sub getNodeName
1854             {
1855             $_[0]->[_EntityName];
1856             }
1857              
1858             #------------------------------------------------------------
1859             # Extra method implementations
1860              
1861             sub getEntityName
1862             {
1863             $_[0]->[_EntityName];
1864             }
1865              
1866             sub isParameterEntity
1867             {
1868             $_[0]->[_Parameter];
1869             }
1870              
1871             sub getData
1872             {
1873             my $self = shift;
1874             my $name = $self->[_EntityName];
1875             my $parameter = $self->[_Parameter];
1876              
1877             my $data;
1878             if ($self->[_NoExpand]) {
1879             $data = "&$name;" if $name;
1880             } else {
1881             $data = $self->[_Doc]->expandEntity ($name, $parameter);
1882             }
1883              
1884             unless (defined $data)
1885             {
1886             #?? this is probably an error, but perhaps requires check to NoExpand
1887             # will fix it?
1888             my $pc = $parameter ? "%" : "&";
1889             $data = "$pc$name;";
1890             }
1891             $data;
1892             }
1893              
1894             sub print
1895             {
1896             my ($self, $FILE) = @_;
1897              
1898             my $name = $self->[_EntityName];
1899              
1900             #?? or do we expand the entities?
1901              
1902             my $pc = $self->[_Parameter] ? "%" : "&";
1903             $FILE->print ("$pc$name;");
1904             }
1905              
1906             # Dom Spec says:
1907             # [...] but if such an Entity exists, then
1908             # the child list of the EntityReference node is the same as that of the
1909             # Entity node.
1910             #
1911             # The resolution of the children of the EntityReference (the replacement
1912             # value of the referenced Entity) may be lazily evaluated; actions by the
1913             # user (such as calling the childNodes method on the EntityReference
1914             # node) are assumed to trigger the evaluation.
1915             sub getChildNodes
1916             {
1917             my $self = shift;
1918             my $entity = $self->[_Doc]->getEntity ($self->[_EntityName]);
1919             defined ($entity) ? $entity->getChildNodes : new XML::DOM::NodeList;
1920             }
1921              
1922             sub cloneNode
1923             {
1924             my $self = shift;
1925             $self->[_Doc]->createEntityReference ($self->[_EntityName],
1926             $self->[_Parameter],
1927             $self->[_NoExpand],
1928             );
1929             }
1930              
1931             sub to_expat
1932             {
1933             my ($self, $iter) = @_;
1934             $iter->EntityRef ($self->getEntityName, $self->isParameterEntity);
1935             }
1936              
1937             sub _to_sax
1938             {
1939             my ($self, $doch, $dtdh, $enth) = @_;
1940             my @par = $self->isParameterEntity ? (Parameter => 1) : ();
1941             #?? not supported by PerlSAX: $self->isParameterEntity
1942              
1943             $doch->entity_reference ( { Name => $self->getEntityName, @par } );
1944             }
1945              
1946             # NOTE: an EntityReference can't really have children, so rejectChild
1947             # is not reimplemented (i.e. it always returns 0.)
1948              
1949             ######################################################################
1950             package XML::DOM::AttDef;
1951             ######################################################################
1952             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
1953              
1954             BEGIN
1955             {
1956             import XML::DOM::Node qw( :DEFAULT :Fields );
1957             XML::DOM::def_fields ("Name Type Fixed Default Required Implied Quote", "XML::DOM::Node");
1958             }
1959              
1960             use XML::DOM::DOMException;
1961             use Carp;
1962              
1963             #------------------------------------------------------------
1964             # Extra method implementations
1965              
1966             # AttDef is not part of DOM Spec
1967             sub new
1968             {
1969             my ($class, $doc, $name, $attrType, $default, $fixed, $hidden) = @_;
1970              
1971             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
1972             "bad Attr name in AttDef [$name]")
1973             unless XML::DOM::isValidName ($name);
1974              
1975             my $self = bless [], $class;
1976              
1977             $self->[_Doc] = $doc;
1978             $self->[_Name] = $name;
1979             $self->[_Type] = $attrType;
1980              
1981             if (defined $default)
1982             {
1983             if ($default eq "#REQUIRED")
1984             {
1985             $self->[_Required] = 1;
1986             }
1987             elsif ($default eq "#IMPLIED")
1988             {
1989             $self->[_Implied] = 1;
1990             }
1991             else
1992             {
1993             # strip off quotes - see Attlist handler in XML::Parser
1994             # this regexp doesn't work with 5.8.0 unicode
1995             # $default =~ m#^(["'])(.*)['"]$#;
1996             # $self->[_Quote] = $1; # keep track of the quote character
1997             # $self->[_Default] = $self->setDefault ($2);
1998              
1999             # workaround for 5.8.0 unicode
2000             $default =~ s!^(["'])!!;
2001             $self->[_Quote] = $1;
2002             $default =~ s!(["'])$!!;
2003             $self->[_Default] = $self->setDefault ($default);
2004            
2005             #?? should default value be decoded - what if it contains e.g. "&"
2006             }
2007             }
2008             $self->[_Fixed] = $fixed if defined $fixed;
2009             $self->[_Hidden] = $hidden if defined $hidden;
2010              
2011             $self;
2012             }
2013              
2014             sub getNodeType
2015             {
2016             ATT_DEF_NODE;
2017             }
2018              
2019             sub getName
2020             {
2021             $_[0]->[_Name];
2022             }
2023              
2024             # So it can be added to a NamedNodeMap
2025             sub getNodeName
2026             {
2027             $_[0]->[_Name];
2028             }
2029              
2030             sub getType
2031             {
2032             $_[0]->[_Type];
2033             }
2034              
2035             sub setType
2036             {
2037             $_[0]->[_Type] = $_[1];
2038             }
2039              
2040             sub getDefault
2041             {
2042             $_[0]->[_Default];
2043             }
2044              
2045             sub setDefault
2046             {
2047             my ($self, $value) = @_;
2048              
2049             # specified=0, it's the default !
2050             my $attr = $self->[_Doc]->createAttribute ($self->[_Name], undef, 0);
2051             $attr->[_ReadOnly] = 1;
2052              
2053             #?? this should be split over Text and EntityReference nodes, just like other
2054             # Attr nodes - just expand the text for now
2055             $value = $self->expandEntityRefs ($value);
2056             $attr->addText ($value);
2057             #?? reimplement in NoExpand mode!
2058              
2059             $attr;
2060             }
2061              
2062             sub isFixed
2063             {
2064             $_[0]->[_Fixed] || 0;
2065             }
2066              
2067             sub isRequired
2068             {
2069             $_[0]->[_Required] || 0;
2070             }
2071              
2072             sub isImplied
2073             {
2074             $_[0]->[_Implied] || 0;
2075             }
2076              
2077             sub print
2078             {
2079             my ($self, $FILE) = @_;
2080              
2081             my $name = $self->[_Name];
2082             my $type = $self->[_Type];
2083             my $fixed = $self->[_Fixed];
2084             my $default = $self->[_Default];
2085              
2086             # $FILE->print ("$name $type");
2087             # replaced line above with the two lines below
2088             # seems to be a bug in perl 5.6.0 that causes
2089             # test 3 of dom_jp_attr.t to fail?
2090             $FILE->print ($name);
2091             $FILE->print (" $type");
2092              
2093             $FILE->print (" #FIXED") if defined $fixed;
2094              
2095             if ($self->[_Required])
2096             {
2097             $FILE->print (" #REQUIRED");
2098             }
2099             elsif ($self->[_Implied])
2100             {
2101             $FILE->print (" #IMPLIED");
2102             }
2103             elsif (defined ($default))
2104             {
2105             my $quote = $self->[_Quote];
2106             $FILE->print (" $quote");
2107             for my $kid (@{$default->[_C]})
2108             {
2109             $kid->print ($FILE);
2110             }
2111             $FILE->print ($quote);
2112             }
2113             }
2114              
2115             sub getDefaultString
2116             {
2117             my $self = shift;
2118             my $default;
2119              
2120             if ($self->[_Required])
2121             {
2122             return "#REQUIRED";
2123             }
2124             elsif ($self->[_Implied])
2125             {
2126             return "#IMPLIED";
2127             }
2128             elsif (defined ($default = $self->[_Default]))
2129             {
2130             my $quote = $self->[_Quote];
2131             $default = $default->toString;
2132             return "$quote$default$quote";
2133             }
2134             undef;
2135             }
2136              
2137             sub cloneNode
2138             {
2139             my $self = shift;
2140             my $node = new XML::DOM::AttDef ($self->[_Doc], $self->[_Name], $self->[_Type],
2141             undef, $self->[_Fixed]);
2142              
2143             $node->[_Required] = 1 if $self->[_Required];
2144             $node->[_Implied] = 1 if $self->[_Implied];
2145             $node->[_Fixed] = $self->[_Fixed] if defined $self->[_Fixed];
2146             $node->[_Hidden] = $self->[_Hidden] if defined $self->[_Hidden];
2147              
2148             if (defined $self->[_Default])
2149             {
2150             $node->[_Default] = $self->[_Default]->cloneNode(1);
2151             }
2152             $node->[_Quote] = $self->[_Quote];
2153              
2154             $node;
2155             }
2156              
2157             sub setOwnerDocument
2158             {
2159             my ($self, $doc) = @_;
2160             $self->SUPER::setOwnerDocument ($doc);
2161              
2162             if (defined $self->[_Default])
2163             {
2164             $self->[_Default]->setOwnerDocument ($doc);
2165             }
2166             }
2167              
2168             ######################################################################
2169             package XML::DOM::AttlistDecl;
2170             ######################################################################
2171             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
2172              
2173             BEGIN
2174             {
2175             import XML::DOM::Node qw( :DEFAULT :Fields );
2176             import XML::DOM::AttDef qw{ :Fields };
2177              
2178             XML::DOM::def_fields ("ElementName", "XML::DOM::Node");
2179             }
2180              
2181             use XML::DOM::DOMException;
2182             use Carp;
2183              
2184             #------------------------------------------------------------
2185             # Extra method implementations
2186              
2187             # AttlistDecl is not part of the DOM Spec
2188             sub new
2189             {
2190             my ($class, $doc, $name) = @_;
2191              
2192             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
2193             "bad Element TagName [$name] in AttlistDecl")
2194             unless XML::DOM::isValidName ($name);
2195              
2196             my $self = bless [], $class;
2197              
2198             $self->[_Doc] = $doc;
2199             $self->[_C] = new XML::DOM::NodeList;
2200             $self->[_ReadOnly] = 1;
2201             $self->[_ElementName] = $name;
2202              
2203             $self->[_A] = new XML::DOM::NamedNodeMap (Doc => $doc,
2204             ReadOnly => 1,
2205             Parent => $self);
2206              
2207             $self;
2208             }
2209              
2210             sub getNodeType
2211             {
2212             ATTLIST_DECL_NODE;
2213             }
2214              
2215             sub getName
2216             {
2217             $_[0]->[_ElementName];
2218             }
2219              
2220             sub getNodeName
2221             {
2222             $_[0]->[_ElementName];
2223             }
2224              
2225             sub getAttDef
2226             {
2227             my ($self, $attrName) = @_;
2228             $self->[_A]->getNamedItem ($attrName);
2229             }
2230              
2231             sub addAttDef
2232             {
2233             my ($self, $attrName, $type, $default, $fixed, $hidden) = @_;
2234             my $node = $self->getAttDef ($attrName);
2235              
2236             if (defined $node)
2237             {
2238             # data will be ignored if already defined
2239             my $elemName = $self->getName;
2240             XML::DOM::warning ("multiple definitions of attribute $attrName for element $elemName, only first one is recognized");
2241             }
2242             else
2243             {
2244             $node = new XML::DOM::AttDef ($self->[_Doc], $attrName, $type,
2245             $default, $fixed, $hidden);
2246             $self->[_A]->setNamedItem ($node);
2247             }
2248             $node;
2249             }
2250              
2251             sub getDefaultAttrValue
2252             {
2253             my ($self, $attr) = @_;
2254             my $attrNode = $self->getAttDef ($attr);
2255             (defined $attrNode) ? $attrNode->getDefault : undef;
2256             }
2257              
2258             sub cloneNode
2259             {
2260             my ($self, $deep) = @_;
2261             my $node = $self->[_Doc]->createAttlistDecl ($self->[_ElementName]);
2262            
2263             $node->[_A] = $self->[_A]->cloneNode ($deep);
2264             $node;
2265             }
2266              
2267             sub setOwnerDocument
2268             {
2269             my ($self, $doc) = @_;
2270             $self->SUPER::setOwnerDocument ($doc);
2271              
2272             $self->[_A]->setOwnerDocument ($doc);
2273             }
2274              
2275             sub print
2276             {
2277             my ($self, $FILE) = @_;
2278              
2279             my $name = $self->getName;
2280             my @attlist = @{$self->[_A]->getValues};
2281              
2282             my $hidden = 1;
2283             for my $att (@attlist)
2284             {
2285             unless ($att->[_Hidden])
2286             {
2287             $hidden = 0;
2288             last;
2289             }
2290             }
2291              
2292             unless ($hidden)
2293             {
2294             $FILE->print ("
2295              
2296             if (@attlist == 1)
2297             {
2298             $FILE->print (" ");
2299             $attlist[0]->print ($FILE);
2300             }
2301             else
2302             {
2303             for my $attr (@attlist)
2304             {
2305             next if $attr->[_Hidden];
2306              
2307             $FILE->print ("\x0A ");
2308             $attr->print ($FILE);
2309             }
2310             }
2311             $FILE->print (">");
2312             }
2313             }
2314              
2315             sub to_expat
2316             {
2317             my ($self, $iter) = @_;
2318             my $tag = $self->getName;
2319             for my $a ($self->[_A]->getValues)
2320             {
2321             my $default = $a->isImplied ? '#IMPLIED' :
2322             ($a->isRequired ? '#REQUIRED' :
2323             ($a->[_Quote] . $a->getDefault->getValue . $a->[_Quote]));
2324              
2325             $iter->Attlist ($tag, $a->getName, $a->getType, $default, $a->isFixed);
2326             }
2327             }
2328              
2329             sub _to_sax
2330             {
2331             my ($self, $doch, $dtdh, $enth) = @_;
2332             my $tag = $self->getName;
2333             for my $a ($self->[_A]->getValues)
2334             {
2335             my $default = $a->isImplied ? '#IMPLIED' :
2336             ($a->isRequired ? '#REQUIRED' :
2337             ($a->[_Quote] . $a->getDefault->getValue . $a->[_Quote]));
2338              
2339             $dtdh->attlist_decl ({ ElementName => $tag,
2340             AttributeName => $a->getName,
2341             Type => $a->[_Type],
2342             Default => $default,
2343             Fixed => $a->isFixed });
2344             }
2345             }
2346              
2347             ######################################################################
2348             package XML::DOM::ElementDecl;
2349             ######################################################################
2350             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
2351              
2352             BEGIN
2353             {
2354             import XML::DOM::Node qw( :DEFAULT :Fields );
2355             XML::DOM::def_fields ("Name Model", "XML::DOM::Node");
2356             }
2357              
2358             use XML::DOM::DOMException;
2359             use Carp;
2360              
2361              
2362             #------------------------------------------------------------
2363             # Extra method implementations
2364              
2365             # ElementDecl is not part of the DOM Spec
2366             sub new
2367             {
2368             my ($class, $doc, $name, $model, $hidden) = @_;
2369              
2370             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
2371             "bad Element TagName [$name] in ElementDecl")
2372             unless XML::DOM::isValidName ($name);
2373              
2374             my $self = bless [], $class;
2375              
2376             $self->[_Doc] = $doc;
2377             $self->[_Name] = $name;
2378             $self->[_ReadOnly] = 1;
2379             $self->[_Model] = $model;
2380             $self->[_Hidden] = $hidden;
2381             $self;
2382             }
2383              
2384             sub getNodeType
2385             {
2386             ELEMENT_DECL_NODE;
2387             }
2388              
2389             sub getName
2390             {
2391             $_[0]->[_Name];
2392             }
2393              
2394             sub getNodeName
2395             {
2396             $_[0]->[_Name];
2397             }
2398              
2399             sub getModel
2400             {
2401             $_[0]->[_Model];
2402             }
2403              
2404             sub setModel
2405             {
2406             my ($self, $model) = @_;
2407              
2408             $self->[_Model] = $model;
2409             }
2410              
2411             sub print
2412             {
2413             my ($self, $FILE) = @_;
2414              
2415             my $name = $self->[_Name];
2416             my $model = $self->[_Model];
2417              
2418             $FILE->print ("")
2419             unless $self->[_Hidden];
2420             }
2421              
2422             sub cloneNode
2423             {
2424             my $self = shift;
2425             $self->[_Doc]->createElementDecl ($self->[_Name], $self->[_Model],
2426             $self->[_Hidden]);
2427             }
2428              
2429             sub to_expat
2430             {
2431             #?? add support for Hidden?? (allover, also in _to_sax!!)
2432              
2433             my ($self, $iter) = @_;
2434             $iter->Element ($self->getName, $self->getModel);
2435             }
2436              
2437             sub _to_sax
2438             {
2439             my ($self, $doch, $dtdh, $enth) = @_;
2440             $dtdh->element_decl ( { Name => $self->getName,
2441             Model => $self->getModel } );
2442             }
2443              
2444             ######################################################################
2445             package XML::DOM::Element;
2446             ######################################################################
2447             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
2448              
2449             BEGIN
2450             {
2451             import XML::DOM::Node qw( :DEFAULT :Fields );
2452             XML::DOM::def_fields ("TagName", "XML::DOM::Node");
2453             }
2454              
2455             use XML::DOM::DOMException;
2456             use XML::DOM::NamedNodeMap;
2457             use Carp;
2458              
2459             sub new
2460             {
2461             my ($class, $doc, $tagName) = @_;
2462              
2463             if ($XML::DOM::SafeMode)
2464             {
2465             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
2466             "bad Element TagName [$tagName]")
2467             unless XML::DOM::isValidName ($tagName);
2468             }
2469              
2470             my $self = bless [], $class;
2471              
2472             $self->[_Doc] = $doc;
2473             $self->[_C] = new XML::DOM::NodeList;
2474             $self->[_TagName] = $tagName;
2475              
2476             # Now we're creating the NamedNodeMap only when needed (REC 2313 => 1147)
2477             # $self->[_A] = new XML::DOM::NamedNodeMap (Doc => $doc,
2478             # Parent => $self);
2479              
2480             $self;
2481             }
2482              
2483             sub getNodeType
2484             {
2485             ELEMENT_NODE;
2486             }
2487              
2488             sub getTagName
2489             {
2490             $_[0]->[_TagName];
2491             }
2492              
2493             sub getNodeName
2494             {
2495             $_[0]->[_TagName];
2496             }
2497              
2498             sub getAttributeNode
2499             {
2500             my ($self, $name) = @_;
2501             return undef unless defined $self->[_A];
2502              
2503             $self->getAttributes->{$name};
2504             }
2505              
2506             sub getAttribute
2507             {
2508             my ($self, $name) = @_;
2509             my $attr = $self->getAttributeNode ($name);
2510             (defined $attr) ? $attr->getValue : "";
2511             }
2512              
2513             sub setAttribute
2514             {
2515             my ($self, $name, $val) = @_;
2516              
2517             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
2518             "bad Attr Name [$name]")
2519             unless XML::DOM::isValidName ($name);
2520              
2521             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2522             "node is ReadOnly")
2523             if $self->isReadOnly;
2524              
2525             my $node = $self->getAttributes->{$name};
2526             if (defined $node)
2527             {
2528             $node->setValue ($val);
2529             }
2530             else
2531             {
2532             $node = $self->[_Doc]->createAttribute ($name, $val);
2533             $self->[_A]->setNamedItem ($node);
2534             }
2535             }
2536              
2537             sub setAttributeNode
2538             {
2539             my ($self, $node) = @_;
2540             my $attr = $self->getAttributes;
2541             my $name = $node->getNodeName;
2542              
2543             # REC 1147
2544             if ($XML::DOM::SafeMode)
2545             {
2546             croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR,
2547             "nodes belong to different documents")
2548             if $self->[_Doc] != $node->[_Doc];
2549              
2550             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2551             "node is ReadOnly")
2552             if $self->isReadOnly;
2553              
2554             my $attrParent = $node->[_UsedIn];
2555             croak new XML::DOM::DOMException (INUSE_ATTRIBUTE_ERR,
2556             "Attr is already used by another Element")
2557             if (defined ($attrParent) && $attrParent != $attr);
2558             }
2559              
2560             my $other = $attr->{$name};
2561             $attr->removeNamedItem ($name) if defined $other;
2562              
2563             $attr->setNamedItem ($node);
2564              
2565             $other;
2566             }
2567              
2568             sub removeAttributeNode
2569             {
2570             my ($self, $node) = @_;
2571              
2572             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2573             "node is ReadOnly")
2574             if $self->isReadOnly;
2575              
2576             my $attr = $self->[_A];
2577             unless (defined $attr)
2578             {
2579             croak new XML::DOM::DOMException (NOT_FOUND_ERR);
2580             return undef;
2581             }
2582              
2583             my $name = $node->getNodeName;
2584             my $attrNode = $attr->getNamedItem ($name);
2585              
2586             #?? should it croak if it's the default value?
2587             croak new XML::DOM::DOMException (NOT_FOUND_ERR)
2588             unless $node == $attrNode;
2589              
2590             # Not removing anything if it's the default value already
2591             return undef unless $node->isSpecified;
2592              
2593             $attr->removeNamedItem ($name);
2594              
2595             # Substitute with default value if it's defined
2596             my $default = $self->getDefaultAttrValue ($name);
2597             if (defined $default)
2598             {
2599             local $XML::DOM::IgnoreReadOnly = 1;
2600              
2601             $default = $default->cloneNode (1);
2602             $attr->setNamedItem ($default);
2603             }
2604             $node;
2605             }
2606              
2607             sub removeAttribute
2608             {
2609             my ($self, $name) = @_;
2610             my $attr = $self->[_A];
2611             unless (defined $attr)
2612             {
2613             croak new XML::DOM::DOMException (NOT_FOUND_ERR);
2614             return;
2615             }
2616            
2617             my $node = $attr->getNamedItem ($name);
2618             if (defined $node)
2619             {
2620             #?? could use dispose() to remove circular references for gc, but what if
2621             #?? somebody is referencing it?
2622             $self->removeAttributeNode ($node);
2623             }
2624             }
2625              
2626             sub cloneNode
2627             {
2628             my ($self, $deep) = @_;
2629             my $node = $self->[_Doc]->createElement ($self->getTagName);
2630              
2631             # Always clone the Attr nodes, even if $deep == 0
2632             if (defined $self->[_A])
2633             {
2634             $node->[_A] = $self->[_A]->cloneNode (1); # deep=1
2635             $node->[_A]->setParentNode ($node);
2636             }
2637              
2638             $node->cloneChildren ($self, $deep);
2639             $node;
2640             }
2641              
2642             sub getAttributes
2643             {
2644             $_[0]->[_A] ||= XML::DOM::NamedNodeMap->new (Doc => $_[0]->[_Doc],
2645             Parent => $_[0]);
2646             }
2647              
2648             #------------------------------------------------------------
2649             # Extra method implementations
2650              
2651             # Added for convenience
2652             sub setTagName
2653             {
2654             my ($self, $tagName) = @_;
2655              
2656             croak new XML::DOM::DOMException (INVALID_CHARACTER_ERR,
2657             "bad Element TagName [$tagName]")
2658             unless XML::DOM::isValidName ($tagName);
2659              
2660             $self->[_TagName] = $tagName;
2661             }
2662              
2663             sub isReadOnly
2664             {
2665             0;
2666             }
2667              
2668             # Added for optimization.
2669             sub isElementNode
2670             {
2671             1;
2672             }
2673              
2674             sub rejectChild
2675             {
2676             my $t = $_[1]->getNodeType;
2677              
2678             $t != TEXT_NODE
2679             && $t != ENTITY_REFERENCE_NODE
2680             && $t != PROCESSING_INSTRUCTION_NODE
2681             && $t != COMMENT_NODE
2682             && $t != CDATA_SECTION_NODE
2683             && $t != ELEMENT_NODE;
2684             }
2685              
2686             sub getDefaultAttrValue
2687             {
2688             my ($self, $attr) = @_;
2689             $self->[_Doc]->getDefaultAttrValue ($self->[_TagName], $attr);
2690             }
2691              
2692             sub dispose
2693             {
2694             my $self = shift;
2695              
2696             $self->[_A]->dispose if defined $self->[_A];
2697             $self->SUPER::dispose;
2698             }
2699              
2700             sub setOwnerDocument
2701             {
2702             my ($self, $doc) = @_;
2703             $self->SUPER::setOwnerDocument ($doc);
2704              
2705             $self->[_A]->setOwnerDocument ($doc) if defined $self->[_A];
2706             }
2707              
2708             sub print
2709             {
2710             my ($self, $FILE) = @_;
2711              
2712             my $name = $self->[_TagName];
2713              
2714             $FILE->print ("<$name");
2715              
2716             if (defined $self->[_A])
2717             {
2718             for my $att (@{$self->[_A]->getValues})
2719             {
2720             # skip un-specified (default) Attr nodes
2721             if ($att->isSpecified)
2722             {
2723             $FILE->print (" ");
2724             $att->print ($FILE);
2725             }
2726             }
2727             }
2728              
2729             my @kids = @{$self->[_C]};
2730             if (@kids > 0)
2731             {
2732             $FILE->print (">");
2733             for my $kid (@kids)
2734             {
2735             $kid->print ($FILE);
2736             }
2737             $FILE->print ("");
2738             }
2739             else
2740             {
2741             my $style = &$XML::DOM::TagStyle ($name, $self);
2742             if ($style == 0)
2743             {
2744             $FILE->print ("/>");
2745             }
2746             elsif ($style == 1)
2747             {
2748             $FILE->print (">");
2749             }
2750             else
2751             {
2752             $FILE->print (" />");
2753             }
2754             }
2755             }
2756              
2757             sub check
2758             {
2759             my ($self, $checker) = @_;
2760             die "Usage: \$xml_dom_elem->check (\$checker)" unless $checker;
2761              
2762             $checker->InitDomElem;
2763             $self->to_expat ($checker);
2764             $checker->FinalDomElem;
2765             }
2766              
2767             sub to_expat
2768             {
2769             my ($self, $iter) = @_;
2770              
2771             my $tag = $self->getTagName;
2772             $iter->Start ($tag);
2773              
2774             if (defined $self->[_A])
2775             {
2776             for my $attr ($self->[_A]->getValues)
2777             {
2778             $iter->Attr ($tag, $attr->getName, $attr->getValue, $attr->isSpecified);
2779             }
2780             }
2781              
2782             $iter->EndAttr;
2783              
2784             for my $kid ($self->getChildNodes)
2785             {
2786             $kid->to_expat ($iter);
2787             }
2788              
2789             $iter->End;
2790             }
2791              
2792             sub _to_sax
2793             {
2794             my ($self, $doch, $dtdh, $enth) = @_;
2795              
2796             my $tag = $self->getTagName;
2797              
2798             my @attr = ();
2799             my $attrOrder;
2800             my $attrDefaulted;
2801              
2802             if (defined $self->[_A])
2803             {
2804             my @spec = (); # names of specified attributes
2805             my @unspec = (); # names of defaulted attributes
2806              
2807             for my $attr ($self->[_A]->getValues)
2808             {
2809             my $attrName = $attr->getName;
2810             push @attr, $attrName, $attr->getValue;
2811             if ($attr->isSpecified)
2812             {
2813             push @spec, $attrName;
2814             }
2815             else
2816             {
2817             push @unspec, $attrName;
2818             }
2819             }
2820             $attrOrder = [ @spec, @unspec ];
2821             $attrDefaulted = @spec;
2822             }
2823             $doch->start_element (defined $attrOrder ?
2824             { Name => $tag,
2825             Attributes => { @attr },
2826             AttributeOrder => $attrOrder,
2827             Defaulted => $attrDefaulted
2828             } :
2829             { Name => $tag,
2830             Attributes => { @attr }
2831             }
2832             );
2833              
2834             for my $kid ($self->getChildNodes)
2835             {
2836             $kid->_to_sax ($doch, $dtdh, $enth);
2837             }
2838              
2839             $doch->end_element ( { Name => $tag } );
2840             }
2841              
2842             ######################################################################
2843             package XML::DOM::CharacterData;
2844             ######################################################################
2845             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
2846              
2847             BEGIN
2848             {
2849             import XML::DOM::Node qw( :DEFAULT :Fields );
2850             XML::DOM::def_fields ("Data", "XML::DOM::Node");
2851             }
2852              
2853             use XML::DOM::DOMException;
2854             use Carp;
2855              
2856              
2857             #
2858             # CharacterData nodes should never be created directly, only subclassed!
2859             #
2860             sub new
2861             {
2862             my ($class, $doc, $data) = @_;
2863             my $self = bless [], $class;
2864              
2865             $self->[_Doc] = $doc;
2866             $self->[_Data] = $data;
2867             $self;
2868             }
2869              
2870             sub appendData
2871             {
2872             my ($self, $data) = @_;
2873              
2874             if ($XML::DOM::SafeMode)
2875             {
2876             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2877             "node is ReadOnly")
2878             if $self->isReadOnly;
2879             }
2880             $self->[_Data] .= $data;
2881             }
2882              
2883             sub deleteData
2884             {
2885             my ($self, $offset, $count) = @_;
2886              
2887             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2888             "bad offset [$offset]")
2889             if ($offset < 0 || $offset >= length ($self->[_Data]));
2890             #?? DOM Spec says >, but >= makes more sense!
2891              
2892             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2893             "negative count [$count]")
2894             if $count < 0;
2895            
2896             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2897             "node is ReadOnly")
2898             if $self->isReadOnly;
2899              
2900             substr ($self->[_Data], $offset, $count) = "";
2901             }
2902              
2903             sub getData
2904             {
2905             $_[0]->[_Data];
2906             }
2907              
2908             sub getLength
2909             {
2910             length $_[0]->[_Data];
2911             }
2912              
2913             sub insertData
2914             {
2915             my ($self, $offset, $data) = @_;
2916              
2917             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2918             "bad offset [$offset]")
2919             if ($offset < 0 || $offset >= length ($self->[_Data]));
2920             #?? DOM Spec says >, but >= makes more sense!
2921              
2922             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2923             "node is ReadOnly")
2924             if $self->isReadOnly;
2925              
2926             substr ($self->[_Data], $offset, 0) = $data;
2927             }
2928              
2929             sub replaceData
2930             {
2931             my ($self, $offset, $count, $data) = @_;
2932              
2933             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2934             "bad offset [$offset]")
2935             if ($offset < 0 || $offset >= length ($self->[_Data]));
2936             #?? DOM Spec says >, but >= makes more sense!
2937              
2938             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2939             "negative count [$count]")
2940             if $count < 0;
2941            
2942             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2943             "node is ReadOnly")
2944             if $self->isReadOnly;
2945              
2946             substr ($self->[_Data], $offset, $count) = $data;
2947             }
2948              
2949             sub setData
2950             {
2951             my ($self, $data) = @_;
2952              
2953             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
2954             "node is ReadOnly")
2955             if $self->isReadOnly;
2956              
2957             $self->[_Data] = $data;
2958             }
2959              
2960             sub substringData
2961             {
2962             my ($self, $offset, $count) = @_;
2963             my $data = $self->[_Data];
2964              
2965             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2966             "bad offset [$offset]")
2967             if ($offset < 0 || $offset >= length ($data));
2968             #?? DOM Spec says >, but >= makes more sense!
2969              
2970             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
2971             "negative count [$count]")
2972             if $count < 0;
2973            
2974             substr ($data, $offset, $count);
2975             }
2976              
2977             sub getNodeValue
2978             {
2979             $_[0]->getData;
2980             }
2981              
2982             sub setNodeValue
2983             {
2984             $_[0]->setData ($_[1]);
2985             }
2986              
2987             ######################################################################
2988             package XML::DOM::CDATASection;
2989             ######################################################################
2990             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
2991              
2992             BEGIN
2993             {
2994             import XML::DOM::CharacterData qw( :DEFAULT :Fields );
2995             import XML::DOM::Node qw( :DEFAULT :Fields );
2996             XML::DOM::def_fields ("", "XML::DOM::CharacterData");
2997             }
2998              
2999             use XML::DOM::DOMException;
3000              
3001             sub getNodeName
3002             {
3003             "#cdata-section";
3004             }
3005              
3006             sub getNodeType
3007             {
3008             CDATA_SECTION_NODE;
3009             }
3010              
3011             sub cloneNode
3012             {
3013             my $self = shift;
3014             $self->[_Doc]->createCDATASection ($self->getData);
3015             }
3016              
3017             #------------------------------------------------------------
3018             # Extra method implementations
3019              
3020             sub isReadOnly
3021             {
3022             0;
3023             }
3024              
3025             sub print
3026             {
3027             my ($self, $FILE) = @_;
3028             $FILE->print ("
3029             $FILE->print (XML::DOM::encodeCDATA ($self->getData));
3030             $FILE->print ("]]>");
3031             }
3032              
3033             sub to_expat
3034             {
3035             my ($self, $iter) = @_;
3036             $iter->CData ($self->getData);
3037             }
3038              
3039             sub _to_sax
3040             {
3041             my ($self, $doch, $dtdh, $enth) = @_;
3042             $doch->start_cdata;
3043             $doch->characters ( { Data => $self->getData } );
3044             $doch->end_cdata;
3045             }
3046              
3047             ######################################################################
3048             package XML::DOM::Comment;
3049             ######################################################################
3050             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3051              
3052             BEGIN
3053             {
3054             import XML::DOM::CharacterData qw( :DEFAULT :Fields );
3055             import XML::DOM::Node qw( :DEFAULT :Fields );
3056             XML::DOM::def_fields ("", "XML::DOM::CharacterData");
3057             }
3058              
3059             use XML::DOM::DOMException;
3060             use Carp;
3061              
3062             #?? setData - could check comment for double minus
3063              
3064             sub getNodeType
3065             {
3066             COMMENT_NODE;
3067             }
3068              
3069             sub getNodeName
3070             {
3071             "#comment";
3072             }
3073              
3074             sub cloneNode
3075             {
3076             my $self = shift;
3077             $self->[_Doc]->createComment ($self->getData);
3078             }
3079              
3080             #------------------------------------------------------------
3081             # Extra method implementations
3082              
3083             sub isReadOnly
3084             {
3085             return 0 if $XML::DOM::IgnoreReadOnly;
3086              
3087             my $pa = $_[0]->[_Parent];
3088             defined ($pa) ? $pa->isReadOnly : 0;
3089             }
3090              
3091             sub print
3092             {
3093             my ($self, $FILE) = @_;
3094             my $comment = XML::DOM::encodeComment ($self->[_Data]);
3095              
3096             $FILE->print ("");
3097             }
3098              
3099             sub to_expat
3100             {
3101             my ($self, $iter) = @_;
3102             $iter->Comment ($self->getData);
3103             }
3104              
3105             sub _to_sax
3106             {
3107             my ($self, $doch, $dtdh, $enth) = @_;
3108             $doch->comment ( { Data => $self->getData });
3109             }
3110              
3111             ######################################################################
3112             package XML::DOM::Text;
3113             ######################################################################
3114             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3115              
3116             BEGIN
3117             {
3118             import XML::DOM::CharacterData qw( :DEFAULT :Fields );
3119             import XML::DOM::Node qw( :DEFAULT :Fields );
3120             XML::DOM::def_fields ("", "XML::DOM::CharacterData");
3121             }
3122              
3123             use XML::DOM::DOMException;
3124             use Carp;
3125              
3126             sub getNodeType
3127             {
3128             TEXT_NODE;
3129             }
3130              
3131             sub getNodeName
3132             {
3133             "#text";
3134             }
3135              
3136             sub splitText
3137             {
3138             my ($self, $offset) = @_;
3139              
3140             my $data = $self->getData;
3141             croak new XML::DOM::DOMException (INDEX_SIZE_ERR,
3142             "bad offset [$offset]")
3143             if ($offset < 0 || $offset >= length ($data));
3144             #?? DOM Spec says >, but >= makes more sense!
3145              
3146             croak new XML::DOM::DOMException (NO_MODIFICATION_ALLOWED_ERR,
3147             "node is ReadOnly")
3148             if $self->isReadOnly;
3149              
3150             my $rest = substr ($data, $offset);
3151              
3152             $self->setData (substr ($data, 0, $offset));
3153             my $node = $self->[_Doc]->createTextNode ($rest);
3154              
3155             # insert new node after this node
3156             $self->[_Parent]->insertBefore ($node, $self->getNextSibling);
3157              
3158             $node;
3159             }
3160              
3161             sub cloneNode
3162             {
3163             my $self = shift;
3164             $self->[_Doc]->createTextNode ($self->getData);
3165             }
3166              
3167             #------------------------------------------------------------
3168             # Extra method implementations
3169              
3170             sub isReadOnly
3171             {
3172             0;
3173             }
3174              
3175             sub print
3176             {
3177             my ($self, $FILE) = @_;
3178             $FILE->print (XML::DOM::encodeText ($self->getData, '<&>"'));
3179             }
3180              
3181             sub isTextNode
3182             {
3183             1;
3184             }
3185              
3186             sub to_expat
3187             {
3188             my ($self, $iter) = @_;
3189             $iter->Char ($self->getData);
3190             }
3191              
3192             sub _to_sax
3193             {
3194             my ($self, $doch, $dtdh, $enth) = @_;
3195             $doch->characters ( { Data => $self->getData } );
3196             }
3197              
3198             ######################################################################
3199             package XML::DOM::XMLDecl;
3200             ######################################################################
3201             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3202              
3203             BEGIN
3204             {
3205             import XML::DOM::Node qw( :DEFAULT :Fields );
3206             XML::DOM::def_fields ("Version Encoding Standalone", "XML::DOM::Node");
3207             }
3208              
3209             use XML::DOM::DOMException;
3210              
3211              
3212             #------------------------------------------------------------
3213             # Extra method implementations
3214              
3215             # XMLDecl is not part of the DOM Spec
3216             sub new
3217             {
3218             my ($class, $doc, $version, $encoding, $standalone) = @_;
3219              
3220             my $self = bless [], $class;
3221              
3222             $self->[_Doc] = $doc;
3223             $self->[_Version] = $version if defined $version;
3224             $self->[_Encoding] = $encoding if defined $encoding;
3225             $self->[_Standalone] = $standalone if defined $standalone;
3226              
3227             $self;
3228             }
3229              
3230             sub setVersion
3231             {
3232             if (defined $_[1])
3233             {
3234             $_[0]->[_Version] = $_[1];
3235             }
3236             else
3237             {
3238             undef $_[0]->[_Version]; # was delete
3239             }
3240             }
3241              
3242             sub getVersion
3243             {
3244             $_[0]->[_Version];
3245             }
3246              
3247             sub setEncoding
3248             {
3249             if (defined $_[1])
3250             {
3251             $_[0]->[_Encoding] = $_[1];
3252             }
3253             else
3254             {
3255             undef $_[0]->[_Encoding]; # was delete
3256             }
3257             }
3258              
3259             sub getEncoding
3260             {
3261             $_[0]->[_Encoding];
3262             }
3263              
3264             sub setStandalone
3265             {
3266             if (defined $_[1])
3267             {
3268             $_[0]->[_Standalone] = $_[1];
3269             }
3270             else
3271             {
3272             undef $_[0]->[_Standalone]; # was delete
3273             }
3274             }
3275              
3276             sub getStandalone
3277             {
3278             $_[0]->[_Standalone];
3279             }
3280              
3281             sub getNodeType
3282             {
3283             XML_DECL_NODE;
3284             }
3285              
3286             sub cloneNode
3287             {
3288             my $self = shift;
3289              
3290             new XML::DOM::XMLDecl ($self->[_Doc], $self->[_Version],
3291             $self->[_Encoding], $self->[_Standalone]);
3292             }
3293              
3294             sub print
3295             {
3296             my ($self, $FILE) = @_;
3297              
3298             my $version = $self->[_Version];
3299             my $encoding = $self->[_Encoding];
3300             my $standalone = $self->[_Standalone];
3301             $standalone = ($standalone ? "yes" : "no") if defined $standalone;
3302              
3303             $FILE->print ("
3304             $FILE->print (" version=\"$version\"") if defined $version;
3305             $FILE->print (" encoding=\"$encoding\"") if defined $encoding;
3306             $FILE->print (" standalone=\"$standalone\"") if defined $standalone;
3307             $FILE->print ("?>");
3308             }
3309              
3310             sub to_expat
3311             {
3312             my ($self, $iter) = @_;
3313             $iter->XMLDecl ($self->getVersion, $self->getEncoding, $self->getStandalone);
3314             }
3315              
3316             sub _to_sax
3317             {
3318             my ($self, $doch, $dtdh, $enth) = @_;
3319             $dtdh->xml_decl ( { Version => $self->getVersion,
3320             Encoding => $self->getEncoding,
3321             Standalone => $self->getStandalone } );
3322             }
3323              
3324             ######################################################################
3325             package XML::DOM::DocumentFragment;
3326             ######################################################################
3327             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3328              
3329             BEGIN
3330             {
3331             import XML::DOM::Node qw( :DEFAULT :Fields );
3332             XML::DOM::def_fields ("", "XML::DOM::Node");
3333             }
3334              
3335             use XML::DOM::DOMException;
3336              
3337             sub new
3338             {
3339             my ($class, $doc) = @_;
3340             my $self = bless [], $class;
3341              
3342             $self->[_Doc] = $doc;
3343             $self->[_C] = new XML::DOM::NodeList;
3344             $self;
3345             }
3346              
3347             sub getNodeType
3348             {
3349             DOCUMENT_FRAGMENT_NODE;
3350             }
3351              
3352             sub getNodeName
3353             {
3354             "#document-fragment";
3355             }
3356              
3357             sub cloneNode
3358             {
3359             my ($self, $deep) = @_;
3360             my $node = $self->[_Doc]->createDocumentFragment;
3361              
3362             $node->cloneChildren ($self, $deep);
3363             $node;
3364             }
3365              
3366             #------------------------------------------------------------
3367             # Extra method implementations
3368              
3369             sub isReadOnly
3370             {
3371             0;
3372             }
3373              
3374             sub print
3375             {
3376             my ($self, $FILE) = @_;
3377              
3378             for my $node (@{$self->[_C]})
3379             {
3380             $node->print ($FILE);
3381             }
3382             }
3383              
3384             sub rejectChild
3385             {
3386             my $t = $_[1]->getNodeType;
3387              
3388             $t != TEXT_NODE
3389             && $t != ENTITY_REFERENCE_NODE
3390             && $t != PROCESSING_INSTRUCTION_NODE
3391             && $t != COMMENT_NODE
3392             && $t != CDATA_SECTION_NODE
3393             && $t != ELEMENT_NODE;
3394             }
3395              
3396             sub isDocumentFragmentNode
3397             {
3398             1;
3399             }
3400              
3401             ######################################################################
3402             package XML::DOM::DocumentType; # forward declaration
3403             ######################################################################
3404              
3405             ######################################################################
3406             package XML::DOM::Document;
3407             ######################################################################
3408             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3409              
3410             BEGIN
3411             {
3412             import XML::DOM::Node qw( :DEFAULT :Fields );
3413             XML::DOM::def_fields ("Doctype XmlDecl", "XML::DOM::Node");
3414             }
3415              
3416             use Carp;
3417             use XML::DOM::NodeList;
3418             use XML::DOM::DOMException;
3419              
3420             sub new
3421             {
3422             my ($class) = @_;
3423             my $self = bless [], $class;
3424              
3425             # keep Doc pointer, even though getOwnerDocument returns undef
3426             $self->[_Doc] = $self;
3427             $self->[_C] = new XML::DOM::NodeList;
3428             $self;
3429             }
3430              
3431             sub getNodeType
3432             {
3433             DOCUMENT_NODE;
3434             }
3435              
3436             sub getNodeName
3437             {
3438             "#document";
3439             }
3440              
3441             #?? not sure about keeping a fixed order of these nodes....
3442             sub getDoctype
3443             {
3444             $_[0]->[_Doctype];
3445             }
3446              
3447             sub getDocumentElement
3448             {
3449             my ($self) = @_;
3450             for my $kid (@{$self->[_C]})
3451             {
3452             return $kid if $kid->isElementNode;
3453             }
3454             undef;
3455             }
3456              
3457             sub getOwnerDocument
3458             {
3459             undef;
3460             }
3461              
3462             sub getImplementation
3463             {
3464             $XML::DOM::DOMImplementation::Singleton;
3465             }
3466              
3467             #
3468             # Added extra parameters ($val, $specified) that are passed straight to the
3469             # Attr constructor
3470             #
3471             sub createAttribute
3472             {
3473             new XML::DOM::Attr (@_);
3474             }
3475              
3476             sub createCDATASection
3477             {
3478             new XML::DOM::CDATASection (@_);
3479             }
3480              
3481             sub createComment
3482             {
3483             new XML::DOM::Comment (@_);
3484              
3485             }
3486              
3487             sub createElement
3488             {
3489             new XML::DOM::Element (@_);
3490             }
3491              
3492             sub createTextNode
3493             {
3494             new XML::DOM::Text (@_);
3495             }
3496              
3497             sub createProcessingInstruction
3498             {
3499             new XML::DOM::ProcessingInstruction (@_);
3500             }
3501              
3502             sub createEntityReference
3503             {
3504             new XML::DOM::EntityReference (@_);
3505             }
3506              
3507             sub createDocumentFragment
3508             {
3509             new XML::DOM::DocumentFragment (@_);
3510             }
3511              
3512             sub createDocumentType
3513             {
3514             new XML::DOM::DocumentType (@_);
3515             }
3516              
3517             sub cloneNode
3518             {
3519             my ($self, $deep) = @_;
3520             my $node = new XML::DOM::Document;
3521              
3522             $node->cloneChildren ($self, $deep);
3523              
3524             my $xmlDecl = $self->[_XmlDecl];
3525             $node->[_XmlDecl] = $xmlDecl->cloneNode ($deep) if defined $xmlDecl;
3526              
3527             $node;
3528             }
3529              
3530             sub appendChild
3531             {
3532             my ($self, $node) = @_;
3533              
3534             # Extra check: make sure we don't end up with more than one Element.
3535             # Don't worry about multiple DocType nodes, because DocumentFragment
3536             # can't contain DocType nodes.
3537              
3538             my @nodes = ($node);
3539             @nodes = @{$node->[_C]}
3540             if $node->getNodeType == DOCUMENT_FRAGMENT_NODE;
3541            
3542             my $elem = 0;
3543             for my $n (@nodes)
3544             {
3545             $elem++ if $n->isElementNode;
3546             }
3547            
3548             if ($elem > 0 && defined ($self->getDocumentElement))
3549             {
3550             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
3551             "document can have only one Element");
3552             }
3553             $self->SUPER::appendChild ($node);
3554             }
3555              
3556             sub insertBefore
3557             {
3558             my ($self, $node, $refNode) = @_;
3559              
3560             # Extra check: make sure sure we don't end up with more than 1 Elements.
3561             # Don't worry about multiple DocType nodes, because DocumentFragment
3562             # can't contain DocType nodes.
3563              
3564             my @nodes = ($node);
3565             @nodes = @{$node->[_C]}
3566             if $node->getNodeType == DOCUMENT_FRAGMENT_NODE;
3567            
3568             my $elem = 0;
3569             for my $n (@nodes)
3570             {
3571             $elem++ if $n->isElementNode;
3572             }
3573            
3574             if ($elem > 0 && defined ($self->getDocumentElement))
3575             {
3576             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
3577             "document can have only one Element");
3578             }
3579             $self->SUPER::insertBefore ($node, $refNode);
3580             }
3581              
3582             sub replaceChild
3583             {
3584             my ($self, $node, $refNode) = @_;
3585              
3586             # Extra check: make sure sure we don't end up with more than 1 Elements.
3587             # Don't worry about multiple DocType nodes, because DocumentFragment
3588             # can't contain DocType nodes.
3589              
3590             my @nodes = ($node);
3591             @nodes = @{$node->[_C]}
3592             if $node->getNodeType == DOCUMENT_FRAGMENT_NODE;
3593            
3594             my $elem = 0;
3595             $elem-- if $refNode->isElementNode;
3596              
3597             for my $n (@nodes)
3598             {
3599             $elem++ if $n->isElementNode;
3600             }
3601            
3602             if ($elem > 0 && defined ($self->getDocumentElement))
3603             {
3604             croak new XML::DOM::DOMException (HIERARCHY_REQUEST_ERR,
3605             "document can have only one Element");
3606             }
3607             $self->SUPER::replaceChild ($node, $refNode);
3608             }
3609              
3610             #------------------------------------------------------------
3611             # Extra method implementations
3612              
3613             sub isReadOnly
3614             {
3615             0;
3616             }
3617              
3618             sub print
3619             {
3620             my ($self, $FILE) = @_;
3621              
3622             my $xmlDecl = $self->getXMLDecl;
3623             if (defined $xmlDecl)
3624             {
3625             $xmlDecl->print ($FILE);
3626             $FILE->print ("\x0A");
3627             }
3628              
3629             for my $node (@{$self->[_C]})
3630             {
3631             $node->print ($FILE);
3632             $FILE->print ("\x0A");
3633             }
3634             }
3635              
3636             sub setDoctype
3637             {
3638             my ($self, $doctype) = @_;
3639             my $oldDoctype = $self->[_Doctype];
3640             if (defined $oldDoctype)
3641             {
3642             $self->replaceChild ($doctype, $oldDoctype);
3643             }
3644             else
3645             {
3646             #?? before root element, but after XmlDecl !
3647             $self->appendChild ($doctype);
3648             }
3649             $_[0]->[_Doctype] = $_[1];
3650             }
3651              
3652             sub removeDoctype
3653             {
3654             my $self = shift;
3655             my $doctype = $self->removeChild ($self->[_Doctype]);
3656              
3657             undef $self->[_Doctype]; # was delete
3658             $doctype;
3659             }
3660              
3661             sub rejectChild
3662             {
3663             my $t = $_[1]->getNodeType;
3664             $t != ELEMENT_NODE
3665             && $t != PROCESSING_INSTRUCTION_NODE
3666             && $t != COMMENT_NODE
3667             && $t != DOCUMENT_TYPE_NODE;
3668             }
3669              
3670             sub expandEntity
3671             {
3672             my ($self, $ent, $param) = @_;
3673             my $doctype = $self->getDoctype;
3674              
3675             (defined $doctype) ? $doctype->expandEntity ($ent, $param) : undef;
3676             }
3677              
3678             sub getDefaultAttrValue
3679             {
3680             my ($self, $elem, $attr) = @_;
3681            
3682             my $doctype = $self->getDoctype;
3683              
3684             (defined $doctype) ? $doctype->getDefaultAttrValue ($elem, $attr) : undef;
3685             }
3686              
3687             sub getEntity
3688             {
3689             my ($self, $entity) = @_;
3690            
3691             my $doctype = $self->getDoctype;
3692              
3693             (defined $doctype) ? $doctype->getEntity ($entity) : undef;
3694             }
3695              
3696             sub dispose
3697             {
3698             my $self = shift;
3699              
3700             $self->[_XmlDecl]->dispose if defined $self->[_XmlDecl];
3701             undef $self->[_XmlDecl]; # was delete
3702             undef $self->[_Doctype]; # was delete
3703             $self->SUPER::dispose;
3704             }
3705              
3706             sub setOwnerDocument
3707             {
3708             # Do nothing, you can't change the owner document!
3709             #?? could throw exception...
3710             }
3711              
3712             sub getXMLDecl
3713             {
3714             $_[0]->[_XmlDecl];
3715             }
3716              
3717             sub setXMLDecl
3718             {
3719             $_[0]->[_XmlDecl] = $_[1];
3720             }
3721              
3722             sub createXMLDecl
3723             {
3724             new XML::DOM::XMLDecl (@_);
3725             }
3726              
3727             sub createNotation
3728             {
3729             new XML::DOM::Notation (@_);
3730             }
3731              
3732             sub createElementDecl
3733             {
3734             new XML::DOM::ElementDecl (@_);
3735             }
3736              
3737             sub createAttlistDecl
3738             {
3739             new XML::DOM::AttlistDecl (@_);
3740             }
3741              
3742             sub createEntity
3743             {
3744             new XML::DOM::Entity (@_);
3745             }
3746              
3747             sub createChecker
3748             {
3749             my $self = shift;
3750             my $checker = XML::Checker->new;
3751              
3752             $checker->Init;
3753             my $doctype = $self->getDoctype;
3754             $doctype->to_expat ($checker) if $doctype;
3755             $checker->Final;
3756              
3757             $checker;
3758             }
3759              
3760             sub check
3761             {
3762             my ($self, $checker) = @_;
3763             $checker ||= XML::Checker->new;
3764              
3765             $self->to_expat ($checker);
3766             }
3767              
3768             sub to_expat
3769             {
3770             my ($self, $iter) = @_;
3771              
3772             $iter->Init;
3773              
3774             for my $kid ($self->getChildNodes)
3775             {
3776             $kid->to_expat ($iter);
3777             }
3778             $iter->Final;
3779             }
3780              
3781             sub check_sax
3782             {
3783             my ($self, $checker) = @_;
3784             $checker ||= XML::Checker->new;
3785              
3786             $self->to_sax (Handler => $checker);
3787             }
3788              
3789             sub _to_sax
3790             {
3791             my ($self, $doch, $dtdh, $enth) = @_;
3792              
3793             $doch->start_document;
3794              
3795             for my $kid ($self->getChildNodes)
3796             {
3797             $kid->_to_sax ($doch, $dtdh, $enth);
3798             }
3799             $doch->end_document;
3800             }
3801              
3802             ######################################################################
3803             package XML::DOM::DocumentType;
3804             ######################################################################
3805             use vars qw{ @ISA @EXPORT_OK %EXPORT_TAGS %HFIELDS };
3806              
3807             BEGIN
3808             {
3809             import XML::DOM::Node qw( :DEFAULT :Fields );
3810             import XML::DOM::Document qw( :Fields );
3811             XML::DOM::def_fields ("Entities Notations Name SysId PubId Internal", "XML::DOM::Node");
3812             }
3813              
3814             use XML::DOM::DOMException;
3815             use XML::DOM::NamedNodeMap;
3816              
3817             sub new
3818             {
3819             my $class = shift;
3820             my $doc = shift;
3821              
3822             my $self = bless [], $class;
3823              
3824             $self->[_Doc] = $doc;
3825             $self->[_ReadOnly] = 1;
3826             $self->[_C] = new XML::DOM::NodeList;
3827              
3828             $self->[_Entities] = new XML::DOM::NamedNodeMap (Doc => $doc,
3829             Parent => $self,
3830             ReadOnly => 1);
3831             $self->[_Notations] = new XML::DOM::NamedNodeMap (Doc => $doc,
3832             Parent => $self,
3833             ReadOnly => 1);
3834             $self->setParams (@_);
3835             $self;
3836             }
3837              
3838             sub getNodeType
3839             {
3840             DOCUMENT_TYPE_NODE;
3841             }
3842              
3843             sub getNodeName
3844             {
3845             $_[0]->[_Name];
3846             }
3847              
3848             sub getName
3849             {
3850             $_[0]->[_Name];
3851             }
3852              
3853             sub getEntities
3854             {
3855             $_[0]->[_Entities];
3856             }
3857              
3858             sub getNotations
3859             {
3860             $_[0]->[_Notations];
3861             }
3862              
3863             sub setParentNode
3864             {
3865             my ($self, $parent) = @_;
3866             $self->SUPER::setParentNode ($parent);
3867              
3868             $parent->[_Doctype] = $self
3869             if $parent->getNodeType == DOCUMENT_NODE;
3870             }
3871              
3872             sub cloneNode
3873             {
3874             my ($self, $deep) = @_;
3875              
3876             my $node = new XML::DOM::DocumentType ($self->[_Doc], $self->[_Name],
3877             $self->[_SysId], $self->[_PubId],
3878             $self->[_Internal]);
3879              
3880             #?? does it make sense to make a shallow copy?
3881              
3882             # clone the NamedNodeMaps
3883             $node->[_Entities] = $self->[_Entities]->cloneNode ($deep);
3884              
3885             $node->[_Notations] = $self->[_Notations]->cloneNode ($deep);
3886              
3887             $node->cloneChildren ($self, $deep);
3888              
3889             $node;
3890             }
3891              
3892             #------------------------------------------------------------
3893             # Extra method implementations
3894              
3895             sub getSysId
3896             {
3897             $_[0]->[_SysId];
3898             }
3899              
3900             sub getPubId
3901             {
3902             $_[0]->[_PubId];
3903             }
3904              
3905             sub getInternal
3906             {
3907             $_[0]->[_Internal];
3908             }
3909              
3910             sub setSysId
3911             {
3912             $_[0]->[_SysId] = $_[1];
3913             }
3914              
3915             sub setPubId
3916             {
3917             $_[0]->[_PubId] = $_[1];
3918             }
3919              
3920             sub setInternal
3921             {
3922             $_[0]->[_Internal] = $_[1];
3923             }
3924              
3925             sub setName
3926             {
3927             $_[0]->[_Name] = $_[1];
3928             }
3929              
3930             sub removeChildHoodMemories
3931             {
3932             my ($self, $dontWipeReadOnly) = @_;
3933              
3934             my $parent = $self->[_Parent];
3935             if (defined $parent && $parent->getNodeType == DOCUMENT_NODE)
3936             {
3937             undef $parent->[_Doctype]; # was delete
3938             }
3939             $self->SUPER::removeChildHoodMemories;
3940             }
3941              
3942             sub dispose
3943             {
3944             my $self = shift;
3945              
3946             $self->[_Entities]->dispose;
3947             $self->[_Notations]->dispose;
3948             $self->SUPER::dispose;
3949             }
3950              
3951             sub setOwnerDocument
3952             {
3953             my ($self, $doc) = @_;
3954             $self->SUPER::setOwnerDocument ($doc);
3955              
3956             $self->[_Entities]->setOwnerDocument ($doc);
3957             $self->[_Notations]->setOwnerDocument ($doc);
3958             }
3959              
3960             sub expandEntity
3961             {
3962             my ($self, $ent, $param) = @_;
3963              
3964             my $kid = $self->[_Entities]->getNamedItem ($ent);
3965             return $kid->getValue
3966             if (defined ($kid) && $param == $kid->isParameterEntity);
3967              
3968             undef; # entity not found
3969             }
3970              
3971             sub getAttlistDecl
3972             {
3973             my ($self, $elemName) = @_;
3974             for my $kid (@{$_[0]->[_C]})
3975             {
3976             return $kid if ($kid->getNodeType == ATTLIST_DECL_NODE &&
3977             $kid->getName eq $elemName);
3978             }
3979             undef; # not found
3980             }
3981              
3982             sub getElementDecl
3983             {
3984             my ($self, $elemName) = @_;
3985             for my $kid (@{$_[0]->[_C]})
3986             {
3987             return $kid if ($kid->getNodeType == ELEMENT_DECL_NODE &&
3988             $kid->getName eq $elemName);
3989             }
3990             undef; # not found
3991             }
3992              
3993             sub addElementDecl
3994             {
3995             my ($self, $name, $model, $hidden) = @_;
3996             my $node = $self->getElementDecl ($name);
3997              
3998             #?? could warn
3999             unless (defined $node)
4000             {
4001             $node = $self->[_Doc]->createElementDecl ($name, $model, $hidden);
4002             $self->appendChild ($node);
4003             }
4004             $node;
4005             }
4006              
4007             sub addAttlistDecl
4008             {
4009             my ($self, $name) = @_;
4010             my $node = $self->getAttlistDecl ($name);
4011              
4012             unless (defined $node)
4013             {
4014             $node = $self->[_Doc]->createAttlistDecl ($name);
4015             $self->appendChild ($node);
4016             }
4017             $node;
4018             }
4019              
4020             sub addNotation
4021             {
4022             my $self = shift;
4023             my $node = $self->[_Doc]->createNotation (@_);
4024             $self->[_Notations]->setNamedItem ($node);
4025             $node;
4026             }
4027              
4028             sub addEntity
4029             {
4030             my $self = shift;
4031             my $node = $self->[_Doc]->createEntity (@_);
4032              
4033             $self->[_Entities]->setNamedItem ($node);
4034             $node;
4035             }
4036              
4037             # All AttDefs for a certain Element are merged into a single ATTLIST
4038             sub addAttDef
4039             {
4040             my $self = shift;
4041             my $elemName = shift;
4042              
4043             # create the AttlistDecl if it doesn't exist yet
4044             my $attListDecl = $self->addAttlistDecl ($elemName);
4045             $attListDecl->addAttDef (@_);
4046             }
4047              
4048             sub getDefaultAttrValue
4049             {
4050             my ($self, $elem, $attr) = @_;
4051             my $elemNode = $self->getAttlistDecl ($elem);
4052             (defined $elemNode) ? $elemNode->getDefaultAttrValue ($attr) : undef;
4053             }
4054              
4055             sub getEntity
4056             {
4057             my ($self, $entity) = @_;
4058             $self->[_Entities]->getNamedItem ($entity);
4059             }
4060              
4061             sub setParams
4062             {
4063             my ($self, $name, $sysid, $pubid, $internal) = @_;
4064              
4065             $self->[_Name] = $name;
4066              
4067             #?? not sure if we need to hold on to these...
4068             $self->[_SysId] = $sysid if defined $sysid;
4069             $self->[_PubId] = $pubid if defined $pubid;
4070             $self->[_Internal] = $internal if defined $internal;
4071              
4072             $self;
4073             }
4074              
4075             sub rejectChild
4076             {
4077             # DOM Spec says: DocumentType -- no children
4078             not $XML::DOM::IgnoreReadOnly;
4079             }
4080              
4081             sub print
4082             {
4083             my ($self, $FILE) = @_;
4084              
4085             my $name = $self->[_Name];
4086              
4087             my $sysId = $self->[_SysId];
4088             my $pubId = $self->[_PubId];
4089              
4090             $FILE->print ("
4091             if (defined $pubId)
4092             {
4093             $FILE->print (" PUBLIC \"$pubId\" \"$sysId\"");
4094             }
4095             elsif (defined $sysId)
4096             {
4097             $FILE->print (" SYSTEM \"$sysId\"");
4098             }
4099              
4100             my @entities = @{$self->[_Entities]->getValues};
4101             my @notations = @{$self->[_Notations]->getValues};
4102             my @kids = @{$self->[_C]};
4103              
4104             if (@entities || @notations || @kids)
4105             {
4106             $FILE->print (" [\x0A");
4107              
4108             for my $kid (@entities)
4109             {
4110             next if $kid->[_Hidden];
4111              
4112             $FILE->print (" ");
4113             $kid->print ($FILE);
4114             $FILE->print ("\x0A");
4115             }
4116              
4117             for my $kid (@notations)
4118             {
4119             next if $kid->[_Hidden];
4120              
4121             $FILE->print (" ");
4122             $kid->print ($FILE);
4123             $FILE->print ("\x0A");
4124             }
4125              
4126             for my $kid (@kids)
4127             {
4128             next if $kid->[_Hidden];
4129              
4130             $FILE->print (" ");
4131             $kid->print ($FILE);
4132             $FILE->print ("\x0A");
4133             }
4134             $FILE->print ("]");
4135             }
4136             $FILE->print (">");
4137             }
4138              
4139             sub to_expat
4140             {
4141             my ($self, $iter) = @_;
4142              
4143             $iter->Doctype ($self->getName, $self->getSysId, $self->getPubId, $self->getInternal);
4144              
4145             for my $ent ($self->getEntities->getValues)
4146             {
4147             next if $ent->[_Hidden];
4148             $ent->to_expat ($iter);
4149             }
4150              
4151             for my $nota ($self->getNotations->getValues)
4152             {
4153             next if $nota->[_Hidden];
4154             $nota->to_expat ($iter);
4155             }
4156              
4157             for my $kid ($self->getChildNodes)
4158             {
4159             next if $kid->[_Hidden];
4160             $kid->to_expat ($iter);
4161             }
4162             }
4163              
4164             sub _to_sax
4165             {
4166             my ($self, $doch, $dtdh, $enth) = @_;
4167              
4168             $dtdh->doctype_decl ( { Name => $self->getName,
4169             SystemId => $self->getSysId,
4170             PublicId => $self->getPubId,
4171             Internal => $self->getInternal });
4172              
4173             for my $ent ($self->getEntities->getValues)
4174             {
4175             next if $ent->[_Hidden];
4176             $ent->_to_sax ($doch, $dtdh, $enth);
4177             }
4178              
4179             for my $nota ($self->getNotations->getValues)
4180             {
4181             next if $nota->[_Hidden];
4182             $nota->_to_sax ($doch, $dtdh, $enth);
4183             }
4184              
4185             for my $kid ($self->getChildNodes)
4186             {
4187             next if $kid->[_Hidden];
4188             $kid->_to_sax ($doch, $dtdh, $enth);
4189             }
4190             }
4191              
4192             ######################################################################
4193             package XML::DOM::Parser;
4194             ######################################################################
4195             use vars qw ( @ISA );
4196             @ISA = qw( XML::Parser );
4197              
4198             sub new
4199             {
4200             my ($class, %args) = @_;
4201              
4202             $args{Style} = 'XML::Parser::Dom';
4203             $class->SUPER::new (%args);
4204             }
4205              
4206             # This method needed to be overriden so we can restore some global
4207             # variables when an exception is thrown
4208             sub parse
4209             {
4210             my $self = shift;
4211              
4212             local $XML::Parser::Dom::_DP_doc;
4213             local $XML::Parser::Dom::_DP_elem;
4214             local $XML::Parser::Dom::_DP_doctype;
4215             local $XML::Parser::Dom::_DP_in_prolog;
4216             local $XML::Parser::Dom::_DP_end_doc;
4217             local $XML::Parser::Dom::_DP_saw_doctype;
4218             local $XML::Parser::Dom::_DP_in_CDATA;
4219             local $XML::Parser::Dom::_DP_keep_CDATA;
4220             local $XML::Parser::Dom::_DP_last_text;
4221              
4222              
4223             # Temporarily disable checks that Expat already does (for performance)
4224             local $XML::DOM::SafeMode = 0;
4225             # Temporarily disable ReadOnly checks
4226             local $XML::DOM::IgnoreReadOnly = 1;
4227              
4228             my $ret;
4229             eval {
4230             $ret = $self->SUPER::parse (@_);
4231             };
4232             my $err = $@;
4233              
4234             if ($err)
4235             {
4236             my $doc = $XML::Parser::Dom::_DP_doc;
4237             if ($doc)
4238             {
4239             $doc->dispose;
4240             }
4241             die $err;
4242             }
4243              
4244             $ret;
4245             }
4246              
4247             my $LWP_USER_AGENT;
4248             sub set_LWP_UserAgent
4249             {
4250             $LWP_USER_AGENT = shift;
4251             }
4252              
4253             sub parsefile
4254             {
4255             my $self = shift;
4256             my $url = shift;
4257              
4258             # Any other URL schemes?
4259             if ($url =~ /^(https?|ftp|wais|gopher|file):/)
4260             {
4261             # Read the file from the web with LWP.
4262             #
4263             # Note that we read in the entire file, which may not be ideal
4264             # for large files. LWP::UserAgent also provides a callback style
4265             # request, which we could convert to a stream with a fork()...
4266              
4267             my $result;
4268             eval
4269             {
4270             use LWP::UserAgent;
4271              
4272             my $ua = $self->{LWP_UserAgent};
4273             unless (defined $ua)
4274             {
4275             unless (defined $LWP_USER_AGENT)
4276             {
4277             $LWP_USER_AGENT = LWP::UserAgent->new;
4278              
4279             # Load proxy settings from environment variables, i.e.:
4280             # http_proxy, ftp_proxy, no_proxy etc. (see LWP::UserAgent(3))
4281             # You need these to go thru firewalls.
4282             $LWP_USER_AGENT->env_proxy;
4283             }
4284             $ua = $LWP_USER_AGENT;
4285             }
4286             my $req = new HTTP::Request 'GET', $url;
4287             my $response = $ua->request ($req);
4288              
4289             # Parse the result of the HTTP request
4290             $result = $self->parse ($response->content, @_);
4291             };
4292             if ($@)
4293             {
4294             die "Couldn't parsefile [$url] with LWP: $@";
4295             }
4296             return $result;
4297             }
4298             else
4299             {
4300             return $self->SUPER::parsefile ($url, @_);
4301             }
4302             }
4303              
4304             ######################################################################
4305             package XML::Parser::Dom;
4306             ######################################################################
4307              
4308             BEGIN
4309             {
4310             import XML::DOM::Node qw( :Fields );
4311             import XML::DOM::CharacterData qw( :Fields );
4312             }
4313              
4314             use vars qw( $_DP_doc
4315             $_DP_elem
4316             $_DP_doctype
4317             $_DP_in_prolog
4318             $_DP_end_doc
4319             $_DP_saw_doctype
4320             $_DP_in_CDATA
4321             $_DP_keep_CDATA
4322             $_DP_last_text
4323             $_DP_level
4324             $_DP_expand_pent
4325             );
4326              
4327             # This adds a new Style to the XML::Parser class.
4328             # From now on you can say: $parser = new XML::Parser ('Style' => 'Dom' );
4329             # but that is *NOT* how a regular user should use it!
4330             $XML::Parser::Built_In_Styles{Dom} = 1;
4331              
4332             sub Init
4333             {
4334             $_DP_elem = $_DP_doc = new XML::DOM::Document();
4335             $_DP_doctype = new XML::DOM::DocumentType ($_DP_doc);
4336             $_DP_doc->setDoctype ($_DP_doctype);
4337             $_DP_keep_CDATA = $_[0]->{KeepCDATA};
4338              
4339             # Prepare for document prolog
4340             $_DP_in_prolog = 1;
4341              
4342             # We haven't passed the root element yet
4343             $_DP_end_doc = 0;
4344              
4345             # Expand parameter entities in the DTD by default
4346              
4347             $_DP_expand_pent = defined $_[0]->{ExpandParamEnt} ?
4348             $_[0]->{ExpandParamEnt} : 1;
4349             if ($_DP_expand_pent)
4350             {
4351             $_[0]->{DOM_Entity} = {};
4352             }
4353              
4354             $_DP_level = 0;
4355              
4356             undef $_DP_last_text;
4357             }
4358              
4359             sub Final
4360             {
4361             unless ($_DP_saw_doctype)
4362             {
4363             my $doctype = $_DP_doc->removeDoctype;
4364             $doctype->dispose;
4365             }
4366             $_DP_doc;
4367             }
4368              
4369             sub Char
4370             {
4371             my $str = $_[1];
4372              
4373             if ($_DP_in_CDATA && $_DP_keep_CDATA)
4374             {
4375             undef $_DP_last_text;
4376             # Merge text with previous node if possible
4377             $_DP_elem->addCDATA ($str);
4378             }
4379             else
4380             {
4381             # Merge text with previous node if possible
4382             # Used to be: $expat->{DOM_Element}->addText ($str);
4383             if ($_DP_last_text)
4384             {
4385             $_DP_last_text->[_Data] .= $str;
4386             }
4387             else
4388             {
4389             $_DP_last_text = $_DP_doc->createTextNode ($str);
4390             $_DP_last_text->[_Parent] = $_DP_elem;
4391             push @{$_DP_elem->[_C]}, $_DP_last_text;
4392             }
4393             }
4394             }
4395              
4396             sub Start
4397             {
4398             my ($expat, $elem, @attr) = @_;
4399             my $parent = $_DP_elem;
4400             my $doc = $_DP_doc;
4401            
4402             if ($parent == $doc)
4403             {
4404             # End of document prolog, i.e. start of first Element
4405             $_DP_in_prolog = 0;
4406             }
4407            
4408             undef $_DP_last_text;
4409             my $node = $doc->createElement ($elem);
4410             $_DP_elem = $node;
4411             $parent->appendChild ($node);
4412            
4413             my $n = @attr;
4414             return unless $n;
4415              
4416             # Add attributes
4417             my $first_default = $expat->specified_attr;
4418             my $i = 0;
4419             while ($i < $n)
4420             {
4421             my $specified = $i < $first_default;
4422             my $name = $attr[$i++];
4423             undef $_DP_last_text;
4424             my $attr = $doc->createAttribute ($name, $attr[$i++], $specified);
4425             $node->setAttributeNode ($attr);
4426             }
4427             }
4428              
4429             sub End
4430             {
4431             $_DP_elem = $_DP_elem->[_Parent];
4432             undef $_DP_last_text;
4433              
4434             # Check for end of root element
4435             $_DP_end_doc = 1 if ($_DP_elem == $_DP_doc);
4436             }
4437              
4438             # Called at end of file, i.e. whitespace following last closing tag
4439             # Also for Entity references
4440             # May also be called at other times...
4441             sub Default
4442             {
4443             my ($expat, $str) = @_;
4444              
4445             # shift; deb ("Default", @_);
4446              
4447             if ($_DP_in_prolog) # still processing Document prolog...
4448             {
4449             #?? could try to store this text later
4450             #?? I've only seen whitespace here so far
4451             }
4452             elsif (!$_DP_end_doc) # ignore whitespace at end of Document
4453             {
4454             # if ($expat->{NoExpand})
4455             # {
4456             # Got a TextDecl () from an external entity here once
4457              
4458             # create non-parameter entity reference, correct?
4459             return unless $str =~ s!^&!!;
4460             return unless $str =~ s!;$!!;
4461             $_DP_elem->appendChild (
4462             $_DP_doc->createEntityReference ($str,0,$expat->{NoExpand}));
4463             undef $_DP_last_text;
4464             # }
4465             # else
4466             # {
4467             # $expat->{DOM_Element}->addText ($str);
4468             # }
4469             }
4470             }
4471              
4472             # XML::Parser 2.19 added support for CdataStart and CdataEnd handlers
4473             # If they are not defined, the Default handler is called instead
4474             # with the text "
4475             sub CdataStart
4476             {
4477             $_DP_in_CDATA = 1;
4478             }
4479              
4480             sub CdataEnd
4481             {
4482             $_DP_in_CDATA = 0;
4483             }
4484              
4485             my $START_MARKER = "__DOM__START__ENTITY__";
4486             my $END_MARKER = "__DOM__END__ENTITY__";
4487              
4488             sub Comment
4489             {
4490             undef $_DP_last_text;
4491              
4492             # These comments were inserted by ExternEnt handler
4493             if ($_[1] =~ /(?:($START_MARKER)|($END_MARKER))/)
4494             {
4495             if ($1) # START
4496             {
4497             $_DP_level++;
4498             }
4499             else
4500             {
4501             $_DP_level--;
4502             }
4503             }
4504             else
4505             {
4506             my $comment = $_DP_doc->createComment ($_[1]);
4507             $_DP_elem->appendChild ($comment);
4508             }
4509             }
4510              
4511             sub deb
4512             {
4513             # return;
4514              
4515             my $name = shift;
4516             print "$name (" . join(",", map {defined($_)?$_ : "(undef)"} @_) . ")\n";
4517             }
4518              
4519             sub Doctype
4520             {
4521             my $expat = shift;
4522             # deb ("Doctype", @_);
4523              
4524             $_DP_doctype->setParams (@_);
4525             $_DP_saw_doctype = 1;
4526             }
4527              
4528             sub Attlist
4529             {
4530             my $expat = shift;
4531             # deb ("Attlist", @_);
4532              
4533             $_[5] = "Hidden" unless $_DP_expand_pent || $_DP_level == 0;
4534             $_DP_doctype->addAttDef (@_);
4535             }
4536              
4537             sub XMLDecl
4538             {
4539             my $expat = shift;
4540             # deb ("XMLDecl", @_);
4541              
4542             undef $_DP_last_text;
4543             $_DP_doc->setXMLDecl (new XML::DOM::XMLDecl ($_DP_doc, @_));
4544             }
4545              
4546             sub Entity
4547             {
4548             my $expat = shift;
4549             # deb ("Entity", @_);
4550            
4551             # check to see if Parameter Entity
4552             if ($_[5])
4553             {
4554              
4555             if (defined $_[2]) # was sysid specified?
4556             {
4557             # Store the Entity mapping for use in ExternEnt
4558             if (exists $expat->{DOM_Entity}->{$_[2]})
4559             {
4560             # If this ever happens, the name of entity may be the wrong one
4561             # when writing out the Document.
4562             XML::DOM::warning ("Entity $_[2] is known as %$_[0] and %" .
4563             $expat->{DOM_Entity}->{$_[2]});
4564             }
4565             else
4566             {
4567             $expat->{DOM_Entity}->{$_[2]} = $_[0];
4568             }
4569             #?? remove this block when XML::Parser has better support
4570             }
4571             }
4572              
4573             # no value on things with sysId
4574             if (defined $_[2] && defined $_[1])
4575             {
4576             # print STDERR "XML::DOM Warning $_[0] had both value($_[1]) And SYSId ($_[2]), removing value.\n";
4577             $_[1] = undef;
4578             }
4579              
4580             undef $_DP_last_text;
4581              
4582             $_[6] = "Hidden" unless $_DP_expand_pent || $_DP_level == 0;
4583             $_DP_doctype->addEntity (@_);
4584             }
4585              
4586             #
4587             # Unparsed is called when it encounters e.g:
4588             #
4589             #
4590             #
4591             sub Unparsed
4592             {
4593             Entity (@_); # same as regular ENTITY, as far as DOM is concerned
4594             }
4595              
4596             sub Element
4597             {
4598             shift;
4599             # deb ("Element", @_);
4600              
4601             # put in to convert XML::Parser::ContentModel object to string
4602             # ($_[1] used to be a string in XML::Parser 2.27 and
4603             # dom_attr.t fails if we don't stringify here)
4604             $_[1] = "$_[1]";
4605              
4606             undef $_DP_last_text;
4607             push @_, "Hidden" unless $_DP_expand_pent || $_DP_level == 0;
4608             $_DP_doctype->addElementDecl (@_);
4609             }
4610              
4611             sub Notation
4612             {
4613             shift;
4614             # deb ("Notation", @_);
4615              
4616             undef $_DP_last_text;
4617             $_[4] = "Hidden" unless $_DP_expand_pent || $_DP_level == 0;
4618             $_DP_doctype->addNotation (@_);
4619             }
4620              
4621             sub Proc
4622             {
4623             shift;
4624             # deb ("Proc", @_);
4625              
4626             undef $_DP_last_text;
4627             push @_, "Hidden" unless $_DP_expand_pent || $_DP_level == 0;
4628             $_DP_elem->appendChild ($_DP_doc->createProcessingInstruction (@_));
4629             }
4630              
4631             #
4632             # ExternEnt is called when an external entity, such as:
4633             #
4634             #
4635             # "http://server/descr.txt">
4636             #
4637             # is referenced in the document, e.g. with: &externalEntity;
4638             # If ExternEnt is not specified, the entity reference is passed to the Default
4639             # handler as e.g. "&externalEntity;", where an EntityReference object is added.
4640             #
4641             # Also for %externalEntity; references in the DTD itself.
4642             #
4643             # It can also be called when XML::Parser parses the DOCTYPE header
4644             # (just before calling the DocType handler), when it contains a
4645             # reference like "docbook.dtd" below:
4646             #
4647             #
4648             # "docbook.dtd" [
4649             # ... rest of DTD ...
4650             #
4651             sub ExternEnt
4652             {
4653             my ($expat, $base, $sysid, $pubid) = @_;
4654             # deb ("ExternEnt", @_);
4655              
4656             # ?? (tjmather) i think there is a problem here
4657             # with XML::Parser > 2.27 since file_ext_ent_handler
4658             # now returns a IO::File object instead of a content string
4659              
4660             # Invoke XML::Parser's default ExternEnt handler
4661             my $content;
4662             if ($XML::Parser::have_LWP)
4663             {
4664             $content = XML::Parser::lwp_ext_ent_handler (@_);
4665             }
4666             else
4667             {
4668             $content = XML::Parser::file_ext_ent_handler (@_);
4669             }
4670              
4671             if ($_DP_expand_pent)
4672             {
4673             return $content;
4674             }
4675             else
4676             {
4677             my $entname = $expat->{DOM_Entity}->{$sysid};
4678             if (defined $entname)
4679             {
4680             $_DP_doctype->appendChild ($_DP_doc->createEntityReference ($entname, 1, $expat->{NoExpand}));
4681             # Wrap the contents in special comments, so we know when we reach the
4682             # end of parsing the entity. This way we can omit the contents from
4683             # the DTD, when ExpandParamEnt is set to 0.
4684            
4685             return "" .
4686             $content . "";
4687             }
4688             else
4689             {
4690             # We either read the entity ref'd by the system id in the
4691             # header, or the entity was undefined.
4692             # In either case, don't bother with maintaining the entity
4693             # reference, just expand the contents.
4694             return "" .
4695             $content . "";
4696             }
4697             }
4698             }
4699              
4700             1; # module return code
4701              
4702             __END__