File Coverage

blib/lib/Class/CSV.pm
Criterion Covered Total %
statement 70 249 28.1
branch 0 76 0.0
condition 0 38 0.0
subroutine 25 48 52.0
pod 6 7 85.7
total 101 418 24.1


line stmt bran cond sub pod time code
1             # Class::CSV
2             # Class Based CSV Parser/Writer
3             # Written by DJ
4             #
5             # $Id: CSV.pm,v 1.2 2005/03/07 02:43:48 david Exp $
6              
7             # Class::CSV::Base
8             package Class::CSV::Base;
9              
10 1     1   43040 use strict;
  1         3  
  1         189  
11 1     1   7 use warnings;
  1         2  
  1         298  
12              
13             BEGIN {
14             ## Modules
15             # Core
16 1     1   7 use Carp qw/confess/;
  1         14  
  1         876  
17              
18             # Base
19 1     1   7 use base qw(Class::Accessor);
  1         2  
  1         4547  
20              
21             ## Constants
22 1     1   5008 use constant TRUE => 1;
  1         4  
  1         138  
23 1     1   8 use constant FALSE => 0;
  1         3  
  1         67  
24              
25             ## Variables
26 1     1   7 use vars qw($VERSION);
  1         3  
  1         348  
27 1     1   4 $VERSION = do { my @r=(q$Revision: 1.3 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
  1         11  
  1         730  
28             }
29              
30             sub _build_fields {
31 0     0     my ($self, $fields) = @_;
32              
33 0 0 0       confess "Field list must be an array reference\n"
34             unless (defined $fields and ref $fields eq 'ARRAY');
35              
36 0           $self->{_field_list} = $fields;
37              
38             # make the accessors via Class::Accessor
39 0           __PACKAGE__->mk_accessors(@{$fields});
  0            
40              
41 0           foreach my $field (@{$fields}) {
  0            
42 0           $self->{__fields}->{$field} = TRUE;
43             }
44             }
45              
46             sub set {
47 0     0     my ($self, %items) = @_;
48              
49 0           foreach my $field (keys %items) {
50 0 0         if (exists $self->{__fields}->{$field}) {
51 0           $self->_set($field, $items{$field});
52             } else {
53 0           confess "Cannot set field: ". $field. " as it doesnt exist!\n";
54             }
55             }
56             }
57              
58             sub _set {
59 0     0     my ($self, $key, $value) = @_;
60              
61 0           return $self->{$key} = $value;
62             }
63              
64             sub get {
65 0     0     my ($self, @fields) = @_;
66              
67             # sanity check
68 0           foreach my $field (@fields) {
69 0 0         unless (exists $self->{__fields}->{$field}) {
70 0           confess "Cannot get field: ". $field. " as it doesnt exist!\n";
71             }
72             }
73              
74 0           return $self->_get(@fields);
75             }
76              
77             sub _get {
78 0     0     my $self = shift;
79              
80 0 0         if(@_ == 1) {
    0          
81 0           return $self->{$_[0]};
82             } elsif( @_ > 1 ) {
83 0           return @{$self}{@_};
  0            
84             }
85              
86 0           return;
87             }
88              
89             1;
90              
91             # Class::CSV::CSV_XS_Options
92             package Class::CSV::CSV_XS_Options;
93              
94 1     1   488 BEGIN {
95             ## Modules
96             # Core
97 1     1   10 use Carp qw/confess/;
  1         3  
  1         300  
98              
99             ## Constants
100 1     1   9 use constant TRUE => 1;
  1         3  
  1         72  
101 1     1   8 use constant FALSE => 0;
  1         2  
  1         80  
102              
103             # Base
104 1     1   8 use base qw(Class::CSV::Base);
  1         3  
  1         1102  
105             }
106              
107             sub new {
108 0     0     my ($class, $opts) = @_;
109              
110 0           my $self = bless({}, $class);
111              
112 0           $self->_build_fields([qw/quote_char eol escape_char sep_char binary
113             types always_quote/]);
114              
115 0 0         if (defined $opts) {
116 0 0         if (ref $opts eq 'HASH') {
117 0           $self->set(%{$opts});
  0            
118             } else {
119 0           confess "Please provide csv_xs_options as a HASH ref!\n";
120             }
121             }
122              
123 0           return $self;
124             }
125              
126             sub set {
127 0     0     my ($self, %items) = @_;
128              
129 0           foreach my $field (keys %items) {
130 0 0         unless (exists $self->{__fields}->{$field}) {
131 0           $self->{__fields}->{$field} = TRUE;
132 0           $self->mk_accessors($field);
133             }
134 0           $self->_set($field => $items{$field});
135             }
136             }
137              
138             sub to_hash_ref {
139 0     0     my ($self) = @_;
140              
141 0           my $hash = {};
142 0           foreach my $field (keys %{$self->{__fields}}) {
  0            
143 0           my $value = $self->get($field);
144 0 0         if (defined $value) {
145 0           $hash->{$field} = $value;
146             }
147             }
148              
149 0           return $hash;
150             }
151              
152             # Class::CSV::Line
153             package Class::CSV::Line;
154              
155 1     1   824 BEGIN {
156             ## Modules
157             # Core
158 1     1   201 use Carp qw/confess/;
  1         3  
  1         101  
159              
160             # CPAN
161 1     1   2706 use Text::CSV_XS;
  1         14420  
  1         119  
162              
163             ## Constants
164 1     1   13 use constant TRUE => 1;
  1         1  
  1         76  
165 1     1   4 use constant FALSE => 0;
  1         2  
  1         42  
166              
167             # Base
168 1     1   5 use base qw(Class::CSV::Base);
  1         1  
  1         479  
169             }
170              
171             sub new {
172 0     0     my ($class, %opts) = @_;
173              
174 0 0         confess "Please provide a list of fields\n"
175             unless (exists $opts{fields});
176              
177 0           my $self = bless({}, $class);
178              
179 0           $self->{__csv_xs_options} = $opts{csv_xs_options};
180              
181 0           $self->_build_fields($opts{fields});
182 0 0         $self->_do_parse($opts{line}) if (exists $opts{line});
183              
184 0           return $self;
185             }
186              
187             sub parse {
188 0     0     my ($class, %opts) = @_;
189              
190 0 0         confess "Please provide a line to parse\n"
191             unless (exists $opts{line});
192              
193 0           my $self = $class->new(%opts);
194              
195 0           $self->_do_parse($opts{line});
196              
197 0           return $self;
198             }
199              
200             sub _build_fields {
201 0     0     my ($self, $fields) = @_;
202              
203 0 0 0       confess "Field list must be an array reference\n"
204             unless (defined $fields and ref $fields eq 'ARRAY');
205              
206 0           $self->{_field_list} = $fields;
207              
208             # make the accessors via Class::Accessor
209 0           __PACKAGE__->mk_accessors(@{$fields});
  0            
210              
211 0           foreach my $field (@{$fields}) {
  0            
212 0           $self->{__fields}->{$field} = TRUE;
213 0           $self->_set($field, undef);
214             }
215             }
216              
217             sub _do_parse {
218 0     0     my ($self, $line) = @_;
219              
220 0 0 0       confess "Unable to find field array ref to build object with\n"
221             unless (defined $self->{_field_list}
222             and ref $self->{_field_list} eq 'ARRAY');
223              
224 0           my $csv = new Text::CSV_XS($self->{__csv_xs_options}->to_hash_ref());
225 0           my $r = $csv->parse($line);
226 0 0 0       if (defined $r and $r) {
227 0           my @columns = $csv->fields();
228 0           for (my $i = 0; $i < @columns; $i++) {
229 0           $self->set(${$self->{_field_list}}[$i], $columns[$i]);
  0            
230             }
231             } else {
232 0 0         if ($csv->error_input()) {
233 0           confess "Failed to parse line: ". $csv->error_input(). "\n";
234             } else {
235 0           confess "Failed to parse line: unknown reason\n";
236             }
237             }
238             }
239              
240             sub string {
241 0     0     my ($self) = @_;
242              
243 0 0 0       confess "Uninitiated Line Objects cannot be converted to a string!\n"
244             unless (exists $self->{_field_list}
245             and ref $self->{_field_list} eq 'ARRAY');
246              
247 0           my @cols = ();
248 0           foreach my $field (@{$self->{_field_list}}) {
  0            
249 0           push(@cols, $self->_get($field));
250             }
251              
252 0           my $csv = new Text::CSV_XS($self->{__csv_xs_options}->to_hash_ref());
253 0           my $r = $csv->combine(@cols);
254 0 0         if ($r) {
255 0           return $csv->string();
256             } else {
257 0           confess "Failed to create CSV line from line: ". $csv->error_input(). "\n"
258             }
259             }
260              
261             1;
262              
263              
264             # Class::CSV
265             package Class::CSV;
266              
267             BEGIN {
268             ## Modules
269             # Core
270 1     1   7 use Carp qw/confess/;
  1         2  
  1         55  
271              
272             # Base
273 1     1   5 use base qw(Class::CSV::Base);
  1         2  
  1         849  
274              
275             ## Constants
276 1     1   7 use constant TRUE => 1;
  1         1  
  1         55  
277 1     1   6 use constant FALSE => 0;
  1         1  
  1         51  
278              
279 1     1   5 use constant DEFAULT_LINE_SEPARATOR => "\n";
  1         1  
  1         64  
280              
281             ## Setup Accessors
282 1     1   12 __PACKAGE__->mk_ro_accessors(qw(fields));
283 1         106 __PACKAGE__->mk_accessors(qw(lines line_separator csv_xs_options));
284             }
285              
286             sub new {
287 0     0 1   my ($class, %opts) = @_;
288              
289 0           my $self = bless({}, $class);
290              
291 0 0 0       confess "Please provide an array ref of fields\n"
292             unless (exists $opts{fields}
293             and ref $opts{fields} eq 'ARRAY');
294              
295 0   0       $self->_private_set(
296             line_separator => $opts{line_separator} || DEFAULT_LINE_SEPARATOR,
297             csv_xs_options =>
298             new Class::CSV::CSV_XS_Options($opts{csv_xs_options}),
299             fields => $opts{fields},
300             lines => []
301             );
302              
303 0           return $self;
304             }
305              
306             sub parse {
307 0     0 1   my ($class, %opts) = @_;
308              
309 0           my $self = $class->new(%opts);
310              
311 0 0         if (exists $opts{classdbi_objects}) {
312 0           $opts{objects} = $opts{classdbi_objects};
313 0           delete($opts{classdbi_objects});
314             }
315              
316 0 0 0       if (exists $opts{filename} or exists $opts{filehandle}) {
    0          
317 0           $self->_do_parse(%opts);
318             } elsif (exists $opts{objects}) {
319 0           $self->_do_parse_objects(%opts);
320             } else {
321 0           confess "Please provide objects or a filename/filehandle to parse\n";
322             }
323              
324 0           return $self;
325             }
326              
327             sub _do_parse {
328 0     0     my ($self, %opts) = @_;
329              
330 0           my @CSV_Content = ();
331 0 0 0       if (exists $opts{'filename'} and defined $opts{'filename'}) {
    0 0        
332 0 0         confess "Cannot find filename: ". $opts{'filename'}. "\n"
333             unless (-f $opts{'filename'});
334 0 0         confess "Cannot read filename: ". $opts{'filename'}. "\n"
335             unless (-r $opts{'filename'});
336 0 0         open(CSV, $opts{'filename'})
337             or confess "Failed to open filename: ". $opts{'filename'}. ': '. $!. "\n";
338 0           while (my $line = ) {
339 0           push(@CSV_Content, $self->strip_crlf($line));
340             }
341 0           close(CSV);
342             } elsif (exists $opts{'filehandle'} and defined $opts{'filehandle'}) {
343 0 0         confess "filehandle provided is not a file handle\n"
344             unless (defined(fileno($opts{'filehandle'})));
345 0           my $fh = $opts{'filehandle'};
346 0           while (my $line = <$fh>) {
347 0           push(@CSV_Content, $self->strip_crlf($line));
348             }
349             } else {
350 0           confess "Please provide a filename/filehandle to parse\n";
351             }
352              
353 0           foreach my $line (@CSV_Content) {
354 0 0 0       unless ($line and $line !~ /^([,"']|\s)+$/) {
355             # Skip empty lines
356 0           next;
357             }
358 0           push(@{$self->{lines}}, $self->new_line(undef, { line => $line }));
  0            
359             }
360             }
361              
362             sub _do_parse_objects {
363 0     0     my ($self, %opts) = @_;
364              
365 0 0         confess "Please specify objects as an ARRAY ref!\n"
366             unless (ref $opts{objects} eq 'ARRAY');
367              
368 0           foreach my $object (@{$opts{objects}}) {
  0            
369 0           my $line = $self->new_line();
370              
371 0           foreach my $field (@{$self->fields()}) {
  0            
372 0 0         confess ((ref $object). " does not contain method ". $field. "\n")
373             unless ($object->can($field));
374              
375 0           $line->set( $field => $object->$field );
376             }
377              
378 0           push(@{$self->{lines}}, $line);
  0            
379             }
380             }
381              
382             sub new_line {
383 0     0 1   my ($self, $args, $opts) = @_;
384              
385 0           my %opts = ();
386 0 0 0       if ($opts and ref $opts eq 'HASH') {
387 0           %opts = %{$opts};
  0            
388             }
389              
390 0           my $line = new Class::CSV::Line(
391             fields => $self->fields(),
392             csv_xs_options => $self->csv_xs_options(),
393             %opts
394             );
395              
396 0 0         confess "Failed to create new line\n"
397             unless ($line);
398              
399 0 0         if (defined $args) {
400 0 0         if (ref $args eq 'ARRAY') {
    0          
401 0           my @dr_array = @{$args};
  0            
402 0           foreach my $field (@{$self->fields()}) {
  0            
403 0           my $value = shift @dr_array;
404 0           $line->set( $field => $value );
405             }
406             } elsif (ref $args eq 'HASH') {
407 0           foreach my $field (keys %{$args}) {
  0            
408 0           $line->set( $field => $args->{$field} );
409             }
410             } else {
411 0           confess "Need the arguments passed as either an ARRAY ref or a HASH ref!\n";
412             }
413             }
414              
415 0           return $line;
416             }
417              
418             sub add_line {
419 0     0 1   my ($self, $args) = @_;
420              
421 0 0 0       confess "Cannot call add_line without an argument!\n"
422             unless (defined $args and $args);
423              
424 0           my $line = $self->new_line($args);
425              
426 0           push(@{$self->{lines}}, $line);
  0            
427             }
428              
429             sub string {
430 0     0 1   my ($self) = @_;
431              
432 0 0         confess "No lines to write!\n" unless (ref $self->lines() eq 'ARRAY');
433              
434 0           my @string = ();
435 0           map { push(@string, $_->string()); } @{$self->lines()};
  0            
  0            
436              
437 0           return join($self->line_separator(), @string). $self->line_separator();
438             }
439              
440             sub print {
441 0     0 1   my ($self) = @_;
442              
443 0           print $self->string();
444             }
445              
446             sub strip_crlf {
447 0     0 0   my ($self, $string) = @_;
448              
449 0           $string =~ s/[\n\r]+$//g;
450              
451 0           return $string;
452             }
453              
454             sub _private_set {
455 0     0     my ($self, %items) = @_;
456              
457 0           foreach my $field (keys %items) {
458 0           $self->{$field} = $items{$field};
459 0           $self->{__fields}->{$field} = TRUE;
460             }
461             }
462              
463             1;
464              
465             __END__