File Coverage

blib/lib/Finance/IIF.pm
Criterion Covered Total %
statement 78 138 56.5
branch 33 74 44.5
condition 8 14 57.1
subroutine 15 17 88.2
pod 7 7 100.0
total 141 250 56.4


line stmt bran cond sub pod time code
1             package Finance::IIF;
2              
3 1     1   56195 use 5.006;
  1         3  
  1         38  
4 1     1   6 use strict;
  1         2  
  1         28  
5 1     1   5 use warnings;
  1         6  
  1         30  
6 1     1   5 use Carp;
  1         2  
  1         53  
7 1     1   1315 use IO::File;
  1         1042  
  1         1003  
8              
9             our $VERSION = '0.20.01';
10             $VERSION = eval $VERSION;
11              
12             sub new {
13 16     16 1 10338 my $class = shift;
14 16         43 my %opt = @_;
15 16         23 my $self = {};
16              
17 16   100     90 $self->{debug} = $opt{debug} || 0;
18 16   100     58 $self->{autodetect} = $opt{autodetect} || 0;
19 16   100     56 $self->{field_separator} = $opt{field_separator} || "\t";
20              
21 16         31 bless( $self, $class );
22              
23 16 100       34 if ( $opt{record_separator} ) {
24 1         3 $self->record_separator( $opt{record_separator} );
25             }
26              
27 16 100       33 if ( $opt{file} ) {
28 6         14 $self->file( $opt{file} );
29 6         17 $self->open;
30             }
31 16         89 return $self;
32             }
33              
34             sub file {
35 28     28 1 53 my $self = shift;
36 28 100       55 if (@_) {
37 10 100       36 my @file = ( ref( $_[0] ) eq "ARRAY" ? @{ shift @_ } : (), @_ );
  1         3  
38 10         30 $self->{file} = [@file];
39             }
40 28 100       57 if ( $self->{file} ) {
41 26 100       72 return wantarray ? @{ $self->{file} } : $self->{file}->[0];
  8         33  
42             }
43             else {
44 2         7 return undef;
45             }
46             }
47              
48             sub record_separator {
49 11     11 1 4083 my $self = shift;
50 11 100       90 if (@_) {
51 4 50       17 $self->{record_separator} = $_[0] if ( $_[0] );
52             }
53 11   66     64 return $self->{record_separator} || $/;
54             }
55              
56             sub _filehandle {
57 26     26   31 my $self = shift;
58 26 100       47 if (@_) {
59 7         11 my @args = @_;
60 7 50       40 $self->{_filehandle} = IO::File->new(@args)
61             or croak("Failed to open file '$args[0]': $!");
62 7         607 $self->{_linecount} = 0;
63             }
64 26 100       57 if ( !$self->{_filehandle} ) {
65 5         572 croak("No filehandle available");
66             }
67 21         78 return $self->{_filehandle};
68             }
69              
70             sub open {
71 8     8 1 18 my $self = shift;
72 8 100       17 if (@_) {
73 1         4 $self->file(@_);
74             }
75 8 100       17 if ( $self->file ) {
76 7         16 $self->_filehandle( $self->file );
77 7 100       18 if ( $self->{autodetect} ) {
78 4 100       10 if ( $self->_filehandle->seek( -2, 2 ) ) {
79 3         30 my $buffer = "";
80 3         8 $self->_filehandle->read( $buffer, 2 );
81 3 100       60 if ( $buffer eq "\r\n" ) {
    100          
    50          
82 1         4 $self->record_separator("\r\n");
83             }
84             elsif ( $buffer =~ /\n$/ ) {
85 1         4 $self->record_separator("\n");
86             }
87             elsif ( $buffer =~ /\r$/ ) {
88 1         3 $self->record_separator("\r");
89             }
90             }
91             }
92 7         34 $self->reset();
93             }
94             else {
95 1         105 croak("No file specified");
96             }
97             }
98              
99             sub next {
100 1     1 1 5 my $self = shift;
101 1         2 my %object;
102 1         2 my $continue = 1;
103 1 0       3 if ( $self->_filehandle->eof ) {
104 0         0 return undef;
105             }
106 0   0     0 while ( !$self->_filehandle->eof && $continue ) {
107 0         0 my $line = $self->_getline;
108 0 0       0 next if ( $line =~ /^\s*$/ );
109 0         0 my @data = $self->_parseline($line);
110              
111 0 0       0 if ( $self->{debug} > 1 ) {
112 0         0 warn("_getline: line($line)\n");
113 0         0 warn( "_parseline: data[" . scalar(@data) . "](@data)\n" );
114             }
115              
116 0 0       0 if ( $data[0] =~ /^!(.*)$/ ) {
    0          
117 0         0 delete( $self->{headerfields} );
118 0         0 shift(@data);
119 0         0 $self->{header} = $1;
120 0         0 $self->{headerfields} = \@data;
121             }
122             elsif ( $data[0] eq $self->{header} ) {
123 0         0 $continue = 0;
124 0         0 $object{header} = shift(@data);
125 0         0 my $num_hdr = scalar( @{ $self->{headerfields} } );
  0         0  
126 0         0 my $num_dat = scalar(@data);
127              
128             # have seen IIF timer data where last column (USEID) was
129             # missing but QuickBooks imports the data without error
130 0 0       0 if ( $num_dat < ( $num_hdr - 1 ) ) {
131 1     1   6 no warnings 'uninitialized';
  1         3  
  1         1007  
132 0         0 $self->_warning( "parse error: found $num_dat fields but"
133             . " expected $num_hdr." );
134 0         0 warn(
135             "error info: [header,data] "
136             . join(
137             ' ',
138             map( "$_" . '['
139             . $self->{headerfields}->[$_] . ','
140             . $data[$_] . ']',
141             0 .. ( $num_hdr - 1 ) )
142             )
143             );
144             }
145             else {
146 0         0 for ( my $i = 0 ; $i <= $#{ $self->{headerfields} } ; $i++ ) {
  0         0  
147 0 0       0 my $val = defined( $data[$i] ) ? $data[$i] : "";
148 0         0 $object{ $self->{headerfields}[$i] } = $val;
149             }
150             }
151             }
152             else {
153 0         0 $self->_warning("unable to parse line '$_'");
154             }
155             }
156              
157 0 0       0 if ($continue) {
158 0         0 return undef;
159             }
160             else {
161 0         0 return \%object;
162             }
163             }
164              
165             sub _parseline {
166 0     0   0 my $self = shift;
167 0         0 my $line = shift;
168 0   0     0 my $sep = $self->{field_separator} || "\t";
169 0         0 my @data;
170 0         0 while ( defined $line ) {
171 0 0       0 if ( $line =~ /^"(.*?)(?:[^\\]["])[$sep](.*)/ ) {
    0          
    0          
    0          
    0          
172 0 0       0 warn("parse1: data($1) line($2)\n") if ( $self->{debug} > 2 );
173 0         0 $line = $2;
174 0         0 push( @data, $1 );
175             }
176             elsif ( $line =~ /^([^$sep]+)[$sep](.*)/ ) {
177 0 0       0 warn("parse2: data($1) line($2)\n") if ( $self->{debug} > 2 );
178 0         0 $line = $2;
179 0         0 push( @data, $1 );
180             }
181             elsif ( $line =~ /^[$sep](.*)/ ) {
182 0 0       0 warn("parse3: data() line($1)\n") if ( $self->{debug} > 2 );
183 0         0 $line = $1;
184 0         0 push( @data, "" );
185             }
186             elsif ( $line =~ /^"(.*?)(?:[^\\]["])$/ ) {
187 0 0       0 warn("parse4: data($1) line()\n") if ( $self->{debug} > 2 );
188 0         0 $line = undef;
189 0         0 push( @data, $1 );
190             }
191             elsif ( $line =~ /^(.+)$/ ) {
192 0 0       0 warn("parse5: data($1) line()\n") if ( $self->{debug} > 2 );
193 0         0 $line = undef;
194 0         0 push( @data, $1 );
195             }
196             else {
197 0 0       0 warn("parse6: data() line($line)\n") if ( $self->{debug} > 2 );
198 0         0 $line = undef;
199 0         0 push( @data, "" );
200             }
201             }
202 0         0 return @data;
203             }
204              
205             sub _getline {
206 1     1   6 my $self = shift;
207 1         4 local $/ = $self->record_separator;
208 1         3 my $line = $self->_filehandle->getline;
209 0         0 chomp($line);
210 0         0 $self->{_linecount}++;
211 0         0 return $line;
212             }
213              
214             sub _warning {
215 0     0   0 my $self = shift;
216 0         0 my $message = shift;
217 0         0 carp( $message
218             . " in file '"
219             . $self->file
220             . "' line "
221             . $self->{_linecount} );
222             }
223              
224             sub reset {
225 8     8 1 15 my $self = shift;
226 8         17 $self->_filehandle->seek( 0, 0 );
227             }
228              
229             sub close {
230 1     1 1 5 my $self = shift;
231 1         3 $self->_filehandle->close;
232             }
233              
234             1;
235              
236             __END__