File Coverage

blib/lib/Palm/MaTirelire/Types.pm
Criterion Covered Total %
statement 13 158 8.2
branch 0 86 0.0
condition 0 18 0.0
subroutine 5 12 41.6
pod 0 7 0.0
total 18 281 6.4


line stmt bran cond sub pod time code
1             #
2             # Author : Maxime Soulé
3             # Created On : Sun Aug 29 21:40:59 2004
4             # Last Modified By: Maximum Solo
5             # Last Modified On: Sat Feb 11 22:37:16 2012
6             # Update Count : 22
7             #
8             # Copyright (C) 2005, Maxime Soulé
9             # You may distribute this file under the terms of the Artistic
10             # License, as specified in the README file.
11             #
12              
13             package Palm::MaTirelire::Types;
14              
15 1     1   1443 use strict;
  1         3  
  1         44  
16              
17 1     1   7 use Palm::BlockPack;
  1         2  
  1         24  
18              
19 1     1   6 use Palm::MaTirelire::DBItemId;
  1         2  
  1         10  
20              
21 1     1   46 use base qw(Palm::MaTirelire::DBItemId);
  1         3  
  1         2142  
22              
23             our $VERSION = '1.0';
24              
25              
26             # Each record
27             my $RECORD_BLOCK = Palm::BlockPack->new
28             (UInt32 => [
29             [ 'type_id:8' => 0 ],
30             [ 'parent_id:8' => 0xff ],
31             [ 'child_id:8' => 0xff ],
32             [ 'brother_id:8' => 0xff ],
33             ],
34             UInt32 => [
35             [ 'sign_depend:2' => 3 ],
36             [ 'folded:1' => 0 ],
37             [ 'reserved:*' => 0 ],
38             ],
39             'Z24' => [ 'only_in_account' => '' ],
40             'Z*' => [ 'name' => '' ],
41             );
42              
43              
44             sub import
45             {
46 1     1   16 &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, [ "MaT2", 'Type' ]);
47             }
48              
49              
50             sub meta_infos ($)
51             {
52 0     0 0   return { name => "MaTi-Types",
53             type => "Type",
54             record_block => $RECORD_BLOCK,
55             id_field => 'type_id',
56             unfiled_name => 'Unfiled',
57             num => (1 << 8),
58             };
59             }
60              
61              
62             sub full_name
63             {
64 0     0 0   my($self, $id) = @_;
65              
66             # "Unfiled" case
67 0           my($num, $unfiled_name) = @{$self->meta_infos}{qw(num unfiled_name)};
  0            
68 0 0         return $unfiled_name if $id == $num - 1;
69              
70 0           my $cache = $self->build_cache_id;
71              
72 0           my $rec = $cache->[$id];
73              
74 0 0         return undef unless defined $rec;
75              
76 0           my $full_name = $rec->{name};
77              
78 0   0       while (defined $rec and $rec->{parent_id} != 0xff)
79             {
80 0           $rec = $cache->[$rec->{parent_id}];
81              
82 0 0         substr($full_name, 0, 0) = (defined($rec) ? $rec->{name} : '?') . '/';
83             }
84              
85 0           return $full_name;
86             }
87              
88              
89             sub find_by_full_name ($$)
90             {
91 0     0 0   my($self, $full_name) = @_;
92              
93 0           my($id_field, $unfiled_name)
94 0           = @{$self->meta_infos}{qw(id_field unfiled_name)};
95              
96 0 0         return wantarray ? () : undef if $full_name eq $unfiled_name;
    0          
97              
98 0           my $parent_id = 0xff;
99 0           my $rec;
100              
101 0           type: foreach my $sub_type (split('/', $full_name))
102             {
103 0 0         $sub_type = '' if $sub_type =~ /^\s*\z/;
104              
105 0           foreach my $cur_rec (@{$self->{records}})
  0            
106             {
107 0 0 0       if ($cur_rec->{parent_id} == $parent_id
108             and $cur_rec->{name} eq $sub_type)
109             {
110 0           $parent_id = $cur_rec->{type_id};
111 0           $rec = $cur_rec;
112 0           next type;
113             }
114             }
115              
116             # Not found...
117 0 0         return wantarray ? () : undef;
118             }
119              
120 0           return $rec;
121             }
122              
123              
124             sub replaceAutoID ($$)
125             {
126 0     0 0   my($self, $rec) = @_;
127              
128 0           my $new_id = $self->get_first_free_id;
129 0 0         return undef unless defined $new_id;
130              
131 0           my $old_id = $rec->{type_id};
132 0           $rec->{type_id} = $new_id;
133              
134 0           foreach $rec (@{$self->{records}})
  0            
135             {
136 0 0         $rec->{parent_id} = $new_id if $rec->{parent_id} == $old_id;
137 0 0         $rec->{child_id} = $new_id if $rec->{child_id} == $old_id;
138 0 0         $rec->{brother_id} = $new_id if $rec->{brother_id} == $old_id;
139             }
140              
141 0           return $new_id;
142             }
143              
144              
145             sub new_RecordWithFullName ($$;$)
146             {
147 0     0 0   my($self, $full_name, $final_id) = @_;
148              
149 0           my($id_field, $unfiled_name)
150 0           = @{$self->meta_infos}{qw(id_field unfiled_name)};
151              
152 0 0         return undef if $full_name eq $unfiled_name;
153              
154 0           my $rec;
155              
156             # This final ID already "auto created" ?
157 0 0 0       if (defined $final_id
158             and defined($rec = $self->build_cache_id->[$final_id]))
159             {
160 0 0         if ($rec->{auto_id})
161             {
162 0 0         return undef unless defined $self->replaceAutoID($rec);
163             }
164             else
165             {
166             # This ID already exists but is not an auto one!
167             # XXX
168             }
169             }
170              
171 0           my $parent_id = 0xff;
172 0           my @sub_types = split('/', $full_name);
173              
174             # No more than 10 depth levels...
175 0 0         return undef if @sub_types > 10;
176              
177 0           type: for (;;)
178             {
179 0           my $sub_type = shift @sub_types;
180 0 0         last unless defined $sub_type;
181              
182 0 0         $sub_type = '' if $sub_type =~ /^\s*\z/;
183              
184 0           foreach my $cur_rec (@{$self->{records}})
  0            
185             {
186 0 0 0       if ($cur_rec->{parent_id} == $parent_id
187             and $cur_rec->{name} eq $sub_type)
188             {
189 0           $parent_id = $cur_rec->{type_id};
190              
191 0 0         return $cur_rec if @sub_types == 0;
192 0           next type;
193             }
194             }
195              
196 0           unshift(@sub_types, $sub_type);
197              
198             # Not found, create this sub type and all behind it
199 0           for (my $sub_idx = 0; $sub_idx < @sub_types; $sub_idx++)
200             {
201 0           $sub_type = $sub_types[$sub_idx];
202              
203 0 0         $sub_type = '' if $sub_type =~ /^\s*\z/;
204              
205 0 0         $rec = $self->new_RecordChildOf($parent_id,
206             $sub_idx == @sub_types - 1
207             ? $final_id : undef);
208 0 0         last unless defined $rec;
209              
210 0           $rec->{name} = $sub_type;
211 0           $parent_id = $rec->{type_id};
212             }
213              
214 0           last;
215             }
216              
217             # This type already exists, returns it (undef if not enough free IDs)
218 0           return $rec;
219             }
220              
221              
222             sub new_RecordChildOf ($$;$)
223             {
224 0     0 0   my($self, $parent_id, $final_id) = @_;
225              
226 0           my $rec;
227              
228             my $id;
229 0           my $auto_id;
230 0 0         if (defined($final_id))
231             {
232 0           $id = $final_id;
233              
234 0           $rec = $self->build_cache_id->[$id];
235 0 0         if (defined $rec)
236             {
237 0 0         if ($rec->{auto_id})
238             {
239 0 0         return undef unless defined $self->replaceAutoID($rec);
240             }
241             else
242             {
243             # This ID already exists but is not an auto one!
244             # XXX
245             }
246             }
247             }
248             else
249             {
250 0           $id = $self->get_first_free_id;
251 0           $auto_id = 1;
252             }
253              
254 0 0         return undef unless defined $id;
255              
256 0           $rec = $self->new_Record;
257              
258 0           $rec->{type_id} = $id;
259 0 0         $rec->{auto_id} = 1 if $auto_id;
260              
261             # Insert the type somewhere in the top level
262 0 0         if ($parent_id == 0xff)
263             {
264             # Nothing to do if no record is present (no brother)
265 0           foreach my $rec_any (@{$self->{records}})
  0            
266             {
267             # Toplevel record
268 0 0         if ($rec_any->{parent_id} == 0xff)
269             {
270 0           $rec->{brother_id} = $rec_any->{brother_id};
271 0           $rec_any->{brother_id} = $id;
272 0           last;
273             }
274             }
275             }
276             else
277             {
278 0           my $parent_rec = $self->get_id($parent_id);
279 0 0         return undef unless defined $parent_rec;
280              
281             # Add the type as the first child of parent
282 0           $rec->{parent_id} = $parent_id;
283 0           $rec->{brother_id} = $parent_rec->{child_id};
284              
285 0           $parent_rec->{child_id} = $id;
286             }
287              
288 0           $self->append_Record($rec);
289              
290 0           return $rec;
291             }
292              
293              
294             sub dump
295             {
296 0     0 0   my $self = shift;
297              
298 0 0         return if @{$self->{records}} == 0;
  0            
299              
300 0           my $first_id = 0xff;
301 0           my $rec;
302 0           my($index, $loops);
303              
304 0           $loops = 0xff;
305              
306             # Search the first type
307 0           for ($index = 0; $index < @{$self->{records}}; $index++)
  0            
308             {
309 0           $rec = $self->{records}[$index];
310              
311 0 0 0       if ($rec->{parent_id} == 0xff
      0        
312             and ($first_id == 0xff or $rec->{brother_id} == $first_id))
313             {
314 0           $first_id = $rec->{type_id};
315 0           $index = -1;
316              
317             # Par sécurité... XXX
318 0 0         if (--$loops == 0)
319             {
320 0           die "Types first ID loop detected...\n";
321 0           last;
322             }
323             }
324             }
325              
326 0           my $ref_cache = $self->build_cache_id;
327              
328 0           my $depth_glyphs = '';
329 0           my $depth = 1;
330 0           $rec = $ref_cache->[$first_id];
331 0           my @types;
332             my $id;
333              
334 0           for (;;)
335             {
336 0 0         push(@types,
337             {
338             depth => $depth,
339             depth_glyphs => $depth_glyphs . ($rec->{brother_id} == 0xff
340             ? '+-' : '|-'),
341             type_id => $rec->{type_id},
342             name => $rec->{name},
343             });
344              
345             # Type has a child
346 0           $id = $rec->{child_id};
347 0 0         if ($id != 0xff)
348             {
349 0 0         $depth_glyphs .= $rec->{brother_id} == 0xff ? ' ' : '| ';
350 0           $depth++;
351              
352 0           goto load_and_continue;
353             }
354              
355             # Else type has a brother
356             brother:
357 0           $id = $rec->{brother_id};
358 0 0         goto load_and_continue if $id != 0xff;
359              
360             # Else, if the type has a parent => go to his brother OR his parent
361 0           $id = $rec->{parent_id};
362 0 0         if ($id != 0xff)
363             {
364 0           substr($depth_glyphs, -2) = '';
365 0           $depth--;
366              
367 0           $rec = $ref_cache->[$id];
368              
369 0           goto brother;
370             }
371              
372             # Else that's all folk...
373 0           last;
374              
375 0           load_and_continue:
376             $rec = $ref_cache->[$id];
377             }
378              
379 0 0         if (@types != @{$self->{records}})
  0            
380             {
381 0           die("Not all types are chained, only ", scalar(@types), " on ",
382 0           scalar(@{$self->{records}}), "\n");
383             }
384              
385 0 0         return @types if wantarray;
386              
387 0           return join("\n",
388 0           map { "$_->{depth_glyphs} $_->{name} ($_->{type_id})" }
389             @types);
390             }
391              
392              
393             1;
394             __END__