File Coverage

blib/lib/Text/NumericData/App/txdfilter.pm
Criterion Covered Total %
statement 103 116 88.7
branch 38 62 61.2
condition 8 18 44.4
subroutine 9 9 100.0
pod 0 7 0.0
total 158 212 74.5


line stmt bran cond sub pod time code
1             package Text::NumericData::App::txdfilter;
2              
3 1     1   423 use Text::NumericData::App;
  1         3  
  1         24  
4              
5 1     1   4 use strict;
  1         2  
  1         906  
6              
7             # This is just a placeholder because of a past build system bug.
8             # The one and only version for Text::NumericData is kept in
9             # the Text::NumericData module itself.
10             our $VERSION = '1';
11             $VERSION = eval $VERSION;
12              
13             #the infostring says it all
14             my $infostring = 'filter textual data files
15              
16             This program filters/transforms textual data files (pipe operation) concering the syntax and header stuff. The data itself is preserved. Any Parameters after options are file title and data column titles (overriding the named parameters).';
17              
18             our @ISA = ('Text::NumericData::App');
19              
20             sub new
21             {
22 1     1 0 80 my $class = shift;
23 1         11 my @pars =
24             (
25             'touchdata',1,'','touch the data lines (otherwise just copy them)',
26             'touchhead',1,'','touch the header (otherwise just copy)',
27             'newhead',0,'n','make completely new header',
28             'comment',[],'C','comment to include between file title and column titles, lines in array',
29             'headlines',undef,'H','use this fixed number of lines as header',
30             'delaftercom',0,'D','delete any comment lines after data',
31             'lhex','','L','regex for last header line (alternative to fdex)',
32             'fdex','','F','regex for first data line (alterative to lhex)',
33             'title',undef,'t','new title',
34             'coltitles',[],'i','new column titles (as in "title1","title2","title3")',
35             'modtitles',{},'m','modify existing titles... hash with column indices as key, in Perl: (1=>"NeSpalte",4=>"AndereSpalte")',
36             'origin',0,'o','create Origin-friendly format with tab separation and coltitles as only header line and NO comment character, triggers also quote=0 and delaftercom=1',
37             'data',1,'','include the data in printout',
38             'head',1,'','include the header in printout',
39             'history',0,'','keep old title(s) lines as historic comments (writing new overall title before, new column titles below), otherwise replace them'
40             );
41              
42 1         14 return $class->SUPER::new
43             ({
44             parconf=>
45             {
46             info=>$infostring
47             # default copyright
48             # default version
49             }
50             ,pardef=>\@pars
51             ,pipemode=>1
52             ,pipe_init=>\&prepare
53             ,pipe_begin=>\&init
54             ,pipe_line=>\&process_line
55             ,pipe_end=>\&endhook
56             });
57             }
58              
59             sub prepare
60             {
61 7     7 0 17 my $self = shift;
62 7         15 my $param = $self->{param};
63              
64 7 100       20 if($param->{origin})
65             {
66 1         3 $param->{comchar} = '';
67 1         2 $param->{outsep} = "\t";
68 1         2 $param->{quote} = 0;
69 1         2 $param->{delaftercom} = 1;
70             }
71              
72             #plain command line parameters are file and column titles
73 7 100       13 $self->{title} = @{$self->{argv}} ? shift(@{$self->{argv}}) : $param->{title};
  7         27  
  2         5  
74 7 100       13 $self->{titles} = @{$self->{argv}} ? $self->{argv} : $param->{coltitles};
  7         29  
75             # precompile header/data matches
76 7 50       23 $self->{lhex} = qr/$param->{lhex}/ if $param->{lhex} ne '';
77 7 50       20 $self->{fdex} = qr/$param->{fdex}/ if $param->{fdex} ne '';
78              
79 7         20 return 0;
80             }
81              
82             sub init
83             {
84 7     7 0 656 my $self = shift;
85 7         42 $self->new_txd();
86             #storage for old header lines
87 7         25 $self->{headlines} = [];
88             #counter/switch
89 7         14 $self->{l} = 0;
90 7         21 $self->{lasthead} = 0;
91             }
92              
93             sub process_line
94             {
95 32     32 0 846 my $self = shift;
96 32         59 my $c = $self->{txd};
97 32         53 my $param = $self->{param};
98 32         47 my $pre = ''; # prepend text just before output
99              
100 32 100       78 if(!$self->{state}{data})
101             {
102             #maybe still in header
103 26 50       91 my $is_data = $self->{lasthead} ? 1 : $c->line_check($_[0]);
104             # Behaviour when both expressions are specified:
105             # The first one that triggers defines beginning of data part.
106             # An idea would be to intentionally skip a part between.
107             # Think about that ...
108 26 50 33     116 if(!$self->{lasthead} and defined $self->{lhex})
109             {
110 0         0 $is_data = 0;
111 0         0 $self->{lasthead} = $_[0] =~ $self->{lhex};
112             }
113 26 50       68 if(defined $self->{fdex})
114             {
115             # possibly overriding line_check
116 0         0 $is_data = $_[0] =~ $self->{fdex};
117             }
118             # End header on specified number of lines or when thinking that data was found.
119 26 100 66     135 if( (!defined $param->{headlines} and $is_data) or (defined $param->{headlines} and ++$self->{l} > $param->{headlines}) )
      33        
      66        
120             {
121             #first data line found
122 4         7 $self->{state}{data} = 1;
123              
124 4         12 $self->header_workout($pre);
125             }
126             else #collect headlines
127             {
128 22         58 my $h = $_[0];
129 22         86 $c->make_naked($h);
130 22         43 push(@{$self->{headlines}},$h);
  22         67  
131 22         65 $_[0] = '';
132             }
133             }
134 32 100       82 if($self->{state}{data})
135             {
136             # skip data section if so desired
137 10 50       19 unless($param->{data})
138             {
139 0         0 $_[0] = '';
140             }
141             else
142             {
143             # data line processing
144             # This logic is not nested right, code duplication has to go.
145 10 0       19 unless($param->{delaftercom})
    50          
146             {
147             # normal handling, repeat everything unless unworthy
148 10 50       38 if($_[0] eq $c->{config}{lineend})
    50          
149             {
150 0 0       0 $_[0] = '' if $param->{noempty};
151             }
152             elsif($param->{touchdata})
153             {
154 10         20 $_[0] = ${$c->data_line($c->line_data($_[0]))};
  10         28  
155             }
156             }
157 0         0 elsif($c->line_check($_,1))
158             {
159 0         0 $_[0] = ${$c->data_line($c->line_data($_[0]))}
160 0 0       0 if($param->{touchdata});
161             }
162 0         0 else{ $_[0] = ''; }
163             }
164             }
165 32 100       129 $_[0] = $pre.$_[0] if $pre ne '';
166             }
167              
168             sub endhook
169             {
170 7     7 0 181 my $self = shift;
171             # If there was no data, still produce a header when it was given via command line.
172 7 100       33 $self->header_workout(@_) unless($self->{state}{data});
173             }
174              
175             # generic helper for applying modtitles (actually, just numeric-key hash to array)
176             sub mod_titles
177             {
178 6     6 0 16 my ($t,$m) = @_;
179 6         13 foreach my $k (keys %{$m})
  6         19  
180             {
181 0 0       0 if($k =~ /^\d+$/)
182             {
183 0         0 $t->[$k-1] = $m->{$k};
184             }
185             }
186             }
187              
188             # construct header and prepend to current line, if demanded
189             sub header_workout
190             {
191 7     7 0 12 my $self = shift;
192 7         13 my $param = $self->{param};
193 7 50       18 return unless $param->{head};
194              
195             #now print header
196 7         13 my $c = $self->{txd};
197              
198 7 100       14 unless($param->{touchhead})
199             {
200 1         6 my $pre = join($c->get_end(), @{$self->{headlines}});
  1         9  
201 1 50       8 $pre .= $c->get_end() if $pre ne '';
202 1         6 $_[0] = $pre.$_[0];
203 1         5 return;
204             }
205              
206 6 100       17 $c->{title} = $self->{title} if defined $self->{title};
207 6 100       8 $c->{titles} = $self->{titles} if @{$self->{titles}};
  6         17  
208 6         21 mod_titles($c->{titles}, $self->{param}{modtitles});
209 6         13 my $pre = '';
210 6 100       16 if($param->{origin})
211             {
212             #mangle it for Origin...
213             #Origin uses "titles" in first two lines as legend text
214             #since normally the same kind of data from different sources
215             #is identified in a plot via the legend, any comment text
216             #provided for the file is repeated here for every column
217             #looks senseless in file, makes sense in Origin
218             #print STDERR "TODO: revisit Origin format ... is this really the best way?\n";
219 1         2 my $titles = $c->{titles};
220 1         2 foreach my $com (@{$param->{comment}})
  1         3  
221             {
222 1         2 my @car = ();
223 1         2 for(my $i = 0; $i <= $#{$titles}; ++$i)
  3         8  
224             {
225 2         4 push(@car, $com);
226             }
227 1         3 $c->{titles} = \@car;
228 1         2 $pre .= ${$c->title_line()};
  1         7  
229             }
230             #the "real" title is still there after any comments
231 1         2 $c->{titles} = $titles;
232 1         3 $pre .= ${$c->title_line()};
  1         3  
233             }
234             else
235             {
236 5 50       12 my $old_comments = $param->{history} ? $self->{headlines} : $c->{comments};
237             #file title
238 5 50       13 if(defined $c->{title}){ $pre .= ${$c->comment_line($c->{title})}; }
  5         7  
  5         22  
239 5         11 foreach my $l (@{$param->{comment}})
  5         17  
240             {
241 1         2 $pre .= ${$c->comment_line($l)};
  1         4  
242             }
243             #old stuff
244 5 50       18 unless($param->{newhead})
245             {
246 5         11 foreach my $h (@{$old_comments})
  5         12  
247             {
248 6         9 $pre .= ${$c->comment_line($h)};
  6         14  
249             }
250             }
251             #only if new one is desired
252             # Is that logic complete?
253 5 0 33     9 $pre .= ${$c->title_line()} if (@{$c->{titles}} or @{$self->{titles}} or (keys %{$param->{modtitles}}));
  5   33     19  
  5         17  
  0         0  
  0         0  
254             }
255 6         22 $_[0] = $pre.$_[0];
256             }
257              
258              
259             1;