package org.openamq.framing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.mina.common.ByteBuffer;
import java.nio.charset.Charset;
public class EncodingUtils
{
private static final Logger _logger = LoggerFactory.getLogger(EncodingUtils.class);
private static final String STRING_ENCODING = "iso8859-15";
private static final Charset _charset = Charset.forName("iso8859-15");
public static final int SIZEOF_UNSIGNED_SHORT = 2;
public static final int SIZEOF_UNSIGNED_INT = 4;
public static int encodedShortStringLength(String s)
{
if (s == null)
{
return 1;
}
else
{
return (short) (1 + s.length());
}
}
public static int encodedLongStringLength(String s)
{
if (s == null)
{
return 4;
}
else
{
return 4 + s.length();
}
}
public static int encodedLongStringLength(char[] s)
{
if (s == null)
{
return 4;
}
else
{
return 4 + s.length;
}
}
public static int encodedLongstrLength(byte[] bytes)
{
if (bytes == null)
{
return 4;
}
else
{
return 4 + bytes.length;
}
}
public static int encodedFieldTableLength(FieldTable table)
{
if (table == null)
{
// size is encoded as 4 octets
return 4;
}
else
{
// size of the table plus 4 octets for the size
return (int)table.getEncodedSize() + 4;
}
}
public static void writeShortStringBytes(ByteBuffer buffer, String s)
{
if (s != null)
{
byte[] encodedString = new byte[s.length()];
char[] cha = s.toCharArray();
for (int i = 0; i < cha.length; i++)
{
encodedString[i] = (byte) cha[i];
}
// TODO: check length fits in an unsigned byte
writeUnsignedByte(buffer, (short)encodedString.length);
buffer.put(encodedString);
}
else
{
// really writing out unsigned byte
buffer.put((byte) 0);
}
}
public static void writeLongStringBytes(ByteBuffer buffer, String s)
{
assert s == null || s.length() <= 0xFFFE;
if (s != null)
{
int len = s.length();
writeUnsignedInteger(buffer, s.length());
byte[] encodedString = new byte[len];
char[] cha = s.toCharArray();
for (int i = 0; i < cha.length; i++)
{
encodedString[i] = (byte) cha[i];
}
buffer.put(encodedString);
}
else
{
writeUnsignedInteger(buffer, 0);
}
}
public static void writeLongStringBytes(ByteBuffer buffer, char[] s)
{
assert s == null || s.length <= 0xFFFE;
if (s != null)
{
int len = s.length;
writeUnsignedInteger(buffer, s.length);
byte[] encodedString = new byte[len];
for (int i = 0; i < s.length; i++)
{
encodedString[i] = (byte) s[i];
}
buffer.put(encodedString);
}
else
{
writeUnsignedInteger(buffer, 0);
}
}
public static void writeLongStringBytes(ByteBuffer buffer, byte[] bytes)
{
assert bytes == null || bytes.length <= 0xFFFE;
if (bytes != null)
{
writeUnsignedInteger(buffer, bytes.length);
buffer.put(bytes);
}
else
{
writeUnsignedInteger(buffer, 0);
}
}
public static void writeUnsignedByte(ByteBuffer buffer, short b)
{
byte bv = (byte) b;
buffer.put(bv);
}
public static void writeUnsignedShort(ByteBuffer buffer, int s)
{
// TODO: Is this comparison safe? Do I need to cast RHS to long?
if (s < Short.MAX_VALUE)
{
buffer.putShort((short)s);
}
else
{
short sv = (short) s;
buffer.put((byte) (0xFF & (sv >> 8)));
buffer.put((byte)(0xFF & sv));
}
}
public static void writeUnsignedInteger(ByteBuffer buffer, long l)
{
// TODO: Is this comparison safe? Do I need to cast RHS to long?
if (l < Integer.MAX_VALUE)
{
buffer.putInt((int)l);
}
else
{
int iv = (int) l;
// FIXME: This *may* go faster if we build this into a local 4-byte array and then
// put the array in a single call.
buffer.put((byte) (0xFF & (iv >> 24)));
buffer.put((byte) (0xFF & (iv >> 16)));
buffer.put((byte) (0xFF & (iv >> 8 )));
buffer.put((byte) (0xFF & iv));
}
}
public static void writeFieldTableBytes(ByteBuffer buffer, FieldTable table)
{
if (table != null)
{
table.writeToBuffer(buffer);
}
else
{
EncodingUtils.writeUnsignedInteger(buffer, 0);
}
}
public static void writeBooleans(ByteBuffer buffer, boolean[] values)
{
byte packedValue = 0;
for (int i = 0; i < values.length; i++)
{
if (values[i])
{
packedValue = (byte)(packedValue | (1 << i));
}
}
buffer.put(packedValue);
}
/**
* This is used for writing longstrs.
* @param buffer
* @param data
*/
public static void writeLongstr(ByteBuffer buffer, byte[] data)
{
if (data != null)
{
writeUnsignedInteger(buffer, data.length);
buffer.put(data);
}
else
{
writeUnsignedInteger(buffer, 0);
}
}
public static boolean[] readBooleans(ByteBuffer buffer)
{
byte packedValue = buffer.get();
boolean[] result = new boolean[8];
for (int i = 0; i < 8; i++)
{
result[i] = ((packedValue & (1 << i)) != 0);
}
return result;
}
public static FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException
{
long length = buffer.getUnsignedInt();
if (length == 0)
{
return null;
}
else
{
return new FieldTable(buffer, length);
}
}
public static String readShortString(ByteBuffer buffer)
{
byte length = buffer.get();
if (length == 0)
{
return null;
}
else
{
// this may seem rather odd to declare two array but testing has shown
// that constructing a string from a byte array is 5 (five) times slower
// than constructing one from a char array.
// this approach here is valid since we know that all the chars are
// ASCII (0-127)
byte[] stringBytes = new byte[length];
buffer.get(stringBytes, 0, length);
char[] stringChars = new char[length];
for (int i = 0; i < stringChars.length; i++)
{
stringChars[i] = (char) stringBytes[i];
}
return new String(stringChars);
}
}
public static String readLongString(ByteBuffer buffer)
{
long length = buffer.getUnsignedInt();
if (length == 0)
{
return null;
}
else
{
// this may seem rather odd to declare two array but testing has shown
// that constructing a string from a byte array is 5 (five) times slower
// than constructing one from a char array.
// this approach here is valid since we know that all the chars are
// ASCII (0-127)
byte[] stringBytes = new byte[(int)length];
buffer.get(stringBytes, 0, (int)length);
char[] stringChars = new char[(int)length];
for (int i = 0; i < stringChars.length; i++)
{
stringChars[i] = (char) stringBytes[i];
}
return new String(stringChars);
}
}
public static byte[] readLongstr(ByteBuffer buffer) throws AMQFrameDecodingException
{
long length = buffer.getUnsignedInt();
if (length == 0)
{
return null;
}
else
{
byte[] result = new byte[(int)length];
buffer.get(result);
return result;
}
}
// Will barf with a NPE on a null input. Not sure whether it should return null or
// an empty field-table (which would be slower - perhaps unnecessarily).
//
// Some sample input and the result output:
//
// Input: "a=1" "a=2 c=3" "a=3 c=4 d" "a='four' b='five'" "a=bad"
//
// Parsing <a=1>...
// {a=1}
// Parsing <a=2 c=3>...
// {a=2, c=3}
// Parsing <a=3 c=4 d>...
// {a=3, c=4, d=null}
// Parsing <a='four' b='five'>...
// {a=four, b=five}
// Parsing <a=bad>...
// java.lang.IllegalArgumentException: a: Invalid integer in <bad> from <a=bad>.
//
public static final FieldTable createFieldTableFromMessageSelector(String selector)
{
boolean debug = _logger.isDebugEnabled();
// TODO: Doesn't support embedded quotes properly.
String[] expressions = selector.split(" +");
FieldTable result = new FieldTable();
for(int i = 0; i < expressions.length; i++)
{
String expr = expressions[i];
if (debug) _logger.debug("Expression = <" + expr + ">");
int equals = expr.indexOf('=');
if (equals < 0)
{
// Existence check
result.put("S" + expr.trim(),null);
}
else
{
String key = expr.substring(0,equals).trim();
String value = expr.substring(equals + 1).trim();
if (debug) _logger.debug("Key = <" + key + ">, Value = <" + value + ">");
if (value.charAt(0) == '\'')
{
if (value.charAt(value.length()- 1) != '\'')
{
throw new IllegalArgumentException(key + ": Missing quote in <" + value + "> from <" + selector + ">.");
}
else
{
value = value.substring(1,value.length() - 1);
result.put("S" + key,value);
}
}
else
{
try
{
int intValue = Integer.parseInt(value);
result.put("i" + key,value);
}
catch(NumberFormatException e)
{
throw new IllegalArgumentException(key + ": Invalid integer in <" + value + "> from <" + selector + ">.");
}
}
}
}
if (debug) _logger.debug("Field-table created from <" + selector + "> is <" + result + ">");
return(result);
}
static byte[] hexToByteArray(String id)
{
// Should check param for null, long enough for this check, upper-case and trailing char
String s = (id.charAt(1) == 'x') ? id.substring(2) : id; // strip 0x
int len = s.length();
int byte_len = len / 2;
byte[] b = new byte[byte_len];
for(int i = 0; i < byte_len; i++)
{
// fixme: refine these repetitive subscript calcs.
int ch = i * 2;
byte b1 = Byte.parseByte(s.substring(ch,ch + 1),16);
byte b2 = Byte.parseByte(s.substring(ch + 1,ch + 2),16);
b[i] = (byte)(b1 * 16 + b2);
}
return(b);
}
public static char[] convertToHexCharArray(byte[] from)
{
int length = from.length;
char[] result_buff = new char[length * 2 + 2];
result_buff[0] = '0';
result_buff[1] = 'x';
int bite;
int dest = 2;
for(int i = 0; i < length; i++)
{
bite = from[i];
if (bite < 0) bite += 256;
result_buff[dest++] = hex_chars[bite >> 4];
result_buff[dest++] = hex_chars[bite & 0x0f];
}
return(result_buff);
}
public static String convertToHexString(byte[] from)
{
return(new String(convertToHexCharArray(from)));
}
public static String convertToHexString(ByteBuffer bb)
{
int size = bb.limit();
byte[] from = new byte[size];
for(int i = 0; i < size; i++)
{
from[i] = bb.get(i);
}
return(new String(convertToHexCharArray(from)));
}
public static void main(String[] args)
{
for(int i = 0; i < args.length; i++)
{
String selector = args[i];
System.err.println("Parsing <" + selector + ">...");
try
{
System.err.println(createFieldTableFromMessageSelector(selector));
}
catch(IllegalArgumentException e)
{
System.err.println(e);
}
}
}
private static char hex_chars[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
}