File Coverage

blib/lib/File/Mosaic.pm
Criterion Covered Total %
statement 192 205 93.6
branch 43 72 59.7
condition n/a
subroutine 32 43 74.4
pod 10 10 100.0
total 277 330 83.9


line stmt bran cond sub pod time code
1             # $Id: Mosaic.pm 2466 2006-06-14 22:30:52Z cboumeno $
2             ######################################################################
3             #
4             # This program is Copyright 2006-2007 by Christopher Boumenot
5             # .
6             #
7             # This program is free software; you can redistribute it and/or modify
8             # it under the terms of the same license as Perl.
9             #
10             # This program is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13             #
14             ######################################################################
15              
16             package File::Mosaic;
17              
18             require 5.006;
19 2     2   79191 use strict;
  2         4  
  2         81  
20 2     2   11 use warnings;
  2         4  
  2         89  
21              
22             our $VERSION = '0.03';
23              
24 2     2   2122 use IO::File;
  2         30949  
  2         369  
25 2     2   19 use File::Spec;
  2         4  
  2         54  
26 2     2   12 use File::Path;
  2         5  
  2         123  
27 2     2   11 use Digest::MD5;
  2         4  
  2         65  
28 2     2   2686 use Storable;
  2         10423  
  2         151  
29 2     2   3005 use Data::Dumper;
  2         40180  
  2         177  
30 2     2   3047 use Log::Log4perl qw(:easy get_logger);
  2         177924  
  2         15  
31 2     2   1344 use Carp;
  2         4  
  2         7025  
32              
33             sub new {
34 8     8 1 1366 my ($class, %options) = @_;
35              
36 8 50       31 unless (defined $options{filename}) {
37 0         0 confess "%Error: the parameter 'filename' is mandatory!\n";
38             }
39              
40 8 50       20 unless (defined $options{mosaic_directory}) {
41 0         0 confess "%Error: the paramter 'mosaic_directory' is mandatory\n";
42             }
43              
44 8         56 my $self = {
45             'mosaic_file' => '.mosaics',
46             '_mosaics' => {},
47             '_count' => 0,
48             '_is_closed' => 0,
49             %options,
50             };
51              
52 8 100       121 unless (-d $self->{mosaic_directory}) {
53 1         229 File::Path::mkpath($self->{mosaic_directory});
54             }
55              
56 8         134 $self->{mosaic_path} = File::Spec->catfile($self->{mosaic_directory}, $self->{mosaic_file});
57              
58 8         28 bless $self, $class;
59              
60 8 100       117 if (-f $self->{mosaic_path}) {
61 7         24 $self->_load_mosaic_directory();
62             }
63              
64 8         37 return $self;
65             }
66              
67             sub DESTROY {
68 8     8   5484 my ($self) = @_;
69 8 100       98 unless ($self->{_is_closed}) {
70 2         6 $self->close();
71             }
72             }
73              
74             sub append {
75 5     5 1 33 my $self = shift;
76 5         22 my %options = (
77             tag => undef,
78             mosaic => undef,
79             @_,
80             );
81              
82 5         10 my $tag = $options{tag};
83 5         9 my $mosaic = $options{mosaic};
84              
85 5 50       15 confess "%Error: 'tag' is a mandatory parameter!\n" unless defined $tag;
86 5 50       12 confess "%Error: 'mosaic' is a mandatory parameter!\n" unless defined $mosaic;
87              
88 5     0   42 DEBUG( sub { "append: tag => $tag\n" });
  0         0  
89              
90 5         76 $self->{_mosaics}->{$tag}->{data} = $mosaic;
91 5         13 $self->{_mosaics}->{$tag}->{count} = $self->{_count}++;
92              
93 5         17 $self->_add_digest_tag($tag, $mosaic);
94             }
95              
96             sub insert_before {
97 1     1 1 11 my $self = shift;
98 1         7 my %options = (
99             tag => undef,
100             before_tag => undef,
101             mosaic => undef,
102             @_,
103             );
104              
105 1         2 my $tag = $options{tag};
106 1         2 my $mosaic = $options{mosaic};
107 1         2 my $before_tag = $options{before_tag};
108              
109 1 50       4 confess "%Error: 'tag' is a mandatory parameter!\n" unless defined $tag;
110 1 50       3 confess "%Error: 'mosaic' is a mandatory parameter!\n" unless defined $mosaic;
111 1 50       3 confess "%Error: 'before_tag' is a mandatory parameter!\n" unless defined $before_tag;
112              
113 1 50       3 confess "%Error: the tag '$tag' already exist!" if $self->_valid_tag($tag);
114 1 50       3 confess "%Error: the tag '$before_tag' does not exists!" unless $self->_valid_tag($before_tag);
115              
116 1     0   5 DEBUG( sub { "insert_before: tag => $tag, before_tag => $before_tag\n" });
  0         0  
117              
118 1         9 my $count = $self->{_mosaics}->{$before_tag}->{count};
119 1         4 $self->_insert($tag, $count, $mosaic);
120             }
121              
122             sub insert_after {
123 1     1 1 11 my $self = shift;
124 1         7 my %options = (
125             tag => undef,
126             after_tag => undef,
127             mosaic => undef,
128             @_,
129             );
130              
131 1         2 my $tag = $options{tag};
132 1         3 my $mosaic = $options{mosaic};
133 1         2 my $after_tag = $options{after_tag};
134              
135 1 50       5 confess "%Error: 'tag' is a mandatory parameter!\n" unless defined $tag;
136 1 50       4 confess "%Error: 'mosaic' is a mandatory parameter!\n" unless defined $mosaic;
137 1 50       3 confess "%Error: 'after_tag' is a mandatory parameter!\n" unless defined $after_tag;
138              
139 1 50       4 confess "%Error: the tag '$tag' already exist!" if $self->_valid_tag($tag);
140 1 50       5 confess "%Error: the tag '$after_tag' does not exist!" unless $self->_valid_tag($after_tag);
141              
142 1     0   7 DEBUG( sub { "insert_after: tag => $tag, after_tag => $after_tag\n" });
  0         0  
143              
144 1         11 my $count = $self->{_mosaics}->{$after_tag}->{count} + 1;
145 1         5 $self->_insert($tag, $count, $mosaic);
146             }
147              
148             sub replace {
149 1     1 1 8 my $self = shift;
150 1         6 my %options = (
151             tag => undef,
152             mosaic => undef,
153             @_,
154             );
155              
156 1         3 my $tag = $options{tag};
157 1         2 my $mosaic = $options{mosaic};
158              
159 1 50       5 confess "%Error: 'tag' is a mandatory parameter!\n" unless defined $tag;
160 1 50       4 confess "%Error: 'mosaic' is a mandatory parameter!\n" unless defined $mosaic;
161 1 50       4 confess "%Error: the tag '$tag' does not exist!" unless $self->_valid_tag($tag);
162              
163 1     0   8 DEBUG( sub { "replace: tag => $tag\n" });
  0         0  
164              
165 1         13 $self->{_mosaics}->{$tag}->{data} = $mosaic;
166 1         4 $self->{_mosaics}->{$tag}->{sum} = $self->_digest_tag($tag, $mosaic);
167             }
168              
169             sub remove {
170 1     1 1 8 my $self = shift;
171 1         5 my %options = (
172             tag => undef,
173             @_,
174             );
175              
176 1         3 my $tag = $options{tag};
177              
178 1 50       4 confess "%Error: 'tag' is a mandatory parameter!\n" unless defined $tag;
179 1 50       6 confess "%Error: the tag '$tag' does not exist!" unless $self->_valid_tag($tag);
180              
181 1     0   11 DEBUG( sub { "remove: tag => $tag\n" });
  0         0  
182              
183 1         11 $self->{_count}--;
184              
185 1         4 delete $self->{_mosaics}->{$tag};
186              
187 1         37 $self->_increment_counts($tag, -1);
188             }
189              
190             sub fetch {
191 17     17 1 1076 my $self = shift;
192 17         50 my %options = (
193             tag => undef,
194             @_,
195             );
196              
197 17 50       48 my $tag = $options{tag} or confess "%Error: 'tag' is a mandatory parameter!\n";
198 17 50       35 confess "%Error: the tag '$tag' does not exist!" unless $self->_valid_tag($tag);
199              
200 17     0   78 DEBUG( sub { "fetch: tag => $tag\n" });
  0         0  
201              
202 17         158 my $count = $self->{_mosaics}->{$tag}->{count};
203 17         80 return $self->{_mosaics}->{$tag}->{data};
204             }
205              
206             sub fetch_tags {
207 3     3 1 16 my $self = shift;
208              
209 3         5 my @tags;
210 3         5 for (sort {$self->{_mosaics}->{$a}->{count} <=>
  28         58  
  3         17  
211             $self->{_mosaics}->{$b}->{count}} keys %{$self->{_mosaics}}) {
212 17         28 push @tags, $_;
213             }
214              
215 3 50       24 return (wantarray) ? @tags : \@tags;
216             }
217              
218             sub reorder_tags {
219 1     1 1 14 my $self = shift;
220 1         6 my %options = (
221             tags => undef,
222             @_,
223             );
224              
225 1 50       16 my $tags = $options{tags} or confess "%Error: 'tags' is a mandatory parameter!\n";
226              
227 1         2 for my $tag (@$tags) {
228 6 50       10 confess "%Error: the tag '$tag' does not exist!" unless $self->_valid_tag($tag);
229             }
230              
231 1         4 for my $i (0..scalar(@$tags)-1) {
232 6         7 my $tag = $tags->[$i];
233 6         15 $self->{_mosaics}->{$tag}->{count} = $i;
234             }
235             }
236              
237              
238             sub close {
239 8     8 1 773 my ($self) = @_;
240              
241 8         22 $self->_write_file();
242 8         386 $self->_write_mosaics();
243 8         348 $self->_write_mosaic_file();
244            
245 8         1408 $self->{_is_closed} = 1;
246             }
247              
248             ##################################################
249             ## PRIVATE
250             ##################################################
251              
252             sub _write_file {
253 8     8   12 my ($self) = @_;
254              
255 8 50       54 my $fouth = IO::File->new(">$self->{filename}") or
256             confess "%Error: $! '$self->{filename}'!\n";
257              
258 8     0   1027 DEBUG( sub { "_write_file:\n" . Dumper($self) });
  0         0  
259            
260 8         73 for my $tag (sort { $self->{_mosaics}->{$a}->{count} <=>
  78         191  
  8         48  
261             $self->{_mosaics}->{$b}->{count} }
262             keys %{$self->{_mosaics}}) {
263            
264 47     0   183 DEBUG( sub { "_write_file: tag => $tag\n" });
  0         0  
265 47         440 print $fouth $self->{_mosaics}->{$tag}->{data};
266             }
267              
268 8         39 $fouth->close();
269             }
270              
271             sub _write_mosaics {
272 8     8   15 my ($self) = @_;
273              
274 8         11 for my $tag (keys %{$self->{_mosaics}}) {
  8         31  
275 47         1613 my $mosaic = $self->{_mosaics}->{$tag}->{data};
276 47         153 my $sum = $self->_digest_tag($tag, $mosaic);
277 47         535 my $fn = File::Spec->catfile($self->{mosaic_directory}, $sum);
278              
279 47 50       425 my $fouth = IO::File->new(">$fn") or
280             confess "%Error: $! '$fn'!\n";
281              
282 47         4871 print $fouth $mosaic;
283            
284 47         146 $fouth->close();
285             }
286             }
287              
288             sub _write_mosaic_file {
289 8     8   15 my ($self) = @_;
290 8         48 store $self->{_mosaics}, $self->{mosaic_path};
291             }
292              
293             sub _valid_tag {
294 29     29   49 my ($self, $tag) = @_;
295 29 100       71 my $rc = (defined $self->{_mosaics}->{$tag}) ? 1 : 0;
296 29 100   0   60 DEBUG(sub { "_valid_tag: tag => $tag\n" . Dumper($self) }) unless $rc;
  0         0  
297 29         104 return $rc;
298             }
299              
300             sub _digest_tag {
301 55     55   102 my ($self, $tag, $mosaic) = @_;
302              
303 55         311 my $ctx = Digest::MD5->new;
304 55         167 $ctx->add($tag);
305 55         146 $ctx->add($mosaic);
306 55         208 my $sum = $ctx->hexdigest;
307              
308 55         274 return $sum;
309             }
310              
311             sub _add_digest_tag {
312 7     7   13 my ($self, $tag, $mosaic) = @_;
313              
314 7         19 my $sum = $self->_digest_tag($tag, $mosaic);
315 7         39 $self->{_mosaics}->{$tag}->{sum} = $sum;
316             }
317              
318             sub _insert {
319 2     2   4 my ($self, $tag, $count, $mosaic) = @_;
320              
321 2     0   10 DEBUG( sub { "_insert: tag => $tag, count => $count\n" });
  0         0  
322              
323 2         18 $self->_increment_counts($count);
324 2         8 $self->{_mosaics}->{$tag}->{count} = $count;
325 2         4 $self->{_mosaics}->{$tag}->{data} = $mosaic;
326              
327 2         10 $self->_add_digest_tag($tag, $mosaic);
328             }
329              
330             sub _increment_counts {
331 3     3   6 my ($self, $count, $offset) = @_;
332              
333 3 100       10 $offset = 1 unless defined $offset;
334            
335 3         4 for my $tag (keys %{$self->{_mosaics}}) {
  3         13  
336 17 100       41 if ($self->{_mosaics}->{$tag}->{count} >= $count) {
337             # DEBUG( sub { "_increment
338 11         26 $self->{_mosaics}->{$tag}->{count} += $offset;
339             }
340             }
341             }
342              
343             sub _load_mosaic_directory {
344 7     7   12 my ($self) = @_;
345              
346 7         22 $self->_load_mosaic_file();
347            
348 7         757 my $count = 0;
349 7         9 for my $tag (sort { $self->{_mosaics}->{$a}->{count} <=>
  71         150  
  7         42  
350             $self->{_mosaics}->{$b}->{count} }
351             keys %{$self->{_mosaics}}) {
352              
353 41         82 my $count = $self->{_mosaics}->{$tag}->{count};
354 41         76 my $sum = $self->{_mosaics}->{$tag}->{sum};
355              
356 41     0   219 DEBUG( sub { "_load_mosaic_directory: tag => $tag, count => $count\n" });
  0         0  
357              
358 41         742 my $mfn = File::Spec->catfile($self->{mosaic_directory}, $sum);
359 41 50       576 confess "%Error: a mosaic file, '$mfn', is missing!\n" unless -f $mfn;
360              
361 41         121 $self->{_mosaics}->{$tag}->{count} = $count;
362 41         92 $self->{_mosaics}->{$tag}->{data} = $self->_slurp_file($mfn);
363             }
364             }
365              
366             sub _load_mosaic_file {
367 7     7   11 my ($self) = @_;
368              
369 7 50       31 $self->{_mosaics} = retrieve($self->{mosaic_path}) or
370             confess "%Error: $! '$self->{mosaic_path}'!\n";
371             }
372              
373             sub _slurp_file {
374 41     41   61 my ($self, $fn) = @_;
375            
376 41         138 local $/;
377 41 50       174 my $finh = IO::File->new($fn) or
378             confess "%Error: $! '$fn'!\n";
379              
380 41         3218 my $mosaic = <$finh>;
381              
382 41         150 $finh->close();
383              
384 41         743 return $mosaic;
385             }
386              
387              
388             #######################################################################
389             1;
390             __END__