1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19
20package org.apache.james.mime4j.message;
21
22import java.io.BufferedWriter;
23import java.io.IOException;
24import java.io.OutputStream;
25import java.io.OutputStreamWriter;
26import java.util.Collections;
27import java.util.Iterator;
28import java.util.LinkedList;
29import java.util.List;
30
31import org.apache.james.mime4j.field.ContentTypeField;
32import org.apache.james.mime4j.field.Field;
33import org.apache.james.mime4j.util.CharsetUtil;
34
35/**
36 * Represents a MIME multipart body (see RFC 2045).A multipart body has a
37 * ordered list of body parts. The multipart body also has a preamble and
38 * epilogue. The preamble consists of whatever characters appear before the
39 * first body part while the epilogue consists of whatever characters come
40 * after the last body part.
41 *
42 *
43 * @version $Id: Multipart.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
44 */
45public class Multipart implements Body {
46    private String preamble = "";
47    private String epilogue = "";
48    private List<BodyPart> bodyParts = new LinkedList<BodyPart>();
49    private Entity parent = null;
50    private String subType = "alternative";
51
52    /**
53     * Creates a new empty <code>Multipart</code> instance.
54     */
55    public Multipart() {
56    }
57
58    /**
59     * Gets the multipart sub-type. E.g. <code>alternative</code> (the default)
60     * or <code>parallel</code>. See RFC 2045 for common sub-types and their
61     * meaning.
62     *
63     * @return the multipart sub-type.
64     */
65    public String getSubType() {
66        return subType;
67    }
68
69    /**
70     * Sets the multipart sub-type. E.g. <code>alternative</code>
71     * or <code>parallel</code>. See RFC 2045 for common sub-types and their
72     * meaning.
73     *
74     * @param subType the sub-type.
75     */
76    public void setSubType(String subType) {
77        this.subType = subType;
78    }
79
80    /**
81     * @see org.apache.james.mime4j.message.Body#getParent()
82     */
83    public Entity getParent() {
84        return parent;
85    }
86
87    /**
88     * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)
89     */
90    public void setParent(Entity parent) {
91        this.parent = parent;
92        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {
93            it.next().setParent(parent);
94        }
95    }
96
97    /**
98     * Gets the epilogue.
99     *
100     * @return the epilogue.
101     */
102    public String getEpilogue() {
103        return epilogue;
104    }
105
106    /**
107     * Sets the epilogue.
108     *
109     * @param epilogue the epilogue.
110     */
111    public void setEpilogue(String epilogue) {
112        this.epilogue = epilogue;
113    }
114
115    /**
116     * Gets the list of body parts. The list is immutable.
117     *
118     * @return the list of <code>BodyPart</code> objects.
119     */
120    public List<BodyPart> getBodyParts() {
121        return Collections.unmodifiableList(bodyParts);
122    }
123
124    /**
125     * Sets the list of body parts.
126     *
127     * @param bodyParts the new list of <code>BodyPart</code> objects.
128     */
129    public void setBodyParts(List<BodyPart> bodyParts) {
130        this.bodyParts = bodyParts;
131        for (Iterator<BodyPart> it = bodyParts.iterator(); it.hasNext();) {
132            it.next().setParent(parent);
133        }
134    }
135
136    /**
137     * Adds a body part to the end of the list of body parts.
138     *
139     * @param bodyPart the body part.
140     */
141    public void addBodyPart(BodyPart bodyPart) {
142        bodyParts.add(bodyPart);
143        bodyPart.setParent(parent);
144    }
145
146    /**
147     * Gets the preamble.
148     *
149     * @return the preamble.
150     */
151    public String getPreamble() {
152        return preamble;
153    }
154
155    /**
156     * Sets the preamble.
157     *
158     * @param preamble the preamble.
159     */
160    public void setPreamble(String preamble) {
161        this.preamble = preamble;
162    }
163
164    /**
165     *
166     * @see org.apache.james.mime4j.message.Body#writeTo(java.io.OutputStream)
167     */
168    public void writeTo(OutputStream out) throws IOException {
169        String boundary = getBoundary();
170        List<BodyPart> bodyParts = getBodyParts();
171
172        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, CharsetUtil.getCharset(getCharset())),8192);
173
174        writer.write(getPreamble() + "\r\n");
175
176        for (int i = 0; i < bodyParts.size(); i++) {
177            writer.write(boundary + "\r\n");
178            bodyParts.get(i).writeTo(out);
179        }
180
181        writer.write(getEpilogue() + "\r\n");
182        writer.write(boundary + "--" + "\r\n");
183
184    }
185
186    /**
187     * Return the boundory of the parent Entity
188     *
189     * @return boundery
190     */
191    private String getBoundary() {
192        Entity e = getParent();
193        ContentTypeField cField = (ContentTypeField) e.getHeader().getField(
194                Field.CONTENT_TYPE);
195        return cField.getBoundary();
196    }
197
198    private String getCharset() {
199        Entity e = getParent();
200        String charString = ((ContentTypeField) e.getHeader().getField(Field.CONTENT_TYPE)).getCharset();
201        return charString;
202    }
203}
204