File Coverage

blib/lib/XML/Writer/Simple.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package XML::Writer::Simple;
2             $XML::Writer::Simple::VERSION = '0.12';
3 7     7   146617 use warnings;
  7         15  
  7         268  
4 7     7   39 use strict;
  7         9  
  7         235  
5 7     7   35 use Exporter ();
  7         22  
  7         151  
6 7     7   51 use vars qw/@ISA @EXPORT/;
  7         14  
  7         451  
7 7     7   6771 use XML::DT;
  0            
  0            
8             use XML::DTDParser qw/ParseDTDFile/;
9              
10             =encoding utf-8
11              
12             =head1 NAME
13              
14             XML::Writer::Simple - Create XML files easily!
15              
16             =cut
17              
18             @ISA = qw/Exporter/;
19             @EXPORT = (qw/powertag xml_header quote_entities/);
20             our %PTAGS = ();
21             our $MODULENAME = "XML::Writer::Simple";
22              
23             our $IS_HTML = 0;
24             our %TAG_SET = (
25             html => {
26             tags => [qw.a abbr acronym address area
27             b base bdo big blockquote body br button
28             caption cite code col colgroup
29             dd del dfn div dl dt
30             em
31             fieldset form frame frameset
32             h1 h2 h3 h4 h5 h6 head hr html
33             i iframe img input ins
34             kbd
35             label legend li link
36             map meta
37             noframes noscript
38             object ol optgroup option
39             p param pre
40             q
41             samp script select small span strong style sub sup
42             table tbody td textarea tfoot th thead title Tr tt
43             u ul var.]
44             },
45             );
46              
47             =head1 SYNOPSIS
48              
49             use XML::Writer::Simple dtd => "file.dtd";
50              
51             print xml_header(encoding => 'iso-8859-1');
52             print para("foo",b("bar"),"zbr");
53              
54              
55             # if you want CGI but you do not want CGI :)
56             use XML::Writer::Simple ':html';
57              
58             =head1 USAGE
59              
60             This module takes some ideas from CGI to make easier the life for
61             those who need to generated XML code. You can use the module in three
62             flavours (or combine them):
63              
64             =over 4
65              
66             =item tags
67              
68             When importing the module you can specify the tags you will be using:
69              
70             use XML::Writer::Simple tags => [qw/p b i tt/];
71              
72             print p("Hey, ",b("you"),"! ", i("Yes ", b("you")));
73              
74             that will generate
75              
76            

Hey you! Yes you

77              
78             =item dtd
79              
80             You can supply a DTD, that will be analyzed, and the tags used:
81              
82             use XML::Writer::Simple dtd => "tmx.dtd";
83              
84             print tu(seg("foo"),seg("bar"));
85              
86             =item xml
87              
88             You can supply an XML (or a reference to a list of XML files). They
89             will be parsed, and the tags used:
90              
91             use XML::Writer::Simple xml => "foo.xml";
92              
93             print foo("bar");
94              
95             =item partial
96              
97             You can supply an 'partial' key, to generate prototypes for partial tags
98             construction. For instance:
99              
100             use XML::Writer::Simple tags => qw/foo bar/, partial => 1;
101              
102             print start_foo;
103             print ...
104             print end_foo;
105              
106             =back
107              
108             You can also use tagsets, where sets of tags from a well known format
109             are imported. For example, to use HTML:
110              
111             use XML::Writer::Simple ':html';
112              
113             =head1 EXPORT
114              
115             This module export one function for each element at the dtd or xml
116             file you are using. See below for details.
117              
118             =head1 FUNCTIONS
119              
120             =head2 import
121              
122             Used when you 'use' the module, should not be used directly.
123              
124             =head2 xml_header
125              
126             This function returns the xml header string, without encoding
127             definition, with a trailing new line. Default XML encoding should
128             be UTF-8, by the way.
129              
130             You can force an encoding passing it as argument:
131              
132             print xml_header(encoding=>'iso-8859-1');
133              
134             =head2 powertag
135              
136             Used to specify a powertag. For instance:
137              
138             powertag("ul","li");
139              
140             ul_li([qw/foo bar zbr ugh/]);
141              
142             will generate
143              
144            
145            
  • foo
  • 146            
  • bar
  • 147            
  • zbr
  • 148            
  • ugh
  • 149            
    150              
    151             You can also supply this information when loading the module, with
    152              
    153             use XML::Writer::Simple powertags=>["ul_li","ol_li"];
    154              
    155             Powertags support three level tags as well:
    156              
    157             use XML::Writer::Simple powertags=>["table_tr_td"];
    158              
    159             print table_tr_td(['a','b','c'],['d','e','f']);
    160              
    161             =head2 quote_entities
    162              
    163             To use the special characters C<< < >>, C<< > >> and C<< & >> on your PCDATA content you need
    164             to protect them. You can either do that yourself or call this function.
    165              
    166             print f(quote_entities("a < b"));
    167              
    168             =cut
    169              
    170             sub xml_header {
    171             my %ops = @_;
    172             my $encoding = "";
    173             $encoding =" encoding=\"$ops{encoding}\"" if exists $ops{encoding};
    174             return "\n";
    175             }
    176              
    177             sub powertag {
    178             my $nfunc = join("_", @_);
    179             $PTAGS{$nfunc}=[@_];
    180             push @EXPORT, $nfunc;
    181             XML::Writer::Simple->export_to_level(1, $MODULENAME, $nfunc);
    182             }
    183              
    184             sub _xml_from {
    185             my ($tag, $attrs, @body) = @_;
    186             return (ref($body[0]) eq "ARRAY")?
    187             join("", map{ _toxml($tag, $attrs, $_) } @{$body[0]})
    188             :_toxml($tag, $attrs, join("", @body));
    189             }
    190              
    191             sub _clean_attrs {
    192             my $attrs = shift;
    193             for (keys %$attrs) {
    194             if (m!^-!) {
    195             $attrs->{$'}=$attrs->{$_};
    196             delete($attrs->{$_});
    197             }
    198             }
    199             return $attrs;
    200             }
    201              
    202             sub _toxml {
    203             my ($tag,$attr,$contents) = @_;
    204             if (defined($contents) && $contents ne "") {
    205             return _start_tag($tag,$attr) . $contents . _close_tag($tag);
    206             }
    207             else {
    208             return _empty_tag($tag,$attr);
    209             }
    210             }
    211              
    212             sub _go_down {
    213             my ($tags, @values) = @_;
    214             my $tag = shift @$tags;
    215              
    216             if (@$tags) {
    217             join("",
    218             map {
    219             my $attrs = {};
    220             if (ref($_->[0]) eq 'HASH') {
    221             $attrs = _clean_attrs(shift @$_);
    222             }
    223             _xml_from($tag,$attrs,_go_down([@$tags],@$_)) } ### REALLY NEED TO COPY
    224             @values)
    225             } else {
    226             join("",
    227             map { _xml_from($tag,{},$_) } @values)
    228             }
    229             }
    230              
    231             sub AUTOLOAD {
    232             my $attrs = {};
    233             my $tag = our $AUTOLOAD;
    234              
    235             $tag =~ s!${MODULENAME}::!!;
    236              
    237             $attrs = shift if ref($_[0]) eq "HASH";
    238             $attrs = _clean_attrs($attrs);
    239              
    240             if (exists($PTAGS{$tag})) {
    241             my @tags = @{$PTAGS{$tag}};
    242             my $toptag = shift @tags;
    243             return _xml_from($toptag, $attrs,
    244             _go_down(\@tags, @_));
    245             }
    246             else {
    247             if ($tag =~ m/^end_(.*)$/) {
    248             return _close_tag($1)."\n";
    249             }
    250             elsif ($tag =~ m/^start_(.*)$/) {
    251             return _start_tag($1, $attrs)."\n";
    252             }
    253             else {
    254             return _xml_from($tag,$attrs,@_);
    255             }
    256             }
    257             }
    258              
    259             sub quote_entities {
    260             my $s = shift;
    261             $s =~ s/&/&/g;
    262             $s =~ s/
    263             $s =~ s/>/>/g;
    264             return $s;
    265             }
    266              
    267             sub _quote_attr {
    268             my $s = shift;
    269             $s =~ s/&/&/g;
    270             $s =~ s/"/"/g;
    271             return $s;
    272             }
    273              
    274             sub _attributes {
    275             my $attr = shift;
    276             return join(" ", map { "$_=\"" . _quote_attr($attr->{$_}) . "\""} keys %$attr);
    277             }
    278              
    279             sub _start_tag {
    280             my ($tag, $attr) = @_;
    281             $tag = "tr" if $tag eq "Tr" && $IS_HTML;
    282             $attr = _attributes($attr);
    283             if ($attr) {
    284             return "<$tag $attr>"
    285             } else {
    286             return "<$tag>"
    287             }
    288             }
    289              
    290             sub _empty_tag {
    291             my ($tag, $attr) = @_;
    292             $tag = "tr" if $tag eq "Tr" && $IS_HTML;
    293             $attr = _attributes($attr);
    294             if ($attr) {
    295             return "<$tag $attr/>"
    296             } else {
    297             return "<$tag/>"
    298             }
    299             }
    300              
    301             sub _close_tag {
    302             my $tag = shift;
    303             $tag = "tr" if $tag eq "Tr" && $IS_HTML;
    304             return "";
    305             }
    306              
    307              
    308             sub import {
    309             my $class = shift;
    310              
    311             my @tags;
    312             my @ptags;
    313             while ($_[0] && $_[0] =~ m!^:(.*)$!) {
    314             shift;
    315             my $pack = $1;
    316             $IS_HTML = 1 if $pack eq "html";
    317             if (exists($TAG_SET{$pack})) {
    318             push @tags => exists $TAG_SET{$pack}{tags} ? @{$TAG_SET{$pack}{tags}} : ();
    319             push @ptags => exists $TAG_SET{$pack}{ptags} ? @{$TAG_SET{$pack}{ptags}} : ();
    320             } else {
    321             die "XML::Writer::Simple - Unknown tagset :$pack\n";
    322             }
    323             }
    324              
    325             my %opts = @_;
    326              
    327             if (exists($opts{tags})) {
    328             if (ref($opts{tags}) eq "ARRAY") {
    329             push @tags => @{$opts{tags}};
    330             }
    331             }
    332              
    333             if (exists($opts{xml})) {
    334             my @xmls = (ref($opts{xml}) eq "ARRAY")?@{$opts{xml}}:($opts{xml});
    335             my $tags;
    336             for my $xml (@xmls) {
    337             dt($xml, -default => sub { $tags->{$q}++ });
    338             }
    339             push @tags => keys %$tags;
    340             }
    341              
    342             if (exists($opts{dtd})) {
    343             my $DTD = ParseDTDFile($opts{dtd});
    344             push @tags => keys %$DTD;
    345             }
    346              
    347             push @EXPORT => @tags;
    348             if (exists($opts{partial})) {
    349             push @EXPORT => map { "start_$_" } @tags;
    350             push @EXPORT => map { "end_$_" } @tags;
    351             }
    352              
    353             if (@ptags || exists($opts{powertags})) {
    354             push @ptags => @{$opts{powertags}} if exists $opts{powertags};
    355             @PTAGS{@ptags} = map { [split/_/] } @ptags;
    356             push @EXPORT => @ptags;
    357             }
    358              
    359             XML::Writer::Simple->export_to_level(1, $class, @EXPORT);
    360             }
    361              
    362             =head1 AUTHOR
    363              
    364             Alberto Simões, C<< >>
    365              
    366             =head1 BUGS
    367              
    368             Please report any bugs or feature requests to
    369             C, or through the web interface at
    370             L.
    371             I will be notified, and then you'll automatically be notified of progress on
    372             your bug as I make changes.
    373              
    374             =head1 COPYRIGHT AND LICENSE
    375              
    376             Copyright 1999-2012 Project Natura.
    377              
    378             This library is free software; you can redistribute it and/or modify
    379             it under the same terms as Perl itself.
    380              
    381             =cut
    382              
    383             1; # End of XML::Writer::Simple