File Coverage

blib/lib/Config/ReadAndCheck.pm
Criterion Covered Total %
statement 15 162 9.2
branch 0 82 0.0
condition 0 12 0.0
subroutine 5 17 29.4
pod 9 9 100.0
total 29 282 10.2


line stmt bran cond sub pod time code
1             package Config::ReadAndCheck;
2              
3 1     1   5719 use strict;
  1         2  
  1         37  
4              
5             #$^W++;
6              
7 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         173  
8              
9             require Exporter;
10              
11             @ISA = qw(Exporter);
12             # Items to export into callers namespace by default. Note: do not export
13             # names by default without a very good reason. Use EXPORT_OK instead.
14             # Do not simply export all your public functions/methods/constants.
15             @EXPORT = qw(
16             );
17              
18             %EXPORT_TAGS = ('print' => [qw(PrintList)],
19             );
20              
21             foreach (keys(%EXPORT_TAGS))
22             { push(@{$EXPORT_TAGS{'all'}}, @{$EXPORT_TAGS{$_}}); };
23              
24             $EXPORT_TAGS{'all'}
25             and @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
26              
27             @EXPORT = qw(
28             );
29              
30             $VERSION = '0.04';
31              
32 1     1   5 use Carp;
  1         7  
  1         72  
33 1     1   833 use IO::File;
  1         14401  
  1         123  
34 1     1   948 use Tie::IxHash;
  1         4948  
  1         3711  
35              
36             my $CheckLoop = undef;
37             $CheckLoop = sub
38             {
39             my ($Params, $Dupes, $Path) = @_;
40             if (!($Dupes && $Path))
41             {
42             $Dupes = {};
43             $Path = 'root'
44             };
45             my $Name = undef;
46             foreach $Name (keys(%{$Params}))
47             {
48             if ($Params->{$Name}->{'SubSection'})
49             {
50             if (exists($Dupes->{$Params->{$Name}->{'SubSection'}}))
51             {
52             $@ = "SubSection loop found: \"$Path\->$Name\"";
53             return 1;
54             };
55             $Dupes->{$Params->{$Name}->{'SubSection'}}++;
56             if (&{$CheckLoop}($Params->{$Name}->{'SubSection'}, $Dupes, "$Path\->$Name"))
57             { return 1; };
58             delete($Dupes->{$Params->{$Name}->{'SubSection'}});
59             };
60             };
61             return 0;
62             };
63              
64             my $CheckParams = undef;
65             $CheckParams = sub
66             {
67             my ($Params, $Path) = @_;
68              
69             my $NewParams = undef;
70             tie(%{$NewParams}, 'Tie::IxHash');
71              
72             ($Path) or $Path = 'root';
73              
74             my $SimpleProcess = sub() { return ($1, $2); };
75              
76             my $Name = undef;
77             foreach $Name (keys(%{$Params}))
78             {
79             if (ref($Params->{$Name}) ne 'HASH')
80             {
81             $@ = "\"$Path\->$Name\" have to be a 'HASH' reference!";
82             #if($^W) { Carp::carp $@; };
83             return;
84             };
85              
86             if (!defined($Params->{$Name}->{'Pattern'}))
87             {
88             $@ = "\"$Path\->$Name\->{'Pattern'}\" have to be defined!";
89             #if($^W) { Carp::carp $@; };
90             return;
91             };
92              
93             $NewParams->{$Name}->{'Pattern'} = $Params->{$Name}->{'Pattern'};
94              
95             $NewParams->{$Name}->{'Type'} = $Params->{$Name}->{'Type'}
96             or $NewParams->{$Name}->{'Type'} = 'UNIQ';
97             $NewParams->{$Name}->{'Type'} = uc($NewParams->{$Name}->{'Type'});
98              
99             if (!(($NewParams->{$Name}->{'Type'} eq 'UNIQ') ||
100             ($NewParams->{$Name}->{'Type'} eq 'LIST') ||
101             ($NewParams->{$Name}->{'Type'} eq 'UNIQLIST') ||
102             ($NewParams->{$Name}->{'Type'} eq 'IGNORE')))
103             {
104             $@ = "$Path\->$Name\->{'Type'} have to be 'UNIQ', or 'LIST', or 'UNIQLIST', or 'IGNORE'!";
105             #if($^W) { Carp::carp $@; };
106             return;
107             };
108              
109             $NewParams->{$Name}->{'Process'} = $Params->{$Name}->{'Process'}
110             or $NewParams->{$Name}->{'Process'} = $SimpleProcess;
111              
112             if (ref($NewParams->{$Name}->{'Process'}) ne 'CODE')
113             {
114             $@ = "$Path\->$Name\->{'Process'} have to be a 'CODE' reference!";
115             #if($^W) { Carp::carp $@; };
116             return;
117             };
118              
119             (!exists($Params->{$Name}->{'Default'}))
120             or $NewParams->{$Name}->{'Default'} = $Params->{$Name}->{'Default'};
121              
122             defined($Params->{$Name}->{'SubSection'})
123             or next;
124              
125             if (ref($Params->{$Name}->{'SubSection'}) ne 'HASH')
126             {
127             $@ = "$Path\->$Name\->{'SubSection'} have to be reference to 'HASH'!";
128             #if($^W) { Carp::carp $@; };
129             return;
130             };
131              
132             $NewParams->{$Name}->{'SubSection'} = &{$CheckParams}($Params->{$Name}->{'SubSection'}, "$Path\->$Name")
133             or return;
134             };
135             return $NewParams;
136             };
137              
138             sub new($%)
139             {
140 0     0 1   my ($class, %Config) = @_;
141              
142 0 0         (!&{$CheckLoop}($Config{'Params'}))
  0            
143             or return;
144              
145 0           my $self = {};
146              
147 0           $self->{'CaseSens'} = $Config{'CaseSens'};
148              
149 0 0         $self->{'Params'} = &{$CheckParams}($Config{'Params'})
  0            
150             or return;
151              
152 0           Reset($self);
153              
154 0           return bless $self => $class;
155             };
156              
157             sub Result($)
158             {
159 0     0 1   my ($self) = @_;
160 0           my %Result = ();
161 0           tie(%Result, 'Tie::IxHash', %{$self->{'Result'}});
  0            
162 0 0         return (wantarray ? %Result : \%Result);
163             };
164              
165             sub Reset($)
166             {
167 0     0 1   my ($self) = @_;
168 0           tie(%{$self->{'Result'}}, 'Tie::IxHash');
  0            
169 0           $self->{'SecStack'} = [];
170 0           unshift(@{$self->{'SecStack'}}, {'Params' => $self->{'Params'},'Result' => $self->{'Result'}});
  0            
171             };
172              
173             sub Params($)
174             {
175 0     0 1   my ($self) = @_;
176 0           my $Params = &{$CheckParams}($self->{'Params'});
  0            
177 0 0         return (wantarray ? %{$Params} : $Params);
  0            
178             };
179              
180             my $ParseLine = sub($$)
181             {
182             my ($self, $Str, $Params) = @_;
183              
184             my $Name = undef;
185              
186             #print "###########################\n".PrintList($Params, 'p: ', ' ');
187              
188             foreach $Name (keys(%{$Params}))
189             {
190             my $Pattern = $Params->{$Name}->{'Pattern'};
191              
192             my ($P1, $P2);
193             ($self->{'CaseSens'} ? $Str =~ m/\A$Pattern\Z/ : $Str =~ m/\A$Pattern\Z/i)
194             or next;
195             $@ = '';
196             if (!(($P1, $P2) = &{$Params->{$Name}->{'Process'}}()))
197             {
198             length($@)
199             or $@ = "Invalid value(s) in '$Name' definition";
200             #if($^W) { Carp::carp $@; };
201             return;
202             };
203             return ($Name, $P1, $P2);
204             };
205              
206             $@ = "Unrecognized string";
207             #if($^W) { Carp::carp $@; };
208             return;
209             };
210              
211             my $CheckRequired = undef;
212             $CheckRequired = sub
213             {
214             my ($Params, $Result, $Path) = @_;
215              
216             defined($Path)
217             or $Path = 'root';
218              
219             my $Key = undef;
220             foreach $Key (keys(%{$Params}))
221             {
222             ($Params->{$Key}->{'Type'} ne 'IGNORE')
223             or next;
224             if (!exists($Result->{$Key}))
225             {
226             if (!exists($Params->{$Key}->{'Default'}))
227             {
228             $@ = "Required parameter $Path\->$Key is not defined";
229             #if($^W) { Carp::carp $@; };
230             return;
231             };
232              
233             if ($Params->{$Key}->{'Type'} eq 'LIST')
234             {
235             if (ref($Params->{$Key}->{'Default'}) ne 'ARRAY')
236             {
237             $@ = "$Path\->$Key\->{'Default'} have to be an 'ARRAY' reference";
238             #if($^W) { Carp::carp $@; };
239             return;
240             };
241             @{$Result->{$Key}} = @{$Params->{$Key}->{'Default'}};
242             }
243             elsif ($Params->{$Key}->{'Type'} eq 'UNIQLIST')
244             {
245             if (ref($Params->{$Key}->{'Default'}) ne 'HASH')
246             {
247             $@ = "$Path\->$Key\->{'Default'} have to be a 'HASH' reference";
248             #if($^W) { Carp::carp $@; };
249             return;
250             };
251             tie(%{$Result->{$Key}}, 'Tie::IxHash', %{$Params->{$Key}->{'Default'}});
252             }
253             else
254             { $Result->{$Key} = $Params->{$Key}->{'Default'}; };
255             };
256             if ($Params->{$Key}->{'SubSection'})
257             {
258             #print "$Path\->$Key\->{'SubSection'}\n";
259             my @SubResults = ();
260              
261             if ($Params->{$Key}->{'Type'} eq 'UNIQ')
262             { $SubResults[0] = $Result->{$Key}; }
263             elsif ($Params->{$Key}->{'Type'} eq 'LIST')
264             { @SubResults = @{$Result->{$Key}}; }
265             elsif ($Params->{$Key}->{'Type'} eq 'UNIQLIST')
266             { @SubResults = values(%{$Result->{$Key}}); };
267              
268             my $SubResult = undef;
269             foreach $SubResult (@SubResults)
270             {
271             if (ref($SubResult) ne 'HASH')
272             {
273             $@ = "Value of parameter $Path\->$Key have to be a 'HASH' reference because of 'SubSection' defined for it";
274             #if($^W) { Carp::carp $@; };
275             return;
276             };
277             &{$CheckRequired}($Params->{$Key}->{'SubSection'}, $SubResult, "$Path\->$Key")
278             or return;
279             };
280             };
281             };
282              
283             return (wantarray ? %{$Result} : $Result);
284             };
285              
286             sub CheckRequired($)
287             {
288 0     0 1   my ($self);
289 0           ($self) = @_;
290              
291 0           my %Result = ();
292 0           tie(%Result, 'Tie::IxHash');
293 0 0         %Result = &{$CheckRequired}($self->{'Params'}, $self->{'Result'})
  0            
294             or return;
295 0 0         return (wantarray ? %Result : \%Result);
296             };
297              
298             my $ParseGetline = sub($$)
299             {
300             my ($self, $GetLine) = @_;
301              
302             my $Line = 0;
303             my $Str = undef;
304             while ($Str = &{$GetLine}())
305             {
306             $Line++;
307             $Str =~ s/\n//g;
308              
309             if (!ParseIncremental($self, $Str))
310             {
311             $@ = "$@, line $Line: \"$Str\"";
312             #if($^W) { Carp::carp $@; };
313             return;
314             };
315             defined($self->{'SecStack'}->[0])
316             or last;
317             };
318              
319             if ($self->{'Params'}->{'EndOfSection'} &&
320             $self->{'SecStack'}->[0])
321             {
322             $@ = "Input dry up before 'EndOfSection' reached";
323             return;
324             };
325              
326             CheckRequired($self)
327             or return;
328              
329             return (wantarray ? %{$self->{'Result'}} : $self->{'Result'});
330             };
331              
332             sub Parse($$)
333             {
334 0     0 1   my ($self, $Input) = @_;
335 0           my $GetLine = undef;
336 0           my $RunIndex = 0;
337 0 0         if (!ref($Input))
    0          
    0          
338             {
339 0           my @tmpArray = split('\n', $Input);
340 0           $Input = \@tmpArray;
341 0     0     $GetLine = sub{return $Input->[$RunIndex++]};
  0            
342             }
343             elsif (ref($Input) eq 'CODE')
344             {
345 0           $GetLine = $Input;
346             }
347             elsif (ref($Input) eq 'ARRAY')
348             {
349 0     0     $GetLine = sub{return $Input->[$RunIndex++]};
  0            
350             }
351             else
352             {
353 0           $@ = "Can not use reference to ".ref($Input)." as an input source";
354 0           return;
355             };
356              
357 0           my %Result = ();
358 0           tie(%Result, 'Tie::IxHash');
359 0 0         %Result = &{$ParseGetline}($self, $GetLine)
  0            
360             or return;
361              
362 0 0         return (wantarray ? %Result : \%Result);
363             };
364              
365             sub ParseFile($$)
366             {
367 0     0 1   my ($self, $FileName) = @_;
368              
369 0           my $File = IO::File->new("< $FileName");
370 0 0         if (!$File)
371             {
372 0           $@ = "Can not open file \"$FileName\" for read";
373 0           return;
374             };
375              
376 0           my %Result = ();
377 0           tie(%Result, 'Tie::IxHash');
378 0     0     %Result = &{$ParseGetline}($self, sub{return $File->getline()})
  0            
379 0 0         or return;
380              
381 0           $File->close();
382              
383 0 0         return (wantarray ? %Result : \%Result);
384             };
385              
386             my $UnshiftSubSecIfNecessary = sub
387             {
388             my ($self, $Params, $Result) = @_;
389              
390             if (!tied(%{$Result}))
391             {
392             tie(%{$Result}, 'Tie::IxHash', %{$Result});
393             $_[2] = $Result;
394             };
395              
396             unshift(@{$self->{'SecStack'}}, {'Params' => $Params, 'Result' => $Result});
397              
398             return $Params;
399             };
400              
401             sub ParseIncremental($$)
402             {
403 0     0 1   my ($self, $Str) = @_;
404              
405 0           my ($Name, $P1, $P2);
406              
407 0           my $Params = undef;
408 0           while($Params = $self->{'SecStack'}->[0]->{'Params'})
409             {
410 0 0         (!(($Name, $P1, $P2) = &{$ParseLine}($self, $Str, $Params)))
  0            
411             or last;
412              
413 0 0         (!defined($Params->{'EndOfSection'}))
414             or return;
415              
416 0           shift(@{$self->{'SecStack'}})
  0            
417             };
418              
419 0 0         defined($Name)
420             or return;
421              
422             #print "$Name, $P1, $P2\n";
423              
424 0           my $Type = $Params->{$Name}->{'Type'};
425 0           my $Result = $self->{'SecStack'}->[0]->{'Result'};
426              
427 0 0         if ($Type eq 'UNIQ')
    0          
    0          
428             {
429 0 0         if (exists($Result->{$Name}))
430             {
431 0           $@ = "Duplicate '$Name' definition";
432             #if($^W) { Carp::carp $@; };
433 0           return;
434             };
435              
436 0           $Result->{$Name} = $P1;
437              
438 0 0         if (defined($Params->{$Name}->{'SubSection'}))
439             {
440 0 0 0       if (defined($Result->{$Name}) && (ref($Result->{$Name}) ne 'HASH'))
441             {
442 0           print $Result->{$Name}.', '.ref($Result->{$Name})."\n";
443 0           $@ = "{'$Name'}->{'Process'} have to return refrence to 'HASH' because of {'$Name'}->{'SubSection'} property is defined";
444 0           return;
445             }
446             else
447             {
448 0           &{$UnshiftSubSecIfNecessary}($self, $Params->{$Name}->{'SubSection'}, $Result->{$Name});
  0            
449             };
450              
451             };
452             }
453             elsif ($Type eq 'LIST')
454             {
455 0 0         (ref($Result->{$Name}) eq 'ARRAY')
456             or $Result->{$Name} = [];
457              
458 0           push(@{$Result->{$Name}}, $P1);
  0            
459              
460 0 0         if (defined($Params->{$Name}->{'SubSection'}))
461             {
462 0 0 0       if (defined($Result->{$Name}->[$#{$Result->{$Name}}]) &&
  0            
  0            
463             (ref($Result->{$Name}->[$#{$Result->{$Name}}]) ne 'HASH'))
464             {
465 0           print $Result->{$Name}->[$#{$Result->{$Name}}].', '.ref($Result->{$Name}->[$#{$Result->{$Name}}])."\n";
  0            
  0            
466 0           $@ = "{'$Name'}->{'Process'} have to return refrence to 'HASH' because of {'$Name'}->{'SubSection'} property is defined";
467 0           return;
468             }
469             else
470             {
471 0           &{$UnshiftSubSecIfNecessary}($self, $Params->{$Name}->{'SubSection'}, $Result->{$Name}->[$#{$Result->{$Name}}]);
  0            
  0            
472             };
473              
474             };
475             }
476             elsif ($Type eq 'UNIQLIST')
477             {
478             #print "$Name'->'$P1': \"$Result->{$Name}->{$P1}\"\n";
479 0 0         if (ref($Result->{$Name}) ne 'HASH')
480             {
481 0           $Result->{$Name} = undef;
482 0           tie(%{$Result->{$Name}}, 'Tie::IxHash');
  0            
483             };
484              
485 0 0         if (!defined($P1))
486             {
487 0           $@ = "{'$Name'}->{'Process'} have to return defined value as a first element of the list";
488 0           return;
489             };
490              
491 0 0         if (exists($Result->{$Name}->{$P1}))
492             {
493 0           $@ = "Duplicate '$Name'->'$P1' definition";
494 0           $@ .= ", the value is \"$Result->{$Name}->{$P1}\"";
495             #if($^W) { Carp::carp $@; };
496 0           return;
497             };
498              
499 0           $Result->{$Name}->{$P1} = $P2;
500              
501 0 0         if (defined($Params->{$Name}->{'SubSection'}))
502             {
503 0 0 0       if (defined($Result->{$Name}->{$P1}) && (ref($Result->{$Name}->{$P1}) ne 'HASH'))
504             {
505 0           $@ = "{'$Name'}->{'Process'} have to return refrence to 'HASH' because of {'$Name'}->{'SubSection'} property is defined";
506 0           return;
507             }
508             else
509             {
510 0           &{$UnshiftSubSecIfNecessary}($self, $Params->{$Name}->{'SubSection'}, $Result->{$Name}->{$P1});
  0            
511             };
512              
513             };
514             };
515              
516 0           ($Name ne 'EndOfSection')
517 0 0         or shift(@{$self->{'SecStack'}});
518              
519 0           return $Name;
520             };
521              
522              
523             my $SafeStr = sub($)
524             {
525             my ($Str) = shift
526             or return '!UNDEF!';
527             $Str =~ s{ ([\x00-\x1f\xff]) } { sprintf("\\x%2.2X", ord($1)) }gsex;
528             return $Str;
529             };
530              
531             sub PrintList
532             {
533 0     0 1   my ($List, $Pref, $Shift) = @_;
534              
535 0 0 0       if (!(ref($List) eq 'ARRAY' || (ref($List) eq 'HASH')))
536             {
537 0           $@ = "First parameter have to be ARRAY or HASH reference!";
538 0 0         if ($^W) { Carp::carp("$@\n"); };
  0            
539 0           return;
540             };
541              
542 0           my $Res = '';
543              
544 0           my $RunIndex = 0;
545 0           my $Name = undef;
546 0 0         foreach $Name ((ref($List) eq 'ARRAY') ? @{$List} : keys(%{$List}))
  0            
  0            
547             {
548 0 0         my $Key = (ref($List) eq 'ARRAY') ? "[$RunIndex]" : "'$Name'";
549 0 0         my $Val = (ref($List) eq 'ARRAY') ? $Name : $List->{$Name};
550 0 0         my $Dlm = (ref($List) eq 'ARRAY') ? '= ' : '=>';
551 0 0         if (ref($Val) eq 'ARRAY')
    0          
552 0           { $Res .= sprintf("%s%s array\n%s", $Pref, &{$SafeStr}($Key), PrintList($Val, $Pref.$Shift, $Shift)); }
  0            
553             elsif (ref($Val) eq 'HASH')
554 0           { $Res .= sprintf("%s%s hash\n%s", $Pref, &{$SafeStr}($Key), PrintList($Val, $Pref.$Shift, $Shift)); }
  0            
555             else
556 0 0         { $Res .= sprintf("%s%s\t%s %s\n", $Pref, &{$SafeStr}($Key), $Dlm, (defined($Val) ? '"'.&{$SafeStr}($Val).'"' : 'undef')); }
  0            
  0            
557 0           $RunIndex++;
558             };
559              
560 0           return $Res;
561             };
562              
563             1;
564             __END__