line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Hub::Data::Handlers; |
2
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
3
|
1
|
|
|
1
|
|
7
|
use Hub qw/:lib/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
4
|
|
|
|
|
|
|
our $VERSION = '4.00043'; |
5
|
|
|
|
|
|
|
our @EXPORT = qw//; |
6
|
|
|
|
|
|
|
our @EXPORT_OK = qw/fetch store getv setv delete/; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
9
|
|
|
|
|
|
|
# fetch - Get a nested value whose parent may need to be loaded from disk |
10
|
|
|
|
|
|
|
# fetch \%data, $index |
11
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub fetch { |
14
|
19
|
|
|
19
|
1
|
62
|
my $result = _traverse($_[0], $_[1]); |
15
|
19
|
100
|
|
|
|
33
|
_transcend($result) if (@{$result->{'not_found'}}); |
|
19
|
|
|
|
|
64
|
|
16
|
19
|
|
|
|
|
113
|
$result->{'value'}; |
17
|
|
|
|
|
|
|
}#fetch |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
20
|
|
|
|
|
|
|
# store - Store a nested value whose parent may need to be loaded from disk |
21
|
|
|
|
|
|
|
# store \%data, $index, ?value |
22
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub store { |
25
|
7
|
|
|
7
|
1
|
38
|
my $result = _traverse($_[0], $_[1]); |
26
|
7
|
100
|
|
|
|
12
|
_transcend($result) if (@{$result->{'not_found'}}); |
|
7
|
|
|
|
|
31
|
|
27
|
7
|
100
|
|
|
|
10
|
_autovivify($result) if (@{$result->{'not_found'}}); |
|
7
|
|
|
|
|
27
|
|
28
|
7
|
|
|
|
|
22
|
_set($result->{'parent'}, $result->{'last_node'}, $_[2]); |
29
|
|
|
|
|
|
|
}#store |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
32
|
|
|
|
|
|
|
# getv - Get a nested value |
33
|
|
|
|
|
|
|
# getv \%data, $index |
34
|
|
|
|
|
|
|
# getv \@data, $index |
35
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub getv { |
38
|
0
|
|
|
0
|
1
|
0
|
my $result = _traverse($_[0], $_[1]); |
39
|
0
|
|
|
|
|
0
|
$result->{'value'}; |
40
|
|
|
|
|
|
|
}#getv |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
43
|
|
|
|
|
|
|
# setv - Store a nested value |
44
|
|
|
|
|
|
|
# setv \%data, $index, ?value |
45
|
|
|
|
|
|
|
# setv \@data, $index, ?value |
46
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub setv { |
49
|
0
|
|
|
0
|
1
|
0
|
my $result = _traverse($_[0], $_[1]); |
50
|
0
|
0
|
|
|
|
0
|
_transcend($result) if (@{$result->{'not_found'}}); |
|
0
|
|
|
|
|
0
|
|
51
|
0
|
0
|
|
|
|
0
|
_autovivify($result) if (@{$result->{'not_found'}}); |
|
0
|
|
|
|
|
0
|
|
52
|
0
|
|
|
|
|
0
|
_set($result->{'parent'}, $result->{'last_node'}, $_[2]); |
53
|
|
|
|
|
|
|
}#setv |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
56
|
|
|
|
|
|
|
# delete - Remove a nested value |
57
|
|
|
|
|
|
|
# delete \%data, $index |
58
|
|
|
|
|
|
|
# delete \@data, $index |
59
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub delete { |
62
|
4
|
|
|
4
|
1
|
11
|
my $result = _traverse($_[0], $_[1]); |
63
|
4
|
50
|
|
|
|
7
|
return if @{$result->{'not_found'}}; |
|
4
|
|
|
|
|
20
|
|
64
|
0
|
|
|
|
|
0
|
_delete($result->{'parent'}, $result->{'last_node'}); |
65
|
|
|
|
|
|
|
}#delete |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
68
|
|
|
|
|
|
|
# _get - Get node value from an array or hash |
69
|
|
|
|
|
|
|
# _get \%data, $node |
70
|
|
|
|
|
|
|
# _get \@data, $node |
71
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _get { |
74
|
54
|
50
|
33
|
54
|
|
160
|
Hub::is_bipolar($_[0]) |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
75
|
|
|
|
|
|
|
? $_[0]->get_data($_[1]) |
76
|
|
|
|
|
|
|
: ref($_[0]) eq 'ARRAY' && $_[1] =~ /^\d+$/ |
77
|
|
|
|
|
|
|
? $_[0]->[$_[1]] |
78
|
|
|
|
|
|
|
: $_[1] =~ /^\{(.*)\}$/ |
79
|
|
|
|
|
|
|
? Hub::subset(@_) |
80
|
|
|
|
|
|
|
: isa($_[0], 'HASH') |
81
|
|
|
|
|
|
|
? $_[0]->{$_[1]} |
82
|
|
|
|
|
|
|
: Hub::subset(@_); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# $_[1] =~ /^\{(.*)\}$/ |
85
|
|
|
|
|
|
|
# ? Hub::subset(@_) |
86
|
|
|
|
|
|
|
# : ref($_[0]) eq 'ARRAY' && $_[1] =~ /^\d+$/ |
87
|
|
|
|
|
|
|
# ? $_[0]->[$_[1]] |
88
|
|
|
|
|
|
|
# : ref($_[0]) eq 'HASH' |
89
|
|
|
|
|
|
|
# ? Hub::subset(@_) |
90
|
|
|
|
|
|
|
# : Hub::is_bipolar($_[0]) |
91
|
|
|
|
|
|
|
# ? $_[0]->get_data($_[1]) |
92
|
|
|
|
|
|
|
# : isa($_[0], 'HASH') |
93
|
|
|
|
|
|
|
# ? Hub::subset(@_) |
94
|
|
|
|
|
|
|
# : undef; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
}#_get |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
99
|
|
|
|
|
|
|
# _set - Set a node value on an array or hash |
100
|
|
|
|
|
|
|
# _set \%data, $node, $value |
101
|
|
|
|
|
|
|
# _set \@data, $node, $value |
102
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub _set { |
105
|
7
|
50
|
33
|
7
|
|
52
|
if ((ref($_[0]) eq 'ARRAY') && ($_[1] =~ /^\d+$/)) { |
|
|
50
|
|
|
|
|
|
106
|
0
|
|
|
|
|
0
|
$_[0]->[$_[1]] = $_[2]; |
107
|
|
|
|
|
|
|
} elsif (isa($_[0], 'HASH')) { |
108
|
7
|
|
|
|
|
30
|
$_[0]->{$_[1]} = $_[2]; |
109
|
|
|
|
|
|
|
} else { |
110
|
0
|
|
|
|
|
0
|
confess "Type mismatch"; |
111
|
|
|
|
|
|
|
} |
112
|
7
|
|
|
|
|
28
|
return $_[2]; |
113
|
|
|
|
|
|
|
}#_set |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
116
|
|
|
|
|
|
|
# _delete - Remove a node from an array or hash |
117
|
|
|
|
|
|
|
# _delete - \%data, $node |
118
|
|
|
|
|
|
|
# _delete - \@data, $node |
119
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub _delete { |
122
|
0
|
0
|
0
|
0
|
|
0
|
if ($_[1] =~ /^\d+$/ && ref($_[0]) eq 'ARRAY') { |
|
|
0
|
|
|
|
|
|
123
|
0
|
|
|
|
|
0
|
delete $_[0]->[$_[1]]; |
124
|
|
|
|
|
|
|
} elsif (isa($_[0], 'HASH')) { |
125
|
0
|
|
|
|
|
0
|
delete $_[0]->{$_[1]}; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
}#_delete |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
130
|
|
|
|
|
|
|
# _autovivify - Create missing parent nodes |
131
|
|
|
|
|
|
|
# _autovivify \%result |
132
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _autovivify { |
135
|
5
|
|
|
5
|
|
9
|
my $result = shift; |
136
|
5
|
|
|
|
|
6
|
my $not_found = $result->{'not_found'}; |
137
|
|
|
|
|
|
|
# autovivify (create parents) if needed |
138
|
5
|
|
|
|
|
15
|
while (@$not_found) { |
139
|
5
|
|
|
|
|
9
|
my $node = shift @$not_found; |
140
|
5
|
50
|
|
|
|
22
|
if (@$not_found) { |
141
|
|
|
|
|
|
|
# fill intermediates as hashes, unless the next node is an array index |
142
|
0
|
0
|
|
|
|
0
|
$result->{'parent'} = |
143
|
|
|
|
|
|
|
_set($result->{'parent'}, $node, |
144
|
|
|
|
|
|
|
$$not_found[0] =~ /^\d+$/ ? [] : {}); |
145
|
0
|
|
|
|
|
0
|
push @{$result->{'found'}}, $node; |
|
0
|
|
|
|
|
0
|
|
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
}#_autovivify |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
151
|
|
|
|
|
|
|
# _traverse - Step into the nested data structure one index node at a time |
152
|
|
|
|
|
|
|
# _traverse \%data, $index |
153
|
|
|
|
|
|
|
# _traverse \@data, $index |
154
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub _traverse { |
157
|
38
|
|
|
38
|
|
49
|
my $ptr = $_[0]; |
158
|
38
|
|
|
|
|
51
|
my $parent = $_[0]; |
159
|
38
|
|
|
|
|
56
|
my @found = (); |
160
|
38
|
|
|
|
|
86
|
my @nodes = _split($_[1]); |
161
|
38
|
50
|
|
|
|
98
|
my $last_node = @nodes ? $nodes[-1] : $_[1]; |
162
|
38
|
|
|
|
|
92
|
while (@nodes) { |
163
|
54
|
|
|
|
|
73
|
$parent = $ptr; |
164
|
54
|
|
|
|
|
104
|
$ptr = _get($ptr, $nodes[0]); |
165
|
54
|
100
|
|
|
|
131
|
last unless defined $ptr; |
166
|
19
|
|
|
|
|
54
|
push @found, shift @nodes; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
return { |
169
|
38
|
|
|
|
|
246
|
'value' => $ptr, |
170
|
|
|
|
|
|
|
'parent' => $parent, |
171
|
|
|
|
|
|
|
'found' => \@found, |
172
|
|
|
|
|
|
|
'not_found' => \@nodes, |
173
|
|
|
|
|
|
|
'last_node' => $last_node, |
174
|
|
|
|
|
|
|
}; |
175
|
|
|
|
|
|
|
}#_traverse |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
178
|
|
|
|
|
|
|
# _transcend - Extend the search to the file system |
179
|
|
|
|
|
|
|
# _transcend \%result |
180
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub _transcend { |
183
|
23
|
|
|
23
|
|
30
|
my $result = shift; |
184
|
23
|
|
|
|
|
31
|
my $base = join '/', @{$result->{'found'}}; |
|
23
|
|
|
|
|
55
|
|
185
|
23
|
|
|
|
|
32
|
my $ptr = $result->{'parent'}; |
186
|
23
|
|
|
|
|
30
|
my $continue = 1; |
187
|
23
|
|
100
|
|
|
59
|
while ($continue && @{$result->{'not_found'}}) { |
|
30
|
|
|
|
|
108
|
|
188
|
23
|
|
|
|
|
49
|
my $node = $result->{'not_found'}[0]; |
189
|
23
|
|
|
|
|
34
|
$result->{'parent'} = $ptr; |
190
|
23
|
100
|
|
|
|
46
|
my $path = $base ? "$base/$node" : $node; |
191
|
23
|
100
|
|
|
|
459
|
if (-e $path) { |
192
|
15
|
|
|
|
|
64
|
$ptr->{$node} = Hub::mkhandler($path); |
193
|
15
|
|
|
|
|
211
|
$continue = -d $path; |
194
|
15
|
|
|
|
|
20
|
$base = $path; |
195
|
15
|
|
|
|
|
30
|
$ptr = $ptr->{$node}; |
196
|
15
|
|
|
|
|
17
|
push @{$result->{'found'}}, shift @{$result->{'not_found'}}; |
|
15
|
|
|
|
|
32
|
|
|
15
|
|
|
|
|
77
|
|
197
|
|
|
|
|
|
|
} else { |
198
|
8
|
|
|
|
|
26
|
$continue = 0; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
23
|
100
|
|
|
|
32
|
if (@{$result->{'not_found'}}) { |
|
23
|
|
|
|
|
64
|
|
202
|
8
|
|
|
|
|
10
|
my $result2 = _traverse($ptr, join('/', @{$result->{'not_found'}})); |
|
8
|
|
|
|
|
24
|
|
203
|
8
|
|
|
|
|
20
|
$result->{'value'} = $result2->{'value'}; |
204
|
8
|
|
|
|
|
15
|
$result->{'parent'} = $result2->{'parent'}; |
205
|
8
|
|
|
|
|
9
|
push @{$result->{'found'}}, @{$result2->{'found'}}; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
14
|
|
206
|
8
|
|
|
|
|
27
|
$result->{'not_found'} = $result2->{'not_found'}; |
207
|
|
|
|
|
|
|
} else { |
208
|
15
|
|
|
|
|
30
|
$result->{'value'} = $ptr; |
209
|
|
|
|
|
|
|
} |
210
|
23
|
|
|
|
|
43
|
$result; |
211
|
|
|
|
|
|
|
}#_transcend |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
214
|
|
|
|
|
|
|
# _get_parser - Get the parser for a given file |
215
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub _get_parser { |
218
|
0
|
|
|
0
|
|
0
|
my $parser = 'File'; |
219
|
0
|
0
|
|
|
|
0
|
if ($_[0] =~ /\.(dat|hf|metadata)$/) { |
220
|
0
|
|
|
|
|
0
|
$parser = 'HashFile'; |
221
|
|
|
|
|
|
|
} |
222
|
0
|
|
|
|
|
0
|
Hub::mkinst($parser, $_[0]); |
223
|
|
|
|
|
|
|
}#_get_parser |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
226
|
|
|
|
|
|
|
# _split - Split an index into nodes, removing empty ones |
227
|
|
|
|
|
|
|
# _split - $index |
228
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub _split { |
231
|
38
|
|
|
38
|
|
106
|
grep {length $_ > 0} split '/', $_[0]; |
|
69
|
|
|
|
|
204
|
|
232
|
|
|
|
|
|
|
}#_split |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
1; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=pod:summary Access nested data |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=pod:synopsis |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=pod:description |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=cut |