blob: db74b388a407ca505febb813e47a19d0e6bc4e6d [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.client.rpc.impl;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* Base class for the client and server serialization streams. This class
* handles the basic serialization and deserialization formatting for primitive
* types since these are common between the client and the server. It also
* handles Object- and String-tracking for building graph references.
*/
public abstract class AbstractSerializationStreamWriter extends
AbstractSerializationStream implements SerializationStreamWriter {
private static final double TWO_PWR_16_DBL = 0x10000;
private static final double TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
/**
* Return a pair of doubles { low, high } that add up to the given number,
* such that "low" is always between 0 and 2^32-1 inclusive and "high" is
* always between -2^63 and 2^63-2^32 inclusive and is a multiple of 2^32.
*/
public static double[] getAsDoubleArray(long value) {
int lowBits = (int) (value & 0xffffffff);
int highBits = (int) (value >> 32);
return makeLongComponents(lowBits, highBits);
}
// Equivalent to getAsDoubleArray((long) highBits << 32 | lowBits);
protected static double[] makeLongComponents(int lowBits, int highBits) {
double high = highBits * TWO_PWR_32_DBL;
double low = lowBits;
if (lowBits < 0) {
low += TWO_PWR_32_DBL;
}
return new double[] {low, high};
}
private int objectCount;
private Map<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
private Map<String, Integer> stringMap = new HashMap<String, Integer>();
private List<String> stringTable = new ArrayList<String>();
public void prepareToWrite() {
objectCount = 0;
objectMap.clear();
stringMap.clear();
stringTable.clear();
}
@Override
public abstract String toString();
public void writeBoolean(boolean fieldValue) {
append(fieldValue ? "1" : "0");
}
public void writeByte(byte fieldValue) {
append(String.valueOf(fieldValue));
}
public void writeChar(char ch) {
// just use an int, it's more foolproof
append(String.valueOf((int) ch));
}
public void writeDouble(double fieldValue) {
append(String.valueOf(fieldValue));
}
public void writeFloat(float fieldValue) {
writeDouble(fieldValue);
}
public void writeInt(int fieldValue) {
append(String.valueOf(fieldValue));
}
public abstract void writeLong(long value);
public void writeObject(Object instance) throws SerializationException {
if (instance == null) {
// write a null string
writeString(null);
return;
}
int objIndex = getIndexForObject(instance);
if (objIndex >= 0) {
// We've already encoded this object, make a backref
// Transform 0-based to negative 1-based
writeInt(-(objIndex + 1));
return;
}
saveIndexForObject(instance);
// Serialize the type signature
String typeSignature = getObjectTypeSignature(instance);
writeString(typeSignature);
// Now serialize the rest of the object
serialize(instance, typeSignature);
}
public void writeShort(short value) {
append(String.valueOf(value));
}
public void writeString(String value) {
writeInt(addString(value));
}
/**
* Add a string to the string table and return its index.
*
* @param string the string to add
* @return the index to the string
*/
protected int addString(String string) {
if (string == null) {
return 0;
}
Integer o = stringMap.get(string);
if (o != null) {
return o;
}
stringTable.add(string);
// index is 1-based
int index = stringTable.size();
stringMap.put(string, index);
return index;
}
/**
* Append a token to the underlying output buffer.
*
* @param token the token to append
*/
protected abstract void append(String token);
/**
* Get the index for an object that may have previously been saved via
* {@link #saveIndexForObject(Object)}.
*
* @param instance the object to save
* @return the index associated with this object, or -1 if this object hasn't
* been seen before
*/
protected int getIndexForObject(Object instance) {
return objectMap.containsKey(instance) ? objectMap.get(instance) : -1;
}
/**
* Compute and return the type signature for an object.
*
* @param instance the instance to inspect
* @return the type signature of the instance
*/
protected abstract String getObjectTypeSignature(Object instance)
throws SerializationException;
/**
* Gets the string table.
*/
protected List<String> getStringTable() {
return stringTable;
}
/**
* Remember this object as having been seen before.
*
* @param instance the object to remember
*/
protected void saveIndexForObject(Object instance) {
objectMap.put(instance, objectCount++);
}
/**
* Serialize an object into the stream.
*
* @param instance the object to serialize
* @param typeSignature the type signature of the object
* @throws SerializationException
*/
protected abstract void serialize(Object instance, String typeSignature)
throws SerializationException;
}