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 * "us-ascii" 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 "us-ascii" 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 * "us-ascii" 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 "us-ascii" 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 * "us-ascii" 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 * "us-ascii" 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 }