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    
020    package org.apache.james.mime4j.message;
021    
022    import java.util.Collections;
023    import java.util.LinkedList;
024    import java.util.List;
025    
026    import org.apache.james.mime4j.util.ByteSequence;
027    import org.apache.james.mime4j.util.ContentUtil;
028    
029    /**
030     * Represents a MIME multipart body (see RFC 2045).A multipart body has a
031     * ordered list of body parts. The multipart body also has a preamble and
032     * epilogue. The preamble consists of whatever characters appear before the
033     * first body part while the epilogue consists of whatever characters come after
034     * the last body part.
035     */
036    public class Multipart implements Body {
037    
038        private List<BodyPart> bodyParts = new LinkedList<BodyPart>();
039        private Entity parent = null;
040    
041        private ByteSequence preamble;
042        private transient String preambleStrCache;
043        private ByteSequence epilogue;
044        private transient String epilogueStrCache;
045    
046        private String subType;
047    
048        /**
049         * Creates a new empty <code>Multipart</code> instance.
050         */
051        public Multipart(String subType) {
052            preamble = ByteSequence.EMPTY;
053            preambleStrCache = "";
054            epilogue = ByteSequence.EMPTY;
055            epilogueStrCache = "";
056    
057            this.subType = subType;
058        }
059    
060        /**
061         * Creates a new <code>Multipart</code> from the specified
062         * <code>Multipart</code>. The <code>Multipart</code> instance is
063         * initialized with copies of preamble, epilogue, sub type and the list of
064         * body parts of the specified <code>Multipart</code>. The parent entity
065         * of the new multipart is <code>null</code>.
066         * 
067         * @param other
068         *            multipart to copy.
069         * @throws UnsupportedOperationException
070         *             if <code>other</code> contains a {@link SingleBody} that
071         *             does not support the {@link SingleBody#copy() copy()}
072         *             operation.
073         * @throws IllegalArgumentException
074         *             if <code>other</code> contains a <code>Body</code> that
075         *             is neither a {@link Message}, {@link Multipart} or
076         *             {@link SingleBody}.
077         */
078        public Multipart(Multipart other) {
079            preamble = other.preamble;
080            preambleStrCache = other.preambleStrCache;
081            epilogue = other.epilogue;
082            epilogueStrCache = other.epilogueStrCache;
083    
084            for (BodyPart otherBodyPart : other.bodyParts) {
085                BodyPart bodyPartCopy = new BodyPart(otherBodyPart);
086                addBodyPart(bodyPartCopy);
087            }
088    
089            subType = other.subType;
090        }
091    
092        /**
093         * Gets the multipart sub-type. E.g. <code>alternative</code> (the
094         * default) or <code>parallel</code>. See RFC 2045 for common sub-types
095         * and their meaning.
096         * 
097         * @return the multipart sub-type.
098         */
099        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    }