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.storage;
021
022 import java.io.IOException;
023 import java.io.InputStream;
024 import java.nio.charset.Charset;
025
026 import org.apache.james.mime4j.codec.DecodeMonitor;
027 import org.apache.james.mime4j.dom.BinaryBody;
028 import org.apache.james.mime4j.dom.Disposable;
029 import org.apache.james.mime4j.dom.SingleBody;
030 import org.apache.james.mime4j.dom.TextBody;
031 import org.apache.james.mime4j.message.BodyFactory;
032 import org.apache.james.mime4j.util.CharsetUtil;
033
034 /**
035 * Factory for creating message bodies.
036 */
037 public class StorageBodyFactory implements BodyFactory {
038
039 private static final Charset FALLBACK_CHARSET = CharsetUtil.DEFAULT_CHARSET;
040
041 private final StorageProvider storageProvider;
042 private final DecodeMonitor monitor;
043
044 /**
045 * Creates a new <code>BodyFactory</code> instance that uses the default
046 * storage provider for creating message bodies from input streams.
047 */
048 public StorageBodyFactory() {
049 this(null, null);
050 }
051
052 /**
053 * Creates a new <code>BodyFactory</code> instance that uses the given
054 * storage provider for creating message bodies from input streams.
055 *
056 * @param storageProvider
057 * a storage provider or <code>null</code> to use the default
058 * one.
059 */
060 public StorageBodyFactory(
061 final StorageProvider storageProvider,
062 final DecodeMonitor monitor) {
063 this.storageProvider =
064 storageProvider != null ? storageProvider : DefaultStorageProvider.getInstance();
065 this.monitor =
066 monitor != null ? monitor : DecodeMonitor.SILENT;
067 }
068
069 /**
070 * Returns the <code>StorageProvider</code> this <code>BodyFactory</code>
071 * uses to create message bodies from input streams.
072 *
073 * @return a <code>StorageProvider</code>.
074 */
075 public StorageProvider getStorageProvider() {
076 return storageProvider;
077 }
078
079 /**
080 * Creates a {@link BinaryBody} that holds the content of the given input
081 * stream.
082 *
083 * @param is
084 * input stream to create a message body from.
085 * @return a binary body.
086 * @throws IOException
087 * if an I/O error occurs.
088 */
089 public BinaryBody binaryBody(InputStream is) throws IOException {
090 if (is == null)
091 throw new IllegalArgumentException();
092
093 Storage storage = storageProvider.store(is);
094 return new StorageBinaryBody(new MultiReferenceStorage(storage));
095 }
096
097 /**
098 * Creates a {@link BinaryBody} that holds the content of the given
099 * {@link Storage}.
100 * <p>
101 * Note that the caller must not invoke {@link Storage#delete() delete()} on
102 * the given <code>Storage</code> object after it has been passed to this
103 * method. Instead the message body created by this method takes care of
104 * deleting the storage when it gets disposed of (see
105 * {@link Disposable#dispose()}).
106 *
107 * @param storage
108 * storage to create a message body from.
109 * @return a binary body.
110 * @throws IOException
111 * if an I/O error occurs.
112 */
113 public BinaryBody binaryBody(Storage storage) throws IOException {
114 if (storage == null)
115 throw new IllegalArgumentException();
116
117 return new StorageBinaryBody(new MultiReferenceStorage(storage));
118 }
119
120 /**
121 * Creates a {@link TextBody} that holds the content of the given input
122 * stream.
123 * <p>
124 * "us-ascii" is used to decode the byte content of the
125 * <code>Storage</code> into a character stream when calling
126 * {@link TextBody#getReader() getReader()} on the returned object.
127 *
128 * @param is
129 * input stream to create a message body from.
130 * @return a text body.
131 * @throws IOException
132 * if an I/O error occurs.
133 */
134 public TextBody textBody(InputStream is) throws IOException {
135 if (is == null)
136 throw new IllegalArgumentException();
137
138 Storage storage = storageProvider.store(is);
139 return new StorageTextBody(new MultiReferenceStorage(storage),
140 CharsetUtil.DEFAULT_CHARSET);
141 }
142
143 /**
144 * Creates a {@link TextBody} that holds the content of the given input
145 * stream.
146 * <p>
147 * The charset corresponding to the given MIME charset name is used to
148 * decode the byte content of the input stream into a character stream when
149 * calling {@link TextBody#getReader() getReader()} on the returned object.
150 * If the MIME charset has no corresponding Java charset or the Java charset
151 * cannot be used for decoding then "us-ascii" is used instead.
152 *
153 * @param is
154 * input stream to create a message body from.
155 * @param mimeCharset
156 * name of a MIME charset.
157 * @return a text body.
158 * @throws IOException
159 * if an I/O error occurs.
160 */
161 public TextBody textBody(InputStream is, String mimeCharset)
162 throws IOException {
163 if (is == null)
164 throw new IllegalArgumentException();
165 if (mimeCharset == null)
166 throw new IllegalArgumentException();
167
168 Storage storage = storageProvider.store(is);
169 Charset charset = toJavaCharset(mimeCharset, false, monitor);
170 return new StorageTextBody(new MultiReferenceStorage(storage), charset);
171 }
172
173 /**
174 * Creates a {@link TextBody} that holds the content of the given
175 * {@link Storage}.
176 * <p>
177 * "us-ascii" is used to decode the byte content of the
178 * <code>Storage</code> into a character stream when calling
179 * {@link TextBody#getReader() getReader()} on the returned object.
180 * <p>
181 * Note that the caller must not invoke {@link Storage#delete() delete()} on
182 * the given <code>Storage</code> object after it has been passed to this
183 * method. Instead the message body created by this method takes care of
184 * deleting the storage when it gets disposed of (see
185 * {@link Disposable#dispose()}).
186 *
187 * @param storage
188 * storage to create a message body from.
189 * @return a text body.
190 * @throws IOException
191 * if an I/O error occurs.
192 */
193 public TextBody textBody(Storage storage) throws IOException {
194 if (storage == null)
195 throw new IllegalArgumentException();
196
197 return new StorageTextBody(new MultiReferenceStorage(storage),
198 CharsetUtil.DEFAULT_CHARSET);
199 }
200
201 /**
202 * Creates a {@link TextBody} that holds the content of the given
203 * {@link Storage}.
204 * <p>
205 * The charset corresponding to the given MIME charset name is used to
206 * decode the byte content of the <code>Storage</code> into a character
207 * stream when calling {@link TextBody#getReader() getReader()} on the
208 * returned object. If the MIME charset has no corresponding Java charset or
209 * the Java charset cannot be used for decoding then "us-ascii" is
210 * used instead.
211 * <p>
212 * Note that the caller must not invoke {@link Storage#delete() delete()} on
213 * the given <code>Storage</code> object after it has been passed to this
214 * method. Instead the message body created by this method takes care of
215 * deleting the storage when it gets disposed of (see
216 * {@link Disposable#dispose()}).
217 *
218 * @param storage
219 * storage to create a message body from.
220 * @param mimeCharset
221 * name of a MIME charset.
222 * @return a text body.
223 * @throws IOException
224 * if an I/O error occurs.
225 */
226 public TextBody textBody(Storage storage, String mimeCharset)
227 throws IOException {
228 if (storage == null)
229 throw new IllegalArgumentException();
230 if (mimeCharset == null)
231 throw new IllegalArgumentException();
232
233 Charset charset = toJavaCharset(mimeCharset, false, monitor);
234 return new StorageTextBody(new MultiReferenceStorage(storage), charset);
235 }
236
237 /**
238 * Creates a {@link TextBody} that holds the content of the given string.
239 * <p>
240 * "us-ascii" is used to encode the characters of the string into
241 * a byte stream when calling
242 * {@link SingleBody#writeTo(java.io.OutputStream) writeTo(OutputStream)} on
243 * the returned object.
244 *
245 * @param text
246 * text to create a message body from.
247 * @return a text body.
248 */
249 public TextBody textBody(String text) {
250 if (text == null)
251 throw new IllegalArgumentException();
252
253 return new StringTextBody(text, CharsetUtil.DEFAULT_CHARSET);
254 }
255
256 /**
257 * Creates a {@link TextBody} that holds the content of the given string.
258 * <p>
259 * The charset corresponding to the given MIME charset name is used to
260 * encode the characters of the string into a byte stream when calling
261 * {@link SingleBody#writeTo(java.io.OutputStream) writeTo(OutputStream)} on
262 * the returned object. If the MIME charset has no corresponding Java
263 * charset or the Java charset cannot be used for encoding then
264 * "us-ascii" is used instead.
265 *
266 * @param text
267 * text to create a message body from.
268 * @param mimeCharset
269 * name of a MIME charset.
270 * @return a text body.
271 */
272 public TextBody textBody(String text, String mimeCharset) {
273 if (text == null)
274 throw new IllegalArgumentException();
275 if (mimeCharset == null)
276 throw new IllegalArgumentException();
277
278 Charset charset = toJavaCharset(mimeCharset, true, monitor);
279 return new StringTextBody(text, charset);
280 }
281
282 private static Charset toJavaCharset(
283 final String mimeCharset,
284 boolean forEncoding,
285 final DecodeMonitor monitor) {
286 Charset charset = CharsetUtil.lookup(mimeCharset);
287 if (charset == null) {
288 if (monitor.isListening()) {
289 monitor.warn(
290 "MIME charset '" + mimeCharset + "' has no "
291 + "corresponding Java charset", "Using "
292 + FALLBACK_CHARSET + " instead.");
293 }
294 return FALLBACK_CHARSET;
295 }
296 return charset;
297 }
298
299 }