File Coverage

blib/lib/Config/Hierarchical.pm
Criterion Covered Total %
statement 53 821 6.4
branch 1 440 0.2
condition 4 84 4.7
subroutine 16 57 28.0
pod n/a
total 74 1402 5.2


line stmt bran cond sub pod time code
1              
2             package Config::Hierarchical ;
3 9     9   444991 use base Exporter ;
  9         25  
  9         753  
4              
5 9     9   52 use strict;
  9         19  
  9         302  
6 9     9   49 use warnings ;
  9         22  
  9         314  
7              
8             BEGIN 
9             {
10 9     9   43 use Exporter ();
  9         13  
  9         233  
11              
12 9     9   49 use vars qw ($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
  9         14  
  9         890  
13              
14 9     9   22 $VERSION     = '0.13' ;
15 9         20 @EXPORT_OK   = qw ();
16 9         219 %EXPORT_TAGS = ();
17             }
18              
19             #-------------------------------------------------------------------------------
20              
21 9     9   50 use Carp ;
  9         16  
  9         613  
22 9     9   20917 use Data::Compare;
  9         110947  
  9         67  
23 9     9   45963 use Sub::Install;
  9         16203  
  9         58  
24              
25 9     9   9267 use English qw( -no_match_vars ) ;
  9         42385  
  9         85  
26              
27 9     9   12601 use Readonly ;
  9         29836  
  9         22286  
28             Readonly my $EMPTY_STRING => q{} ;
29              
30             Readonly my $VALID_OPTIONS =>
31             { 
32             map{$_ => 1}
33             qw(
34             NAME VALUE
35             EVAL EVALUATOR
36             HISTORY ATTRIBUTE
37             COMMENT
38             CATEGORY CATEGORIES_TO_EXTRACT_FROM
39             GET_CATEGORY WARN_FOR_EXPLICIT_CATEGORY
40             SET_VALIDATOR
41             VALIDATORS
42             CHECK_LOWER_LEVEL_CATEGORIES
43             LOCK FORCE_LOCK
44             OVERRIDE
45             SILENT_NOT_EXISTS SILENT_OVERRIDE
46             DIE_NOT_EXISTS
47             VERBOSE
48             FILE LINE
49             ALIAS_CATEGORY
50             DATA_TREEDUMPER_OPTIONS
51             )
52             } ;
53              
54             Readonly my $CONSTRUCTOR_VALID_OPTIONS =>
55             { 
56             map{$_ => 1}
57             qw(
58             LOG_ACCESS
59             INITIAL_VALUES
60             CATEGORY_NAMES
61             DEFAULT_CATEGORY
62             INTERACTION
63             DISABLE_SILENT_OPTIONS
64             LOCKED_CATEGORIES
65             GET_CATEGORIES
66             )
67             } ;
68              
69             #-------------------------------------------------------------------------------
70              
71             =head1 NAME
72            
73             Config::Hierarchical - Hierarchical configuration container
74            
75             =head1 SYNOPSIS
76            
77             use Config::Hierarchical ;
78            
79             my $config = new Config::Hierarchical();
80            
81             # or
82            
83             my $config = new Config::Hierarchical
84             (
85             NAME => 'some_namespace',
86             VERBOSE => 0,
87             DISABLE_SILENT_OPTIONS => 0,
88             CATEGORY_NAMES => ['<CLI>', '<PBS>', 'PARENT', 'LOCAL', 'CURRENT'],
89             DEFAULT_CATEGORY => 'CURRENT',
90            
91             WARN_FOR_EXPLICIT_CATEGORY => 0,
92            
93             GET_CATEGORIES =>
94             {
95             Inheritable => ['CLI', 'PBS', 'PARENT', 'CURRENT'],
96             },
97            
98             INTERACTION =>
99             {
100             INFO => \&sub,
101             WARN => \&sub,
102             DIE => \&sub,
103             DEBUG => \&sub,
104             },
105            
106             SET_VALIDATOR => \&my_set_validator,
107            
108             VALIDATORS =>
109             [
110             {
111             CATEGORY_NAMES => ['CLI', 'CURRENT',] ,
112             NAMES => ['CC', 'LD'],
113             VALIDATORS =>
114             {
115             alphanumeric => \&alphanumeric,
116             other_validator => \&other_validator,
117             },
118             },
119            
120             {
121             CATEGORY_NAMES => ['CURRENT',] ,
122             NAMES => ['CC',],
123             VALIDATORS => {only_gcc => \&only_gcc,},
124             },
125             ],
126            
127             INITIAL_VALUES =>
128             [
129             {
130             CATEGORY => 'PBS',
131             ALIAS_CATEGORY => $pbs_config,
132             HISTORY => ....,
133             COMMENT => ....,
134             },
135            
136             {CATEGORY => 'CLI', NAME => 'CC', VALUE => 1,},
137             {CATEGORY => 'CLI', NAME => 'LD', VALUE => 2, LOCK => 1},
138            
139             {CATEGORY => 'CURRENT', NAME => 'CC', VALUE => 3, OVERRIDE => 1},
140             {CATEGORY => 'CURRENT', NAME => 'AS', VALUE => 4,},
141             {CATEGORY => 'CURRENT', NAME => 'VARIABLE_WITH_HISTORY', VALUE => $previous_value, HISTORY => $history },
142             ] ,
143            
144             LOCKED_CATEGORIES => ['CLI'],
145             ) ;
146            
147             $config->Set(NAME => 'CC', VALUE => 'gcc') ;
148             $config->Set(NAME => 'CC', VALUE => 'gcc', CATEGORY => 'CLI') ;
149             $config->Set(NAME => 'CC', VALUE => 'gcc', FORCE_LOCK => 1) ;
150             $config->Set(NAME => 'CC', VALUE => 'gcc', SILENT_OVERRIDE => 1, COMMENT => 'we prefer gcc') ;
151            
152             $config->Exists(NAME => 'CC') ;
153            
154             $config->GetKeyValueTuples() ;
155            
156             $config->SetMultiple
157             (
158             {FORCE_LOCK => 1}
159             {NAME => 'CC', VALUE => 'gcc', SILENT_OVERRIDE => 1},
160             {NAME => 'LD', VALUE => 'ld'},
161             ) ;
162            
163             $config->Set(CC => 'gcc') ;
164            
165             $value = $config->Get(NAME => 'CC') ;
166             $value = $config->Get(NAME => 'NON_EXISTANT', SILENT_NOT_EXISTS => 1) ;
167            
168             @values = $config->GetMultiple(@config_variables_names) ;
169             @values = $config->GetMultiple({SILENT_NOT_EXISTS => 1}, @config_variables_names) ;
170            
171             $hash_ref = $config->GetHashRef() ; # no warnings
172            
173             $config->GetInheritable() ;
174            
175             $config->SetDisableSilentOptions(1) ;
176            
177             $config->LockCategories('PBS') ;
178             $config->UnlockCategories('CLI', 'PBS') ;
179             $config->IsCategoryLocked('PBS') ;
180            
181             $config->Lock(NAME => 'CC') ;
182             $config->Unlock(NAME => 'CC', CATEGORY => 'CLI') ;
183             $config->IsLocked(NAME => 'CC') ;
184            
185             $history = $config->GetHistory(NAME => 'CC') ;
186             $dump = $config->GetDump() ;
187            
188            
189             =head1 DESCRIPTION
190            
191             This module implements a configuration variable container. The container has multiple categories which are
192             declared in decreasing priority order.
193            
194             A variable can exist in multiple categories within the container. When queried for a variable, the container
195             will return the variable in the category with the highest priority.
196            
197             When setting a variable, the container will display a warning message if it is set in a category with lower priority
198             than a category already containing the same variable.
199            
200             Priority overriding is also possible.
201            
202             =head1 DOCUMENTATION
203            
204             I'll start by giving a usage example. In a build system, configuration variables can have different source.
205            
206             =over 2
207            
208             =item * the build tool
209            
210             =item * the command line
211            
212             =item * the parent build file (in a hierarchical build system)
213            
214             =item * the current build file
215            
216             =back
217            
218             It is likely that a configuration variable set on the command line should be used regardless of a local
219             setting. Also, a configuration variable set by the build tool itself should have the highest priority.
220            
221             Among the most difficult errors to find are configuration errors in complex build systems. Build tools
222             generally don't help much when variables are overridden. it's also difficult to get a variable's history.
223            
224             This module provides the necessary functionality to handle most of the cases needed in a modern build system.
225            
226             Test t/099_cookbook.t is also a cookbook you can generate with POD::Tested. It's a nice complement to this
227             documentation.
228            
229             =head1 SUBROUTINES/METHODS
230            
231             Subroutines that are not part of the public interface are marked with [p].
232            
233             =cut
234              
235             #-------------------------------------------------------------------------------
236              
237             sub new
238             {
239              
240             =head2 new(@named_arguments)
241            
242             Create a Config::Hierarchical .
243            
244             my $config = new Config::Hierarchical() ;
245            
246             I<Arguments>
247            
248             The arguments are named. All argument are optional. The order is not important.
249            
250             my $config = new Config::Hierarchical(NAME => 'some_namespace', VERBOSE => 1) ;
251            
252             =over 2
253            
254             =item * NAME
255            
256             A string that will be used in all the dumps and interaction with the user.
257            
258             =item * CATEGORY_NAMES
259            
260             A list of category names. The first named category has the highest priority.
261             Only categories listed in this list can be manipulated. Using an unregistered
262             category in a C<Set> or C<Get> operation will generate an error.
263            
264             my $config = new Config::Hierarchical
265             (
266             CATEGORY_NAMES => ['CLI', '<PBS>', 'PARENT', 'CURRENT', 'LOCAL'],
267             DEFAULT_CATEGORY => 'CURRENT',
268             ) ;
269            
270             A category can be B<protected> by enclosing its name in angle bracket, IE: B<<PBS>>. Protected
271             categories will not be overridden by lesser priority categories even if the OVERRIDE option is used.
272            
273             If no category names are given, B<'CURRENT'> will be used and B<DEFAULT_CATEGORY> will
274             be set accordingly.
275            
276             =item * DEFAULT_CATEGORY
277            
278             The name of the category used when C<Set> is called without a I<CATEGORY> argument.
279            
280             If the B<CATEGORY_NAMES> list contains more than one entry, B<DEFAULT_CATEGORY> must be set or
281             an error will be generated.
282            
283             =item * DIE_NOT_EXISTS
284            
285             my $config = new Config::Hierarchical(..., DIE_NOT_EXISTS => 0) ;
286            
287             Calling L<Get> on an unexisting variable will generate an exception when this option is set. The option
288             is not set by default.
289            
290             =item * DISABLE_SILENT_OPTIONS
291            
292             my $config = new Config::Hierarchical(NAME => 'some_namespace', DISABLE_SILENT_OPTIONS => 1) ;
293            
294             When this option is set, B<SILENT_OVERRIDE> and B<SILENT_NOT_EXISTS> will be ignored and
295             B<Config::Hierarchical> will display a warning.
296            
297             =item * GET_CATEGORIES
298            
299             This option allows you to define functions that fetch variables in a specific category
300             list and in a specific order.
301            
302             my $config = new Config::Hierarchical
303             (
304             CATEGORY_NAMES => ['CLI', '<PBS>', 'PARENT', 'CURRENT', 'LOCAL'],
305            
306             GET_CATEGORIES =>
307             {
308             Inheritable => ['CLI', 'PBS', 'PARENT', 'CURRENT'],
309             }
310             ...
311             ) ;
312            
313             my $value = $config->GetInheritable(NAME => 'CC') ;
314             my $hash_ref = $config->GetInheritableHashRef() ;
315            
316            
317             In the example above, the B<LOCAL> category will not be used by B<GetInheritable>.
318            
319             =item * WARN_FOR_EXPLICIT_CATEGORY
320            
321             if set, B<Config::Hierarchical> will display a warning if any category is specified in C<Get> or C<Set>.
322            
323             =item * VERBOSE
324            
325             This module will display information about its actions when this option is set.
326            
327             See B<INTERACTION> and C<SetDisplayExplicitCategoryWarningOption>.
328            
329             =item * INTERACTION
330            
331             Lets you define subs used to interact with the user.
332            
333             my $config = new Config::Hierarchical
334             (
335             INTERACTION =>
336             {
337             INFO => \&sub,
338             WARN => \&sub,
339             DIE => \&sub,
340             DEBUG => \&sub,
341             }
342             ) ;
343            
344             =over 4
345            
346             =item INFO
347            
348             This sub will be used when displaying B<verbose> information.
349            
350             =item WARN
351            
352             This sub will be used when a warning is displayed. e.g. a configuration that is refused or an override.
353            
354             =item DIE
355            
356             Used when an error occurs. E.g. a locked variable is set.
357            
358             =item DEBUG
359            
360             If this option is set, Config::Hierarchical will call the sub before and after acting on the configuration.
361             This can act as a breakpoint in a debugger or allows you to pinpoint a configuration problem.
362            
363             =back
364            
365             The functions default to:
366            
367             =over 2
368            
369             =item * INFO => CORE::print
370            
371             =item * WARN => Carp::carp
372            
373             =item * DIE => Carp::confess
374            
375             =back
376            
377             =item * FILE and LINE
378            
379             These will be used in the information message and the history information if set. If not set, the values
380             returned by I<caller> will be used. These options allow you to write wrapper functions that report the
381             callers location properly.
382            
383             =item * INITIAL_VALUES
384            
385             Lets you initialize the Config::Hierarchical object. Each entry will be passed to C<Set>.
386            
387             my $config = new Config::Hierarchical
388             (
389             ...
390            
391             EVALUATOR => \&sub,
392            
393             INITIAL_VALUES =>
394             [
395             { # aliased category
396             CATEGORY => 'PBS',
397             ALIAS_CATEGORY => $pbs_config,
398             HISTORY => ....,
399             COMMENT => ....,
400             },
401            
402             {CATEGORY => 'CLI', NAME => 'CC', VALUE => 1},
403             {CATEGORY => 'CLI', NAME => 'LD', VALUE => 2, LOCK => 1},
404            
405             {CATEGORY => 'CURRENT', NAME => 'CC', VALUE => 3, OVERRIDE => 1},
406             {CATEGORY => 'CURRENT', NAME => 'AS', VALUE => 4,},
407             } ,
408             ) ;
409            
410             See C<Set> for options to B<INITIAL_VALUES> and a details explanation about B<EVALUATOR>.
411            
412             B<Aliased categories> allow you to use a category to refer to an existing Config::Hierarchical object.
413             The referenced object is read only. This is because multiple configurations might alias to the same
414             B<Config::Hierarchical> object.
415            
416             Variables from aliased category can still be overridden.
417            
418             =item * LOG_ACCESS
419            
420             If this set, B<Config::Hierarchical> will log all access made through C<Get>.
421            
422             =item * LOCKED_CATEGORIES
423            
424             Lets you lock categories making them read only. Values in B<INITIAL_VALUES> are used before locking
425             the category.
426            
427             my $config = new Config::Hierarchical(..., LOCKED_CATEGORIES => ['CLI', 'PBS']) ;
428            
429             See C<LockCategories> and C<IsCategoryLocked>.
430            
431             =item * SET_VALIDATOR
432            
433             This gives you full control over what gets into the config. Pass a sub reference that will be used to check
434             the configuration variable passed to the subroutine C<Set>.
435            
436             Argument passed to the subroutine reference:
437            
438             =over 4
439            
440             =item $config
441            
442             The configuration object. Yous should use the objects interaction subs for message display.
443            
444             =item $options
445            
446             The options passed to C<Set>.
447            
448             =item $location
449            
450             The location where C<Set> was called. Useful when displaying an error message.
451            
452             =back
453            
454             sub my_set_validator
455             {
456             my ($config, $options, $location) = @_ ;
457            
458             # eg, check the variable name
459             if($options->{NAME} !~ /^CFG_[A-Z]+/)
460             {
461             $config->{INTERACTION}{DIE}->("$config->{NAME}: Invalid variable name '$options->{NAME}' at at '$location'!")
462             }
463            
464             # all OK, let Config::Hierarchical handle variable setting
465             }
466            
467             my $config = new Config::Hierarchical(SET_VALIDATOR => \&my_set_validator) ;
468            
469             =item * VALIDATORS
470            
471             my $config = new Config::Hierarchical
472             (
473             ...
474             VALIDATORS =>
475             [
476             {
477             CATEGORY_NAMES => ['CURRENT', 'OTHER'] ,
478             NAMES => ['CC', 'LD'],
479             VALIDATORS =>
480             {
481             validator_name => \&PositiveValueValidator,
482             other_validator => \&SecondValidator
483             },
484             },
485             ],
486             ) ;
487            
488             Let you add validation subs to B<Config::Hierarchical> for specific variables.
489            
490             Each variable in I<NAMES> in each category in I<CATEGORY_NAMES> will be assigned the validators
491             defined in I<Validators>.
492            
493             The example above will add a validator I<PositiveValueValidator> and validator I<SecondValidator> to
494             B<CURRENT::CC>, B<CURRENT::LD>, B<OTHER::CC> and B<OTHER::LD>.
495            
496             A validator is sub that will be called every time a value is assigned to a variable. The sub is passed a single argument, the
497             value to be assigned to the variable. If false is returned by any of the validators, an Exception will be raised through
498             B<INTERACTION::DIE>.
499            
500             see C<AddValidator>.
501            
502             =back
503            
504             =cut
505              
506 1     1   20 my ($invocant, @setup_data) = @_ ;
507              
508 1   33     9 my $class = ref($invocant) || $invocant ;
509              
510 1 50       4 confess 'Invalid constructor call!' unless defined $class ;
511              
512 1         3 my $self = {} ;
513              
514 1         11 my ($package, $file_name, $line) = caller() ;
515 1         36 bless $self, $class ;
516              
517 1         6 $self->Setup($package, $file_name, $line, @setup_data) ;
518              
519 0         0 return($self) ;
520             }
521              
522             #-------------------------------------------------------------------------------
523              
524             sub GetInformation
525             {
526              
527             =head2 GetInformation()
528            
529             I<Arguments> - None
530            
531             I<Returns>
532            
533             =over 2
534            
535             =item * The configuration name
536            
537             =item * The configuration object's creation location
538            
539             =back
540            
541             =cut
542              
543 0     0   0 my ($self) = @_ ;
544              
545 0         0 return($self->{NAME}, "$self->{FILE}:$self->{LINE}") ;
546             }
547              
548             #-------------------------------------------------------------------------------
549              
550             sub Setup
551             {
552              
553             =head2 [p] Setup
554            
555             Helper sub called by new. This shall not be used directly.
556            
557             =cut
558              
559 1     1   3 my ($self, $package, $file_name, $line, @setup_data) = @_ ;
560              
561 1         5 SetInteractionDefault($self) ;
562 1         311 $self->CheckOptionNames
563             (
564 1         2 { %{$VALID_OPTIONS}, %{$CONSTRUCTOR_VALID_OPTIONS}},
  0         0  
565             @setup_data,
566             NAME => 'Anonymous eval context', FILE => $file_name, LINE => $line,
567             ) ;
568              
569 0         0 %{$self} =
  0         0  
570             (
571             NAME                   => 'Anonymous',
572             CATEGORY_NAMES         => ['CURRENT'],
573             DISABLE_SILENT_OPTIONS => 0,
574             FILE                   => $file_name,
575             LINE                   => $line,
576            
577             @setup_data,
578            
579             CATEGORIES             => {},
580             TIME_STAMP             => 0,
581             ) ;
582              
583 0         0 SetInteractionDefault($self) ;
584              
585 0         0 my $location = "$self->{FILE}:$self->{LINE}" ;
586              
587 0 0       0 if($self->{VERBOSE})
588             {
589 0         0 $self->{INTERACTION}{INFO}('Creating ' . ref($self) . " '$self->{NAME}' at $location.\n") ;
590             }
591              
592 0         0 $self->SetupCategories($location) ;
593              
594 0 0       0 if(exists $self->{VALIDATORS})
595             {
596 0         0 $self->AddValidators($self->{VALIDATORS}, $location) ;
597 0         0 delete $self->{VALIDATORS} ;
598             }
599              
600 0 0       0 if(exists $self->{SET_VALIDATOR})
601             {
602 0 0       0 if('CODE' ne ref $self->{SET_VALIDATOR})
603             {
604 0         0 $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid 'SET_VALIDATOR' definition, expecting a sub reference at '$location'!") ;
605             }
606             }
607              
608 0 0       0 if(exists $self->{EVALUATOR})
609             {
610 0 0       0 if('CODE' ne ref $self->{EVALUATOR})
611             {
612 0         0 $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid 'EVALUATOR' definition, expecting a sub reference at '$location'!") ;
613             }
614             }
615              
616             # temporarely remove the locked categories till we have handled INITIAL_VALUES
617 0         0 my $category_locks ;
618              
619 0 0       0 if(exists $self->{LOCKED_CATEGORIES})
620             {
621 0 0       0 if('ARRAY' ne ref $self->{LOCKED_CATEGORIES})
622             {
623 0         0 $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid 'LOCKED_CATEGORIES' at '$location'!") ;
624             }
625            
626 0         0 $category_locks = $self->{LOCKED_CATEGORIES} ;
627 0         0 delete $self->{LOCKED_CATEGORIES} ;
628             }
629            
630 0 0       0 if(exists $self->{INITIAL_VALUES})
631             {
632 0         0 for my $element_data (@{$self->{INITIAL_VALUES}})
  0         0  
633             {
634 0 0       0 if(exists $element_data->{ALIAS_CATEGORY})
635             {
636 0         0 $self->SetCategoryAlias(FILE => $self->{FILE}, LINE => $self->{LINE}, %{$element_data}) ;
  0         0  
637             }
638             else
639             {
640 0         0 $self->Set(FILE => $self->{FILE}, LINE => $self->{LINE}, %{$element_data}) ;
  0         0  
641             }
642             }
643            
644 0         0 delete $self->{INITIAL_VALUES} ;
645            
646 0 0       0 if(defined $category_locks)
647             {
648             #TODO: should be a category attribute not a config attribute
649 0         0 $self->{LOCKED_CATEGORIES}  = { map {$_ => 1} @{$category_locks} } ;
  0         0  
  0         0  
650             }
651             }
652            
653 0 0       0 CreateCustomGetFunctions(keys %{ $self->{GET_CATEGORIES} }) if exists $self->{GET_CATEGORIES} ;
  0         0  
654              
655 0         0 return(1) ;
656             }
657              
658             #-------------------------------------------------------------------------------
659              
660             sub SetInteractionDefault
661             {
662            
663             =head2 [p] SetInteractionDefault
664            
665             Sets {INTERACTION} fields that are not set by the user.
666            
667             =cut
668              
669 1     1   3 my ($interaction_container) = @_ ;
670              
671 1   50 0   26 $interaction_container->{INTERACTION}{INFO} ||= sub {print @_} ; ## no critic (InputOutput::RequireCheckedSyscalls)
  0         0  
672 1   50     9 $interaction_container->{INTERACTION}{WARN} ||= \&Carp::carp ;
673 1   50     8 $interaction_container->{INTERACTION}{DIE}  ||= \&Carp::confess ;
674              
675 1         3 return ;
676             }
677              
678             #-------------------------------------------------------------------------------
679              
680             sub SetupCategories
681             {
682              
683             =head2 [p] SetupCategories
684            
685             Helper sub called by new.
686            
687             =cut
688              
689 0     0     my ($self, $location) = @_ ;
690              
691             # find the protected categories and removes the brackets from the name
692 0 0         $self->{PROTECTED_CATEGORIES} = { map{ if(/^<(.*)>$/sxm) {$1 => 1} else {} } @{ $self->{CATEGORY_NAMES} } } ; ## no critic (BuiltinFunctions::ProhibitComplexMappings)
  0            
  0            
  0            
693              
694 0           my @seen_categories ;
695 0           for my $name (@{$self->{CATEGORY_NAMES}})
  0            
696             {
697 0 0         if($name =~ /^<(.*)>$/sxm)
698             {
699 0           my $name_without_brackets = $1 ;
700            
701 0 0         if($name_without_brackets =~ /<|>/sxm)
702             {
703 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category name '$name_without_brackets' at '$location'!") ;
704             }
705             else
706             {
707 0           $name = $1 ;
708             }
709             }
710            
711             # create a list of higher level categories to avoid computing it at run time
712 0           $self->{REVERSED_HIGHER_LEVEL_CATEGORIES}{$name} = [reverse @seen_categories] ;
713 0           push @seen_categories, $name ;
714             }
715              
716 0           $self->{VALID_CATEGORIES} = { map{$_ => 1} @{$self->{CATEGORY_NAMES}}} ;
  0            
  0            
717              
718             # set and check the default category
719 0 0         if(1 == @{$self->{CATEGORY_NAMES}})
  0            
720             {
721 0           $self->{DEFAULT_CATEGORY} = @{$self->{CATEGORY_NAMES}}[0] ;
  0            
722             }
723             else
724             {
725 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: No default category at '$location'!") unless exists $self->{DEFAULT_CATEGORY} ;
726             }
727              
728 0 0         unless(exists $self->{VALID_CATEGORIES}{$self->{DEFAULT_CATEGORY}})
729             {
730 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid default category '$self->{DEFAULT_CATEGORY}' at '$location'!") ;
731             }
732            
733 0           return(1) ;
734             }
735              
736             #-------------------------------------------------------------------------------
737              
738             sub AddValidator
739             {
740            
741             =head2 AddValidator(CATEGORY_NAMES => \@categories, NAMES => \@names, VALIDATORS => \%validators)
742            
743             $config->AddValidator
744             (
745             CATEGORY_NAMES => ['CLI'] ,
746             NAMES => ['CC', 'LD'],
747             VALIDATORS => {positive_value => \&PositiveValueValidator},
748             ) ;
749            
750             You can add validators after creating a configuration and even after adding variables to your configuration. The
751             existing variables will be checked when the validators are added.
752            
753             I<Arguments>
754            
755             =over 2
756            
757             =item * CATEGORY_NAMES => \@catagories - A reference to an array containing the names of the categories to add the validators to
758            
759             =item * NAMES => \@names - A reference to an array containing the names of the variables that will be validated
760            
761             =item * VALIDATORS => \%validators - A reference to a hash where keys are validator_names and values are validator code references
762            
763             =back
764            
765             I<Returns> - Nothing
766            
767             B<Config::Hierarchical> will warn you if you override a validator.
768            
769             =cut
770              
771 0     0     my ($self, %setup) = @_ ;
772              
773 0           my ($package, $file_name, $line) = caller() ;
774 0           my $location = "$self->{FILE}:$self->{LINE}" ;
775              
776 0           $self->AddValidators([{%setup}], $location) ;
777              
778 0           return(1) ;
779             }
780              
781             #-------------------------------------------------------------------------------
782              
783             Readonly my $EXPECTED_NUMBER_OF_ARGUMENTS_FOR_ADD_VALIDATOR => 3 ;
784              
785             sub AddValidators
786             {
787            
788             =head2 [p] AddValidators
789            
790             =cut
791              
792 0     0     my ($self, $validators, $location) = @_ ;
793              
794 0           for my $validator_definition (@{$validators})
  0            
795             {
796 0 0 0       if
      0        
      0        
      0        
      0        
      0        
      0        
797             (
798 0           'HASH' ne ref $validator_definition
799             ||  $EXPECTED_NUMBER_OF_ARGUMENTS_FOR_ADD_VALIDATOR != keys %{$validator_definition}
800            
801             || ! exists $validator_definition->{CATEGORY_NAMES}
802             || 'ARRAY' ne ref $validator_definition->{CATEGORY_NAMES}
803            
804             || ! exists $validator_definition->{NAMES}
805             || 'ARRAY' ne ref $validator_definition->{NAMES}
806            
807             || ! exists $validator_definition->{VALIDATORS}
808             || 'HASH' ne ref $validator_definition->{VALIDATORS}
809             )
810             {
811 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid validator definition at '$location'!")  ;
812             }
813            
814 0           for my $category_name (@{$validator_definition->{CATEGORY_NAMES}})
  0            
815             {
816 0 0         unless(exists $self->{VALID_CATEGORIES}{$category_name})
817             {
818 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category_name' in validator setup at '$location'!") ;
819             }
820            
821 0           for my $variable_name (@{$validator_definition->{NAMES}})
  0            
822             {
823 0           $self->AddVariableValidator($category_name, $variable_name, $validator_definition, $location) ;
824             }
825             }
826             }
827            
828 0           return(1) ;
829             }
830              
831             #-------------------------------------------------------------------------------
832              
833             sub AddVariableValidator
834             {
835            
836             =head2 [p] AddVariableValidator
837            
838            
839             =cut
840              
841 0     0     my ($self, $category_name, $variable_name, $validator_definition, $location) = @_ ;
842              
843 0           for my $validator (keys %{$validator_definition->{VALIDATORS}})
  0            
844             {
845 0           my ($config_variable_value_exists, $config_variable_value) ;
846              
847 0 0         if(exists $self->{ALIASED_CATEGORIES}{$category_name})
848             {
849 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't Add validator '$validator' to aliased category '${category_name}'at '$location'.\n") ;
850             }
851            
852 0 0         if($self->{VERBOSE})
853             {
854 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Adding validator '$validator' defined at '$location' to '${category_name}::$variable_name'.\n") ;
855             }
856            
857 0 0         if(exists $self->{CATEGORIES}{$category_name}{$variable_name})
858             {
859 0 0         if(exists $self->{CATEGORIES}{$category_name}{$variable_name}{VALUE})
860             {
861 0           $config_variable_value_exists++ ;
862 0           $config_variable_value = $self->{CATEGORIES}{$category_name}{$variable_name}{VALUE} ;
863             }
864             }
865             else
866             {
867 0           $self->{CATEGORIES}{$category_name}{$variable_name} = {} ;
868             }
869              
870 0           my $config_variable = $self->{CATEGORIES}{$category_name}{$variable_name} ;
871              
872 0 0         if('CODE' ne ref $validator_definition->{VALIDATORS}{$validator})
873             {
874 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid validator '$validator' (must be a code reference) at '$location'!") ;
875             }
876            
877 0 0         if(exists $config_variable->{VALIDATORS}{$validator})
878             {
879 0           $self->{INTERACTION}{WARN}->
880             (
881             "$self->{NAME}: Overriding variable '$variable_name' validator '$validator' "
882             . '(originaly defined at ' . $config_variable->{VALIDATORS}{$validator}{ORIGIN} . ') '
883             . "at '$location'!"
884             ) ;
885             }
886            
887 0           $config_variable->{VALIDATORS}{$validator}{ORIGIN} = $location ;
888 0           $config_variable->{VALIDATORS}{$validator}{SUB} = $validator_definition->{VALIDATORS}{$validator} ;
889              
890 0 0         if($config_variable_value_exists)
891             {
892             # check already existing value
893            
894 0 0         unless($validator_definition->{VALIDATORS}{$validator}->($config_variable_value))
895             {
896 0           $self->{INTERACTION}{DIE}->
897             ("$self->{NAME}: Invalid value '$config_variable_value' for variable '$variable_name'. Validator '$validator' defined at '$location'.\n") ;
898             }
899             }
900             }
901              
902 0           return(1) ;
903             }
904              
905             #-------------------------------------------------------------------------------
906              
907             sub SetCategoryAlias
908             {
909            
910             =head2 [p] SetCategoryAlias
911            
912             Used to handle category aliases.
913            
914             my $pbs_config = new Config::Hierarchical(...) ;
915            
916             my $config = new Config::Hierarchical
917             (
918             NAME => 'some_namespace',
919             CATEGORY_NAMES => ['<CLI>', '<PBS>', 'PARENT', 'LOCAL', 'CURRENT'],
920            
921             INITIAL_VALUES =>
922             [
923             {
924             CATEGORY => 'PBS',
925             ALIAS_CATEGORY => $pbs_config,
926             HISTORY => ....,
927             COMMENT => ....,
928             },
929             {NAME => 'CC1', VALUE => 'gcc'},
930             ...
931             ] ,
932            
933             ) ;
934            
935             B<CATEGORY> and B<ALIAS_CATEGORY> must be passed as arguments. See C<new> for details about aliased categories.
936            
937             I<Arguments>
938            
939             =over 2
940            
941             =item * HISTORY
942            
943             =item * COMMENT
944            
945             =item * CHECK_LOWER_LEVEL_CATEGORIES
946            
947             =back
948            
949             =cut
950              
951 0     0     my ($self, @options) = @_ ;
952              
953 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
954              
955 0           my %options = @options ;
956              
957 0           my $location = "$options{FILE}:$options{LINE}" ;
958 0           my $category = $options{CATEGORY} ;
959              
960 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
961 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid 'NAME' at '$location'!") if defined $options{NAME} ;
962 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid 'VALUE' at '$location'!") if defined $options{VALUE} ;
963              
964             # category must not have been set or aliased
965 0 0         if(exists $self->{CATEGORIES}{$category})
966             {
967 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't alias a category that's is already set at '$location'!") ;
968             }
969              
970             # inform of action if option set
971 0 0         if($self->{VERBOSE})
972             {
973 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: SetCategoryAlias called for category '$category' at '$location'.\n") ;
974             }
975              
976 9     9   6354 use Config::Hierarchical::Tie::ReadOnly ;
  9         32  
  9         100389  
977              
978 0           my %alias_hash ;
979 0           tie %alias_hash, 'Config::Hierarchical::Tie::ReadOnly', $options{ALIAS_CATEGORY} ; ## no critic (ProhibitTies)
980              
981             # first check we can do this
982 0           for($options{ALIAS_CATEGORY}->GetKeyValueTuples())
983             {
984 0           $self->Set
985             (
986             CATEGORY => $category,
987             NAME => $_->{NAME},
988             VALUE => $_->{VALUE},
989             CHECK_LOWER_LEVEL_CATEGORIES => $options{CHECK_LOWER_LEVEL_CATEGORIES},
990             FILE => $options{FILE},
991             LINE => $options{LINE},
992             )
993             }
994              
995             #override everything
996 0           $self->{CATEGORIES}{$category} = \%alias_hash ;
997              
998 0           $self->{ALIASED_CATEGORIES}{$category} = {} ;
999 0 0         $self->{ALIASED_CATEGORIES}{$category}{COMMENT} = $options{COMMENT} if exists $options{COMMENT} ;
1000 0 0         $self->{ALIASED_CATEGORIES}{$category}{HISTORY} = {TIME => $self->{TIME_STAMP}, EVENT => $options{HISTORY}} if exists $options{HISTORY} ;
1001 0           $self->{ALIASED_CATEGORIES}{$category}{TIME_STAMP} = $self->{TIME_STAMP}++ ;
1002              
1003 0           $self->{LOCKED_CATEGORIES}{$category}++ ;
1004              
1005 0           return(1) ;
1006             }
1007              
1008             #-------------------------------------------------------------------------------
1009              
1010             sub CreateCustomGetFunctions
1011             {
1012            
1013             =head2 [p] CreateCustomGetFunctions
1014            
1015             Creates custom B<Get*> functions.
1016            
1017             =cut
1018              
1019 0     0     my (@function_names) = @_ ;
1020              
1021 0           for my $function_name (@function_names)
1022             {
1023             my $get_code = sub
1024             {
1025 0     0     my($self, @arguments) = @_ ;
1026            
1027             return
1028             (
1029 0           $self->Get(@arguments, CATEGORIES_TO_EXTRACT_FROM => $self->{GET_CATEGORIES}{$function_name})
1030             ) ;
1031 0           } ;
1032            
1033 0           Sub::Install::install_sub
1034             ({
1035             code => $get_code,
1036             as   => 'Get' . $function_name
1037             });
1038            
1039             my $get_hash_ref_code = sub
1040             {
1041 0     0     my($self, @arguments) = @_ ;
1042            
1043             return
1044             (
1045 0           $self->GetHashRef(CATEGORIES_TO_EXTRACT_FROM => $self->{GET_CATEGORIES}{$function_name})
1046             ) ;
1047 0           } ;
1048            
1049 0           Sub::Install::install_sub
1050             ({
1051             code => $get_hash_ref_code ,
1052             as   => 'Get' . $function_name . 'HashRef'
1053             });
1054             }
1055              
1056 0           return(1) ;
1057             }
1058              
1059             #-------------------------------------------------------------------------------
1060              
1061             sub CheckOptionNames
1062             {
1063              
1064             =head2 [p] CheckOptionNames
1065            
1066             Verifies the options passed to the members of this class. Calls B<{INTERACTION}{DIE}> in case
1067             of error.
1068            
1069             =cut
1070              
1071 0     0     my ($self, $valid_options, @options) = @_ ;
1072              
1073 0 0         if (@options % 2)
1074             {
1075 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
1076             }
1077              
1078 0           my %options = @options ;
1079              
1080 0           for my $option_name (keys %options)
1081             {
1082 0 0         unless(exists $valid_options->{$option_name})
1083             {
1084 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid Option '$option_name' at '$self->{FILE}:$self->{LINE}'!")  ;
1085             }
1086             }
1087              
1088 0 0 0       if
      0        
      0        
1089             (
1090             (defined $options{FILE} && ! defined $options{LINE})
1091             || (!defined $options{FILE} && defined $options{LINE})
1092             )
1093             {
1094 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Incomplete option FILE::LINE!") ;
1095             }
1096              
1097 0           return(1) ;
1098             }
1099              
1100             #-------------------------------------------------------------------------------
1101              
1102             sub Set
1103             {
1104              
1105             =head2 Set(@named_arguments)
1106            
1107             my $config = new Config::Hierarchical() ;
1108            
1109             $config->Set(NAME => 'CC', VALUE => 'gcc') ;
1110            
1111             $config->Set
1112             (
1113             NAME => 'CC', VALUE => 'gcc',
1114            
1115             # options
1116             HISTORY => $history,
1117             COMMENT => 'we like gcc'
1118             CATEGORY => 'CLI',
1119             VALIDATORS => {positive_value => \&PositiveValueValidator,}
1120             FORCE_LOCK => 1,
1121             LOCK => 1,
1122             OVERRIDE => 1,
1123             SILENT_OVERRIDE => 1,
1124             ATTRIBUTE => 'some attribute',
1125             FILE => 'some_file',
1126             LINE => 1,
1127            
1128             CHECK_LOWER_LEVEL_CATEGORIES => 1,
1129             ) ;
1130            
1131             I<ARGUMENTS>
1132            
1133             =over 2
1134            
1135             =item * NAME - The variable's name. MANDATORY
1136            
1137             =item * EVAL - Can be used instead for B<NAME>. See I<'Using EVAL instead for VALUE'>
1138            
1139             =item * VALUE - A scalar value associated with the 'B<NAME>' variable. MANDATORY
1140            
1141             =item * HISTORY
1142            
1143             The argument passed is kept in the configuration variable. You can pass any scalar variable; B<Config::Hierarchical> will
1144             not manipulate this information.
1145            
1146             See C<GetHistory>.
1147            
1148             =item * COMMENT
1149            
1150             A comment that will be added to the variable history.
1151            
1152             =item * CATEGORY
1153            
1154             The name of the category where the variable resides. If no B<CATEGORY> is given, the default category is used.
1155            
1156             =item * ATTRIBUTE
1157            
1158             Set the configuration variable's attribute to the passed argument. See <SetAttribute>.
1159            
1160             =item * SET_VALIDATOR
1161            
1162             Configuration validators that will only be used during this call to B<Set>. The I<SET_VALIDATOR> set in the constructor
1163             will not be called if this option is set. This lets you add configuration variable from different source and check them
1164             with specialized validators.
1165            
1166             =item * VALIDATORS
1167            
1168             Extra validators that will only be used during this call to B<Set>.
1169            
1170             =item * FORCE_LOCK
1171            
1172             If a variable is locked, trying to set it will generate an error. It is possible to temporarily force
1173             the lock with this option. A warning is displayed when a lock is forced.
1174            
1175             =item * LOCK
1176            
1177             Will lock the variable if set to 1, unlock if set to 0.
1178            
1179             =item * OVERRIDE
1180            
1181             This allows the variable in a category to override the variable in a category with higher priority. Once a variable
1182             is overridden, it's value will always be the override value even if it is set again.
1183            
1184             my $config = new Config::Hierarchical
1185             (
1186             NAME => 'Test config',
1187            
1188             CATEGORY_NAMES => ['PARENT', 'CURRENT'],
1189             DEFAULT_CATEGORY => 'CURRENT',
1190            
1191             INITIAL_VALUES =>
1192             [
1193             {NAME => 'CC', CATEGORY => 'PARENT', VALUE => 'parent'},
1194             ] ,
1195             ) ;
1196            
1197             $config->Set(NAME => 'CC', CATEGORY => 'CURRENT', OVERRIDE => 1, VALUE => 'current') ;
1198             $config->Set(NAME => 'CC', CATEGORY => 'PARENT', VALUE => 'parent') ;
1199            
1200             $config->Get(NAME => 'CC') ; # will return 'current'
1201            
1202             =item * SILENT_OVERRIDE
1203            
1204             Disables the warning displayed when overriding a variable.
1205            
1206             =item * FILE and LINE
1207            
1208             See B<FILE and LINE> in C<new>.
1209            
1210             =item * CHECK_LOWER_LEVEL_CATEGORIES
1211            
1212             B<Config::Hierarchical> display warnings about all the collisions with higher priority
1213             categories. If this option is set, warnings will also be displayed for lower priority categories.
1214            
1215             =back
1216            
1217             =head3 History
1218            
1219             B<Config::Hierarchical> will keep a history of all the setting you make. The history can be retrieved with C<GetHistory>.
1220             The history is also part of the dump generated by C<GetDump>.
1221            
1222             =head3 Using B<EVAL> instead for B<VALUE>
1223            
1224             Quite often configuration variables values are base on other configuration variable values. A typical example
1225             would be a set of paths.
1226            
1227             my $config = new Config::Hierarchical() ;
1228            
1229             $config->Set(NAME => 'BASE', VALUE => '/somewhere') ;
1230             $config->Set(NAME => 'MODULE', VALUE => 'module') ;
1231             $config->Set(NAME => 'CONFIG_FILE', VALUE => 'my_config') ;
1232            
1233             If you wanted to set a variable to the full path of your config file you have to write:
1234            
1235             $config->Set
1236             (
1237             NAME => 'PATH_TO_CONFIG_FILE',
1238             VALUE => $config->Get(NAME => 'BASE') . '/'
1239             . $config->Get(NAME => 'MODULE') . '/'
1240             . $config->Get(NAME => 'CONFIG_FILE'),
1241             ) ;
1242            
1243             If you have many variables that are based on other variables, you code get messy quite fast.
1244             With a little work, B<Config::Hierarchical> let's you write code like this:
1245            
1246             $config->Set(NAME => 'PATH_TO_CONFIG_FILE', EVAL => q~ "$BASE/$MODULE/$CONFIG_FILE" ~) ;
1247            
1248             To achieve this, B<Config::Hierarchical> let's you implement an I<"EVALUATOR">, a subroutine
1249             responsible for handling B<EVAL>. It is set during the call to C<new> or C<Set>. The subroutine
1250             is passed the following arguments:
1251            
1252             =over 2
1253            
1254             =item * $config - A reference to the B<Config::Hierarchical> object
1255            
1256             =item * $arguments - A hash reference containing the arguments passed to C<Set>
1257            
1258             =back
1259            
1260             Below is an example using L<Eval::Context>. See I<t/020_eval.t> for a complete example.
1261            
1262             sub eval_engine
1263             {
1264             my ($config, $arguments) = @_ ;
1265             my $hash_ref = $config->GetHashRef() ;
1266            
1267             my $context = new Eval::Context
1268             (
1269             INSTALL_VARIABLES =>
1270             [
1271             map {["\$$_" => $hash_ref->{$_} ]} keys %$hash_ref
1272             ],
1273             INTERACTION =>
1274             {
1275             EVAL_DIE => sub { my($self, $error) = @_ ; croak $error; },
1276             }
1277             ) ;
1278            
1279             my $value = eval {$context->eval(CODE => $arguments->{EVAL})} ;
1280            
1281             if($@)
1282             {
1283             $config->{INTERACTION}{DIE}->
1284             (
1285             "Error: Config::Hierarchical evaluating variable '$arguments->{NAME}' "
1286             . "at $arguments->{FILE}:$arguments->{LINE}:\n\t". $@
1287             ) ;
1288             }
1289            
1290             return $value ;
1291             }
1292            
1293             my $config = new Config::Hierarchical(EVALUATOR => \&eval_engine, ...) ;
1294            
1295            
1296             B<EVAL> can be used in C<Set> and in B<INITIAL_VALUES>.
1297            
1298             =cut
1299              
1300 0     0     my ($self, @options) = @_ ;
1301              
1302 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
1303              
1304 0           my %options = @options ;
1305              
1306 0 0         unless(defined $options{FILE})
1307             {
1308 0           my ($package, $file_name, $line) = caller() ;
1309            
1310 0           $options{FILE} = $file_name ;
1311 0           $options{LINE} = $line ;
1312             }
1313              
1314 0           my $location = "$options{FILE}:$options{LINE}" ;
1315              
1316 0 0         if(exists $options{CATEGORY})
1317             {
1318 0 0         if($self->{WARN_FOR_EXPLICIT_CATEGORY})
1319             {
1320 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Setting '$options{NAME}' using explicit category at '$location'!\n") ;
1321             }
1322             }
1323             else
1324             {
1325 0           $options{CATEGORY} = $self->{DEFAULT_CATEGORY} ;
1326             }
1327              
1328             #~ use Data::TreeDumper ;
1329             #~ print DumpTree {Options => \%options, Self => $self} ;
1330              
1331 0           $self->CheckSetArguments(\%options, $location) ;
1332              
1333 0 0         my $value_to_display = defined $options{VALUE} ? "'$options{VALUE}'" : 'undef' ;
1334              
1335             # inform of action if option set
1336 0 0         if($self->{VERBOSE})
1337             {
1338 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Setting '$options{CATEGORY}::$options{NAME}' to $value_to_display at '$location'.\n") ;
1339             }
1340              
1341             # run debug hook if any
1342 0 0         if(defined $self->{INTERACTION}{DEBUG})
1343             {
1344 0           $self->{INTERACTION}{DEBUG}->
1345             (
1346             "Setting '$options{CATEGORY}::$options{NAME}' to $value_to_display at '$location'.",
1347             $self,
1348             \%options,
1349             ) ;
1350             }
1351            
1352 0 0         if(exists $self->{LOCKED_CATEGORIES}{$options{CATEGORY}})
1353             {
1354 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Variable '$options{CATEGORY}::$options{NAME}', category '$options{CATEGORY}' was locked at '$location'.\n") ;
1355             }
1356              
1357 0 0 0       if
      0        
1358             (
1359             exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}}
1360             && defined $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}}{OVERRIDE}
1361             && ! exists $options{OVERRIDE}
1362             )
1363             {
1364 0           my $override_location = $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}}{OVERRIDE} ;
1365            
1366 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: '$options{NAME}' is of OVERRIDE type set at '$override_location' at '$location'!\n") ;
1367 0           $options{OVERRIDE} = '1 (due to previous override)' ;
1368             }
1369            
1370 0           my ($high_priority_check_set_status, $high_priority_check_warnings) = $self->CheckHigherPriorityCategories(\%options, $location) ;
1371 0           my ($low_priority_check_set_status, $low_priority_check_warnings) = ($EMPTY_STRING, $EMPTY_STRING) ;
1372              
1373 0 0 0       if($self->{CHECK_LOWER_LEVEL_CATEGORIES} || $options{CHECK_LOWER_LEVEL_CATEGORIES})
1374             {
1375 0           ($low_priority_check_set_status, $low_priority_check_warnings) = $self->CheckLowerPriorityCategories(\%options, $location) ;
1376             }
1377              
1378 0           my $warnings = $high_priority_check_warnings . $low_priority_check_warnings ;
1379 0           my $set_status = $high_priority_check_set_status . $low_priority_check_set_status ;
1380              
1381 0 0         if($warnings ne $EMPTY_STRING)
1382             {
1383 0           $self->{INTERACTION}{WARN}->
1384             (
1385             "$self->{NAME}: Setting '$options{CATEGORY}::$options{NAME}' at '$location':\n$warnings" 
1386             ) ;
1387             }
1388              
1389 0           $self->CheckAndSetVariable(\%options, $set_status, $location) ;
1390              
1391 0           return(1) ;
1392             }
1393              
1394             #-------------------------------------------------------------------------------
1395              
1396             sub CheckSetArguments
1397             {
1398              
1399             =head2 [p] CheckSetArguments
1400            
1401             Checks input to B<Set>.
1402            
1403             =cut
1404              
1405 0     0     my ($self, $options, $location) = @_ ;
1406              
1407 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$options->{CATEGORY}' at at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options->{CATEGORY}} ;
1408 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options->{NAME} ;
1409              
1410 0 0 0       if(exists $options->{VALUE} && exists $options->{EVAL})
1411             {
1412 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't have 'VALUE' and 'EVAL' at '$location'!") ;
1413             }
1414              
1415 0 0 0       if(! exists $options->{VALUE} && ! exists $options->{EVAL})
1416             {
1417 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing 'VALUE' or 'EVAL' at '$location'!") ;
1418             }
1419              
1420 0 0         if(exists $options->{EVAL})
1421             {
1422 0 0         if(exists $self->{EVALUATOR})
1423             {
1424 0           $options->{VALUE} = $self->{EVALUATOR}->($self, $options) ;
1425             }
1426             else
1427             {
1428 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: No 'EVALUATOR' defined at '$location'!") ;
1429             }
1430             }
1431              
1432 0 0         if(exists $options->{SET_VALIDATOR})
    0          
1433             {
1434 0           $options->{SET_VALIDATOR}->($self, $options, $location)
1435             }
1436             elsif(exists $self->{SET_VALIDATOR})
1437             {
1438 0           $self->{SET_VALIDATOR}->($self, $options, $location) ;
1439             }
1440              
1441 0 0         if(exists $self->{ALIASED_CATEGORIES}{$options->{CATEGORY}})
1442             {
1443 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't set aliased category (read only) at '$location'!")  ;
1444             }
1445            
1446 0           return(1) ;
1447             }
1448              
1449             #-------------------------------------------------------------------------------
1450              
1451             sub CheckHigherPriorityCategories
1452             {
1453              
1454             =head2 [p] CheckHigherPriorityCategories
1455            
1456             Check if a config variable setting overrides a higher priority category.
1457            
1458             =cut
1459              
1460 0     0     my ($self, $options, $location) = @_ ;
1461              
1462 0           my (@reversed_higher_priority_categories) = @{$self->{REVERSED_HIGHER_LEVEL_CATEGORIES}{$options->{CATEGORY}}} ;
  0            
1463              
1464 0           my ($warnings, $set_status) = ($EMPTY_STRING, $EMPTY_STRING) ;
1465              
1466 0           my $crossed_protected_category = 0 ;
1467              
1468 0           for my $category (@reversed_higher_priority_categories)
1469             {
1470             # categories are travesed in reverse order as it is not allowed to override across a protected category
1471             # check all higher priority categories and warn of override
1472            
1473 0 0 0       if((! $crossed_protected_category) && $options->{OVERRIDE})
1474             {
1475 0 0         if(exists $self->{PROTECTED_CATEGORIES}{$category})
1476             {
1477 0           my $message = "'<${category}>::$options->{NAME}' takes precedence." ;
1478 0           $set_status .= $message ;
1479            
1480 0           my ($name_exists_in_category, $value_exists_in_category, $value_in_category)
1481             = $self->CheckVariableInCategory($category, $options->{NAME}) ;
1482            
1483 0 0 0       if($name_exists_in_category && $value_exists_in_category)
1484             {
1485 0 0         if(!Compare($value_in_category, $options->{VALUE}))
1486             {
1487 0           $warnings   .= "\t$message\n" ;
1488             }
1489            
1490 0           last; # can't override over a protected category
1491             }
1492             else
1493             {
1494 0           $crossed_protected_category++ ; # keep looking for a category that can take precedence
1495             }
1496             }
1497             else
1498             {
1499 0           my ($override_set_status, $override_warnings)
1500             = $self->OverrideVariable
1501             (
1502             $category,
1503             $options->{NAME},
1504             $options->{VALUE},
1505             $options->{CATEGORY},
1506             $location,
1507             $options->{SILENT_OVERRIDE}
1508             ) ;
1509            
1510 0           $set_status .= $override_set_status ;
1511 0           $warnings .= $override_warnings ;
1512             }
1513             }
1514             else
1515             {
1516 0 0         my $message = exists $self->{PROTECTED_CATEGORIES}{$category} ?
1517             "'<${category}>::$options->{NAME}' takes precedence ." :
1518             "'${category}::$options->{NAME}' takes precedence ." ;
1519            
1520 0           $set_status .= $message ;
1521            
1522 0           my ($name_exists_in_category, $value_exists_in_category, $value_in_category)
1523             = $self->CheckVariableInCategory($category, $options->{NAME}) ;
1524            
1525 0 0 0       if($name_exists_in_category && $value_exists_in_category)
1526             {
1527 0 0         if(!Compare($value_in_category, $options->{VALUE}))
1528             {
1529 0           $warnings   .= "\t$message\n" ;
1530             }
1531            
1532 0 0         last if(exists $self->{PROTECTED_CATEGORIES}{$category}) ;
1533             }
1534             }
1535             }
1536              
1537 0           return($set_status, $warnings) ;
1538             }
1539              
1540             sub CheckVariableInCategory
1541             {
1542              
1543             =head2 [p] CheckVariableInCategory
1544            
1545            
1546             =cut
1547              
1548 0     0     my ($self, $category, $name) = @_ ;
1549 0           my ($name_exists, $value_exists, $value, $overridden) ;
1550              
1551 0 0 0       if(exists $self->{CATEGORIES}{$category} && exists $self->{CATEGORIES}{$category}{$name})
1552             {
1553 0           $name_exists++ ;
1554            
1555 0 0         if(exists $self->{ALIASED_CATEGORIES}{$category})
1556             {
1557 0           $value_exists = 1 ;
1558 0           $value = $self->{CATEGORIES}{$category}{$name} ;
1559            
1560 0 0         if(exists $self->{ALIASED_CATEGORIES}{$category}{$name}{OVERRIDDEN})
1561             {
1562 0           $overridden = $self->{ALIASED_CATEGORIES}{$category}{$name}{OVERRIDDEN}
1563             }
1564             }
1565             else
1566             {
1567 0 0         if(exists $self->{CATEGORIES}{$category}{$name}{VALUE})
1568             {
1569 0           $value_exists = 1 ;
1570 0           $value = $self->{CATEGORIES}{$category}{$name}{VALUE} ;
1571            
1572 0 0         if(exists $self->{CATEGORIES}{$category}{$name}{OVERRIDDEN})
1573             {
1574 0           $overridden = $self->{CATEGORIES}{$category}{$name}{OVERRIDDEN} ;
1575             }
1576             }
1577             }
1578             }
1579              
1580 0           return ($name_exists, $value_exists, $value, $overridden) ;
1581             }
1582              
1583             #-------------------------------------------------------------------------------
1584              
1585             sub OverrideVariable ## no critic (Subroutines::ProhibitManyArgs)
1586             {
1587            
1588             =head2 [p] OverrideVariable
1589            
1590            
1591             =cut
1592              
1593 0     0     my ($self, $category, $variable_name, $value, $overriding_category, $location, $silent_override) = @_ ;
1594              
1595 0           my ($set_status, $warnings) = ($EMPTY_STRING, $EMPTY_STRING) ;
1596              
1597 0           my $override_message = "Overriding '${category}::$variable_name'" ;
1598              
1599 0           my ($name_exists_in_category, $value_exists_in_category, $value_in_category)
1600             = $self->CheckVariableInCategory($category, $variable_name) ;
1601              
1602 0 0 0       if($name_exists_in_category && $value_exists_in_category)
1603             {
1604 0 0         if(!Compare($value_in_category, $value))
1605             {
1606 0   0       my $no_silent_override = (! ($silent_override || $self->{DISABLE_SILENT_OPTIONS})) ;
1607            
1608 0 0         $warnings   .= "\t$override_message\n" if($no_silent_override) ;
1609 0           $set_status .= "$override_message (existed, value was different)." ;
1610             }
1611             else
1612             {
1613 0           $set_status .= "$override_message (existed, value was equal)." ;
1614             }
1615             }
1616             else
1617             {
1618 0           $set_status .= "$override_message (didn't exist)" ;
1619             }
1620            
1621             #last to avoid autovivication
1622 0 0         if(exists $self->{ALIASED_CATEGORIES}{$category})
1623             {
1624             # override localy, aliased config is not modified
1625 0           push @{ $self->{ALIASED_CATEGORIES}{$category}{$variable_name}{OVERRIDDEN} }, {CATEGORY => $overriding_category, AT => $location} ;
  0            
1626             }
1627             else
1628             {
1629 0           push @{ $self->{CATEGORIES}{$category}{$variable_name}{OVERRIDDEN} }, {CATEGORY => $overriding_category, AT => $location} ;
  0            
1630             }
1631              
1632 0           return($set_status, $warnings) ;
1633             }
1634              
1635             #-------------------------------------------------------------------------------
1636              
1637             sub CheckLowerPriorityCategories
1638             {
1639              
1640             =head2 [p] CheckLowerPriorityCategories
1641            
1642             Check if a config variable setting takes precedence over a lower priority category.
1643            
1644             =cut
1645              
1646 0     0     my ($self, $options, $location) = @_ ;
1647              
1648 0           my ($warnings, $set_status, @lower_priority_categories) = ($EMPTY_STRING, $EMPTY_STRING) ;
1649              
1650 0           for my $category (reverse @{$self->{CATEGORY_NAMES}})
  0            
1651             {
1652 0 0         if($category eq $options->{CATEGORY})
1653             {
1654 0           last ;
1655             }
1656             else
1657             {
1658 0           push @lower_priority_categories, $category ;
1659             }
1660             }
1661            
1662 0           for my $category (reverse @lower_priority_categories)
1663             {
1664 0           my ($name_exists_in_category, $value_exists_in_category, $value_in_category)
1665             = $self->CheckVariableInCategory($category, $options->{NAME}) ;
1666            
1667 0 0 0       if
1668             (
1669             $name_exists_in_category 
1670             && !Compare($value_in_category, $options->{VALUE})
1671             )
1672             {
1673 0 0         my $message = exists $self->{PROTECTED_CATEGORIES}{$category} ?
1674             "Takes Precedence over lower category '<${category}>::$options->{NAME}'" :
1675             "Takes Precedence over lower category '${category}::$options->{NAME}'" ;
1676            
1677 0           $set_status .= $message ;
1678 0           $warnings   .= "\t$message\n" ;
1679             }
1680             }
1681            
1682 0           return($set_status, $warnings) ;
1683             }
1684              
1685             #-------------------------------------------------------------------------------
1686              
1687             sub CheckAndSetVariable
1688             { ## no critic (ProhibitExcessComplexity)
1689              
1690             =head2 [p] CheckAndSetVariable
1691            
1692             Set the variable in its category, verify lock, etc..
1693            
1694             =cut
1695              
1696 0     0     my($self, $options, $set_status, $location) = @_ ;
1697              
1698 0           my $config_variable_exists = exists $self->{CATEGORIES}{$options->{CATEGORY}}{$options->{NAME}} ;
1699              
1700 0           my $action = $EMPTY_STRING ;
1701 0           my $config_variable ;
1702              
1703 0           $self->Validate($options, $set_status, $location, $config_variable_exists) ;
1704              
1705 0 0         unless($config_variable_exists)
1706             {
1707             # didn't exist before this call
1708            
1709 0           $config_variable = $self->{CATEGORIES}{$options->{CATEGORY}}{$options->{NAME}} = {} ;
1710              
1711 0           $action .= 'CREATE' ;
1712 0 0         $action .= exists $options->{HISTORY} ? ', SET HISTORY' : $EMPTY_STRING ;
1713 0 0         $action .= exists $options->{ATTRIBUTE} ? ', SET ATTRIBUTE' : $EMPTY_STRING ;
1714 0           $action .= ' AND SET' ;
1715            
1716 0           $set_status .= 'OK.' ;
1717             }
1718             else
1719             {
1720 0           $action = 'SET' ;
1721 0 0         $action .= exists $options->{ATTRIBUTE} ? ', SET ATTRIBUTE' : $EMPTY_STRING ;
1722              
1723 0 0         if(exists $options->{HISTORY})
1724             {
1725 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't add history for already existing variable '$options->{CATEGORY}::$options->{NAME}' at '$location'.\n") ;
1726             }
1727            
1728 0           $config_variable = $self->{CATEGORIES}{$options->{CATEGORY}}{$options->{NAME}} ;
1729            
1730 0 0         if(exists $config_variable->{OVERRIDDEN})
1731             {
1732 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Variable '$options->{CATEGORY}::$options->{NAME}' was overridden at '$config_variable->{OVERRIDDEN}'. The new value defined at '$location' might not be used.\n") ;
1733             }
1734            
1735 0 0         if(! Compare($config_variable->{VALUE}, $options->{VALUE}))
1736             {
1737             # not the same value
1738            
1739 0 0         unless(exists $config_variable->{LOCKED})
1740             {
1741             #~ Not locked, set
1742 0           $set_status .= 'OK.' ;
1743             }
1744             else
1745             {
1746 0 0         if($options->{FORCE_LOCK})
1747             {
1748 0           $set_status .= 'OK, forced lock.' ;
1749 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Forcing locked variable '$options->{CATEGORY}::$options->{NAME}' at '$location'.\n") ;
1750             }
1751             else 
1752             {
1753 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Variable '$options->{CATEGORY}::$options->{NAME}' was locked and couldn't be set at '$location'.\n") ;
1754             }
1755             }
1756             }
1757             else
1758             {
1759 0           $set_status .= 'OK, same value.' ;
1760             }
1761             }
1762              
1763 0           $config_variable->{VALUE} = $options->{VALUE} ;
1764 0 0         $config_variable->{OVERRIDE} = $location if $options->{OVERRIDE} ;
1765 0 0         $config_variable->{ATTRIBUTE} = $options->{ATTRIBUTE} if $options->{ATTRIBUTE} ;
1766              
1767             #~ set lock state
1768 0           my $lock = $EMPTY_STRING ;
1769 0 0         my $force_lock = $options->{FORCE_LOCK} ? 'FORCE_LOCK, ' : $EMPTY_STRING ;
1770              
1771 0 0         if(exists $options->{LOCK})
1772             {
1773 0 0         if($options->{LOCK})
1774             {
1775 0           $lock = 'LOCK(1), ' ;
1776 0           $config_variable->{LOCKED} = $location ;
1777             }
1778             else
1779             {
1780 0           $lock = 'LOCK(0), ' ;
1781 0           delete $config_variable->{LOCKED} ;
1782             }
1783             }
1784            
1785             # update history
1786              
1787 0 0         my $override = exists $options->{OVERRIDE} ? 'OVERRIDE, ' : $EMPTY_STRING ;
1788              
1789 0 0         my $value_to_display = defined $options->{VALUE} ? "'$options->{VALUE}'" : 'undef' ;
1790 0           my $history = "$action. value = $value_to_display, ${override}${force_lock}${lock}category = '$options->{CATEGORY}' at '$options->{FILE}:$options->{LINE}', status = $set_status" ;
1791              
1792 0           my $history_data = {TIME => $self->{TIME_STAMP}, EVENT => $history} ;
1793 0 0         $history_data->{HISTORY} = $options->{HISTORY} if exists $options->{HISTORY} ;
1794 0 0         $history_data->{COMMENT} = $options->{COMMENT} if exists $options->{COMMENT} ;
1795              
1796 0           push @{$config_variable->{HISTORY}}, $history_data ;
  0            
1797              
1798 0           $self->{TIME_STAMP}++ ;
1799              
1800 0           return(1) ;
1801             }
1802              
1803             #-------------------------------------------------------------------------------
1804              
1805             sub SetAttribute
1806             {
1807              
1808             =head2 SetAttribute(NAME => $variable_name, ATTRIBUTE => $attribute, CATEGORY => $category)
1809            
1810             This sub allows you to attach an attribute per variable (the attribute you set is per category) other than a value.
1811             It will raise an exception if you try to set a variable that does not exists or if you try to set an attribute to a variable
1812             in an aliased category.
1813            
1814             $config->SetAttribute(NAME => 'CC', ATTRIBUTE => 'attribute') ;
1815            
1816             # or directly in the 'Set' call
1817            
1818             $config->Set(NAME => 'CC', VALUE => 'CC', ATTRIBUTE => 'attribute') ;
1819            
1820             my ($attribute, $attribute_exists) = $config->GetAttribute(NAME => 'CC') ;
1821            
1822             I<Arguments>
1823            
1824             =over 2
1825            
1826             =item * NAME => $variable_name - The variable name
1827            
1828             =item * ATTRIBUTE => $attribute - A scalar attribute
1829            
1830             =item * CATEGORY => $category - Category in which you want to set the attribute.
1831            
1832             =back
1833            
1834             I<Returns> - Nothing
1835            
1836             =cut
1837              
1838 0     0     my ($self, @options) = @_ ;
1839              
1840 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
1841              
1842 0           my %options = @options ;
1843              
1844 0 0         unless(defined $options{FILE})
1845             {
1846 0           my ($package, $file_name, $line) = caller() ;
1847            
1848 0           $options{FILE} = $file_name ;
1849 0           $options{LINE} = $line ;
1850             }
1851              
1852 0           my $location = "$options{FILE}:$options{LINE}" ;
1853              
1854 0 0         if(exists $options{CATEGORY})
1855             {
1856 0 0         if($self->{WARN_FOR_EXPLICIT_CATEGORY})
1857             {
1858 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Setting '$options{NAME}' using explicit category at '$location'!\n") ;
1859             }
1860             }
1861             else
1862             {
1863 0           $options{CATEGORY} = $self->{DEFAULT_CATEGORY} ;
1864             }
1865              
1866             #~ use Data::TreeDumper ;
1867             #~ print DumpTree {Options => \%options, Self => $self} ;
1868              
1869 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$options{CATEGORY}' at at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
1870 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
1871 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing value at '$location'!") unless exists $options{VALUE} ;
1872              
1873 0 0         my $value_to_display = defined $options{VALUE} ? "'$options{VALUE}'" : 'undef' ;
1874              
1875 0 0         if(exists $self->{ALIASED_CATEGORIES}{$options{CATEGORY}})
1876             {
1877 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't set aliased category attribute (read only) at '$location'!")  ;
1878             }
1879              
1880             # inform of action if option set
1881 0 0         if($self->{VERBOSE})
1882             {
1883 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: SetAttribute for '$options{CATEGORY}::$options{NAME}' to $value_to_display at '$location'.\n") ;
1884             }
1885              
1886 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}})
1887             {
1888 0           my $config_variable = $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}} ;
1889 0           $config_variable->{ATTRIBUTE} = $options{VALUE};
1890            
1891 0           my $history = "SET_ATTRIBUTE. category = '$options{CATEGORY}', value = $value_to_display at '$location', status = OK." ;
1892 0           push @{$config_variable->{HISTORY}}, {TIME => $self->{TIME_STAMP}, EVENT => $history} ;
  0            
1893              
1894 0           $self->{TIME_STAMP}++ ;
1895             }
1896             else
1897             {
1898 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't set attribute, variable '$options{NAME}' doesn't exist at '$location'!")  ;
1899             }
1900            
1901 0           return(1) ;
1902             }
1903              
1904             #-------------------------------------------------------------------------------
1905              
1906             sub GetAttribute
1907             {
1908              
1909             =head2 GetAttribute(NAME => $variable_name)
1910            
1911             This sub returns the attribute as well as the existence of the attribute. If the attribute didn't exist, the value is
1912             set to B<undef>. No warnings are displayed if you query the attribute of a variable that does not have an attribute.
1913            
1914             A warning message is displayed if you call this sub in void or scalar context.
1915            
1916             my ($attribute, $attribute_exists) = $config->GetAttribute(NAME => 'CC') ;
1917            
1918             I<Arguments>
1919            
1920             =over 2
1921            
1922             =item * NAME => $variable_name - The name of the variable you want to get the attribute for
1923            
1924             =back
1925            
1926             I<Returns> - a list
1927            
1928             =over 2
1929            
1930             =item * The attribute
1931            
1932             =item * A boolean. Set if the attribute existed
1933            
1934             =back
1935            
1936             I<Exceptions> - This sub will raise an exception if you query a variable that does not exists.
1937            
1938             =cut
1939              
1940 0     0     my ($self, @options) = @_ ;
1941              
1942 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
1943              
1944 0           my %options = @options ;
1945              
1946 0 0         unless(defined $options{FILE})
1947             {
1948 0           my ($package, $file_name, $line) = caller() ;
1949            
1950 0           $options{FILE} = $file_name ;
1951 0           $options{LINE} = $line ;
1952             }
1953              
1954 0           my $location = "$options{FILE}:$options{LINE}" ;
1955              
1956 0 0         if(defined wantarray)
1957             {
1958 0 0         unless(wantarray)
1959             {
1960 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: GetAttribute: called in scalar context at '$location'!\n") ;
1961             }
1962             }
1963             else
1964             {
1965 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetAttribute' called in void context at '$location'!\n") ;
1966             }
1967            
1968 0 0         if(exists $options{CATEGORY})
1969             {
1970 0 0         if($self->{WARN_FOR_EXPLICIT_CATEGORY})
1971             {
1972 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Setting '$options{NAME}' using explicit category at '$location'!\n") ;
1973             }
1974             }
1975             else
1976             {
1977 0           $options{CATEGORY} = $self->{DEFAULT_CATEGORY} ;
1978             }
1979              
1980             #~ use Data::TreeDumper ;
1981             #~ print DumpTree {Options => \%options, Self => $self} ;
1982              
1983 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$options{CATEGORY}' at at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
1984 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
1985 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Unexpected field VALUE at '$location'!") if exists $options{VALUE} ;
1986              
1987             # inform of action if option set
1988 0 0         if($self->{VERBOSE})
1989             {
1990 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: GetAttribute for '$options{CATEGORY}::$options{NAME}' at '$location'.\n") ;
1991             }
1992              
1993 0           my @result ;
1994              
1995 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}})
1996             {
1997 0           my $config_variable = $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}} ;
1998 0           my $attribute_exist = exists $config_variable->{ATTRIBUTE};
1999            
2000 0 0         if($attribute_exist)
2001             {
2002 0           @result = ($config_variable->{ATTRIBUTE}, $attribute_exist) ;
2003             }
2004             else
2005             {
2006 0           @result = (undef, $attribute_exist) ;
2007             }
2008             }
2009             else
2010             {
2011 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Can't get attribute, variable '$options{NAME}' doesn't exist at '$location'!")  ;
2012             }
2013              
2014 0           return(@result) ;
2015             }
2016              
2017             #-------------------------------------------------------------------------------
2018              
2019             sub Validate
2020             {
2021              
2022             =head2 [p] Validate
2023            
2024            
2025             =cut
2026              
2027 0     0     my ($self,$options, $set_status, $location, $config_variable_exists) = @_ ;
2028              
2029 0 0         if($config_variable_exists)
2030             {
2031 0           my $config_variable = $self->{CATEGORIES}{$options->{CATEGORY}}{$options->{NAME}} ;
2032              
2033             # run variable validators
2034 0           for my $validator (keys %{$config_variable->{VALIDATORS}})
  0            
2035             {
2036 0           my$validator_origin = $config_variable->{VALIDATORS}{$validator}{ORIGIN} ;
2037 0           my $validator_sub = $config_variable->{VALIDATORS}{$validator}{SUB} ;
2038            
2039 0 0         if($self->{VERBOSE})
2040             {
2041 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: running validator '$validator' defined at '$validator_origin' on '$options->{CATEGORY}::$options->{NAME}'.\n") ;
2042             }
2043            
2044 0 0         unless($validator_sub->($options->{VALUE}))
2045             {
2046 0           $self->{INTERACTION}{DIE}->
2047             ("$self->{NAME}: Invalid value '$options->{VALUE}' for variable '$options->{NAME}'. Validator '$validator' defined at '$validator_origin'.\n") ;
2048             }
2049             }
2050             }
2051            
2052 0 0         if(exists $options->{VALIDATORS})
2053             {
2054             # run local validator
2055 0           for my $validator (keys %{$options->{VALIDATORS}})
  0            
2056             {
2057 0 0         if($self->{VERBOSE})
2058             {
2059 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: running local validator '$validator' defined at '$location'.\n") ;
2060             }
2061            
2062 0 0         unless($options->{VALIDATORS}{$validator}->($options->{VALUE}))
2063             {
2064 0           $self->{INTERACTION}{DIE}->
2065             ("$self->{NAME}: Invalid value '$options->{VALUE}' for variable '$options->{NAME}'. Local validator '$validator' defined at '$location'.\n") ;
2066             }
2067             }
2068             }
2069            
2070 0           return(1) ;
2071             }
2072              
2073             #-------------------------------------------------------------------------------
2074              
2075             sub Get
2076             { ## no critic (ProhibitExcessComplexity)
2077              
2078             =head2 Get(@named_arguments)
2079            
2080             Returns the value associated with the variable passed as argument. If more than one category contains the variable,
2081             the variable from the category with the highest priority, which is not overridden, will be used.
2082            
2083             If the variable doesn't exist in the container, a warning is displayed and B<undef> is returned.
2084            
2085             my $config = new Config::Hierarchical(INITIAL_VALUES => [{NAME => 'CC', VALUE => 'gcc'}]) ;
2086            
2087             my $cc = $config->Get(NAME => 'CC') ;
2088             my $ld = $config->Get(NAME => 'LD', SILENT_NOT_EXISTS => 1) ;
2089            
2090             I<Arguments>
2091            
2092             =over 2
2093            
2094             =item * SILENT_NOT_EXISTS
2095            
2096             Setting this option will disable the warning generated when the variable doesn't exist in the container.
2097            
2098             =item * CATEGORIES_TO_EXTRACT_FROM
2099            
2100             If set, B<Get> will only search in the specified categories. A warning is displayed if the categories are
2101             not in the same order as passed to the constructor.
2102            
2103             =item * GET_CATEGORY
2104            
2105             If this option is set, B<Get> will return the value _and_ the category it it comes from.
2106            
2107             =back
2108            
2109             I<Returns>
2110            
2111             If B<GET_CATEGORY> is B<not> set:
2112            
2113             =over 2
2114            
2115             =item * The variable's value
2116            
2117             =back
2118            
2119             If B<GET_CATEGORY> B<is> set:
2120            
2121             =over 2
2122            
2123             =item * The variable's value
2124            
2125             =item * The category the value comes from
2126            
2127             =back
2128            
2129             I<Warnings>
2130            
2131             This function verifies its calling context and will generate a warning if it is called in void context.
2132            
2133             =cut
2134              
2135 0     0     my ($self, @options) = @_ ;
2136              
2137 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
2138              
2139 0           my %options = @options ;
2140              
2141 0 0         unless(defined $options{FILE})
2142             {
2143 0           my ($package, $file_name, $line) = caller() ;
2144            
2145 0           $options{FILE} = $file_name ;
2146 0           $options{LINE} = $line ;
2147             }
2148              
2149 0           my $location = "$options{FILE}:$options{LINE}" ;
2150              
2151 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
2152             {
2153 0 0         if($self->{WARN_FOR_EXPLICIT_CATEGORY})
2154             {
2155 0           my $plural = 'y' ;
2156 0 0         $plural = 'ies' if (@{$options{CATEGORIES_TO_EXTRACT_FROM}} > 1) ;
  0            
2157            
2158 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Getting '$options{NAME}' using explicit categor$plural at '$location'!\n") ;
2159             }
2160             }
2161              
2162             #~ use Data::TreeDumper ;
2163             #~ print DumpTree {Options => \%options, Self => $self} ;
2164              
2165 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at $location!") unless defined $options{NAME} ;
2166              
2167             # inform of action if option set
2168 0 0         if($self->{VERBOSE})
2169             {
2170 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Getting '$options{NAME}' at '$location'.\n") ;
2171             }
2172              
2173 0 0         unless(defined wantarray)
2174             {
2175 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Getting '$options{NAME}' in void context at '$location'!\n") ;
2176             }
2177              
2178             # run debug hook if any
2179 0 0         if(defined $self->{INTERACTION}{DEBUG})
2180             {
2181 0 0         my $category = exists $options{CATEGORY} ? "$options{CATEGORY}::" : $EMPTY_STRING ;
2182            
2183 0           $self->{INTERACTION}{DEBUG}->
2184             (
2185             "Getting '$category$options{NAME}' at '$location'.",
2186             $self,
2187             \%options,
2188             ) ;
2189             }
2190            
2191 0           my @categories_to_extract_from ;
2192             my %categories_to_extract_from ;
2193 0           my $user_defined_categories_to_extract_from = 0 ;
2194              
2195 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
2196             {
2197 0           $self->CheckCategoriesOrder(\%options, $location) ;
2198            
2199 0           @categories_to_extract_from = @{$options{CATEGORIES_TO_EXTRACT_FROM}} ;
  0            
2200            
2201 0           $user_defined_categories_to_extract_from++ ;
2202 0           %categories_to_extract_from = map {$_, 1} @categories_to_extract_from ;
  0            
2203             }
2204             else
2205             {
2206 0           @categories_to_extract_from = @{$self->{CATEGORY_NAMES}} ;
  0            
2207             }
2208              
2209              
2210 0           my ($value_found, $value, $found_in_category) = (0, undef, undef) ;
2211              
2212 0           for my $category (@categories_to_extract_from)
2213             {
2214 0           my ($name_exists_in_category, $value_exists_in_category, $value_in_category, $name_in_category_is_overriden)
2215             = $self->CheckVariableInCategory($category, $options{NAME}) ;
2216            
2217 0 0         next unless ($name_exists_in_category) ;
2218            
2219             # remember the value in case the overriding category is not in the list of categories to
2220             # extract from
2221 0           $value_found++ ;
2222            
2223 0           $value             = $value_in_category ;
2224 0           $found_in_category = $category ;
2225            
2226             # check if lower priority category did an override
2227 0 0         if($name_in_category_is_overriden)
2228             {
2229 0 0         if(! $user_defined_categories_to_extract_from)
2230             {
2231             # get value from overriding category
2232             }
2233             else
2234             {
2235             # if this category was overridden by a category passed by the user
2236             # in CATEGORIES_TO_EXTRACT_FROM, use the overridden variable
2237             # otherwise, we are done
2238            
2239 0           my $current_categories_contain_overriding_category = 0 ;
2240            
2241 0           for my $override (@{ $name_in_category_is_overriden})
  0            
2242             {
2243 0 0         if(exists $categories_to_extract_from{$override->{CATEGORY}})
2244             {
2245 0           $current_categories_contain_overriding_category++ ;
2246 0           last ;
2247             }
2248             }
2249            
2250 0 0         if(! $current_categories_contain_overriding_category)
2251             {
2252             # we're done, return value from this category
2253            
2254 0 0         if($self->{VERBOSE})
2255             {
2256 0           $self->{INTERACTION}{INFO}->("\tfound in category '$found_in_category'.\n") ;
2257             }
2258 0           last ;
2259             }
2260             }
2261             }
2262             else
2263             {
2264             # we're done, return value from this category
2265            
2266 0 0         if($self->{VERBOSE})
2267             {
2268 0           $self->{INTERACTION}{INFO}->("\tfound in category '$found_in_category'.\n") ;
2269             }
2270 0           last ;
2271             }
2272             }
2273              
2274              
2275 0 0         if($self->{LOG_ACCESS})
2276             {
2277 0           push @{$self->{ACCESS_TO_VARIABLE}}, {%options} ;
  0            
2278             }
2279              
2280 0 0         if(! $value_found)
2281             {
2282             #~ use Data::TreeDumper ;
2283             #~ warn DumpTree \%options ;
2284             #~ warn DumpTree $self ;
2285            
2286 0 0 0       if($self->{DIE_NOT_EXISTS} || $options{DIE_NOT_EXISTS})
2287             {
2288 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Variable '$options{NAME}' doesn't exist in categories [@categories_to_extract_from]at '$location'.\n") ;
2289             }
2290             else
2291             {
2292 0 0 0       if(! ($options{SILENT_NOT_EXISTS} || $self->{DISABLE_SILENT_OPTIONS}))
2293             {
2294 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: Variable '$options{NAME}' doesn't exist in categories [@categories_to_extract_from]at '$location'. Returning undef!\n") ;
2295             }
2296             }
2297             }
2298              
2299 0 0         if($options{GET_CATEGORY})
2300             {
2301 0           return($value, $found_in_category) ;
2302             }
2303             else
2304             {
2305 0           return($value) ;
2306             }
2307             }
2308              
2309             #-------------------------------------------------------------------------------
2310              
2311             sub CheckCategoriesOrder
2312             {
2313            
2314             =head2 [p] CheckCategoriesOrder
2315            
2316            
2317             =cut
2318              
2319 0     0     my ($self, $options, $location) = @_ ;
2320              
2321 0           my @default_categories = @{ $self->{CATEGORY_NAMES} } ;
  0            
2322              
2323 0           for my $category ( @{ $options->{CATEGORIES_TO_EXTRACT_FROM} })
  0            
2324             {
2325 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at at '$location'!") unless exists $self->{VALID_CATEGORIES}{$category} ;
2326              
2327 0   0       shift @default_categories while(@default_categories && ($category ne $default_categories[0])) ;
2328            
2329 0 0         if(@default_categories)
2330             {
2331 0           shift @default_categories ;
2332             }
2333             else
2334             {
2335 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: categories in 'CATEGORIES_TO_EXTRACT_FROM' in an unexpected order at '$location'.\n") ;
2336 0           last ;
2337             }
2338             }
2339            
2340 0           return ;
2341             }
2342              
2343             #-------------------------------------------------------------------------------
2344              
2345             sub SetMultiple
2346             {
2347            
2348             =head2 SetMultiple(\%options, \@variable_to_set, \@variable_to_set, ...)
2349            
2350             Set multiple configuration in one call.
2351            
2352             $config->SetMultiple
2353             (
2354             {FORCE_LOCK => 1},
2355            
2356             [NAME => 'CC', VALUE => 'gcc', SILENT_OVERRIDE => 1],
2357             [NAME => 'LD', VALUE => 'ld'],
2358             ) ;
2359            
2360             I<Arguments>
2361            
2362             =over 2
2363            
2364             =item * \%options - An optional hash reference with options applied to each C<Set> call
2365            
2366             =item * \@variable_to_set - An array reference containing the parameter for the C<Set> call
2367            
2368             Multiple \@variable_to_set can be passed.
2369            
2370             =back
2371            
2372             I<Returns> - Nothing
2373            
2374             see C<Set>.
2375            
2376             =cut
2377              
2378 0     0     my ($self, $options, @sets) = @_ ;
2379              
2380 0           my ($package, $file_name, $line) = caller() ;
2381              
2382 0 0         if('HASH' eq ref $options)
2383             {
2384 0 0         unless(defined $options->{FILE})
2385             {
2386 0           $options->{FILE} = $file_name ;
2387 0           $options->{LINE} = $line ;
2388             }
2389            
2390             }
2391             else
2392             {
2393 0 0         unshift @sets, $options if defined $options ;
2394            
2395 0           $options = {FILE => $file_name, LINE => $line} ;
2396             }
2397            
2398 0           my $location = "$options->{FILE}:$options->{LINE}" ;
2399              
2400 0           for my $set (@sets)
2401             {
2402 0 0         unless( 'ARRAY' eq ref $set)
2403             {
2404 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: 'SetMultiple' must be passed array reference at '$location'!\n") ;
2405             }
2406            
2407 0           $self->Set(%{$options}, @{$set}) ;
  0            
  0            
2408             }
2409              
2410 0           return(1) ;
2411             }
2412              
2413             #-------------------------------------------------------------------------------
2414              
2415             sub GetMultiple
2416             {
2417              
2418             =head2 GetMultiple(\%options, @variables_to_get)
2419            
2420             Get multiple configuration in one call.
2421            
2422             my $config = new Config::Hierarchical(INITIAL_VALUES => [{NAME => 'CC', VALUE => 'gcc'}]) ;
2423            
2424             my @values = $config->GetMultiple('CC') ;
2425            
2426             my @other_values = $config->GetMultiple
2427             (
2428             {SILENT_NOT_EXISTS => 1},
2429             'CC',
2430             'AR'
2431             ) ;
2432            
2433             I<Arguments>
2434            
2435             =over 2
2436            
2437             =item * \%options - An optional hash reference with options applied to each C<Get> call
2438            
2439             =item * @variable_to_get - A list containing the names of the variables to get.
2440            
2441             =back
2442            
2443             Option B<GET_CATEGORY> will be ignored in this sub.
2444            
2445             I<Returns> - Nothing
2446            
2447             see C<Get>.
2448            
2449             =cut
2450              
2451 0     0     my ($self, $options, @names) = @_ ;
2452              
2453 0           my ($package, $file_name, $line) = caller() ;
2454              
2455 0 0         if('HASH' eq ref $options)
2456             {
2457 0 0         unless(defined $options->{FILE})
2458             {
2459 0           $options->{FILE} = $file_name ;
2460 0           $options->{LINE} = $line ;
2461             }
2462            
2463             }
2464             else
2465             {
2466 0 0         unshift @names, $options if defined $options ;
2467            
2468 0           $options = {FILE => $file_name, LINE => $line} ;
2469             }
2470            
2471 0           my $location = "$options->{FILE}:$options->{LINE}" ;
2472              
2473 0 0         if(defined wantarray)
2474             {
2475 0 0         unless(wantarray)
2476             {
2477 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetMultiple' is not called in scalar context at '$location'!\n") ;
2478             }
2479             }
2480             else
2481             {
2482 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetMultiple' called in void context at '$location'!\n") ;
2483             }
2484              
2485 0           my @values ;
2486 0           for my $name (@names)
2487             {
2488 0 0         unless( $EMPTY_STRING eq ref $name)
2489             {
2490 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: 'GetMultiple' must be passed scalars at '$location'!\n") ;
2491             }
2492            
2493 0           push @values, scalar($self->Get(%{$options}, NAME => $name, GET_CATEGORY => 0)) ;
  0            
2494             }
2495            
2496 0           return(@values) ;
2497             }
2498              
2499             #-------------------------------------------------------------------------------
2500              
2501             sub GetKeys
2502             {
2503            
2504             =head2 GetKeys()
2505            
2506             my @keys = $config->GetKeys() ;
2507            
2508             Returns the names of the element in the config object.
2509            
2510             I<Arguments>
2511            
2512             =over 2
2513            
2514             =item * Optional, CATEGORIES_TO_EXTRACT_FROM
2515            
2516             if set, B<GetKeys> will only search in the specified categories.
2517            
2518             =back
2519            
2520             I<Returns>
2521            
2522             The list of variables contained in the B<Config::Hierarchical> object.
2523            
2524             I<Warnings>
2525            
2526             A warning will be generated if I<GetKeys> is called in void context.
2527            
2528             =cut
2529              
2530 0     0     my ($self, @options) = @_ ;
2531              
2532 0           my ($package, $file_name, $line) = caller() ;
2533 0           my $location = "$file_name:$line" ;
2534              
2535 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
2536              
2537 0           my %options = @options ;
2538              
2539 0 0         if($self->{VERBOSE})
2540             {
2541 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: 'GetKeys' at '$location'\n") ;
2542             }
2543              
2544 0 0         unless(defined wantarray)
2545             {
2546 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetKeys' called in void context at '$file_name:$line'!\n") ;
2547             }
2548            
2549 0           my (%variables, @categories_to_extract_from) ;
2550              
2551 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
2552             {
2553 0           @categories_to_extract_from = @{$options{CATEGORIES_TO_EXTRACT_FROM}} ;
  0            
2554             }
2555             else
2556             {
2557 0           @categories_to_extract_from = @{$self->{CATEGORY_NAMES}} ;
  0            
2558             }
2559            
2560 0           my %hash = map
2561             {
2562 0           $_ => 1
2563             } map
2564             {
2565 0           keys %{$self->{CATEGORIES}{$_}} ;
  0            
2566             }  @categories_to_extract_from ;
2567              
2568 0           return(keys %hash) ;
2569             }
2570              
2571             #-------------------------------------------------------------------------------
2572              
2573             sub GetKeyValueTuples
2574             {
2575              
2576             =head2 GetKeyValueTuples()
2577            
2578             Returns a list of hash references containing the name and the value of each configuration variable
2579             contained in the object. This can be useful when you you create config objects from data in other objects.
2580            
2581             my $config_1 = new Config::Hierarchical(.....) ;
2582            
2583             my $config_2 = new Config::Hierarchical
2584             (
2585             NAME => 'config 2',
2586            
2587             CATEGORY_NAMES => ['PARENT', 'CURRENT'],
2588             DEFAULT_CATEGORY => 'CURRENT',
2589            
2590             INITIAL_VALUES =>
2591             [
2592             # Initializing a category from another config
2593             map
2594             ({
2595             {
2596             NAME => $_->{NAME},
2597             VALUE => $_->{VALUE},
2598             CATEGORY => 'PARENT',
2599             LOCK => 1,
2600             HISTORY => $config_1->GetHistory(NAME => $_->{NAME}),
2601             }
2602             } $config_1->GetKeyValueTuples()),
2603            
2604             {NAME => 'CC', VALUE => 1,},
2605             ]
2606             ) ;
2607            
2608             I<Argument>
2609            
2610             =over 2
2611            
2612             =item * Optional, CATEGORIES_TO_EXTRACT_FROM
2613            
2614             If set, B<GetKeyValueTuples> will only search in the specified categories.
2615            
2616             =back
2617            
2618             I<Returns>
2619            
2620             =over 2
2621            
2622             =item * A list of hash references. Each hash has a B<NAME> and B<VALUE> key.
2623            
2624             =back
2625            
2626             =cut
2627              
2628 0     0     my ($self, @options) = @_ ;
2629              
2630 0           my ($package, $file_name, $line) = caller() ;
2631              
2632 0 0         if($self->{VERBOSE})
2633             {
2634 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: 'GetKeyValueTuples' at '$file_name:$line'\n") ;
2635             }
2636              
2637 0 0         unless(defined wantarray)
2638             {
2639 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetKeyValueTuples' in void context at '$file_name:$line'!\n") ;
2640             }
2641              
2642             # run debug hook if any
2643 0 0         if(defined $self->{INTERACTION}{DEBUG})
2644             {
2645 0           $self->{INTERACTION}{DEBUG}->("'GetKeyValueTuples' at '$file_name:$line'.", $self, \@options,) ;
2646             }
2647              
2648 0           my @list ;
2649 0           my %hash = %{$self->GetHashRef(@options)} ;
  0            
2650              
2651 0           while(my($n, $v) = each %hash)
2652             {
2653 0           push @list, {NAME => $n, VALUE => $v} ;
2654             }
2655            
2656 0           return(@list) ;
2657             }
2658              
2659             #-------------------------------------------------------------------------------
2660              
2661             sub GetHashRef
2662             {
2663              
2664             =head2 GetHashRef()
2665            
2666             my $hash_ref = $config->GetHashRef() ;
2667            
2668             I<Arguments> - None
2669            
2670             This function will generate an error if any argument is passed to it.
2671            
2672             I<Returns> - A hash reference containing a copy of all the elements in the container.
2673            
2674             I<Warnings>
2675            
2676             C<GetHashRef> will generate a warning if:
2677            
2678             =over 2
2679            
2680             =item * it is called in void context
2681            
2682             =item * it is called in array context
2683            
2684             =back
2685            
2686             =cut
2687              
2688 0     0     my ($self, @options) = @_ ;
2689              
2690 0           my ($package, $file_name, $line) = caller() ;
2691 0           my $location = "$file_name:$line" ;
2692              
2693 0           $self->CheckOptionNames($VALID_OPTIONS, @options) ;
2694              
2695 0           my %options = @options ;
2696              
2697 0 0         if($self->{VERBOSE})
2698             {
2699 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: 'GetHashRef' at '$location'\n") ;
2700             }
2701              
2702 0 0         if(defined wantarray)
2703             {
2704 0 0         if(wantarray)
2705             {
2706 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetHashRef' is called in array context at '$file_name:$line'!\n") ;
2707             }
2708             }
2709             else
2710             {
2711 0           $self->{INTERACTION}{WARN}->("$self->{NAME}: 'GetHashRef' called in void context at '$file_name:$line'!\n") ;
2712             }
2713            
2714 0           my (%variables, @categories_to_extract_from) ;
2715              
2716 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
2717             {
2718 0           @categories_to_extract_from = @{$options{CATEGORIES_TO_EXTRACT_FROM}} ;
  0            
2719             }
2720             else
2721             {
2722 0           @categories_to_extract_from = @{$self->{CATEGORY_NAMES}} ;
  0            
2723             }
2724            
2725             return 
2726             {
2727 0           map
2728             {
2729 0           $_ => scalar($self->Get(NAME => $_, CATEGORIES_TO_EXTRACT_FROM => [@categories_to_extract_from], FILE => $file_name, LINE => $line))
2730             } map
2731             {
2732 0           keys %{$self->{CATEGORIES}{$_}} ;
  0            
2733             }  @categories_to_extract_from
2734             } ;
2735             }
2736              
2737              
2738             #-------------------------------------------------------------------------------
2739              
2740             sub SetDisplayExplicitCategoryWarningOption
2741             {
2742              
2743             =head2 SetDisplayExplicitCategoryWarningOption($boolean)
2744            
2745             $config->SetDisplayExplicitCategoryWarningOption(1) ;
2746             $config->SetDisplayExplicitCategoryWarningOption(0) ;
2747            
2748             I<Arguments>
2749            
2750             =over 2
2751            
2752             =item * $boolean - controls if messages are displayed if an explicit category is used in C<Get> or C<Set>.
2753            
2754             =back
2755            
2756             I<Return> - Nothing
2757            
2758             =cut
2759              
2760 0     0     my ($self, $value) = @_ ;
2761              
2762 0           $self->{WARN_FOR_EXPLICIT_CATEGORY} = $value ;
2763              
2764 0 0         if($self->{VERBOSE})
2765             {
2766 0           my ($package, $file_name, $line) = caller() ;
2767 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Setting 'WARN_FOR_EXPLICIT_CATEGORY' to '$value' at '$file_name:$line'.\n") ;
2768             }
2769            
2770 0           return(1) ;
2771             }
2772              
2773             #-------------------------------------------------------------------------------
2774              
2775             sub SetDisableSilentOptions
2776             {
2777              
2778             =head2 SetDisableSilentOptions($boolean)
2779            
2780             $config->SetDisableSilentOptions(1) ;
2781             $config->SetDisableSilentOptions(0) ;
2782            
2783             I<Arguments>
2784            
2785             =over 2
2786            
2787             =item * $boolean - controls if messages are displayed regardless of local warning disabling options
2788            
2789             This is useful when debugging your configuration as it forces all the warning to be displayed.
2790            
2791             =back
2792            
2793             I<Return> - Nothing
2794            
2795            
2796             =cut
2797              
2798 0     0     my ($self, $silent) = @_ ;
2799              
2800 0           $self->{DISABLE_SILENT_OPTIONS} = $silent ;
2801              
2802 0 0         if($self->{VERBOSE})
2803             {
2804 0           my ($package, $file_name, $line) = caller() ;
2805 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Setting 'DISABLE_SILENT_OPTIONS' to '$silent' at '$file_name:$line'.\n") ;
2806             }
2807            
2808 0           return(1) ;
2809             }
2810              
2811             #-------------------------------------------------------------------------------
2812            
2813             sub LockCategories
2814             {
2815              
2816             =head2 LockCategories(@categories)
2817            
2818             Locks the categories passed as argument. A variable in a locked category can not be set.
2819             An attempt to set a locked variable will generate an error. B<FORCE_LOCK> has no effect on locked categories.
2820            
2821             $config->LockCategories('PARENT', 'OTHER') ;
2822            
2823             I<Arguments>
2824            
2825             =over 2
2826            
2827             =item * @categories - a list of categories to lock
2828            
2829             =back
2830            
2831             I<Returns> - Nothing
2832            
2833             I<Exceptions> - An exception is generated if you try to lock a category that doesn't exist.
2834            
2835             See C<UnlockCategories>.
2836            
2837             =cut
2838              
2839 0     0     my ($self, @categories) = @_ ;
2840              
2841 0           my ($package, $file_name, $line) = caller() ;
2842 0           my $location = "$file_name:$line" ;
2843              
2844 0           for my $category (@categories)
2845             {
2846 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at '$location'!") unless exists $self->{VALID_CATEGORIES}{$category} ;
2847 0           $self->{LOCKED_CATEGORIES}{$category} = 1 ;
2848             }
2849            
2850 0           return(1) ;
2851             }
2852              
2853             #-------------------------------------------------------------------------------
2854            
2855             sub Lock
2856             {
2857              
2858             =head2 Lock(NAME => $variable_name, CATEGORY => $category)
2859            
2860             Locks a variable in the default category or an explicit category. A locked variable can not be set.
2861            
2862             To set a locked variable, B<FORCE_LOCK> can be used. B<FORCE_LOCK> usually pinpoints a problem
2863             in your configuration.
2864            
2865             $config->Lock(NAME => 'CC') ;
2866             $config->Lock(NAME => 'CC', CATEGORY => 'PARENT') ;
2867            
2868             I<Arguments>
2869            
2870             =over 2
2871            
2872             =item * NAME => $variable_name - Name of the variable to lock
2873            
2874             =item * CATEGORY => $category - Name of the category containing the variable
2875            
2876             =back
2877            
2878             I<Returns> - Nothing
2879            
2880             I<Exceptions> - An exception is generated if you try to lock a variable that doesn't exist.
2881            
2882             See C<Set>.
2883            
2884             =cut
2885              
2886 0     0     my ($self, @options) = @_ ;
2887              
2888 0 0         if (@options % 2)
2889             {
2890 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
2891             }
2892              
2893 0           my %options = @options ;
2894              
2895 0 0         unless(defined $options{FILE})
2896             {
2897 0           my ($package, $file_name, $line) = caller() ;
2898            
2899 0           $options{FILE} = $file_name ;
2900 0           $options{LINE} = $line ;
2901             }
2902              
2903 0           my $location = "$options{FILE}:$options{LINE}" ;
2904              
2905 0 0         $options{CATEGORY} = $self->{DEFAULT_CATEGORY} unless exists $options{CATEGORY} ;
2906              
2907 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
2908              
2909 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
2910 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
2911              
2912 0 0         if($self->{VERBOSE})
2913             {
2914 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Locking '$options{CATEGORY}::$options{NAME}' at '$location'.\n") ;
2915             }
2916              
2917 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}})
2918             {
2919 0           my $config_variable = $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}} ;
2920            
2921 0           $config_variable->{LOCKED} = $location ;
2922            
2923 0           my $history = "LOCK. category = '$options{CATEGORY}' at '$options{FILE}:$options{LINE}', status = Lock: OK." ;
2924 0           push @{$config_variable->{HISTORY}}, {TIME => $self->{TIME_STAMP}, EVENT => $history} ;
  0            
2925              
2926 0           $self->{TIME_STAMP}++ ;
2927             }
2928             else
2929             {
2930 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: Locking unexisting '$options{CATEGORY}::$options{NAME}' at '$location'.\n") ;
2931             }
2932              
2933 0           return(1) ;
2934             }
2935              
2936             #-------------------------------------------------------------------------------
2937            
2938             sub UnlockCategories
2939             {
2940              
2941             =head2 UnlockCategories(@categories)
2942            
2943             Unlocks the categories passed as argument.
2944            
2945             $config->UnlockCategories('PARENT', 'OTHER') ;
2946            
2947             I<Arguments>
2948            
2949             =over 2
2950            
2951             =item * @categories - a list of categories to unlock
2952            
2953             =back
2954            
2955             I<Returns> - Nothing
2956            
2957             See C<LockCategories>.
2958            
2959             =cut
2960              
2961              
2962 0     0     my ($self, @categories) = @_ ;
2963              
2964 0           for my $category (@categories)
2965             {
2966 0           delete $self->{LOCKED_CATEGORIES}{$category} ;
2967             }
2968            
2969 0           return(1) ;
2970             }
2971              
2972             #-------------------------------------------------------------------------------
2973              
2974             sub Unlock
2975             {
2976              
2977             =head2 Unlock(NAME => $variable_name, CATEGORY => $category)
2978            
2979             Unlocks a variable in the default category or an explicit category.
2980            
2981             $config->Unlock(NAME => 'CC') ;
2982             $config->Unlock(NAME => 'CC', CATEGORY => 'PARENT') ;
2983            
2984             I<Arguments>
2985            
2986             =over 2
2987            
2988             =item * NAME => $variable_name - Name of the variable to unlock
2989            
2990             =item * CATEGORY => $category - Name of the category containing the variable
2991            
2992             =back
2993            
2994             I<Returns> - Nothing
2995            
2996             I<Exceptions> - An exception is generated if you pass a category that doesn't exist.
2997            
2998             See C<Lock>.
2999            
3000             =cut
3001              
3002 0     0     my ($self, @options) = @_ ;
3003              
3004 0 0         if (@options % 2)
3005             {
3006 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
3007             }
3008              
3009 0           my %options = @options ;
3010              
3011 0 0         unless(defined $options{FILE})
3012             {
3013 0           my ($package, $file_name, $line) = caller() ;
3014            
3015 0           $options{FILE} = $file_name ;
3016 0           $options{LINE} = $line ;
3017             }
3018              
3019 0           my $location = "$options{FILE}:$options{LINE}" ;
3020              
3021 0 0         $options{CATEGORY} = $self->{DEFAULT_CATEGORY} unless exists $options{CATEGORY} ;
3022              
3023 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
3024              
3025 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
3026 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
3027              
3028 0 0         if($self->{VERBOSE})
3029             {
3030 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Unlocking '$options{CATEGORY}::$options{NAME}' at '$location'.\n") ;
3031             }
3032            
3033 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}})
3034             {
3035 0           my $config_variable = $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}} ;
3036            
3037 0           delete $config_variable->{LOCKED} ;
3038            
3039 0           my $history = "UNLOCK. category = '$options{CATEGORY}' at '$options{FILE}:$options{LINE}', status = Unlock: OK." ;
3040 0           push @{$config_variable->{HISTORY}}, {TIME => $self->{TIME_STAMP}, EVENT => $history} ;
  0            
3041            
3042 0           $self->{TIME_STAMP}++ ;
3043             }
3044              
3045 0           return(1) ;
3046             }
3047               
3048             #-------------------------------------------------------------------------------
3049              
3050             sub IsCategoryLocked
3051             {
3052              
3053             =head2 IsCategoryLocked($category)
3054            
3055             Query the lock state of a category.
3056            
3057             $config->IsCategoryLocked('PARENT') ;
3058            
3059             I<Arguments>
3060            
3061             =over 2
3062            
3063             =item * $category - Name of the category containing to query
3064            
3065             =back
3066            
3067             I<Returns> - A boolean
3068            
3069             I<Exceptions> - Querying the lock state of a category that doesn't exist generates an exception.
3070            
3071             =cut
3072              
3073 0     0     my ($self, $category) = @_ ;
3074              
3075 0           my ($package, $file_name, $line) = caller() ;
3076 0           my $location = "$file_name:$line" ;
3077              
3078 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: No category at '$location'!") unless defined $category ;
3079 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at '$location'!") unless exists $self->{VALID_CATEGORIES}{$category} ;
3080              
3081 0 0         if(exists $self->{LOCKED_CATEGORIES}{$category})
3082             {
3083 0           return(1) ;
3084             }
3085             else
3086             {
3087 0           return(0) ;
3088             }
3089              
3090             }
3091              
3092             #-------------------------------------------------------------------------------
3093              
3094             sub IsLocked
3095             {
3096              
3097             =head2 IsLocked(NAME => $variable_name, CATEGORY => $category)
3098            
3099             Query the lock state of a variable.
3100            
3101             $config->IsLocked(NAME => 'CC') ;
3102             $config->IsLocked(NAME => 'CC', CATEGORY => 'PARENT') ;
3103            
3104             I<Arguments>
3105            
3106             =over 2
3107            
3108             =item * NAME => $variable_name - Name of the variable to query
3109            
3110             =item * Optional, CATEGORY => $category - Name of the category containing the variable
3111            
3112             =back
3113            
3114             I<Returns> - A boolean
3115            
3116             I<Exceptions> - Querying the lock state of a variable that doesn't exist does not generate an exception.
3117            
3118             =cut
3119              
3120 0     0     my ($self, @options) = @_ ;
3121              
3122 0 0         if (@options % 2)
3123             {
3124 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
3125             }
3126              
3127 0           my %options = @options ;
3128              
3129 0 0         unless(defined $options{FILE})
3130             {
3131 0           my ($package, $file_name, $line) = caller() ;
3132            
3133 0           $options{FILE} = $file_name ;
3134 0           $options{LINE} = $line ;
3135             }
3136              
3137 0           my $location = "$options{FILE}:$options{LINE}" ;
3138              
3139 0 0         $options{CATEGORY} = $self->{DEFAULT_CATEGORY} unless exists $options{CATEGORY} ;
3140              
3141 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
3142              
3143 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$options{CATEGORY}' at '$location'!") unless exists $self->{VALID_CATEGORIES}{$options{CATEGORY}} ;
3144 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
3145              
3146 0 0         if($self->{VERBOSE})
3147             {
3148 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Checking Lock of '$options{CATEGORY}::$options{NAME}' at '$location'.\n") ;
3149             }
3150            
3151 0           my $locked = undef ;
3152              
3153 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}})
3154             {
3155 0 0         if(exists $self->{CATEGORIES}{$options{CATEGORY}}{$options{NAME}}{LOCKED})
3156             {
3157 0           $locked = 1 ;
3158             }
3159             else
3160             {
3161 0           $locked = 0 ;
3162             }
3163             }
3164              
3165 0           return($locked) ;
3166             }
3167               
3168             #-------------------------------------------------------------------------------
3169              
3170             sub Exists
3171             {
3172              
3173             =head2 Exists(NAME => $variable_name, CATEGORIES_TO_EXTRACT_FROM => \@categories)
3174            
3175             $config->Exists(NAME => 'CC') ;
3176            
3177             Returns B<true> if the variable exist, B<false> otherwise. All the categories are checked.
3178            
3179             I<Arguments>
3180            
3181             =over 2
3182            
3183             =item * NAME => $variable_name - Name of the variable to check
3184            
3185             =item * CATEGORIES_TO_EXTRACT_FROM => \@categories - list of category names
3186            
3187             =back
3188            
3189             I<Returns> - A boolean
3190            
3191             I<Exceptions> - An exception is generated if you pass a category that doesn't exist.
3192            
3193             =cut
3194              
3195 0     0     my ($self, @options) = @_ ;
3196              
3197 0 0         if (@options % 2)
3198             {
3199 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
3200             }
3201              
3202 0           my %options = @options ;
3203              
3204 0 0         unless(defined $options{FILE})
3205             {
3206 0           my ($package, $file_name, $line) = caller() ;
3207            
3208 0           $options{FILE} = $file_name ;
3209 0           $options{LINE} = $line ;
3210             }
3211              
3212 0           my $location = "$options{FILE}:$options{LINE}" ;
3213              
3214 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
3215              
3216 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
3217 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: 'CATEGORY' not used at '$location'!") if exists $options{CATEGORY} ;
3218              
3219 0 0         if($self->{VERBOSE})
3220             {
3221 0           $self->{INTERACTION}{INFO}->("$self->{NAME}: Checking Existance of '$options{NAME}' at '$location'.\n") ;
3222             }
3223            
3224 0           my @categories_to_extract_from ;
3225              
3226 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
3227             {
3228 0 0         if(defined $options{CATEGORIES_TO_EXTRACT_FROM})
3229             {
3230 0           @categories_to_extract_from = @{$options{CATEGORIES_TO_EXTRACT_FROM}} ;
  0            
3231             }
3232             else
3233             {
3234 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: undefined category 'CATEGORIES_TO_EXTRACT_FROM' at '$location'!") ;
3235             }
3236             }
3237             else
3238             {
3239 0           @categories_to_extract_from = @{$self->{CATEGORY_NAMES}} ;
  0            
3240             }
3241            
3242 0           my ($exists) = (0) ;
3243              
3244 0           for my $category (@categories_to_extract_from)
3245             {
3246 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at '$location'!") unless exists $self->{VALID_CATEGORIES}{$category} ;
3247            
3248 0 0         if(exists $self->{CATEGORIES}{$category}{$options{NAME}})
3249             {
3250 0           $exists++ ;
3251             }
3252             }
3253              
3254 0           return($exists) ;
3255             }
3256               
3257             #-------------------------------------------------------------------------------
3258               
3259             sub GetHistory
3260             {
3261              
3262             =head2 GetHistory(NAME => $variable_name, CATEGORIES_TO_EXTRACT_FROM => \@categories)
3263            
3264             Returns a variable history.
3265            
3266             $history = $config->GetHistory(NAME => 'CC') ;
3267             $history = $config->GetHistory(NAME => 'CC', CATEGORIES_TO_EXTRACT_FROM => ['PARENT']) ;
3268            
3269             I<Arguments>
3270            
3271             =over 2
3272            
3273             =item * NAME => $variable_name - Name of the variable to check
3274            
3275             =item * CATEGORIES_TO_EXTRACT_FROM => \@categories - list of category names
3276            
3277             =back
3278            
3279             I<Returns> - Returns a reference to the variable's history or an empty list if the variable doesn't exist.
3280            
3281             my $config = new Config::Hierarchical
3282             (
3283             NAME => 'Test config',
3284            
3285             CATEGORY_NAMES => ['PARENT', 'CURRENT'],
3286             DEFAULT_CATEGORY => 'CURRENT',
3287            
3288             INITIAL_VALUES =>
3289             [
3290             {NAME => 'CC', CATEGORY => 'PARENT', VALUE => 'parent'},
3291             ] ,
3292             ) ;
3293            
3294             $config->Set(NAME => 'CC', OVERRIDE => 1, VALUE => 'override value') ;
3295            
3296             my($value, $category) = $config->Get(NAME => 'CC', GET_CATEGORY => 1) ;
3297            
3298             my $title = "'CC' = '$value' from category '$category':" ;
3299             print DumpTree($config->GetHistory(NAME=> 'CC'), $title, DISPLAY_ADDRESS => 0) ;
3300            
3301             Would print as:
3302            
3303             'CC' = 'override value' from category 'CURRENT':
3304             |- 0
3305             | |- EVENT = . CREATE AND SET. value = 'parent', category = 'PARENT' at 'nadim2.pl:21', status = OK.
3306             | `- TIME = 0
3307             `- 1
3308             |- EVENT = value = CREATE AND SET, OVERRIDE. 'override value', category = 'CURRENT' at 'nadim2.pl:34', status =
3309             | Overriding 'PARENT::CC' (existed, value was different).OK.
3310             `- TIME = 1
3311            
3312             while
3313            
3314             my($value, $category) = $config->Get(NAME => 'CC', GET_CATEGORY => 1, CATEGORIES_TO_EXTRACT_FROM => ['PARENT']) ;
3315            
3316             my $title = "'CC' = '$value' from category '$category':" ;
3317             print DumpTree($config->GetHistory(NAME=> 'CC', CATEGORIES_TO_EXTRACT_FROM => ['PARENT']), $title, DISPLAY_ADDRESS => 0) ;
3318            
3319             Would print as:
3320            
3321             'CC' = 'parent' from category 'PARENT':
3322             `- 0
3323             |- EVENT = value = CREATE AND SET. 'parent', category = 'PARENT' at 'nadim2.pl:21', status = OK.
3324             `- TIME = 0
3325            
3326             =head3 Explicit history and comments
3327            
3328             If you passed a B<HISTORY> or a B<COMMENT> when you created or modified a variable, that information
3329             will be included in the history structure returned by B<GetHistory>.
3330            
3331             my $config3 = new Config::Hierarchical
3332             (
3333             NAME => 'config3',
3334             ...
3335             INITIAL_VALUES =>
3336             [
3337             {
3338             COMMENT => "history and value from category 2",
3339             NAME => 'CC', CATEGORY => 'PARENT', VALUE => $value2,
3340             HISTORY => $history2,
3341             },
3342             ] ,
3343             ...
3344             ) ;
3345            
3346             my($value3, $category3) = $config3->Get(NAME => 'CC', GET_CATEGORY => 1) ;
3347             my $title3 = "'CC' = '$value3' from category '$category3':" ;
3348             my $history3 = $config3->GetHistory(NAME=> 'CC') ;
3349             print DumpTree($history3, $title3, DISPLAY_ADDRESS => 0) ;
3350            
3351             Would print as:
3352            
3353             'CC' = '3' from category 'PARENT':
3354             |- 0
3355             | |- COMMENT = history and value from config 2
3356             | |- EVENT = CREATE, SET HISTORY AND SET. value = '3', category = 'PARENT' at 'history.pl:56', status = OK.
3357             | |- HISTORY
3358             | | |- 0
3359             ...
3360            
3361             =head3 Aliased category history
3362            
3363             if you used an aliased category, The history structure returned by B<GetHistory> will automatically include the
3364             history of the aliased config.
3365            
3366             my $config0 = (...) ;
3367             my $config1 = (...) ;
3368             my $config2 = new Config::Hierarchical
3369             (
3370             ...
3371             INITIAL_VALUES =>
3372             [
3373             {
3374             CATEGORY => 'PBS',
3375             ALIAS_CATEGORY => $config1,
3376             HISTORY => ....,
3377             COMMENT => ....,
3378             },
3379             ...
3380             ) ;
3381            
3382             ...
3383             print DumpTree $config_3->GetHistory( NAME => 'CC1'), 'CC1', DISPLAY_ADDRESS => 0;
3384            
3385             Would print as:
3386            
3387             CC1
3388             |- 0
3389             | |- HISTORY FROM ALIASED CATEGORY 'config 1'
3390             | | |- 0
3391             | | | |- HISTORY FROM ALIASED CATEGORY 'config 0'
3392             | | | | `- 0
3393             | | | | |- EVENT = CREATE AND SET. value = '1', category = 'CURRENT' at 'nadim.pl:21', status = OK.
3394             | | | | `- TIME = 0
3395             | | | `- TIME = 2
3396             | | |- 1
3397             | | | |- EVENT = CREATE AND SET. value = '1', category = 'A' at 'nadim.pl:33', status = OK.
3398             | | | `- TIME = 3
3399             | | `- 2
3400             | | |- EVENT = Set. value = '1.1', category = 'A' at 'nadim.pl:50', status = OK.
3401             | | `- TIME = 6
3402             | `- TIME = 3
3403             |- 1
3404             | |- EVENT = CREATE AND SET, OVERRIDE. value = 'A', category = 'A' at 'nadim.pl:64', status = OK.
3405             | `- TIME = 4
3406             `- 2
3407             |- EVENT = SET, OVERRIDE. value = 'A2', category = 'A' at 'nadim.pl:65', status = OK.
3408             `- TIME = 5
3409            
3410             =head4 Compact display
3411            
3412             Given the following Data::TreeDumper filter
3413            
3414             sub Compact
3415             {
3416             my ($s, $level, $path, $keys, $setup, $arg) = @_ ;
3417            
3418             if('ARRAY' eq ref $s)
3419             {
3420             my ($index, @replacement, @keys) = (0) ;
3421            
3422             for my $entry( @$s)
3423             {
3424             if(exists $entry->{EVENT})
3425             {
3426             push @replacement, $entry->{EVENT} ; #. 'time: ' . $entry->{TIME};
3427             push@keys, $index++ ;
3428             }
3429             else
3430             {
3431             my ($aliased_history_name) = grep {$_ ne 'TIME'} keys %$entry ;
3432            
3433             push @replacement, $entry->{$aliased_history_name} ;
3434             push@keys, [$index, "$index = $aliased_history_name"] ;
3435             $index++ ;
3436             }
3437             }
3438            
3439             return('ARRAY', \@replacement, @keys) ;
3440             }
3441             }
3442            
3443             print DumpTree $config_2->GetHistory( NAME => 'CC1'), 'CC1', DISPLAY_ADDRESS => 0, FILTER => \&Compact ;
3444            
3445             the above output becomes:
3446            
3447             CC1
3448             |- 0 = HISTORY FROM ALIASED CATEGORY 'config 1'
3449             | |- 0 = HISTORY FROM ALIASED CATEGORY 'config 0'
3450             | | `- 0 = CREATE AND SET. value = '1', category = 'CURRENT' at 'nadim.pl:21', status = OK.
3451             | |- 1 = CREATE AND SET. value = '1', category = 'A' at 'nadim.pl:33', status = OK.
3452             | `- 2 = SET. value = '1.1', category = 'A' at 'nadim.pl:50', status = OK.
3453             |- 1 = CREATE AND SET, OVERRIDE. value = 'A', category = 'A' at 'nadim.pl:64', status = OK.
3454             `- 2 = SET, OVERRIDE. value = 'A2', category = 'A' at 'nadim.pl:65', status = OK.
3455            
3456             Note that comments are also removed.
3457            
3458             =cut
3459              
3460 0     0     my ($self, @options) = @_ ;
3461              
3462 0 0         if (@options % 2)
3463             {
3464 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
3465             }
3466              
3467 0           my %options = @options ;
3468              
3469 0 0         unless(defined $options{FILE})
3470             {
3471 0           my ($package, $file_name, $line) = caller() ;
3472            
3473 0           $options{FILE} = $file_name ;
3474 0           $options{LINE} = $line ;
3475             }
3476              
3477 0           my $location = "$options{FILE}:$options{LINE}" ;
3478              
3479 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
3480              
3481 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
3482 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: bad argument 'CATEGORY' did you mean 'CATEGORIES_TO_EXTRACT_FROM'? at '$location'!") if exists $options{CATEGORY} ;
3483              
3484 0           my @history ;
3485             my @categories_to_extract_from ;
3486              
3487 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
3488             {
3489 0 0         if(defined $options{CATEGORIES_TO_EXTRACT_FROM})
3490             {
3491 0           @categories_to_extract_from = @{$options{CATEGORIES_TO_EXTRACT_FROM}} ;
  0            
3492             }
3493             else
3494             {
3495 0           $self->{INTERACTION}{DIE}->("$self->{NAME}: undefined category 'CATEGORIES_TO_EXTRACT_FROM' at '$location'!") ;
3496             }
3497             }
3498             else
3499             {
3500 0           @categories_to_extract_from = @{$self->{CATEGORY_NAMES}} ;
  0            
3501             }
3502            
3503 0           for my $category (@categories_to_extract_from)
3504             {
3505 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid category '$category' at '$location'!") unless exists $self->{VALID_CATEGORIES}{$category} ;
3506            
3507 0           push @history, $self->GetVariableHistory($category, $options{NAME}) ;
3508             }
3509            
3510 0           @history = sort {$a->{TIME} <=> $b->{TIME}} @history ;
  0            
3511              
3512 0           return(\@history) ;
3513             }
3514              
3515             #-------------------------------------------------------------------------------
3516              
3517             sub GetVariableHistory
3518             {
3519            
3520             =head2 [p] GetVariableHistory
3521            
3522             This shall not be used directly. Use C<GetHistory>.
3523            
3524             =cut
3525              
3526 0     0     my ($self, $category, $name) = @_ ;
3527              
3528 0 0         if(exists $self->{ALIASED_CATEGORIES}{$category})
3529             {
3530 0           my $aliased = tied(%{ $self->{CATEGORIES}{$category} }) ;
  0            
3531 0           my $aliased_history = $aliased->{CONFIG}->GetHistory(NAME => $name) ;
3532            
3533 0 0         if(@{$aliased_history})
  0            
3534             {
3535             return 
3536             {
3537 0           "HISTORY FROM '$category' ALIASED TO '$aliased->{CONFIG}{NAME}'" => $aliased_history,
3538             TIME => $self->{ALIASED_CATEGORIES}{$category}{TIME_STAMP},
3539             } ;
3540             }
3541             else
3542             {
3543 0           return ;
3544             }
3545             }
3546             else
3547             {
3548 0 0         if(exists $self->{CATEGORIES}{$category}{$name})
3549             {
3550 0           return(@{$self->{CATEGORIES}{$category}{$name}{HISTORY}}) ;
  0            
3551             }
3552             else
3553             {
3554 0           return  ;
3555             }
3556             }
3557             }
3558              
3559             #-------------------------------------------------------------------------------
3560              
3561             sub GetHistoryDump
3562             {
3563              
3564             =head2 GetHistoryDump(@named_arguments)
3565            
3566             Returns a dump, of the variable history, generated by B<Data::TreeDumper::DumpTree>.
3567            
3568             $dump = $config->GetHistoryDump(NAME => 'CC') ;
3569            
3570             $dump = $config->GetHistoryDump(CATEGORIES_TO_EXTRACT_FROM => ['A', 'B'], NAME => 'CC', DATA_TREEDUMPER_OPTIONS => []) ;
3571            
3572             I<Arguments>
3573            
3574             =over 2
3575            
3576             =item * NAME => $variable_name - Name of the variable to check
3577            
3578             =item * Optional, CATEGORIES_TO_EXTRACT_FROM => \@categories - list of category names
3579            
3580             =back
3581            
3582             I<Returns> - Returns a reference to the variable's history or an empty list if the variable doesn't exist.
3583            
3584            
3585             See L<Data::TreeDumper>.
3586            
3587             =cut
3588              
3589 0     0     my ($self, @options) = @_ ;
3590              
3591 0 0         if (@options % 2)
3592             {
3593 0           $self->{INTERACTION}{DIE}->('Invalid number of argument!') ;
3594             }
3595              
3596 0           my %options = @options ;
3597              
3598 0           $self->CheckOptionNames($VALID_OPTIONS, %options) ;
3599              
3600 0 0         unless(defined $options{FILE})
3601             {
3602 0           my ($package, $file_name, $line) = caller() ;
3603            
3604 0           $options{FILE} = $file_name ;
3605 0           $options{LINE} = $line ;
3606             }
3607              
3608 0           my $location = "$options{FILE}:$options{LINE}" ;
3609              
3610 0 0         $self->{INTERACTION}{DIE}->("$self->{NAME}: Missing name at '$location'!") unless defined $options{NAME} ;
3611              
3612 0           my ($config_name, $config_location) = $self->GetInformation() ;
3613 0           my $config_information = "from config '$config_name' created at '$config_location'" ;
3614              
3615 0           my @categories_to_extract_from ;
3616 0 0         if(exists $options{CATEGORIES_TO_EXTRACT_FROM})
3617             {
3618 0           @categories_to_extract_from = (CATEGORIES_TO_EXTRACT_FROM => $options{CATEGORIES_TO_EXTRACT_FROM}) ;
3619             }
3620            
3621 0           my @data_treedumper_options ;
3622 0 0         if(exists $options{DATA_TREEDUMPER_OPTIONS})
3623             {
3624 0           @data_treedumper_options = @{ $options{DATA_TREEDUMPER_OPTIONS} } ;
  0            
3625             }
3626              
3627             return
3628             (
3629 0           DumpTree
3630             (
3631             $self->GetHistory(NAME => $options{NAME}, @categories_to_extract_from),
3632             "History for variable '$options{NAME}' $config_information:",
3633             DISPLAY_ADDRESS => 0,
3634             @data_treedumper_options
3635             ) 
3636             ) ;
3637             }
3638              
3639             #-------------------------------------------------------------------------------
3640              
3641             sub GetAccessLog
3642             {
3643              
3644             =head2 GetAccessLog()
3645            
3646             Returns a list of all the B<Config::Hierarchical> accesses.
3647            
3648             my $config = new Config::Hierarchical( LOG_ACCESS => 1, ...) ;
3649            
3650             my $value = $config->Get(NAME => 'A') ;
3651             $value = $config->Get(NAME => 'B') ;
3652             $value = $config->Get(NAME => 'A', CATEGORIES_TO_EXTRACT_FROM => ['PARENT']) ;
3653            
3654             my $access_log = $config->GetAccessLog() ;
3655            
3656             would return the following structure :
3657            
3658             access log:
3659             |- 0
3660             | |- FILE = test.pl
3661             | |- LINE = 28
3662             | `- NAME = A
3663             |- 1
3664             | |- FILE = test.pl
3665             | |- LINE = 29
3666             | `- NAME = B
3667             `- 2
3668             |- CATEGORIES_TO_EXTRACT_FROM
3669             | `- 0 = PARENT
3670             |- FILE = test.pl
3671             |- LINE = 30
3672             `- NAME = A
3673            
3674             I<Arguments> - None
3675            
3676             I<Returns> - An array reference containing all the read accesses.
3677            
3678             If B<LOG_ACCESS> was not set, an empty array reference is returned.
3679            
3680             =cut
3681              
3682 0     0     my ($self) = @_ ;
3683              
3684 0 0         if(exists $self->{ACCESS_TO_VARIABLE})
3685             {
3686 0           return($self->{ACCESS_TO_VARIABLE}) ;
3687             }
3688             else
3689             {
3690 0           return [] ;
3691             }
3692             }
3693              
3694             #-------------------------------------------------------------------------------
3695              
3696             sub GetDump
3697             {
3698              
3699             =head2 GetDump()
3700            
3701             $dump = $config->GetDump(@data_treedumper_options) ;
3702             $dump = $config->GetDump(@data_treedumper_options) ;
3703            
3704             I<Arguments>
3705            
3706             =over 2
3707            
3708             =item * @data_treedumper_options - A list of options forwarded to L<Data::TreeDumper::DumpTree>.
3709            
3710             =back
3711            
3712             I<Returns>
3713            
3714             A dump, of the Config::Hierarchical object, generated by L<Data::TreeDumper::DumpTree>.
3715            
3716             See L<Data::TreeDumper>.
3717            
3718             =cut
3719              
3720             my ($self, @data_treedumper_options) = @_ ;
3721              
3722             my ($package, $file_name, $line) = caller() ;
3723              
3724 9     9   945087 use Data::TreeDumper ;
  0         0  
  0         0  
3725              
3726             my $sort_categories =
3727             sub
3728             {
3729             # DTD dumps hash with sorted keys
3730             # we display the categories in hierarchical order
3731            
3732             my ($s, $level, $path, $keys) = @_ ;
3733            
3734             if($level == 1 && $path eq q<{'CATEGORIES'}>)
3735             {
3736             return('HASH', undef, @{$self->{CATEGORY_NAMES}}) ;
3737             }
3738             else
3739             {
3740             return(Data::TreeDumper::DefaultNodesToDisplay($s)) ;
3741             }
3742             } ;
3743            
3744             return(DumpTree($self, $self->{NAME}, FILTER => $sort_categories, @data_treedumper_options)) ;
3745             }
3746               
3747             #-------------------------------------------------------------------------------
3748              
3749             1 ;
3750              
3751             =head1 BUGS AND LIMITATIONS
3752            
3753             None so far.
3754            
3755             =head1 AUTHOR
3756            
3757             Khemir Nadim ibn Hamouda
3758             CPAN ID: NKH
3759             mailto:nadim@khemir.net
3760            
3761             =head1 LICENSE AND COPYRIGHT
3762            
3763             Copyright 2006-2007 Khemir Nadim. All rights reserved.
3764            
3765             This program is free software; you can redistribute
3766             it and/or modify it under the same terms as Perl itself.
3767            
3768             =head1 SUPPORT
3769            
3770             You can find documentation for this module with the perldoc command.
3771            
3772             perldoc Config::Hierarchical
3773            
3774             You can also look for information at:
3775            
3776             =over 4
3777            
3778             =item * AnnoCPAN: Annotated CPAN documentation
3779            
3780             L<http://annocpan.org/dist/Config-Hierarchical>
3781            
3782             =item * RT: CPAN's request tracker
3783            
3784             Please report any bugs or feature requests to L <bug-config-hierarchical@rt.cpan.org>.
3785            
3786             We will be notified, and then you'll automatically be notified of progress on
3787             your bug as we make changes.
3788            
3789             =item * Search CPAN
3790            
3791             L<http://search.cpan.org/dist/Config-Hierarchical>
3792            
3793             =back
3794            
3795             =head1 SEE ALSO
3796            
3797             L<Config::Hierarchical::Tie::ReadOnly>
3798            
3799             L<Config::Hierarchical::Delta>
3800            
3801             =cut
3802