File Coverage

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