1 16 package com.google.gwt.user.server.rpc.impl; 17 18 import com.google.gwt.user.client.rpc.SerializationException; 19 import com.google.gwt.user.client.rpc.SerializationStreamWriter; 20 import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter; 21 22 import java.lang.reflect.Field ; 23 import java.lang.reflect.InvocationTargetException ; 24 import java.lang.reflect.Method ; 25 import java.lang.reflect.Modifier ; 26 import java.util.ArrayList ; 27 import java.util.HashMap ; 28 import java.util.IdentityHashMap ; 29 30 34 public final class ServerSerializationStreamWriter extends 35 AbstractSerializationStreamWriter { 36 37 private static final char NON_BREAKING_HYPHEN = '\u2011'; 38 39 42 private static final int NUMBER_OF_JS_ESCAPED_CHARS = 128; 43 44 49 private static final char[] JS_CHARS_ESCAPED = new char[NUMBER_OF_JS_ESCAPED_CHARS]; 50 51 55 private static final char JS_ESCAPE_CHAR = '\\'; 56 57 60 private static final char JS_QUOTE_CHAR = '\"'; 61 62 66 private static final char NIBBLE_TO_HEX_CHAR[] = { 67 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 68 'E', 'F'}; 69 70 static { 71 76 JS_CHARS_ESCAPED['\u0000'] = '0'; 77 JS_CHARS_ESCAPED['\b'] = 'b'; 78 JS_CHARS_ESCAPED['\t'] = 't'; 79 JS_CHARS_ESCAPED['\n'] = 'n'; 80 JS_CHARS_ESCAPED['\f'] = 'f'; 81 JS_CHARS_ESCAPED['\r'] = 'r'; 82 JS_CHARS_ESCAPED[JS_ESCAPE_CHAR] = JS_ESCAPE_CHAR; 83 JS_CHARS_ESCAPED[JS_QUOTE_CHAR] = JS_QUOTE_CHAR; 84 } 85 86 94 private static String escapeString(String toEscape) { 95 char[] input = toEscape.toCharArray(); 97 CharVector charVector = new CharVector(input.length * 2 + 2, input.length); 98 99 charVector.add(JS_QUOTE_CHAR); 100 101 for (int i = 0, n = input.length; i < n; ++i) { 102 char c = input[i]; 103 if (c < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[c] != 0) { 104 charVector.add(JS_ESCAPE_CHAR); 105 charVector.add(JS_CHARS_ESCAPED[c]); 106 } else if (needsUnicodeEscape(c)) { 107 charVector.add(JS_ESCAPE_CHAR); 108 unicodeEscape(c, charVector); 109 } else { 110 charVector.add(c); 111 } 112 } 113 114 charVector.add(JS_QUOTE_CHAR); 115 return String.valueOf(charVector.asArray(), 0, charVector.getSize()); 116 } 117 118 160 private static boolean needsUnicodeEscape(char ch) { 161 switch (Character.getType(ch)) { 162 case Character.COMBINING_SPACING_MARK: 164 case Character.ENCLOSING_MARK: 165 case Character.NON_SPACING_MARK: 166 case Character.UNASSIGNED: 167 case Character.PRIVATE_USE: 168 case Character.SPACE_SEPARATOR: 169 case Character.CONTROL: 170 171 case Character.LINE_SEPARATOR: 173 case Character.FORMAT: 174 case Character.PARAGRAPH_SEPARATOR: 175 case Character.SURROGATE: 176 return true; 177 178 default: 179 if (ch == NON_BREAKING_HYPHEN) { 180 return true; 182 } 183 break; 184 } 185 186 return false; 187 } 188 189 196 private static void unicodeEscape(char ch, CharVector charVector) { 197 if (ch < 256) { 198 charVector.add('x'); 199 charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]); 200 charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]); 201 } else { 202 charVector.add('u'); 203 charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 12) & 0x0F]); 204 charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 8) & 0x0F]); 205 charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]); 206 charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]); 207 } 208 } 209 210 private int objectCount; 211 212 private IdentityHashMap objectMap = new IdentityHashMap (); 213 214 private ServerSerializableTypeOracle serializableTypeOracle; 215 216 private HashMap stringMap = new HashMap (); 217 218 private ArrayList stringTable = new ArrayList (); 219 220 private ArrayList tokenList = new ArrayList (); 221 222 private int tokenListCharCount; 223 224 public ServerSerializationStreamWriter( 225 ServerSerializableTypeOracle serializableTypeOracle) { 226 this.serializableTypeOracle = serializableTypeOracle; 227 } 228 229 public void prepareToWrite() { 230 objectCount = 0; 231 objectMap.clear(); 232 tokenList.clear(); 233 tokenListCharCount = 0; 234 stringMap.clear(); 235 stringTable.clear(); 236 } 237 238 public void serializeValue(Object value, Class type) 239 throws SerializationException { 240 if (type == boolean.class) { 241 writeBoolean(((Boolean ) value).booleanValue()); 242 } else if (type == byte.class) { 243 writeByte(((Byte ) value).byteValue()); 244 } else if (type == char.class) { 245 writeChar(((Character ) value).charValue()); 246 } else if (type == double.class) { 247 writeDouble(((Double ) value).doubleValue()); 248 } else if (type == float.class) { 249 writeFloat(((Float ) value).floatValue()); 250 } else if (type == int.class) { 251 writeInt(((Integer ) value).intValue()); 252 } else if (type == long.class) { 253 writeLong(((Long ) value).longValue()); 254 } else if (type == short.class) { 255 writeShort(((Short ) value).shortValue()); 256 } else if (type == String .class) { 257 writeString((String ) value); 258 } else { 259 writeObject(value); 260 } 261 } 262 263 270 public String toString() { 271 int capacityGuess = 2 * tokenListCharCount + 2 * tokenList.size(); 275 StringBuffer buffer = new StringBuffer (capacityGuess); 276 buffer.append("["); 277 writePayload(buffer); 278 writeStringTable(buffer); 279 writeHeader(buffer); 280 buffer.append("]"); 281 return buffer.toString(); 282 } 283 284 protected int addString(String string) { 285 if (string == null) { 286 return 0; 287 } 288 Integer o = (Integer ) stringMap.get(string); 289 if (o != null) { 290 return o.intValue(); 291 } 292 stringTable.add(string); 293 int index = stringTable.size(); 295 stringMap.put(string, new Integer (index)); 296 return index; 297 } 298 299 protected void append(String token) { 300 tokenList.add(token); 301 if (token != null) { 302 tokenListCharCount += token.length(); 303 } 304 } 305 306 protected int getIndexForObject(Object instance) { 307 Integer o = (Integer ) objectMap.get(instance); 308 if (o != null) { 309 return o.intValue(); 310 } 311 return -1; 312 } 313 314 protected String getObjectTypeSignature(Object instance) { 315 if (shouldEnforceTypeVersioning()) { 316 return serializableTypeOracle.encodeSerializedInstanceReference(instance.getClass()); 317 } else { 318 return serializableTypeOracle.getSerializedTypeName(instance.getClass()); 319 } 320 } 321 322 protected void saveIndexForObject(Object instance) { 323 objectMap.put(instance, new Integer (objectCount++)); 324 } 325 326 protected void serialize(Object instance, String typeSignature) 327 throws SerializationException { 328 serializeImpl(instance, instance.getClass()); 329 } 330 331 private void serializeClass(Object instance, Class instanceClass) 332 throws SerializationException { 333 assert (instance != null); 334 335 Field [] declFields = instanceClass.getDeclaredFields(); 336 Field [] serializableFields = serializableTypeOracle.applyFieldSerializationPolicy(declFields); 337 for (int index = 0; index < serializableFields.length; ++index) { 338 Field declField = serializableFields[index]; 339 assert (declField != null); 340 341 boolean isAccessible = declField.isAccessible(); 342 boolean needsAccessOverride = !isAccessible 343 && !Modifier.isPublic(declField.getModifiers()); 344 if (needsAccessOverride) { 345 declField.setAccessible(true); 347 } 348 349 Object value; 350 try { 351 value = declField.get(instance); 352 serializeValue(value, declField.getType()); 353 354 } catch (IllegalArgumentException e) { 355 throw new SerializationException(e); 356 357 } catch (IllegalAccessException e) { 358 throw new SerializationException(e); 359 } 360 361 if (needsAccessOverride) { 362 declField.setAccessible(isAccessible); 364 } 365 } 366 367 Class superClass = instanceClass.getSuperclass(); 368 if (superClass != null && serializableTypeOracle.isSerializable(superClass)) { 369 serializeImpl(instance, superClass); 370 } 371 } 372 373 private void serializeImpl(Object instance, Class instanceClass) 374 throws SerializationException { 375 376 assert (instance != null); 377 378 Class customSerializer = serializableTypeOracle.hasCustomFieldSerializer(instanceClass); 379 if (customSerializer != null) { 380 serializeWithCustomSerializer(customSerializer, instance, instanceClass); 381 } else { 382 assert (!instanceClass.isArray()); 386 serializeClass(instance, instanceClass); 387 } 388 } 389 390 private void serializeWithCustomSerializer(Class customSerializer, 391 Object instance, Class instanceClass) throws SerializationException { 392 393 Method serialize; 394 try { 395 if (instanceClass.isArray()) { 396 Class componentType = instanceClass.getComponentType(); 397 if (!componentType.isPrimitive()) { 398 instanceClass = Class.forName("[Ljava.lang.Object;"); 399 } 400 } 401 402 serialize = customSerializer.getMethod("serialize", new Class [] { 403 SerializationStreamWriter.class, instanceClass}); 404 405 serialize.invoke(null, new Object [] {this, instance}); 406 407 } catch (SecurityException e) { 408 throw new SerializationException(e); 409 410 } catch (NoSuchMethodException e) { 411 throw new SerializationException(e); 412 413 } catch (IllegalArgumentException e) { 414 throw new SerializationException(e); 415 416 } catch (IllegalAccessException e) { 417 throw new SerializationException(e); 418 419 } catch (InvocationTargetException e) { 420 throw new SerializationException(e); 421 422 } catch (ClassNotFoundException e) { 423 throw new SerializationException(e); 424 } 425 } 426 427 431 private void writeHeader(StringBuffer buffer) { 432 buffer.append(","); 433 buffer.append(getFlags()); 434 buffer.append(","); 435 buffer.append(SERIALIZATION_STREAM_VERSION); 436 } 437 438 private void writePayload(StringBuffer buffer) { 439 for (int i = tokenList.size() - 1; i >= 0; --i) { 440 String token = (String ) tokenList.get(i); 441 buffer.append(token); 442 if (i > 0) { 443 buffer.append(","); 444 } 445 } 446 } 447 448 private void writeStringTable(StringBuffer buffer) { 449 if (tokenList.size() > 0) { 450 buffer.append(","); 451 } 452 buffer.append("["); 453 for (int i = 0, c = stringTable.size(); i < c; ++i) { 454 if (i > 0) { 455 buffer.append(","); 456 } 457 buffer.append(escapeString((String ) stringTable.get(i))); 458 } 459 buffer.append("]"); 460 } 461 } 462 | Popular Tags |