/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.function;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.util.Arrays;
import org.teiid.core.util.Base64;
public final class CharsetUtils {
public static final String HEX_NAME = "HEX"; //$NON-NLS-1$
static final char[] hex_alphabet = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static Charset HEX = new Charset(HEX_NAME, new String[0]) {
@Override
public CharsetEncoder newEncoder() {
return new FixedEncoder(this, 2, .5f, 1) {
char[] chars = new char[2];
@Override
protected CoderResult encode(ByteBuffer out) {
this.cb.get(chars);
out.put((byte)(Integer.parseInt(new String(chars), 16) & 0xff));
return CoderResult.UNDERFLOW;
}
};
}
@Override
public CharsetDecoder newDecoder() {
return new FixedDecoder(this, 1, 2, 2) {
@Override
public void decode(CharBuffer out) {
byte b = this.bb.get();
toHex(out, b);
}
};
}
@Override
public boolean contains(Charset cs) {
return false;
}
};
public static void toHex(CharBuffer out, byte b) {
out.put(hex_alphabet[(b & 0xf0) >> 4]);
out.put(hex_alphabet[b & 0x0f]);
}
public static final String BASE64_NAME = "BASE64"; //$NON-NLS-1$
public static Charset BASE64 = new Charset(BASE64_NAME, new String[0]) {
@Override
public CharsetEncoder newEncoder() {
return new FixedEncoder(this, 4, .75f, 1) {
@Override
protected CoderResult encode(ByteBuffer out) {
try {
out.put(Base64.decode(cb));
return CoderResult.UNDERFLOW;
} catch (IllegalArgumentException e) {
return CoderResult.unmappableForLength(4);
}
}
};
}
@Override
public CharsetDecoder newDecoder() {
return new FixedDecoder(this, 3, 1.25f, 3) {
@Override
public void decode(CharBuffer out) {
if (bb.limit() == bb.array().length) {
out.put(Base64.encodeBytes(bb.array()));
} else {
out.put(Base64.encodeBytes(Arrays.copyOf(bb.array(), bb.limit())));
}
}
};
}
@Override
public boolean contains(Charset cs) {
return false;
}
};
public static abstract class FixedEncoder extends CharsetEncoder {
protected CharBuffer cb;
protected FixedEncoder(Charset cs, int encodeChars, float averageBytesPerChar, float maxBytesPerChar) {
super(cs, averageBytesPerChar, maxBytesPerChar);
cb = CharBuffer.wrap(new char[encodeChars]);
}
@Override
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
while (in.hasRemaining()) {
cb.put(in.get());
if (!cb.hasRemaining()) {
if (!out.hasRemaining()) {
return CoderResult.OVERFLOW;
}
cb.flip();
CoderResult result = encode(out);
if (result != CoderResult.UNDERFLOW) {
return result;
}
cb.clear();
}
}
return CoderResult.UNDERFLOW;
}
abstract protected CoderResult encode(ByteBuffer out);
@Override
protected CoderResult implFlush(ByteBuffer out) {
if (cb.position() != 0) {
return CoderResult.unmappableForLength(cb.position());
}
return super.implFlush(out);
}
@Override
protected void implReset() {
cb.clear();
}
}
public static abstract class FixedDecoder extends CharsetDecoder {
protected ByteBuffer bb;
protected FixedDecoder(Charset cs, int decodeBytes,
float averageCharsPerByte,
float maxCharsPerByte) {
super(cs, averageCharsPerByte, maxCharsPerByte);
this.bb = ByteBuffer.wrap(new byte[decodeBytes]);
}
@Override
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
while (in.hasRemaining()) {
bb.put(in.get());
if (!bb.hasRemaining()) {
if (!out.hasRemaining()) {
return CoderResult.OVERFLOW;
}
bb.flip();
decode(out);
bb.clear();
}
}
return CoderResult.UNDERFLOW;
}
protected abstract void decode(CharBuffer out);
@Override
protected CoderResult implFlush(CharBuffer out) {
if (bb.position() != 0) {
if (!out.hasRemaining()) {
return CoderResult.OVERFLOW;
}
bb.flip();
decode(out);
bb.clear();
}
return super.implFlush(out);
}
@Override
protected void implReset() {
bb.clear();
}
}
}