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.io.IOException; 023 import java.io.InputStream; 024 import java.nio.charset.Charset; 025 026 import org.apache.commons.logging.Log; 027 import org.apache.commons.logging.LogFactory; 028 import org.apache.james.mime4j.storage.DefaultStorageProvider; 029 import org.apache.james.mime4j.storage.MultiReferenceStorage; 030 import org.apache.james.mime4j.storage.Storage; 031 import org.apache.james.mime4j.storage.StorageProvider; 032 import org.apache.james.mime4j.util.CharsetUtil; 033 034 /** 035 * Factory for creating message bodies. 036 */ 037 public class BodyFactory { 038 039 private static Log log = LogFactory.getLog(BodyFactory.class); 040 041 private static final Charset FALLBACK_CHARSET = CharsetUtil.DEFAULT_CHARSET; 042 043 private StorageProvider storageProvider; 044 045 /** 046 * Creates a new <code>BodyFactory</code> instance that uses the default 047 * storage provider for creating message bodies from input streams. 048 */ 049 public BodyFactory() { 050 this.storageProvider = DefaultStorageProvider.getInstance(); 051 } 052 053 /** 054 * Creates a new <code>BodyFactory</code> instance that uses the given 055 * storage provider for creating message bodies from input streams. 056 * 057 * @param storageProvider 058 * a storage provider or <code>null</code> to use the default 059 * one. 060 */ 061 public BodyFactory(StorageProvider storageProvider) { 062 if (storageProvider == null) 063 storageProvider = DefaultStorageProvider.getInstance(); 064 065 this.storageProvider = storageProvider; 066 } 067 068 /** 069 * Returns the <code>StorageProvider</code> this <code>BodyFactory</code> 070 * uses to create message bodies from input streams. 071 * 072 * @return a <code>StorageProvider</code>. 073 */ 074 public StorageProvider getStorageProvider() { 075 return storageProvider; 076 } 077 078 /** 079 * Creates a {@link BinaryBody} that holds the content of the given input 080 * stream. 081 * 082 * @param is 083 * input stream to create a message body from. 084 * @return a binary body. 085 * @throws IOException 086 * if an I/O error occurs. 087 */ 088 public BinaryBody binaryBody(InputStream is) throws IOException { 089 if (is == null) 090 throw new IllegalArgumentException(); 091 092 Storage storage = storageProvider.store(is); 093 return new StorageBinaryBody(new MultiReferenceStorage(storage)); 094 } 095 096 /** 097 * Creates a {@link BinaryBody} that holds the content of the given 098 * {@link Storage}. 099 * <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 }