File Coverage

blib/lib/Tree/Binary/Dictionary.pm
Criterion Covered Total %
statement 127 129 98.4
branch 30 38 78.9
condition 5 8 62.5
subroutine 27 27 100.0
pod 11 11 100.0
total 200 213 93.9


line stmt bran cond sub pod time code
1             package Tree::Binary::Dictionary;
2              
3             =head1 NAME
4              
5             Tree::Binary::Dictionary - A dictionary API to a binary tree
6              
7             =head1 DESCRIPTION
8              
9             A simple class to provide a dictionary style API
10             to a binary tree of data.
11              
12             This can provide a useful alternative to a long-lived
13             hash in long running daemons and processes.
14              
15              
16             =head1 SYNOPSIS
17              
18             use Tree::Binary::Dictionary;
19              
20             my $dictionary = Tree::Binary::Dictionary->new;
21              
22             # populate
23             $dictionary->add(aaaa => "One");
24             $dictionary->add(cccc => "Three");
25             $dictionary->add(dddd => "Four");
26             $dictionary->add(eeee => "Five");
27             $dictionary->add(foo => "Foo");
28             $dictionary->add(bar => "quuz");
29              
30             # interact
31             $dictionary->exists('bar');
32             $dictionary->get('eeee');
33             $dictionary->delete('cccc');
34              
35             # hash stuff
36             my %hash = $dictionary->to_hash;
37             my @values = $dictionary->values;
38             my @keys = $dictionary->keys;
39              
40             # for long running processes
41             $dictionary->rebuild();
42              
43             =head1 METHODS
44              
45             =head2 new - constructor
46              
47             my $dictionary = Tree::Binary::Dictionary->new (cache => 0);
48              
49             Instantiates and returns a new dictionary object.
50              
51             Optional arguments are cache, which will re-use internal objects and structures
52             rather than rebuilding them as required. Default is 1 / True.
53              
54             =head2 rebuild
55              
56             $dictionary->rebuild();
57              
58             Rebuilds the internal binary tree to reduce wasteful memory use
59              
60             =head2 add
61              
62             my $added = $dictionary->add(bar => "quuz");
63              
64             Adds new key and value in dictionary object, returns true on success, warns and returns 0
65             on duplicate key or other failure.
66              
67             =head2 set
68              
69             my $set = $dictionary->set(bar => 'quuuz');
70              
71             Sets key and value in dictionary object, returns true on success, 0 on error.
72              
73             This will add a new key and value if the key is new, or update the value of an existing key
74              
75             =head2 exists
76              
77             $dictionary->exists('bar');
78              
79             =head2 get
80              
81             my $value = $dictionary->get('eeee');
82              
83             =head2 delete
84              
85             my $deleted = $dictionary->delete('cccc');
86              
87             =head2 to_hash
88              
89             my %hash = $dictionary->to_hash;
90              
91             returns a hash populated from the dictionary
92              
93             =head2 values
94              
95             my @values = $dictionary->values;
96              
97             returns dictionary values as an array
98              
99             =head2 keys
100              
101             my @keys = $dictionary->keys;
102              
103             returns dictionary keys as an array
104              
105             =head2 count
106              
107             my $count = $dictionary->count
108              
109             returns the number of entries in the dictionary
110              
111             =cut
112              
113 2     2   67759 use strict;
  2         4  
  2         112  
114             our $VERSION = 1.01;
115              
116 2     2   1898 use Tree::Binary::Search;
  2         13741  
  2         54  
117 2     2   1832 use Tree::Binary::Visitor::InOrderTraversal;
  2         3618  
  2         65  
118              
119 2     2   16 use constant _COUNTER => 0;
  2         5  
  2         125  
120 2     2   10 use constant _BTREE => 1;
  2         5  
  2         88  
121 2     2   12 use constant _KEYS_VIS => 2;
  2         4  
  2         91  
122 2     2   10 use constant _VALUES_VIS => 3;
  2         4  
  2         79  
123 2     2   11 use constant _HASH_VIS => 4;
  2         3  
  2         104  
124 2     2   10 use constant _CACHE_FLAG => 5;
  2         4  
  2         3057  
125              
126             sub new {
127 2     2 1 1731 my ($class,%args) = @_;
128              
129 2 100       13 my $cache_flag = (defined $args{cache}) ? $args{cache} : 1;
130 2         20 my $btree = Tree::Binary::Search->new();
131 2         48 $btree->useStringComparison();
132 2         19 my $self = [ 0, $btree, undef, undef, undef, $cache_flag];
133              
134 2 100       11 if ($cache_flag) {
135 1         15 my $hash_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
136             $hash_visitor->setNodeFilter(sub {
137 11     11   291 my ($t) = @_;
138 11         27 return ($t->getNodeKey, $t->getNodeValue());
139 1         32 });
140 1         7 $self->[_HASH_VIS] = $hash_visitor;
141              
142 1         4 my $keys_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
143 1     16   15 $keys_visitor->setNodeFilter(sub { return shift()->getNodeKey; } );
  16         403  
144 1         6 $self->[_KEYS_VIS] = $keys_visitor;
145              
146 1         3 my $values_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
147 1     11   18 $values_visitor->setNodeFilter(sub { return shift()->getNodeValue; } );
  11         246  
148 1         6 $self->[_VALUES_VIS] = $values_visitor;
149             }
150 2   33     22 return bless $self, ref $class || $class;
151             }
152              
153             sub count {
154 13     13 1 497 return shift()->[_COUNTER];
155             }
156              
157             sub keys {
158 4     4 1 48 my $self = shift;
159 4 50       13 return () unless ($self->[_COUNTER]);
160 4         6 my $keys_visitor;
161 4 100       9 if ($self->[_CACHE_FLAG]) {
162 2         4 $keys_visitor = $self->[_KEYS_VIS];
163             } else {
164 2         8 $keys_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
165 2     11   26 $keys_visitor->setNodeFilter(sub { return shift()->getNodeKey; } );
  11         241  
166             }
167 4         22 $self->[_BTREE]->accept($keys_visitor);
168 4         87 return $keys_visitor->getResults();
169             }
170              
171             sub values {
172 4     4 1 68 my $self = shift;
173 4 50       12 return () unless ($self->[_COUNTER]);
174 4         5 my $values_visitor;
175 4 100       15 if ($self->[_CACHE_FLAG]) {
176 2         4 $values_visitor = $self->[_VALUES_VIS];
177             } else {
178 2         7 $values_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
179 2     11   28 $values_visitor->setNodeFilter(sub { return shift()->getNodeValue; } );
  11         231  
180             }
181 4         33 $self->[_BTREE]->accept($values_visitor);
182 4         132 return $values_visitor->getResults();
183             }
184              
185             sub to_hash {
186 4     4 1 11 my $self = shift;
187 4 50       23 return () unless ($self->[_COUNTER]);
188 4         6 my $hash_visitor;
189 4 100       10 if ($self->[_CACHE_FLAG]) {
190 2         4 $hash_visitor = $self->[_HASH_VIS];
191             } else {
192 2         10 $hash_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
193             $hash_visitor->setNodeFilter(sub {
194 11     11   273 my ($t) = @_;
195 11         25 return ($t->getNodeKey, $t->getNodeValue());
196 2         35 });
197             }
198 4         22 $self->[_BTREE]->accept($hash_visitor);
199 4         108 return $hash_visitor->getResults();
200             }
201              
202             sub delete {
203 8     8 1 19 my $self = shift;
204 8 50       23 return 0 unless ($self->[_COUNTER]);
205 8         13 my $ok = eval { $self->[_BTREE]->delete(shift()); };
  8         29  
206 8 100       1608 warn "attempted to delete non-existant key" if $@;
207 8 100       96 $self->[_COUNTER]-- unless ($@);
208 8   100     44 return $ok || 0;
209             }
210              
211             sub exists {
212 4     4 1 758 my $self = shift;
213 4 50       37 return 0 unless ($self->[_COUNTER]);
214 4         17 return $self->[_BTREE]->exists(shift());
215             }
216              
217             sub get {
218 8     8 1 2622 my $self = shift;
219 8 50       30 return 0 unless ($self->[_COUNTER]);
220 8         12 my $value = eval {$self->[_BTREE]->select(shift()); };
  8         31  
221 8 100       739 warn "attempted to get value from non-existant key" if $@;
222 8         40 return $value;
223             }
224              
225             sub add {
226 20     20 1 11879 my $self = shift;
227 20         30 eval { $self->[_BTREE]->insert(@_); };
  20         79  
228 20 100       2606 if ($@) {
229 2         76 warn $@;
230 2         13 return 0;
231             } else {
232 18         26 $self->[_COUNTER]++;
233 18         43 return 1;
234             }
235             }
236              
237             sub set {
238 4     4 1 6 my $self = shift;
239 4 100 66     30 if ($self->[_COUNTER] && $self->[_BTREE]->exists($_[0])) {
240 2         122 eval { $self->[_BTREE]->update(@_); };
  2         17  
241             } else {
242 2         204 eval { $self->[_BTREE]->insert(@_); };
  2         10  
243 2 50       312 $self->[_COUNTER]++ unless ($@);
244             }
245 4 50       147 if ($@) {
246 0         0 warn $@;
247 0         0 return 0;
248             } else {
249 4         9 return 1;
250             }
251             }
252              
253             sub rebuild {
254 3     3 1 496 my $self = shift;
255 3         5 my $keys_visitor;
256 3 100       12 if ($self->[_CACHE_FLAG]) {
257 1         3 $keys_visitor = $self->[_KEYS_VIS];
258             } else {
259 2         20 $keys_visitor = Tree::Binary::Visitor::InOrderTraversal->new();
260 2     11   54 $keys_visitor->setNodeFilter(sub { return shift()->getNodeKey; } );
  11         340  
261             }
262 3         25 $self->[_BTREE]->accept($keys_visitor);
263 3         120 my @keys = $keys_visitor->getResults();
264              
265 3         31 my $btree = Tree::Binary::Search->new();
266 3         47 $btree->useStringComparison();
267              
268 3         21 foreach my $key (@keys) {
269 16         1691 $btree->insert($key, $self->[_BTREE]->select($key));
270             }
271 3         639 $self->[_BTREE] = $btree;
272 3         16 return 1;
273             }
274              
275             =head1 SEE ALSO
276              
277             Tree::Binary
278              
279             =head1 AUTHOR
280              
281             aaron trevena, Eteejay@droogs.orgE
282              
283             =head1 COPYRIGHT AND LICENSE
284              
285             Copyright (C) 2006 by Aaron Trevena
286              
287             This library is free software; you can redistribute it and/or modify
288             it under the same terms as Perl itself, either Perl version 5.8.5 or,
289             at your option, any later version of Perl 5 you may have available.
290              
291             =cut
292              
293              
294             1;