File Coverage

blib/lib/Mail/Box/Thread/Node.pm
Criterion Covered Total %
statement 126 155 81.2
branch 43 62 69.3
condition 24 39 61.5
subroutine 28 35 80.0
pod 19 22 86.3
total 240 313 76.6


line stmt bran cond sub pod time code
1             # Copyrights 2001-2019 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution Mail-Box. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Mail::Box::Thread::Node;
10 4     4   23 use vars '$VERSION';
  4         8  
  4         165  
11             $VERSION = '3.008';
12              
13 4     4   20 use base 'Mail::Reporter';
  4         8  
  4         279  
14              
15 4     4   22 use strict;
  4         7  
  4         59  
16 4     4   16 use warnings;
  4         7  
  4         81  
17              
18 4     4   17 use Carp;
  4         15  
  4         6005  
19              
20              
21             sub new(@)
22 150     150 1 370 { my ($class, %args) = @_;
23 150         314 (bless {}, $class)->init(\%args);
24             }
25              
26             sub init($)
27 150     150 0 184 { my ($self, $args) = @_;
28              
29 150 100       254 if(my $message = $args->{message})
    50          
30 135         153 { push @{$self->{MBTN_messages}}, $message;
  135         595  
31 135   33     308 $self->{MBTN_msgid} = $args->{msgid} || $message->messageId;
32             }
33             elsif(my $msgid = $args->{msgid})
34 15         25 { $self->{MBTN_msgid} = $msgid;
35             }
36             else
37 0         0 { croak "Need to specify message or message-id";
38             }
39              
40 150         213 $self->{MBTN_dummy_type} = $args->{dummy_type};
41 150         324 $self;
42             }
43              
44             #-------------------------------------------
45              
46              
47             sub message()
48 504     504 1 1209 { my $self = shift;
49              
50 504 100       939 unless($self->{MBTN_messages})
51 10 50       22 { return () if wantarray;
52              
53             my $dummy = $self->{MBTN_dummy_type}->new
54             ( messageId =>$self->{MBTN_msgid}
55 10         50 );
56              
57 10         18 push @{$self->{MBTN_messages}}, $dummy;
  10         22  
58 10         21 return $dummy;
59             }
60              
61 494         533 my @messages = @{$self->{MBTN_messages}};
  494         747  
62 494 100       896 return @messages if wantarray;
63 351 50       881 return $messages[0] if @messages==1;
64              
65 0         0 foreach (@messages)
66 0 0       0 { return $_ unless $_->isDeleted;
67             }
68              
69 0         0 $messages[0];
70             }
71              
72              
73             sub addMessage($)
74 0     0 1 0 { my ($self, $message) = @_;
75            
76 0 0       0 return $self->{MBTN_messages} = [ $message ]
77             if $self->isDummy;
78              
79 0         0 push @{$self->{MBTN_messages}}, $message;
  0         0  
80 0         0 $message;
81             }
82              
83              
84             sub isDummy()
85 202     202 1 227 { my $self = shift;
86 202 100       832 !defined $self->{MBTN_messages} || $self->{MBTN_messages}[0]->isDummy;
87             }
88              
89              
90 648     648 1 1174 sub messageId() { shift->{MBTN_msgid} }
91              
92              
93             sub expand(;$)
94 121     121 1 140 { my $self = shift;
95 121 100 100     233 return $self->message->label('folded') || 0
96             unless @_;
97              
98 2         4 my $fold = not shift;
99 2         5 $_->label(folded => $fold) foreach $self->message;
100 2         4 $fold;
101             }
102              
103             sub folded(;$) # compatibility <2.0
104 121 100   121 0 252 { @_ == 1 ? shift->expand : shift->expand(not shift) }
105              
106             #-------------------------------------------
107              
108             sub repliedTo()
109 296     296 1 321 { my $self = shift;
110              
111             return wantarray
112             ? ($self->{MBTN_parent}, $self->{MBTN_quality})
113 296 50       593 : $self->{MBTN_parent};
114             }
115              
116              
117             sub follows($$)
118 138     138 1 208 { my ($self, $thread, $how) = @_;
119 138         172 my $quality = $self->{MBTN_quality};
120              
121             # Do not create cyclic constructs caused by erroneous refs.
122              
123 138         187 my $msgid = $self->messageId; # Look up for myself, upwards in thread
124 138         233 for(my $walker = $thread; defined $walker; $walker = $walker->repliedTo)
125 191 50       254 { return undef if $walker->messageId eq $msgid;
126             }
127              
128 138         178 my $threadid = $thread->messageId; # a->b and b->a (ref order reversed)
129 138         199 foreach ($self->followUps)
130 41 50       59 { return undef if $_->messageId eq $threadid;
131             }
132              
133             # Register
134              
135 138 100 100     336 if($how eq 'REPLY' || !defined $quality)
136 76         103 { $self->{MBTN_parent} = $thread;
137 76         105 $self->{MBTN_quality} = $how;
138 76         258 return $self;
139             }
140            
141 62 100       201 return $self if $quality eq 'REPLY';
142              
143 9 50 0     27 if($how eq 'REFERENCE' || ($how eq 'GUESS' && $quality ne 'REFERENCE'))
      33        
144 9         14 { $self->{MBTN_parent} = $thread;
145 9         14 $self->{MBTN_quality} = $how;
146             }
147              
148 9         29 $self;
149             }
150              
151              
152             sub followedBy(@)
153 138     138 1 164 { my $self = shift;
154 138         251 $self->{MBTN_followUps}{$_->messageId} = $_ foreach @_;
155 138         187 $self;
156             }
157              
158              
159             sub followUps()
160 337     337 1 385 { my $self = shift;
161 337 100       587 $self->{MBTN_followUps} ? values %{$self->{MBTN_followUps}} : ();
  136         337  
162             }
163              
164              
165             sub sortedFollowUps()
166 119     119 1 149 { my $self = shift;
167 119   50 60   438 my $prepare = shift || sub {shift->startTimeEstimate||0};
  60         91  
168 119   50 7   351 my $compare = shift || sub {(shift) <=> (shift)};
  7         25  
169              
170 119         202 my %value = map { ($prepare->($_) => $_) } $self->followUps;
  60         158  
171 119         2157 map { $value{$_} } sort {$compare->($a, $b)} keys %value;
  60         204  
  7         14  
172             }
173              
174             #-------------------------------------------
175              
176             sub threadToString(;$$$) # two undocumented parameters for layout args
177 119     119 1 1755 { my $self = shift;
178 119   100 106   363 my $code = shift || sub {shift->head->study('subject')};
  106         193  
179 119   100     361 my ($first, $other) = (shift || '', shift || '');
      100        
180 119         229 my $message = $self->message;
181 119         201 my @follows = $self->sortedFollowUps;
182              
183 119         155 my @out;
184 119 100       196 if($self->folded)
    100          
185 1   50     4 { my $text = $code->($message) || '';
186 1         99 chomp $text;
187 1         53 return " $first [" . $self->nrMessages . "] $text\n";
188             }
189             elsif($message->isDummy)
190 13 100       105 { $first .= $first ? '-*-' : ' *-';
191 13 100       41 return (shift @follows)->threadToString($code, $first, "$other " )
192             if @follows==1;
193              
194 7         30 push @out, (shift @follows)->threadToString($code, $first, "$other | " )
195             while @follows > 1;
196             }
197             else
198 105   100     457 { my $text = $code->($message) || '';
199 105         111363 chomp $text;
200 105         3550 my $size = $message->shortSize;
201 105         4679 @out = "$size$first $text\n";
202 105         3573 push @out, (shift @follows)
203             ->threadToString($code, "$other |-", "$other | " )
204             while @follows > 1;
205             }
206              
207 112 100       548 push @out, (shift @follows)->threadToString($code, "$other `-","$other " )
208             if @follows;
209              
210 112         438 join '', @out;
211             }
212              
213              
214             sub startTimeEstimate()
215 130     130 1 167 { my $self = shift;
216              
217 130 100       180 return $self->message->timestamp
218             unless $self->isDummy;
219              
220 10         19 my $earliest;
221 10         17 foreach ($self->followUps)
222 14         26 { my $stamp = $_->startTimeEstimate;
223              
224 14 100 66     1172 $earliest = $stamp
      100        
225             if !defined $earliest || (defined $stamp && $stamp < $earliest);
226             }
227              
228 10         46 $earliest;
229             }
230              
231              
232             sub endTimeEstimate()
233 0     0 1 0 { my $self = shift;
234              
235 0         0 my $latest;
236             $self->recurse
237 0     0   0 ( sub { my $node = shift;
238 0 0       0 unless($node->isDummy)
239 0         0 { my $stamp = $node->message->timestamp;
240 0 0 0     0 $latest = $stamp if !$latest || $stamp > $latest;
241             }
242             }
243 0         0 );
244              
245 0         0 $latest;
246             }
247              
248              
249             sub recurse($)
250 70     70 1 95 { my ($self, $code) = @_;
251              
252 70 50       118 $code->($self) or return $self;
253              
254             $_->recurse($code) or last
255 70   50     129 foreach $self->followUps;
256              
257 70         150 $self;
258             }
259              
260              
261             sub totalSize()
262 0     0 1 0 { my $self = shift;
263 0         0 my $total = 0;
264              
265             $self->recurse
266             ( sub {
267 0     0   0 my @msgs = shift->messages;
268 0 0       0 $total += $msgs[0]->size if @msgs;
269 0         0 1;}
270 0         0 );
271              
272 0         0 $total;
273             }
274              
275              
276             sub numberOfMessages()
277 29     29 1 725 { my $self = shift;
278 29         32 my $total = 0;
279 29 100   54   83 $self->recurse( sub {++$total unless shift->isDummy; 1} );
  54         68  
  54         121  
280 29         101 $total;
281             }
282              
283 1     1 0 4 sub nrMessages() {shift->numberOfMessages} # compatibility
284              
285              
286             sub threadMessages()
287 3     3 1 10 { my $self = shift;
288 3         6 my @messages;
289             $self->recurse
290             ( sub
291 6     6   10 { my $node = shift;
292 6 50       11 push @messages, $node->message unless $node->isDummy;
293 6         12 1;
294             }
295 3         15 );
296              
297 3         13 @messages;
298             }
299              
300              
301              
302             sub ids()
303 0     0 1   { my $self = shift;
304 0           my @ids;
305 0     0     $self->recurse( sub {push @ids, shift->messageId} );
  0            
306 0           @ids;
307             }
308              
309             #-------------------------------------------
310              
311              
312              
313             1;