View Javadoc

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  
20  package org.apache.james.mime4j.message;
21  
22  import java.util.Collections;
23  import java.util.LinkedList;
24  import java.util.List;
25  
26  import org.apache.james.mime4j.util.ByteSequence;
27  import org.apache.james.mime4j.util.ContentUtil;
28  
29  /**
30   * Represents a MIME multipart body (see RFC 2045).A multipart body has a
31   * ordered list of body parts. The multipart body also has a preamble and
32   * epilogue. The preamble consists of whatever characters appear before the
33   * first body part while the epilogue consists of whatever characters come after
34   * the last body part.
35   */
36  public class Multipart implements Body {
37  
38      private List<BodyPart> bodyParts = new LinkedList<BodyPart>();
39      private Entity parent = null;
40  
41      private ByteSequence preamble;
42      private transient String preambleStrCache;
43      private ByteSequence epilogue;
44      private transient String epilogueStrCache;
45  
46      private String subType;
47  
48      /**
49       * Creates a new empty <code>Multipart</code> instance.
50       */
51      public Multipart(String subType) {
52          preamble = ByteSequence.EMPTY;
53          preambleStrCache = "";
54          epilogue = ByteSequence.EMPTY;
55          epilogueStrCache = "";
56  
57          this.subType = subType;
58      }
59  
60      /**
61       * Creates a new <code>Multipart</code> from the specified
62       * <code>Multipart</code>. The <code>Multipart</code> instance is
63       * initialized with copies of preamble, epilogue, sub type and the list of
64       * body parts of the specified <code>Multipart</code>. The parent entity
65       * of the new multipart is <code>null</code>.
66       * 
67       * @param other
68       *            multipart to copy.
69       * @throws UnsupportedOperationException
70       *             if <code>other</code> contains a {@link SingleBody} that
71       *             does not support the {@link SingleBody#copy() copy()}
72       *             operation.
73       * @throws IllegalArgumentException
74       *             if <code>other</code> contains a <code>Body</code> that
75       *             is neither a {@link Message}, {@link Multipart} or
76       *             {@link SingleBody}.
77       */
78      public Multipart(Multipart other) {
79          preamble = other.preamble;
80          preambleStrCache = other.preambleStrCache;
81          epilogue = other.epilogue;
82          epilogueStrCache = other.epilogueStrCache;
83  
84          for (BodyPart otherBodyPart : other.bodyParts) {
85              BodyPart bodyPartCopy = new BodyPart(otherBodyPart);
86              addBodyPart(bodyPartCopy);
87          }
88  
89          subType = other.subType;
90      }
91  
92      /**
93       * Gets the multipart sub-type. E.g. <code>alternative</code> (the
94       * default) or <code>parallel</code>. See RFC 2045 for common sub-types
95       * and their meaning.
96       * 
97       * @return the multipart sub-type.
98       */
99      public String getSubType() {
100         return subType;
101     }
102 
103     /**
104      * Sets the multipart sub-type. E.g. <code>alternative</code> or
105      * <code>parallel</code>. See RFC 2045 for common sub-types and their
106      * meaning.
107      * 
108      * @param subType
109      *            the sub-type.
110      */
111     public void setSubType(String subType) {
112         this.subType = subType;
113     }
114 
115     /**
116      * @see org.apache.james.mime4j.message.Body#getParent()
117      */
118     public Entity getParent() {
119         return parent;
120     }
121 
122     /**
123      * @see org.apache.james.mime4j.message.Body#setParent(org.apache.james.mime4j.message.Entity)
124      */
125     public void setParent(Entity parent) {
126         this.parent = parent;
127         for (BodyPart bodyPart : bodyParts) {
128             bodyPart.setParent(parent);
129         }
130     }
131 
132     /**
133      * Returns the number of body parts.
134      * 
135      * @return number of <code>BodyPart</code> objects.
136      */
137     public int getCount() {
138         return bodyParts.size();
139     }
140 
141     /**
142      * Gets the list of body parts. The list is immutable.
143      * 
144      * @return the list of <code>BodyPart</code> objects.
145      */
146     public List<BodyPart> getBodyParts() {
147         return Collections.unmodifiableList(bodyParts);
148     }
149 
150     /**
151      * Sets the list of body parts.
152      * 
153      * @param bodyParts
154      *            the new list of <code>BodyPart</code> objects.
155      */
156     public void setBodyParts(List<BodyPart> bodyParts) {
157         this.bodyParts = bodyParts;
158         for (BodyPart bodyPart : bodyParts) {
159             bodyPart.setParent(parent);
160         }
161     }
162 
163     /**
164      * Adds a body part to the end of the list of body parts.
165      * 
166      * @param bodyPart
167      *            the body part.
168      */
169     public void addBodyPart(BodyPart bodyPart) {
170         if (bodyPart == null)
171             throw new IllegalArgumentException();
172 
173         bodyParts.add(bodyPart);
174         bodyPart.setParent(parent);
175     }
176 
177     /**
178      * Inserts a body part at the specified position in the list of body parts.
179      * 
180      * @param bodyPart
181      *            the body part.
182      * @param index
183      *            index at which the specified body part is to be inserted.
184      * @throws IndexOutOfBoundsException
185      *             if the index is out of range (index &lt; 0 || index &gt;
186      *             getCount()).
187      */
188     public void addBodyPart(BodyPart bodyPart, int index) {
189         if (bodyPart == null)
190             throw new IllegalArgumentException();
191 
192         bodyParts.add(index, bodyPart);
193         bodyPart.setParent(parent);
194     }
195 
196     /**
197      * Removes the body part at the specified position in the list of body
198      * parts.
199      * 
200      * @param index
201      *            index of the body part to be removed.
202      * @return the removed body part.
203      * @throws IndexOutOfBoundsException
204      *             if the index is out of range (index &lt; 0 || index &gt;=
205      *             getCount()).
206      */
207     public BodyPart removeBodyPart(int index) {
208         BodyPart bodyPart = bodyParts.remove(index);
209         bodyPart.setParent(null);
210         return bodyPart;
211     }
212 
213     /**
214      * Replaces the body part at the specified position in the list of body
215      * parts with the specified body part.
216      * 
217      * @param bodyPart
218      *            body part to be stored at the specified position.
219      * @param index
220      *            index of body part to replace.
221      * @return the replaced body part.
222      * @throws IndexOutOfBoundsException
223      *             if the index is out of range (index &lt; 0 || index &gt;=
224      *             getCount()).
225      */
226     public BodyPart replaceBodyPart(BodyPart bodyPart, int index) {
227         if (bodyPart == null)
228             throw new IllegalArgumentException();
229 
230         BodyPart replacedBodyPart = bodyParts.set(index, bodyPart);
231         if (bodyPart == replacedBodyPart)
232             throw new IllegalArgumentException(
233                     "Cannot replace body part with itself");
234 
235         bodyPart.setParent(parent);
236         replacedBodyPart.setParent(null);
237 
238         return replacedBodyPart;
239     }
240 
241     // package private for now; might become public someday
242     ByteSequence getPreambleRaw() {
243         return preamble;
244     }
245 
246     void setPreambleRaw(ByteSequence preamble) {
247         this.preamble = preamble;
248         this.preambleStrCache = null;
249     }
250 
251     /**
252      * Gets the preamble.
253      * 
254      * @return the preamble.
255      */
256     public String getPreamble() {
257         if (preambleStrCache == null) {
258             preambleStrCache = ContentUtil.decode(preamble);
259         }
260         return preambleStrCache;
261     }
262 
263     /**
264      * Sets the preamble.
265      * 
266      * @param preamble
267      *            the preamble.
268      */
269     public void setPreamble(String preamble) {
270         this.preamble = ContentUtil.encode(preamble);
271         this.preambleStrCache = preamble;
272     }
273 
274     // package private for now; might become public someday
275     ByteSequence getEpilogueRaw() {
276         return epilogue;
277     }
278 
279     void setEpilogueRaw(ByteSequence epilogue) {
280         this.epilogue = epilogue;
281         this.epilogueStrCache = null;
282     }
283 
284     /**
285      * Gets the epilogue.
286      * 
287      * @return the epilogue.
288      */
289     public String getEpilogue() {
290         if (epilogueStrCache == null) {
291             epilogueStrCache = ContentUtil.decode(epilogue);
292         }
293         return epilogueStrCache;
294     }
295 
296     /**
297      * Sets the epilogue.
298      * 
299      * @param epilogue
300      *            the epilogue.
301      */
302     public void setEpilogue(String epilogue) {
303         this.epilogue = ContentUtil.encode(epilogue);
304         this.epilogueStrCache = epilogue;
305     }
306 
307     /**
308      * Disposes of the BodyParts of this Multipart. Note that the dispose call
309      * does not get forwarded to the parent entity of this Multipart.
310      * 
311      * @see org.apache.james.mime4j.message.Disposable#dispose()
312      */
313     public void dispose() {
314         for (BodyPart bodyPart : bodyParts) {
315             bodyPart.dispose();
316         }
317     }
318 
319 }