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 020package org.apache.james.mime4j.storage; 021 022import java.io.IOException; 023import java.io.InputStream; 024import java.nio.charset.Charset; 025 026import org.apache.james.mime4j.codec.DecodeMonitor; 027import org.apache.james.mime4j.dom.BinaryBody; 028import org.apache.james.mime4j.dom.Disposable; 029import org.apache.james.mime4j.dom.SingleBody; 030import org.apache.james.mime4j.dom.TextBody; 031import org.apache.james.mime4j.message.BodyFactory; 032import org.apache.james.mime4j.util.CharsetUtil; 033 034/** 035 * Factory for creating message bodies. 036 */ 037public 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}