File Coverage

blib/lib/Fsdb/Filter/dbcolsplittorows.pm
Criterion Covered Total %
statement 18 61 29.5
branch 0 24 0.0
condition n/a
subroutine 6 15 40.0
pod 5 5 100.0
total 29 105 27.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             #
4             # dbcolsplittorows.pm
5             # Copyright (C) 1991-2008 by John Heidemann
6             # $Id: 56d2923e0064a9026e77232e84b7f8a1c48e1947 $
7             #
8             # This program is distributed under terms of the GNU general
9             # public license, version 2. See the file COPYING
10             # in $dblibdir for details.
11             #
12              
13             package Fsdb::Filter::dbcolsplittorows;
14              
15             =head1 NAME
16              
17             dbcolsplittorows - split an existing column into multiple new rows
18              
19             =head1 SYNOPSIS
20              
21             dbcolsplittorows [-C ElementSeperator] [-e null] [-E] [-N enumerated-name] column [column...]
22              
23             =head1 DESCRIPTION
24              
25             Split column into pieces, outputting one row for each piece.
26              
27             By default, any empty fields are ignored.
28             If an empty field value is given with -e, then they produce output.
29              
30             When a null value is given, empty fields at the beginning and end of
31             lines are suppressed (like perl split). Unlike perl, if ALL fields
32             are empty, we generate one (and not zero) empty fields.
33              
34              
35             =head1 OPTIONS
36              
37             =over 4
38              
39             =item B<-C S> or B<--element-separator S>
40              
41             Specify the separator used to split columns.
42             (Defaults to a single underscore.)
43              
44             =item B<-E> or B<--enumerate>
45              
46             Enumerate output columns: rather than assuming the column name uses
47             the element separator, we keep it whole and fill in with indexes
48             starting from 0.
49              
50             =item B<-N> or B<--new-name> N
51              
52             Name the new column N for enumeration.
53             Defaults to C.
54              
55             =item B<-e E> or B<--empty E>
56              
57             give value E as the value for empty (null) records
58              
59             =back
60              
61             =for comment
62             begin_standard_fsdb_options
63              
64             This module also supports the standard fsdb options:
65              
66             =over 4
67              
68             =item B<-d>
69              
70             Enable debugging output.
71              
72             =item B<-i> or B<--input> InputSource
73              
74             Read from InputSource, typically a file name, or C<-> for standard input,
75             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
76              
77             =item B<-o> or B<--output> OutputDestination
78              
79             Write to OutputDestination, typically a file name, or C<-> for standard output,
80             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
81              
82             =item B<--autorun> or B<--noautorun>
83              
84             By default, programs process automatically,
85             but Fsdb::Filter objects in Perl do not run until you invoke
86             the run() method.
87             The C<--(no)autorun> option controls that behavior within Perl.
88              
89             =item B<--help>
90              
91             Show help.
92              
93             =item B<--man>
94              
95             Show full manual.
96              
97             =back
98              
99             =for comment
100             end_standard_fsdb_options
101              
102              
103             =head1 SAMPLE USAGE
104              
105             =head2 Input:
106              
107             #fsdb name uid
108             John_Heidemann 2274
109             Greg_Johnson 2275
110             Root 0
111             # this is a simple database
112             # | dbcol fullname uid
113             # | dbcolrename fullname name
114              
115             =head2 Command:
116              
117             cat data.fsdb | dbcolsplittorows name
118              
119             =head2 Output:
120              
121             #fsdb name uid
122             John 2274
123             Heidemann 2274
124             Greg 2275
125             Johnson 2275
126             Root 0
127             # this is a simple database
128             # | dbcol fullname uid
129             # | dbcolrename fullname name
130             # | dbcolsplittorows name
131              
132             =head1 SEE ALSO
133              
134             L.
135             L.
136             L.
137             L.
138              
139              
140             =head1 CLASS FUNCTIONS
141              
142             =cut
143              
144             @ISA = qw(Fsdb::Filter);
145             $VERSION = 2.0;
146              
147 1     1   5396 use strict;
  1         2  
  1         38  
148 1     1   4 use Pod::Usage;
  1         2  
  1         134  
149 1     1   6 use Carp;
  1         1  
  1         61  
150              
151 1     1   4 use Fsdb::Filter;
  1         2  
  1         39  
152 1     1   5 use Fsdb::IO::Reader;
  1         1  
  1         26  
153 1     1   4 use Fsdb::IO::Writer;
  1         2  
  1         752  
154              
155              
156             =head2 new
157              
158             $filter = new Fsdb::Filter::dbcolsplittocols(@arguments);
159              
160             Create a new dbcolsplittocols object, taking command-line arguments.
161              
162             =cut
163              
164             sub new ($@) {
165 0     0 1   my $class = shift @_;
166 0           my $self = $class->SUPER::new(@_);
167 0           bless $self, $class;
168 0           $self->set_defaults;
169 0           $self->parse_options(@_);
170 0           $self->SUPER::post_new();
171 0           return $self;
172             }
173              
174              
175             =head2 set_defaults
176              
177             $filter->set_defaults();
178              
179             Internal: set up defaults.
180              
181             =cut
182              
183             sub set_defaults ($) {
184 0     0 1   my($self) = @_;
185 0           $self->SUPER::set_defaults();
186 0           $self->{_elem_separator} = '_';
187 0           $self->{_enumerate} = undef;
188 0           $self->{_destination_column} = 'count';
189 0           $self->{_target_column} = undef;
190 0           $self->{_empty} = undef;
191             }
192              
193             =head2 parse_options
194              
195             $filter->parse_options(@ARGV);
196              
197             Internal: parse command-line arguments.
198              
199             =cut
200              
201             sub parse_options ($@) {
202 0     0 1   my $self = shift @_;
203              
204 0           my(@argv) = @_;
205             $self->get_options(
206             \@argv,
207 0     0     'help|?' => sub { pod2usage(1); },
208 0     0     'man' => sub { pod2usage(-verbose => 2); },
209             'autorun!' => \$self->{_autorun},
210             'close!' => \$self->{_close},
211             'C|element-separator=s' => \$self->{_elem_separator},
212             'd|debug+' => \$self->{_debug},
213             'e|empty=s' => \$self->{_empty},
214             'E|enumerate!' => \$self->{_enumerate},
215 0     0     'i|input=s' => sub { $self->parse_io_option('input', @_); },
216             'log!' => \$self->{_logprog},
217             'N|new-name=s' => \$self->{_destination_column},
218 0     0     'o|output=s' => sub { $self->parse_io_option('output', @_); },
219 0 0         ) or pod2usage(2);
220 0           $self->parse_target_column(\@argv);
221             }
222              
223             =head2 setup
224              
225             $filter->setup();
226              
227             Internal: setup, parse headers.
228              
229             =cut
230              
231             sub setup ($) {
232 0     0 1   my($self) = @_;
233              
234 0           $self->finish_io_option('input', -comment_handler => $self->create_pass_comments_sub);
235              
236 0 0         pod2usage(2) if (!defined($self->{_target_column}));
237              
238 0           $self->{_target_coli} = $self->{_in}->col_to_i($self->{_target_column});
239             croak $self->{_prog} . ": target column " . $self->{_target_column} . " is not in input stream.\n"
240 0 0         if (!defined($self->{_target_coli}));
241              
242             # Sanity check user's input to avoid injection attacks.
243             croak $self->{_prog} . ": bad element separator.\n"
244 0 0         if ($self->{_element_separator} =~ /\'/);
245             croak $self->{_prog} . ": bad empty value.\n"
246 0 0         if ($self->{_empty} =~ /\'/);
247              
248 0           $self->finish_io_option('output', -clone => $self->{_in}, -outputheader => 'delay');
249 0 0         if ($self->{_enumerate}) {
250             $self->{_out}->col_create($self->{_destination_column})
251 0 0         or croak $self->{_prog} . ": cannot create column '" . $self->{_destination_column} . "' (maybe it already existed?)\n";
252             };
253             }
254              
255             =head2 run
256              
257             $filter->run();
258              
259             Internal: run over each rows.
260              
261             =cut
262             sub run ($) {
263 0     0 1   my($self) = @_;
264              
265 0           my $read_fastpath_sub = $self->{_in}->fastpath_sub();
266 0           my $write_fastpath_sub = $self->{_out}->fastpath_sub();
267              
268 0           my $empty = $self->{_empty};
269 0           my $enum_coli = undef;
270 0 0         if ($self->{_enumerate}) {
271 0           $enum_coli = $self->{_out}->col_to_i($self->{_destination_column});
272 0 0         croak $self->{_prog} . ": enumeration column " . $self->{_destion_column} . " doesn't exist, even though we created it.\n"
273             if (!defined($enum_coli));
274             };
275              
276              
277             my($loop) = q'{
278             my $fref;
279             while ($fref = &$read_fastpath_sub()) {
280             my @p = split(/' . quotemeta($self->{_elem_separator}) . '/, $fref->[' . $self->{_target_coli} . ']);
281             push(@p, undef) if ($#p == -1);
282             my($i) = 0;
283             foreach (@p) {
284             if (!defined($_) || $_ eq "") {
285             ' . (!defined($empty) ? "next;\n" : '$_ = ' . "'" . $empty . "';" ) . '
286             };
287 0 0         $fref->[' . $self->{_target_coli} . '] = $_;' .
    0          
288             (defined($enum_coli) ? ' $fref->[' . $enum_coli . '] = $i++;' : '') . '
289             &$write_fastpath_sub($fref);
290             };
291             };
292             }';
293 0           eval $loop;
294 0 0         $@ && croak $self->{_prog} . ": interal eval error: $@.\ncode:\n$loop";
295             }
296              
297              
298             =head1 AUTHOR and COPYRIGHT
299              
300             Copyright (C) 1991-2008 by John Heidemann
301              
302             This program is distributed under terms of the GNU general
303             public license, version 2. See the file COPYING
304             with the distribution for details.
305              
306             =cut
307              
308             1;
309