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.io.IOException;
23  import java.io.InputStream;
24  import java.nio.charset.Charset;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.james.mime4j.storage.DefaultStorageProvider;
29  import org.apache.james.mime4j.storage.MultiReferenceStorage;
30  import org.apache.james.mime4j.storage.Storage;
31  import org.apache.james.mime4j.storage.StorageProvider;
32  import org.apache.james.mime4j.util.CharsetUtil;
33  
34  /**
35   * Factory for creating message bodies.
36   */
37  public class BodyFactory {
38  
39      private static Log log = LogFactory.getLog(BodyFactory.class);
40  
41      private static final Charset FALLBACK_CHARSET = CharsetUtil.DEFAULT_CHARSET;
42  
43      private StorageProvider storageProvider;
44  
45      /**
46       * Creates a new <code>BodyFactory</code> instance that uses the default
47       * storage provider for creating message bodies from input streams.
48       */
49      public BodyFactory() {
50          this.storageProvider = DefaultStorageProvider.getInstance();
51      }
52  
53      /**
54       * Creates a new <code>BodyFactory</code> instance that uses the given
55       * storage provider for creating message bodies from input streams.
56       * 
57       * @param storageProvider
58       *            a storage provider or <code>null</code> to use the default
59       *            one.
60       */
61      public BodyFactory(StorageProvider storageProvider) {
62          if (storageProvider == null)
63              storageProvider = DefaultStorageProvider.getInstance();
64  
65          this.storageProvider = storageProvider;
66      }
67  
68      /**
69       * Returns the <code>StorageProvider</code> this <code>BodyFactory</code>
70       * uses to create message bodies from input streams.
71       * 
72       * @return a <code>StorageProvider</code>.
73       */
74      public StorageProvider getStorageProvider() {
75          return storageProvider;
76      }
77  
78      /**
79       * Creates a {@link BinaryBody} that holds the content of the given input
80       * stream.
81       * 
82       * @param is
83       *            input stream to create a message body from.
84       * @return a binary body.
85       * @throws IOException
86       *             if an I/O error occurs.
87       */
88      public BinaryBody binaryBody(InputStream is) throws IOException {
89          if (is == null)
90              throw new IllegalArgumentException();
91  
92          Storage storage = storageProvider.store(is);
93          return new StorageBinaryBody(new MultiReferenceStorage(storage));
94      }
95  
96      /**
97       * Creates a {@link BinaryBody} that holds the content of the given
98       * {@link Storage}.
99       * <p>
100      * Note that the caller must not invoke {@link Storage#delete() delete()} on
101      * the given <code>Storage</code> object after it has been passed to this
102      * method. Instead the message body created by this method takes care of
103      * deleting the storage when it gets disposed of (see
104      * {@link Disposable#dispose()}).
105      * 
106      * @param storage
107      *            storage to create a message body from.
108      * @return a binary body.
109      * @throws IOException
110      *             if an I/O error occurs.
111      */
112     public BinaryBody binaryBody(Storage storage) throws IOException {
113         if (storage == null)
114             throw new IllegalArgumentException();
115 
116         return new StorageBinaryBody(new MultiReferenceStorage(storage));
117     }
118 
119     /**
120      * Creates a {@link TextBody} that holds the content of the given input
121      * stream.
122      * <p>
123      * &quot;us-ascii&quot; is used to decode the byte content of the
124      * <code>Storage</code> into a character stream when calling
125      * {@link TextBody#getReader() getReader()} on the returned object.
126      * 
127      * @param is
128      *            input stream to create a message body from.
129      * @return a text body.
130      * @throws IOException
131      *             if an I/O error occurs.
132      */
133     public TextBody textBody(InputStream is) throws IOException {
134         if (is == null)
135             throw new IllegalArgumentException();
136 
137         Storage storage = storageProvider.store(is);
138         return new StorageTextBody(new MultiReferenceStorage(storage),
139                 CharsetUtil.DEFAULT_CHARSET);
140     }
141 
142     /**
143      * Creates a {@link TextBody} that holds the content of the given input
144      * stream.
145      * <p>
146      * The charset corresponding to the given MIME charset name is used to
147      * decode the byte content of the input stream into a character stream when
148      * calling {@link TextBody#getReader() getReader()} on the returned object.
149      * If the MIME charset has no corresponding Java charset or the Java charset
150      * cannot be used for decoding then &quot;us-ascii&quot; is used instead.
151      * 
152      * @param is
153      *            input stream to create a message body from.
154      * @param mimeCharset
155      *            name of a MIME charset.
156      * @return a text body.
157      * @throws IOException
158      *             if an I/O error occurs.
159      */
160     public TextBody textBody(InputStream is, String mimeCharset)
161             throws IOException {
162         if (is == null)
163             throw new IllegalArgumentException();
164         if (mimeCharset == null)
165             throw new IllegalArgumentException();
166 
167         Storage storage = storageProvider.store(is);
168         Charset charset = toJavaCharset(mimeCharset, false);
169         return new StorageTextBody(new MultiReferenceStorage(storage), charset);
170     }
171 
172     /**
173      * Creates a {@link TextBody} that holds the content of the given
174      * {@link Storage}.
175      * <p>
176      * &quot;us-ascii&quot; is used to decode the byte content of the
177      * <code>Storage</code> into a character stream when calling
178      * {@link TextBody#getReader() getReader()} on the returned object.
179      * <p>
180      * Note that the caller must not invoke {@link Storage#delete() delete()} on
181      * the given <code>Storage</code> object after it has been passed to this
182      * method. Instead the message body created by this method takes care of
183      * deleting the storage when it gets disposed of (see
184      * {@link Disposable#dispose()}).
185      * 
186      * @param storage
187      *            storage to create a message body from.
188      * @return a text body.
189      * @throws IOException
190      *             if an I/O error occurs.
191      */
192     public TextBody textBody(Storage storage) throws IOException {
193         if (storage == null)
194             throw new IllegalArgumentException();
195 
196         return new StorageTextBody(new MultiReferenceStorage(storage),
197                 CharsetUtil.DEFAULT_CHARSET);
198     }
199 
200     /**
201      * Creates a {@link TextBody} that holds the content of the given
202      * {@link Storage}.
203      * <p>
204      * The charset corresponding to the given MIME charset name is used to
205      * decode the byte content of the <code>Storage</code> into a character
206      * stream when calling {@link TextBody#getReader() getReader()} on the
207      * returned object. If the MIME charset has no corresponding Java charset or
208      * the Java charset cannot be used for decoding then &quot;us-ascii&quot; is
209      * used instead.
210      * <p>
211      * Note that the caller must not invoke {@link Storage#delete() delete()} on
212      * the given <code>Storage</code> object after it has been passed to this
213      * method. Instead the message body created by this method takes care of
214      * deleting the storage when it gets disposed of (see
215      * {@link Disposable#dispose()}).
216      * 
217      * @param storage
218      *            storage to create a message body from.
219      * @param mimeCharset
220      *            name of a MIME charset.
221      * @return a text body.
222      * @throws IOException
223      *             if an I/O error occurs.
224      */
225     public TextBody textBody(Storage storage, String mimeCharset)
226             throws IOException {
227         if (storage == null)
228             throw new IllegalArgumentException();
229         if (mimeCharset == null)
230             throw new IllegalArgumentException();
231 
232         Charset charset = toJavaCharset(mimeCharset, false);
233         return new StorageTextBody(new MultiReferenceStorage(storage), charset);
234     }
235 
236     /**
237      * Creates a {@link TextBody} that holds the content of the given string.
238      * <p>
239      * &quot;us-ascii&quot; is used to encode the characters of the string into
240      * a byte stream when calling
241      * {@link SingleBody#writeTo(java.io.OutputStream) writeTo(OutputStream)} on
242      * the returned object.
243      * 
244      * @param text
245      *            text to create a message body from.
246      * @return a text body.
247      */
248     public TextBody textBody(String text) {
249         if (text == null)
250             throw new IllegalArgumentException();
251 
252         return new StringTextBody(text, CharsetUtil.DEFAULT_CHARSET);
253     }
254 
255     /**
256      * Creates a {@link TextBody} that holds the content of the given string.
257      * <p>
258      * The charset corresponding to the given MIME charset name is used to
259      * encode the characters of the string into a byte stream when calling
260      * {@link SingleBody#writeTo(java.io.OutputStream) writeTo(OutputStream)} on
261      * the returned object. If the MIME charset has no corresponding Java
262      * charset or the Java charset cannot be used for encoding then
263      * &quot;us-ascii&quot; is used instead.
264      * 
265      * @param text
266      *            text to create a message body from.
267      * @param mimeCharset
268      *            name of a MIME charset.
269      * @return a text body.
270      */
271     public TextBody textBody(String text, String mimeCharset) {
272         if (text == null)
273             throw new IllegalArgumentException();
274         if (mimeCharset == null)
275             throw new IllegalArgumentException();
276 
277         Charset charset = toJavaCharset(mimeCharset, true);
278         return new StringTextBody(text, charset);
279     }
280 
281     private static Charset toJavaCharset(String mimeCharset, boolean forEncoding) {
282         String charset = CharsetUtil.toJavaCharset(mimeCharset);
283         if (charset == null) {
284             if (log.isWarnEnabled())
285                 log.warn("MIME charset '" + mimeCharset + "' has no "
286                         + "corresponding Java charset. Using "
287                         + FALLBACK_CHARSET + " instead.");
288             return FALLBACK_CHARSET;
289         }
290 
291         if (forEncoding && !CharsetUtil.isEncodingSupported(charset)) {
292             if (log.isWarnEnabled())
293                 log.warn("MIME charset '" + mimeCharset
294                         + "' does not support encoding. Using "
295                         + FALLBACK_CHARSET + " instead.");
296             return FALLBACK_CHARSET;
297         }
298 
299         if (!forEncoding && !CharsetUtil.isDecodingSupported(charset)) {
300             if (log.isWarnEnabled())
301                 log.warn("MIME charset '" + mimeCharset
302                         + "' does not support decoding. Using "
303                         + FALLBACK_CHARSET + " instead.");
304             return FALLBACK_CHARSET;
305         }
306 
307         return Charset.forName(charset);
308     }
309 
310 }