line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# This file is part of Config-Model |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This software is Copyright (c) 2005-2022 by Dominique Dumont. |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This is free software, licensed under: |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# The GNU Lesser General Public License, Version 2.1, February 1999 |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
package Config::Model::ObjTreeScanner 2.153; # TRIAL |
11
|
|
|
|
|
|
|
|
12
|
59
|
|
|
59
|
|
439
|
use strict; |
|
59
|
|
|
|
|
140
|
|
|
59
|
|
|
|
|
1782
|
|
13
|
59
|
|
|
59
|
|
363
|
use Config::Model::Exception; |
|
59
|
|
|
|
|
179
|
|
|
59
|
|
|
|
|
1414
|
|
14
|
59
|
|
|
59
|
|
378
|
use Scalar::Util qw/blessed/; |
|
59
|
|
|
|
|
183
|
|
|
59
|
|
|
|
|
3379
|
|
15
|
59
|
|
|
59
|
|
35749
|
use Carp::Assert::More; |
|
59
|
|
|
|
|
287306
|
|
|
59
|
|
|
|
|
10062
|
|
16
|
59
|
|
|
59
|
|
553
|
use Carp; |
|
59
|
|
|
|
|
175
|
|
|
59
|
|
|
|
|
2860
|
|
17
|
59
|
|
|
59
|
|
405
|
use warnings; |
|
59
|
|
|
|
|
187
|
|
|
59
|
|
|
|
|
1710
|
|
18
|
|
|
|
|
|
|
|
19
|
59
|
|
|
59
|
|
393
|
use Carp qw/croak confess cluck/; |
|
59
|
|
|
|
|
141
|
|
|
59
|
|
|
|
|
108220
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub new { |
22
|
247
|
|
|
247
|
1
|
13611
|
my $type = shift; |
23
|
247
|
|
|
|
|
1523
|
my %args = @_; |
24
|
|
|
|
|
|
|
|
25
|
247
|
|
|
|
|
1035
|
my $self = { auto_vivify => 1, check => 'yes' }; |
26
|
247
|
|
|
|
|
529
|
bless $self, $type; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$self->{leaf_cb} = delete $args{leaf_cb} |
29
|
247
|
50
|
|
|
|
1130
|
or croak __PACKAGE__, "->new: missing leaf_cb parameter"; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# we may use leaf_cb |
32
|
247
|
|
100
|
|
|
1307
|
$self->create_fallback( delete $args{fallback} || 'all' ); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# get all call_backs |
35
|
|
|
|
|
|
|
my @value_cb = |
36
|
247
|
|
|
|
|
710
|
map { $_ . '_value_cb' } qw/boolean enum string uniline integer number reference/; |
|
1729
|
|
|
|
|
4147
|
|
37
|
|
|
|
|
|
|
|
38
|
247
|
|
|
|
|
920
|
foreach my $param ( |
39
|
|
|
|
|
|
|
qw/check node_element_cb hash_element_cb |
40
|
|
|
|
|
|
|
list_element_cb check_list_element_cb node_content_cb |
41
|
|
|
|
|
|
|
node_content_hook list_element_hook hash_element_hook |
42
|
|
|
|
|
|
|
auto_vivify up_cb/, @value_cb |
43
|
|
|
|
|
|
|
) { |
44
|
4446
|
100
|
|
|
|
10622
|
$self->{$param} = $args{$param} if defined $args{$param}; |
45
|
4446
|
|
|
|
|
6797
|
delete $args{$param}; # may exists but be undefined |
46
|
|
|
|
|
|
|
croak __PACKAGE__, "->new: missing $param parameter" |
47
|
4446
|
50
|
|
|
|
8844
|
unless defined $self->{$param}; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
247
|
50
|
|
|
|
1076
|
if (delete $args{experience}) { |
51
|
0
|
|
|
|
|
0
|
carp "->new: experience parameter is deprecated"; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# this parameter is optional and does not need a fallback |
55
|
247
|
|
100
|
|
|
1380
|
$self->{node_dispatch_cb} = delete $args{node_dispatch_cb} || {}; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
croak __PACKAGE__, "->new: node_dispatch_cb is not a hash ref" |
58
|
247
|
50
|
|
|
|
2394
|
unless ref( $self->{node_dispatch_cb} ) eq 'HASH'; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
croak __PACKAGE__, "->new: unexpected check: $self->{check}" |
61
|
247
|
50
|
|
|
|
1906
|
unless $self->{check} =~ /yes|no|skip/; |
62
|
|
|
|
|
|
|
|
63
|
247
|
50
|
|
|
|
717
|
croak __PACKAGE__, "->new: unexpected parameter: ", join( ' ', keys %args ) |
64
|
|
|
|
|
|
|
if scalar %args; |
65
|
|
|
|
|
|
|
|
66
|
247
|
|
|
|
|
1083
|
return $self; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# internal |
70
|
|
|
|
|
|
|
sub create_fallback { |
71
|
247
|
|
|
247
|
0
|
446
|
my $self = shift; |
72
|
247
|
|
|
|
|
430
|
my $fallback = shift; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
map { |
75
|
247
|
|
|
|
|
608
|
$self->{$_} = |
76
|
|
|
|
2105
|
|
|
sub { } |
77
|
741
|
|
|
|
|
2963
|
} qw/node_content_hook hash_element_hook list_element_hook/; |
78
|
|
|
|
|
|
|
|
79
|
247
|
50
|
33
|
|
|
1320
|
return if not defined $fallback or $fallback eq 'none'; |
80
|
|
|
|
|
|
|
|
81
|
247
|
|
|
|
|
485
|
my $done = 0; |
82
|
|
|
|
|
|
|
|
83
|
247
|
50
|
33
|
|
|
1185
|
if ( $fallback eq 'node' or $fallback eq 'all' ) { |
84
|
247
|
|
|
|
|
462
|
$done++; |
85
|
|
|
|
|
|
|
my $node_content_cb = sub { |
86
|
984
|
|
|
984
|
|
2934
|
my ( $scanner, $data_r, $node, @element ) = @_; |
87
|
984
|
|
|
|
|
1952
|
map { $scanner->scan_element( $data_r, $node, $_ ) } @element; |
|
5559
|
|
|
|
|
15462
|
|
88
|
247
|
|
|
|
|
1028
|
}; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
my $node_element_cb = sub { |
91
|
291
|
|
|
291
|
|
680
|
my ( $scanner, $data_r, $node, $element_name, $key, $next_node ) = @_; |
92
|
291
|
|
|
|
|
780
|
$scanner->scan_node( $data_r, $next_node ); |
93
|
247
|
|
|
|
|
1014
|
}; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $hash_element_cb = sub { |
96
|
75
|
|
|
75
|
|
186
|
my ( $scanner, $data_r, $node, $element_name, @keys ) = @_; |
97
|
75
|
|
|
|
|
182
|
map { $scanner->scan_hash( $data_r, $node, $element_name, $_ ) } @keys; |
|
72
|
|
|
|
|
236
|
|
98
|
247
|
|
|
|
|
1067
|
}; |
99
|
|
|
|
|
|
|
|
100
|
247
|
|
|
|
|
576
|
$self->{list_element_cb} = $hash_element_cb; |
101
|
247
|
|
|
|
|
719
|
$self->{hash_element_cb} = $hash_element_cb; |
102
|
247
|
|
|
|
|
558
|
$self->{node_element_cb} = $node_element_cb; |
103
|
247
|
|
|
|
|
541
|
$self->{node_content_cb} = $node_content_cb; |
104
|
247
|
|
|
1264
|
|
956
|
$self->{up_cb} = sub { }; # do nothing |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
247
|
50
|
33
|
|
|
1269
|
if ( $fallback eq 'leaf' or $fallback eq 'all' ) { |
108
|
247
|
|
|
|
|
449
|
$done++; |
109
|
247
|
|
33
|
|
|
1126
|
my $l = $self->{string_value_cb} ||= $self->{leaf_cb}; |
110
|
|
|
|
|
|
|
|
111
|
247
|
|
33
|
|
|
1134
|
$self->{check_list_element_cb} ||= $l; |
112
|
247
|
|
33
|
|
|
1171
|
$self->{enum_value_cb} ||= $l; |
113
|
247
|
|
33
|
|
|
1171
|
$self->{integer_value_cb} ||= $l; |
114
|
247
|
|
33
|
|
|
1398
|
$self->{number_value_cb} ||= $l; |
115
|
247
|
|
33
|
|
|
1178
|
$self->{boolean_value_cb} ||= $l; |
116
|
247
|
|
33
|
|
|
1122
|
$self->{reference_value_cb} ||= $l; |
117
|
247
|
|
33
|
|
|
927
|
$self->{uniline_value_cb} ||= $l; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
247
|
50
|
|
|
|
707
|
croak __PACKAGE__, "->new: Unexpected fallback value '$fallback'. ", |
121
|
|
|
|
|
|
|
"Expected 'node', 'leaf', 'all' or 'none'" |
122
|
|
|
|
|
|
|
if not $done; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub scan_node { |
126
|
1281
|
|
|
1281
|
1
|
3831
|
my ( $self, $data_r, $node ) = @_; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
#print "scan_node ",$node->name,"\n"; |
129
|
|
|
|
|
|
|
# get all elements according to catalog |
130
|
|
|
|
|
|
|
|
131
|
1281
|
50
|
33
|
|
|
9207
|
Config::Model::Exception::Internal->throw( error => "'$node' is not a Config::Model object" ) |
132
|
|
|
|
|
|
|
unless blessed($node) |
133
|
|
|
|
|
|
|
and $node->isa("Config::Model::AnyThing"); |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# skip exploration of warped out node |
136
|
1281
|
100
|
|
|
|
5679
|
if ( $node->isa('Config::Model::WarpedNode') ) { |
137
|
254
|
|
|
|
|
921
|
$node = $node->get_actual_node; |
138
|
254
|
50
|
|
|
|
630
|
return unless defined $node; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
1281
|
|
|
|
|
4891
|
my $config_class = $node->config_class_name; |
142
|
1281
|
|
|
|
|
2513
|
my $node_dispatch_cb = $self->{node_dispatch_cb}{$config_class}; |
143
|
|
|
|
|
|
|
|
144
|
1281
|
|
66
|
|
|
4217
|
my $actual_cb = $node_dispatch_cb || $self->{node_content_cb}; |
145
|
|
|
|
|
|
|
|
146
|
1281
|
|
|
|
|
4243
|
my @element_list = $node->get_element_name( check => $self->{check} ); |
147
|
|
|
|
|
|
|
|
148
|
1281
|
|
|
|
|
4642
|
$self->{node_content_hook}->( $self, $data_r, $node, @element_list ); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# we could add here a "last element" call-back, but it's not |
151
|
|
|
|
|
|
|
# very useful if the last element is a hash. |
152
|
1281
|
|
|
|
|
4455
|
$actual_cb->( $self, $data_r, $node, @element_list ); |
153
|
|
|
|
|
|
|
|
154
|
1277
|
|
|
|
|
3788
|
$self->{up_cb}->( $self, $data_r, $node ); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub scan_element { |
158
|
7208
|
|
|
7208
|
1
|
15338
|
my ( $self, $data_r, $node, $element_name ) = @_; |
159
|
|
|
|
|
|
|
|
160
|
7208
|
|
|
|
|
20982
|
my $element_type = $node->element_type($element_name); |
161
|
|
|
|
|
|
|
|
162
|
7208
|
|
|
|
|
13089
|
my $autov = $self->{auto_vivify}; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
#print "scan_element $element_name "; |
165
|
7208
|
100
|
|
|
|
29231
|
if ( $element_type eq 'hash' ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
#print "type hash\n"; |
168
|
373
|
|
|
|
|
1145
|
my @keys = $self->get_keys( $node, $element_name ); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# if hash element grab keys and perform callback |
171
|
373
|
|
|
|
|
1410
|
$self->{hash_element_hook}->( $self, $data_r, $node, $element_name, @keys ); |
172
|
373
|
|
|
|
|
1206
|
$self->{hash_element_cb}->( $self, $data_r, $node, $element_name, @keys ); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
elsif ( $element_type eq 'list' ) { |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
#print "type list\n"; |
177
|
498
|
|
|
|
|
1366
|
my @keys = $self->get_keys( $node, $element_name ); |
178
|
498
|
|
|
|
|
1793
|
$self->{list_element_hook}->( $self, $data_r, $node, $element_name, @keys ); |
179
|
498
|
|
|
|
|
1497
|
$self->{list_element_cb}->( $self, $data_r, $node, $element_name, @keys ); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
elsif ( $element_type eq 'check_list' ) { |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
#print "type list\n"; |
184
|
91
|
|
|
|
|
368
|
my $cl_elt = $node->fetch_element( name => $element_name, check => $self->{check} ); |
185
|
91
|
|
|
|
|
415
|
$self->{check_list_element_cb}->( $self, $data_r, $node, $element_name, undef, $cl_elt ); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
elsif ( $element_type eq 'node' ) { |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
#print "type object\n"; |
190
|
|
|
|
|
|
|
# avoid auto-vivification |
191
|
|
|
|
|
|
|
my $next_obj = |
192
|
|
|
|
|
|
|
( $autov or $node->is_element_defined($element_name) ) |
193
|
|
|
|
|
|
|
? $node->fetch_element( name => $element_name, check => $self->{check} ) |
194
|
362
|
50
|
66
|
|
|
1610
|
: undef; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# if obj element, cb |
197
|
362
|
|
|
|
|
1550
|
$self->{node_element_cb}->( $self, $data_r, $node, $element_name, undef, $next_obj ); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
elsif ( $element_type eq 'warped_node' ) { |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
#print "type warped\n"; |
202
|
|
|
|
|
|
|
my $next_obj = |
203
|
|
|
|
|
|
|
( $autov or $node->is_element_defined($element_name) ) |
204
|
|
|
|
|
|
|
? $node->fetch_element( name => $element_name, check => $self->{check} ) |
205
|
257
|
50
|
66
|
|
|
1237
|
: undef; |
206
|
257
|
50
|
|
|
|
1433
|
$self->{node_element_cb}->( $self, $data_r, $node, $element_name, undef, $next_obj ) if $next_obj; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
elsif ( $element_type eq 'leaf' ) { |
209
|
5627
|
|
|
|
|
16079
|
my $next_obj = $node->fetch_element( name => $element_name, check => $self->{check} ); |
210
|
5627
|
|
|
|
|
20590
|
my $type = $next_obj->value_type; |
211
|
5627
|
50
|
|
|
|
12189
|
return unless $type; |
212
|
5627
|
|
|
|
|
11095
|
my $cb_name = $type . '_value_cb'; |
213
|
5627
|
|
|
|
|
10282
|
my $cb = $self->{$cb_name}; |
214
|
5627
|
50
|
|
|
|
10816
|
croak "scan_element: No call_back specified for '$cb_name'" |
215
|
|
|
|
|
|
|
unless defined $cb; |
216
|
5627
|
|
|
|
|
14782
|
$cb->( $self, $data_r, $node, $element_name, undef, $next_obj ); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
else { |
219
|
0
|
|
|
|
|
0
|
croak "Unexpected element_type: $element_type"; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub scan_hash { |
224
|
837
|
|
|
837
|
1
|
2133
|
my ( $self, $data_r, $node, $element_name, $key ) = @_; |
225
|
|
|
|
|
|
|
|
226
|
837
|
|
|
|
|
2562
|
assert_like( $node->element_type($element_name), qr/(hash|list)/ ); |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
#print "scan_hash ",$node->name," element $element_name key $key "; |
229
|
837
|
|
|
|
|
15994
|
my $item = $node->fetch_element( name => $element_name, check => $self->{check} ); |
230
|
|
|
|
|
|
|
|
231
|
837
|
|
|
|
|
2758
|
my $cargo_type = $item->cargo_type($element_name); |
232
|
837
|
|
|
|
|
2863
|
my $next_obj = $item->fetch_with_id( index => $key, check => $self->{check} ); |
233
|
|
|
|
|
|
|
|
234
|
837
|
100
|
|
|
|
3603
|
if ( $cargo_type =~ /node$/ ) { |
|
|
50
|
|
|
|
|
|
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#print "type object or warped\n"; |
237
|
445
|
|
|
|
|
1538
|
$self->{node_element_cb}->( $self, $data_r, $node, $element_name, $key, $next_obj ); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
elsif ( $cargo_type eq 'leaf' ) { |
240
|
392
|
|
|
|
|
1861
|
my $cb_name = $next_obj->value_type . '_value_cb'; |
241
|
392
|
|
|
|
|
788
|
my $cb = $self->{$cb_name}; |
242
|
392
|
50
|
|
|
|
880
|
croak "scan_hash: No call_back specified for '$cb_name'" |
243
|
|
|
|
|
|
|
unless defined $cb; |
244
|
392
|
|
|
|
|
1154
|
$cb->( $self, $data_r, $node, $element_name, $key, $next_obj ); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
else { |
247
|
0
|
|
|
|
|
0
|
croak "Unexpected cargo_type: $cargo_type"; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub scan_list { |
252
|
91
|
|
|
91
|
1
|
274
|
goto &scan_hash; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub get_keys { |
256
|
874
|
|
|
874
|
1
|
1806
|
my ( $self, $node, $element_name ) = @_; |
257
|
|
|
|
|
|
|
|
258
|
874
|
|
|
|
|
1913
|
my $element_type = $node->element_type($element_name); |
259
|
874
|
|
|
|
|
2762
|
my $item = $node->fetch_element( name => $element_name, check => $self->{check} ); |
260
|
|
|
|
|
|
|
|
261
|
874
|
50
|
66
|
|
|
4930
|
return $item->fetch_all_indexes |
262
|
|
|
|
|
|
|
if $element_type eq 'hash' |
263
|
|
|
|
|
|
|
|| $element_type eq 'list'; |
264
|
|
|
|
|
|
|
|
265
|
0
|
|
|
|
|
|
Config::Model::Exception::Internal->throw( |
266
|
|
|
|
|
|
|
error => "called get_keys on non hash or non list" . " element $element_name", |
267
|
|
|
|
|
|
|
object => $node |
268
|
|
|
|
|
|
|
); |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
1; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# ABSTRACT: Scan config tree and perform call-backs for each element or node |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
__END__ |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=pod |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=encoding UTF-8 |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=head1 NAME |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Config::Model::ObjTreeScanner - Scan config tree and perform call-backs for each element or node |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=head1 VERSION |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
version 2.153 |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head1 SYNOPSIS |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
use Config::Model ; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# define configuration tree object |
295
|
|
|
|
|
|
|
my $model = Config::Model->new ; |
296
|
|
|
|
|
|
|
$model ->create_config_class ( |
297
|
|
|
|
|
|
|
name => "MyClass", |
298
|
|
|
|
|
|
|
element => [ |
299
|
|
|
|
|
|
|
[qw/foo bar/] => { |
300
|
|
|
|
|
|
|
type => 'leaf', |
301
|
|
|
|
|
|
|
value_type => 'string' |
302
|
|
|
|
|
|
|
}, |
303
|
|
|
|
|
|
|
baz => { |
304
|
|
|
|
|
|
|
type => 'hash', |
305
|
|
|
|
|
|
|
index_type => 'string' , |
306
|
|
|
|
|
|
|
cargo => { |
307
|
|
|
|
|
|
|
type => 'leaf', |
308
|
|
|
|
|
|
|
value_type => 'string', |
309
|
|
|
|
|
|
|
}, |
310
|
|
|
|
|
|
|
}, |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
], |
313
|
|
|
|
|
|
|
) ; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my $inst = $model->instance(root_class_name => 'MyClass' ); |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
my $root = $inst->config_root ; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# put some data in config tree the hard way |
320
|
|
|
|
|
|
|
$root->fetch_element('foo')->store('yada') ; |
321
|
|
|
|
|
|
|
$root->fetch_element('bar')->store('bla bla') ; |
322
|
|
|
|
|
|
|
$root->fetch_element('baz')->fetch_with_id('en')->store('hello') ; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# put more data the easy way |
325
|
|
|
|
|
|
|
my $steps = 'baz:fr=bonjour baz:hr="dobar dan"'; |
326
|
|
|
|
|
|
|
$root->load( steps => $steps ) ; |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# define leaf call back |
329
|
|
|
|
|
|
|
my $disp_leaf = sub { |
330
|
|
|
|
|
|
|
my ($scanner, $data_ref, $node,$element_name,$index, $leaf_object) = @_ ; |
331
|
|
|
|
|
|
|
$$data_ref .= "disp_leaf called for '". $leaf_object->name. |
332
|
|
|
|
|
|
|
"' value '".$leaf_object->fetch."'\n"; |
333
|
|
|
|
|
|
|
} ; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# simple scanner, (print all values) |
336
|
|
|
|
|
|
|
my $scan = Config::Model::ObjTreeScanner-> new ( |
337
|
|
|
|
|
|
|
leaf_cb => $disp_leaf, # only mandatory parameter |
338
|
|
|
|
|
|
|
) ; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
my $result = ''; |
341
|
|
|
|
|
|
|
$scan->scan_node(\$result, $root) ; |
342
|
|
|
|
|
|
|
print $result ; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=head1 DESCRIPTION |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
This module creates an object that explores (depth first) a |
347
|
|
|
|
|
|
|
configuration tree. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
For each part of the configuration tree, ObjTreeScanner object calls |
350
|
|
|
|
|
|
|
one of the subroutine reference passed during construction. (a call-back |
351
|
|
|
|
|
|
|
or a hook) |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Call-back and hook routines are called: |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=over |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=item * |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
For each node containing elements (including root node) |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item * |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
For each element of a node. This element can be a list, hash, node or |
364
|
|
|
|
|
|
|
leaf element. |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item * |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
For each item contained in a node, hash or list. This item can be a |
369
|
|
|
|
|
|
|
leaf or another node. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=back |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
To continue the exploration, these call-backs must also call the |
374
|
|
|
|
|
|
|
scanner. (i.e. perform another call-back). In other words the user's |
375
|
|
|
|
|
|
|
subroutine and the scanner play a game of ping-pong until the tree is |
376
|
|
|
|
|
|
|
completely explored. |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Hooks routines are not required to resume the exploration, i.e. to call |
379
|
|
|
|
|
|
|
the scanner. This is done once the hook routine has returned. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
The scanner provides a set of default callback for the nodes. This |
382
|
|
|
|
|
|
|
way, the user only have to provide call-backs for the leaves. |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
The scan is started with a call to C<scan_node>. The first parameter |
385
|
|
|
|
|
|
|
of scan_node is a ref that is passed untouched to all call-back. This |
386
|
|
|
|
|
|
|
ref may be used to store whatever result you want. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=head2 new |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
One way or another, the ObjTreeScanner object must be able to find all |
393
|
|
|
|
|
|
|
callback for all the items of the tree. All the possible call-back and |
394
|
|
|
|
|
|
|
hooks are listed below: |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=over |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=item leaf callback: |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
C<leaf_cb> is a catch-all generic callback. All other are specialized |
401
|
|
|
|
|
|
|
call-back : C<enum_value_cb>, C<integer_value_cb>, C<number_value_cb>, |
402
|
|
|
|
|
|
|
C<boolean_value_cb>, C<string_value_cb>, C<uniline_value_cb>, |
403
|
|
|
|
|
|
|
C<reference_value_cb> |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item node callback: |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
C<node_content_cb> , C<node_dispatch_cb> |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=item node hooks: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
C<node_content_hook> |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=item element callback: |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
All these call-backs are called on the elements of a node: |
416
|
|
|
|
|
|
|
C<list_element_cb>, C<check_list_element_cb>, C<hash_element_cb>, |
417
|
|
|
|
|
|
|
C<node_element_cb>, C<node_content_cb>. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=item element hooks: |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
C<list_element_hook>, C<hash_element_hook>. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=back |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
The user may specify all of them by passing a sub ref to the |
426
|
|
|
|
|
|
|
constructor: |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
$scan = Config::Model::ObjTreeScanner-> new |
429
|
|
|
|
|
|
|
( |
430
|
|
|
|
|
|
|
list_element_cb => sub { ... }, |
431
|
|
|
|
|
|
|
... |
432
|
|
|
|
|
|
|
) |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Or use some default callback using the fallback parameter. Note that |
435
|
|
|
|
|
|
|
at least one callback must be provided: C<leaf_cb>. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Optional parameter: |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=over |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item fallback |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
If set to C<node>, the scanner provides default call-back for node |
444
|
|
|
|
|
|
|
items. If set to C<leaf>, the scanner sets all leaf callback (like |
445
|
|
|
|
|
|
|
enum_value_cb ...) to string_value_cb or to the mandatory leaf_cb |
446
|
|
|
|
|
|
|
value. "fallback" callback does not override callbacks provided by the |
447
|
|
|
|
|
|
|
user. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
If set to C<all> , the scanner provides fallbacks for leaf and node. |
450
|
|
|
|
|
|
|
By default, all fallback are provided. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=item auto_vivify |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
Whether to create configuration objects while scanning (default is 1). |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=item check |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
C<yes>, C<no> or C<skip>. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=back |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head1 Callback prototypes |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head2 Leaf callback |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
C<leaf_cb> is called for each leaf of the tree. The leaf callback is |
467
|
|
|
|
|
|
|
called with the following parameters: |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
($scanner, $data_ref,$node,$element_name,$index, $leaf_object) |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
where: |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=over |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=item * |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
C<$scanner> is the scanner object. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=item * |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
C<$data_ref> is a reference that is first passed to the first call of |
482
|
|
|
|
|
|
|
the scanner. Then C<$data_ref> is relayed through the various |
483
|
|
|
|
|
|
|
call-backs |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=item * |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
C<$node> is the node that contain the leaf. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=item * |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
C<$element_name> is the element (or attribute) that contain the leaf. |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=item * |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
C<$index> is the index (or hash key) used to get the leaf. This may |
496
|
|
|
|
|
|
|
be undefined if the element type is scalar. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item * |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
C<$leaf_object> is a L<Config::Model::Value> object. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=back |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head2 List element callback |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
C<list_element_cb> is called on all list element of a node, i.e. call |
507
|
|
|
|
|
|
|
on the list object itself and not in the elements contained in the |
508
|
|
|
|
|
|
|
list. |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
($scanner, $data_ref,$node,$element_name,@indexes) |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
C<@indexes> is a list containing all the indexes of the list. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Example: |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
sub my_list_element_cb { |
517
|
|
|
|
|
|
|
my ($scanner, $data_ref,$node,$element_name,@idx) = @_ ; |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# custom code using $data_ref |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
# resume exploration (if needed) |
522
|
|
|
|
|
|
|
map {$scanner->scan_list($data_ref,$node,$element_name,$_)} @idx ; |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
# note: scan_list and scan_hash are equivalent |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 List element hook |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
C<list_element_hook>: Works like the list element callback. Except that the calls to |
530
|
|
|
|
|
|
|
C<scan_list> are not required. This is done once the hook returns. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head2 Check list element callback |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
C<check_list_element_cb>: Like C<list_element_cb>, but called on a |
535
|
|
|
|
|
|
|
check_list element. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
($scanner, $data_ref,$node,$element_name, index, check_list_obj) |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
C<index> is always undef as a check_list cannot be contained in a hash or list (yet) |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head2 Hash element callback |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
C<hash_element_cb>: Like C<list_element_cb>, but called on a |
544
|
|
|
|
|
|
|
hash element. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
($scanner, $data_ref,$node,$element_name,@keys) |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
C<@keys> is an list containing all the keys of the hash. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
Example: |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
sub my_hash_element_cb { |
553
|
|
|
|
|
|
|
my ($scanner, $data_ref,$node,$element_name,@keys) = @_ ; |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# custom code using $data_ref |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# resume exploration |
558
|
|
|
|
|
|
|
map {$scanner->scan_hash($data_ref,$node,$element_name,$_)} @keys ; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 Hash element hook |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
C<hash_element_hook>: Works like the hash element callback. Except that the calls to |
564
|
|
|
|
|
|
|
C<scan_hash> are not required. This is done once the hook returns. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head2 Node content callback |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
C<node_content_cb>: This call-back is called foreach node (including |
569
|
|
|
|
|
|
|
root node). |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
($scanner, $data_ref,$node,@element_list) |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
C<@element_list> contains all the element names of the node. |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Example: |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
sub my_content_cb { |
578
|
|
|
|
|
|
|
my ($scanner, $data_ref,$node,@element) = @_ ; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
# custom code using $data_ref |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# resume exploration |
583
|
|
|
|
|
|
|
map {$scanner->scan_element($data_ref, $node,$_)} @element ; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=head2 Node content hook |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
C<node_content_hook>: This hook is called foreach node (including |
589
|
|
|
|
|
|
|
root node). Works like the node content call-back. Except that the calls to |
590
|
|
|
|
|
|
|
C<scan_element> are not required. This is done once the hook returns. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 Dispatch node callback |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
C<node_dispatch_cb>: Any callback specified in the hash is called for |
595
|
|
|
|
|
|
|
each instance of the specified configuration class. |
596
|
|
|
|
|
|
|
(this may include the root node). |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
For instance, if you have: |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
node_dispach_cb => { |
601
|
|
|
|
|
|
|
ClassA => \&my_class_a_dispatch_cb, |
602
|
|
|
|
|
|
|
ClassB => \&my_class_b_dispatch_cb, |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
C<&my_class_a_dispatch_cb> is called for each instance of C<ClassA> and |
606
|
|
|
|
|
|
|
C<&my_class_b_dispatch_cb> is called for each instance of C<ClassB>. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
They is called with the following parameters: |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
($scanner, $data_ref,$node,@element_list) |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
C<@element_list> contains all the element names of the node. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Example: |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
sub my_class_a_dispatch_cb = { |
617
|
|
|
|
|
|
|
my ($scanner, $data_ref,$node,@element) = @_ ; |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
# custom code using $data_ref |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# resume exploration |
622
|
|
|
|
|
|
|
map {$scanner->scan_element($data_ref, $node,$_)} @element ; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head2 Node element callback |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
C<node_element_cb> is called for each node contained within a node |
628
|
|
|
|
|
|
|
(i.e not with root node). This node can be held by a plain element or |
629
|
|
|
|
|
|
|
a hash element or a list element: |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
($scanner, $data_ref,$node,$element_name,$key, $contained_node) |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
C<$key> may be undef if C<$contained_node> is not a part of a hash or |
634
|
|
|
|
|
|
|
a list. C<$element_name> and C<$key> specifies the element name and |
635
|
|
|
|
|
|
|
key of the the contained node you want to scan. (passed with |
636
|
|
|
|
|
|
|
C<$contained_node>) Note that C<$contained_node> may be undef if |
637
|
|
|
|
|
|
|
C<auto_vivify> is 0. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Example: |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
sub my_node_element_cb { |
642
|
|
|
|
|
|
|
my ($scanner, $data_ref,$node,$element_name,$key, $contained_node) = @_; |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# your custom code using $data_ref |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# explore next node |
647
|
|
|
|
|
|
|
$scanner->scan_node($data_ref,$contained_node); |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=head1 METHODS |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=head2 scan_node |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
Parameters: C<< ($data_r,$node) >> |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Explore the node and call either C<node_dispatch_cb> (if the node class |
657
|
|
|
|
|
|
|
name matches the dispatch_node hash) B<or> (e.g. xor) C<node_element_cb> passing |
658
|
|
|
|
|
|
|
all element names. |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
C<up_cb> is called once the first callback returns. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=head2 scan_element |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Parameters: C<< ($data_r,$node,$element_name) >> |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Explore the element and call either C<hash_element_cb>, |
667
|
|
|
|
|
|
|
C<list_element_cb>, C<node_content_cb> or a leaf call-back (the leaf |
668
|
|
|
|
|
|
|
call-back called depends on the Value object properties: enum, string, |
669
|
|
|
|
|
|
|
integer and so on) |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head2 scan_hash |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
Parameters: C<< ($data_r,$node,$element_name,$key) >> |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Explore the hash member (or hash value) and call either C<node_content_cb> or |
676
|
|
|
|
|
|
|
a leaf call-back. |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=head2 scan_list |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Parameters: C<< ($data_r,$node,$element_name,$index) >> |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
Just like C<scan_hash>: Explore the list member and call either |
683
|
|
|
|
|
|
|
C<node_content_cb> or a leaf call-back. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head2 get_keys |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Parameters: C<< ($node, $element_name) >> |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Returns an list containing the sorted keys of a hash element or returns |
690
|
|
|
|
|
|
|
an list containing (0.. last_index) of an list element. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Throws an exception if element is not an list or a hash element. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head1 AUTHOR |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
Dominique Dumont, (ddumont at cpan dot org) |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=head1 SEE ALSO |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
L<Config::Model>,L<Config::Model::Node>,L<Config::Model::Instance>, |
701
|
|
|
|
|
|
|
L<Config::Model::HashId>, |
702
|
|
|
|
|
|
|
L<Config::Model::ListId>, |
703
|
|
|
|
|
|
|
L<Config::Model::CheckList>, |
704
|
|
|
|
|
|
|
L<Config::Model::Value> |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head1 AUTHOR |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Dominique Dumont |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
This software is Copyright (c) 2005-2022 by Dominique Dumont. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
This is free software, licensed under: |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
The GNU Lesser General Public License, Version 2.1, February 1999 |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
=cut |