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

Hey you! Yes you

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