File Coverage

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


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