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