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-2023 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.03.
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   33 use vars '$VERSION';
  4         13  
  4         303  
11             $VERSION = '3.010';
12              
13 4     4   26 use base 'Mail::Reporter';
  4         12  
  4         386  
14              
15 4     4   37 use strict;
  4         19  
  4         96  
16 4     4   29 use warnings;
  4         12  
  4         128  
17              
18 4     4   22 use Carp;
  4         11  
  4         8377  
19              
20              
21             sub new(@)
22 150     150 1 463 { my ($class, %args) = @_;
23 150         392 (bless {}, $class)->init(\%args);
24             }
25              
26             sub init($)
27 150     150 0 223 { my ($self, $args) = @_;
28              
29 150 100       677 if(my $message = $args->{message})
    50          
30 135         169 { push @{$self->{MBTN_messages}}, $message;
  135         348  
31 135   33     326 $self->{MBTN_msgid} = $args->{msgid} || $message->messageId;
32             }
33             elsif(my $msgid = $args->{msgid})
34 15         43 { $self->{MBTN_msgid} = $msgid;
35             }
36             else
37 0         0 { croak "Need to specify message or message-id";
38             }
39              
40 150         254 $self->{MBTN_dummy_type} = $args->{dummy_type};
41 150         445 $self;
42             }
43              
44             #-------------------------------------------
45              
46              
47             sub message()
48 504     504 1 1726 { my $self = shift;
49              
50 504 100       1420 unless($self->{MBTN_messages})
51 10 50       27 { return () if wantarray;
52              
53             my $dummy = $self->{MBTN_dummy_type}->new
54             ( messageId =>$self->{MBTN_msgid}
55 10         72 );
56              
57 10         25 push @{$self->{MBTN_messages}}, $dummy;
  10         32  
58 10         20 return $dummy;
59             }
60              
61 494         626 my @messages = @{$self->{MBTN_messages}};
  494         930  
62 494 100       1141 return @messages if wantarray;
63 351 50       1095 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 262 { my $self = shift;
86 202 100       1067 !defined $self->{MBTN_messages} || $self->{MBTN_messages}[0]->isDummy;
87             }
88              
89              
90 658     658 1 1412 sub messageId() { shift->{MBTN_msgid} }
91              
92              
93             sub expand(;$)
94 121     121 1 173 { my $self = shift;
95 121 100 100     282 return $self->message->label('folded') || 0
96             unless @_;
97              
98 2         6 my $fold = not shift;
99 2         6 $_->label(folded => $fold) foreach $self->message;
100 2         6 $fold;
101             }
102              
103             sub folded(;$) # compatibility <2.0
104 121 100   121 0 313 { @_ == 1 ? shift->expand : shift->expand(not shift) }
105              
106             #-------------------------------------------
107              
108             sub repliedTo()
109 302     302 1 368 { my $self = shift;
110              
111             return wantarray
112             ? ($self->{MBTN_parent}, $self->{MBTN_quality})
113 302 50       764 : $self->{MBTN_parent};
114             }
115              
116              
117             sub follows($$)
118 138     138 1 268 { my ($self, $thread, $how) = @_;
119 138         232 my $quality = $self->{MBTN_quality};
120              
121             # Do not create cyclic constructs caused by erroneous refs.
122              
123 138         217 my $msgid = $self->messageId; # Look up for myself, upwards in thread
124 138         281 for(my $walker = $thread; defined $walker; $walker = $walker->repliedTo)
125 197 50       301 { return undef if $walker->messageId eq $msgid;
126             }
127              
128 138         217 my $threadid = $thread->messageId; # a->b and b->a (ref order reversed)
129 138         227 foreach ($self->followUps)
130 45 50       82 { return undef if $_->messageId eq $threadid;
131             }
132              
133             # Register
134              
135 138 100 100     391 if($how eq 'REPLY' || !defined $quality)
136 77         136 { $self->{MBTN_parent} = $thread;
137 77         116 $self->{MBTN_quality} = $how;
138 77         286 return $self;
139             }
140            
141 61 100       237 return $self if $quality eq 'REPLY';
142              
143 8 50 0     31 if($how eq 'REFERENCE' || ($how eq 'GUESS' && $quality ne 'REFERENCE'))
      33        
144 8         17 { $self->{MBTN_parent} = $thread;
145 8         12 $self->{MBTN_quality} = $how;
146             }
147              
148 8         33 $self;
149             }
150              
151              
152             sub followedBy(@)
153 138     138 1 206 { my $self = shift;
154 138         303 $self->{MBTN_followUps}{$_->messageId} = $_ foreach @_;
155 138         226 $self;
156             }
157              
158              
159             sub followUps()
160 337     337 1 459 { my $self = shift;
161 337 100       765 $self->{MBTN_followUps} ? values %{$self->{MBTN_followUps}} : ();
  140         492  
162             }
163              
164              
165             sub sortedFollowUps()
166 119     119 1 167 { my $self = shift;
167 119   50 60   508 my $prepare = shift || sub {shift->startTimeEstimate||0};
  60         115  
168 119   50 7   392 my $compare = shift || sub {(shift) <=> (shift)};
  7         38  
169              
170 119         259 my %value = map { ($prepare->($_) => $_) } $self->followUps;
  60         213  
171 119         2796 map { $value{$_} } sort {$compare->($a, $b)} keys %value;
  60         258  
  7         18  
172             }
173              
174             #-------------------------------------------
175              
176             sub threadToString(;$$$) # two undocumented parameters for layout args
177 119     119 1 3080 { my $self = shift;
178 119   100 106   430 my $code = shift || sub {shift->head->study('subject')};
  106         219  
179 119   100     447 my ($first, $other) = (shift || '', shift || '');
      100        
180 119         242 my $message = $self->message;
181 119         228 my @follows = $self->sortedFollowUps;
182              
183 119         191 my @out;
184 119 100       223 if($self->folded)
    100          
185 1   50     5 { my $text = $code->($message) || '';
186 1         137 chomp $text;
187 1         51 return " $first [" . $self->nrMessages . "] $text\n";
188             }
189             elsif($message->isDummy)
190 13 100       143 { $first .= $first ? '-*-' : ' *-';
191 13 100       46 return (shift @follows)->threadToString($code, $first, "$other " )
192             if @follows==1;
193              
194 7         38 push @out, (shift @follows)->threadToString($code, $first, "$other | " )
195             while @follows > 1;
196             }
197             else
198 105   100     645 { my $text = $code->($message) || '';
199 105         153976 chomp $text;
200 105         4738 my $size = $message->shortSize;
201 105         6365 @out = "$size$first $text\n";
202 105         4783 push @out, (shift @follows)
203             ->threadToString($code, "$other |-", "$other | " )
204             while @follows > 1;
205             }
206              
207 112 100       707 push @out, (shift @follows)->threadToString($code, "$other `-","$other " )
208             if @follows;
209              
210 112         539 join '', @out;
211             }
212              
213              
214             sub startTimeEstimate()
215 130     130 1 195 { my $self = shift;
216              
217 130 100       230 return $self->message->timestamp
218             unless $self->isDummy;
219              
220 10         17 my $earliest;
221 10         25 foreach ($self->followUps)
222 14         34 { my $stamp = $_->startTimeEstimate;
223              
224 14 100 66     1469 $earliest = $stamp
      100        
225             if !defined $earliest || (defined $stamp && $stamp < $earliest);
226             }
227              
228 10         54 $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 118 { my ($self, $code) = @_;
251              
252 70 50       111 $code->($self) or return $self;
253              
254             $_->recurse($code) or last
255 70   50     144 foreach $self->followUps;
256              
257 70         125 $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 868 { my $self = shift;
278 29         38 my $total = 0;
279 29 100   54   87 $self->recurse( sub {++$total unless shift->isDummy; 1} );
  54         88  
  54         105  
280 29         90 $total;
281             }
282              
283 1     1 0 5 sub nrMessages() {shift->numberOfMessages} # compatibility
284              
285              
286             sub threadMessages()
287 3     3 1 8 { my $self = shift;
288 3         6 my @messages;
289             $self->recurse
290             ( sub
291 6     6   8 { 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         15 @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;