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-2020 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   28 use vars '$VERSION';
  4         9  
  4         236  
11             $VERSION = '3.009';
12              
13 4     4   25 use base 'Mail::Reporter';
  4         12  
  4         428  
14              
15 4     4   28 use strict;
  4         9  
  4         115  
16 4     4   84 use warnings;
  4         10  
  4         132  
17              
18 4     4   22 use Carp;
  4         16  
  4         7949  
19              
20              
21             sub new(@)
22 150     150 1 471 { my ($class, %args) = @_;
23 150         387 (bless {}, $class)->init(\%args);
24             }
25              
26             sub init($)
27 150     150 0 230 { my ($self, $args) = @_;
28              
29 150 100       298 if(my $message = $args->{message})
    50          
30 135         167 { push @{$self->{MBTN_messages}}, $message;
  135         323  
31 135   33     311 $self->{MBTN_msgid} = $args->{msgid} || $message->messageId;
32             }
33             elsif(my $msgid = $args->{msgid})
34 15         32 { $self->{MBTN_msgid} = $msgid;
35             }
36             else
37 0         0 { croak "Need to specify message or message-id";
38             }
39              
40 150         251 $self->{MBTN_dummy_type} = $args->{dummy_type};
41 150         414 $self;
42             }
43              
44             #-------------------------------------------
45              
46              
47             sub message()
48 504     504 1 1373 { my $self = shift;
49              
50 504 100       985 unless($self->{MBTN_messages})
51 10 50       26 { return () if wantarray;
52              
53             my $dummy = $self->{MBTN_dummy_type}->new
54             ( messageId =>$self->{MBTN_msgid}
55 10         49 );
56              
57 10         25 push @{$self->{MBTN_messages}}, $dummy;
  10         26  
58 10         24 return $dummy;
59             }
60              
61 494         662 my @messages = @{$self->{MBTN_messages}};
  494         866  
62 494 100       1108 return @messages if wantarray;
63 351 50       1114 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 273 { my $self = shift;
86 202 100       976 !defined $self->{MBTN_messages} || $self->{MBTN_messages}[0]->isDummy;
87             }
88              
89              
90 653     653 1 1320 sub messageId() { shift->{MBTN_msgid} }
91              
92              
93             sub expand(;$)
94 121     121 1 168 { my $self = shift;
95 121 100 100     273 return $self->message->label('folded') || 0
96             unless @_;
97              
98 2         4 my $fold = not shift;
99 2         6 $_->label(folded => $fold) foreach $self->message;
100 2         5 $fold;
101             }
102              
103             sub folded(;$) # compatibility <2.0
104 121 100   121 0 350 { @_ == 1 ? shift->expand : shift->expand(not shift) }
105              
106             #-------------------------------------------
107              
108             sub repliedTo()
109 302     302 1 380 { my $self = shift;
110              
111             return wantarray
112             ? ($self->{MBTN_parent}, $self->{MBTN_quality})
113 302 50       744 : $self->{MBTN_parent};
114             }
115              
116              
117             sub follows($$)
118 138     138 1 248 { my ($self, $thread, $how) = @_;
119 138         220 my $quality = $self->{MBTN_quality};
120              
121             # Do not create cyclic constructs caused by erroneous refs.
122              
123 138         222 my $msgid = $self->messageId; # Look up for myself, upwards in thread
124 138         282 for(my $walker = $thread; defined $walker; $walker = $walker->repliedTo)
125 197 50       296 { return undef if $walker->messageId eq $msgid;
126             }
127              
128 138         216 my $threadid = $thread->messageId; # a->b and b->a (ref order reversed)
129 138         229 foreach ($self->followUps)
130 40 50       71 { return undef if $_->messageId eq $threadid;
131             }
132              
133             # Register
134              
135 138 100 100     395 if($how eq 'REPLY' || !defined $quality)
136 73         124 { $self->{MBTN_parent} = $thread;
137 73         103 $self->{MBTN_quality} = $how;
138 73         288 return $self;
139             }
140            
141 65 100       262 return $self if $quality eq 'REPLY';
142              
143 6 50 0     22 if($how eq 'REFERENCE' || ($how eq 'GUESS' && $quality ne 'REFERENCE'))
      33        
144 6         12 { $self->{MBTN_parent} = $thread;
145 6         10 $self->{MBTN_quality} = $how;
146             }
147              
148 6         26 $self;
149             }
150              
151              
152             sub followedBy(@)
153 138     138 1 187 { my $self = shift;
154 138         303 $self->{MBTN_followUps}{$_->messageId} = $_ foreach @_;
155 138         238 $self;
156             }
157              
158              
159             sub followUps()
160 337     337 1 440 { my $self = shift;
161 337 100       690 $self->{MBTN_followUps} ? values %{$self->{MBTN_followUps}} : ();
  135         412  
162             }
163              
164              
165             sub sortedFollowUps()
166 119     119 1 165 { my $self = shift;
167 119   50 60   519 my $prepare = shift || sub {shift->startTimeEstimate||0};
  60         115  
168 119   50 7   401 my $compare = shift || sub {(shift) <=> (shift)};
  7         33  
169              
170 119         255 my %value = map { ($prepare->($_) => $_) } $self->followUps;
  60         183  
171 119         2724 map { $value{$_} } sort {$compare->($a, $b)} keys %value;
  60         269  
  7         16  
172             }
173              
174             #-------------------------------------------
175              
176             sub threadToString(;$$$) # two undocumented parameters for layout args
177 119     119 1 2211 { my $self = shift;
178 119   100 106   446 my $code = shift || sub {shift->head->study('subject')};
  106         231  
179 119   100     488 my ($first, $other) = (shift || '', shift || '');
      100        
180 119         243 my $message = $self->message;
181 119         251 my @follows = $self->sortedFollowUps;
182              
183 119         209 my @out;
184 119 100       272 if($self->folded)
    100          
185 1   50     3 { my $text = $code->($message) || '';
186 1         120 chomp $text;
187 1         48 return " $first [" . $self->nrMessages . "] $text\n";
188             }
189             elsif($message->isDummy)
190 13 100       135 { $first .= $first ? '-*-' : ' *-';
191 13 100       46 return (shift @follows)->threadToString($code, $first, "$other " )
192             if @follows==1;
193              
194 7         34 push @out, (shift @follows)->threadToString($code, $first, "$other | " )
195             while @follows > 1;
196             }
197             else
198 105   100     522 { my $text = $code->($message) || '';
199 105         137509 chomp $text;
200 105         4337 my $size = $message->shortSize;
201 105         5906 @out = "$size$first $text\n";
202 105         4464 push @out, (shift @follows)
203             ->threadToString($code, "$other |-", "$other | " )
204             while @follows > 1;
205             }
206              
207 112 100       711 push @out, (shift @follows)->threadToString($code, "$other `-","$other " )
208             if @follows;
209              
210 112         536 join '', @out;
211             }
212              
213              
214             sub startTimeEstimate()
215 130     130 1 177 { my $self = shift;
216              
217 130 100       253 return $self->message->timestamp
218             unless $self->isDummy;
219              
220 10         20 my $earliest;
221 10         23 foreach ($self->followUps)
222 14         46 { my $stamp = $_->startTimeEstimate;
223              
224 14 100 66     1476 $earliest = $stamp
      100        
225             if !defined $earliest || (defined $stamp && $stamp < $earliest);
226             }
227              
228 10         67 $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 121 { my ($self, $code) = @_;
251              
252 70 50       118 $code->($self) or return $self;
253              
254             $_->recurse($code) or last
255 70   50     153 foreach $self->followUps;
256              
257 70         122 $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 815 { my $self = shift;
278 29         40 my $total = 0;
279 29 100   54   93 $self->recurse( sub {++$total unless shift->isDummy; 1} );
  54         80  
  54         100  
280 29         88 $total;
281             }
282              
283 1     1 0 4 sub nrMessages() {shift->numberOfMessages} # compatibility
284              
285              
286             sub threadMessages()
287 3     3 1 9 { my $self = shift;
288 3         5 my @messages;
289             $self->recurse
290             ( sub
291 6     6   9 { my $node = shift;
292 6 50       11 push @messages, $node->message unless $node->isDummy;
293 6         13 1;
294             }
295 3         17 );
296              
297 3         14 @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;