line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Presenter::Combo; |
2
|
|
|
|
|
|
|
$VERSION = 1.03; # 02-10-2008 |
3
|
|
|
|
|
|
|
@ISA = qw(Data::Presenter); |
4
|
4
|
|
|
4
|
|
14391
|
use strict; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
197
|
|
5
|
4
|
|
|
4
|
|
25
|
use warnings; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
168
|
|
6
|
4
|
|
|
4
|
|
24
|
use Carp; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
322
|
|
7
|
4
|
|
|
4
|
|
25
|
use Data::Dumper; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
5928
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub _init { |
10
|
9
|
|
|
9
|
|
21
|
my ($self, $source) = @_; |
11
|
9
|
|
|
|
|
29
|
my @objects = @$source; |
12
|
9
|
100
|
|
|
|
306
|
croak "Not enough sources to create a Combo data source: $!" |
13
|
|
|
|
|
|
|
if scalar(@objects) < 2; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# Designate the first object named as the 'base' |
16
|
8
|
|
|
|
|
22
|
my $base = shift @objects; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Merge the second object into the first and repeat the process as many |
19
|
|
|
|
|
|
|
# times as needed, with the result of the merging as the new base |
20
|
8
|
|
|
|
|
44
|
while (scalar(@objects)) { |
21
|
10
|
|
|
|
|
19
|
my $sec = shift @objects; |
22
|
10
|
|
|
|
|
78
|
$base = $self->_merge_into_base($base, $sec); |
23
|
|
|
|
|
|
|
} |
24
|
6
|
|
|
|
|
27
|
return $base; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
################################################################################ |
28
|
|
|
|
|
|
|
##### &_merge_into_base: Called from within &_init |
29
|
|
|
|
|
|
|
##### Iteratively merges the data structures in the Data::Presenter |
30
|
|
|
|
|
|
|
##### objects passed as arguments |
31
|
|
|
|
|
|
|
################################################################################ |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub _merge_into_base { |
34
|
10
|
|
|
10
|
|
22
|
my ($self, $base, $sec) = @_; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# for readability |
37
|
10
|
|
|
|
|
129
|
my %basehash = %$base; |
38
|
10
|
|
|
|
|
101
|
my %sechash = %$sec; |
39
|
10
|
|
|
|
|
25
|
my @basefields = @{$basehash{fields}}; |
|
10
|
|
|
|
|
47
|
|
40
|
10
|
|
|
|
|
16
|
my @secfields = @{$sechash{fields}}; |
|
10
|
|
|
|
|
43
|
|
41
|
10
|
|
|
|
|
22
|
my @baseparameters = @{$basehash{parameters}}; |
|
10
|
|
|
|
|
34
|
|
42
|
10
|
|
|
|
|
17
|
my @secparameters = @{$sechash{parameters}}; |
|
10
|
|
|
|
|
25
|
|
43
|
10
|
|
|
|
|
24
|
my $baseindex = $base->{index}; |
44
|
10
|
|
|
|
|
22
|
my $secindex = $sec->{index}; |
45
|
10
|
|
|
|
|
16
|
my @baseindexparams = @{$baseparameters[$baseindex]}; |
|
10
|
|
|
|
|
28
|
|
46
|
10
|
|
|
|
|
17
|
my @secindexparams = @{$secparameters[$secindex]}; |
|
10
|
|
|
|
|
30
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Verify that all sources share a commonly named, identically specified |
49
|
|
|
|
|
|
|
# index field |
50
|
10
|
100
|
|
|
|
397
|
croak "All data sources must have an identically named index field\n in the configuration file: $!" |
51
|
|
|
|
|
|
|
unless ($basefields[$baseindex] eq $secfields[$secindex]); |
52
|
9
|
|
|
|
|
39
|
for (my $i=0; $i
|
53
|
33
|
100
|
|
|
|
336
|
croak "All data sources must have identically specified parameters\n for the index field in the configuration file: $!" |
54
|
|
|
|
|
|
|
unless ($baseindexparams[$i] eq $secindexparams[$i]); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Build the new Combo::[subclass] object's 'fields' entry |
58
|
8
|
|
|
|
|
94
|
my $augmented_fields_ref = _augment_fields(\@basefields, \@secfields); |
59
|
8
|
|
|
|
|
17
|
my $newbasefieldsref = ${$augmented_fields_ref}{newfields}; |
|
8
|
|
|
|
|
20
|
|
60
|
8
|
|
|
|
|
15
|
my $secfieldspresentref = ${$augmented_fields_ref}{secfieldspresent}; |
|
8
|
|
|
|
|
18
|
|
61
|
8
|
|
|
|
|
14
|
my $secfieldsneededref = ${$augmented_fields_ref}{secfieldsneeded}; |
|
8
|
|
|
|
|
20
|
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# Build the new Combo::[subclass] object's replacement for %fieldlabels |
64
|
8
|
|
|
|
|
13
|
my %newbasefieldlabels; |
65
|
8
|
|
|
|
|
17
|
for (my $i = 0; $i < scalar(@{$newbasefieldsref}); $i++) { |
|
92
|
|
|
|
|
190
|
|
66
|
84
|
|
|
|
|
91
|
$newbasefieldlabels{${$newbasefieldsref}[$i]} = $i; |
|
84
|
|
|
|
|
185
|
|
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Build the new Combo::[subclass] object's replacement for %fp |
70
|
8
|
|
|
|
|
33
|
my %newbasefp; |
71
|
8
|
|
|
|
|
39
|
for (my $i = 0; $i < scalar(@basefields); $i++) { |
72
|
62
|
|
|
|
|
69
|
$newbasefp{$basefields[$i]} = \@{ $baseparameters[$i] }; |
|
62
|
|
|
|
|
174
|
|
73
|
|
|
|
|
|
|
} |
74
|
8
|
|
|
|
|
14
|
foreach my $el (keys %{$secfieldsneededref}) { |
|
8
|
|
|
|
|
35
|
|
75
|
22
|
|
|
|
|
27
|
$newbasefp{$secfields[$el]} = \@{ $secparameters[$el] }; |
|
22
|
|
|
|
|
65
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# %secpresentflip: Look-up table listing keys in sec also found in base; |
79
|
|
|
|
|
|
|
# value contains index position of that field; |
80
|
|
|
|
|
|
|
# needed because I don't want a key eq '0'. |
81
|
8
|
|
|
|
|
23
|
my %secpresentflip = (); |
82
|
|
|
|
|
|
|
|
83
|
8
|
|
|
|
|
24
|
foreach my $key (keys %$secfieldspresentref) { |
84
|
24
|
|
|
|
|
53
|
$secpresentflip{${$secfieldspresentref}{$key}} = [1, $key]; |
|
24
|
|
|
|
|
64
|
|
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
8
|
|
|
|
|
18
|
my %basefieldlabels; # Example: 'cno' => '2' |
88
|
|
|
|
|
|
|
my %secfieldlabels; # Example: 'cno' => '4' |
89
|
8
|
|
|
|
|
29
|
for (my $i = 0; $i < scalar(@basefields); $i++) { |
90
|
62
|
|
|
|
|
164
|
$basefieldlabels{$basefields[$i]} = $i; |
91
|
|
|
|
|
|
|
} |
92
|
8
|
|
|
|
|
30
|
for (my $i = 0; $i < scalar(@secfields); $i++) { |
93
|
46
|
|
|
|
|
120
|
$secfieldlabels{$secfields[$i]} = $i; |
94
|
|
|
|
|
|
|
} |
95
|
8
|
|
|
|
|
20
|
my %basefields = map {$_,1} @basefields; |
|
62
|
|
|
|
|
135
|
|
96
|
8
|
|
|
|
|
22
|
my %secfields = map {$_,1} @secfields; |
|
46
|
|
|
|
|
90
|
|
97
|
8
|
|
|
|
|
23
|
my %sharedfields; # Example: 'cno' => [ 2, 4 ] |
98
|
8
|
|
|
|
|
28
|
foreach my $field (keys %basefields) { |
99
|
62
|
100
|
|
|
|
181
|
$sharedfields{$field} = [ |
100
|
|
|
|
|
|
|
$basefieldlabels{$field}, |
101
|
|
|
|
|
|
|
$secfieldlabels{$field} |
102
|
|
|
|
|
|
|
] if ($secfields{$field}); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# %newbase will become the hash of arrays blessed into the |
106
|
|
|
|
|
|
|
# Combo::[subclass] object; |
107
|
|
|
|
|
|
|
# a reference to it will be _merge_into_base's return value. |
108
|
|
|
|
|
|
|
# Since its population is iterative, I have to start with an |
109
|
|
|
|
|
|
|
# empty hash reference. |
110
|
8
|
|
|
|
|
27
|
my $newbaseref = {}; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# pass variables to $self->_merge_engine(); |
113
|
8
|
|
|
|
|
150
|
$newbaseref = $self->_merge_engine( |
114
|
|
|
|
|
|
|
{ |
115
|
|
|
|
|
|
|
base => $base, |
116
|
|
|
|
|
|
|
basefieldlabels => \%basefieldlabels, |
117
|
|
|
|
|
|
|
secondary => $sec, |
118
|
|
|
|
|
|
|
secfieldlabels => \%secfieldlabels, |
119
|
|
|
|
|
|
|
secpresentflip => \%secpresentflip, |
120
|
|
|
|
|
|
|
secfieldsneeded => $secfieldsneededref, |
121
|
|
|
|
|
|
|
newbase => $newbaseref, |
122
|
|
|
|
|
|
|
newbasefields => $newbasefieldsref, |
123
|
|
|
|
|
|
|
newfp => \%newbasefp, |
124
|
|
|
|
|
|
|
newfieldlabels => \%newbasefieldlabels, |
125
|
|
|
|
|
|
|
sharedfields => \%sharedfields, |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# populate the Combo::[subclass] object's data structure |
130
|
8
|
|
|
|
|
74
|
my %newbase = %$newbaseref; |
131
|
8
|
|
|
|
|
22
|
$newbase{'fields'} = $newbasefieldsref; |
132
|
8
|
|
|
|
|
13
|
$newbase{'index'} = $baseindex; |
133
|
8
|
|
|
|
|
169
|
return \%newbase; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
################################################################################ |
137
|
|
|
|
|
|
|
##### &_augment_fields: called from within &_merge_into_base |
138
|
|
|
|
|
|
|
##### Prepares the 'fields' entry in the new Combo::[subclass] object |
139
|
|
|
|
|
|
|
################################################################################ |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub _augment_fields { |
142
|
8
|
|
|
8
|
|
13
|
my @basefields = @{+shift}; |
|
8
|
|
|
|
|
39
|
|
143
|
8
|
|
|
|
|
13
|
my @secfields = @{+shift}; |
|
8
|
|
|
|
|
22
|
|
144
|
8
|
|
|
|
|
25
|
my %seen = (); |
145
|
8
|
|
|
|
|
15
|
my @additions = (); |
146
|
8
|
|
|
|
|
27
|
my @total = (); |
147
|
8
|
|
|
|
|
15
|
my %secneeded = (); |
148
|
8
|
|
|
|
|
18
|
my %secpresent = (); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# Work thru sec's @fields by index |
151
|
8
|
|
|
|
|
31
|
for (my $d = 0; $d < scalar(@secfields); $d++) { |
152
|
|
|
|
|
|
|
# Identify fields in base which are found in sec; store in seen-hash. |
153
|
|
|
|
|
|
|
# Along the way build a look-up table %secpresent whose |
154
|
|
|
|
|
|
|
# value is the name of a field in sec which is *also* found in base, |
155
|
|
|
|
|
|
|
# and whose |
156
|
|
|
|
|
|
|
# key is the index that that value *originally* had in sec's @fields |
157
|
46
|
|
|
|
|
74
|
foreach my $c (@basefields) { |
158
|
214
|
100
|
|
|
|
430
|
if ($c eq $secfields[$d]) { |
159
|
24
|
|
|
|
|
41
|
$seen{$secfields[$d]} = 1; |
160
|
|
|
|
|
|
|
# $secfields[$d] is member of @basefields |
161
|
24
|
|
|
|
|
73
|
$secpresent{$d} = $secfields[$d]; |
162
|
24
|
|
|
|
|
33
|
last; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
# If a field in sec is *not* found in base, |
166
|
|
|
|
|
|
|
# add field's name to list of fields to be added |
167
|
46
|
100
|
|
|
|
127
|
push @additions, $secfields[$d] unless ($seen{$secfields[$d]}); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Build a look-up table %secneeded whose |
170
|
|
|
|
|
|
|
# value is the name of a field to be added, |
171
|
|
|
|
|
|
|
# and whose key is the index that that value |
172
|
|
|
|
|
|
|
# *originally* had in sec's @fields |
173
|
46
|
|
|
|
|
96
|
foreach my $j (@additions) { |
174
|
42
|
100
|
|
|
|
188
|
$secneeded{$d} = $j if ($j eq $secfields[$d]); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Add those fields that need to be added |
179
|
8
|
|
|
|
|
29
|
@total = (@basefields, @additions); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# Return references to the combined list of 'fields' and to the |
182
|
|
|
|
|
|
|
# two look-up tables |
183
|
8
|
|
|
|
|
52
|
my $augmented_fields_ref = { |
184
|
|
|
|
|
|
|
newfields => \@total, |
185
|
|
|
|
|
|
|
secfieldspresent => \%secpresent, |
186
|
|
|
|
|
|
|
secfieldsneeded => \%secneeded, |
187
|
|
|
|
|
|
|
}; |
188
|
8
|
|
|
|
|
88
|
return $augmented_fields_ref; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub _extract_rows { |
192
|
5
|
|
|
5
|
|
24
|
my ($self, $column, $relation, $choicesref, $fpref, $flref, |
193
|
|
|
|
|
|
|
$_analyze_relation_subref, $_strip_non_matches_subref) = @_; |
194
|
5
|
|
|
|
|
41
|
my %data = %$self; |
195
|
5
|
|
|
|
|
43
|
my %fp = %$fpref; |
196
|
5
|
|
|
|
|
34
|
my %fieldlabels = %$flref; |
197
|
5
|
|
|
|
|
15
|
my ($inequality_ref, $dataref); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Analysis of $column |
200
|
|
|
|
|
|
|
# DATA MUNGING STARTS HERE |
201
|
|
|
|
|
|
|
# $column = lc($column); |
202
|
|
|
|
|
|
|
# DATA MUNGING ENDS HERE |
203
|
5
|
100
|
|
|
|
251
|
croak "Column (field) name requested does not exist in \@fields: $!" |
204
|
|
|
|
|
|
|
unless (exists $fieldlabels{$column}); |
205
|
4
|
|
|
|
|
14
|
my $sortorder = $fp{$column}[1]; |
206
|
4
|
|
|
|
|
12
|
my $sorttype = $fp{$column}[2]; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Analysis of $relation: |
209
|
|
|
|
|
|
|
# &_analyze_relation passed by reference from Data::Presenter |
210
|
4
|
|
|
|
|
22
|
($relation, $inequality_ref) = |
211
|
|
|
|
|
|
|
&$_analyze_relation_subref($relation, $sorttype); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Analysis of @choices (partial) |
214
|
4
|
|
|
|
|
24
|
my $choice = ''; |
215
|
4
|
|
|
|
|
9
|
my @corrected = (); |
216
|
4
|
|
|
|
|
9
|
my %seen = (); |
217
|
2
|
|
|
|
|
157
|
croak "Too many choices for less than\/greater than comparison: $!" |
218
|
4
|
100
|
100
|
|
|
33
|
if (scalar(@$choicesref) > 1 && ${$inequality_ref}{$relation}); |
219
|
3
|
|
|
|
|
11
|
foreach $choice (@$choicesref) { |
220
|
|
|
|
|
|
|
# DATA MUNGING STARTS HERE |
221
|
|
|
|
|
|
|
# $choice = uc($choice); # because all data in 'census.txt' is u.c. |
222
|
|
|
|
|
|
|
# DATA MUNGING ENDS HERE |
223
|
4
|
|
|
|
|
8
|
push(@corrected, $choice); |
224
|
4
|
|
|
|
|
16
|
$seen{$choice} = 1; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Strip out non-matching rows: |
228
|
|
|
|
|
|
|
# &_strip_non_matches passed by reference from Data::Presenter |
229
|
3
|
|
|
|
|
19
|
$dataref = &$_strip_non_matches_subref( |
230
|
|
|
|
|
|
|
\%data, \%fieldlabels, $column, $relation, \@corrected, \%seen); |
231
|
3
|
|
|
|
|
24
|
return $dataref; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
1; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
############################## DOCUMENTATION ############################## |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 NAME |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Data::Presenter::Combo |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head1 VERSION |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This document refers to version 1.03 of Data::Presenter::Combo, released |
245
|
|
|
|
|
|
|
February 10, 2008. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head1 DESCRIPTION |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
This package is a subclass of, and inherits from, Data::Presenter. Please see the Data::Presenter documentation to learn how to use Data::Presenter::Combo. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head1 AUTHOR |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
James E. Keenan (jkeenan@cpan.org). |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Creation date: October 28, 2001. Last modification date: February 10, 2008. |
256
|
|
|
|
|
|
|
Copyright (c) 2001-5 James E. Keenan. United States. All rights reserved. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
All data presented in this documentation or in the sample files in the |
259
|
|
|
|
|
|
|
archive accompanying this documentation are dummy copy. The data was |
260
|
|
|
|
|
|
|
entirely fabricated by the author for heuristic purposes. Any resemblance |
261
|
|
|
|
|
|
|
to any person, living or dead, is coincidental. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
This is free software which you may distribute under the same terms as |
264
|
|
|
|
|
|
|
Perl itself. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=cut |
267
|
|
|
|
|
|
|
|