/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services 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.
*
* Granite Data Services 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, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.messaging.jmf.codec.std.impl;
import java.io.IOException;
import java.io.OutputStream;
import org.granite.messaging.jmf.DumpContext;
import org.granite.messaging.jmf.InputContext;
import org.granite.messaging.jmf.OutputContext;
import org.granite.messaging.jmf.codec.std.StringCodec;
import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil;
/**
* @author Franck WOLFF
*/
public class StringCodecImpl extends AbstractStandardCodec<String> implements StringCodec {
protected static final int INDEX_OR_LENGTH_BYTE_COUNT_OFFSET = 4;
protected static final int UUID_FLAG = 0x40;
protected static final int UUID_LOWERCASE_FLAG = 0x00;
protected static final int UUID_UPPERCASE_FLAG = 0x10;
protected static final char[] LOWER_HEX = "0123456789abcdef".toCharArray();
protected static final char[] UPPER_HEX = "0123456789ABCDEF".toCharArray();
protected static final int[] HEX_INDICES = new int[256];
static {
for (int i = 0; i < HEX_INDICES.length; i++) {
if (i >= '0' && i <= '9')
HEX_INDICES[i] = i - '0';
else if (i >= 'a' && i <= 'f')
HEX_INDICES[i] = i - 'a' + 10;
else if (i >= 'A' && i <= 'F')
HEX_INDICES[i] = i - 'A' + 10;
else
HEX_INDICES[i] = -1;
}
}
public int getObjectType() {
return JMF_STRING;
}
public Class<?> getObjectClass() {
return String.class;
}
public void encode(OutputContext ctx, String v) throws IOException {
final OutputStream os = ctx.getOutputStream();
if (v.length() == 0) {
os.write(JMF_STRING);
os.write(0x00);
return;
}
int indexOfStoredString = ctx.indexOfString(v);
if (indexOfStoredString >= 0) {
int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredString);
os.write(0x80 | (count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_STRING);
IntegerUtil.encodeInteger(ctx, indexOfStoredString, count);
}
else {
ctx.addToStrings(v);
int uuidCaseFlag = isUUID(v);
if (uuidCaseFlag != -1)
encodeUUID(ctx, v, uuidCaseFlag);
else {
// char[] cs = v.toCharArray();
// byte[] bytes = new byte[cs.length * 3];
//
// int length = 0;
// for (char c : cs) {
// if (c <= 0x7F)
// bytes[length++] = (byte)c;
// else if (c <= 0x7FF) {
// bytes[length++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
// bytes[length++] = (byte)(0x80 | ((c >> 0) & 0x3F));
// }
// else {
// bytes[length++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
// bytes[length++] = (byte)(0x80 | ((c >> 6) & 0x3F));
// bytes[length++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
// }
// }
byte[] bytes = v.getBytes(UTF8);
int length = bytes.length;
int count = IntegerUtil.significantIntegerBytesCount0(length);
os.write((count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_STRING);
IntegerUtil.encodeInteger(ctx, length, count);
os.write(bytes, 0, length);
}
}
}
public String decode(InputContext ctx, int parameterizedJmfType) throws IOException {
if ((parameterizedJmfType & 0x80) != 0) {
int index = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03);
return ctx.getString(index);
}
if ((parameterizedJmfType & UUID_FLAG) != 0) {
String uid = decodeUUID(ctx, parameterizedJmfType);
ctx.addToStrings(uid);
return uid;
}
int length = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03);
if (length == 0)
return "";
byte[] bytes = new byte[length];
ctx.safeReadFully(bytes);
String s = new String(bytes, UTF8);
ctx.addToStrings(s);
return s;
}
public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
ctx.indentPrintLn(String.class.getName() + ": \"" + escape(decode(ctx, parameterizedJmfType)) + "\"");
}
protected int isUUID(String v) {
if (v.length() != 36 || v.charAt(8) != '-')
return -1;
int flag = -1;
for (int i = 0; i < 36; i++) {
char c = v.charAt(i);
switch (i) {
case 8: case 13: case 18: case 23:
if (c != '-')
return -1;
break;
default:
if (!(c >= '0' && c <= '9')) {
if (c >= 'a' && c <= 'f') {
if (flag == -1)
flag = UUID_LOWERCASE_FLAG;
else if (flag != UUID_LOWERCASE_FLAG)
return -1;
}
else if (c >= 'A' && c <= 'F') {
if (flag == -1)
flag = UUID_UPPERCASE_FLAG;
else if (flag != UUID_UPPERCASE_FLAG)
return -1;
}
else
return -1;
}
break;
}
}
// No letters...
if (flag == -1)
flag = UUID_LOWERCASE_FLAG;
return flag;
}
protected void encodeUUID(OutputContext ctx, String v, int caseFlag) throws IOException {
final OutputStream os = ctx.getOutputStream();
os.write(caseFlag | UUID_FLAG | JMF_STRING);
byte[] bytes = new byte[16];
int i = 0, j = 0;
while (i < 36) {
char c1 = v.charAt(i++);
if (c1 == '-')
c1 = v.charAt(i++);
char c2 = v.charAt(i++);
bytes[j++] = (byte)(HEX_INDICES[c1] << 4 | HEX_INDICES[c2]);
}
os.write(bytes);
}
protected String decodeUUID(InputContext ctx, int parameterizedJmfType) throws IOException {
final char[] hex = (parameterizedJmfType & UUID_UPPERCASE_FLAG) != 0 ? UPPER_HEX : LOWER_HEX;
byte[] bytes = new byte[16];
ctx.safeReadFully(bytes);
char[] chars = new char[36];
int i = 0;
for (byte b : bytes) {
if (i == 8 || i == 13 || i == 18 || i == 23)
chars[i++] = '-';
chars[i++] = hex[(b & 0xF0) >>> 4];
chars[i++] = hex[b & 0x0F];
}
return new String(chars);
}
}