1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package de.saly.javamail.mock2;
27
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.TreeSet;
35
36 import javax.mail.Flags;
37 import javax.mail.Flags.Flag;
38 import javax.mail.Message;
39 import javax.mail.MessagingException;
40 import javax.mail.UIDFolder;
41 import javax.mail.internet.MimeMessage;
42 import javax.mail.search.SearchTerm;
43
44 public class MailboxFolder implements MockMessage.FlagChangeListener {
45
46 public static interface MailboxEventListener {
47
48 void folderCreated(MailboxFolder mf);
49
50 void folderDeleted(MailboxFolder mf);
51
52 void folderRenamed(String from, MailboxFolder to);
53
54 void messageAdded(MailboxFolder mf, MockMessage msg);
55
56 void messageChanged(MailboxFolder mf, MockMessage msg, boolean headerChanged, boolean flagsChanged);
57
58
59
60
61
62
63
64
65
66
67 void messageExpunged(MailboxFolder mf, MockMessage msg, boolean removed);
68
69 void uidInvalidated();
70
71 }
72
73 public static final char SEPARATOR = '/';
74 private final List<MailboxFolder> children = new ArrayList<MailboxFolder>();
75 private boolean exists = true;
76 protected final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
77
78 private final MockMailbox mailbox;
79
80 private volatile List<MailboxEventListener> mailboxEventListeners = Collections.synchronizedList(new ArrayList<MailboxEventListener>());
81
82 private final Map<Long, MockMessage> messages = new HashMap<Long, MockMessage>();
83 private String name;
84 private MailboxFolder parent;
85 private boolean simulateError = false;
86 private boolean subscribed;
87
88 private long uidValidity = 50;
89
90 private long uniqueMessageId = 10;
91
92 protected MailboxFolder(final String name, final MockMailbox mb, final boolean exists) {
93 super();
94
95 if (name == null) {
96 this.name = "";
97 } else {
98 this.name = name;
99 }
100
101 this.mailbox = mb;
102 this.exists = exists;
103
104 logger.debug("Created " + name + " (exists: " + exists + ")");
105 }
106
107 public synchronized Message add(final MimeMessage e) throws MessagingException {
108 checkExists();
109
110 uniqueMessageId++;
111
112 final MockMessage mockMessage = new MockMessage(e, uniqueMessageId, this, this);
113
114 mockMessage.setSpecialHeader("Message-ID", String.valueOf(uniqueMessageId));
115 mockMessage.setSpecialHeader("X-Mock-Folder", getFullName());
116 mockMessage.setFlags(new Flags(Flag.RECENT), true);
117
118
119 messages.put(uniqueMessageId, mockMessage);
120
121 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
122 mailboxEventListener.messageAdded(this, mockMessage);
123 }
124
125 logger.debug("Message ID " + uniqueMessageId + " to " + getFullName() + " added for user " + mailbox.getAddress());
126
127 return mockMessage;
128 }
129
130 public synchronized void addMailboxEventListener(final MailboxEventListener l) {
131 if (l != null) {
132 mailboxEventListeners.add(l);
133 }
134 }
135
136 protected MailboxFolder addSpecialSubFolder(final String name) {
137 final MailboxFolder mbt = new MailboxFolder(name, mailbox, true);
138 mbt.parent = this;
139 children.add(mbt);
140 return mbt;
141 }
142
143 protected void checkExists() {
144 if (!exists) {
145 throw new IllegalStateException("folder does not exist");
146 }
147 }
148
149 protected void checkFolderName(final String name) {
150 checkFolderName(name, true);
151 }
152
153 protected void checkFolderName(final String name, final boolean checkSeparator) {
154
155
156 if (name == null || name.trim().equals("") || name.equalsIgnoreCase("inbox") || checkSeparator
157 && name.contains(String.valueOf(SEPARATOR))) {
158 throw new IllegalArgumentException("name '" + name + "' is not valid");
159 }
160 }
161
162 public synchronized MailboxFolder create() {
163 if (isExists()) {
164 throw new IllegalStateException("already exists");
165 }
166 checkFolderName(this.name);
167
168 exists = true;
169
170
171
172 if (parent != null && !parent.isExists()) {
173 parent.create();
174 }
175
176
177
178
179
180
181
182
183
184
185
186 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
187 mailboxEventListener.folderCreated(this);
188 }
189
190 logger.debug("Folder " + this.getFullName() + " created");
191 return this;
192
193 }
194
195 public synchronized void deleteFolder(final boolean recurse) {
196 checkExists();
197 checkFolderName(this.name);
198
199 if (isRoot()) {
200 throw new IllegalArgumentException("root cannot be deleted");
201 }
202
203 messages.clear();
204
205
206 if (recurse) {
207 for (final MailboxFolder mf : getChildren()) {
208 mf.deleteFolder(recurse);
209 }
210 }
211
212 parent.children.remove(this);
213 this.exists = false;
214
215 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
216 mailboxEventListener.folderDeleted(this);
217 }
218 logger.debug("Folder " + this.getFullName() + " deleted");
219
220 }
221
222 @Override
223 public boolean equals(final Object obj) {
224 if (this == obj) {
225 return true;
226 }
227 if (obj == null) {
228 return false;
229 }
230 if (getClass() != obj.getClass()) {
231 return false;
232 }
233 final MailboxFolder other = (MailboxFolder) obj;
234 if (name == null) {
235 if (other.name != null) {
236 return false;
237 }
238 } else if (!name.equals(other.name)) {
239 return false;
240 }
241 if (parent == null) {
242 if (other.parent != null) {
243 return false;
244 }
245 } else if (!parent.equals(other.parent)) {
246 return false;
247 }
248 return true;
249 }
250
251 public synchronized Message[] expunge() throws MessagingException {
252 checkExists();
253 final List<Message> expunged = new ArrayList<Message>();
254 for (final Message msg : getByFlags(new Flags(Flag.DELETED), true)) {
255
256 expunged.add(messages.remove(((MockMessage) msg).getMockid()));
257 ((MockMessage) msg).setExpunged(true);
258
259 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
260 mailboxEventListener.messageExpunged(this, (MockMessage) msg, true);
261 }
262 }
263
264 logger.debug(expunged.size() + " messages expunged (deleted) from" + getFullName());
265 return expunged.toArray(new Message[expunged.size()]);
266
267 }
268
269 public synchronized Message[] expunge(final Message[] msgs) throws MessagingException {
270 checkExists();
271 final List<Long> toExpunge = new ArrayList<Long>();
272
273 for (final Message msg : msgs) {
274 toExpunge.add(((MockMessage) msg).getMockid());
275 }
276
277 final List<Message> expunged = new ArrayList<Message>();
278 for (final Message msg : getByFlags(new Flags(Flag.DELETED), true)) {
279
280 if (!toExpunge.contains(new Long(((MockMessage) msg).getMockid()))) {
281 continue;
282 }
283
284 expunged.add(messages.remove(((MockMessage) msg).getMockid()));
285 ((MockMessage) msg).setExpunged(true);
286
287 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
288 mailboxEventListener.messageExpunged(this, (MockMessage) msg, true);
289 }
290 }
291
292 logger.debug(expunged.size() + " messages expunged (deleted) from " + getFullName());
293 return expunged.toArray(new Message[expunged.size()]);
294
295 }
296
297 public synchronized Message[] getByFlags(final Flags flags, final boolean mustSet ) throws MessagingException {
298 checkExists();
299 final List<MockMessage> sms = new ArrayList<MockMessage>();
300 int num = 0;
301
302 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
303
304 if (mustSet && mockMessage.getFlags().contains(flags) || !mustSet && !mockMessage.getFlags().contains(flags)) {
305 mockMessage.setMessageNumber(++num);
306
307 sms.add(mockMessage);
308 }
309
310 }
311 logger.debug("getByFlags() for " + getFullName() + " returns " + sms.size());
312 return sms.toArray(new Message[sms.size()]);
313 }
314
315 public synchronized Message getById(final long id ) {
316 checkExists();
317 final Message m = messages.get(id);
318
319 if (m == null) {
320
321 logger.debug("No message with id " + id + ", return null");
322 return null;
323
324 }
325
326
327 logger.debug("getById(" + id + ") for " + getFullName() + " returns successful");
328
329 return m;
330 }
331
332
333
334 public synchronized Message[] getByIds(final long start, final long end) {
335 checkExists();
336 final List<MockMessage> sms = new ArrayList<MockMessage>();
337 int num = 0;
338
339 MockMessage lastMsg = null;
340
341 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
342
343 lastMsg = mockMessage;
344
345 if (end == UIDFolder.LASTUID) {
346 if (getMessageCount() != 1 && mockMessage.getMockid() < start) {
347
348 continue;
349 }
350 } else {
351 if (mockMessage.getMockid() < start || mockMessage.getMockid() > end) {
352 continue;
353 }
354 }
355
356 mockMessage.setMessageNumber(++num);
357
358 sms.add(mockMessage);
359 }
360
361 if (end == UIDFolder.LASTUID && sms.size() == 0) {
362 lastMsg.setMessageNumber(++num);
363
364 sms.add(lastMsg);
365 }
366
367 logger.debug("getByIds(" + start + "," + end + " for " + getFullName() + " returns " + sms.size());
368 return sms.toArray(new Message[sms.size()]);
369 }
370
371 public synchronized Message[] getByIds(final long[] id ) {
372 checkExists();
373 final List<Long> idlist = new ArrayList<Long>();
374 for (final long value : id) {
375 idlist.add(value);
376 }
377 final List<MockMessage> sms = new ArrayList<MockMessage>();
378 int num = 0;
379
380 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
381
382 if (!idlist.contains(mockMessage.getMockid())) {
383 continue;
384 }
385
386 mockMessage.setMessageNumber(++num);
387
388 sms.add(mockMessage);
389 }
390
391 logger.debug("getByIds(" + Arrays.toString(id) + ") for " + getFullName() + " returns " + sms.size());
392 return sms.toArray(new Message[sms.size()]);
393 }
394
395 public synchronized Message getByMsgNum(final int msgnum) {
396 checkExists();
397 final List<MockMessage> sms = new ArrayList<MockMessage>();
398
399 int num = 0;
400
401 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
402
403 mockMessage.setMessageNumber(++num);
404
405 sms.add(mockMessage);
406 }
407
408 logger.debug("getByMsgNum(" + msgnum + "), size is " + sms.size());
409
410 if (msgnum - 1 < 0 || msgnum > sms.size()) {
411 throw new ArrayIndexOutOfBoundsException("message number (" + msgnum + ") out of bounds (" + sms.size() + ") for "
412 + getFullName());
413 }
414
415 final Message m = sms.get(msgnum - 1);
416 return m;
417 }
418
419
420
421
422
423 public synchronized List<MailboxFolder> getChildren() {
424 checkExists();
425 return Collections.unmodifiableList(new ArrayList<MailboxFolder>(children));
426 }
427
428 public synchronized String getFullName() {
429
430 if (isRoot()) {
431 return "";
432 }
433
434 return parent.isRoot() ? name : parent.getFullName() + SEPARATOR + name;
435
436 }
437
438
439
440
441 public MockMailbox getMailbox() {
442 return mailbox;
443 }
444
445 public synchronized int getMessageCount() {
446 checkExists();
447 logger.debug("getMessageCount() for " + getFullName() + " returns " + messages.size());
448 return messages.size();
449 }
450
451 public synchronized Message[] getMessages() {
452 checkExists();
453 final List<MockMessage> sms = new ArrayList<MockMessage>();
454 int num = 0;
455
456 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
457
458 mockMessage.setMessageNumber(++num);
459
460 sms.add(mockMessage);
461 }
462 logger.debug("getMessages() for " + getFullName() + " returns " + sms.size());
463 return sms.toArray(new Message[sms.size()]);
464 }
465
466 public String getName() {
467 return name;
468 }
469
470 public MailboxFolder getOrAddSubFolder(final String name) throws MessagingException {
471
472
473 if (name == null || "".equals(name.trim())) {
474 throw new MessagingException("cannot get or add root folder");
475 }
476
477 logger.debug("getOrAddSubFolder(" + name + ") on " + getFullName());
478
479 final String[] path = name.split(String.valueOf(SEPARATOR));
480
481 MailboxFolder last = this;
482 for (int i = 0; i < path.length; i++) {
483
484 final String element = path[i];
485
486 if ("inbox".equalsIgnoreCase(element)) {
487 last = mailbox.getInbox();
488 } else {
489 checkFolderName(element);
490 final MailboxFolder mbt = new MailboxFolder(element, mailbox, false);
491 mbt.parent = last;
492
493 int index = -1;
494 if ((index = last.children.indexOf(mbt)) != -1) {
495
496 final MailboxFolder tmp = last.children.get(index);
497 if (tmp.isExists()) {
498 last = tmp;
499 continue;
500 }
501 }
502
503 last.children.add(mbt);
504
505 logger.debug("Subfolder " + mbt.getFullName() + " added");
506 last = mbt;
507 }
508
509 }
510
511 return last;
512
513 }
514
515 public synchronized MailboxFolder getParent() {
516 checkExists();
517 return parent;
518 }
519
520 public synchronized int getSizeInBytes() throws MessagingException {
521 checkExists();
522 int size = 0;
523
524 for (final MockMessage mockMessage : new TreeSet<MockMessage>(messages.values())) {
525
526 if (mockMessage.getSize() > 0) {
527 size += mockMessage.getSize();
528 }
529
530 }
531
532 return size;
533 }
534
535 public synchronized long getUID(final Message msg) {
536 checkExists();
537 return ((MockMessage) msg).getMockid();
538 }
539
540
541
542
543 public synchronized long getUidValidity() {
544 checkExists();
545 return uidValidity;
546 }
547
548
549
550
551 protected long getUniqueMessageId() {
552 return uniqueMessageId;
553 }
554
555 @Override
556 public int hashCode() {
557 final int prime = 31;
558 int result = 1;
559 result = prime * result + (name == null ? 0 : name.hashCode());
560 result = prime * result + (parent == null ? 0 : parent.hashCode());
561 return result;
562 }
563
564 public synchronized boolean hasMessages() {
565 checkExists();
566 return messages.isEmpty();
567 }
568
569 public synchronized void invalidateUid() {
570 checkExists();
571 uidValidity += 10;
572
573 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
574 mailboxEventListener.uidInvalidated();
575 }
576 logger.debug("UidValidity invalidated, new UidValidity is " + uidValidity);
577 }
578
579
580
581
582 public boolean isExists() {
583 return exists;
584 }
585
586 public boolean isInbox() {
587 return name != null && name.equalsIgnoreCase("inbox");
588 }
589
590 public boolean isRoot() {
591 return name == null || name.equals("") || parent == null;
592 }
593
594
595
596
597 public boolean isSimulateError() {
598 return simulateError;
599 }
600
601 protected boolean isSubscribed() {
602 return subscribed;
603 }
604
605 public synchronized void markMessageAsDeleted(final Message e) throws MessagingException {
606 checkExists();
607 ((MockMessage) e).setFlag(Flag.DELETED, true);
608
609
610 logger.debug("Mark message " + ((MockMessage) e).getMockid() + " as deleted (Flag DELETED set)");
611 }
612
613 public synchronized void markMessageAsSeen(final Message e) throws MessagingException {
614 checkExists();
615 ((MockMessage) e).setFlag(Flag.SEEN, true);
616
617
618 logger.debug("Mark message " + ((MockMessage) e).getMockid() + " as seen (Flag SEEN set)");
619 }
620
621 @Override
622 public void onFlagChange(final MockMessage msg, final Flags flags, final boolean set) {
623
624 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
625 mailboxEventListener.messageChanged(this, msg, false, true);
626 }
627
628 logger.debug("Flags of message " + msg.getMockid() + " change");
629
630 if (messages.size() > 0 && messages.get(msg.getMockid()) != null) {
631 try {
632 if (set && messages.get(msg.getMockid()).getFlags().contains(flags)) {
633 return;
634
635 }
636
637 if (set && !messages.get(msg.getMockid()).getFlags().contains(flags)) {
638 messages.get(msg.getMockid()).setFlags(flags, set);
639
640 }
641
642 if (!set && messages.get(msg.getMockid()).getFlags().contains(flags)) {
643 messages.get(msg.getMockid()).setFlags(flags, set);
644
645 }
646
647 if (!set && !messages.get(msg.getMockid()).getFlags().contains(flags)) {
648 return;
649
650 }
651 } catch (final Exception e) {
652 logger.error("Error while changing flags " + e.toString(), e);
653 }
654 }
655
656 }
657
658 public synchronized void removeMailboxEventListener(final MailboxEventListener l) {
659 if (l != null) {
660 mailboxEventListeners.remove(l);
661 }
662 }
663
664 public synchronized void renameFolder(final String newName) {
665 checkExists();
666 checkFolderName(this.name);
667 checkFolderName(newName);
668 final String tmpOldName = name;
669
670 name = newName;
671
672 for (final MailboxEventListener mailboxEventListener : mailboxEventListeners) {
673 mailboxEventListener.folderRenamed(tmpOldName, this);
674 }
675
676
677
678
679
680
681
682
683 logger.debug("Folder " + tmpOldName + " renamed to " + newName + newName + " - New Fullname is " + this.getFullName());
684
685 }
686
687 public Message[] search(final SearchTerm term, final Message[] msgsToSearch) {
688 final List<MockMessage> sms = new ArrayList<MockMessage>();
689 final List<Message> msgsToSearchL = new ArrayList<Message>();
690
691 if (msgsToSearch != null) {
692 msgsToSearchL.addAll(Arrays.asList(msgsToSearch));
693 }
694
695 for (final Message msg : getMessages()) {
696 if (term != null && term.match(msg)) {
697
698 if (msgsToSearch == null || msgsToSearchL.contains(msg)) {
699 sms.add((MockMessage) msg);
700 }
701 }
702 }
703 return sms.toArray(new Message[sms.size()]);
704 }
705
706
707
708
709
710 public void setSimulateError(final boolean simulateError) {
711 this.simulateError = simulateError;
712 }
713
714 protected void setSubscribed(final boolean subscribed) {
715 this.subscribed = subscribed;
716 }
717 }