001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.mime4j.message;
021
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Date;
026import java.util.TimeZone;
027
028import org.apache.james.mime4j.dom.Header;
029import org.apache.james.mime4j.dom.Message;
030import org.apache.james.mime4j.dom.address.Address;
031import org.apache.james.mime4j.dom.address.AddressList;
032import org.apache.james.mime4j.dom.address.Mailbox;
033import org.apache.james.mime4j.dom.address.MailboxList;
034import org.apache.james.mime4j.dom.field.AddressListField;
035import org.apache.james.mime4j.dom.field.DateTimeField;
036import org.apache.james.mime4j.dom.field.FieldName;
037import org.apache.james.mime4j.dom.field.MailboxField;
038import org.apache.james.mime4j.dom.field.MailboxListField;
039import org.apache.james.mime4j.dom.field.ParsedField;
040import org.apache.james.mime4j.dom.field.UnstructuredField;
041import org.apache.james.mime4j.stream.Field;
042
043/**
044 * Abstract MIME message.
045 */
046public abstract class AbstractMessage extends AbstractEntity implements Message {
047
048    /**
049     * Returns the value of the <i>Message-ID</i> header field of this message
050     * or <code>null</code> if it is not present.
051     *
052     * @return the identifier of this message.
053     */
054    public String getMessageId() {
055        Field field = obtainField(FieldName.MESSAGE_ID);
056        if (field == null)
057            return null;
058
059        return field.getBody();
060    }
061
062    /**
063     * Creates and sets a new <i>Message-ID</i> header field for this message.
064     * A <code>Header</code> is created if this message does not already have
065     * one.
066     *
067     * @param hostname
068     *            host name to be included in the identifier or
069     *            <code>null</code> if no host name should be included.
070     */
071    public void createMessageId(String hostname) {
072        Header header = obtainHeader();
073
074        header.setField(newMessageId(hostname));
075    }
076
077    protected abstract ParsedField newMessageId(String hostname);
078
079    /**
080     * Returns the (decoded) value of the <i>Subject</i> header field of this
081     * message or <code>null</code> if it is not present.
082     *
083     * @return the subject of this message.
084     */
085    public String getSubject() {
086        UnstructuredField field = obtainField(FieldName.SUBJECT);
087        if (field == null)
088            return null;
089
090        return field.getValue();
091    }
092
093    /**
094     * Sets the <i>Subject</i> header field for this message. The specified
095     * string may contain non-ASCII characters, in which case it gets encoded as
096     * an 'encoded-word' automatically. A <code>Header</code> is created if
097     * this message does not already have one.
098     *
099     * @param subject
100     *            subject to set or <code>null</code> to remove the subject
101     *            header field.
102     */
103    public void setSubject(String subject) {
104        Header header = obtainHeader();
105
106        if (subject == null) {
107            header.removeFields(FieldName.SUBJECT);
108        } else {
109            header.setField(newSubject(subject));
110        }
111    }
112
113    /**
114     * Returns the value of the <i>Date</i> header field of this message as
115     * <code>Date</code> object or <code>null</code> if it is not present.
116     *
117     * @return the date of this message.
118     */
119    public Date getDate() {
120        DateTimeField dateField = obtainField(FieldName.DATE);
121        if (dateField == null)
122            return null;
123
124        return dateField.getDate();
125    }
126
127    /**
128     * Sets the <i>Date</i> header field for this message. This method uses the
129     * default <code>TimeZone</code> of this host to encode the specified
130     * <code>Date</code> object into a string.
131     *
132     * @param date
133     *            date to set or <code>null</code> to remove the date header
134     *            field.
135     */
136    public void setDate(Date date) {
137        setDate(date, null);
138    }
139
140    /**
141     * Sets the <i>Date</i> header field for this message. The specified
142     * <code>TimeZone</code> is used to encode the specified <code>Date</code>
143     * object into a string.
144     *
145     * @param date
146     *            date to set or <code>null</code> to remove the date header
147     *            field.
148     * @param zone
149     *            a time zone.
150     */
151    public void setDate(Date date, TimeZone zone) {
152        Header header = obtainHeader();
153
154        if (date == null) {
155            header.removeFields(FieldName.DATE);
156        } else {
157            header.setField(newDate(date, zone));
158        }
159    }
160
161    /**
162     * Returns the value of the <i>Sender</i> header field of this message as
163     * <code>Mailbox</code> object or <code>null</code> if it is not
164     * present.
165     *
166     * @return the sender of this message.
167     */
168    public Mailbox getSender() {
169        return getMailbox(FieldName.SENDER);
170    }
171
172    /**
173     * Sets the <i>Sender</i> header field of this message to the specified
174     * mailbox address.
175     *
176     * @param sender
177     *            address to set or <code>null</code> to remove the header
178     *            field.
179     */
180    public void setSender(Mailbox sender) {
181        setMailbox(FieldName.SENDER, sender);
182    }
183
184    /**
185     * Returns the value of the <i>From</i> header field of this message as
186     * <code>MailboxList</code> object or <code>null</code> if it is not
187     * present.
188     *
189     * @return value of the from field of this message.
190     */
191    public MailboxList getFrom() {
192        return getMailboxList(FieldName.FROM);
193    }
194
195    /**
196     * Sets the <i>From</i> header field of this message to the specified
197     * mailbox address.
198     *
199     * @param from
200     *            address to set or <code>null</code> to remove the header
201     *            field.
202     */
203    public void setFrom(Mailbox from) {
204        setMailboxList(FieldName.FROM, from);
205    }
206
207    /**
208     * Sets the <i>From</i> header field of this message to the specified
209     * mailbox addresses.
210     *
211     * @param from
212     *            addresses to set or <code>null</code> or no arguments to
213     *            remove the header field.
214     */
215    public void setFrom(Mailbox... from) {
216        setMailboxList(FieldName.FROM, from);
217    }
218
219    /**
220     * Sets the <i>From</i> header field of this message to the specified
221     * mailbox addresses.
222     *
223     * @param from
224     *            addresses to set or <code>null</code> or an empty collection
225     *            to remove the header field.
226     */
227    public void setFrom(Collection<Mailbox> from) {
228        setMailboxList(FieldName.FROM, from);
229    }
230
231    /**
232     * Returns the value of the <i>To</i> header field of this message as
233     * <code>AddressList</code> object or <code>null</code> if it is not
234     * present.
235     *
236     * @return value of the to field of this message.
237     */
238    public AddressList getTo() {
239        return getAddressList(FieldName.TO);
240    }
241
242    /**
243     * Sets the <i>To</i> header field of this message to the specified
244     * address.
245     *
246     * @param to
247     *            address to set or <code>null</code> to remove the header
248     *            field.
249     */
250    public void setTo(Address to) {
251        setAddressList(FieldName.TO, to);
252    }
253
254    /**
255     * Sets the <i>To</i> header field of this message to the specified
256     * addresses.
257     *
258     * @param to
259     *            addresses to set or <code>null</code> or no arguments to
260     *            remove the header field.
261     */
262    public void setTo(Address... to) {
263        setAddressList(FieldName.TO, to);
264    }
265
266    /**
267     * Sets the <i>To</i> header field of this message to the specified
268     * addresses.
269     *
270     * @param to
271     *            addresses to set or <code>null</code> or an empty collection
272     *            to remove the header field.
273     */
274    public void setTo(Collection<? extends Address> to) {
275        setAddressList(FieldName.TO, to);
276    }
277
278    /**
279     * Returns the value of the <i>Cc</i> header field of this message as
280     * <code>AddressList</code> object or <code>null</code> if it is not
281     * present.
282     *
283     * @return value of the cc field of this message.
284     */
285    public AddressList getCc() {
286        return getAddressList(FieldName.CC);
287    }
288
289    /**
290     * Sets the <i>Cc</i> header field of this message to the specified
291     * address.
292     *
293     * @param cc
294     *            address to set or <code>null</code> to remove the header
295     *            field.
296     */
297    public void setCc(Address cc) {
298        setAddressList(FieldName.CC, cc);
299    }
300
301    /**
302     * Sets the <i>Cc</i> header field of this message to the specified
303     * addresses.
304     *
305     * @param cc
306     *            addresses to set or <code>null</code> or no arguments to
307     *            remove the header field.
308     */
309    public void setCc(Address... cc) {
310        setAddressList(FieldName.CC, cc);
311    }
312
313    /**
314     * Sets the <i>Cc</i> header field of this message to the specified
315     * addresses.
316     *
317     * @param cc
318     *            addresses to set or <code>null</code> or an empty collection
319     *            to remove the header field.
320     */
321    public void setCc(Collection<? extends Address> cc) {
322        setAddressList(FieldName.CC, cc);
323    }
324
325    /**
326     * Returns the value of the <i>Bcc</i> header field of this message as
327     * <code>AddressList</code> object or <code>null</code> if it is not
328     * present.
329     *
330     * @return value of the bcc field of this message.
331     */
332    public AddressList getBcc() {
333        return getAddressList(FieldName.BCC);
334    }
335
336    /**
337     * Sets the <i>Bcc</i> header field of this message to the specified
338     * address.
339     *
340     * @param bcc
341     *            address to set or <code>null</code> to remove the header
342     *            field.
343     */
344    public void setBcc(Address bcc) {
345        setAddressList(FieldName.BCC, bcc);
346    }
347
348    /**
349     * Sets the <i>Bcc</i> header field of this message to the specified
350     * addresses.
351     *
352     * @param bcc
353     *            addresses to set or <code>null</code> or no arguments to
354     *            remove the header field.
355     */
356    public void setBcc(Address... bcc) {
357        setAddressList(FieldName.BCC, bcc);
358    }
359
360    /**
361     * Sets the <i>Bcc</i> header field of this message to the specified
362     * addresses.
363     *
364     * @param bcc
365     *            addresses to set or <code>null</code> or an empty collection
366     *            to remove the header field.
367     */
368    public void setBcc(Collection<? extends Address> bcc) {
369        setAddressList(FieldName.BCC, bcc);
370    }
371
372    /**
373     * Returns the value of the <i>Reply-To</i> header field of this message as
374     * <code>AddressList</code> object or <code>null</code> if it is not
375     * present.
376     *
377     * @return value of the reply to field of this message.
378     */
379    public AddressList getReplyTo() {
380        return getAddressList(FieldName.REPLY_TO);
381    }
382
383    /**
384     * Sets the <i>Reply-To</i> header field of this message to the specified
385     * address.
386     *
387     * @param replyTo
388     *            address to set or <code>null</code> to remove the header
389     *            field.
390     */
391    public void setReplyTo(Address replyTo) {
392        setAddressList(FieldName.REPLY_TO, replyTo);
393    }
394
395    /**
396     * Sets the <i>Reply-To</i> header field of this message to the specified
397     * addresses.
398     *
399     * @param replyTo
400     *            addresses to set or <code>null</code> or no arguments to
401     *            remove the header field.
402     */
403    public void setReplyTo(Address... replyTo) {
404        setAddressList(FieldName.REPLY_TO, replyTo);
405    }
406
407    /**
408     * Sets the <i>Reply-To</i> header field of this message to the specified
409     * addresses.
410     *
411     * @param replyTo
412     *            addresses to set or <code>null</code> or an empty collection
413     *            to remove the header field.
414     */
415    public void setReplyTo(Collection<? extends Address> replyTo) {
416        setAddressList(FieldName.REPLY_TO, replyTo);
417    }
418
419    private Mailbox getMailbox(String fieldName) {
420        MailboxField field = obtainField(fieldName);
421        if (field == null)
422            return null;
423
424        return field.getMailbox();
425    }
426
427    private void setMailbox(String fieldName, Mailbox mailbox) {
428        Header header = obtainHeader();
429
430        if (mailbox == null) {
431            header.removeFields(fieldName);
432        } else {
433            header.setField(newMailbox(fieldName, mailbox));
434        }
435    }
436
437    private MailboxList getMailboxList(String fieldName) {
438        MailboxListField field = obtainField(fieldName);
439        if (field == null)
440            return null;
441
442        return field.getMailboxList();
443    }
444
445    private void setMailboxList(String fieldName, Mailbox mailbox) {
446        setMailboxList(fieldName, mailbox == null ? null : Collections
447                .singleton(mailbox));
448    }
449
450    private void setMailboxList(String fieldName, Mailbox... mailboxes) {
451        setMailboxList(fieldName, mailboxes == null ? null : Arrays
452                .asList(mailboxes));
453    }
454
455    private void setMailboxList(String fieldName, Collection<Mailbox> mailboxes) {
456        Header header = obtainHeader();
457
458        if (mailboxes == null || mailboxes.isEmpty()) {
459            header.removeFields(fieldName);
460        } else {
461            header.setField(newMailboxList(fieldName, mailboxes));
462        }
463    }
464
465    private AddressList getAddressList(String fieldName) {
466        AddressListField field = obtainField(fieldName);
467        if (field == null)
468            return null;
469
470        return field.getAddressList();
471    }
472
473    private void setAddressList(String fieldName, Address address) {
474        setAddressList(fieldName, address == null ? null : Collections
475                .singleton(address));
476    }
477
478    private void setAddressList(String fieldName, Address... addresses) {
479        setAddressList(fieldName, addresses == null ? null : Arrays
480                .asList(addresses));
481    }
482
483    private void setAddressList(String fieldName, Collection<? extends Address> addresses) {
484        Header header = obtainHeader();
485
486        if (addresses == null || addresses.isEmpty()) {
487            header.removeFields(fieldName);
488        } else {
489            header.setField(newAddressList(fieldName, addresses));
490        }
491    }
492
493    protected abstract AddressListField newAddressList(String fieldName, Collection<? extends Address> addresses);
494
495    protected abstract UnstructuredField newSubject(String subject);
496
497    protected abstract DateTimeField newDate(Date date, TimeZone zone);
498
499    protected abstract MailboxField newMailbox(String fieldName, Mailbox mailbox);
500
501    protected abstract MailboxListField newMailboxList(String fieldName, Collection<Mailbox> mailboxes);
502
503
504}