blob: b5c365dcd54ff28663e4bd3ac23e051b5a865f84 [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.mime4j.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.apache.james.mime4j.Charsets;
/**
* Utility methods for converting textual content of a message.
*/
public class ContentUtil {
protected static final ThreadLocal<SoftReference<BufferRecycler>> _recyclerRef = new ThreadLocal<>();
protected static final ThreadLocal<SoftReference<ByteArrayOutputStreamRecycler>> _outputStreamRecyclerRef = new ThreadLocal<>();
public static BufferRecycler getBufferRecycler() {
SoftReference<BufferRecycler> ref = _recyclerRef.get();
BufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new BufferRecycler();
ref = new SoftReference<>(br);
_recyclerRef.set(ref);
}
return br;
}
public static ByteArrayOutputStreamRecycler getOutputStreamRecycler() {
SoftReference<ByteArrayOutputStreamRecycler> ref = _outputStreamRecyclerRef.get();
ByteArrayOutputStreamRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new ByteArrayOutputStreamRecycler();
ref = new SoftReference<>(br);
_outputStreamRecyclerRef.set(ref);
}
return br;
}
private ContentUtil() {
}
static final int DEFAULT_COPY_BUFFER_SIZE = 4096;
/**
* Copies the contents of one stream to the other.
* @param in not null
* @param out not null
* @throws IOException
*/
public static void copy(final InputStream in, final OutputStream out) throws IOException {
BufferRecycler bufferRecycler = getBufferRecycler();
byte[] buffer = bufferRecycler.allocByteBuffer(0, DEFAULT_COPY_BUFFER_SIZE);
int inputLength;
while (-1 != (inputLength = in.read(buffer))) {
out.write(buffer, 0, inputLength);
}
bufferRecycler.releaseByteBuffer(0, buffer);
}
/**
* Copies the contents of one stream to the other.
* @param in not null
* @param out not null
* @throws IOException
*/
public static void copy(final Reader in, final Writer out) throws IOException {
BufferRecycler bufferRecycler = getBufferRecycler();
char[] buffer = bufferRecycler.allocCharBuffer(0, DEFAULT_COPY_BUFFER_SIZE);
int inputLength;
while (-1 != (inputLength = in.read(buffer))) {
out.write(buffer, 0, inputLength);
}
bufferRecycler.releaseCharBuffer(0, buffer);
}
public static byte[] buffer(final InputStream in) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Input stream may not be null");
}
UnsynchronizedByteArrayOutputStream buf = new UnsynchronizedByteArrayOutputStream();
copy(in, buf);
return buf.toByteArray();
}
public static ByteArrayOutputStreamRecycler.Wrapper bufferEfficient(final InputStream in) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Input stream may not be null");
}
ByteArrayOutputStreamRecycler.Wrapper buf = getOutputStreamRecycler().allocOutputStream();
copy(in, buf.getValue());
return buf;
}
public static String buffer(final Reader in) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Reader may not be null");
}
StringWriter buf = new StringWriter();
copy(in, buf);
return buf.toString();
}
/**
* Encodes the specified string into an immutable sequence of bytes using
* the US-ASCII charset or UTF-8 in case none ASCII characters are in the
* sequence.
*
* @param string
* string to encode.
* @return encoded string as an immutable sequence of bytes.
*/
public static ByteSequence encode(CharSequence string) {
if (string == null) {
return null;
}
if (!CharsetUtil.isASCII(string)) {
return encode(StandardCharsets.UTF_8, string);
}
ByteArrayBuffer buf = new ByteArrayBuffer(string.length());
for (int i = 0; i < string.length(); i++) {
buf.append((byte) string.charAt(i));
}
return buf;
}
/**
* Encodes the specified string into an immutable sequence of bytes using
* the specified charset.
*
* @param charset
* Java charset to be used for the conversion.
* @param string
* string to encode.
* @return encoded string as an immutable sequence of bytes.
*/
public static ByteSequence encode(Charset charset, CharSequence string) {
if (string == null) {
return null;
}
if (charset == null) {
charset = Charset.defaultCharset();
}
ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
ByteArrayBuffer buf = new ByteArrayBuffer(encoded.remaining());
buf.append(encoded.array(), encoded.position(), encoded.remaining());
return buf;
}
/**
* Decodes the specified sequence of bytes into a string using the US-ASCII
* charset.
*
* @param byteSequence
* sequence of bytes to decode.
* @return decoded string.
*/
public static String decode(ByteSequence byteSequence) {
if (byteSequence == null) {
return null;
}
return decode(byteSequence, 0, byteSequence.length());
}
/**
* Decodes the specified sequence of bytes into a string using the specified
* charset.
*
* @param charset
* Java charset to be used for the conversion.
* @param byteSequence
* sequence of bytes to decode.
* @return decoded string.
*/
public static String decode(Charset charset, ByteSequence byteSequence) {
return decode(charset, byteSequence, 0, byteSequence.length());
}
/**
* Decodes a sub-sequence of the specified sequence of bytes into a string
* using the US-ASCII charset.
*
* @param byteSequence
* sequence of bytes to decode.
* @param offset
* offset into the byte sequence.
* @param length
* number of bytes.
* @return decoded string.
*/
public static String decode(ByteSequence byteSequence, int offset, int length) {
if (byteSequence == null) {
return null;
}
char[] underlying = new char[length];
for (int i = offset; i < offset + length; i++) {
underlying[i - offset] = (char) (byteSequence.byteAt(i) & 0xff);
}
return new String(underlying);
}
/**
* Decodes a sub-sequence of the specified sequence of bytes into a string
* using the US-ASCII charset with falling back to {@link #decode(Charset, ByteSequence, int, int)}
* on a first non US-ASCII character.
*
* @param byteSequence
* sequence of bytes to decode.
* @param offset
* offset into the byte sequence.
* @param length
* number of bytes.
* @param charset
* fallback charset.
* @return decoded string.
*/
public static String decode(ByteSequence byteSequence, int offset, int length, Charset charset) {
if (byteSequence == null) {
return null;
}
StringBuilder buf = new StringBuilder(length);
for (int i = offset; i < offset + length; i++) {
char ch = (char) (byteSequence.byteAt(i) & 0xff);
if (!CharsetUtil.isASCII(ch)) {
return decode(charset, byteSequence, offset, length);
}
buf.append(ch);
}
return buf.toString();
}
/**
* Decodes a sub-sequence of the specified sequence of bytes into a string
* using the specified charset.
*
* @param charset
* Java charset to be used for the conversion.
* @param byteSequence
* sequence of bytes to decode.
* @param offset
* offset into the byte sequence.
* @param length
* number of bytes.
* @return decoded string.
*/
public static String decode(Charset charset, ByteSequence byteSequence,
int offset, int length) {
if (byteSequence == null) {
return null;
}
if (charset == null) {
charset = Charset.defaultCharset();
}
if (byteSequence instanceof ByteArrayBuffer) {
ByteArrayBuffer bab = (ByteArrayBuffer) byteSequence;
return decode(charset, bab.buffer(), offset, length);
} else {
byte[] bytes = byteSequence.toByteArray();
return decode(charset, bytes, offset, length);
}
}
private static String decode(Charset charset, byte[] buffer, int offset,
int length) {
return charset.decode(ByteBuffer.wrap(buffer, offset, length))
.toString();
}
public static byte[] toByteArray(final String s, final Charset charset) {
if (s == null) {
return null;
}
try {
return s.getBytes((charset != null ? charset : Charsets.DEFAULT_CHARSET).name());
} catch (UnsupportedEncodingException ex) {
// Should never happen
throw new Error(ex);
}
}
public static byte[] toAsciiByteArray(final String s) {
return toByteArray(s, Charsets.US_ASCII);
}
public static String toString(final byte[] b, final Charset charset) {
if (b == null) {
return null;
}
try {
return new String(b, (charset != null ? charset : Charsets.DEFAULT_CHARSET).name());
} catch (UnsupportedEncodingException ex) {
// Should never happen
throw new Error(ex);
}
}
public static String toAsciiString(final byte[] b) {
return toString(b, Charsets.US_ASCII);
}
public static String toString(final byte[] b, int off, int len, final Charset charset) {
if (b == null) {
return null;
}
try {
return new String(b, off, len,
(charset != null ? charset : Charsets.DEFAULT_CHARSET).name());
} catch (UnsupportedEncodingException ex) {
// Should never happen
throw new Error(ex);
}
}
public static String toAsciiString(final byte[] b, int off, int len) {
return toString(b, off, len, Charsets.US_ASCII);
}
public static String toString(final ByteArrayBuffer b, final Charset charset) {
if (b == null) {
return null;
}
try {
return new String(b.buffer(), 0, b.length(),
(charset != null ? charset : Charsets.DEFAULT_CHARSET).name());
} catch (UnsupportedEncodingException ex) {
// Should never happen
throw new Error(ex);
}
}
public static String toAsciiString(final ByteArrayBuffer b) {
return toString(b, Charsets.US_ASCII);
}
}