001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.jmf.codec.std.impl; 023 024import java.io.IOException; 025import java.io.OutputStream; 026 027import org.granite.messaging.jmf.DumpContext; 028import org.granite.messaging.jmf.InputContext; 029import org.granite.messaging.jmf.OutputContext; 030import org.granite.messaging.jmf.codec.std.StringCodec; 031import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil; 032 033/** 034 * @author Franck WOLFF 035 */ 036public class StringCodecImpl extends AbstractStandardCodec<String> implements StringCodec { 037 038 protected static final int INDEX_OR_LENGTH_BYTE_COUNT_OFFSET = 4; 039 040 protected static final int UUID_FLAG = 0x40; 041 protected static final int UUID_LOWERCASE_FLAG = 0x00; 042 protected static final int UUID_UPPERCASE_FLAG = 0x10; 043 044 protected static final char[] LOWER_HEX = "0123456789abcdef".toCharArray(); 045 protected static final char[] UPPER_HEX = "0123456789ABCDEF".toCharArray(); 046 047 protected static final int[] HEX_INDICES = new int[256]; 048 static { 049 for (int i = 0; i < HEX_INDICES.length; i++) { 050 if (i >= '0' && i <= '9') 051 HEX_INDICES[i] = i - '0'; 052 else if (i >= 'a' && i <= 'f') 053 HEX_INDICES[i] = i - 'a' + 10; 054 else if (i >= 'A' && i <= 'F') 055 HEX_INDICES[i] = i - 'A' + 10; 056 else 057 HEX_INDICES[i] = -1; 058 } 059 } 060 061 public int getObjectType() { 062 return JMF_STRING; 063 } 064 065 public Class<?> getObjectClass() { 066 return String.class; 067 } 068 069 public void encode(OutputContext ctx, String v) throws IOException { 070 final OutputStream os = ctx.getOutputStream(); 071 072 if (v.length() == 0) { 073 os.write(JMF_STRING); 074 os.write(0x00); 075 return; 076 } 077 078 int indexOfStoredString = ctx.indexOfString(v); 079 if (indexOfStoredString >= 0) { 080 int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredString); 081 os.write(0x80 | (count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_STRING); 082 IntegerUtil.encodeInteger(ctx, indexOfStoredString, count); 083 } 084 else { 085 ctx.addToStrings(v); 086 087 int uuidCaseFlag = isUUID(v); 088 if (uuidCaseFlag != -1) 089 encodeUUID(ctx, v, uuidCaseFlag); 090 else { 091// char[] cs = v.toCharArray(); 092// byte[] bytes = new byte[cs.length * 3]; 093// 094// int length = 0; 095// for (char c : cs) { 096// if (c <= 0x7F) 097// bytes[length++] = (byte)c; 098// else if (c <= 0x7FF) { 099// bytes[length++] = (byte)(0xC0 | ((c >> 6) & 0x1F)); 100// bytes[length++] = (byte)(0x80 | ((c >> 0) & 0x3F)); 101// } 102// else { 103// bytes[length++] = (byte)(0xE0 | ((c >> 12) & 0x0F)); 104// bytes[length++] = (byte)(0x80 | ((c >> 6) & 0x3F)); 105// bytes[length++] = (byte)(0xE0 | ((c >> 12) & 0x0F)); 106// } 107// } 108 109 byte[] bytes = v.getBytes(UTF8); 110 int length = bytes.length; 111 int count = IntegerUtil.significantIntegerBytesCount0(length); 112 113 os.write((count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_STRING); 114 IntegerUtil.encodeInteger(ctx, length, count); 115 os.write(bytes, 0, length); 116 } 117 } 118 } 119 120 public String decode(InputContext ctx, int parameterizedJmfType) throws IOException { 121 if ((parameterizedJmfType & 0x80) != 0) { 122 int index = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03); 123 return ctx.getString(index); 124 } 125 126 if ((parameterizedJmfType & UUID_FLAG) != 0) { 127 String uid = decodeUUID(ctx, parameterizedJmfType); 128 ctx.addToStrings(uid); 129 return uid; 130 } 131 132 int length = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03); 133 if (length == 0) 134 return ""; 135 136 byte[] bytes = new byte[length]; 137 ctx.safeReadFully(bytes); 138 String s = new String(bytes, UTF8); 139 ctx.addToStrings(s); 140 141 return s; 142 } 143 144 public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException { 145 ctx.indentPrintLn(String.class.getName() + ": \"" + escape(decode(ctx, parameterizedJmfType)) + "\""); 146 } 147 148 protected int isUUID(String v) { 149 if (v.length() != 36 || v.charAt(8) != '-') 150 return -1; 151 152 int flag = -1; 153 154 for (int i = 0; i < 36; i++) { 155 char c = v.charAt(i); 156 157 switch (i) { 158 case 8: case 13: case 18: case 23: 159 if (c != '-') 160 return -1; 161 break; 162 default: 163 if (!(c >= '0' && c <= '9')) { 164 if (c >= 'a' && c <= 'f') { 165 if (flag == -1) 166 flag = UUID_LOWERCASE_FLAG; 167 else if (flag != UUID_LOWERCASE_FLAG) 168 return -1; 169 } 170 else if (c >= 'A' && c <= 'F') { 171 if (flag == -1) 172 flag = UUID_UPPERCASE_FLAG; 173 else if (flag != UUID_UPPERCASE_FLAG) 174 return -1; 175 } 176 else 177 return -1; 178 } 179 break; 180 } 181 } 182 183 // No letters... 184 if (flag == -1) 185 flag = UUID_LOWERCASE_FLAG; 186 187 return flag; 188 } 189 190 protected void encodeUUID(OutputContext ctx, String v, int caseFlag) throws IOException { 191 final OutputStream os = ctx.getOutputStream(); 192 193 os.write(caseFlag | UUID_FLAG | JMF_STRING); 194 195 byte[] bytes = new byte[16]; 196 197 int i = 0, j = 0; 198 while (i < 36) { 199 char c1 = v.charAt(i++); 200 if (c1 == '-') 201 c1 = v.charAt(i++); 202 char c2 = v.charAt(i++); 203 204 bytes[j++] = (byte)(HEX_INDICES[c1] << 4 | HEX_INDICES[c2]); 205 } 206 207 os.write(bytes); 208 } 209 210 protected String decodeUUID(InputContext ctx, int parameterizedJmfType) throws IOException { 211 final char[] hex = (parameterizedJmfType & UUID_UPPERCASE_FLAG) != 0 ? UPPER_HEX : LOWER_HEX; 212 213 byte[] bytes = new byte[16]; 214 ctx.safeReadFully(bytes); 215 216 char[] chars = new char[36]; 217 int i = 0; 218 for (byte b : bytes) { 219 if (i == 8 || i == 13 || i == 18 || i == 23) 220 chars[i++] = '-'; 221 chars[i++] = hex[(b & 0xF0) >>> 4]; 222 chars[i++] = hex[b & 0x0F]; 223 } 224 225 return new String(chars); 226 } 227}