File Coverage

blib/lib/XML/API.pm
Criterion Covered Total %
statement 343 478 71.7
branch 131 220 59.5
condition 28 67 41.7
subroutine 43 57 75.4
pod 1 1 100.0
total 546 823 66.3


line stmt bran cond sub pod time code
1             # Private package (not to be used outside XML::API)
2             package XML::API::Element;
3 9     9   286427 use strict;
  9         21  
  9         235  
4 9     9   44 use warnings;
  9         17  
  9         267  
5 9     9   46 use Carp qw(croak);
  9         18  
  9         541  
6 9     9   47 use Scalar::Util qw(weaken refaddr);
  9         22  
  9         9186  
7              
8             our $VERSION = '0.28';
9              
10             sub new {
11 64     64   582 my $proto = shift;
12 64   33     250 my $class = ref($proto) || $proto;
13 64         363 my $self = {
14             attrs => {},
15             contents => [],
16             @_
17             };
18              
19              
20 64 100       186 if ($self->{comment}) {
21 2         10 $self->{comment} =~ s/--/- -/go;
22             }
23              
24 64 100       232 weaken($self->{parent}) if (exists $self->{parent});
25              
26 64         98 bless ($self, $class);
27 64         126 return $self;
28             }
29              
30              
31              
32             sub parent {
33 85     85   95 my $self = shift;
34 85         179 return $self->{parent};
35             }
36              
37              
38             sub inline {
39 48     48   63 my $self = shift;
40 48         194 return $self->{inline};
41             }
42              
43              
44             sub attrs_as_string {
45 78     78   95 my $self = shift;
46 78         93 my @strings;
47              
48 78         88 foreach my $key (sort keys %{$self->{attrs}}) {
  78         243  
49 26         44 my $val = $self->{attrs}->{$key};
50 26 50       64 if (!defined($val)) {
51 0         0 warn "Attribute '$key' (element '$self->{element}') is undefined";
52 0         0 $val = '*undef*';
53             }
54 26         74 push(@strings, $key .'="'. $val .'"');
55             }
56              
57 78 100       269 return '' unless(@strings);
58 21         75 return ' ' . join(' ', @strings);
59             }
60              
61              
62             sub add {
63 84     84   120 my $self = shift;
64 84         94 push(@{$self->{contents}}, @_);
  84         325  
65             }
66              
67              
68             sub as_string {
69 81     81   1076 my $self = shift;
70 81   100     246 my $indent = shift || '';
71 81   100     190 my $growindent = shift || '';
72              
73 81 100       185 if ($self->{comment}) {
74 2         11 return $indent . '';
75             }
76              
77 79 100       199 if ($self->{cdata}) {
78 2         12 return $indent . '{cdata} . ']]>';
79             }
80              
81 77 100       88 if (!@{$self->{contents}}) {
  77         201  
82             return $indent . '<'. ($self->{ns} ? $self->{ns}.':' : '')
83 8 50       33 . $self->{element} . $self->attrs_as_string . ' />';
84             }
85              
86             my $str = $indent . '<'. ($self->{ns} ? $self->{ns}.':' : '')
87 69 100       253 . $self->{element} . $self->attrs_as_string .'>';
88 69         107 my $complex = 0;
89              
90 69         74 foreach my $c (@{$self->{contents}}) {
  69         140  
91 104 100       129 if ( eval { $c->isa( __PACKAGE__ ) and !$c->inline } ) {
  104 100       588  
    100          
92 48         58 $complex = 1;
93 48         188 $str .= "\n" . $c->as_string($indent . $growindent, $growindent);
94             }
95 56         290 elsif ( eval { $c->isa( 'XML::API' ) } ) { # assume it is complex?
96             $str .= "\n" . join("\n",
97 3         8 map {$_->as_string($indent . $growindent, $growindent)}
  3         10  
98             $c->_elements);
99             }
100             else {
101 53 50       202 $str .= $c if (defined($c));
102             }
103             }
104              
105 69 100       154 if ($complex) {
106 28         47 $str .= "\n" . $indent;
107             }
108 69 100       201 $str .= '{ns} ? $self->{ns}.':' : '') . $self->{element} .'>';
109 69         284 return $str;
110             }
111              
112              
113             sub fast_string {
114 1     1   2 my $self = shift;
115              
116 1 50       4 $self->{comment} && return '';
117 1 50       3 $self->{cdata} && return '{cdata} . ']]>';
118              
119             return '<'. ($self->{ns} ? $self->{ns}.':' : '')
120             . $self->{element} . $self->attrs_as_string .'>'
121             . join('', map {
122 1         7 eval { $_->isa( __PACKAGE__ ) }
123             ? $_->fast_string
124 1         14 : (eval { $_->isa('XML::API') }
125 1 50       2 ? join('', map {$_->fast_string} $_->_elements)
  0 50       0  
126             : $_)
127 1         2 } @{$self->{contents}})
128             . '{ns} ? $self->{ns}.':' : '') . $self->{element}
129 1 50       5 . '>';
    50          
130             }
131              
132              
133             # Private package (not to be used outside XML::API)
134             package XML::API::SAXHandler;
135 9     9   50 use strict;
  9         18  
  9         190  
136 9     9   42 use warnings;
  9         15  
  9         312  
137 9     9   41 use base qw(XML::SAX::Base);
  9         17  
  9         13421  
138              
139             sub new {
140 2     2   5 my $proto = shift;
141 2   33     13 my $class = ref($proto) || $proto;
142 2         8 my $self = {
143             xmlapi => undef,
144             @_,
145             };
146 2         6 bless($self, $class);
147 2         20 return $self;
148             }
149              
150              
151             sub start_element {
152 0     0   0 my $self = shift;
153 0         0 my $hash = shift;
154 0 0       0 if($hash->{Name} eq '_xml_api_ignore') {
155 0         0 $self->{xml_api_ignore} = 1;
156 0         0 return;
157             }
158 0         0 $self->{xml_api_ignore} = 0;
159              
160 0         0 my $attrs = {};
161 0         0 foreach my $val (values %{$hash->{Attributes}}) {
  0         0  
162 0         0 $attrs->{$val->{Name}} = $val->{Value};
163             }
164              
165 0         0 $self->{xmlapi}->_open($hash->{Name}, $attrs);
166 0         0 return;
167             }
168              
169              
170             sub characters {
171 0     0   0 my $self = shift;
172 0         0 my $hash = shift;
173 0 0       0 $self->{xml_api_ignore} && return;
174 0         0 $self->{xmlapi}->_add($hash->{Data});
175 0         0 return;
176             }
177              
178              
179             sub end_element {
180 0     0   0 my $self = shift;
181 0         0 my $hash = shift;
182 0 0       0 if($hash->{Name} eq '_xml_api_ignore') {
183 0         0 return;
184             }
185              
186 0         0 $self->{xmlapi}->_close($hash->{Name});
187 0         0 return;
188             }
189              
190              
191             # ----------------------------------------------------------------------
192             # XML::API - Perl extension for creating XML documents
193             # ----------------------------------------------------------------------
194             package XML::API;
195 9     9   221972 use strict;
  9         24  
  9         269  
196 9     9   58 use warnings;
  9         16  
  9         493  
197 9     9   121 use overload '""' => \&_as_string, 'fallback' => 1;
  9         20  
  9         70  
198 9     9   841 use Carp qw(carp croak confess);
  9         17  
  9         714  
199 9     9   46 use Scalar::Util qw(weaken refaddr);
  9         15  
  9         1665  
200 9     9   7875 use XML::SAX;
  9         43607  
  9         47293  
201              
202             our $VERSION = '0.28';
203             our $DEFAULT_ENCODING = 'UTF-8';
204             our $ENCODING = undef;
205             our $Indent = ' ';
206             our $AUTOLOAD;
207              
208              
209             # Not implemented yet:
210             # strict => 0|1 # Optional, defaults to 0
211             #By default strict checking is performed to make sure that the structure
212             #of the document matches the Schema. This can be turned off by setting
213             #'strict' to false (0 or undef).
214              
215             sub new {
216 16     16 1 2477 my $proto = shift;
217 16   33     95 my $class = ref($proto) || $proto;
218              
219 16         76 my $self = {
220             doctype => undef,
221             encoding => undef,
222             debug => undef,
223             @_,
224             };
225              
226             #
227             # Derived classes
228             #
229 16 50       79 if ($class ne __PACKAGE__) {
    100          
230 0 0       0 if ($self->{doctype}) {
231 0         0 confess("Must not specify doctype when instantiating $class");
232             }
233             }
234             elsif ($self->{doctype}) {
235 5         20 $class = $class . '::' . uc($self->{doctype});
236 5 100       315 if (! eval "require $class;1;") {
237 1         11 die "Could not load module '$class'";
238             }
239             }
240 15         35 delete $self->{doctype};
241 15         31 bless ($self, $class);
242              
243 15   33     485 $self->{encoding} = $self->{encoding} || $ENCODING || $DEFAULT_ENCODING;
244 15         36 $self->{elements} = [];
245 15         46 $self->{current} = undef;
246 15         32 $self->{string} = undef;
247 15         30 $self->{ids} = {};
248 15         34 $self->{langs} = {};
249              
250 15         116 return $self;
251             }
252              
253              
254             sub _root_element {
255 54     54   146 return '';
256             }
257              
258              
259             sub _root_attrs {
260 0     0   0 return {};
261             }
262              
263              
264             sub _doctype {
265 18     18   55 return '';
266             }
267              
268              
269             sub _elements {
270 3     3   5 my $self = shift;
271 3         4 return @{$self->{elements}};
  3         6  
272             }
273              
274              
275             sub _open {
276 58     58   879 my $self = shift;
277 58   33     137 my $element = shift || croak '_open($element,...)';
278              
279 58         87 my $namespace = $self->{namespace};
280              
281             # reset the output string in case it has been cached
282 58         86 $self->{string} = undef;
283              
284 58 100       141 if ($element eq $self->_root_element) {
285 4         13 $self->{has_root_element} = 1;
286             }
287              
288 58         99 my $attrs = {};
289 58         75 my @content;
290              
291 58         97 my $total = scalar(@_) - 1;
292 58         63 my $next;
293              
294 58         120 foreach my $i (0..$total) {
295 42 100       89 if ($next) {
296 6         9 $next = undef;
297 6         10 next;
298             }
299              
300 36         71 my $arg = $_[$i];
301 36 100 66     219 if (ref($arg) eq 'HASH') {
    100          
302 1         6 while (my ($key,$val) = each %$arg) {
303 0         0 $attrs->{$key} = _escapeXML($val);
304 0 0       0 if (!defined($val)) {
305 0         0 carp "attribute '$key' undefined (element '$element')";
306 0         0 $attrs->{$key} = ''
307             }
308             }
309             }
310             elsif (defined($arg) and $arg =~ m/^-[^0-9\.]+/o) {
311 6         19 $arg =~ s/^-//o;
312 6         20 $attrs->{$arg} = _escapeXML($_[++$i]);
313 6 50       18 if (!defined($attrs->{$arg})) {
314 0         0 carp "attribute '$arg' undefined (element '$element') ";
315 0         0 $attrs->{$arg} = ''
316             }
317 6         8 $next = 1;
318 6         14 next;
319             }
320             else {
321 29         68 push(@content, $arg);
322             }
323             }
324              
325             #
326             # Start with the default root element attributes and add those
327             # given if this is the root element
328             #
329 58 100       146 if ($element eq $self->_root_element) {
330 4         12 my $rootattrs = $self->_root_attrs;
331 4         21 while (my ($key,$val) = each %$attrs) {
332 0         0 $rootattrs->{$key} = $val;
333             }
334 4         7 $attrs = $rootattrs;
335             }
336              
337 58 50       159 my ($file,$line) = (caller)[1,2] if($self->{debug});
338              
339 58 100       128 if ($self->{langnext}) {
340 4         12 $attrs->{'xml:lang'} = delete $self->{langnext};
341             }
342 58 100       127 if ($self->{dirnext}) {
343 1         3 $attrs->{'dir'} = delete $self->{dirnext};
344             }
345              
346 58         103 my $e;
347 58 100       119 if ($self->{current}) {
348             $e = XML::API::Element->new(
349             element => $element,
350             attrs => $attrs,
351             ns => $namespace,
352             parent => $self->{current},
353 44         123 );
354 44         114 $self->_add($e);
355             }
356             else {
357 14         70 $e = XML::API::Element->new(
358             element => $element,
359             attrs => $attrs,
360             ns => $namespace,
361             );
362 14         22 push(@{$self->{elements}}, $e);
  14         39  
363             }
364              
365 58         92 $self->{current} = $e;
366 58 100       124 if ($self->{_raw}) {
367 1         5 $self->_raw(@content);
368             }
369             else {
370 57         131 $self->_add(@content);
371             }
372              
373             $self->_comment("DEBUG: '$element' (open) at $file:$line")
374 58 50       136 if($self->{debug});
375              
376 58         127 return $e;
377             }
378              
379              
380              
381             sub _add {
382 107     107   152 my $self = shift;
383 107         136 $self->{string} = undef;
384 107 50       266 if (!$self->{current}) {
385 0         0 croak 'Cannot use _add with no current element';
386             }
387              
388 107         205 foreach my $item (@_) {
389 78 50       162 carp "undefined input" unless(defined($item));
390 78 100       100 if ( eval { $item->isa( __PACKAGE__ ) } ) {
  78         423  
391 1 50       8 if (refaddr($item) == refaddr($self)) {
392 0         0 croak 'Cannot _add object to itself';
393             }
394 1 50       4 if (!$self->{current}) {
395 0         0 push(@{$self->{elements}}, $item);
  0         0  
396             }
397             else {
398 1         3 $self->{current}->add($item);
399             }
400 1         3 bless($item, ref($self));
401 1         2 $item->{parent} = $self;
402 1         4 weaken($item->{parent});
403              
404 1         2 foreach my $lang (keys %{$item->{langs}}) {
  1         5  
405 0         0 $self->{langs}->{$lang} = 1;
406             }
407             }
408             else {
409 77 100       89 if ( eval { $item->isa( 'XML::API::Element' ) } ) {
  77 100       336  
410 44         110 $self->{current}->add($item);
411             }
412 33         186 elsif ( eval { $item->isa( 'XML::API::Cache' ) } ) {
413 1         4 foreach my $lang ($item->langs) {
414 1         3 $self->{langs}->{$lang} = 1;
415             }
416 1         3 $self->{current}->add($item);
417             }
418             else {
419 32         76 $self->{current}->add(_escapeXML($item));
420             }
421             }
422             }
423             }
424              
425              
426             sub _raw {
427 4     4   7 my $self = shift;
428 4         7 $self->{string} = undef;
429 4         7 foreach my $item (@_) {
430 4 50       11 carp "undefined input" unless(defined($item));
431 4 50 66     30 if (ref($item) and $item->isa( __PACKAGE__ )) {
432 0         0 croak 'Cannot add XML::API objects as raw';
433             }
434 4 100       10 if ($self->{current}) {
435 3         7 $self->{current}->add($item);
436             }
437             else {
438 1         2 push(@{$self->{elements}}, $item);
  1         4  
439             }
440             }
441             }
442              
443              
444             sub _close {
445 46     46   66 my $self = shift;
446 46   33     124 my $element = shift || croak '_close($element)';
447              
448 46 50       151 my ($file,$line) = (caller)[1,2] if($self->{debug});
449              
450 46 50       107 if (!$self->{current}) {
451 0         0 carp 'attempt to close non-existent element "' . $element . '"';
452 0         0 return;
453             }
454              
455 46 50       106 if ($element eq $self->{current}->{element}) {
456 46 100       104 if ($self->{current}->parent) {
457 39         111 $self->{current} = $self->{current}->parent;
458 39 50       97 $self->_comment("DEBUG: '$element' close at $file:$line") if($self->{debug});
459             }
460             else {
461 7         16 $self->{current} = undef;
462             }
463             }
464             else {
465             carp 'attempted to close element "' . $element . '" when current ' .
466 0         0 'element is "' . $self->{current}->{element} . '"';
467             }
468 46         208 return;
469             }
470              
471              
472             sub _element {
473 3     3   10 my $self = shift;
474 3   33     11 my $element = shift || croak '_element($element)';
475 3         8 my $e = $self->_open($element,@_);
476 3         9 $self->_close($element);
477 3         6 return $e;
478             }
479              
480              
481             #
482             # The implementation for element, element_open and element_close
483             #
484              
485             sub AUTOLOAD {
486 60     60   431 my $self = shift;
487 60         86 my $element = $AUTOLOAD;
488              
489 60         86 my ($open, $close) = (0,0);
490              
491 60 100       323 if ($element =~ s/.*::(.+)_open$/$1/o) {
    100          
    100          
492 23         39 my $old_ns = $self->{namespace};
493              
494 23 50       58 if ($element =~ s/(.+)__(.+)/$2/o) {
495 0         0 $self->{namespace} = $1;
496             }
497              
498 23         74 my $e = $self->_open($element, @_);
499 23         46 $self->{namespace} = $old_ns;
500 23         130 return $e;
501             }
502             elsif ($element =~ s/.*::(.+)_close$/$1/o) {
503 11         14 $element =~ s/(.+)__(.+)/$2/o;
504 11         30 return $self->_close($element);
505             }
506             elsif ($element =~ s/.*::(.+)_raw$/$1/o) {
507 1         2 $element =~ s/(.+)__(.+)/$2/o;
508 1         3 $self->{_raw} = 1;
509 1         4 $self->_open($element, @_);
510 1         2 $self->{_raw} = 0;
511 1         3 return $self->_close($element);
512             }
513              
514 25         92 $element =~ s/.*:://o;
515 25 50       60 croak 'element not defined' unless($element);
516              
517 25 50       57 if ($element =~ /^_/o) {
518 0         0 croak 'Undefined subroutine &' . ref($self) . "::$element called";
519             }
520              
521 25         37 my $old_ns = $self->{namespace};
522              
523 25 100       61 if ($element =~ s/(.+)__(.+)/$2/o) {
524 1         3 $self->{namespace} = $1;
525             }
526 25         58 my $e = $self->_open($element, @_);
527 25         45 $self->{namespace} = $old_ns;
528 25         54 $self->_close($element);
529 25         253 return $e;
530             }
531              
532              
533             sub _ast {
534 1     1   8 my $self = shift;
535              
536 1         5 foreach my $i (1 .. int(scalar(@_) / 2)) {
537 1         4 my ($e,$val) = splice(@_,0,2);
538              
539 1 50       5 if (!ref($val)) {
540 0         0 $self->_element($e,$val);
541 0         0 next;
542             }
543              
544 1         2 my $attr = {};
545 1         3 my @contents = ();
546              
547 1 50 33     8 if (ref($val) and ref($val) eq 'ARRAY') {
548 1         3 my @val = @$val;
549              
550 1         3 foreach my $i (1 .. int(scalar(@val) / 2)) {
551 1         3 my ($arg,$arg2) = splice(@val,0,2);
552              
553 1 50 33     8 if ($arg =~ s/^-(.+)/$1/o) {
    50          
554 0         0 $attr->{$arg} = $arg2;
555             }
556             elsif (ref($arg2) and ref($arg2) eq 'ARRAY') {
557 0         0 push(@contents, [$arg, $arg2]);
558             }
559             else {
560 1         4 push(@contents, {$arg => $arg2});
561             }
562             }
563              
564 1 50       4 push(@contents, @val) if(@val);
565             }
566             else {
567 0         0 push(@contents, $val);
568             }
569 1         3 $self->_open($e,$attr);
570              
571 1         2 foreach my $c (@contents) {
572 1 50 33     19 if (ref($c) and ref($c) eq 'ARRAY') {
    50 33        
573 0         0 $self->_ast(@$c);
574             }
575             elsif (ref($c) and ref($c) eq 'HASH') {
576 1         4 my ($k,$v) = each %$c;
577 1         5 $self->_open($k);
578 1         3 $self->_add($v);
579 1         5 $self->_close($k);
580             }
581             else {
582 0         0 $self->_add($c);
583             }
584             }
585              
586 1         2 $self->_close($e);
587             }
588              
589 1         3 return;
590             }
591              
592              
593             sub _comment {
594 1     1   6 my $self = shift;
595             # FIXME: should escape?
596 1         8 $self->_raw(XML::API::Element->new(comment => join('',@_)));
597 1         13 return;
598             }
599              
600              
601             sub _cdata {
602 1     1   2 my $self = shift;
603 1         4 $self->_raw(XML::API::Element->new(cdata => join('',@_)));
604 1         3 return;
605             }
606              
607              
608             sub _css {
609 0     0   0 my $self = shift;
610 0         0 my $content = shift;
611 0 0       0 if ($content =~ /\n/s) {
612 0         0 $self->_raw('/**/');
613             }
614             else {
615 0         0 $self->_raw('/**/');
616             }
617 0         0 return;
618             }
619              
620              
621             sub _javascript {
622 0     0   0 my $self = shift;
623 0         0 $self->script_open(-type => 'text/javascript');
624 0         0 $self->_raw('// -------- JavaScript Begin --------
625 0         0 $self->_raw(@_);
626 0         0 $self->_raw('// --------- JavaScript End --------- ]]>');
627 0         0 $self->script_close;
628 0         0 return;
629             }
630              
631              
632             sub _parse {
633 2     2   538 my $self = shift;
634 2         5 my $current = $self->{current};
635              
636 2         6 foreach (@_) {
637 2 50 33     31 next unless(defined($_) and $_ ne '');
638 2         5 local $XML::SAX::ParserPackage = 'XML::LibXML::SAX';
639 2         15 my $parser = XML::SAX::ParserFactory->parser(
640             Handler => XML::API::SAXHandler->new(xmlapi => $self),
641             );
642              
643             # remove leading and trailing space, otherwise SAX barfs at us.
644 0         0 (my $t = $_) =~ s/(^\s+)|(\s+$)//go;
645             # escape '&' as well
646 0         0 $t =~ s/\&(\w+\;)/__AMP__$1/go;
647             # escape '&' in urls
648 0         0 $t =~ s/\&(\w+=)/__AMP__amp;$1/go;
649 0         0 $parser->parse_string('<_xml_api_ignore>'.$t.'');
650             }
651              
652             # always make sure that we finish where we started
653 0         0 $self->{current} = $current;
654             }
655              
656              
657             sub _parse_chunk {
658 0     0   0 my $self = shift;
659 0         0 my $current = $self->{current};
660              
661 0         0 foreach (@_) {
662 0 0 0     0 next unless(defined($_) and $_ ne '');
663 0         0 local $XML::SAX::ParserPackage = 'XML::LibXML::SAX';
664 0         0 my $parser = XML::SAX::ParserFactory->parser(
665             Handler => XML::API::SAXHandler->new(xmlapi => $self),
666             );
667              
668             # remove leading and trailing space, otherwise SAX barfs at us.
669 0         0 (my $t = $_) =~ s/(^\s+)|(\s+$)//go;
670             # escape '&' as well
671 0         0 $t =~ s/\&(\w+\;)/__AMP__$1/go;
672             # escape '&' in urls
673 0         0 $t =~ s/\&(\w+=)/__AMP__amp;$1/go;
674             # escape '&' on their own.
675 0         0 $t =~ s/(\W)\&(\W)/$1__AMP__amp;$2/go;
676 0         0 $parser->parse_chunk($t);
677             }
678              
679             # always make sure that we finish where we started
680 0         0 $self->{current} = $current;
681             }
682              
683              
684             sub _attrs {
685 5     5   10 my $self = shift;
686              
687 5 100       12 if (@_) {
688 2         3 my $attrs = shift;
689 2 50 33     18 if (!$attrs or ref($attrs) ne 'HASH') {
690 0         0 croak 'usage: _attrs($hashref)';
691             }
692 2         5 $self->{current}->{attrs} = $attrs;
693             }
694 5         24 return $self->{current}->{attrs};
695             }
696              
697              
698             sub _encoding {
699 0     0   0 my $self = shift;
700 0 0       0 if (@_) {
701 0         0 $self->{encoding} = shift;
702             }
703 0         0 return $self->{encoding};
704             }
705              
706              
707             sub _set_lang {
708 5     5   18 my $self = shift;
709 5   33     15 my $lang = shift || croak 'usage: set_lang($lang)';
710 5         7 my $dir = shift;
711              
712 5 100 100     26 if ($self->{has_root_element} and !$self->_lang) {
713 1         3 $self->{elements}->[0]->{attrs}->{'xml:lang'} = $lang;
714 1 50       3 if ($dir) {
715 0         0 $self->{elements}->[0]->{attrs}->{'dir'} = $dir;
716             }
717             }
718             else {
719 4         10 $self->{langnext} = $lang;
720 4 100       14 $self->{dirnext} = $dir if($dir);
721             }
722 5         13 $self->{langs}->{$lang} = 1;
723              
724 5         12 return;
725             }
726              
727              
728             sub _lang {
729 8     8   22 my $self = shift;
730              
731 8 100       24 if ($self->{current}) {
732             return $self->{current}->{attrs}->{'xml:lang'}
733 7 100       35 if(exists($self->{current}->{attrs}->{'xml:lang'}));
734              
735 2         3 my $e = $self->{current};
736 2         8 while ($e = $e->{parent}) {
737             return $e->{attrs}->{'xml:lang'}
738 3 100       14 if(exists($e->{attrs}->{'xml:lang'}));
739             }
740             }
741 2 50       6 return $self->{langnext} if ($self->{langnext});
742 2 50       6 return $self->{parent}->_lang if ($self->{parent});
743 2         12 return;
744             }
745              
746              
747             sub _langs {
748 8     8   315 my $self = shift;
749 8         9 return keys %{$self->{langs}};
  8         45  
750             }
751              
752              
753             sub _dir {
754 5     5   9 my $self = shift;
755              
756 5 100       16 if ($self->{current}) {
757             return $self->{current}->{attrs}->{'dir'}
758 4 100       16 if(exists($self->{current}->{attrs}->{'dir'}));
759              
760 3         31 my $e = $self->{current};
761 3         10 while ($e = $e->{parent}) {
762             return $e->{attrs}->{'dir'}
763 0 0       0 if(exists($e->{attrs}->{'dir'}));
764             }
765             }
766 4 50       11 return $self->{dirnext} if ($self->{dirnext});
767 4 50       9 return $self->{parent}->_dir if ($self->{parent});
768 4         13 return;
769             }
770              
771              
772             sub _ns {
773 0     0   0 my $self = shift;
774 0 0       0 if (@_) {
775 0         0 $self->{namespace} = shift;
776             }
777 0         0 return $self->{namespace};
778             }
779              
780              
781             sub _debug {
782 0     0   0 my $self = shift;
783 0 0       0 if (@_) {
784 0         0 $self->{debug} = shift;
785             }
786 0         0 return $self->{debug};
787             }
788              
789              
790             sub _current {
791 0     0   0 my $self = shift;
792 0         0 return $self->{current};
793             }
794              
795             sub _set_id {
796 0     0   0 my $self = shift;
797 0         0 my $id = shift;
798              
799 0 0 0     0 if (!defined($id) or $id eq '') {
800 0         0 carp '_set_id called without a valid id';
801 0         0 return;
802             }
803 0 0       0 if (defined($self->{ids}->{$id})) {
804 0         0 carp 'id '.$id.' already defined - overwriting';
805             }
806 0         0 $self->{ids}->{$id} = $self->{current};
807             }
808              
809              
810             sub _goto {
811 0     0   0 my $self = shift;
812              
813 0 0       0 if (@_) {
814 0         0 my $id = shift;
815 0 0       0 if (!defined $id) {
816 0         0 $self->{current} = undef;
817 0         0 return;
818             }
819 0 0       0 if ($id->isa( 'XML::API::Element' )) {
    0          
820 0         0 $self->{current} = $id;
821             }
822             elsif (defined($self->{ids}->{$id})) {
823 0         0 $self->{current} = $self->{ids}->{$id};
824             }
825             else {
826             carp "Nonexistent ID given to _goto: '$id'. ",
827 0         0 '(Known IDs: ', join(',',keys(%{$self->{ids}})),')';
  0         0  
828 0         0 $self->{current} = undef;
829             }
830             }
831 0         0 return $self->{current};
832             }
833              
834              
835             sub _as_string {
836 23     23   1519 my $self = shift;
837 23         33 my $file = shift;
838              
839 23         35 my $string = '';
840              
841 23 50 66     104 if (ref($self) eq __PACKAGE__ or $self->{has_root_element}) {
842 23         65 $string = qq{{encoding}" ?>\n};
843 23 100       59 $string .= $self->_doctype . "\n" if($self->_doctype);
844             }
845              
846             $string .= join("\n", map {
847             $_->isa( __PACKAGE__ )
848 24 50       148 ? join("\n", map {$_->as_string} $_->_elements)
  0         0  
849             : $_->as_string('', ' ')
850 23         40 } @{$self->{elements}});
  23         82  
851              
852 23 50       67 if ($file) {
853 0 0       0 open(FH, '>'.$file) || die "open '$file': $!";
854 0 0       0 if ($self->_encoding eq 'UTF-8') {
855 0         0 binmode(FH, ':utf8');
856             }
857 0         0 print FH $string;
858 0         0 close(FH);
859 0         0 return;
860             }
861 23         129 return $string;
862             }
863              
864              
865             sub _fast_string {
866 1     1   2 my $self = shift;
867 1         2 my $file = shift;
868              
869 1         2 my $string = '';
870              
871 1 50 33     9 if (ref($self) eq __PACKAGE__ or $self->{has_root_element}) {
872 1         3 $string = qq{{encoding}" ?>};
873 1 50       3 $string .= $self->_doctype if($self->_doctype);
874             }
875              
876 1         2 $string .= join("\n", map {$_->fast_string('', ' ')} @{$self->{elements}});
  1         3  
  1         3  
877              
878 1 50       4 if ($file) {
879 0 0       0 open(FH, '>'.$file) || die "open '$file': $!";
880 0 0       0 if ($self->_encoding eq 'UTF-8') {
881 0         0 binmode(FH, ':utf8');
882             }
883 0         0 print FH $string;
884 0         0 close(FH);
885 0         0 return;
886             }
887 1         3 return $string;
888             }
889              
890              
891             sub _escapeXML {
892 41     41   70 my $data = $_[0];
893 41 50       88 return '' unless(defined($data));
894 41 100       131 if ($data =~ /[\&\<\>\"(__AMP__)\']/o) {
895 10         30 $data =~ s/\&(?!\w+\;)/\&\;/go;
896 10         22 $data =~ s/\
897 10         17 $data =~ s/\>/\>\;/go;
898 10         14 $data =~ s/\"/\"\;/go;
899 10         19 $data =~ s/__AMP__/\&/go;
900 10         16 $data =~ s/\'/\&apos\;/go;
901             }
902 41         118 return $data;
903             }
904              
905              
906             #
907             # We must specify the DESTROY function explicitly otherwise our AUTOLOAD
908             # function gets called at object death.
909             #
910       0     DESTROY {};
911              
912              
913             1;
914             __END__