View Javadoc

1   package org.unitedfront2.domain.communication;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.Date;
7   import java.util.List;
8   import java.util.Locale;
9   
10  import org.apache.commons.lang.RandomStringUtils;
11  import org.apache.commons.lang.builder.EqualsBuilder;
12  import org.apache.commons.lang.builder.HashCodeBuilder;
13  import org.apache.commons.lang.builder.ToStringBuilder;
14  import org.apache.commons.logging.Log;
15  import org.apache.commons.logging.LogFactory;
16  import org.unitedfront2.dao.MailDao;
17  import org.unitedfront2.domain.AccountTable;
18  import org.unitedfront2.domain.Domain;
19  import org.unitedfront2.domain.Identifiable;
20  import org.unitedfront2.domain.SimpleUser;
21  import org.unitedfront2.domain.SimpleUserTable;
22  import org.unitedfront2.domain.Storable;
23  import org.unitedfront2.domain.User;
24  
25  /**
26   * メールクラスです。メールは1対1のコミュニケーション用のツールです。親メールに対して返信していくことで、リスト
27   * 構造を持つ一連のメール群を形成できます。これをメールのスレッドと呼びます。
28   *
29   * @author kurokkie
30   *
31   */
32  public class Mail implements Identifiable<Mail>, Storable, Serializable,
33      Domain {
34  
35      /** ランダムで生成されるときのコードの長さ (32) */
36      public static final int GENERATED_CODE_LENGTH = 32;
37  
38      /** シリアル番号 */
39      private static final long serialVersionUID = -8522418960016784969L;
40  
41      /** ランダム生成コードが重複した際に、再生成して再チャレンジする回数 (10) */
42      private static final int MAX_CHALLENGE_COUNT = 10;
43  
44      /**
45       * 宛先と差出人のユーザオブジェクトを復元します。
46       *
47       * @param mails メールリスト
48       * @ensure 全てのメールの宛先にユーザオブジェクトが設定される。もし宛先 ID が
49       * <code>null</code> なら設定されない
50       * @ensure 全てのメールの差出人にユーザオブジェクトが設定される。もし差出人 ID が
51       * <code>null</code> なら設定されない
52       */
53      public static void retrieveUsers(List<Mail> mails) {
54          for (Mail m : mails) {
55              m.retrieveTo();
56              m.retrieveFrom();
57          }
58      }
59  
60      /** ログ */
61      protected final transient Log logger = LogFactory.getLog(getClass());
62  
63      /** ID */
64      private Integer id;
65  
66      /** コード */
67      private String code;
68  
69      /** 宛先のユーザ ID */
70      private Integer toId;
71  
72      /** 差出人のユーザ ID */
73      private Integer fromId;
74  
75      /** 送信日時 */
76      private Date sentDate;
77  
78      /** 件名 */
79      private String subject;
80  
81      /** 本文 */
82      private String body;
83  
84      /** 宛先ユーザが既読の場合は true 、そうでなければ false */
85      private boolean read;
86  
87      /** 宛先ユーザ */
88      private transient SimpleUser to;
89  
90      /** 差出人ユーザ */
91      private transient SimpleUser from;
92  
93      /** 宛先のメールアドレス。実際にメールを送信するための変数。 */
94      private transient String toMailAddr;
95  
96      /** このメールに対して返信したメール */
97      private transient Mail next;
98  
99      /** メールデータアクセスオブジェクト **/
100     private transient MailDao mailDao;
101 
102     /** ユーザテーブル */
103     private transient SimpleUserTable simpleUserTable;
104 
105     /** アカウントテーブル */
106     private transient AccountTable accountTable;
107 
108     public Mail() {
109         super();
110     }
111 
112     public Mail(Integer toId, Integer fromId, String subject, String body) {
113         super();
114         this.toId = toId;
115         this.fromId = fromId;
116         this.subject = subject;
117         this.body = body;
118     }
119 
120     public Mail(Integer id, String code, Integer toId, Integer fromId,
121             String subject, String body, Date sentDate, boolean read) {
122         this(toId, fromId, subject, body);
123         this.id = id;
124         this.code = code;
125         this.sentDate = (Date) sentDate.clone();
126         this.read = read;
127     }
128 
129     @Override
130     public String toString() {
131         ToStringBuilder tsb = new ToStringBuilder(this)
132             .append("id", id)
133             .append("code", code)
134             .append("toId", toId)
135             .append("fromId", fromId)
136             .append("sentDate", sentDate)
137             .append("subject", subject)
138             .append("body", body)
139             .append("read", read)
140             .append("next", next);
141         if (to != null) {
142             tsb.append("to", to);
143         }
144         if (from != null) {
145             tsb.append("from", from);
146         }
147         return tsb.toString();
148     }
149 
150     @Override
151     public boolean equals(final Object other) {
152         if (!(other instanceof Mail)) {
153             return false;
154         }
155         Mail castOther = (Mail) other;
156         return new EqualsBuilder()
157             .append(id, castOther.id)
158             .append(code, castOther.code)
159             .append(toId, castOther.toId)
160             .append(fromId, castOther.fromId)
161             .append(sentDate, castOther.sentDate)
162             .append(subject, castOther.subject)
163             .append(body, castOther.body)
164             .append(read, castOther.read).isEquals();
165     }
166 
167     @Override
168     public int hashCode() {
169         return new HashCodeBuilder()
170             .append(id)
171             .append(code)
172             .append(toId)
173             .append(fromId)
174             .append(sentDate)
175             .append(subject)
176             .append(body)
177             .append(read).toHashCode();
178     }
179 
180     @Override
181     public boolean identify(Mail other) {
182         if (id == null || other.getId() == null) {
183             return false;
184         }
185         return id.equals(other.getId());
186     }
187 
188     /**
189      * {@link #send()} と同様の処理です。
190      *
191      * @see Storable#store()
192      */
193     @Override
194     public void store() {
195         send();
196     }
197 
198     /**
199      * このメールを送信します。コードは自動で発行されます。送信日時は現在日時が設定されます。
200      *
201      * @require ${this.toId} is not null.
202      * @ensure ${this.code} is auto generated.
203      * @ensure ${this.lastUpdateDate} is current date.
204      */
205     public void send() {
206         if (toId == null) {
207             String message = "The toId must not be null. Mail[" + this + "]";
208             logger.error(message);
209             throw new IllegalStateException(message);
210         }
211         this.code = generateCode();
212         this.sentDate = mailDao.getCurrentDate();
213         mailDao.register(this);
214     }
215 
216     private String generateCode() {
217         for (int i = 0; i < MAX_CHALLENGE_COUNT; i++) {
218             String code = RandomStringUtils.randomAlphanumeric(
219                     GENERATED_CODE_LENGTH).toLowerCase(Locale.ENGLISH);
220             if (mailDao.findByCode(code) == null) {
221                 return code;
222             }
223         }
224         String message = "Failed to generate code.";
225         logger.error(message);
226         throw new IllegalStateException(message);
227     }
228 
229     /**
230      * メールを返信します。コードは自動で発行されます。送信日時は現在日時が設定されます。
231      *
232      * @param parentId 親メール ID
233      * @require ${this.toId} is not null.
234      * @require ${this.fromId} is not null.
235      * @ensure ${this.code} is auto generated.
236      * @ensure ${this.lastUpdateDate} is current date.
237      * @see #store()
238      */
239     public void send(int parentId) {
240         if (toId == null) {
241             String message = "The To must not be null. Mail[" + this + "]";
242             logger.error(message);
243             throw new IllegalStateException(message);
244         }
245         if (fromId == null) {
246             String message = "The from must not be null. Mail[" + this + "]";
247             logger.error(message);
248             throw new IllegalStateException(message);
249         }
250         this.code = generateCode();
251         this.sentDate = mailDao.getCurrentDate();
252         mailDao.register(this, parentId);
253     }
254 
255     /**
256      * このメールが ${userId} 宛の場合、このメールを既読にします。
257      *
258      * @param userId 閲覧ユーザの ID
259      * @ensure このメールが ${userId} 宛の場合、このメールの既読/未読状態がデータベースと同期する
260      */
261     public void read(int userId) {
262         if (userId == toId.intValue()) {
263             read = true;
264             mailDao.updateRead(id, true);
265         }
266     }
267 
268     /**
269      * このメールスレッド中の全ての ${userId} 宛のメールを既読にします。
270      *
271      * @param userId 閲覧ユーザの ID
272      * @ensure このメールスレッド中の全ての ${userId} 宛のメールの既読/未読状態がデータベースと同
273      * 期する
274      */
275     public void readAll(int userId) {
276         read(userId);
277         if (next != null) {
278             next.readAll(userId);
279         }
280     }
281 
282     /**
283      * このメールが ${userId} 宛の場合、このメールを未読にします。
284      *
285      * @param userId 閲覧ユーザの ID
286      * @ensure このメールが ${userId} 宛の場合、このメールの既読/未読状態がデータベースと同期する
287      */
288     public void unread(int userId) {
289         if (userId == toId.intValue()) {
290             read = false;
291             mailDao.updateRead(id, false);
292         }
293     }
294 
295     /**
296      * このメールスレッド中の全ての ${userId} 宛のメールを未読にします。
297      *
298      * @param userId 閲覧ユーザの ID
299      * @ensure このメールスレッド中の全ての ${userId} 宛のメールの既読/未読状態がデータベースと同
300      * 期する
301      */
302     public void unreadAll(int userId) {
303         unread(userId);
304         if (next != null) {
305             next.unreadAll(userId);
306         }
307     }
308 
309     /**
310      * メールスレッド中に未読メールがあるかどうか判定します。
311      *
312      * @param toId 宛先ユーザ ID
313      * @return 未読のメールがあれば <code>true</code> 、なければ <code>false</code>
314      */
315     public boolean hasUnread(int toId) {
316         if (toId == this.toId && !isRead()) {
317             return true;
318         } else {
319             return next != null && next.hasUnread(toId);
320         }
321     }
322 
323     /**
324      * スレッド内のメール数を取得します。
325      *
326      * @return スレッド内のメール数
327      */
328     public int getCount() {
329         int count = 1;
330         if (next != null) {
331             count += next.getCount();
332         }
333         return count;
334     }
335 
336     /**
337      * メールスレッドを親から順のリストとして返します。
338      *
339      * @return メールリスト
340      */
341     public List<Mail> asList() {
342         List<Mail> list = new ArrayList<Mail>(getCount());
343         addToList(list, this);
344         return list;
345     }
346 
347     private void addToList(List<Mail> list, Mail mail) {
348         list.add(mail);
349         if (mail.next != null) {
350             addToList(list, mail.next);
351         }
352     }
353 
354     /**
355      * メールスレッドを子から順のリストとして返します。
356      *
357      * @return メールリスト
358      */
359     public List<Mail> asListDesc() {
360         List<Mail> list = asList();
361         Collections.reverse(list);
362         return list;
363     }
364 
365     /**
366      * 末端に位置するサブメールを返します。サブメールが設定されていない場合は自身が返ります。
367      *
368      * @return 末端のメール
369      */
370     public Mail getTail() {
371         Mail mail = this;
372         while (mail.getNext() != null) {
373             mail = mail.getNext();
374         }
375         return mail;
376     }
377 
378     /**
379      * <code>user</code> にとっての通信相手を返します。<code>user</code> はこのメールの宛先
380      * または差出人に含まれている必要があります。
381      *
382      * @param user ユーザ
383      * @require ${this.to} not null.
384      * @require ${this.from} not null.
385      * @return 通信相手
386      */
387     public SimpleUser other(User user) {
388         if (user.identify(to)) {
389             return from;
390         } else if (user.identify(from)) {
391             return to;
392         } else {
393             throw new IllegalArgumentException("The user '" + user
394                 + "' is neither TO or FROM.");
395         }
396     }
397 
398     /**
399      * ${this.toId} が設定されていれば、${this.to} に値を設定します。
400      */
401     public void retrieveTo() {
402         if (this.toId != null) {
403             this.to = simpleUserTable.find(this.toId);
404         }
405     }
406 
407     /**
408      * 宛先ユーザのメールアドレスを復元します。${this.to} が復元されていなければ復元します。
409      */
410     public void retrieveToMailAddr() {
411         if (to == null) {
412             retrieveTo();
413         }
414         this.toMailAddr = accountTable.find(to.getId()).getMailAddr();
415     }
416 
417     /**
418      * ${this.fromId} が設定されていれば、${this.from} に値を設定します。
419      */
420     public void retrieveFrom() {
421         if (this.fromId != null) {
422             this.from = simpleUserTable.find(this.fromId);
423         }
424     }
425 
426     public Integer getId() {
427         return id;
428     }
429 
430     public void setId(Integer id) {
431         this.id = id;
432     }
433 
434     public String getCode() {
435         return code;
436     }
437 
438     public void setCode(String code) {
439         this.code = code;
440     }
441 
442     public Integer getToId() {
443         return toId;
444     }
445 
446     public void setToId(Integer toId) {
447         this.toId = toId;
448     }
449 
450     public Integer getFromId() {
451         return fromId;
452     }
453 
454     public void setFromId(Integer fromId) {
455         this.fromId = fromId;
456     }
457 
458     public Date getSentDate() {
459         if (sentDate == null) {
460             return null;
461         } else {
462             return (Date) sentDate.clone();
463         }
464     }
465 
466     public void setSentDate(Date sentDate) {
467         if (sentDate == null) {
468             this.sentDate = null;
469         } else {
470             this.sentDate = (Date) sentDate.clone();
471         }
472     }
473 
474     public String getSubject() {
475         return subject;
476     }
477 
478     public void setSubject(String subject) {
479         this.subject = subject;
480     }
481 
482     public String getBody() {
483         return body;
484     }
485 
486     public void setBody(String body) {
487         this.body = body;
488     }
489 
490     public boolean isRead() {
491         return read;
492     }
493 
494     public void setRead(boolean read) {
495         this.read = read;
496     }
497 
498     public SimpleUser getTo() {
499         return to;
500     }
501 
502     public void setTo(SimpleUser to) {
503         this.to = to;
504     }
505 
506     public SimpleUser getFrom() {
507         return from;
508     }
509 
510     public void setFrom(SimpleUser from) {
511         this.from = from;
512     }
513 
514     public String getToMailAddr() {
515         return toMailAddr;
516     }
517 
518     public void setToMailAddr(String toMailAddr) {
519         this.toMailAddr = toMailAddr;
520     }
521 
522     public Mail getNext() {
523         return next;
524     }
525 
526     public void setNext(Mail next) {
527         this.next = next;
528     }
529 
530     public void setMailDao(MailDao mailDao) {
531         this.mailDao = mailDao;
532     }
533 
534     public void setSimpleUserTable(SimpleUserTable simpleUserTable) {
535         this.simpleUserTable = simpleUserTable;
536     }
537 
538     public void setAccountTable(AccountTable accountTable) {
539         this.accountTable = accountTable;
540     }
541 }