File Coverage

blib/lib/Data/Table.pm
Criterion Covered Total %
statement 1499 1721 87.1
branch 609 1040 58.5
condition 129 263 49.0
subroutine 102 110 92.7
pod 68 81 83.9
total 2407 3215 74.8


\n"; \n"; \n"; \n"; \n"; \n"; \n"; \n"; \n"; \n"; \n"; \n"; \n";
line stmt bran cond sub pod time code
1             package Data::Table;
2 2 50   2   71792 BEGIN { die "Your perl version is old, see README for instructions" if $] < 5.005; }
3              
4 2     2   19 use strict;
  2         4  
  2         49  
5 2     2   8 use vars qw($VERSION %DEFAULTS);
  2         3  
  2         96  
6 2     2   9 use Carp;
  2         4  
  2         325  
7             #use Data::Dumper;
8              
9             $VERSION = '1.76';
10             %DEFAULTS = (
11             "CSV_DELIMITER"=>',', # controls how to read/write CSV file
12             "CSV_QUALIFIER"=>'"',
13             "OS"=>0,
14             # operatoring system: 0 for UNIX (\n as linebreak), 1 for Windows
15             # (\r\n as linebreak), 2 for MAC (\r as linebreak)
16             # this controls how to read and write CSV/TSV file
17             "ENCODING"=>'UTF-8'
18             # default encoding for fromFile, fromCSV, fromTSV
19             );
20             %Data::Table::TSV_ESC = ( '0'=>"\0", 'n'=>"\n", 't'=>"\t", 'r'=>"\r", 'b'=>"\b",
21             "'"=>"'", '"'=>"\"", '\\'=>"\\" );
22             %Data::Table::TSV_ENC = ( "\0"=>'0', "\n"=>'n', "\t"=>'t', "\r"=>'r', "\b"=>'b',
23             "'"=>"'", "\""=>'"', "\\"=>'\\' );
24 2     2   12 use constant ROW_BASED => 0;
  2         3  
  2         151  
25 2     2   10 use constant COL_BASED => 1;
  2         7  
  2         77  
26 2     2   9 use constant NUMBER => 0;
  2         2  
  2         69  
27 2     2   10 use constant STRING => 1;
  2         3  
  2         73  
28 2     2   8 use constant ASC => 0;
  2         3  
  2         69  
29 2     2   12 use constant DESC => 1;
  2         3  
  2         78  
30 2     2   9 use constant INNER_JOIN => 0;
  2         2  
  2         67  
31 2     2   10 use constant LEFT_JOIN => 1;
  2         2  
  2         82  
32 2     2   8 use constant RIGHT_JOIN => 2;
  2         3  
  2         68  
33 2     2   8 use constant FULL_JOIN => 3;
  2         4  
  2         63  
34 2     2   8 use constant OS_UNIX => 0;
  2         4  
  2         82  
35 2     2   8 use constant OS_PC => 1;
  2         4  
  2         69  
36 2     2   9 use constant OS_MAC => 2;
  2         3  
  2         29703  
37              
38             sub new {
39 63     63 1 237 my ($pkg, $data, $header, $type, $enforceCheck) = @_;
40 63   33     246 my $class = ref($pkg) || $pkg;
41 63 100       148 $type = 0 unless defined($type);
42 63 50       121 $header=[] unless defined($header);
43 63 50       148 $data=[] unless defined($data);
44 63 50       205 $enforceCheck = 1 unless defined($enforceCheck);
45             confess "new Data::Table: Size of data does not match header\n"
46 2         5 if (($type && (scalar @$data) && $#{$data} != $#{$header}) ||
  2         10  
47 63 50 100     363 (!$type && (scalar @$data) && $#{$data->[0]} != $#{$header}));
  60   33     135  
  60   100     217  
      66        
      33        
48 63         170 my $colHash = checkHeader($header);
49 63 100 66     265 if ($enforceCheck && scalar @$data > 0) {
    50          
50 62         150 my $size=scalar @{$data->[0]};
  62         117  
51 62         170 for (my $j =1; $j
52 340 50       696 confess "Inconsistent array size at data[$j]" unless (scalar @{$data->[$j]} == $size);
  340         820  
53             }
54             } elsif (scalar @$data == 0) {
55 1         3 $type = 0;
56             }
57 63         392 my $self={ data=>$data, header=>$header, type=>$type, colHash=>$colHash, OK=>[], MATCH=>[]};
58 63         797 return bless $self, $class;
59             }
60              
61             sub checkHeader {
62 64     64 0 106 my $header = shift;
63 64         111 my $colHash = {};
64 64         188 for (my $i = 0; $i < scalar @$header; $i++) {
65 295         454 my $elm = $header->[$i];
66             #warn "Column name: $elm at column ".($i+1)." is an integer, using an integer column name will mask the corresponding column index!" if ($elm =~ /^\d+$/);
67 295 50       507 confess "Undefined column name (empty or all space) at column ".($i+1) unless $elm;
68             #confess "Header name ".$colHash->{$elm}." appears more than once" if defined($colHash->{$elm});
69 295 50       606 if (defined($colHash->{$elm})) {
70 0         0 confess "Header name ($elm) appears more than once: in column ".($colHash->{$elm}+1)." and column ".($i+1).".";
71             }
72 295         806 $colHash->{$elm} = $i;
73             }
74 64         135 return $colHash;
75             }
76              
77             # translate a column name into its position in the header
78             # (also in column-based table)
79             sub colIndex {
80 851     851 1 2198 my ($self, $colID) = @_;
81 851 100       2023 return $self->{colHash}->{$colID} if exists $self->{colHash}->{$colID};
82 698 100       3327 return $colID if $colID =~ /^\d+$/;
83 11         43 return -1;
84             #if ($colID =~ /\D/) {
85             # my $i = $self->{colHash}->{$colID};
86             # return -1 unless defined($i);
87             # return $i;
88             #}
89             #return $colID; # assume an index already
90             }
91              
92             sub hasCol {
93 4     4 1 18 my ($self, $col) = @_;
94 4         16 return $self->colIndex($col) >= 0;
95             }
96              
97             sub nofCol {
98 186     186 1 313 my $self = shift;
99 186         244 return scalar @{$self->{header}};
  186         562  
100             }
101              
102             sub isEmpty {
103 8     8 1 19 my $self = shift;
104 8         22 return $self->nofCol == 0;
105             }
106              
107             sub nofRow {
108 1654     1654 1 2420 my $self = shift;
109 1654 100       1967 return 0 if (scalar @{$self->{data}} == 0);
  1654         3428  
110             return ($self->{type})?
111 1652 100       3095 scalar @{$self->{data}->[0]} : scalar @{$self->{data}};
  679         1651  
  973         2209  
112             }
113              
114             sub lastRow {
115 1     1 1 6 my $self = shift;
116 1         3 return $self->nofRow - 1;
117             }
118              
119             sub lastCol {
120 1     1 1 3 my $self = shift;
121 1         2 return $self->nofCol - 1;
122             }
123              
124             sub colName {
125 0     0 1 0 my ($self, $colNumericIndex) = @_;
126 0         0 return ($self->header())[$colNumericIndex];
127             }
128              
129             sub iterator {
130 1     1 1 5 my ($self, $arg_ref) = @_;
131 1 50       5 my %arg = defined $arg_ref ? %$arg_ref : ();
132 1 50       6 $arg{reverse} = 0 unless exists $arg{reverse};
133 1 50       4 my $current_row = $arg{reverse} ? $self->lastRow : 0;
134              
135             return sub {
136 155     155   510 my $rowIdx = shift;
137 155 100       271 if (defined $rowIdx) { # return row index for previously returned record
138 77 50       154 my $prevRow = $arg{reverse} ? $current_row+1 : $current_row-1;
139 77 50 33     1132 return ($prevRow<0 or $prevRow > $self->nofRow-1)? undef: $prevRow;
140             }
141 78 100 66     193 return undef if $current_row < 0 or $current_row > $self->nofRow - 1;
142 77         116 my $oldRow = $current_row;
143 77 50       127 $arg{reverse} ? $current_row-- : $current_row++;
144 77         177 return $self->rowHashRef($oldRow);
145             }
146 1         7 }
147              
148             # still need to consider quotes and comma in string
149             # need to get csv specification
150             sub csvEscape {
151 86     86 1 125 my ($s, $arg_ref) = @_;
152 86         142 my ($delimiter, $qualifier) = ($Data::Table::DEFAULTS{CSV_DELIMITER}, $Data::Table::DEFAULTS{CSV_QUALIFIER});
153 86 50 33     270 $delimiter = $arg_ref->{'delimiter'} if (defined($arg_ref) && defined($arg_ref->{'delimiter'}));
154 86 50 33     224 $qualifier = $arg_ref->{'qualifier'} if (defined($arg_ref) && defined($arg_ref->{'qualifier'}));
155 86 50       131 return '' unless defined($s);
156 86         99 my $qualifier2 = $qualifier;
157 86 50       155 $qualifier2 = substr($qualifier, 1, 1) if length($qualifier)>1; # in case qualifier is a special symbol for regular expression
158 86         185 $s =~ s/$qualifier/$qualifier2$qualifier2/g;
159 86 100       270 if ($s =~ /[$qualifier$delimiter\r\n]/) { return "$qualifier2$s$qualifier2"; }
  2         8  
160 84         281 return $s;
161             }
162              
163             sub tsvEscape {
164 357     357 1 502 my $s = shift;
165             #my %ESC = ( "\0"=>'0', "\n"=>'n', "\t"=>'t', "\r"=>'r', "\b"=>'b',
166             # "'"=>"'", "\""=>'"', "\\"=>'\\' );
167             ## what about \f? MySQL treats \f as f.
168 357 50       637 return "\\N" unless defined($s);
169 357         647 $s =~ s/([\0\\\b\r\n\t"'])/\\$Data::Table::TSV_ENC{$1}/g;
170 357         795 return $s;
171             }
172              
173             # output table in CSV format
174             sub csv {
175 4     4 1 37 my ($self, $header, $arg_ref)=@_;
176 4         10 my ($status, @t);
177 4         9 my $s = '';
178 4         11 my ($OS, $fileName_or_handler) = ($Data::Table::DEFAULTS{OS}, undef);
179 4 50 33     18 $OS = $arg_ref->{'OS'} if (defined($arg_ref) && defined($arg_ref->{'OS'}));
180 4         13 my ($delimiter, $qualifier) = ($Data::Table::DEFAULTS{CSV_DELIMITER}, $Data::Table::DEFAULTS{CSV_QUALIFIER});
181 4 50       13 if (defined($arg_ref)) {
182 0 0       0 $delimiter = $arg_ref->{'delimiter'} if defined($arg_ref->{'delimiter'});
183 0 0       0 $qualifier = $arg_ref->{'qualifier'} if defined($arg_ref->{'qualifier'});
184 0 0       0 $fileName_or_handler = $arg_ref->{'file'} if defined($arg_ref->{'file'});
185             }
186 4 50       6 my $delimiter2 = $delimiter; $delimiter2 = substr($delimiter, 1, 1) if length($delimiter)>1;
  4         13  
187 4 50       17 my $endl = ($OS==2)?"\r":(($OS==1)?"\r\n":"\n");
    50          
188 4 50       12 $header=1 unless defined($header);
189 4 50       10 $s=join($delimiter2, map {csvEscape($_, {delimiter=>$delimiter, qualifier=>$qualifier})} @{$self->{header}}) . $endl if $header;
  14         42  
  4         25  
190             ###### $self->rotate if $self->{type};
191 4 50       14 if ($self->{data}) {
192 4 50       14 $self->rotate() if ($self->{type});
193 4         7 my $data=$self->{data};
194 4         16 for (my $i=0; $i<=$#{$data}; $i++) {
  20         49  
195 16         23 $s .= join($delimiter2, map {csvEscape($_, {delimiter=>$delimiter, qualifier=>$qualifier})} @{$data->[$i]}) . $endl;
  72         167  
  16         30  
196             }
197             }
198 4 50       16 if (defined($fileName_or_handler)) {
199 0         0 my $OUT;
200 0         0 my $isFileHandler = ref($fileName_or_handler) ne '';
201 0 0       0 if ($isFileHandler) {
202 0         0 $OUT = $fileName_or_handler;
203             } else {
204 0 0       0 open($OUT, "> $fileName_or_handler") or confess "Cannot open $fileName_or_handler to write.\n";
205 0         0 binmode $OUT;
206             }
207 0         0 print $OUT $s;
208 0 0       0 close($OUT) unless $isFileHandler;
209             }
210 4         27 return $s;
211             }
212              
213             # output table in TSV format
214             sub tsv {
215 4     4 1 31 my ($self, $header, $arg_ref)=@_;
216 4         7 my ($status, @t);
217 4         10 my $s = '';
218 4         14 my ($OS, $fileName_or_handler, $transform_element) = ($Data::Table::DEFAULTS{OS}, undef, 1);
219 4 50       14 if (defined($arg_ref)) {
220 0 0       0 $OS = $arg_ref->{'OS'} if (defined($arg_ref->{'OS'}));
221 0 0       0 $fileName_or_handler = $arg_ref->{'file'} if (defined($arg_ref->{'file'}));
222 0 0       0 $transform_element = $arg_ref->{'transform_element'} if (defined($arg_ref->{'transform_element'}));
223             }
224 4 50       17 my $endl = ($OS==2)?"\r":(($OS==1)?"\r\n":"\n");
    50          
225 4 50       14 $header=1 unless defined($header);
226 4 50       11 if ($header) {
227 4 50       10 if ($transform_element) {
228 4         7 $s=join("\t", map {tsvEscape($_)} @{$self->{header}}) . $endl;
  19         32  
  4         12  
229             } else {
230 0         0 $s=join("\t",@{$self->{header}}) . $endl;
  0         0  
231             }
232             }
233             ###### $self->rotate if $self->{type};
234 4 50       18 if ($self->{data}) {
235 4 50       11 $self->rotate() if ($self->{type});
236 4         8 my $data=$self->{data};
237 4         8 for (my $i=0; $i<=$#{$data}; $i++) {
  33         77  
238 29 50       45 if ($transform_element) {
239 29         38 $s .= join("\t", map {tsvEscape($_)} @{$data->[$i]}) . $endl;
  164         224  
  29         46  
240             } else {
241 0         0 $s .= join("\t", @{$data->[$i]}) . $endl;
  0         0  
242             }
243             }
244             }
245 4 50       13 if (defined($fileName_or_handler)) {
246 0         0 my $OUT;
247 0         0 my $isFileHandler = ref($fileName_or_handler) ne '';
248 0 0       0 if ($isFileHandler) {
249 0         0 $OUT = $fileName_or_handler;
250             } else {
251 0 0       0 open($OUT, "> $fileName_or_handler") or confess "Cannot open $fileName_or_handler to write.\n";
252 0         0 binmode $OUT;
253             }
254 0         0 print $OUT $s;
255 0 0       0 close($OUT) unless $isFileHandler;;
256             }
257 4         22 return $s;
258             }
259              
260             # output table in HTML format
261             sub html {
262 5     5 1 41 my ($self, $colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, $portrait, $callback) = @_;
263 5         22 my ($s, $s_tr, $s_td, $s_th) = ("", "tr", "", "th");
264 5         10 my $key;
265 5 50       31 $tag_tbl = { class => "data_table" } unless (ref $tag_tbl eq 'HASH');
266 5 50       21 $tag_tr = {} unless (ref $tag_tr eq 'HASH');
267 5 50       26 $tag_th = {} unless (ref $tag_th eq 'HASH');
268 5 50       19 $tag_td = {} unless (ref $tag_td eq 'HASH');
269 5 100       20 $portrait = 1 unless defined($portrait);
270 5         13 my $cb=0;
271 5 100       15 if (defined($callback)) {
272 2 50       60 confess "wiki: Expecting subroutine for callback parameter!" if ref($callback) ne 'CODE';
273 2         6 $cb=1;
274             }
275              
276             my $tag2str = sub {
277 1065     1065   7508 my $tag = shift;
278 1065         1658 my $s="";
279 1065         2678 foreach my $key (keys %$tag) {
280 368 50       802 next unless $tag->{$key};
281 368 50       755 if ($key eq '') {
282 0         0 $s .=" ".$tag->{$key};
283             #for backward compatibility, in case the tag is a str
284             # '' => 'align="right" valign="bottom"'
285             } else {
286 368         1161 $s .= " $key=\"$tag->{$key}\"";
287             }
288             }
289 1065         3777 return $s;
290 5         41 };
291              
292 5         18 $s = "($tag_tbl).">\n";
293 5         19 my $header=$self->{header};
294 5         14 my $l_colorByClass = 0;
295 5         21 my @BG_COLOR=("#D4D4BF","#ECECE4","#CCCC99");
296 5         24 my @CELL_CLASSES=("data_table_odd","data_table_even","data_table_header");
297 5 100 66     40 if (ref($colorArrayRef_or_classHashRef) eq "HASH") {
    100          
298 1         4 $l_colorByClass = 1;
299 1 50       5 $CELL_CLASSES[1]=$colorArrayRef_or_classHashRef->{even} if defined($colorArrayRef_or_classHashRef->{even});
300 1 50       4 $CELL_CLASSES[0]=$colorArrayRef_or_classHashRef->{odd} if defined($colorArrayRef_or_classHashRef->{odd});
301 1 50       5 $CELL_CLASSES[2]=$colorArrayRef_or_classHashRef->{header} if defined($colorArrayRef_or_classHashRef->{header});
302             } elsif ((ref($colorArrayRef_or_classHashRef) eq "ARRAY") && (scalar @$colorArrayRef_or_classHashRef==3)) {
303 2         9 @BG_COLOR=@$colorArrayRef_or_classHashRef;
304             }
305            
306 5         19 $s_tr = $tag2str->($tag_tr);
307 5         13 $s_th = $tag2str->($tag_th);
308            
309 5 100       17 if ($portrait) {
310 3         7 $s .= "
311 3         7 my $clr="";
312 3 100       9 if ($l_colorByClass) {
313 1 50       4 $clr=" class=\"".$CELL_CLASSES[2]."\"" if ($CELL_CLASSES[2]);
314             } else {
315 2 100       8 $clr=" style=\"background-color:".$BG_COLOR[2].";\"" if ($BG_COLOR[2]);
316             }
317 3         11 $s .= "\n";
318 3         7 for (my $i=0; $i<=$#{$header}; $i++) {
  17         41  
319 14 100       53 $s .="($callback->({%$tag_th}, -1, $i, $header->[$i], $self)) : $s_th) .">".$header->[$i]."\n";
320             }
321 3         7 $s .="
322 3         10 $s .= "
323 3 50       7 $self->rotate() if $self->{type};
324 3         8 my $data=$self->{data};
325 3         9 $s .= "
326 3         8 for (my $i=0; $i<=$#{$data}; $i++) {
  91         239  
327 88         164 $clr="";
328 88 100       179 if ($l_colorByClass) {
329 2 50       8 $clr=" class=\"".$CELL_CLASSES[$i%2]."\"" if ($CELL_CLASSES[$i%2]);
330             } else {
331 86 100       236 $clr=" style=\"background-color:".$BG_COLOR[$i%2].";\"" if ($BG_COLOR[$i%2]);
332             }
333 88         207 $s .= "\n";
334 88         187 for (my $j=0; $j<=$#{$header}; $j++) {
  608         1544  
335 520   50     2470 my $td = $tag_td->{$j} || $tag_td->{$header->[$j]} || {};
336 520   100     1910 my $s_td=$tag2str->($cb ? $callback->({%$td}, $i, $j, $header->[$j], $self) : $td) || "";
337 520 100       1809 $s .= ($s_td)? "":"";
338 520 50 33     2235 $s .= (defined($data->[$i][$j]) && $data->[$i][$j] ne '')?$data->[$i][$j]:" ";
339 520         1577 $s .= "
340             }
341 88         225 $s .= "
342             }
343 3         12 $s .= "
344             } else {
345 2 50       17 $self->rotate() unless $self->{type};
346 2         7 my $tag_th_def={};
347 2 50       9 if ($l_colorByClass) {
348 0 0       0 $tag_th_def->{"class"}=$CELL_CLASSES[2] if $CELL_CLASSES[2];
349             } else {
350 2 100       12 $tag_th_def->{"style"}="background-color:".$BG_COLOR[2].";" if $BG_COLOR[2];
351             }
352             my $merge_tag = sub {
353 518     518   942 my ($old, $usr)=@_;
354 518         1217 foreach my $k(keys %$usr) {
355 0 0       0 if (exists $old->{$k}) {
356 0 0 0     0 if (!defined($usr->{k}) or $usr->{k} eq '') {
    0 0        
357 0         0 undef $old->{k};
358             } elsif ($k eq 'style' and (index($usr->{k}, 'background-color:')!=-1)) {
359 0         0 $old->{$k}=$usr->{$k};
360             } else {
361 0         0 $old->{$k}.= " "+$usr->{$k};
362             }
363             } else {
364 0 0       0 $old->{$k}=$usr->{$k} if $usr->{$k};
365             }
366             }
367 2         16 };
368 2 50       14 $merge_tag->($tag_th_def, $tag_th) if defined($tag_th);
369 2         7 $s_th=$tag2str->($tag_th_def);
370              
371 2         8 my $data=$self->{data};
372 2         7 $s .="
373 2         7 for (my $i = 0; $i <= $#{$header}; $i++) {
  14         45  
374 12         70 $s .= "
375 12 100       62 $s .= "($callback->({%$tag_th_def}, -1, $i, $header->[$i], $self)) : $s_th) .">". $header->[$i] . "
376 12   50     89 my $td_def = $tag_td->{$i} || $tag_td->{$header->[$i]} || {};
377 12 50       39 $td_def = {'' => $td_def} unless ref $td_def;
378 12         26 for (my $j=0; $j<=$#{$data->[0]}; $j++) {
  528         1314  
379 516         880 my $td = {};
380 516 50       935 if ($l_colorByClass) {
381 0 0       0 $td->{"class"}=$CELL_CLASSES[$j%2] if $CELL_CLASSES[$j%2];
382             } else {
383 516 100       1164 $td->{"style"}="background-color:".$BG_COLOR[$j%2].";" if $BG_COLOR[$j%2];
384             }
385 516         1156 $merge_tag->($td, $td_def);
386 516   100     1686 my $s_td=$tag2str->($cb ? $callback->({%$td}, $j, $i, $header->[$i], $self) : $td) || "";
387 516 100       1762 $s .= ($s_td)? "":"";
388 516 50 33     2030 $s .= (defined($data->[$i][$j]) && $data->[$i][$j] ne '')?$data->[$i][$j]:' ';
389 516         1370 $s .= "
390             }
391 12         41 $s .= "
392             }
393 2         24 $s .="
394             }
395 5         19 $s .= "
\n"; 396 5         176 return $s; 397             } 398               399             # output table in wikitable 400             # this method accepts the same parameters as the html() method 401             sub wiki { 402 4     4 1 17 my ($self, $colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, $portrait, $callback) = @_; 403 4         19 my ($s, $s_tr, $s_td, $s_th) = ("", "", "", ""); 404 4         9 my $key; 405 4 50       25 $tag_tbl = { class => "wikitable" } unless (ref $tag_tbl eq 'HASH'); 406 4 50       16 $tag_tr = {} unless (ref $tag_tr eq 'HASH'); 407 4 50       31 $tag_th = {} unless (ref $tag_th eq 'HASH'); 408 4 50       19 $tag_td = {} unless (ref $tag_td eq 'HASH'); 409 4 100       16 $portrait = 1 unless defined($portrait); 410 4         9 my $cb=0; 411 4 100       13 if (defined($callback)) { 412 2 50       11 confess "wiki: Expecting subroutine for callback parameter!" if ref($callback) ne 'CODE'; 413 2         5 $cb=1; 414             } 415               416             my $tag2str = sub { 417 1058     1058   5485 my $tag = shift; 418 1058         1364 my $s=""; 419 1058         1990 foreach my $key (keys %$tag) { 420 367 50       677 next unless $tag->{$key}; 421 367 50       536 if ($key eq '') { 422 0         0 $s .=" ".$tag->{$key}; 423             #for backward compatibility, in case the tag is a str 424             # '' => 'align="right" valign="bottom"' 425             } else { 426 367         849 $s .= " $key=\"$tag->{$key}\""; 427             } 428             } 429 1058         2775 return $s; 430 4         27 }; 431               432 4         13 $s = "{|".$tag2str->($tag_tbl)."\n"; 433 4         13 my $header=$self->{header}; 434 4         8 my $l_colorByClass = 0; 435 4         14 my @BG_COLOR=("#D4D4BF","#ECECE4","#CCCC99"); 436 4         15 my @CELL_CLASSES=("wikitable_odd","wikitable_even","wikitable_header"); 437 4 50 66     38 if (ref($colorArrayRef_or_classHashRef) eq "HASH") {     100           438 0         0 $l_colorByClass = 1; 439 0 0       0 $CELL_CLASSES[1]=$colorArrayRef_or_classHashRef->{even} if defined($colorArrayRef_or_classHashRef->{even}); 440 0 0       0 $CELL_CLASSES[0]=$colorArrayRef_or_classHashRef->{odd} if defined($colorArrayRef_or_classHashRef->{odd}); 441 0 0       0 $CELL_CLASSES[2]=$colorArrayRef_or_classHashRef->{header} if defined($colorArrayRef_or_classHashRef->{header}); 442             } elsif ((ref($colorArrayRef_or_classHashRef) eq "ARRAY") && (scalar @$colorArrayRef_or_classHashRef==3)) { 443 2         11 @BG_COLOR=@$colorArrayRef_or_classHashRef; 444             } 445 4         16 $s_tr = $tag2str->($tag_tr); 446 4         11 $s_th = $tag2str->($tag_th); 447             448 4 100       11 if ($portrait) { 449 2         8 for (my $i=0; $i<=$#{$header}; $i++) {   14         32   450 12         19 my $clr=""; 451 12 50       24 if ($l_colorByClass) { 452 0 0       0 $clr=" class=\"".$CELL_CLASSES[2]."\"" if $CELL_CLASSES[2]; 453             } else { 454 12 100       26 $clr=" style=\"background-color:".$BG_COLOR[2].";\"" if $BG_COLOR[2]; 455             } 456 12         29 $s .= "!$s_tr$clr"; 457             # make a copy of $tag_th to pass as a parameter 458 12 100       37 $s .= $cb ? $tag2str->($callback->({%$tag_th}, -1, $i, $header->[$i], $self)) : $s_th; 459 12         72 $s .= " | ".$header->[$i]."\n"; # $join(" || ", @$header)."\n"; 460             } 461 2 50       15 $self->rotate() if $self->{type}; 462 2         9 my $data=$self->{data}; 463 2         8 for (my $i=0; $i<=$#{$data}; $i++) {   88         177   464 86         124 my $clr=""; 465 86 50       134 if ($l_colorByClass) { 466 0 0       0 $clr=" class=\"".$CELL_CLASSES[$i%2]."\"" if $CELL_CLASSES[$i%2]; 467             } else { 468 86 100       206 $clr=" style=\"background-color:".$BG_COLOR[$i%2].";\"" if $BG_COLOR[$i%2]; 469             } 470 86         147 $s .= "|-$clr\n"; 471 86         152 for (my $j=0; $j<=$#{$header}; $j++) {   602         1187   472 516   50     2051 my $td = $tag_td->{$j} || $tag_td->{$header->[$j]} || {}; 473             # backward compatibility, when str is used instead of hash for $tag_td->{'col'} 474 516 50       927 $td = {'' => $td} unless ref $td; 475 516   100     1341 my $s_td=$tag2str->($cb ? $callback->({%$td}, $i, $j, $header->[$j], $self) : $td) || ""; 476 516 100       1420 $s .= ($s_td)? "|$s_td | ":"| "; 477 516 50 33     1711 $s .= (defined($data->[$i][$j]) && $data->[$i][$j] ne '')?$data->[$i][$j]:" "; 478 516         1120 $s .= "\n"; 479             } 480             } 481             } else { 482 2 50       16 $self->rotate() unless $self->{type}; 483 2         6 my $tag_th_def={}; 484 2 50       9 if ($l_colorByClass) { 485 0 0       0 $tag_th_def->{"class"}=$CELL_CLASSES[2] if $CELL_CLASSES[2]; 486             } else { 487 2 100       11 $tag_th_def->{"style"}="background-color:".$BG_COLOR[2].";" if $BG_COLOR[2]; 488             } 489             my $merge_tag = sub { 490 518     518   703 my ($old, $usr)=@_; 491 518         890 foreach my $k(keys %$usr) { 492 0 0       0 if (exists $old->{$k}) { 493 0 0 0     0 if (!defined($usr->{k}) or $usr->{k} eq '') {     0 0         494 0         0 undef $old->{k}; 495             } elsif ($k eq 'style' and (index($usr->{k}, 'background-color:')!=-1)) { 496 0         0 $old->{$k}=$usr->{$k}; 497             } else { 498 0         0 $old->{$k}.= " "+$usr->{$k}; 499             } 500             } else { 501 0         0 $old->{$k}=$usr->{$k}; 502             } 503             } 504 2         11 }; 505               506 2 50       31 $merge_tag->($tag_th_def, $tag_th) if defined($tag_th); 507 2         5 $s_th=$tag2str->($tag_th_def); 508 2         5 my $data=$self->{data}; 509 2         4 for (my $i = 0; $i <= $#{$header}; $i++) {   14         46   510 12         19 $s .= "|-\n"; 511 12         22 $s .= "!"; 512 12 100       37 $s .= $cb ? $tag2str->($callback->({%$tag_th_def}, -1, $i, $header->[$i], $self)) : $s_th; 513 12         33 $s .= " | ".$header->[$i]."\n"; 514 12   50     60 my $td = $tag_td->{$i} || $tag_td->{$header->[$i]} || {}; 515 12 50       29 $td = {'' => $td} unless ref $td; 516 12         16 for (my $j=0; $j<=$#{$data->[0]}; $j++) {   528         1010   517 516         692 my $td_def={}; 518 516 50       748 if ($l_colorByClass) { 519 0 0       0 $td_def->{"class"}=$CELL_CLASSES[$j%2] if $CELL_CLASSES[$j%2]; 520             } else { 521 516 100       917 $td_def->{"style"}="background-color:".$BG_COLOR[$j%2].";" if $BG_COLOR[$j%2]; 522             } 523 516         884 $merge_tag->($td_def, $td); 524 516   100     1267 my $s_td=$tag2str->($cb ? $callback->({%$td_def}, $j, $i, $header->[$i], $self) : $td_def) || ""; 525 516 100       1236 $s .= ($s_td)? "|$s_td | ":"| "; 526 516 50 33     1610 $s .= (defined($data->[$i][$j]) && $data->[$i][$j] ne '')?$data->[$i][$j]:' '; 527 516         968 $s .= "\n"; 528             } 529             } 530             } 531 4         10 $s .= "|}\n"; 532 4         147 return $s; 533             } 534               535             # output table in wikitable format, with table orientation rotated, 536             # so that each wikitable row is a column in the table 537             # This is useful for a slim table (few columns but many rows) 538             # The method accepts the same parameters as html2() method 539             sub wiki2 { 540 2     2 1 9 my ($self, $colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, $callback) = @_; 541 2         13 return $self->wiki($colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, 0, $callback); 542             } 543               544             # output table in HTML format, with table orientation rotated, 545             # so that each HTML table row is a column in the table 546             # This is useful for a slim table (few columns but many rows) 547             sub html2 { 548 2     2 1 10 my ($self, $colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, $callback) = @_; 549 2         13 return $self->html($colorArrayRef_or_classHashRef, $tag_tbl, $tag_tr, $tag_th, $tag_td, 0, $callback); 550             } 551               552             # apply a $fun to each elm in a col 553             # function only has access to one element per row 554             sub colMap { 555 1     1 1 3 my ($self, $colID, $fun) = @_; 556 1         7 my $c=$self->checkOldCol($colID); 557 1 50       3 return undef unless defined $c; 558 1 50       5 $self->rotate() unless $self->{type}; 559 1         2 my $ref = $self->{data}->[$c]; 560 1         3 my @tmp = map {scalar $fun->($_)} @$ref;   9         27   561 1         5 $self->{data}->[$c] = \@tmp; 562 1         11 return 1; 563             } 564               565             # apply a $fun to each row in the table 566             # function has access to all elements in that row 567             sub colsMap { 568 1     1 1 3 my ($self, $fun) = @_; 569 1 50       5 $self->rotate() if $self->{type}; 570 1         2 map {&$fun} @{$self->{data}};   9         36     1         3   571 1         10 return 1; 572             } 573               574             sub addRow { 575 8     8 1 24 my ($self, $rowRef, $rowIdx, $arg_ref) = @_; 576 8 100       31 my %arg = defined $arg_ref ? %$arg_ref : (); 577 8 100       29 $arg{addNewCol} = 0 unless exists $arg{addNewCol}; 578               579 8         24 my $numRow=$self->nofRow(); 580 8         14 my @t; 581 8         13 my $myRowRef = $rowRef; 582               583 8 100       23 if ($arg{addNewCol}) { 584 1 50       8 if (ref $myRowRef eq 'HASH') {     0           585 1         6 foreach my $key (keys %$myRowRef) { 586 2 50       9 next if $self->colIndex($key) >= 0; 587 2         10 my @col = (undef) x $self->nofRow; 588 2         13 $self->addCol(\@col, $key); 589             } 590             } elsif (ref $myRowRef eq 'ARRAY') { 591 0         0 for (my $i=$self->nofCol; $i< scalar @$myRowRef; $i++) { 592 0         0 my @col = (undef) x $self->nofRow; 593 0         0 $self->addCol(\@col, "col".($i+1)); 594             } 595             } 596             } 597               598 8 100       27 if (ref $myRowRef eq 'HASH') {     50           599 2 50       11 if ($self->isEmpty) { 600 0         0 my $i = 0; 601 0         0 foreach my $s (keys %$myRowRef) { 602 0         0 push @{$self->{header}}, $s;   0         0   603 0         0 $self->{colHash}->{$s} = $i++; 604             } 605             } 606 2         10 my @one = (); 607 2         12 my @header = $self->header; 608 2         12 for (my $i=0; $i< scalar @header; $i++) { 609 11         32 $one[$i] = $myRowRef->{$header[$i]}; 610             } 611 2         9 $myRowRef = \@one; 612             } elsif (ref $myRowRef eq 'ARRAY') { 613 6 50       13 confess "addRow: size of added row does not match those in the table\n" 614             if scalar @$myRowRef != $self->nofCol(); 615             } else { 616 0         0 confess "addRow: parameter rowRef has to be either an array_ref or a hash_ref\n"; 617             } 618 8 100       26 $rowIdx=$numRow unless defined($rowIdx); 619 8 50       24 return undef unless defined $self->checkNewRow($rowIdx); 620 8 100       22 $self->rotate() if $self->{type}; 621 8         16 my $data=$self->{data}; 622 8 100       23 if ($rowIdx == 0) {     100           623 2         5 unshift @$data, $myRowRef; 624             } elsif ($rowIdx == $numRow) { 625 3         9 push @$data, $myRowRef; 626             } else { 627 3         9 @t = splice @$data, $rowIdx; 628 3         9 push @$data, $myRowRef, @t; 629             } 630 8         27 return 1; 631             } 632               633             sub delRow { 634 18     18 1 28 my ($self, $rowIdx ) = @_; 635 18 50       34 return undef unless defined $self->checkOldRow($rowIdx); 636 18 50       34 $self->rotate() if $self->{type}; 637 18         22 my $data=$self->{data}; 638 18         32 my @dels=splice(@$data, $rowIdx, 1); 639 18         28 return shift @dels; 640             } 641               642             sub delRows { 643 4     4 1 13 my ($self, $rowIdcsRef) = @_; 644 4         8 my $rowIdx; 645 4 50       13 $self->rotate() if $self->{type}; 646 4         8 my @dels = @{$self->{data}}[@$rowIdcsRef];   4         17   647 4         24 my @indices = sort { $b <=> $a } @$rowIdcsRef;   17         34   648             #my @dels=(); 649 4         10 foreach $rowIdx (@indices) { 650             #push @dels, $self->delRow($rowIdx); 651 17         28 $self->delRow($rowIdx); 652             } 653 4         17 return @dels; 654             } 655               656             # append a column to the table, input is a referenceof_array 657               658             sub addCol { 659 12     12 1 45 my ($self, $colRef, $colName, $colIdx) = @_; 660 12         38 my $numCol=$self->nofCol(); 661 12         25 my @t; 662 12 100 66     82 if (!defined($colRef) || ref($colRef) eq '') { 663             # fill the new column with $colRef as the default value 664 1         7 my @col = ($colRef) x $self->nofRow; 665 1         6 $colRef = \@col; 666             } else { 667 11 50 33     36 confess "addCol: size of added col does not match rows in the table\n" 668             if @$colRef != $self->nofRow() and $numCol > 0; 669             } 670 12 100       37 $colIdx=$numCol unless defined($colIdx); 671 12 50       57 return undef unless defined $self->checkNewCol($colIdx, $colName); 672 12 100       44 $self->rotate() unless $self->{type}; 673 12         26 my $data=$self->{data}; 674 12         24 my $header=$self->{header}; 675 12 100       49 if ($colIdx == 0) {     100           676 1         11 unshift @$header, $colName; 677             } elsif ($colIdx == $numCol) { 678 7         23 push @$header, $colName; 679             } else { 680 4         14 @t = splice @$header, $colIdx; 681 4         18 push @$header, $colName, @t; 682             } 683               684 12 100       52 if ($colIdx == 0) {     100           685 1         4 unshift @$data, $colRef; 686             } elsif ($colIdx == $numCol) { 687 7         21 push @$data, $colRef; 688             } else { 689 4         14 @t = splice @$data, $colIdx; 690 4         11 push @$data, $colRef, @t; 691             } 692               693 12         51 for (my $i = 0; $i < scalar @$header; $i++) { 694 65         117 my $elm = $header->[$i]; 695 65         202 $self->{colHash}->{$elm} = $i; 696             } 697 12         68 return 1; 698             } 699               700             sub delCol { 701 6     6 1 18 my ($self, $colID) = @_; 702 6         18 my $c=$self->checkOldCol($colID); 703 6 50       20 return undef unless defined $c; 704 6 100       20 $self->rotate() unless $self->{type}; 705 6         10 my $header=$self->{header}; 706 6         12 my $name=$self->{header}->[$c]; 707 6         11 splice @$header, $c, 1; 708 6         14 my $data=$self->{data}; 709 6         14 my @dels=splice @$data, $c, 1; 710 6         16 delete $self->{colHash}->{$name}; 711 6         20 for (my $i = $c; $i < scalar @$header; $i++) { 712 15         22 my $elm = $header->[$i]; 713 15         42 $self->{colHash}->{$elm} = $i; 714             } 715 6         16 return shift @dels; 716             } 717               718             sub delCols { 719 1     1 1 3 my ($self, $colIDsRef) = @_; 720 1         2 my $idx; 721 1         3 my @indices = map { $self->colIndex($_) } @$colIDsRef;   3         5   722 1 50       4 $self->rotate() unless $self->{type}; 723 1         3 my @dels = @{$self->{data}}[@indices];   1         3   724 1         4 @indices = sort { $b <=> $a } @indices;   3         5   725             #my @dels=(); 726 1         3 foreach my $colIdx (@indices) { 727 3         5 $self->delCol($colIdx); 728             } 729 1         4 return @dels; 730             } 731               732               733             sub rowRef { 734 48     48 1 105 my ($self, $rowIdx) = @_; 735 48 50       115 return undef unless defined $self->checkOldRow($rowIdx); 736 48 100       113 $self->rotate if $self->{type}; 737 48         111 return $self->{data}->[$rowIdx]; 738             } 739               740             sub rowRefs { 741 25     25 1 120 my ($self, $rowIdcsRef) = @_; 742 25 100       86 $self->rotate if $self->{type}; 743 25 50       174 return $self->{data} unless defined $rowIdcsRef; 744 0         0 my @ones = (); 745 0         0 my $rowIdx; 746 0         0 foreach $rowIdx (@$rowIdcsRef) { 747 0         0 push @ones, $self->rowRef($rowIdx); 748             } 749 0         0 return \@ones; 750             } 751               752             sub row { 753 61     61 1 92 my ($self, $rowIdx) = @_; 754 61         112 my $data = $self->{data}; 755 61 50       110 return undef unless defined $self->checkOldRow($rowIdx); 756 61 50       116 if ($self->{type}) { 757 0         0 my @one=(); 758 0         0 for (my $i = 0; $i < scalar @$data; $i++) { 759 0         0 push @one, $data->[$i]->[$rowIdx]; 760             } 761 0         0 return @one; 762             } else { 763 61         72 return @{$data->[$rowIdx]};   61         377   764             } 765             } 766               767             sub rowHashRef { 768 175     175 1 319 my ($self, $rowIdx) = @_; 769 175         267 my $data = $self->{data}; 770 175 50       305 return undef unless defined $self->checkOldRow($rowIdx); 771 175         299 my $header=$self->{header}; 772 175         275 my $one = {}; 773 175         375 for (my $i = 0; $i < scalar @$header; $i++) { 774             $one->{$header->[$i]} = ($self->{type})? 775 1094 100       3408 $self->{data}->[$i]->[$rowIdx]:$self->{data}->[$rowIdx]->[$i]; 776             } 777 175         810 return $one; 778             } 779               780             sub colRef { 781 4     4 1 6 my ($self, $colID) = @_; 782 4         8 my $c=$self->checkOldCol($colID); 783 4 50       8 return undef unless defined $c; 784 4 100       9 $self->rotate() unless $self->{type}; 785 4         10 return $self->{data}->[$c]; 786             } 787               788             sub colRefs { 789 1     1 1 4 my ($self, $colIDsRef) = @_; 790 1 50       4 $self->rotate unless $self->{type}; 791 1 50       4 return $self->{data} unless defined $colIDsRef; 792 1         2 my @ones = (); 793 1         2 my $colID; 794 1         3 foreach $colID (@$colIDsRef) { 795 3         5 push @ones, $self->colRef($colID); 796             } 797 1         4 return \@ones; 798             } 799               800             sub col { 801 5     5 1 23 my ($self, $colID) = @_; 802 5         15 my $data = $self->{data}; 803 5         14 my $c=$self->checkOldCol($colID); 804 5 50       19 return undef unless defined $c; 805 5 100       19 if (!$self->{type}) { 806 3         9 my @one=(); 807 3         16 for (my $i = 0; $i < scalar @$data; $i++) { 808 16         46 push @one, $data->[$i]->[$c]; 809             } 810 3         25 return @one; 811             } else { 812 2 50       46 return () unless ref($data->[$c]) eq "ARRAY"; 813 2         6 return @{$data->[$c]};   2         14   814             } 815             } 816               817             sub rename { 818 16     16 1 96 my ($self, $colID, $name) = @_; 819 16         24 my $oldName; 820 16         38 my $c=$self->checkOldCol($colID); 821 16 50       38 return undef unless defined $c; 822 16         36 $oldName=$self->{header}->[$c]; 823 16 50       40 return if ($oldName eq $name); 824 16 50       51 return undef unless defined $self->checkNewCol($c, $name); 825 16         31 $self->{header}->[$c]=$name; 826             # $self->{colHash}->{$oldName}=undef; # undef still keeps the entry, use delete instead! 827 16         39 delete $self->{colHash}->{$oldName}; 828 16         39 $self->{colHash}->{$name}=$c; 829 16         41 return 1; 830             } 831               832             sub replace{ 833 2     2 1 7 my ($self, $oldColID, $newColRef, $newName) = @_; 834 2         5 my $oldName; 835 2         5 my $c=$self->checkOldCol($oldColID); 836 2 50       8 return undef unless defined $c; 837 2         5 $oldName=$self->{header}->[$c]; 838 2 50       6 $newName=$oldName unless defined($newName); 839 2 50       8 unless ($oldName eq $newName) { 840 2 50       4 return undef unless defined $self->checkNewCol($c, $newName); 841             } 842 2 50       6 confess "New column size ".(scalar @$newColRef)." must be ".$self->nofRow() unless (scalar @$newColRef==$self->nofRow()); 843 2         7 $self->rename($c, $newName); 844 2 50       5 $self->rotate() unless $self->{type}; 845 2         4 my $old=$self->{data}->[$c]; 846 2         4 $self->{data}->[$c]=$newColRef; 847 2         13 return $old; 848             } 849               850             sub swap{ 851 2     2 1 6 my ($self, $colID1, $colID2) = @_; 852 2         8 my $c1=$self->checkOldCol($colID1); 853 2 50       7 return undef unless defined $c1; 854 2         5 my $c2=$self->checkOldCol($colID2); 855 2 50       7 return undef unless defined $c2; 856 2         6 my $name1=$self->{header}->[$c1]; 857 2         6 my $name2=$self->{header}->[$c2]; 858               859 2         5 $self->{header}->[$c1]=$name2; 860 2         4 $self->{header}->[$c2]=$name1; 861 2         5 $self->{colHash}->{$name1}=$c2; 862 2         10 $self->{colHash}->{$name2}=$c1; 863 2 50       8 $self->rotate() unless $self->{type}; 864 2         6 my $data1=$self->{data}->[$c1]; 865 2         5 my $data2=$self->{data}->[$c2]; 866 2         6 $self->{data}->[$c1]=$data2; 867 2         4 $self->{data}->[$c2]=$data1; 868 2         11 return 1; 869             } 870               871             sub moveCol { 872 1     1 1 6 my ($self, $colID, $colIdx, $newColName) = @_; 873 1         8 my $c=$self->checkOldCol($colID); 874 1 50       5 return undef unless defined $c; 875 1 50 33     11 confess "New column location out of bound!" unless ($colIdx >= 0 && $colIdx < $self->nofCol); 876 1 50       6 return if $c == $colIdx; 877 1         5 my $colName = $self->{header}->[$c]; 878 1         7 my $col = $self->delCol($colID); 879 1         9 $self->addCol($col, $colName, $colIdx); 880 1 50       5 $self->rename($colIdx, $newColName) if defined $newColName; 881 1         6 return 1; 882             } 883               884             sub checkOldRow { 885 1077     1077 0 1751 my ($self, $rowIdx) = @_; 886 1077         1982 my $maxIdx=$self->nofRow()-1; 887 1077 50       2132 unless (defined $rowIdx) { 888 0         0 print STDERR " Invalid row index in call to checkOldRow\n"; 889 0         0 return undef; 890             } 891 1077 50 33     3705 if ($rowIdx<0 || $rowIdx>$maxIdx) { 892 0         0 print STDERR "Row index out of range [0..$maxIdx]" ; 893 0         0 return undef; 894             } 895 1077         2403 return $rowIdx; 896             } 897               898             sub checkNewRow { 899 8     8 0 74 my ($self, $rowIdx) = @_; 900 8         18 my $maxIdx=$self->nofRow()-1; 901 8 50       20 unless (defined $rowIdx) { 902 0         0 print STDERR "Invalid row index: $rowIdx \n"; 903 0         0 return undef; 904             } 905 8         12 $maxIdx+=1; 906 8 50 33     32 if ($rowIdx<0 || $rowIdx>$maxIdx) { 907 0         0 print STDERR "Row index out of range [0..$maxIdx]" ; 908 0         0 return undef; 909             } 910 8         21 return $rowIdx; 911             } 912               913             sub checkOldCol { 914 833     833 0 1470 my ($self, $colID) = @_; 915 833         1509 my $c=$self->colIndex($colID); 916 833 50       1852 if ($c < 0) { 917 0         0 print STDERR "Invalid column $colID"; 918 0         0 return undef; 919             } 920 833         1658 return $c; 921             } 922               923             sub checkNewCol { 924 30     30 0 69 my ($self, $colIdx, $colName) = @_; 925 30         70 my $numCol=$self->nofCol(); 926 30 50       77 unless (defined $colIdx) { 927 0         0 print STDERR "Invalid column index $colIdx"; 928 0         0 return undef; 929             } 930 30 50 33     129 if ($colIdx<0 || $colIdx>$numCol) { 931 0         0 print STDERR "Column index $colIdx out of range [0..$numCol]"; 932 0         0 return undef; 933             } 934 30 50       92 if (defined $self->{colHash}->{$colName} ) { 935 0         0 print STDERR "Column name $colName already exists" ; 936 0         0 return undef; 937             } 938 30 50       133 unless ($colName =~ /\D/) { 939 0         0 print STDERR "Invalid column name $colName" ; 940 0         0 return undef; 941             } 942 30         89 return $colIdx; 943             } 944               945             sub elm { 946 628     628 1 4205 my ($self, $rowIdx, $colID) = @_; 947 628         1117 my $c=$self->checkOldCol($colID); 948 628 50       1284 return undef unless defined $c; 949 628 50       1224 return undef unless defined $self->checkOldRow($rowIdx); 950             return ($self->{type})? 951             $self->{data}->[$c]->[$rowIdx]: 952 628 100       2351 $self->{data}->[$rowIdx]->[$c]; 953             } 954               955             sub elmRef { 956 1     1 1 4 my ($self, $rowIdx, $colID) = @_; 957 1         3 my $c=$self->checkOldCol($colID); 958 1 50       4 return undef unless defined $c; 959 1 50       4 return undef unless defined $self->checkOldRow($rowIdx); 960             return ($self->{type})? 961             \$self->{data}->[$c]->[$rowIdx]: 962 1 50       9 \$self->{data}->[$rowIdx]->[$c]; 963             } 964               965             sub setElm { 966 80     80 1 180 my ($self, $rowIdx, $colID, $val) = @_; 967 80 100       203 $rowIdx = [$rowIdx] if ref($rowIdx) eq ''; 968 80 50       188 $colID = [$colID] if ref($colID) eq ''; 969 80         145 foreach my $col (@$colID) { 970 80         156 my $c=$self->checkOldCol($col); 971 80 50       174 return undef unless defined $c; 972 80         130 foreach my $row (@$rowIdx) { 973 116 50       229 return undef unless defined $self->checkOldRow($row); 974 116 50       196 if ($self->{type}) { 975 116         245 $self->{data}->[$c]->[$row]=$val; 976             } else { 977 0         0 $self->{data}->[$row]->[$c]=$val; 978             } 979             } 980             } 981 80         287 return 1; 982             } 983               984             # convert the internal structure of a table between row-based and column-based 985             sub rotate { 986 26     26 1 51 my $self=shift; 987 26         49 my $newdata=[]; 988 26         52 my $data=$self->{data}; 989 26 100       80 $self->{type} = ($self->{type})?0:1; 990 26 50 66     122 if ($self->{type} && scalar @$data == 0) { 991 0         0 for (my $i=0; $i < $self->nofCol; $i++) { 992 0         0 $newdata->[$i] = []; 993             } 994             } else { 995 26         50 for (my $i=$#{$data->[0]}; $i>=0; $i--) {   26         103   996 366         448 for (my $j=$#{$data}; $j>=0; $j--) {   366         725   997 3740         8138 $newdata->[$i][$j]=$data->[$j][$i]; 998             } 999             } 1000             } 1001 26         72 $self->{data}=$newdata; 1002 26         232 return 1; 1003             } 1004               1005             sub header { 1006 15     15 1 55 my ($self, $header) = @_; 1007 15 100       48 unless (defined($header)) { 1008 14         26 return @{$self->{header}};   14         80   1009             } else { 1010 1 50       3 if (scalar @$header != scalar @{$self->{header}}) {   1         7   1011 0         0 confess "Header array should have size ".(scalar @{$self->{header}});   0         0   1012             } else { 1013 1         5 my $colHash = checkHeader($header); 1014 1         5 $self->{header} = $header; 1015 1         7 $self->{colHash} = $colHash; 1016             } 1017             } 1018             } 1019               1020             sub type { 1021 0     0 1 0 my $self=shift; 1022 0         0 return $self->{type}; 1023             } 1024               1025             sub data { 1026 3     3 1 5 my $self=shift; 1027 3         8 return $self->{data}; 1028             } 1029               1030             # $t->sort(colID1, type1, order1, colID2, type2, order2, ... ); 1031             # where 1032             # colID is a column index (integer) or name (string), 1033             # type is 0 for numerical and 1 for others 1034             # order is 0 for ascending and 1 for descending 1035             # Sorting is done with priority of colname1, colname2, ... 1036               1037             sub sort_v0 { 1038 0     0 0 0 my $self = shift; 1039 0         0 my ($str, $i) = ("", 0); 1040 0         0 my @cols = (); 1041 0         0 while (scalar @_) { 1042 0         0 my $c = shift; 1043 0         0 my $col = $self->checkOldCol($c); 1044 0 0       0 return undef unless defined $col; 1045 0         0 push @cols, $col; 1046 0         0 my $op = '<=>'; 1047 0 0       0 $op = 'cmp' if shift; # string 1048 0 0       0 $str .=(shift)? "(\$b->[$i] $op \$a->[$i]) || " : 1049             "(\$a->[$i] $op \$b->[$i]) || " ; 1050 0         0 $i++; 1051             } 1052 0         0 substr($str, -3) = ""; # removes || from the end of $str 1053 0 0       0 $self->rotate() if $self->{type}; 1054             # construct a pre-ordered array 1055 0     0   0 my $fun = sub { my ($cols, $data) = @_; 1056 0         0 my @ext; 1057 0         0 @ext = map {$data->[$_]} @$cols;   0         0   1058 0         0 push @ext, $data; 1059 0         0 return \@ext; 1060 0         0 }; 1061 0         0 my @preordered = map {&$fun(\@cols, $_)} @{$self->{data}};   0         0     0         0   1062 0         0 $self->{data} = [ map {$_->[$i]} eval "sort {$str} \@preordered;" ];   0         0   1063 0         0 return 1; 1064             } 1065             1066             sub sort { 1067 4     4 1 26 my $self = shift; 1068 4         14 my @cols = @_; 1069 4 50       18 confess "Parameters be in groups of three!\n" if ($#cols % 3 != 2); 1070 4         41 foreach (0 .. ($#cols/3)) { 1071 5         17 my $col = $self->checkOldCol($cols[$_*3]); 1072 5 50       15 return undef unless defined $col; 1073 5         14 $cols[$_*3]=$col; 1074             } 1075 4         10 my @subs=(); 1076 4         17 for (my $i=0; $i<=$#cols; $i+=3) { 1077 5         9 my $mysub; 1078 5 50       28 if ($cols[$i+1] == 0) {     100               50           1079 0 0   0   0 $mysub = ($cols[$i+2]? sub {defined($_[1])?(defined($_[0])? $_[1] <=> $_[0]:1):(defined($_[0])?-1:0)} : sub {defined($_[1])?(defined($_[0])? $_[0] <=> $_[1]:-1):(defined($_[0])?1:0)});   0 0       0     0 0       0       0               0               0               0           1080             } elsif ($cols[$i+1] == 1) { 1081 4 50   21   31 $mysub = ($cols[$i+2]? sub {defined($_[1])?(defined($_[0])? $_[1] cmp $_[0]:1):(defined($_[0])?-1:0)} : sub {defined($_[1])?(defined($_[0])? $_[0] cmp $_[1]:-1):(defined($_[0])?1:0)});   21 0       54     39 50       126       50               0               50               100           1082             } elsif (ref $cols[$i+1] eq 'CODE') { 1083 1         4 my $predicate=$cols[$i+1]; 1084 0 0   0   0 $mysub = ($cols[$i+2]? sub {defined($_[1])?(defined($_[0])? $predicate->($_[1],$_[0]) : 1): (defined($_[0])?-1:0)} :     0               0           1085 1 50   14   10 sub {defined($_[1])?(defined($_[0])? $predicate->($_[0],$_[1]) : -1): (defined($_[0])?1:0)} );   14 0       83       50               50           1086             } else { 1087 0         0 confess "Sort method should be 0 (numerical), 1 (other type), or a subroutine reference!\n"; 1088             } 1089 5         17 push @subs, $mysub; 1090             } 1091             my $func = sub { 1092 68     68   92 my $res = 0; 1093 68         128 foreach (0 .. ($#cols/3)) { 1094 74   66     226 $res ||= $subs[$_]->($a->[$cols[$_*3]], $b->[$cols[$_*3]]); 1095 74 100       268 return $res unless $res==0; 1096             } 1097 5         11 return $res; 1098 4         35 }; 1099 4 100       27 $self->rotate() if $self->{type}; 1100 4         8 $self->{data} = [sort $func @{$self->{data}}];   4         24   1101 4         44 return 1; 1102             } 1103               1104             # return rows as sub table in which 1105             # a pattern $pattern is matched 1106             sub match_pattern { 1107 1     1 1 6 my ($self, $pattern, $countOnly) = @_; 1108 1         5 my @data=(); 1109 1 50       5 $countOnly=0 unless defined($countOnly); 1110 1         3 my $cnt=0; 1111 1 50       4 $self->rotate() if $self->{type}; 1112 1         166 @Data::Table::OK= eval "map { $pattern?1:0; } \@{\$self->{data}};"; 1113 1         8 my @ok = @Data::Table::OK; 1114 1         6 $self->{OK} = \@ok; 1115 1         8 for (my $i=0; $i<$self->nofRow(); $i++) { 1116 9 100       23 if ($self->{OK}->[$i]) { 1117 2 50       9 push @data, $self->{data}->[$i] unless $countOnly; 1118 2         6 $cnt++; 1119 2         5 $self->{OK}->[$i] = 1; 1120 2         15 $Data::Table::OK[$i] = 1; 1121             } else { 1122             # in case sometimes eval results is '' instead of 0 1123 7         12 $self->{OK}->[$i] = 0; 1124 7         19 $Data::Table::OK[$i] = 0; 1125             } 1126             } 1127 1         6 $self->{MATCH} = []; 1128 1 100       6 map { push @{$self->{MATCH}}, $_ if $self->{OK}->[$_] } 0 .. $#ok;   9         35     2         16   1129 1 50       7 return $cnt if $countOnly; 1130 1         3 my @header=@{$self->{header}};   1         7   1131 1         8 return new Data::Table(\@data, \@header, 0); 1132             } 1133               1134             # return rows as sub table in which 1135             # a pattern $pattern is matched 1136             # each row is passed to the patern as a hash, where column names are keys 1137             sub match_pattern_hash { 1138 2     2 1 19 my ($self, $pattern, $countOnly) = @_; 1139 2         6 my @data=(); 1140 2 50       13 $countOnly=0 unless defined($countOnly); 1141 2         6 my $cnt=0; 1142 2 100       13 $self->rotate() if $self->{type}; 1143 2         11 @Data::Table::OK = (); 1144 2         13 for (my $i=0; $i<$self->nofRow(); $i++) { 1145 86         115 local %_ = %{$self->rowHashRef($i)};   86         163   1146 86         4414 $Data::Table::OK[$i] = eval "$pattern?1:0"; 1147             } 1148             #@Data::Table::OK= eval "map { $pattern?1:0; } \@{\$self->{data}};"; 1149 2         18 my @ok = @Data::Table::OK; 1150 2         11 $self->{OK} = \@ok; 1151 2         12 for (my $i=0; $i<$self->nofRow(); $i++) { 1152 86 100       156 if ($self->{OK}->[$i]) { 1153 39 50       86 push @data, $self->{data}->[$i] unless $countOnly; 1154 39         48 $cnt++; 1155 39         46 $self->{OK}->[$i] = 1; 1156 39         67 $Data::Table::OK[$i] = 1; 1157             } else { 1158             # in case sometimes eval results is '' instead of 0 1159 47         64 $self->{OK}->[$i] = 0; 1160 47         76 $Data::Table::OK[$i] = 0; 1161             } 1162             } 1163 2         9 $self->{MATCH} = []; 1164 2 100       19 map { push @{$self->{MATCH}}, $_ if $self->{OK}->[$_] } 0 .. $#ok;   86         163     39         90   1165 2 50       10 return $cnt if $countOnly; 1166 2         5 my @header=@{$self->{header}};   2         11   1167 2         15 return new Data::Table(\@data, \@header, 0); 1168             } 1169               1170             # return rows as sub table in which 1171             # a string elm in an array @$s is matched 1172             sub match_string { 1173 2     2 1 9 my ($self, $s, $caseIgn, $countOnly) = @_; 1174 2 50       6 confess unless defined($s); 1175 2 50       6 $countOnly=0 unless defined($countOnly); 1176 2         6 my @data=(); 1177 2         4 my $r; 1178 2 50       6 $self->rotate() if $self->{type}; 1179 2         7 @Data::Table::OK=(); 1180 2         6 $self->{OK} = []; 1181 2 50       7 $caseIgn=0 unless defined($caseIgn); 1182               1183             ### comment out next line if your perl version < 5.005 ### 1184 2 50       43 $r = ($caseIgn)?qr/$s/i : qr/$s/; 1185 2         5 my $cnt=0; 1186               1187 2         4 foreach my $row_ref (@{$self->data}) {   2         9   1188 18         22 push @Data::Table::OK, 0; 1189 18         19 push @{$self->{OK}}, 0;   18         27   1190 18         24 foreach my $elm (@$row_ref) { 1191 83 50       114 next unless defined($elm); 1192             1193             ### comment out the next line if your perl version < 5.005 1194 83 100       219 if ($elm =~ /$r/) { 1195             ### uncomment the next line if your perl version < 5.005 1196             # if ($elm =~ /$s/ || ($elm=~ /$s/i && $caseIgn)) { 1197               1198 5 50       13 push @data, $row_ref unless $countOnly; 1199 5         8 $Data::Table::OK[$#Data::Table::OK]=1; 1200 5         8 $self->{OK}->[$#{$self->{OK}}]=1;   5         11   1201 5         6 $cnt++; 1202 5         8 last; 1203             } 1204             } 1205             } 1206 2         6 $self->{MATCH} = []; 1207 2 100       6 map { push @{$self->{MATCH}}, $_ if $self->{OK}->[$_] } 0 .. $#{$self->{OK}};   18         40     5         12     2         8   1208 2 50       8 return $cnt if $countOnly; 1209 2         4 my @header=@{$self->{header}};   2         8   1210 2         9 return new Data::Table(\@data, \@header, 0); 1211             } 1212             1213             sub rowMask { 1214 1     1 1 9 my ($self, $OK, $c) = @_; 1215 1 50       5 confess unless defined($OK); 1216 1 50       2 $c = 0 unless defined ($c); 1217 1         3 my @data=(); 1218 1 50       4 $self->rotate() if $self->{type}; 1219 1         4 my $data0=$self->data; 1220 1         6 for (my $i=0; $i<$self->nofRow(); $i++) { 1221 9 50       13 if ($c) { 1222 9 100       22 push @data, $data0->[$i] unless $OK->[$i]; 1223             } else { 1224 0 0       0 push @data, $data0->[$i] if $OK->[$i]; 1225             } 1226             } 1227 1         2 my @header=@{$self->{header}};   1         4   1228 1         2 return new Data::Table(\@data, \@header, 0); 1229             } 1230               1231             sub rowMerge { 1232 4     4 1 18 my ($self, $tbl, $arg_ref) = @_; 1233 4 100       19 my %arg = defined $arg_ref ? %$arg_ref : (); 1234 4 100       16 $arg{byName} =0 unless exists $arg{byName}; 1235 4 100       14 $arg{addNewCol} = 0 unless exists $arg{addNewCol}; 1236 4 50 33     11 if ($self->isEmpty && !$tbl->isEmpty) { 1237 0         0 my @header = $tbl->header; 1238 0         0 my $i = 0; 1239 0         0 foreach my $s (@header) { 1240 0         0 push @{$self->{header}}, $s;   0         0   1241 0         0 $self->{colHash}->{$s} = $i++; 1242             } 1243             } 1244 4 100 100     22 if ($arg{byName} == 0 && $arg{addNewCol} == 0) { 1245 1 50       4 confess "Tables must have the same number of columns" unless ($self->nofCol()==$tbl->nofCol()); 1246             } else { 1247 3 100       13 if ($arg{addNewCol}) { 1248 2 100       8 unless ($arg{byName}) { # add extra column by index 1249 1 50       5 if ($self->nofCol < $tbl->nofCol) {     50           1250 0         0 my @header = $tbl->header; 1251 0         0 my $nCols = $self->nofCol(); 1252 0         0 my $nRows = $self->nofRow(); 1253 0         0 for (my $i = $nCols; $i<@header; $i++) { 1254 0         0 my @one = (undef) x $nRows; 1255 0         0 $self->addCol(\@one, $header[$i]); 1256             } 1257             } elsif ($self->nofCol > $tbl->nofCol) { 1258 1         5 my @header = $self->header; 1259 1         4 my %h = (); 1260 1         4 my @header2 = $tbl->header; 1261 1         5 map {$h{$_} = 1} @header2;   2         7   1262 1         6 my $nCols = $tbl->nofCol(); 1263 1         4 my $nRows = $tbl->nofRow(); 1264 1         8 for (my $i = $nCols; $i<$self->nofCol; $i++) { 1265 2         8 my @one = (undef) x $nRows; 1266             # make sure new col name is unique 1267 2         6 my $s = $header[$i]; 1268 2         5 my $cnt = 2; 1269 2         7 while (exists $h{$s}) { 1270 0         0 $s = $header[$i]."_".$cnt ++; 1271             } 1272 2         8 $tbl->addCol(\@one, $s); 1273 2         5 $h{$s} = 1; 1274             } 1275             } 1276             } else { 1277 1         5 my @header = $tbl->header; 1278 1         5 my $nRows = $self->nofRow(); 1279 1         5 foreach my $col (@header) { 1280 2 50       8 if ($self->colIndex($col) < 0) { 1281 2         9 my @one = (undef) x $nRows; 1282 2         9 $self->addCol(\@one, $col); 1283             } 1284             } 1285             } 1286             } 1287             } 1288 4 100       14 $self->rotate() if $self->{type}; 1289 4 100       16 $tbl->rotate() if $tbl->{type}; 1290 4         7 my $data=$self->{data}; 1291 4 100       9 if ($arg{byName} == 0) { 1292 2         5 push @$data, @{$tbl->{data}};   2         8   1293             } else { 1294 2         6 my @header = $self->header; 1295 2         5 my $nCols = scalar @header; 1296 2         5 my @colIndex = map { $tbl->colIndex($_) } @header;   6         11   1297 2         4 foreach my $rowRef (@{$tbl->{data}}) {   2         6   1298 6         8 my @one = (); 1299 6         14 for (my $j=0; $j< $nCols; $j++) { 1300 18 100       36 $one[$j] = $colIndex[$j]>=0 ? $rowRef->[$colIndex[$j]]:undef; 1301             } 1302 6         12 push @$data, \@one; 1303             } 1304             } 1305 4         11 return 1; 1306             } 1307               1308             sub colMerge { 1309 2     2 1 18 my ($self, $tbl, $arg_ref) = @_; 1310 2 100       11 my %arg = defined $arg_ref ? %$arg_ref : (); 1311 2 100       11 $arg{renameCol} =0 unless exists $arg{renameCol}; 1312 2 50 33     9 confess "Tables must have the same number of rows" unless ($self->isEmpty || $self->nofRow()==$tbl->nofRow()); 1313 2         5 my $col; 1314 2         14 my %h = (); 1315 2         6 map {$h{$_} = 1} @{$self->{header}};   12         27     2         8   1316 2         8 my @header2 = (); 1317 2         11 foreach $col ($tbl->header) { 1318 7         10 my $s = $col; 1319 7 100       17 if (exists $h{$s}) { 1320 6 50       11 confess "Duplicate column $col in two tables" unless $arg{renameCol}; 1321 6         8 my $cnt = 2; 1322 6         10 while (exists $h{$s}) { 1323 6         20 $s = $col ."_". $cnt++; 1324             } 1325             } 1326 7         16 $h{$s} = 1; 1327 7         14 push @header2, $s; 1328             } 1329 2 50       15 $self->rotate() unless $self->{type}; 1330 2 50       13 $tbl->rotate() unless $tbl->{type}; 1331 2         8 my $i = $self->nofCol(); 1332 2         7 for my $s (@header2) { 1333 7         13 push @{$self->{header}}, $s;   7         18   1334 7         24 $self->{colHash}->{$s} = $i++; 1335             } 1336 2         6 my $data=$self->{data}; 1337 2         8 for ($i=0; $i<$tbl->nofCol(); $i++) { 1338 7         14 push @$data, $tbl->{data}->[$i]; 1339             } 1340 2         9 return 1; 1341             } 1342               1343             sub subTable { 1344 7     7 1 21 my ($self, $rowIdcsRef, $colIDsRef, $arg_ref) = @_; 1345 7         14 my @newdata=(); 1346 7         13 my @newheader=(); 1347             # to avoid the side effect of modifying $colIDsRef, 4/30/2012 1348 7         12 my $useRowMask = 0; 1349 7 100       22 $useRowMask = $arg_ref->{useRowMask} if defined $arg_ref->{useRowMask}; 1350 7         10 my @rowIdcs = (); 1351 7 100       25 @rowIdcs = defined $rowIdcsRef ? @$rowIdcsRef : 0..($self->nofRow()-1) unless $useRowMask;     100           1352 7 100       29 my @colIDs = defined $colIDsRef ? @$colIDsRef : 0..($self->nofCol()-1); 1353             ##$rowIdcsRef = [0..($self->nofRow()-1)] unless defined $rowIdcsRef; 1354             #$colIDsRef = [0..($self->nofCol()-1)] unless defined $colIDsRef; 1355 7         23 for (my $i = 0; $i < scalar @colIDs; $i++) { 1356 33         70 $colIDs[$i]=$self->checkOldCol($colIDs[$i]); 1357             #return undef unless defined $colIDsRef; 1358 33         102 push @newheader, $self->{header}->[$colIDs[$i]]; 1359             } 1360 7 100       19 if ($useRowMask) { 1361 1         7 my @OK = @$rowIdcsRef; 1362 1         5 my $n = $self->nofRow; 1363 1         6 for (my $i = 0; $i < $n; $i++) { 1364 9 100       30 push @rowIdcs, $i if $OK[$i]; 1365             } 1366             } 1367 7 50       19 if ($self->{type}) { 1368 0         0 for (my $i = 0; $i < scalar @colIDs; $i++) { 1369 0         0 my @one=(); 1370 0         0 for (my $j = 0; $j < scalar @rowIdcs; $j++) { 1371 0 0       0 return undef unless defined $self->checkOldRow($rowIdcs[$j]); 1372 0         0 push @one, $self->{data}->[$colIDs[$i]]->[$rowIdcs[$j]]; 1373             } 1374 0         0 push @newdata, \@one; 1375             } 1376             } else { 1377 7         21 for (my $i = 0; $i < scalar @rowIdcs; $i++) { 1378 30 50       50 return undef unless defined $self->checkOldRow($rowIdcs[$i]); 1379 30         45 my @one=(); 1380 30         52 for (my $j = 0; $j < scalar @colIDs; $j++) { 1381 127         275 push @one, $self->{data}->[$rowIdcs[$i]]->[$colIDs[$j]]; 1382             } 1383 30         72 push @newdata, \@one; 1384             } 1385             } 1386 7         22 return new Data::Table(\@newdata, \@newheader, $self->{type}); 1387             } 1388               1389             sub reorder { 1390 1     1 1 5 my ($self, $colIDsRef, $arg_ref) = @_; 1391 1 50       7 return unless defined $colIDsRef; 1392 1 50       6 $arg_ref = {keepRest => 1} unless defined $arg_ref; 1393 1         11 my @newdata=(); 1394 1         3 my @newheader=(); 1395 1         2 my @colIDs = (); 1396 1         2 my %inNew = (); 1397 1         5 for (my $i = 0; $i < scalar @$colIDsRef; $i++) { 1398 3         13 my $idx = $self->checkOldCol($colIDsRef->[$i]); 1399 3 50       6 confess "Invalide column $colIDsRef->[$i]" unless defined $idx; 1400 3         5 $colIDs[$i] = $idx; 1401 3         7 $inNew{$idx} = 1; 1402             #return undef unless defined $colIDsRef; 1403 3         11 push @newheader, $self->{header}->[$idx]; 1404             } 1405 1 50       4 if ($arg_ref->{keepRest}) { 1406 1         6 for (my $i = 0; $i<$self->nofCol; $i++) { 1407 6 100       15 unless (exists $inNew{$i}) { 1408 3         7 push @colIDs, $i; 1409 3         6 push @newheader, $self->{header}->[$i]; 1410             } 1411             } 1412             } 1413             1414 1 50       3 if ($self->{type}) { 1415 1         4 for (my $i = 0; $i < scalar @colIDs; $i++) { 1416 6         13 push @newdata, $self->{data}->[$colIDs[$i]]; 1417             } 1418             } else { 1419 0         0 my $n = $self->nofRow; 1420 0         0 for (my $i = 0; $i < $n; $i++) { 1421 0         0 my @one=(); 1422 0         0 for (my $j = 0; $j < scalar @colIDs; $j++) { 1423 0         0 push @one, $self->{data}->[$i]->[$colIDs[$j]]; 1424             } 1425 0         0 push @newdata, \@one; 1426             } 1427             } 1428 1         4 $self->{header} = \@newheader; 1429 1         2 $self->{colHash} = (); 1430 1         5 for (my $i = 0; $i < scalar @colIDs; $i++) { 1431 6         14 $self->{colHash}->{$newheader[$i]} = $i; 1432             } 1433 1         5 $self->{data} = \@newdata; 1434             } 1435               1436             sub clone { 1437 4     4 1 710 my $self = shift; 1438 4         10 my $data = $self->{data}; 1439 4         12 my @newheader = @{$self->{header}};   4         19   1440 4         11 my @newdata = (); 1441 4         12 for (my $i = 0; $i < scalar @{$data}; $i++) {   34         72   1442 30         48 my @one=(); 1443 30         53 for (my $j = 0; $j < scalar @{$data->[$i]}; $j++) {   198         435   1444 168         356 push @one, $data->[$i]->[$j]; 1445             } 1446 30         61 push @newdata, \@one; 1447             } 1448 4         18 return new Data::Table(\@newdata, \@newheader, $self->{type}); 1449             } 1450               1451             sub fromCSVi { 1452 2     2 1 7 my $self = shift; 1453 2         9 return fromCSV(@_); 1454             } 1455               1456             sub getOneLine { 1457 216     216 0 445 my ($fh, $linebreak, $qualifier) = @_; 1458 216         324 my $s = ''; 1459 216 50       441 $qualifier = '' unless defined $qualifier; 1460 216         808 local($/) = $linebreak; 1461 216 100       515 return <$fh> unless $qualifier; 1462 214         1341 while (my $s2 = <$fh>) { 1463 197         752 $s .= $s2; 1464 197         708 my @S = ($s =~ /$qualifier/g); 1465 197 50       1122 return $s if (scalar @S % 2 == 0); 1466             } 1467 17         90 return $s; 1468             } 1469               1470             sub fromCSV { 1471 17     17 1 219 my ($name_or_handler, $includeHeader, $header, $arg_ref) = @_; 1472 17 100       75 $includeHeader = 1 unless defined($includeHeader); 1473 17         112 my ($OS, $delimiter, $qualifier, $skip_lines, $skip_pattern, $encoding) = ($Data::Table::DEFAULTS{OS}, $Data::Table::DEFAULTS{CSV_DELIMITER}, $Data::Table::DEFAULTS{CSV_QUALIFIER}, 0, undef, $Data::Table::DEFAULTS{ENCODING}); 1474 17 100 100     96 $OS = $arg_ref->{'OS'} if (defined($arg_ref) && defined($arg_ref->{'OS'})); 1475             # OS: 0 for UNIX (\n as linebreak), 1 for Windows (\r\n as linebreak) 1476             ### 2 for MAC (\r as linebreak) 1477 17 100       54 if (defined($arg_ref)) { 1478 8 50       38 $delimiter = $arg_ref->{'delimiter'} if defined($arg_ref->{'delimiter'}); 1479 8 100       28 $qualifier = $arg_ref->{'qualifier'} if defined($arg_ref->{'qualifier'}); 1480 8 100 66     36 $skip_lines = $arg_ref->{'skip_lines'} if (defined($arg_ref->{'skip_lines'}) && $arg_ref->{'skip_lines'}>0); 1481 8 100       25 $skip_pattern = $arg_ref->{'skip_pattern'} if defined($arg_ref->{'skip_pattern'}); 1482 8 50       51 $encoding = $arg_ref->{'encoding'} if defined($arg_ref->{'encoding'}); 1483             } 1484 17         38 my @header; 1485 17         32 my $givenHeader = 0; 1486 17 50 33     71 if (defined($header) && ref($header) eq 'ARRAY') { 1487 0         0 $givenHeader = 1; 1488 0         0 @header= @$header; 1489             } 1490 17         60 my $SRC=openFileWithEncoding($name_or_handler, $encoding); 1491 17         50 my @data = (); 1492 17         60 my $oldRowDelimiter=$/; 1493 17 100       96 my $newRowDelimiter=($OS==2)?"\r":(($OS==1)?"\r\n":"\n");     100           1494 17         43 my $n_endl = length($newRowDelimiter); 1495 17         63 $/=$newRowDelimiter; 1496 17         58 my $s; 1497 17         84 for (my $i=0; $i<$skip_lines; $i++) { 1498             #$s=<$SRC>; 1499 1         6 $s = getOneLine($SRC, $newRowDelimiter, $qualifier); 1500             } 1501             #$s=<$SRC>; 1502 17         73 $s = getOneLine($SRC, $newRowDelimiter, $qualifier); 1503 17 100 66     72 if (defined($skip_pattern)) { while (defined($s) && $s =~ /$skip_pattern/) { $s = getOneLine($SRC, $newRowDelimiter, $qualifier); }}   1         30     1         7   1504             #{ $s = <$SRC> }; } 1505 17 50       96 if (substr($s, -$n_endl, $n_endl) eq $newRowDelimiter) { for (1..$n_endl) { chop $s }}   17         64     19         75   1506             # $_=~ s/$newRowDelimiter$//; 1507 17 50       53 unless ($s) { 1508             #confess "Empty data file" unless $givenHeader; 1509 0 0       0 return undef unless $givenHeader; 1510 0         0 $/=$oldRowDelimiter; 1511 0         0 return new Data::Table(\@data, \@header, 0); 1512             } 1513 17         37 my $one; 1514 17 50       138 if ($s =~ /$delimiter$/) { # if the line ends by ',', the size of @one will be incorrect 1515             # due to the tailing of split function in perl 1516 0         0 $s .= ' '; # e.g., split $s="a," will only return a list of size 1. 1517 0         0 $one = parseCSV($s, undef, {delimiter=>$delimiter, qualifier=>$qualifier}); 1518 0         0 $one->[$#{$one}]=undef;   0         0   1519             } else { 1520 17         107 $one = parseCSV($s, undef, {delimiter=>$delimiter, qualifier=>$qualifier}); 1521             } 1522             #print join("|", @$one), scalar @$one, "\n"; 1523 17         64 my $size = scalar @$one; 1524 17 50       53 unless ($givenHeader) { 1525 17 100       49 if ($includeHeader) { 1526 16         66 @header = @$one; 1527             } else { 1528 1         5 @header = map {"col$_"} (1..$size); # name each column as col1, col2, .. etc   3         12   1529             } 1530             } 1531 17 100       62 push @data, $one unless ($includeHeader); 1532               1533             #while($s = <$SRC>) { 1534 17         60 while($s = getOneLine($SRC, $newRowDelimiter, $qualifier)) { 1535 171 50 66     528 next if (defined($skip_pattern) && $s =~ /$skip_pattern/); 1536 171 100       557 if (substr($s, -$n_endl, $n_endl) eq $newRowDelimiter) { for (1..$n_endl) { chop $s }}   170         402     249         467   1537             # $_=~ s/$newDelimiter$//; 1538 171         562 my $one = parseCSV($s, $size, {delimiter=>$delimiter, qualifier=>$qualifier}); 1539 171 50       547 confess "Inconsistent column number at data entry: ".($#data+1) unless ($size==scalar @$one); 1540 171         475 push @data, $one; 1541             } 1542 17         185 close($SRC); 1543 17         66 $/=$oldRowDelimiter; 1544 17         149 return new Data::Table(\@data, \@header, 0); 1545             } 1546               1547             # Idea: use \ as the escape char to encode a CSV string, 1548             # replace \ by \\ and comma inside a field by \c. 1549             # A comma inside a field must have odd number of " in front of it, 1550             # therefore it can be distinguished from comma used as the deliminator. 1551             # After escape, and split by comma, we unescape each field string. 1552             # 1553             # This parser will never be crashed by any illegal CSV format, 1554             # it always return an array! 1555             sub parseCSV { 1556 237     237 1 557 my ($s, $size, $arg_ref)=@_; 1557 237 100       472 $size = 0 unless defined $size; 1558 237         541 my ($delimiter, $qualifier) = ($Data::Table::DEFAULTS{CSV_DELIMITER}, $Data::Table::DEFAULTS{CSV_QUALIFIER}); 1559 237 50 33     937 $delimiter = $arg_ref->{'delimiter'} if (defined($arg_ref) && defined($arg_ref->{'delimiter'})); 1560 237 100 66     812 $qualifier = $arg_ref->{'qualifier'} if (defined($arg_ref) && defined($arg_ref->{'qualifier'})); 1561 237 50       365 my $delimiter2 = $delimiter; $delimiter2 = substr($delimiter, 1, 1) if length($delimiter)>1;   237         466   1562 237 50       344 my $qualifier2 = $qualifier; $qualifier2 = substr($qualifier, 1, 1) if length($qualifier)>1;   237         476   1563             # $s =~ s/\n$//; # chop" # assume extra characters has been cleaned before 1564 237 100       809 if (-1==index $s, $qualifier) { 1565 227 100       458 if ($size == 0) { 1566 57         110 my $s2 = $s; 1567 57         509 $s2 =~ s/$delimiter//g; 1568 57         218 $size = length($s)-length($s2)+1; 1569             } 1570 227         1795 return [split /$delimiter/, $s , $size]; 1571             } 1572 10         43 $s =~ s/\\/\\\\/g; # escape \ => \\ 1573 10         25 my $n = length($s); 1574 10         29 my ($q, $i)=(0, 0); 1575 10         34 while ($i < $n) { 1576 672         1133 my $ch=substr($s, $i, 1); 1577 672         799 $i++; 1578 672 100 100     1866 if ($ch eq $delimiter2 && ($q%2)) {     100           1579 9         43 substr($s, $i-1, 1)='\\c'; # escape , => \c if it's not a deliminator 1580 9         19 $i++; 1581 9         21 $n++; 1582             } elsif ($ch eq $qualifier2) { 1583 78         160 $q++; 1584             } 1585             } 1586             # add look-ahead avoid the speical case where $delimiter is a tab 1587 10         320 $s =~ s/(^$qualifier)|($qualifier((?!$delimiter)\s)*$)//g; # get rid of boundary ", then restore "" => " 1588 10         235 $s =~ s/$qualifier((?!$delimiter)\s)*$delimiter/$delimiter2/g; 1589 10         157 $s =~ s/$delimiter((?!$delimiter)\s)*$qualifier/$delimiter2/g; 1590 10         86 $s =~ s/$qualifier$qualifier/$qualifier2/g; 1591 10 100       42 if ($size == 0) { 1592 9         25 my $s2 = $s; 1593 9         80 $s2 =~ s/$delimiter//g; 1594 9         45 $size = length($s)-length($s2)+1; 1595             } 1596 10         104 my @parts=split(/$delimiter/, $s, $size); 1597 10 50       37 @parts = map {$_ =~ s/(\\c|\\\\)/$1 eq '\c'?$delimiter2:'\\'/eg; $_ } @parts;   57         158     9         61     57         160   1598             # my @parts2=(); 1599             # foreach $s2 (@parts) { 1600             # $s2 =~ s/\\c/,/g; # restore \c => , 1601             # $s2 =~ s/\\\\/\\/g; # restore \\ => \ 1602             # push @parts2, $s2; 1603             # } 1604 10         48 return \@parts; 1605             } 1606               1607             sub transformElement { 1608 29     29 0 48 my $one = shift; 1609 29         69 for (my $i=0; $i < scalar @$one; $i++) { 1610 164 50       269 next unless defined($one->[$i]); 1611 164 50       250 if ($one->[$i] eq "\\N") { 1612 0         0 $one->[$i]=undef; 1613             } else { 1614 164         345 $one->[$i] =~ s/\\([0ntrb'"\\])/$Data::Table::TSV_ESC{$1}/g; 1615             } 1616             } 1617 29         52 return $one; 1618             } 1619               1620             sub fromTSVi { 1621 1     1 1 3 my $self = shift; 1622 1         4 return fromTSV(@_); 1623             } 1624               1625             sub fromTSV { 1626 5     5 1 27 my ($name_or_handler, $includeHeader, $header, $arg_ref) = @_; 1627 5         30 my ($OS, $skip_lines, $skip_pattern, $transform_element, $encoding) = ($Data::Table::DEFAULTS{OS}, 0, undef, 1, $Data::Table::DEFAULTS{ENCODING}); 1628 5 100 66     31 $OS = $arg_ref->{'OS'} if (defined($arg_ref) && defined($arg_ref->{'OS'})); 1629             # OS: 0 for UNIX (\n as linebreak), 1 for Windows (\r\n as linebreak) 1630             ### 2 for MAC (\r as linebreak) 1631 5 50 66     34 $skip_lines = $arg_ref->{'skip_lines'} if (defined($arg_ref) && defined($arg_ref->{'skip_lines'}) && $arg_ref->{'skip_lines'}>0);       33         1632 5 50       23 $skip_pattern = $arg_ref->{'skip_pattern'} if defined($arg_ref->{'skip_pattern'}); 1633 5 100       22 $transform_element = $arg_ref->{'transform_element'} if (defined($arg_ref->{'transform_element'})); 1634 5 50       22 $encoding = $arg_ref->{'encoding'} if (defined($arg_ref->{'encoding'})); 1635             #my %ESC = ( '0'=>"\0", 'n'=>"\n", 't'=>"\t", 'r'=>"\r", 'b'=>"\b", 1636             # "'"=>"'", '"'=>"\"", '\\'=>"\\" ); 1637             ## what about \f? MySQL treats \f as f. 1638               1639 5 100       22 $includeHeader = 1 unless defined($includeHeader); 1640 5 50       17 $OS=0 unless defined($OS); 1641             1642 5         11 my @header; 1643 5         10 my $givenHeader = 0; 1644 5 50 33     21 if (defined($header) && ref($header) eq 'ARRAY') { 1645 0         0 $givenHeader = 1; 1646 0         0 @header= @$header; 1647             } 1648 5         20 my $SRC=openFileWithEncoding($name_or_handler, $encoding); 1649 5         17 my @data = (); 1650 5         16 my $oldRowDelimiter=$/; 1651 5 50       30 my $newRowDelimiter=($OS==2)?"\r":(($OS==1)?"\r\n":"\n");     50           1652 5         11 my $n_endl = length($newRowDelimiter); 1653 5         18 $/=$newRowDelimiter; 1654 5         11 my $s; 1655 5         42 for (my $i=0; $i<$skip_lines; $i++) { 1656 0         0 $s=<$SRC>; 1657             } 1658 5         119 $s=<$SRC>; 1659 5 50 0     63 if (defined($skip_pattern)) { while (defined($s) && $s =~ /$skip_pattern/) { $s = <$SRC> }; }   0         0     0         0   1660 5 50       33 if (substr($s, -$n_endl, $n_endl) eq $newRowDelimiter) { for (1..$n_endl) { chop $s }}   5         20     5         22   1661             # $_=~ s/$newRowDelimiter$//; 1662 5 50       21 unless ($s) { 1663 0 0       0 confess "Empty data file" unless $givenHeader; 1664 0         0 $/=$oldRowDelimiter; 1665 0         0 return new Data::Table(\@data, \@header, 0); 1666             } 1667             #chop; 1668 5         9 my $one; 1669 5 50       27 if ($s =~ /\t$/) { # if the line ends by ',', the size of @$one will be incorrect 1670             # due to the tailing of split function in perl 1671 0         0 $s .= ' '; # e.g., split $s="a," will only return a list of size 1. 1672 0         0 @$one = split(/\t/, $s); 1673 0         0 $one->[$#{$one}]='';   0         0   1674             } else { 1675 5         39 @$one = split(/\t/, $s); 1676             } 1677             # print join("|", @$one), scalar @$one, "\n"; 1678 5         14 my $size = scalar @$one; 1679 5 50       16 unless ($givenHeader) { 1680 5 50       14 if ($includeHeader) { 1681 5 100       14 if ($transform_element) { 1682 4         12 @header = map { $_ =~ s/\\([0ntrb'"\\])/$Data::Table::TSV_ESC{$1}/g; $_ } @$one;   19         61     19         44   1683             } else { 1684 1         6 @header = @$one; 1685             } 1686             } else { 1687 0         0 @header = map {"col$_"} (1..$size); # name each column as col1, col2, .. etc   0         0   1688             } 1689             } 1690 5 50       20 unless ($includeHeader) { 1691 0 0       0 transformElement($one) if $transform_element; 1692 0         0 push @data, $one; 1693             } 1694 5         31 while($s = <$SRC>) { 1695             #chop; 1696             # $_=~ s/$newRowDelimiter$//; 1697 31 50 33     76 next if (defined($skip_pattern) && $s =~ /$skip_pattern/); 1698 31 50       87 if (substr($s, -$n_endl, $n_endl) eq $newRowDelimiter) { for (1..$n_endl) { chop $s }}   31         73     31         64   1699 31         225 my @one = split(/\t/, $s, $size); 1700 31 100       106 transformElement(\@one) if $transform_element; 1701             #for (my $i=0; $i < $size; $i++) { 1702             # next unless defined($one[$i]); 1703             # if ($one[$i] eq "\\N") { 1704             # $one[$i]=undef; 1705             # } else { 1706             # $one[$i] =~ s/\\([0ntrb'"\\])/$Data::Table::TSV_ESC{$1}/g; 1707             # } 1708             #} 1709 31 50       73 confess "Inconsistent column number at data entry: ".($#data+1) unless ($size==scalar @one); 1710 31         147 push @data, \@one; 1711             } 1712 5         53 close($SRC); 1713 5         48 $/=$oldRowDelimiter; 1714 5         47 return new Data::Table(\@data, \@header, 0); 1715             } 1716               1717             sub fromSQLi { 1718 0     0 1 0 my $self = shift; 1719 0         0 return fromSQL(@_); 1720             } 1721               1722             sub fromSQL { 1723 0     0 1 0 my ($dbh, $sql, $vars) = @_; 1724 0         0 my ($sth, $header, $t); 1725 0 0       0 if (ref $sql eq 'DBI::st') { 1726 0         0 $sth = $sql; 1727             } else { 1728 0 0       0 $sth = $dbh->prepare($sql) or confess "Preparing: , ".$dbh->errstr; 1729             } 1730 0 0       0 my @vars=() unless defined $vars; 1731 0 0       0 $sth->execute(@$vars) or confess "Executing: ".$dbh->errstr; 1732             # $Data::Table::ID = undef; 1733             # $Data::Table::ID = $sth->{'mysql_insertid'}; 1734 0 0       0 if ($sth->{NUM_OF_FIELDS}) { 1735 0         0 $header=$sth->{'NAME'}; 1736 0         0 $t = new Data::Table($sth->fetchall_arrayref(), $header, 0); 1737             } else { 1738 0         0 $t = undef; 1739             } 1740 0         0 $sth->finish; 1741 0         0 return $t; 1742             } 1743               1744             sub join { 1745 5     5 1 53 my ($self, $tbl, $type, $cols1, $cols2, $arg_ref) = @_; 1746 5         9 my $n1 = scalar @$cols1; 1747 5         24 my %arg= ( renameCol => 0, matchNULL => 0, NULLasEmpty => 0); 1748 5 100       18 $arg{renameCol} = $arg_ref->{renameCol} if exists $arg_ref->{renameCol}; 1749 5 50       15 $arg{matchNULL} = $arg_ref->{matchNULL} if exists $arg_ref->{matchNULL}; 1750 5 50       13 $arg{NULLasEmpty} = $arg_ref->{NULLasEmpty} if exists $arg_ref->{NULLasEmpty}; 1751             #%arg = %$arg_ref if defined $arg_ref; 1752             # default cols2 to cols1 if not specified 1753 5 50 33     19 if (!defined($cols2) && $n1>0) { 1754 0         0 $cols2 = []; 1755 0         0 foreach my $c (@$cols1) { 1756 0         0 push @$cols2, $c; 1757             } 1758             } 1759 5         9 my $n2 = scalar @$cols2; 1760 5 50       11 confess "The number of join columns must be the same: $n1 != $n2" unless $n1==$n2; 1761 5 50       14 confess "At least one join column must be specified" unless $n1; 1762 5         12 my ($i, $j, $k); 1763 5         10 my @cols3 = (); 1764 5         15 for ($i = 0; $i < $n1; $i++) { 1765 9         25 $cols1->[$i]=$self->checkOldCol($cols1->[$i]); 1766 9 50       23 confess "Unknown column ". $cols1->[$i] unless defined($cols1->[$i]); 1767 9         36 $cols2->[$i]=$tbl->checkOldCol($cols2->[$i]); 1768 9 50       19 confess "Unknown column ". $cols2->[$i] unless defined($cols2->[$i]); 1769 9         21 $cols3[$cols2->[$i]]=1; 1770             } 1771 5         9 my @cols4 = (); # the list of remaining columns 1772 5         10 my @header2 = (); 1773 5         141 for ($i = 0; $i < $tbl->nofCol; $i++) { 1774 30 100       56 unless (defined($cols3[$i])) { 1775 21         34 push @cols4, $i; 1776 21         44 push @header2, $tbl->{header}->[$i]; 1777             } 1778             } 1779               1780 5 50       17 $self->rotate() if $self->{type}; 1781 5 50       12 $tbl->rotate() if $tbl->{type}; 1782 5         8 my $data1 = $self->{data}; 1783 5         8 my $data2 = $tbl->{data}; 1784 5         9 my %H=(); 1785 5         9 my $key; 1786             my @subRow; 1787 5         15 for ($i = 0; $i < $self->nofRow; $i++) { 1788 37         47 @subRow = @{$data1->[$i]}[@$cols1];   37         89   1789 37         65 my @S = map {tsvEscape($_)} @subRow;   65         99   1790 37 0       84 map { $_ = '' if $_ eq '\\N' } @S if $arg{NULLasEmpty};   0 50       0   1791 37         72 $key = join("\t", @S); 1792 37 50       159 unless (defined($H{$key})) { 1793 37         159 $H{$key} = [[$i], []]; 1794             } else { 1795 0         0 push @{$H{$key}->[0]}, $i;   0         0   1796             } 1797             } 1798 5         14 for ($i = 0; $i < $tbl->nofRow; $i++) { 1799 33         49 @subRow = @{$data2->[$i]}[@$cols2];   33         77   1800             # we intentionally make the second table undef keys to be '\\N\\N', 1801             # so that they are different from the first table undef keys 1802             # avoid NULL == NULL in the join 1803 33         58 my @S = map {tsvEscape($_)} @subRow;   57         75   1804 33 0       52 map { $_ = ($arg{NULLasEmpty})? '':($arg{matchNULL} ? $_ : '\\N\\N') if $_ eq '\\N' } @S;   57 0       137       50           1805             #if ($j>= @S) { 1806 33         67 $key = join("\t", @S); 1807             #} else { 1808             # $key = $arg{matchNULL} ? '\\N' : '\\N\\N'; 1809             #} 1810 33 100       72 unless (defined($H{$key})) { 1811 8         34 $H{$key} = [[], [$i]]; 1812             } else { 1813 25         36 push @{$H{$key}->[1]}, $i;   25         89   1814             } 1815             } 1816             # $type 1817             # 0: inner join 1818             # 1: left outer join 1819             # 2: right outer join 1820             # 3: full outer join 1821 5         8 my @ones = (); 1822 5         8 my @null1 = (); 1823 5         9 my @null2 = (); 1824 5         8 my @null3 = (); 1825 5         10 $null1[$self->nofCol-1]=undef; 1826 5         11 $null3[$self->nofCol-1]=undef; 1827 5 50       17 if ($#cols4>=0) { $null2[$#cols4]=undef; }   5         12   1828 5         26 foreach $key (keys %H) { 1829 45         88 my ($rows1, $rows2) = @{$H{$key}};   45         107   1830 45         101 my $nr1 = scalar @$rows1; 1831 45         54 my $nr2 = scalar @$rows2; 1832 45 100 100     95 next if ($nr1 == 0 && ($type == 0 || $type == 1));       100         1833 41 100 100     101 next if ($nr2 == 0 && ($type == 0 || $type == 2));       100         1834 35 50 66     72 if ($nr2 == 0 && ($type == 1 || $type == 3)) {       66         1835 6         16 for ($i = 0; $i < $nr1; $i++) { 1836 6         12 push @ones, [$self->row($rows1->[$i]), @null2]; 1837             } 1838 6         12 next; 1839             } 1840 29 50 66     60 if ($nr1 == 0 && ($type == 2 || $type == 3)) {       66         1841 4         12 for ($j = 0; $j < $nr2; $j++) { 1842 4         10 my @row2 = $tbl->row($rows2->[$j]); 1843 4         11 for ($k = 0; $k< scalar @$cols1; $k++) { 1844 8         18 $null3[$cols1->[$k]] = $row2[$cols2->[$k]]; 1845             } 1846 4 50       9 if ($#cols4>=0) { 1847 4         15 push @ones, [@null3, @row2[@cols4]]; 1848             } else { 1849 0         0 push @ones, [@null3]; 1850             } 1851             } 1852 4         8 next; 1853             } 1854 25         54 for ($i = 0; $i < $nr1; $i++) { 1855 25         47 for ($j = 0; $j < $nr2; $j++) { 1856 25         63 my @row2 = $tbl->row($rows2->[$j]); 1857 25         58 push @ones, [$self->row($rows1->[$i]), @row2[@cols4]]; 1858             } 1859             } 1860             } 1861 5 100       21 if ($arg{renameCol}) { 1862 1         5 my %h = (); 1863 1         3 map {$h{$_} = 1} @{$self->{header}};   6         21     1         6   1864 1         7 for (my $i=0; $i<@header2; $i++) { 1865 5         15 my $s = $header2[$i]; 1866 5         9 my $cnt = 2; 1867 5         25 while (exists $h{$s}) { 1868 5         32 $s = $header2[$i] ."_". $cnt++; 1869             } 1870 5         14 $header2[$i] = $s; 1871 5         28 $h{$s} = 1; 1872             } 1873             } 1874 5         8 my $header = [@{$self->{header}}, @header2];   5         27   1875 5         19 return new Data::Table(\@ones, $header, 0); 1876             } 1877               1878             sub melt { 1879 1     1 1 3 my ($self, $keyCols, $variableCols, $arg_ref) = @_; 1880 1 50 33     9 confess "key columns have to be specified!" unless defined($keyCols) && ref($keyCols) eq "ARRAY"; 1881 1         2 my $variableColName = 'variable'; 1882 1         3 my $valueColName = 'value'; 1883 1         2 my $skip_NULL = 1; 1884 1         3 my $skip_empty = 0; 1885 1 50 33     4 $variableColName = $arg_ref->{'variableColName'} if (defined($arg_ref) && defined($arg_ref->{'variableColName'})); 1886 1 50 33     5 $valueColName = $arg_ref->{'valueColName'} if (defined($arg_ref) && defined($arg_ref->{'valueColName'})); 1887 1 50 33     4 $skip_NULL = $arg_ref->{'skip_NULL'} if (defined($arg_ref) && defined($arg_ref->{'skip_NULL'})); 1888 1 50 33     3 $skip_empty= $arg_ref->{'skip_empty'} if (defined($arg_ref) && defined($arg_ref->{'skip_empty'})); 1889 1         3 my @X = (); 1890 1         2 my %X = (); 1891 1         2 foreach my $x (@$keyCols) { 1892 2         5 my $x_idx = $self->checkOldCol($x); 1893 2 50       5 confess "Unknown column ". $x unless defined($x_idx); 1894 2         5 push @X, $x_idx; 1895 2         10 $X{$x_idx} = 1; 1896             } 1897 1         3 my @Y = (); 1898 1         2 my %Y = (); 1899 1 50       4 unless (defined($variableCols)) { 1900 1         3 $variableCols = []; 1901 1         3 foreach my $x (0 .. $self->nofCol-1) { 1902 4 100       10 next if $X{$x}; 1903 2         5 push @$variableCols, $x; 1904             } 1905             } 1906 1 50       4 unless (scalar @$variableCols) { 1907 0         0 confess "Variable columns have to be specified!"; 1908             } 1909 1         3 foreach my $y (@$variableCols) { 1910 2         8 my $y_idx = $self->checkOldCol($y); 1911 2 50       6 confess "Unknown column ". $y unless defined($y_idx); 1912 2         4 push @Y, $y_idx; 1913 2         6 $Y{$y_idx} = 1; 1914             } 1915               1916 1         2 my @newHeader = (); 1917 1         3 my @header = $self->header; 1918 1         4 for (my $i=0; $i<= $#X; $i++) { 1919 2         33 push @newHeader, $header[$X[$i]]; 1920             } 1921 1         4 push @newHeader, $variableColName; 1922 1         2 push @newHeader, $valueColName; 1923 1         3 my @newRows = (); 1924 1         4 for (my $i=0; $i<$self->nofRow; $i++) { 1925 4         10 my $row = $self->rowRef($i); 1926 4         11 my @key = @$row[@X]; 1927 4         9 foreach my $y (@Y) { 1928 8 50 33     22 next if (!defined($row->[$y]) && $skip_NULL); 1929 8 50 33     21 next if ($row->[$y] eq '' && $skip_empty); 1930 8         19 my @one = @key; 1931 8         21 push @one, $header[$y], $row->[$y]; 1932 8         29 push @newRows, \@one; 1933             } 1934             } 1935 1         7 return new Data::Table(\@newRows, \@newHeader, 0); 1936             } 1937               1938             sub cast { 1939 3     3 1 15 my ($self, $colsToGroupBy, $colToSplit, $colToSplitIsStringOrNumeric, $colToCalculate, $funToApply) = @_; 1940             #$colToSplit = 'variable' unless defined $colToSplit; 1941             #$colToCalculate = 'value' unless defined $colToCalculate; 1942 3 100       16 $colsToGroupBy = [] unless defined $colsToGroupBy; 1943 3         9 my $tmpColName = '_calcColumn'; 1944 3         9 my $cnt = 2; 1945 3         9 my $s = $tmpColName; 1946 3         21 while ($self->hasCol($s)) { 1947 0         0 $s = $tmpColName."_".$cnt++; 1948             } 1949 3         8 $tmpColName = $s; 1950 3         8 my %grpBy = (); 1951 3         8 map {$grpBy{$_} = 1} @$colsToGroupBy;   2         9   1952 3         9 my @grpBy = @$colsToGroupBy; 1953 3 50 66     16 confess "colToSplit cannot be contained in the list of colsToGroupBy!" if defined $colToSplit and $grpBy{$colToSplit}; 1954 3 100       11 push @grpBy, $colToSplit if defined $colToSplit; 1955 3         20 my $t = $self->group(\@grpBy, [$colToCalculate], [$funToApply], [$tmpColName], 0); 1956 3         23 $t = $t->pivot($colToSplit, $colToSplitIsStringOrNumeric, $tmpColName, $colsToGroupBy); 1957 3         36 return $t; 1958             } 1959               1960             sub each_group { 1961 1     1 1 13 my ($self, $colsToGroupBy, $funToApply) = @_; 1962 1 50       8 $colsToGroupBy = [] unless defined $colsToGroupBy; 1963 1 50 33     18 confess "colsToGroupBy has to be specified!" unless defined($colsToGroupBy) && ref($colsToGroupBy) eq "ARRAY"; 1964 1 50       5 confess "funToApply has to be a reference to CODE!" unless ref($funToApply) eq "CODE"; 1965 1 50       3 unless (scalar @$colsToGroupBy) { # all rows are treated as one group 1966 0         0 $funToApply->($self->clone, 0 .. $self->nofRow - 1); 1967 0         0 return; 1968             } 1969 1         2 my @X = (); 1970 1         3 my %grpBy = (); 1971 1         3 foreach my $x (@$colsToGroupBy) { 1972 1         4 my $x_idx = $self->checkOldCol($x); 1973 1 50       4 confess "Unknown column ". $x unless defined($x_idx); 1974 1         2 push @X, $x_idx; 1975 1         4 $grpBy{$x_idx} = 1; 1976             } 1977 1         3 my %X = (); 1978 1         4 for (my $i=0; $i<$self->nofRow; $i++) { 1979 4         8 my $myRow = $self->rowRef($i); 1980             #my @val = (); 1981             #foreach my $x (@X) { 1982             # push @val, defined($myRow->[$x])?$myRow->[$x]:""; 1983             #} 1984 4         8 my @val = map {tsvEscape($_)} @{$myRow}[@X];   4         8     4         7   1985 4         10 my $myKey = CORE::join("\t", @val); 1986 4         5 push @{$X{$myKey}}, $i;   4         11   1987             } 1988 1         9 foreach my $myKey ( sort {$a cmp $b} keys %X) {   1         6   1989 2         7 $funToApply->($self->subTable($X{$myKey}, undef), $X{$myKey}); 1990             } 1991             } 1992               1993             sub group { 1994 5     5 1 32 my ($self, $colsToGroupBy, $colsToCalculate, $funsToApply, $newColNames, $keepRestCols) = @_; 1995 5 100       18 $keepRestCols = 1 unless defined($keepRestCols); 1996 5 50       47 $colsToGroupBy = [] unless defined $colsToGroupBy; 1997 5 50 33     36 confess "colsToGroupBy has to be specified!" unless defined($colsToGroupBy) && ref($colsToGroupBy) eq "ARRAY"; 1998 5         15 my @X = (); 1999 5         9 my %grpBy = (); 2000 5         14 foreach my $x (@$colsToGroupBy) { 2001 5         15 my $x_idx = $self->checkOldCol($x); 2002 5 50       17 confess "Unknown column ". $x unless defined($x_idx); 2003 5         11 push @X, $x_idx; 2004 5         19 $grpBy{$x_idx} = 1; 2005             } 2006 5         13 my @Y = (); 2007 5         11 my %Y= (); 2008 5 50       14 if (defined($colsToCalculate)) { 2009 5         13 foreach my $y (@$colsToCalculate) { 2010 7         20 my $y_idx = $self->checkOldCol($y); 2011 7 50       22 confess "Unknown column ". $y unless defined($y_idx); 2012 7         12 push @Y, $y_idx; 2013 7         21 $Y{$y_idx} = 1; 2014             } 2015             } 2016 5 50       18 if (scalar @Y) { 2017 5 50 33     32 confess "The size of colsToCalculate, funcsToApply and newColNames should be the same!\n" 2018             unless (scalar @Y == scalar @$funsToApply && scalar @Y == scalar @$newColNames); 2019             } 2020               2021 5         11 my @header = (); 2022 5         11 my @X_name = (); 2023 5         12 my $cnt = 0; 2024 5         8 my $i; 2025 5         20 for ($i=0; $i<$self->nofCol; $i++) { 2026 20 100 66     85 if ($grpBy{$i} || ($keepRestCols && !defined($Y{$i}))) {       66         2027 5         23 push @X_name, $i; 2028 5         17 push @header, $self->{header}->[$i]; 2029 5         20 $cnt += 1; 2030             } 2031             } 2032 5 50       19 if (defined($newColNames)) { 2033 5         14 foreach my $y (@$newColNames) { 2034 7         14 push @header, $y; 2035 7         15 $cnt += 1; 2036             } 2037             } 2038 5         12 my @ones = (); 2039 5         12 my %X = (); 2040 5         9 my %val = (); 2041 5         12 my %rowIdx = (); 2042 5         12 my $idx = 0; 2043 5         19 for ($i=0; $i<$self->nofRow; $i++) { 2044 38         69 my @row = (); 2045 38         91 my $myRow = $self->rowRef($i); 2046 38         72 my $myKey = '(all)'; 2047 38 100       94 if (@X) { 2048             # if colsToGroupBy is not specified, all rows has myKey = '(all)', therefore treated as one group 2049 23         41 my @val = map {tsvEscape($_)} @{$myRow}[@X];   38         91     23         69   2050             #foreach my $x (@X) { 2051             # push @val, defined($myRow->[$x])?$myRow->[$x]:""; 2052             #} 2053 23         107 $myKey = CORE::join("\t", @val); 2054             } 2055 38 50       88 if (scalar @Y) { 2056 38         61 my %Y = (); 2057 38         78 foreach my $y (@Y) { 2058 52 50       116 next if defined($Y{$y}); 2059 52         91 $Y{$y} = 1; 2060 52 100       121 if (defined($val{$y}->{$myKey})) { 2061 35         46 push @{$val{$y}->{$myKey}}, $myRow->[$y];   35         151   2062             } else { 2063 17         76 $val{$y}->{$myKey} = [$myRow->[$y]]; 2064             } 2065             } 2066             } 2067 38 100       151 next if defined($X{$myKey}); 2068 12         23 $X{$myKey} = 1; 2069 12         37 foreach my $j (@X_name) { 2070 18         48 push @row, $myRow->[$j]; 2071             } 2072 12 50       42 $row[$cnt-1] = undef if (scalar @row < $cnt); 2073 12         33 push @ones, \@row; 2074 12         57 $rowIdx{$myKey} = $idx++; 2075             } 2076               2077 5 50       18 if (scalar @Y) { 2078 5         10 $cnt -= scalar @Y; 2079 5         19 for($i=0; $i 2080 7         32 foreach my $s (keys %X) { 2081 17 50       176 if (ref($funsToApply->[$i]) eq "CODE") { 2082 17         28 $ones[$rowIdx{$s}]->[$cnt+$i] = $funsToApply->[$i]->(@{$val{$Y[$i]}->{$s}});   17         88   2083             } else { 2084 0         0 $ones[$rowIdx{$s}]->[$cnt+$i] = scalar @{$val{$Y[$i]}->{$s}};   0         0   2085             #confess "The ${i}th element in the function array is not a valid reference!\n"; 2086             } 2087             } 2088             } 2089             } 2090               2091 5         141 return new Data::Table(\@ones, \@header, 0); 2092             } 2093               2094             sub pivot { 2095 4     4 1 22 my ($self, $colToSplit, $colToSplitIsStringOrNumeric, $colToFill, $colsToGroupBy, $keepRestCols) = @_; 2096 4 50       20 $keepRestCols = 0 unless defined($keepRestCols); 2097 4 50       15 $colToSplitIsStringOrNumeric = 0 unless defined($colToSplitIsStringOrNumeric); 2098 4 50       16 $colsToGroupBy = [] unless defined $colsToGroupBy; 2099 4         10 my $y = undef; 2100 4 100       20 $y = $self->checkOldCol($colToSplit) if defined $colToSplit; 2101 4 100       21 my $y_name = defined($y)?$self->{header}->[$y]:undef; 2102 4 50 66     26 confess "Unknown column ". $colToSplit if (!defined($y) && defined($colToSplit)); 2103 4         12 my $z = undef; 2104 4 50       21 $z = $self->checkOldCol($colToFill) if defined($colToFill); 2105 4 50       22 my $z_name = defined($z)?$self->{header}->[$z]:undef; 2106 4 50 33     25 confess "Unknown column ". $colToFill if (!defined($z) && defined($colToFill)); 2107             #confess "Cannot take colToFill, if colToSplit is 'undef'" if (defined($z) && !defined($y)); 2108 4         15 my @X = (); 2109 4 50       17 if (defined($colsToGroupBy)) { 2110 4         12 foreach my $x (@$colsToGroupBy) { 2111 3         15 my $x_idx = $self->checkOldCol($x); 2112 3 50       22 confess "Unknown column ". $x unless defined($x_idx); 2113 3         19 push @X, $self->{header}->[$x_idx]; 2114             } 2115             } 2116 4         14 my (@Y, %Y); 2117               2118 4 100       16 if (defined($colToSplit)) { 2119 2         12 @Y = $self->col($y); 2120 2         7 %Y = (); 2121 2         7 foreach my $val (@Y) { 2122 8 50       22 $val = "NULL" unless defined($val); 2123 8         20 $Y{$val} = 1; 2124             } 2125             } else { 2126 2         12 @Y = ('(all)') x $self->nofCol; 2127 2         11 %Y = ('(all)' => 1); 2128 2         15 $colToSplitIsStringOrNumeric = 1; 2129             } 2130 4 50       17 if ($colToSplitIsStringOrNumeric == 0) { 2131 0         0 foreach my $y (keys %Y) { 2132 0 0       0 if ($y =~ /\D/) { 2133 0         0 $colToSplitIsStringOrNumeric = 1; 2134 0         0 last; 2135             } 2136             } 2137             } 2138 4 50       22 if ($colToSplitIsStringOrNumeric) { 2139 4         44 @Y = sort { $a cmp $b } (keys %Y);   2         17   2140             } else { 2141 0         0 @Y = sort { $a <=> $b } (keys %Y);   0         0   2142             } 2143               2144 4         13 my @header = (); 2145 4         12 my $i; 2146 4         10 my @X_name = (); 2147               2148 4 50       17 if (!$keepRestCols) { 2149 4         13 foreach my $x (@X) { 2150 3         20 push @X_name, $x; 2151             } 2152             } else { 2153 0         0 for ($i=0; $i<$self->nofCol; $i++) { 2154 0 0 0     0 next if ((defined($y) && $i==$y) || (defined($z) && $i==$z));       0               0         2155 0         0 push @X_name, $self->{header}->[$i]; 2156             } 2157             } 2158 4         12 my $cnt = 0; 2159 4         23 for ($i=0; $i < @X_name; $i++) { 2160 3         10 my $s = $X_name[$i]; 2161 3         15 while (defined($Y{$s})) { 2162 0         0 $s = "_".$s; 2163             } 2164 3         9 push @header, $s; 2165 3         18 $Y{$s} = $cnt++; 2166             } 2167               2168             #if (defined($y)) { 2169 4         18 foreach my $val (@Y) { 2170 6 50       32 push @header, ($colToSplitIsStringOrNumeric?"":"$y_name=") . $val; 2171 6         23 $Y{$val} = $cnt++; 2172             } 2173             #} 2174               2175 4         13 my @ones = (); 2176 4         12 my %X = (); 2177 4         12 my $rowIdx = 0; 2178 4         22 for ($i=0; $i<$self->nofRow; $i++) { 2179 11         30 my @row = (); 2180 11         107 my $myRow = $self->rowHashRef($i); 2181 11         29 my $myKey = '(all)'; # set to '' to work with total agreegation (group all rows into one) 2182 11 100       34 if (scalar @X) { 2183 10         22 my @val = (); 2184 10         22 foreach my $x (@X) { 2185 10         35 push @val, tsvEscape($myRow->{$x}); 2186             } 2187 10         35 $myKey = CORE::join("\t", @val); 2188             } 2189 11 100       35 unless (defined($X{$myKey})) { 2190 7         21 foreach my $s (@X_name) { 2191 6         23 push @row, $myRow->{$s}; 2192             } 2193 7         30 for (my $j = scalar @row; $j<$cnt; $j++) { 2194 11         38 $row[$j] = undef; 2195             } 2196             #$row[$cnt-1] = undef if (scalar @row < $cnt); 2197             } 2198             #if (defined($y)) { 2199 11 100       35 my $val = defined($y) ? $myRow->{$y_name} : "(all)"; 2200 11 50       26 $val = "NULL" unless defined($val); 2201 11 100       39 if (!defined($X{$myKey})) { 2202 7 50       28 $row[$Y{$val}] = defined($z)?$myRow->{$z_name}: $row[$Y{$val}]+1; 2203             } else { 2204 4 50       20 $ones[$X{$myKey}][$Y{$val}] = defined($z)?$myRow->{$z_name}: $ones[$X{$myKey}][$Y{$val}]+1; 2205             } 2206             #} 2207 11 100       40 unless (defined($X{$myKey})) { 2208 7         26 push @ones, \@row; 2209 7         47 $X{$myKey} = $rowIdx++; 2210             } 2211             } 2212 4         24 return new Data::Table(\@ones, \@header, 0); 2213             } 2214               2215             sub fromFileGuessOS { 2216 9     9 0 129 my ($name, $arg_ref) = @_; 2217 9         38 my @OS=("\n", "\r\n", "\r"); 2218             # operatoring system: 0 for UNIX (\n as linebreak), 1 for Windows 2219             # (\r\n as linebreak), 2 for MAC (\r as linebreak) 2220 9         23 my $qualifier = ''; 2221 9         26 my $encoding = $Data::Table::DEFAULTS{ENCODING}; 2222 9 50 66     64 $qualifier = $arg_ref->{qualifier} if (defined($arg_ref) && exists $arg_ref->{qualifier}); 2223 9 50 66     63 $encoding = $arg_ref->{encoding} if (defined($arg_ref) && exists $arg_ref->{encoding}); 2224 9         28 my ($len, $os)=(-1, -1); 2225 9         32 my $SRC=openFileWithEncoding($name, $encoding); 2226             #local($/)="\n"; 2227 9         35 my $s = getOneLine($SRC, "\n", $qualifier); #<$SRC>; 2228 9         167 close($SRC); 2229             #$s =~ s/\n$//; 2230             #my $myLen=length($s); 2231             #$s =~ s/\r$//; 2232 9 100       113 if ($s =~ /\r\n$/) {     100               50           2233 2         15 return 1; 2234             } elsif ($s =~ /\n$/) { 2235 5         44 return 0; 2236             } elsif ($s =~ /\r/) { 2237 2         17 return 2; 2238             } 2239 0         0 return 0; 2240             #if (length($s) == $myLen) { 2241             # return 0; 2242             #} elsif (length($s) == $myLen - 1) { 2243             # return 1; 2244             #} else { 2245             # return 2; 2246             #} 2247             # for (my $i=0; $i<@OS; $i++) { 2248             # open($SRC, $name) or confess "Cannot open $name to read"; 2249             # binmode $SRC; 2250             # local($/)=$OS[$i]; 2251             # my $s = <$SRC>; 2252             # #print ">> $i => ". (length($s)-length($OS[$i]))."\n"; 2253             # my $myLen=length($s)-length($OS[$i]); 2254             # if ($len<0 || ($myLen>0 && $myLen<$len)) { 2255             # $len=length($s)-length($OS[$i]); 2256             # $os=$i; 2257             # } 2258             # close($SRC); 2259             # } 2260             # # find the OS linebreak that gives the shortest first line 2261             # return $os; 2262             } 2263               2264             sub openFileWithEncoding { 2265 38     38 0 123 my ($name_or_handler, $encoding) = @_; 2266 38         103 my $isFileHandler=ref($name_or_handler) ne ""; 2267 38         72 my $SRC; 2268 38 100       103 if ($isFileHandler) { 2269 3         8 $SRC = $name_or_handler; # a file handler 2270             } else { 2271 35 50       1164 open($SRC, $name_or_handler) or confess "Cannot open $name_or_handler to read"; 2272             } 2273             # check if Perl version is recent enough to support encoding 2274 38 50 33     881 $encoding ='' if (!$^V or $^V lt v5.8.1); 2275 38 100       204 if ($encoding) { 2276 36 50       291 $encoding='UTF-8' if ($encoding =~ /^utf-?8$/i); 2277 2     2   14 binmode($SRC, ":encoding($encoding)");   2         5     2         16     36         536   2278             } else { 2279 2         9 binmode $SRC; 2280             } 2281 38         26613 return $SRC; 2282             } 2283               2284             sub fromFileGetTopLines { 2285 7     7 0 26 my ($name, $os, $numLines, $arg_ref) = @_; 2286 7 50       26 $os = fromFileGuessOS($name) unless defined($os); 2287 7 50       22 $numLines = 2 unless defined($numLines); 2288 7         23 my @OS=("\n", "\r\n", "\r"); 2289             # operatoring system: 0 for UNIX (\n as linebreak), 1 for Windows 2290             # (\r\n as linebreak), 2 for MAC (\r as linebreak) 2291 7         21 my $encoding = $Data::Table::DEFAULTS{ENCODING}; 2292 7 50 33     44 $encoding = $arg_ref->{encoding} if (defined($arg_ref) && exists $arg_ref->{encoding}); 2293 7         18 my @lines=(); 2294 7         19 my $SRC = openFileWithEncoding($name, $encoding); 2295 7         38 local($/)=$OS[$os]; 2296 7         25 my $n_endl = length($OS[$os]); 2297 7         16 my $cnt=0; 2298 7         104 while(my $line = <$SRC>) { 2299 14         92 $cnt++; 2300 14         41 for (1..$n_endl) { chop($line); }   18         50   2301 14         36 push @lines, $line; 2302 14 100 66     84 last if ($numLines>0 && $cnt>=$numLines); 2303             } 2304 7         64 close($SRC); 2305 7         60 return @lines; 2306             } 2307               2308             sub fromFileIsHeader { 2309 7     7 0 23 my ($s, $delimiter, $allowNumericHeader) = @_; 2310 7 50       46 $delimiter=$Data::Table::DEFAULTS{'CSV_DELIMITER'} unless defined($delimiter); 2311 7 50 33     122 return 0 if (!defined($s) || $s eq "" || $s=~ /$delimiter$/);       33         2312 7         39 my $fields=parseCSV($s, 0, {delimiter=>$delimiter}); 2313 7         28 my $allNumbers = 1; 2314 7         21 foreach my $name (@$fields) { 2315 20 50       54 return 0 unless $name; 2316             #next if $name=~/[^0-9.eE\-+]/; 2317 20 100 66     112 return 0 if $name=~/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/ && !$allowNumericHeader; 2318             # modified, so that we allow some columns to be numeric, but not all columns 2319 19 50       84 $allNumbers = 0 unless $name =~ /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/; 2320             } 2321             #return 0 if $allNumbers; 2322 6         27 return 1; 2323             } 2324               2325             sub fromFileGuessDelimiter { 2326 7     7 0 15 my $s_line= shift; 2327 7         25 my @DELIMITER=(",","\t",":"); 2328 7         15 my $numCol=-1; my $i=-1;   7         15   2329 7 50       22 return $Data::Table::DEFAULTS{CSV_DELIMITER} unless @$s_line; 2330 7         30 for (my $d=0; $d<@DELIMITER; $d++) { 2331 21         39 my $colFound=-1; 2332 21         46 foreach my $line (@$s_line) { 2333 42 50       99 unless (defined($line)) { 2334 0         0 return $Data::Table::DEFAULTS{CSV_DELIMITER}; 2335             } else { 2336 42         157 my $header = parseCSV($line, 0, {delimiter=>$DELIMITER[$d]}); 2337 42 100       204 if ($colFound<0) {     50           2338 21         69 $colFound = scalar @$header; 2339             } elsif ($colFound != scalar @$header) { 2340 0         0 $colFound = -1; 2341 0         0 last; 2342             } 2343             } 2344             } 2345 21 50       57 next if $colFound<0; 2346 21 100       71 if ($colFound>$numCol) { 2347 8         19 $numCol=$colFound; $i=$d;   8         27   2348             } 2349             } 2350 7 50       37 return ($i<0)?$Data::Table::DEFAULTS{CSV_DELIMITER}:$DELIMITER[$i]; 2351             } 2352               2353             sub fromFile { 2354 7     7 1 45 my ($name, $arg_ref) = @_; 2355 7         20 my $linesChecked = 2; 2356 7         19 my $os = undef; 2357 7         15 my $hasHeader = undef; 2358 7         16 my $delimiter = undef; 2359 7         16 my $format = undef; 2360 7         27 my $qualifier = $Data::Table::DEFAULTS{CSV_QUALIFIER}; 2361 7         14 my $allowNumericHeader = 0; 2362 7         21 my $encoding=$Data::Table::DEFAULTS{ENCODING}; 2363               2364 7 100       32 if (defined($arg_ref)) { 2365 1 50       6 $linesChecked = $arg_ref->{'linesChecked'} if defined($arg_ref->{'linesChecked'}); 2366 1         4 $os = $arg_ref->{'OS'}; 2367 1         4 $hasHeader = $arg_ref->{'has_header'}; 2368 1         4 $delimiter = $arg_ref->{'delimiter'}; 2369 1         9 $format = $arg_ref->{'format'}; 2370 1 50       5 $qualifier = $arg_ref->{'qualifier'} if defined($arg_ref->{'qualifier'}); 2371 1         4 $allowNumericHeader = $arg_ref->{'allowNumericHeader'}; 2372 1         4 $encoding = $arg_ref->{'encoding'}; 2373             } 2374               2375 7 50 33     28 $qualifier = '' if ($format and uc($format) eq 'TSV'); 2376 7 50       25 unless (defined($os)) { 2377 7         43 $os = fromFileGuessOS($name, {qualifier=>$qualifier, encoding=>$encoding}); 2378 7         38 $arg_ref->{'OS'}=$os; 2379             } 2380 7         36 my @S = fromFileGetTopLines($name, $os, $linesChecked, {encoding=>$encoding}); 2381 7 50       34 return undef unless scalar @S; 2382 7 50       22 unless (defined($delimiter)) { 2383 7         27 $delimiter = fromFileGuessDelimiter(\@S); 2384 7         24 $arg_ref->{'delimiter'} = $delimiter; 2385             } 2386 7 50       24 unless (defined($hasHeader)) { 2387 7         31 $hasHeader = fromFileIsHeader($S[0], $delimiter, $allowNumericHeader); 2388             } 2389 7         21 my $t = undef; 2390             #print ">>>". join("\n", @S)."\n"; 2391             #print "OS=$os, hasHeader=$hasHeader, delimiter=$delimiter\n"; 2392 7 100       26 if ($delimiter eq "\t") { 2393 1         7 $t=fromTSV($name, $hasHeader, undef, $arg_ref); 2394             } else { 2395 6         27 $t=fromCSV($name, $hasHeader, undef, $arg_ref); 2396             } 2397 7         71 return $t; 2398             } 2399               2400             ## interface to GD::Graph 2401             # use GD::Graph::points; 2402             # $graph = GD::Graph::points->new(400, 300); 2403             # $graph->plot([$t->colRef(1), $t->colRef(2)]); 2404             2405             1; 2406               2407             __END__