File Coverage

blib/lib/Hash/WithDefaults.pm
Criterion Covered Total %
statement 21 357 5.8
branch 0 166 0.0
condition 0 24 0.0
subroutine 11 47 23.4
pod 2 3 66.6
total 34 597 5.7


line stmt bran cond sub pod time code
1             package Hash::WithDefaults;
2 1     1   7253 use strict;
  1         2  
  1         33  
3 1     1   4 use Carp;
  1         6  
  1         80  
4             require Tie::Hash;
5 1     1   5 use vars qw(@ISA $VERSION);
  1         12  
  1         426  
6             @ISA = qw(Tie::StdHash);
7             $VERSION = '0.04';
8              
9             sub DATA () {0}
10             sub DEFAULTS () {1}
11             sub ACTDEFAULT () {2}
12             sub SEEN () {3}
13              
14             sub makeTIEHASH {
15 6     6 0 11 my ($class, $set) = @_;
16 6         10 $class = 'Hash::WithDefaults::' . $class;
17 6 0 0 0   1501 eval "sub ${class}::TIEHASH {" . <<'*END*' . "\t\t\t" . $set . <<'*END*' . "\t\t\t" . $set . <<'*END*';
  0 0 0 0      
  0 0 0 0      
  0 0 0 0      
  0 0 0 0      
  0 0 0 0      
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
18             my $class = shift();
19             my $data = {};
20              
21             if (! @_) {
22             # no parameters
23             return bless [ $data, []], $class;
24             }
25              
26             if (@_ == 1 and ref $_[0] eq 'HASH') {
27             my $input=$_[0];
28             my ($key,$value);
29             while (($key,$value) = each(%$input)) {
30             *END*
31              
32             }
33             } else {
34             my ($i, $arr) = (0);
35             if (ref $_[0] eq 'ARRAY') {
36             $arr = $_[0];
37             } elsif (@_ % 2 == 0) {
38             $arr = \@_;
39             } else {
40             croak "Ussage: tie %hashname, $class, \%hash\n or tie %hashname, $class, \\\%hash\n or tie %hashname, $class, \\\@array\n";
41             }
42             while ($i <= $#$arr) {
43             my ($key,$value)=($arr->[$i],$arr->[$i+1]); $i+=2;
44             *END*
45              
46             }
47             }
48              
49             bless [$data, []];
50             }
51             *END*
52             }
53              
54             makeTIEHASH 'sensitive', '$data->{$key} = $value;';
55             makeTIEHASH 'tolower', '$data->{lc $key} = $value;';
56             makeTIEHASH 'toupper', '$data->{uc $key} = $value;';
57             makeTIEHASH 'lower', '$data->{lc $key} = $value;';
58             makeTIEHASH 'upper', '$data->{uc $key} = $value;';
59             makeTIEHASH 'preserve', '$data->{lc $key} = [$key,$value];';
60              
61             sub TIEHASH {
62 0     0     shift(); # shift out class name
63 0 0         if (@_ == 0) {
64             # no parameters
65 0           unshift @_, 'Hash::WithDefaults::preserve';
66 0           goto &Hash::WithDefaults::preserve::TIEHASH;
67             }
68              
69 0 0 0       if (!ref $_[0] and (ref $_[1] eq 'HASH' or @_ % 2 == 1)) {
      0        
70             # type plus either \%hash or %hash
71 0           my $type = lc(splice(@_, 0, 1));
72 0 0         if ($type =~ /^(?:sensitive|preserve|lower|upper|tolower|toupper)$/) {
73 0           unshift @_, 'Hash::WithDefaults::' . $type;
74 1     1   6 no strict 'refs';
  1         2  
  1         217  
75 0           goto &{"Hash::WithDefaults::".$type."::TIEHASH"};
  0            
76             } else {
77 0           croak "Unknown type '$type'! Use one of:\n\tsensitive, preserve, lower, upper, tolower, toupper";
78             }
79             } else {
80 0           unshift @_, 'Hash::WithDefaults::preserve';
81 0           goto &Hash::WithDefaults::preserve::TIEHASH;
82             }
83             }
84              
85             sub AddDefault {
86 0     0 1   push @{$_[0]->[DEFAULTS]}, $_[1];
  0            
87 0           return 1;
88             }
89              
90             sub GetDefaults {
91 0     0 1   my $self = shift;
92 0           return $self->[DEFAULTS];
93             }
94              
95             sub CLEAR {
96 0     0     my $self = shift;
97 0           undef $self->[SEEN];
98 0           undef $self->[ACTDEFAULT];
99 0           $self
100             }
101              
102              
103             #############################
104              
105             package Hash::WithDefaults::preserve;
106 1     1   820 BEGIN {*Hash::WithDefaults::Preserve:: = \%Hash::WithDefaults::preserve::;}
107             @Hash::WithDefaults::preserve::ISA = qw(Hash::WithDefaults);
108             sub DATA () {0}
109             sub DEFAULTS () {1}
110             sub ACTDEFAULT () {2}
111             sub SEEN () {3}
112              
113             sub TIEHASH {
114             splice( @_, 1, 0, 'preserve');
115             goto &Hash::WithDefaults::TIEHASH;
116             }
117              
118             sub STORE {
119 0     0     $_[0]->[DATA]->{lc $_[1]} = [$_[1],$_[2]];
120             }
121              
122             sub FETCH {
123 0     0     my $lc_key = lc $_[1];
124 0 0         return ${$_[0]->[DATA]->{$lc_key}}[1]
  0            
125             if exists $_[0]->[DATA]->{$lc_key};
126              
127 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
128 0 0         return $default->{$_[1]}
129             if exists($default->{$_[1]});
130             }
131              
132 0           return;
133             }
134              
135             sub EXISTS {
136 0 0   0     return 1
137             if exists $_[0]->[DATA]->{lc $_[1]};
138              
139 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
140 0 0         return 1
141             if exists($default->{$_[1]});
142             }
143              
144 0           return;
145             }
146              
147             sub DELETE {
148 0     0     delete $_[0]->[DATA]->{lc $_[1]}
149             }
150              
151             sub FIRSTKEY {
152 0     0     my $self = $_[0];
153 0           undef $self->[ACTDEFAULT];
154 0           $self->[SEEN] = {};
155 0           keys %{$self->[DATA]};
  0            
156 0           my ($key,$val);
157 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0 0          
  0            
158 0           $self->[SEEN]->{$key}=1;
159 0 0         return wantarray ? ($val->[0], $val->[1]) : $val->[0];
160             } elsif (@{$self->[DEFAULTS]}) {
161 0           return $self->NEXTKEY();
162             } else {
163 0           return;
164             }
165             }
166              
167             sub NEXTKEY {
168 0     0     my $self = $_[0];
169 0           my $seen = $self->[SEEN];
170 0           my ($key,$val);
171 0 0         if (!defined $self->[ACTDEFAULT]) {
172             # processing the base hash
173 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0            
174 0           $seen->{$key}=1;
175 0 0         return wantarray ? ($val->[0], $val->[1]) : $val->[0];
176             } else {
177             # base hash done
178 0 0         if (! @{$self->[DEFAULTS]}) {
  0            
179             # no defaults
180 0           return;
181             } else {
182 0           $self->[ACTDEFAULT]=0;
183             # reset the first default
184 0           keys %{$self->[DEFAULTS]->[0]};
  0            
185             }
186             }
187             }
188              
189 0           while (exists $self->[DEFAULTS]->[$self->[ACTDEFAULT]]) {
190 0           while (($key,$val) = each %{$self->[DEFAULTS]->[$self->[ACTDEFAULT]]}) {
  0            
191 0 0         return wantarray ? ($key, $val) : $key
    0          
192             unless $seen->{lc $key}++;
193             }
194              
195 0           $self->[ACTDEFAULT]++;
196 0 0         keys %{$self->[DEFAULTS]->[$self->[ACTDEFAULT]]}
  0            
197             if exists $self->[DEFAULTS]->[$self->[ACTDEFAULT]];
198             }
199              
200             # all hashes done. Cleanup
201 0           undef $self->[SEEN];
202 0           undef $self->[ACTDEFAULT];
203 0           return;
204             }
205              
206             #############################
207              
208             package Hash::WithDefaults::lower;
209 1     1   858 BEGIN {*Hash::WithDefaults::Lower:: = \%Hash::WithDefaults::lower::;}
210             @Hash::WithDefaults::lower::ISA = qw(Hash::WithDefaults::preserve);
211             sub DATA () {0}
212             sub DEFAULTS () {1}
213             sub ACTDEFAULT () {2}
214             sub SEEN () {3}
215              
216             sub TIEHASH {
217             splice( @_, 1, 0, 'lower');
218             goto &Hash::WithDefaults::TIEHASH;
219             }
220              
221             sub STORE {
222 0     0     $_[0]->[DATA]->{lc $_[1]} = $_[2];
223             }
224              
225             sub FETCH {
226 0 0   0     return $_[0]->[DATA]->{lc $_[1]}
227             if exists $_[0]->[DATA]->{lc $_[1]};
228              
229 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
230 0 0         return $default->{$_[1]}
231             if exists($default->{$_[1]});
232             }
233              
234 0           return;
235             }
236              
237             sub EXISTS {
238 0 0   0     return 1
239             if exists $_[0]->[DATA]->{lc $_[1]};
240              
241 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
242 0 0         return 1
243             if exists($default->{$_[1]});
244             }
245              
246 0           return;
247             }
248              
249             sub DELETE {
250 0     0     delete $_[0]->[DATA]->{lc $_[1]}
251             }
252              
253             sub FIRSTKEY {
254 0     0     my $self = $_[0];
255 0           $self->[ACTDEFAULT] = -1;
256 0           $self->[SEEN] = {};
257 0           keys %{$self->[DATA]};
  0            
258 0           my ($key,$val);
259 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0 0          
  0            
260 0           $self->[SEEN]->{$key}=1;
261 0 0         return wantarray ? ($key, $val) : $key;
262             } elsif (@{$self->[DEFAULTS]}) {
263 0           return $self->NEXTKEY();
264             } else {
265 0           return;
266             }
267             }
268              
269             sub NEXTKEY {
270 0     0     my $self = $_[0];
271 0           my $seen = $self->[SEEN];
272 0           my $defaults = $self->[DEFAULTS];
273 0           my ($key,$val);
274 0 0         if ($self->[ACTDEFAULT] == -1) {
275             # processing the base hash
276 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0            
277 0           $seen->{$key}=1;
278 0 0         return wantarray ? ($key, $val) : $key;
279             } else {
280             # base hash done
281 0           $self->[ACTDEFAULT]=0;
282 0 0         if (! @$defaults) {
283             # no defaults
284 0           return;
285             } else {
286             # reset the first default
287 0           keys %{$defaults->[0]};
  0            
288             }
289             }
290             }
291 0           while (exists $defaults->[$self->[ACTDEFAULT]]) {
292 0           while (($key,$val) = each %{$defaults->[$self->[ACTDEFAULT]]}) {
  0            
293 0 0         return wantarray ? ($key, $val) : $key
    0          
294             unless $seen->{lc $key}++;
295             }
296              
297 0           $self->[ACTDEFAULT]++;
298 0 0         keys %{$defaults->[$self->[ACTDEFAULT]]}
  0            
299             if exists $defaults->[$self->[ACTDEFAULT]];
300             }
301              
302             # all hashes done. Cleanup
303 0           undef $self->[SEEN];
304 0           undef $self->[ACTDEFAULT];
305 0           return;
306             }
307              
308             #############################
309              
310             package Hash::WithDefaults::upper;
311 1     1   852 BEGIN {*Hash::WithDefaults::Upper:: = \%Hash::WithDefaults::upper::;}
312             @Hash::WithDefaults::upper::ISA = qw(Hash::WithDefaults::preserve);
313             sub DATA () {0}
314             sub DEFAULTS () {1}
315             sub ACTDEFAULT () {2}
316             sub SEEN () {3}
317              
318             sub TIEHASH {
319             splice( @_, 1, 0, 'upper');
320             goto &Hash::WithDefaults::TIEHASH;
321             }
322              
323             sub STORE {
324 0     0     $_[0]->[DATA]->{uc $_[1]} = $_[2];
325             }
326              
327             sub FETCH {
328 0 0   0     return $_[0]->[DATA]->{uc $_[1]}
329             if exists $_[0]->[DATA]->{uc $_[1]};
330              
331 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
332 0 0         return $default->{$_[1]}
333             if exists($default->{$_[1]});
334             }
335              
336 0           return;
337             }
338              
339             sub EXISTS {
340 0 0   0     return 1
341             if exists $_[0]->[DATA]->{uc $_[1]};
342              
343 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
344 0 0         return 1
345             if exists($default->{$_[1]});
346             }
347              
348 0           return;
349             }
350              
351             sub DELETE {
352 0     0     delete $_[0]->[DATA]->{uc $_[1]}
353             }
354              
355             sub FIRSTKEY {
356 0     0     my $self = $_[0];
357 0           $self->[ACTDEFAULT] = -1;
358 0           $self->[SEEN] = {};
359 0           keys %{$self->[DATA]};
  0            
360 0           my ($key,$val);
361 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0 0          
  0            
362 0           $self->[SEEN]->{$key}=1;
363 0 0         return wantarray ? ($key, $val) : $key;
364             } elsif (@{$self->[DEFAULTS]}) {
365 0           return $self->NEXTKEY();
366             } else {
367 0           return;
368             }
369             }
370              
371             sub NEXTKEY {
372 0     0     my $self = $_[0];
373 0           my $seen = $self->[SEEN];
374 0           my $defaults = $self->[DEFAULTS];
375 0           my ($key,$val);
376 0 0         if ($self->[ACTDEFAULT] == -1) {
377             # processing the base hash
378 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0            
379 0           $seen->{$key}=1;
380 0 0         return wantarray ? ($key, $val) : $key;
381             } else {
382             # base hash done
383 0           $self->[ACTDEFAULT]=0;
384 0 0         if (! @$defaults) {
385             # no defaults
386 0           return;
387             } else {
388             # reset the first default
389 0           keys %{$defaults->[0]};
  0            
390             }
391             }
392             }
393 0           while (exists $defaults->[$self->[ACTDEFAULT]]) {
394 0           while (($key,$val) = each %{$defaults->[$self->[ACTDEFAULT]]}) {
  0            
395 0 0         return wantarray ? ($key, $val) : $key
    0          
396             unless $seen->{uc $key}++;
397             }
398              
399 0           $self->[ACTDEFAULT]++;
400 0 0         keys %{$defaults->[$self->[ACTDEFAULT]]}
  0            
401             if exists $defaults->[$self->[ACTDEFAULT]];
402             }
403              
404             # all hashes done. Cleanup
405 0           undef $self->[SEEN];
406 0           undef $self->[ACTDEFAULT];
407 0           return;
408             }
409              
410              
411             #############################
412              
413             package Hash::WithDefaults::sensitive;
414 1     1   781 BEGIN {*Hash::WithDefaults::Sensitive:: = \%Hash::WithDefaults::sensitive::;}
415             @Hash::WithDefaults::sensitive::ISA = qw(Hash::WithDefaults);
416             sub DATA () {0}
417             sub DEFAULTS () {1}
418             sub ACTDEFAULT () {2}
419             sub SEEN () {3}
420              
421             sub TIEHASH {
422             splice( @_, 1, 0, 'sensitive');
423             goto &Hash::WithDefaults::TIEHASH;
424             }
425              
426             sub STORE {
427 0     0     $_[0]->[DATA]->{$_[1]} = $_[2];
428             }
429              
430             sub FETCH {
431 0 0   0     return $_[0]->[DATA]->{$_[1]}
432             if exists $_[0]->[DATA]->{$_[1]};
433              
434 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
435 0 0         return $default->{$_[1]}
436             if exists($default->{$_[1]});
437             }
438              
439 0           return;
440             }
441              
442             sub EXISTS {
443 0 0   0     return 1
444             if exists $_[0]->[DATA]->{$_[1]};
445              
446 0           foreach my $default (@{$_[0]->[DEFAULTS]}) {
  0            
447 0 0         return 1
448             if exists($default->{$_[1]});
449             }
450              
451 0           return;
452             }
453              
454             sub DELETE {
455 0     0     delete $_[0]->[DATA]->{$_[1]}
456             }
457              
458             sub FIRSTKEY {
459 0     0     my $self = $_[0];
460 0           $self->[ACTDEFAULT] = -1;
461 0           $self->[SEEN] = {};
462 0           keys %{$self->[DATA]};
  0            
463 0           my ($key,$val);
464 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0 0          
  0            
465 0           $self->[SEEN]->{$key}=1;
466 0 0         return wantarray ? ($key, $val) : $key;
467             } elsif (@{$self->[DEFAULTS]}) {
468 0           return $self->NEXTKEY();
469             } else {
470 0           return;
471             }
472             }
473              
474             sub NEXTKEY {
475 0     0     my $self = $_[0];
476 0           my $seen = $self->[SEEN];
477 0           my $defaults = $self->[DEFAULTS];
478 0           my ($key,$val);
479 0 0         if ($self->[ACTDEFAULT] == -1) {
480             # processing the base hash
481 0 0         if (($key,$val) = each %{$self->[DATA]}) {
  0            
482 0           $seen->{$key}=1;
483 0 0         return wantarray ? ($key, $val) : $key;
484             } else {
485             # base hash done
486 0           $self->[ACTDEFAULT]=0;
487 0 0         if (! @$defaults) {
488             # no defaults
489 0           return;
490             } else {
491             # reset the first default
492 0           keys %{$defaults->[0]};
  0            
493             }
494             }
495             }
496 0           while (exists $defaults->[$self->[ACTDEFAULT]]) {
497 0           while (($key,$val) = each %{$defaults->[$self->[ACTDEFAULT]]}) {
  0            
498 0 0         return wantarray ? ($key, $val) : $key
    0          
499             unless $seen->{$key}++;
500             }
501              
502 0           $self->[ACTDEFAULT]++;
503 0 0         keys %{$defaults->[$self->[ACTDEFAULT]]}
  0            
504             if exists $defaults->[$self->[ACTDEFAULT]];
505             }
506              
507             # all hashes done. Cleanup
508 0           undef $self->[SEEN];
509 0           undef $self->[ACTDEFAULT];
510 0           return;
511             }
512              
513              
514             #############################
515              
516             package Hash::WithDefaults::toupper;
517 1     1   175 BEGIN {*Hash::WithDefaults::Toupper:: = \%Hash::WithDefaults::toupper::;}
518             @Hash::WithDefaults::toupper::ISA = qw(Hash::WithDefaults::sensitive);
519             sub DATA () {0}
520             sub DEFAULTS () {1}
521             sub ACTDEFAULT () {2}
522             sub SEEN () {3}
523              
524             sub TIEHASH {
525             splice( @_, 1, 0, 'toupper');
526             goto &Hash::WithDefaults::TIEHASH;
527             }
528              
529             sub STORE {
530 0     0     $_[0]->[DATA]->{uc $_[1]} = $_[2];
531             }
532              
533             #############################
534              
535             package Hash::WithDefaults::tolower;
536 1     1   152 BEGIN {*Hash::WithDefaults::Tolower:: = \%Hash::WithDefaults::tolower::;}
537             @Hash::WithDefaults::tolower::ISA = qw(Hash::WithDefaults::sensitive);
538             sub DATA () {0}
539             sub DEFAULTS () {1}
540             sub ACTDEFAULT () {2}
541             sub SEEN () {3}
542              
543             sub TIEHASH {
544             splice( @_, 1, 0, 'tolower');
545             goto &Hash::WithDefaults::TIEHASH;
546             }
547              
548             sub STORE {
549 0     0     $_[0]->[DATA]->{lc $_[1]} = $_[2];
550             }
551              
552             1;
553              
554             __END__