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