File Coverage

blib/lib/XML/Writer.pm
Criterion Covered Total %
statement 650 650 100.0
branch 200 200 100.0
condition 99 99 100.0
subroutine 101 101 100.0
pod 27 27 100.0
total 1077 1077 100.0


line stmt bran cond sub pod time code
1             ########################################################################
2             # Writer.pm - write an XML document.
3             # Copyright (c) 1999 by Megginson Technologies.
4             # Copyright (c) 2003 Ed Avis
5             # Copyright (c) 2004-2010 Joseph Walton
6             # Redistribution and use in source and compiled forms, with or without
7             # modification, are permitted under any circumstances. No warranty.
8             ########################################################################
9              
10             package XML::Writer;
11              
12             require 5.004;
13              
14 2     2   69089 use strict;
  2         14  
  2         82  
15 2     2   13 use vars qw($VERSION);
  2         2  
  2         159  
16 2     2   14 use Carp;
  2         3  
  2         136  
17 2     2   546 use IO::Handle;
  2         6218  
  2         118  
18             $VERSION = "0.900";
19              
20 2     2   1168 use overload '""' => \&_overload_string;
  2         1043  
  2         15  
21              
22            
23             ########################################################################
24             # Constructor.
25             ########################################################################
26              
27             #
28             # Public constructor.
29             #
30             # This actually does most of the work of the module: it defines closures
31             # for all of the real processing, and selects the appropriate closures
32             # to use based on the value of the UNSAFE parameter. The actual methods
33             # are just stubs.
34             #
35             sub new {
36 329     329 1 71345 my ($class, %params) = (@_);
37              
38             # If the user wants namespaces,
39             # intercept the request here; it will
40             # come back to this constructor
41             # from within XML::Writer::Namespaces::new()
42 329 100       773 if ($params{NAMESPACES}) {
43 159         311 delete $params{NAMESPACES};
44 159         481 return XML::Writer::Namespaces->new(%params);
45             }
46              
47             # Set up $self and basic parameters
48 170         246 my $self;
49             my $output;
50 170         242 my $unsafe = $params{UNSAFE};
51 170         261 my $newlines = $params{NEWLINES};
52 170         223 my $dataMode = $params{DATA_MODE};
53 170         247 my $dataIndent;
54             my $selfcontained_output;
55 170         236 my $use_selfcontained_output = 0;
56              
57             # If the NEWLINES parameter is specified,
58             # set the $nl variable appropriately
59 170         216 my $nl = '';
60 170 100       283 if ($newlines) {
61 1         3 $nl = "\n";
62             }
63              
64 170   100     564 my $outputEncoding = $params{ENCODING} || "";
65 170         233 my ($checkUnencodedRepertoire, $escapeEncoding);
66 170 100       402 if (lc($outputEncoding) eq 'us-ascii') {
67 15         34 $checkUnencodedRepertoire = \&_croakUnlessASCII;
68 15         27 $escapeEncoding = \&_escapeASCII;
69             } else {
70 155     364   586 my $doNothing = sub {};
71 155         268 $checkUnencodedRepertoire = $doNothing;
72 155         236 $escapeEncoding = $doNothing;
73             }
74              
75             # Parse variables
76 170         284 my @elementStack = ();
77 170         239 my $elementLevel = 0;
78 170         205 my %seen = ();
79              
80 170         211 my $hasData = 0;
81 170         225 my @hasDataStack = ();
82 170         212 my $hasElement = 0;
83 170         201 my @hasElementStack = ();
84 170         201 my $hasHeading = 0; # Does this document have anything before the first element?
85              
86             #
87             # Private method to show attributes.
88             #
89             my $showAttributes = sub {
90 186     186   246 my $atts = $_[0];
91 186         245 my $i = 1;
92 186         400 while ($atts->[$i]) {
93 72         206 my $aname = $atts->[$i++];
94 72         137 my $value = _escapeLiteral($atts->[$i++]);
95 72         152 $value =~ s/\x0a/\ \;/g;
96 72         115 $value =~ s/\x0d/\ \;/g;
97 72         89 $value =~ s/\x09/\ \;/g;
98 72         93 &{$escapeEncoding}($value);
  72         146  
99 72         230 $output->print(" $aname=\"$value\"");
100             }
101 170         591 };
102              
103             # Method implementations: the SAFE_
104             # versions perform error checking
105             # and then call the regular ones.
106             my $end = sub {
107 88     88   206 $output->print("\n");
108              
109 88 100 100     566 return $selfcontained_output
110             if $use_selfcontained_output and defined wantarray;
111 170         423 };
112              
113             my $SAFE_end = sub {
114 80 100   80   190 if (!$seen{ELEMENT}) {
    100          
115 1         77 croak("Document cannot end without a document element");
116             } elsif ($elementLevel > 0) {
117 1         121 croak("Document ended with unmatched start tag(s): @elementStack");
118             } else {
119 78         109 @elementStack = ();
120 78         105 $elementLevel = 0;
121 78         143 %seen = ();
122 78         102 &{$end};
  78         126  
123             }
124 170         432 };
125              
126             my $xmlDecl = sub {
127 22     22   45 my ($encoding, $standalone) = (@_);
128 22 100 100     60 if ($standalone && $standalone ne 'no') {
129 1         2 $standalone = 'yes';
130             }
131              
132             # Only include an encoding if one has been explicitly supplied,
133             # either here or on construction. Allow the empty string
134             # to suppress it.
135 22 100       44 if (!defined($encoding)) {
136 18         27 $encoding = $outputEncoding;
137             }
138 22         75 $output->print("
139 21 100       178 if ($encoding) {
140 7         25 $output->print(" encoding=\"$encoding\"");
141             }
142 21 100       66 if ($standalone) {
143 2         12 $output->print(" standalone=\"$standalone\"");
144             }
145 21         55 $output->print("?>\n");
146 21         118 $hasHeading = 1;
147 170         479 };
148              
149             my $SAFE_xmlDecl = sub {
150 23 100   23   55 if ($seen{ANYTHING}) {
151 2         155 croak("The XML declaration is not the first thing in the document");
152             } else {
153 21         41 $seen{ANYTHING} = 1;
154 21         28 $seen{XMLDECL} = 1;
155 21         26 &{$xmlDecl};
  21         34  
156             }
157 170         374 };
158              
159             my $pi = sub {
160 8     8   17 my ($target, $data) = (@_);
161 8 100       64 if ($data) {
162 3         16 $output->print("");
163             } else {
164 5         23 $output->print("");
165             }
166 8 100       59 if ($elementLevel == 0) {
167 7         18 $output->print("\n");
168 7         42 $hasHeading = 1;
169             }
170 170         440 };
171              
172             my $SAFE_pi = sub {
173 11     11   21 my ($name, $data) = (@_);
174 11         20 $seen{ANYTHING} = 1;
175 11 100 100     56 if (($name =~ /^xml/i) && ($name !~ /^xml-(stylesheet|model)$/i)) {
176 1         108 carp("Processing instruction target begins with 'xml'");
177             }
178              
179 11 100 100     124 if ($name =~ /\?\>/ || (defined($data) && $data =~ /\?\>/)) {
    100 100        
180 2         195 croak("Processing instruction may not contain '?>'");
181             } elsif ($name =~ /\s/) {
182 1         97 croak("Processing instruction name may not contain whitespace");
183             } else {
184 8         11 &{$pi};
  8         15  
185             }
186 170         409 };
187              
188             my $comment = sub {
189 14     14   20 my $data = $_[0];
190 14 100 100     47 if ($dataMode && $elementLevel) {
191 5         13 $output->print("\n");
192 5         33 $output->print($dataIndent x $elementLevel);
193             }
194 14         80 $output->print("");
195 14 100 100     118 if ($dataMode && $elementLevel) {
    100          
196 5         10 $hasElement = 1;
197             } elsif ($elementLevel == 0) {
198 8         21 $output->print("\n");
199 8         44 $hasHeading = 1;
200             }
201 170         459 };
202              
203             my $SAFE_comment = sub {
204 14     14   23 my $data = $_[0];
205 14 100       63 if ($data =~ /--/) {
206 2         167 carp("Interoperability problem: \"--\" in comment text");
207             }
208              
209 14 100       126 if ($data =~ /-->/) {
210 1         67 croak("Comment may not contain '-->'");
211             } else {
212 13         25 &{$checkUnencodedRepertoire}($data);
  13         26  
213 12         20 $seen{ANYTHING} = 1;
214 12         17 &{$comment};
  12         23  
215             }
216 170         414 };
217              
218             my $doctype = sub {
219 10     10   22 my ($name, $publicId, $systemId) = (@_);
220 10         45 $output->print("
221 10 100       76 if ($publicId) {
    100          
222 5 100       12 unless ( defined $systemId) {
223 2         248 croak("A DOCTYPE declaration with a public ID must also have a system ID");
224             }
225 3         10 $output->print(" PUBLIC \"$publicId\" \"$systemId\"");
226             } elsif ( defined $systemId ) {
227 2         7 $output->print(" SYSTEM \"$systemId\"");
228             }
229 8         41 $output->print(">\n");
230 8         47 $hasHeading = 1;
231 170         486 };
232              
233             my $SAFE_doctype = sub {
234 11     11   21 my $name = $_[0];
235 11 100       28 if ($seen{DOCTYPE}) {
    100          
236 1         76 croak("Attempt to insert second DOCTYPE declaration");
237             } elsif ($seen{ELEMENT}) {
238 1         76 croak("The DOCTYPE declaration must come before the first start tag");
239             } else {
240 9         18 $seen{ANYTHING} = 1;
241 9         15 $seen{DOCTYPE} = $name;
242 9         10 &{$doctype};
  9         16  
243             }
244 170         410 };
245              
246             my $startTag = sub {
247 106     106   168 my $name = $_[0];
248 106 100 100     294 if ($dataMode && ($hasHeading || $elementLevel)) {
      100        
249 29         94 $output->print("\n");
250 29         202 $output->print($dataIndent x $elementLevel);
251             }
252 106         250 $elementLevel++;
253 106         176 push @elementStack, $name;
254 106         402 $output->print("<$name");
255 105         728 &{$showAttributes}(\@_);
  105         249  
256 105         513 $output->print("$nl>");
257 105 100       673 if ($dataMode) {
258 43         95 $hasElement = 1;
259 43         73 push @hasDataStack, $hasData;
260 43         59 $hasData = 0;
261 43         65 push @hasElementStack, $hasElement;
262 43         104 $hasElement = 0;
263             }
264 170         477 };
265              
266             my $SAFE_startTag = sub {
267 103     103   186 my $name = $_[0];
268              
269 103         234 _croakUnlessValidName($name);
270 103         147 &{$checkUnencodedRepertoire}($name);
  103         254  
271 103         249 _checkAttributes(\@_, $checkUnencodedRepertoire);
272              
273 103 100 100     661 if ($seen{ELEMENT} && $elementLevel == 0) {
    100 100        
    100 100        
      100        
274 1         112 croak("Attempt to insert start tag after close of document element");
275             } elsif ($elementLevel == 0 && $seen{DOCTYPE} && $name ne $seen{DOCTYPE}) {
276             croak("Document element is \"$name\", but DOCTYPE is \""
277             . $seen{DOCTYPE}
278 1         110 . "\"");
279             } elsif ($dataMode && $hasData) {
280 2         223 croak("Mixed content not allowed in data mode: element $name");
281             } else {
282 99         171 $seen{ANYTHING} = 1;
283 99         144 $seen{ELEMENT} = 1;
284 99         129 &{$startTag};
  99         150  
285             }
286 170         449 };
287              
288             my $emptyTag = sub {
289 81     81   122 my $name = $_[0];
290 81 100 100     182 if ($dataMode && ($hasHeading || $elementLevel)) {
      100        
291 18         58 $output->print("\n");
292 18         125 $output->print($dataIndent x $elementLevel);
293             }
294 81         370 $output->print("<$name");
295 81         522 &{$showAttributes}(\@_);
  81         175  
296 81         382 $output->print("$nl />");
297 81 100       472 if ($dataMode) {
298 19         30 $hasElement = 1;
299             }
300 170         429 };
301              
302             my $SAFE_emptyTag = sub {
303 93     93   133 my $name = $_[0];
304              
305 93         216 _croakUnlessValidName($name);
306 88         116 &{$checkUnencodedRepertoire}($name);
  88         193  
307 86         290 _checkAttributes(\@_, $checkUnencodedRepertoire);
308              
309 80 100 100     478 if ($seen{ELEMENT} && $elementLevel == 0) {
    100 100        
    100 100        
      100        
310 1         109 croak("Attempt to insert empty tag after close of document element");
311             } elsif ($elementLevel == 0 && $seen{DOCTYPE} && $name ne $seen{DOCTYPE}) {
312             croak("Document element is \"$name\", but DOCTYPE is \""
313             . $seen{DOCTYPE}
314 1         111 . "\"");
315             } elsif ($dataMode && $hasData) {
316 1         111 croak("Mixed content not allowed in data mode: element $name");
317             } else {
318 77         130 $seen{ANYTHING} = 1;
319 77         112 $seen{ELEMENT} = 1;
320 77         88 &{$emptyTag};
  77         125  
321             }
322 170         443 };
323              
324             my $endTag = sub {
325 81     81   116 my $name = $_[0];
326 81         115 my $currentName = pop @elementStack;
327 81 100       163 $name = $currentName unless $name;
328 81         102 $elementLevel--;
329 81 100 100     203 if ($dataMode && $hasElement) {
330 20         52 $output->print("\n");
331 20         122 $output->print($dataIndent x $elementLevel);
332             }
333 81         347 $output->print("");
334 81 100       474 if ($dataMode) {
335 36         59 $hasData = pop @hasDataStack;
336 36         54 $hasElement = pop @hasElementStack;
337             }
338 170         457 };
339              
340             my $SAFE_endTag = sub {
341 76     76   104 my $name = $_[0];
342 76         118 my $oldName = $elementStack[$#elementStack];
343 76 100 100     292 if ($elementLevel <= 0) {
    100          
344 1         101 croak("End tag \"$name\" does not close any open element");
345             } elsif ($name && ($name ne $oldName)) {
346 1         113 croak("Attempt to end element \"$oldName\" with \"$name\" tag");
347             } else {
348 74         126 &{$endTag};
  74         116  
349             }
350 170         407 };
351              
352             my $characters = sub {
353 40     40   58 my $data = $_[0];
354 40 100       87 if ($data =~ /[\&\<\>]/) {
355 1         5 $data =~ s/\&/\&\;/g;
356 1         3 $data =~ s/\
357 1         4 $data =~ s/\>/\>\;/g;
358             }
359 40         64 &{$escapeEncoding}($data);
  40         82  
360 40         105 $output->print($data);
361 40         233 $hasData = 1;
362 170         399 };
363              
364             my $SAFE_characters = sub {
365 42 100 100 42   141 if ($elementLevel < 1) {
    100          
366 1         83 croak("Attempt to insert characters outside of document element");
367             } elsif ($dataMode && $hasElement) {
368 1         79 croak("Mixed content not allowed in data mode: characters");
369             } else {
370 40         92 _croakUnlessDefinedCharacters($_[0]);
371 38         46 &{$characters};
  38         66  
372             }
373 170         379 };
374              
375             my $raw = sub {
376 1     1   3 $output->print($_[0]);
377             # Don't set $hasData or any other information: we know nothing
378             # about what was just written.
379             #
380 170         334 };
381              
382             my $SAFE_raw = sub {
383 1     1   120 croak('raw() is only available when UNSAFE is set');
384 170         347 };
385              
386             my $cdata = sub {
387 10     10   17 my $data = $_[0];
388 10         25 $data =~ s/\]\]>/\]\]\]\]>/g;
389 10         40 $output->print("");
390 10         60 $hasData = 1;
391 170         408 };
392              
393             my $SAFE_cdata = sub {
394 11 100 100 11   40 if ($elementLevel < 1) {
    100          
395 1         100 croak("Attempt to insert characters outside of document element");
396             } elsif ($dataMode && $hasElement) {
397 1         77 croak("Mixed content not allowed in data mode: characters");
398             } else {
399 9         19 _croakUnlessDefinedCharacters($_[0]);
400 8         8 &{$checkUnencodedRepertoire}($_[0]);
  8         31  
401 7         12 &{$cdata};
  7         11  
402             }
403 170         407 };
404              
405             # Assign the correct closures based on
406             # the UNSAFE parameter
407 170 100       305 if ($unsafe) {
408 10         62 $self = {'END' => $end,
409             'XMLDECL' => $xmlDecl,
410             'PI' => $pi,
411             'COMMENT' => $comment,
412             'DOCTYPE' => $doctype,
413             'STARTTAG' => $startTag,
414             'EMPTYTAG' => $emptyTag,
415             'ENDTAG' => $endTag,
416             'CHARACTERS' => $characters,
417             'RAW' => $raw,
418             'CDATA' => $cdata
419             };
420             } else {
421 160         906 $self = {'END' => $SAFE_end,
422             'XMLDECL' => $SAFE_xmlDecl,
423             'PI' => $SAFE_pi,
424             'COMMENT' => $SAFE_comment,
425             'DOCTYPE' => $SAFE_doctype,
426             'STARTTAG' => $SAFE_startTag,
427             'EMPTYTAG' => $SAFE_emptyTag,
428             'ENDTAG' => $SAFE_endTag,
429             'CHARACTERS' => $SAFE_characters,
430             'RAW' => $SAFE_raw, # This will intentionally fail
431             'CDATA' => $SAFE_cdata
432             };
433             }
434              
435             # Query methods
436             $self->{'IN_ELEMENT'} = sub {
437 1     1   3 my ($ancestor) = (@_);
438 1         16 return $elementStack[$#elementStack] eq $ancestor;
439 170         490 };
440              
441             $self->{'WITHIN_ELEMENT'} = sub {
442 3     3   5 my ($ancestor) = (@_);
443 3         5 my $el;
444 3         5 foreach $el (@elementStack) {
445 4 100       13 return 1 if $el eq $ancestor;
446             }
447 1         6 return 0;
448 170         454 };
449              
450             $self->{'CURRENT_ELEMENT'} = sub {
451 1     1   7 return $elementStack[$#elementStack];
452 170         396 };
453              
454             $self->{'ANCESTOR'} = sub {
455 5     5   9 my ($n) = (@_);
456 5 100       13 if ($n < scalar(@elementStack)) {
457 3         13 return $elementStack[$#elementStack-$n];
458             } else {
459 2         9 return undef;
460             }
461 170         402 };
462              
463             # Set and get the output destination.
464             $self->{'GETOUTPUT'} = sub {
465 7 100   7   18 if (ref($output) ne 'XML::Writer::_PrintChecker') {
466 4         19 return $output;
467             } else {
468 3         15 return $output->{HANDLE};
469             }
470 170         548 };
471              
472             $self->{'SETOUTPUT'} = sub {
473 176     176   257 my $newOutput = $_[0];
474              
475 176 100 100     698 if (defined($newOutput) && !ref($newOutput)) {
476 3 100       8 if ('self' eq $newOutput ) {
477 2         4 $newOutput = \$selfcontained_output;
478 2         4 $use_selfcontained_output = 1;
479             } else {
480 1         66 die "Output must be a handle, a reference or 'self'";
481             }
482             }
483              
484 175 100       339 if (ref($newOutput) eq 'SCALAR') {
485 7         27 $output = XML::Writer::_String->new($newOutput);
486             } else {
487             # If there is no OUTPUT parameter,
488             # use standard output
489 168   100     418 $output = $newOutput || \*STDOUT;
490 168 100 100     407 if ($outputEncoding && (ref($output) eq 'GLOB' || $output->isa('IO::Handle'))) {
      100        
491 21 100       73 if (lc($outputEncoding) eq 'utf-8') {
    100          
492 4         64 binmode($output, ':encoding(utf-8)');
493             } elsif (lc($outputEncoding) eq 'us-ascii') {
494 1     1   11 binmode($output, ':encoding(us-ascii)');
  1         2  
  1         7  
  15         228  
495             } else {
496 2         135 die 'The only supported encodings are utf-8 and us-ascii';
497             }
498             }
499             }
500              
501 173 100       13768 if ($params{CHECK_PRINT}) {
502 6         18 $output = XML::Writer::_PrintChecker->new($output);
503             }
504 170         569 };
505              
506             $self->{OVERLOADSTRING} = sub {
507             # if we don't use the self-contained output,
508             # simple passthrough
509 5 100   5   37 return $use_selfcontained_output ? $selfcontained_output : undef;
510 170         424 };
511              
512             $self->{TOSTRING} = sub {
513 2 100   2   12 die "'to_string' can only be used with self-contained output\n"
514             unless $use_selfcontained_output;
515              
516 1         5 return $selfcontained_output;
517 170         441 };
518              
519             $self->{'SETDATAMODE'} = sub {
520 7     7   13 $dataMode = $_[0];
521 170         389 };
522              
523             $self->{'GETDATAMODE'} = sub {
524 2     2   18 return $dataMode;
525 170         378 };
526              
527             $self->{'SETDATAINDENT'} = sub {
528 178 100   178   854 if ($_[0] =~ /^\s*$/) {
529 168         318 $dataIndent = $_[0];
530             } else {
531 10         43 $dataIndent = ' ' x $_[0];
532             }
533 170         407 };
534              
535             $self->{'GETDATAINDENT'} = sub {
536 8 100   8   36 if ($dataIndent =~ /^ *$/) {
537 7         35 return length($dataIndent);
538             } else {
539 1         4 return $dataIndent;
540             }
541 170         377 };
542              
543             # Set the indent.
544 170   100     543 &{$self->{'SETDATAINDENT'}}($params{'DATA_INDENT'} || '');
  170         464  
545              
546             # Set the output.
547 170         273 &{$self->{'SETOUTPUT'}}($params{'OUTPUT'});
  170         371  
548              
549             # Return the blessed object.
550 167         1894 return bless $self, $class;
551             }
552              
553              
554            
555             ########################################################################
556             # Public methods
557             ########################################################################
558              
559             #
560             # Finish writing the document.
561             #
562             sub end {
563 90     90 1 1171 my $self = shift;
564 90         103 &{$self->{END}};
  90         163  
565             }
566              
567             #
568             # Write an XML declaration.
569             #
570             sub xmlDecl {
571 24     24 1 524 my $self = shift;
572 24         30 &{$self->{XMLDECL}};
  24         55  
573             }
574              
575             #
576             # Write a processing instruction.
577             #
578             sub pi {
579 12     12 1 91 my $self = shift;
580 12         18 &{$self->{PI}};
  12         27  
581             }
582              
583             #
584             # Write a comment.
585             #
586             sub comment {
587 16     16 1 118 my $self = shift;
588 16         21 &{$self->{COMMENT}};
  16         37  
589             }
590              
591             #
592             # Write a DOCTYPE declaration.
593             #
594             sub doctype {
595 12     12 1 90 my $self = shift;
596 12         17 &{$self->{DOCTYPE}};
  12         29  
597             }
598              
599             #
600             # Write a start tag.
601             #
602             sub startTag {
603 110     110 1 588 my $self = shift;
604 110         147 &{$self->{STARTTAG}};
  110         241  
605             }
606              
607             #
608             # Write an empty tag.
609             #
610             sub emptyTag {
611 102     102 1 660 my $self = shift;
612 102         137 &{$self->{EMPTYTAG}};
  102         227  
613             }
614              
615             #
616             # Write an end tag.
617             #
618             sub endTag {
619 83     83 1 311 my $self = shift;
620 83         128 &{$self->{ENDTAG}};
  83         165  
621             }
622              
623             #
624             # Write a simple data element.
625             #
626             sub dataElement {
627 20     20 1 710 my ($self, $name, $data, @atts) = (@_);
628 20         51 $self->startTag($name, @atts);
629 20         51 $self->characters($data);
630 20         44 $self->endTag($name);
631             }
632              
633             #
634             # Write a simple CDATA element.
635             #
636             sub cdataElement {
637 1     1 1 18 my ($self, $name, $data, %atts) = (@_);
638 1         6 $self->startTag($name, %atts);
639 1         5 $self->cdata($data);
640 1         5 $self->endTag($name);
641             }
642              
643             #
644             # Write character data.
645             #
646             sub characters {
647 44     44 1 145 my $self = shift;
648 44         56 &{$self->{CHARACTERS}};
  44         104  
649             }
650              
651             #
652             # Write raw, unquoted, completely unchecked character data.
653             #
654             sub raw {
655 2     2 1 17 my $self = shift;
656 2         3 &{$self->{RAW}};
  2         5  
657             }
658              
659             #
660             # Write CDATA.
661             #
662             sub cdata {
663 14     14 1 62 my $self = shift;
664 14         20 &{$self->{CDATA}};
  14         27  
665             }
666              
667             #
668             # Query the current element.
669             #
670             sub in_element {
671 1     1 1 9 my $self = shift;
672 1         3 return &{$self->{IN_ELEMENT}};
  1         4  
673             }
674              
675             #
676             # Query the ancestors.
677             #
678             sub within_element {
679 3     3 1 21 my $self = shift;
680 3         4 return &{$self->{WITHIN_ELEMENT}};
  3         8  
681             }
682              
683             #
684             # Get the name of the current element.
685             #
686             sub current_element {
687 1     1 1 14 my $self = shift;
688 1         2 return &{$self->{CURRENT_ELEMENT}};
  1         3  
689             }
690              
691             #
692             # Get the name of the numbered ancestor (zero-based).
693             #
694             sub ancestor {
695 5     5 1 26 my $self = shift;
696 5         8 return &{$self->{ANCESTOR}};
  5         13  
697             }
698              
699             #
700             # Get the current output destination.
701             #
702             sub getOutput {
703 7     7 1 330 my $self = shift;
704 7         12 return &{$self->{GETOUTPUT}};
  7         14  
705             }
706              
707              
708             #
709             # Set the current output destination.
710             #
711             sub setOutput {
712 6     6 1 461 my $self = shift;
713 6         10 return &{$self->{SETOUTPUT}};
  6         13  
714             }
715              
716             #
717             # Set the current data mode (true or false).
718             #
719             sub setDataMode {
720 7     7 1 45 my $self = shift;
721 7         11 return &{$self->{SETDATAMODE}};
  7         17  
722             }
723              
724              
725             #
726             # Get the current data mode (true or false).
727             #
728             sub getDataMode {
729 2     2 1 20 my $self = shift;
730 2         3 return &{$self->{GETDATAMODE}};
  2         7  
731             }
732              
733              
734             #
735             # Set the current data indent step.
736             #
737             sub setDataIndent {
738 8     8 1 28 my $self = shift;
739 8         12 return &{$self->{SETDATAINDENT}};
  8         17  
740             }
741              
742              
743             #
744             # Get the current data indent step.
745             #
746             sub getDataIndent {
747 8     8 1 34 my $self = shift;
748 8         12 return &{$self->{GETDATAINDENT}};
  8         17  
749             }
750              
751              
752             #
753             # Empty stub.
754             #
755       1 1   sub addPrefix {
756             }
757              
758              
759             #
760             # Empty stub.
761             #
762       1 1   sub removePrefix {
763             }
764              
765             sub to_string {
766 2     2 1 5 my $self = shift;
767              
768 2         5 $self->{TOSTRING}->();
769             }
770              
771              
772            
773             ########################################################################
774             # Private functions.
775             ########################################################################
776              
777             #
778             # Private: check for duplicate attributes and bad characters.
779             # Note - this starts at $_[1], because $_[0] is assumed to be an
780             # element name.
781             #
782             sub _checkAttributes {
783 189     189   249 my %anames;
784 189         230 my $i = 1;
785 189         247 my $checkUnencodedRepertoire = $_[1];
786              
787 189         447 while ($_[0]->[$i]) {
788 73         120 my $name = $_[0]->[$i];
789 73         86 $i += 1;
790 73 100       150 if ($anames{$name}) {
791 2         240 croak("Two attributes named \"$name\"");
792             } else {
793 71         127 $anames{$name} = 1;
794             }
795 71         142 _croakUnlessValidName($name);
796 69         87 &{$checkUnencodedRepertoire}($name);
  69         138  
797 68         152 _croakUnlessDefinedCharacters($_[0]->[$i]);
798 67         173 $i += 1;
799             }
800             }
801              
802             #
803             # Private: escape an attribute value literal.
804             #
805             sub _escapeLiteral {
806 72     72   117 my $data = $_[0];
807 72 100       182 if ($data =~ /[\&\<\>\"]/) {
808 2         7 $data =~ s/\&/\&\;/g;
809 2         5 $data =~ s/\
810 2         7 $data =~ s/\>/\>\;/g;
811 2         5 $data =~ s/\"/\"\;/g;
812             }
813 72         120 return $data;
814             }
815              
816             sub _escapeASCII($) {
817 7     7   26 $_[0] =~ s/([^\x00-\x7F])/sprintf('&#x%X;', ord($1))/ge;
  5         29  
818             }
819              
820             sub _croakUnlessASCII($) {
821 22 100   22   63 if ($_[0] =~ /[^\x00-\x7F]/) {
822 5         603 croak('Non-ASCII characters are not permitted in this part of a US-ASCII document');
823             }
824             }
825              
826             # Enforce XML 1.0, section 2.2's definition of "Char" (only reject low ASCII,
827             # so as not to require Unicode support from perl)
828             sub _croakUnlessDefinedCharacters($) {
829 117 100   117   302 if ($_[0] =~ /([\x00-\x08\x0B-\x0C\x0E-\x1F])/) {
830 4         405 croak(sprintf('Code point \u%04X is not a valid character in XML', ord($1)));
831             }
832             }
833              
834             # Ensure element and attribute names are non-empty, contain no whitespace and are
835             # otherwise valid XML names
836             sub _croakUnlessValidName($) {
837 267 100   267   485 if ($_[0] eq '') {
838 2         260 croak('Empty identifiers are not permitted in this part of an XML document');
839             }
840 265 100       690 if ($_[0] =~ /\s/) {
841 4         562 croak('Space characters are not permitted in this part of an XML identifier');
842             }
843              
844             # From REC-xml-20081126
845             # [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
846             # [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
847             # [5] Name ::= NameStartChar (NameChar)*
848              
849 261 100       968 if ($_[0] !~ /^[:A-Z_a-z\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{370}-\x{37D}\x{37F}-\x{1FFF}\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}\x{EFFFF}][-.0-9\x{B7}\x{0300}-\x{036F}\x{203F}-\x{2040}:A-Z_a-z\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{370}-\x{37D}\x{37F}-\x{1FFF}\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}\x{EFFFF}]*$/) {
850 1         139 croak('Not a valid XML name: '.$_[0]);
851             }
852              
853             # ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
854              
855             # | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
856             }
857              
858             sub _overload_string {
859 5     5   1273 my $self = shift;
860 5 100       18 $self->{OVERLOADSTRING}->() || overload::StrVal($self);
861             }
862            
863             ########################################################################
864             # XML::Writer::Namespaces - subclass for Namespace processing.
865             ########################################################################
866              
867             package XML::Writer::Namespaces;
868 2     2   9687 use strict;
  2         4  
  2         54  
869 2     2   10 use vars qw(@ISA);
  2         3  
  2         105  
870 2     2   13 use Carp;
  2         4  
  2         3798  
871              
872             @ISA = qw(XML::Writer);
873              
874             #
875             # Constructor
876             #
877             sub new {
878 159     159   356 my ($class, %params) = (@_);
879              
880 159         295 my $unsafe = $params{UNSAFE};
881              
882             # Snarf the prefix map, if any, and
883             # note the default prefix.
884 159         229 my %prefixMap = ();
885 159 100       329 if ($params{PREFIX_MAP}) {
886 8         15 %prefixMap = (%{$params{PREFIX_MAP}});
  8         26  
887 8         16 delete $params{PREFIX_MAP};
888             }
889 159         282 $prefixMap{'http://www.w3.org/XML/1998/namespace'} = 'xml';
890              
891             # Generate the reverse map for URIs
892 159         279 my $uriMap = {};
893 159         212 my $key;
894 159         423 foreach $key (keys(%prefixMap)) {
895 168         443 $uriMap->{$prefixMap{$key}} = $key;
896             }
897              
898 159         344 my $defaultPrefix = $uriMap->{''};
899 159 100       312 delete $prefixMap{$defaultPrefix} if ($defaultPrefix);
900              
901             # Create an instance of the parent.
902 159         383 my $self = XML::Writer->new(%params);
903              
904             # Snarf the parent's methods that we're
905             # going to override.
906 156         384 my $OLD_startTag = $self->{STARTTAG};
907 156         237 my $OLD_emptyTag = $self->{EMPTYTAG};
908 156         220 my $OLD_endTag = $self->{ENDTAG};
909              
910             # State variables
911 156         224 my @stack;
912 156         198 my $prefixCounter = 1;
913 156         369 my $nsDecls = {'http://www.w3.org/XML/1998/namespace' => 'xml'};
914 156         211 my $nsDefaultDecl = undef;
915 156         199 my $nsCopyFlag = 0;
916 156         241 my @forcedNSDecls = ();
917              
918 156 100       313 if ($params{FORCED_NS_DECLS}) {
919 2         3 @forcedNSDecls = @{$params{FORCED_NS_DECLS}};
  2         6  
920 2         4 delete $params{FORCED_NS_DECLS};
921             }
922              
923             #
924             # Push the current declaration state.
925             #
926             my $pushState = sub {
927 194     194   508 push @stack, [$nsDecls, $nsDefaultDecl, $nsCopyFlag, $uriMap];
928 194         331 $nsCopyFlag = 0;
929 156         506 };
930              
931              
932             #
933             # Pop the current declaration state.
934             #
935             my $popState = sub {
936 150     150   190 ($nsDecls, $nsDefaultDecl, $nsCopyFlag, $uriMap) = @{pop @stack};
  150         530  
937 156         378 };
938              
939             #
940             # Generate a new prefix.
941             #
942             my $genPrefix = sub {
943 34     34   52 my $uri = $_[0];
944 34         46 my $prefixCounter = 1;
945 34         45 my $prefix = $prefixMap{$uri};
946 34         47 my %clashMap = %{$uriMap};
  34         116  
947 34         113 while( my ($u, $p) = each(%prefixMap)) {
948 45         135 $clashMap{$p} = $u;
949             }
950              
951 34 100       72 if (!defined($prefix)) {
952             do {
953 36         65 $prefix = "__NS$prefixCounter";
954 36         77 $prefixCounter++;
955 24         31 } while ($clashMap{$prefix});
956             }
957              
958 34         81 return $prefix;
959 156         439 };
960              
961             #
962             # Perform namespace processing on a single name.
963             #
964             my $processName = sub {
965 71     71   121 my ($nameref, $atts, $attFlag) = (@_);
966 71         94 my ($uri, $local) = @{$$nameref};
  71         135  
967 71         128 my $prefix = $nsDecls->{$uri};
968              
969             # Is this an element name that matches
970             # the default NS?
971 71 100 100     301 if (!$attFlag && $defaultPrefix && ($uri eq $defaultPrefix)) {
    100 100        
972 17 100 100     48 unless ($nsDefaultDecl && ($nsDefaultDecl eq $uri)) {
973 12         15 push @{$atts}, 'xmlns';
  12         22  
974 12         15 push @{$atts}, $uri;
  12         19  
975 12         16 $nsDefaultDecl = $uri;
976             }
977 17         26 $$nameref = $local;
978              
979 17 100       35 if (defined($uriMap->{''})) {
980 9         19 delete ($nsDecls->{$uriMap->{''}});
981             }
982              
983 17         31 $nsDecls->{$uri} = '';
984 17 100       33 unless ($nsCopyFlag) {
985 16         20 $uriMap = {%{$uriMap}};
  16         50  
986 16         26 $nsDecls = {%{$nsDecls}};
  16         43  
987 16         24 $nsCopyFlag = 1;
988             }
989 17         39 $uriMap->{''} = $uri;
990              
991             # Is there a straight-forward prefix?
992             } elsif ($prefix) {
993 20         88 $$nameref = "$prefix:$local";
994             } else {
995 34         52 $prefix = &{$genPrefix}($uri);
  34         53  
996 34 100       73 unless ($nsCopyFlag) {
997 30         37 $uriMap = {%{$uriMap}};
  30         73  
998 30         48 $nsDecls = {%{$nsDecls}};
  30         64  
999 30         52 $nsCopyFlag = 1;
1000             }
1001 34         60 $uriMap->{$prefix} = $uri;
1002 34         60 $nsDecls->{$uri} = $prefix;
1003 34         48 push @{$atts}, "xmlns:$prefix";
  34         76  
1004 34         45 push @{$atts}, $uri;
  34         56  
1005 34         90 $$nameref = "$prefix:$local";
1006             }
1007 156         559 };
1008              
1009              
1010             #
1011             # Perform namespace processing on element and attribute names.
1012             #
1013             my $nsProcess = sub {
1014 194 100   194   435 if (ref($_[0]->[0]) eq 'ARRAY') {
1015 54         71 my $x = \@{$_[0]->[0]};
  54         102  
1016 54         78 &{$processName}(\$x, $_[0], 0);
  54         113  
1017 54         75 splice(@{$_[0]}, 0, 1, $x);
  54         146  
1018             }
1019 194         261 my $i = 1;
1020 194         389 while ($_[0]->[$i]) {
1021 74 100       155 if (ref($_[0]->[$i]) eq 'ARRAY') {
1022 14         22 my $x = \@{$_[0]->[$i]};
  14         24  
1023 14         24 &{$processName}(\$x, $_[0], 1);
  14         31  
1024 14         16 splice(@{$_[0]}, $i, 1, $x);
  14         37  
1025             }
1026 74         155 $i += 2;
1027             }
1028              
1029             # We do this if any declarations are forced, due either to
1030             # constructor arguments or to a call during processing.
1031 194 100       419 if (@forcedNSDecls) {
1032 3         8 foreach (@forcedNSDecls) {
1033 3         7 my @dummy = ($_, 'dummy');
1034 3         6 my $d2 = \@dummy;
1035 3 100 100     15 if ($defaultPrefix && ($_ eq $defaultPrefix)) {
1036 1         2 &{$processName}(\$d2, $_[0], 0);
  1         6  
1037             } else {
1038 2         4 &{$processName}(\$d2, $_[0], 1);
  2         6  
1039             }
1040             }
1041 3         6 @forcedNSDecls = ();
1042             }
1043 156         600 };
1044              
1045              
1046             # Indicate that a namespace should be declared by the next open element
1047             $self->{FORCENSDECL} = sub {
1048 1     1   3 push @forcedNSDecls, $_[0];
1049 156         391 };
1050              
1051              
1052             #
1053             # Start tag, with NS processing
1054             #
1055             $self->{STARTTAG} = sub {
1056 101     101   180 my $name = $_[0];
1057 101 100       208 unless ($unsafe) {
1058 94         201 _checkNSNames(\@_);
1059             }
1060 101         141 &{$pushState}();
  101         208  
1061 101         146 &{$nsProcess}(\@_);
  101         204  
1062 101         166 &{$OLD_startTag};
  101         180  
1063 156         436 };
1064              
1065              
1066             #
1067             # Empty tag, with NS processing
1068             #
1069             $self->{EMPTYTAG} = sub {
1070 98 100   98   206 unless ($unsafe) {
1071 94         203 _checkNSNames(\@_);
1072             }
1073 93         117 &{$pushState}();
  93         203  
1074 93         140 &{$nsProcess}(\@_);
  93         236  
1075 93         129 &{$OLD_emptyTag};
  93         196  
1076 78         116 &{$popState}();
  78         139  
1077 156         401 };
1078              
1079              
1080             #
1081             # End tag, with NS processing
1082             #
1083             $self->{ENDTAG} = sub {
1084 74     74   111 my $name = $_[0];
1085 74 100       143 if (ref($_[0]) eq 'ARRAY') {
1086 22         41 my $pfx = $nsDecls->{$_[0]->[0]};
1087 22 100       44 if ($pfx) {
1088 17         37 $_[0] = $pfx . ':' . $_[0]->[1];
1089             } else {
1090 5         9 $_[0] = $_[0]->[1];
1091             }
1092             } else {
1093 52         73 $_[0] = $_[0];
1094             }
1095             # &{$nsProcess}(\@_);
1096 74         96 &{$OLD_endTag};
  74         144  
1097 72         107 &{$popState}();
  72         166  
1098 156         430 };
1099              
1100              
1101             #
1102             # Processing instruction, but only if not UNSAFE.
1103             #
1104 156 100       315 unless ($unsafe) {
1105 146         258 my $OLD_pi = $self->{PI};
1106             $self->{PI} = sub {
1107 12     12   19 my $target = $_[0];
1108 12 100       37 if (index($target, ':') >= 0) {
1109 1         95 croak "PI target '$target' contains a colon.";
1110             }
1111 11         15 &{$OLD_pi};
  11         21  
1112             }
1113 146         412 };
1114              
1115              
1116             #
1117             # Add a prefix to the prefix map.
1118             #
1119             $self->{ADDPREFIX} = sub {
1120 18     18   66 my ($uri, $prefix) = (@_);
1121 18 100       40 if ($prefix) {
1122 7         17 $prefixMap{$uri} = $prefix;
1123             } else {
1124 11 100       23 if (defined($defaultPrefix)) {
1125 4         8 delete($prefixMap{$defaultPrefix});
1126             }
1127 11         24 $defaultPrefix = $uri;
1128             }
1129 156         483 };
1130              
1131              
1132             #
1133             # Remove a prefix from the prefix map.
1134             #
1135             $self->{REMOVEPREFIX} = sub {
1136 3     3   7 my ($uri) = (@_);
1137 3 100 100     15 if ($defaultPrefix && ($defaultPrefix eq $uri)) {
1138 1         2 $defaultPrefix = undef;
1139             }
1140 3         8 delete $prefixMap{$uri};
1141 156         507 };
1142              
1143              
1144             #
1145             # Bless and return the object.
1146             #
1147 156         13696 return bless $self, $class;
1148             }
1149              
1150              
1151             #
1152             # Add a preferred prefix for a namespace URI.
1153             #
1154             sub addPrefix {
1155 18     18   144 my $self = shift;
1156 18         29 return &{$self->{ADDPREFIX}};
  18         85  
1157             }
1158              
1159              
1160             #
1161             # Remove a preferred prefix for a namespace URI.
1162             #
1163             sub removePrefix {
1164 3     3   25 my $self = shift;
1165 3         5 return &{$self->{REMOVEPREFIX}};
  3         7  
1166             }
1167              
1168              
1169             #
1170             # Check names.
1171             #
1172             sub _checkNSNames {
1173 188     188   250 my $names = $_[0];
1174 188         266 my $i = 1;
1175 188         287 my $name = $names->[0];
1176              
1177             # Check the element name.
1178 188 100       562 if (ref($name) eq 'ARRAY') {
    100          
1179 55 100       154 if (index($name->[1], ':') >= 0) {
1180 1         115 croak("Local part of element name '" .
1181             $name->[1] .
1182             "' contains a colon.");
1183             }
1184             } elsif (index($name, ':') >= 0) {
1185 1         177 croak("Element name '$name' contains a colon.");
1186             }
1187              
1188             # Check the attribute names.
1189 186         459 while ($names->[$i]) {
1190 29         44 my $name = $names->[$i];
1191 29 100       64 if (ref($name) eq 'ARRAY') {
1192 13         22 my $local = $name->[1];
1193 13 100       32 if (index($local, ':') >= 0) {
1194 1         117 croak "Local part of attribute name '$local' contains a colon.";
1195             }
1196             } else {
1197 16 100       57 if ($name =~ /^xmlns/) {
    100          
1198 1         148 croak "Attribute name '$name' begins with 'xmlns'";
1199             } elsif (index($name, ':') >= 0) {
1200 1         119 croak "Attribute name '$name' contains ':'";
1201             }
1202             }
1203 26         67 $i += 2;
1204             }
1205             }
1206              
1207             sub forceNSDecl
1208             {
1209 1     1   6 my $self = shift;
1210 1         2 return &{$self->{FORCENSDECL}};
  1         4  
1211             }
1212              
1213              
1214             package XML::Writer::_String;
1215              
1216             # Internal class, behaving sufficiently like an IO::Handle,
1217             # that stores written output in a string
1218             #
1219             # Heavily inspired by Simon Oliver's XML::Writer::String
1220              
1221             sub new
1222             {
1223 7     7   13 my $class = shift;
1224 7         14 my $scalar_ref = shift;
1225 7         18 return bless($scalar_ref, $class);
1226             }
1227              
1228             sub print
1229             {
1230 43     43   62 ${(shift)} .= join('', @_);
  43         101  
1231 43         61 return 1;
1232             }
1233              
1234              
1235             package XML::Writer::_PrintChecker;
1236              
1237 2     2   51 use Carp;
  2         5  
  2         321  
1238              
1239             sub new
1240             {
1241 6     6   11 my $class = shift;
1242 6         19 return bless({HANDLE => shift}, $class);
1243             }
1244              
1245             sub print
1246             {
1247 4     4   7 my $self = shift;
1248 4 100       16 if ($self->{HANDLE}->print(shift)) {
1249 2         16 return 1;
1250             } else {
1251 2         171 croak "Failed to write output: $!";
1252             }
1253             }
1254              
1255             1;
1256             __END__