File Coverage

blib/lib/XML/SAXDriver/CSV.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package XML::SAXDriver::CSV;
2            
3 1     1   8240 use strict;
  1         3  
  1         41  
4            
5 1     1   1724 use Text::CSV_XS;
  1         15208  
  1         97  
6            
7 1     1   13 use base qw(XML::SAX::Base);
  1         9  
  1         6317  
8             use vars qw($VERSION $NS_SAXDriver_CSV);
9             $VERSION = '0.07';
10             $NS_SAXDriver_CSV = 'http://xmlns.perl.org/sax/XML::SAXDriver::CSV';
11            
12             sub _parse_bytestream
13             {
14             my $self = shift;
15             my $stream = shift;
16            
17             $self->_parse_CSV(\$stream);
18             }
19             sub _parse_string
20             {
21             my $self = shift;
22             my $stream = shift;
23            
24             my @strings = split("\n", $self->{ParseOptions}->{Source}{String});
25             $self->_parse_CSV(\@strings);
26             }
27             sub _parse_systemid
28             {
29             my $self = shift;
30             my $path = shift;
31            
32             require IO::File;
33             my $ioref = IO::File->new($self->{ParseOptions}->{Source}{SystemId})
34             || die "Cannot open SystemId '$self->{ParseOptions}->{Source}{SystemId}' : $!";
35            
36             $self->_parse_CSV($ioref);
37             }
38            
39             sub _parse_CSV
40             {
41            
42             my $self = shift;
43             my $source = shift;
44            
45             $self->{ParseOptions}->{Parser} ||= Text::CSV_XS->new();
46            
47             $self->{ParseOptions}->{Declaration}->{Version} ||= '1.0';
48            
49             my $document = {};
50             $self->start_document($document);
51             $self->xml_decl($self->{ParseOptions}->{Declaration});
52             my $pm_csv = $self->_create_node(
53             Prefix => 'SAXDriver::CSV',
54             NamespaceURI => $NS_SAXDriver_CSV,
55             );
56             $self->start_prefix_mapping($pm_csv);
57             $self->end_prefix_mapping($pm_csv);
58            
59             my $doc_element = {
60             Name => $self->{ParseOptions}->{File_Tag} || "records",
61             Attributes => {},
62             };
63            
64             $self->start_element($doc_element);
65            
66             $self->{ParseOptions}->{Col_Headings} ||= [];
67            
68             $self->{ParseOptions}->{Headings_Handler} ||= \&_normalize_heading;
69            
70             while (my $row = _get_row($self->{ParseOptions}->{Parser}, $source)) {
71             my $el = {
72             Name => $self->{ParseOptions}->{Parent_Tag} || "record",
73             Attributes => {},
74             };
75            
76             if (!@{$self->{ParseOptions}->{Col_Headings}} && !$self->{ParseOptions}->{Dynamic_Col_Headings})
77             {
78             my $i = 1;
79             @{$self->{ParseOptions}->{Col_Headings}} = map { "column" . $i++ } @$row;
80             }
81             elsif (!@{$self->{ParseOptions}->{Col_Headings}} && $self->{ParseOptions}->{Dynamic_Col_Headings})
82             {
83             @{$self->{ParseOptions}->{Col_Headings}} = map { $self->{ParseOptions}->{Headings_Handler}->($_, $self->{ParseOptions}->{SubChar}); } @$row;
84             next; # causes the first (heading) row to be skipped
85            
86             }
87            
88             $self->start_element($el);
89            
90             for (my $i = 0; $i <= $#{$row}; $i++) {
91             my $column = { Name => $self->{ParseOptions}->{Col_Headings}->[$i], Attributes => {} };
92            
93             $self->start_element($column);
94             $self->characters({Data => $row->[$i]});
95             $self->end_element($column);
96             }
97            
98             $self->end_element($el);
99             }
100            
101             $self->end_element($doc_element);
102            
103             return $self->end_document($document);
104             }
105            
106             sub _normalize_heading ### Default if no Headings_Handler is provided
107             {
108             my $heading= shift;
109             my $sub_char = shift || '_';
110             $heading =~ s/^\s//g;
111             $heading =~ s/\s$//g;
112             $heading =~ s/^([^a-zA-Z|^_|^:])/$sub_char/g; ### We used to also replace the xml in the beginning, but I took it of per recommendation of Michael Rodriguez.
113             $heading =~ s/[^a-zA-Z|^-|^.|^0-9|^:]/$sub_char/g;
114             return $heading;
115             }
116            
117             sub _get_row {
118             my ($parser, $source, $strings) = @_;
119            
120             if (ref($source) eq "ARRAY")
121             {
122             my $line = shift @$source;
123             if ($line && $parser->parse($line)) {
124             return [$parser->fields()];
125             }
126             }
127             else
128             {
129             my $line = <$source>;
130             if ($line && $parser->parse($line)) {
131             return [$parser->fields()];
132             }
133             }
134            
135             return;
136             }
137            
138             sub _create_node {
139             shift;
140             # this may check for a factory later
141             return {@_};
142             }
143            
144             1;
145             __END__