001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.user.client.rpc.impl;
017:
018: import com.google.gwt.core.client.JavaScriptObject;
019: import com.google.gwt.user.client.rpc.SerializationException;
020:
021: import java.util.ArrayList;
022:
023: /**
024: * For internal use only. Used for server call serialization.
025: */
026: public final class ClientSerializationStreamWriter extends
027: AbstractSerializationStreamWriter {
028:
029: private static void append(StringBuffer sb, String token) {
030: assert (token != null);
031: sb.append(token);
032: sb.append('\uffff');
033: }
034:
035: private StringBuffer encodeBuffer;
036:
037: private final String moduleBaseURL;
038:
039: private int objectCount;
040:
041: /*
042: * Accessed from JSNI code, so ignore unused warning.
043: */
044: @SuppressWarnings("unused")
045: private JavaScriptObject objectMap;
046:
047: private final String serializationPolicyStrongName;
048:
049: private final Serializer serializer;
050:
051: /*
052: * Accesses need to be prefixed with ':' to prevent conflict with built-in
053: * JavaScript properties.
054: *
055: * Accessed from JSNI code, so ignore unused warning.
056: */
057: @SuppressWarnings("unused")
058: private JavaScriptObject stringMap;
059:
060: private ArrayList<String> stringTable = new ArrayList<String>();
061:
062: /**
063: * Constructs a <code>ClientSerializationStreamWriter</code> that does not
064: * use a serialization policy file.
065: *
066: * @param serializer the {@link Serializer} to use
067: */
068: public ClientSerializationStreamWriter(Serializer serializer) {
069: this .serializer = serializer;
070: this .moduleBaseURL = null;
071: this .serializationPolicyStrongName = null;
072: // Override the default version if no policy info is given.
073: setVersion(SERIALIZATION_STREAM_VERSION_WITHOUT_SERIALIZATION_POLICY);
074: }
075:
076: /**
077: * Constructs a <code>ClientSerializationStreamWriter</code> using the
078: * specified module base URL and the serialization policy.
079: *
080: * @param serializer the {@link Serializer} to use
081: * @param moduleBaseURL the location of the module
082: * @param serializationPolicyStrongName the strong name of serialization
083: * policy
084: */
085: public ClientSerializationStreamWriter(Serializer serializer,
086: String moduleBaseURL, String serializationPolicyStrongName) {
087: this .serializer = serializer;
088: this .moduleBaseURL = moduleBaseURL;
089: this .serializationPolicyStrongName = serializationPolicyStrongName;
090: }
091:
092: /**
093: * Call this method before attempting to append any tokens. This method
094: * implementation <b>must</b> be called by any overridden version.
095: */
096: public void prepareToWrite() {
097: objectCount = 0;
098: objectMap = JavaScriptObject.createObject();
099: stringMap = JavaScriptObject.createObject();
100: stringTable.clear();
101: encodeBuffer = new StringBuffer();
102:
103: if (hasSerializationPolicyInfo()) {
104: writeString(moduleBaseURL);
105: writeString(serializationPolicyStrongName);
106: }
107: }
108:
109: @Override
110: public String toString() {
111: StringBuffer buffer = new StringBuffer();
112: writeHeader(buffer);
113: writeStringTable(buffer);
114: writePayload(buffer);
115: return buffer.toString();
116: }
117:
118: @Override
119: protected int addString(String string) {
120: if (string == null) {
121: return 0;
122: }
123:
124: int index = getIntForString(string);
125: if (index > 0) {
126: return index;
127: }
128: stringTable.add(string);
129: // index is 1-based (that's why we're taking the size AFTER add)
130: index = stringTable.size();
131: setIntForString(string, index);
132: return index;
133: }
134:
135: /**
136: * Appends a token to the end of the buffer.
137: */
138: @Override
139: protected void append(String token) {
140: append(encodeBuffer, token);
141: }
142:
143: @Override
144: protected int getIndexForObject(Object instance) {
145: return getIntForInt(System.identityHashCode(instance));
146: }
147:
148: @Override
149: protected String getObjectTypeSignature(Object o) {
150: Class<?> clazz = o.getClass();
151:
152: if (o instanceof Enum) {
153: Enum<?> e = (Enum<?>) o;
154: clazz = e.getDeclaringClass();
155: }
156:
157: String typeName = clazz.getName();
158:
159: String serializationSignature = serializer
160: .getSerializationSignature(typeName);
161: if (serializationSignature != null) {
162: typeName += "/" + serializationSignature;
163: }
164: return typeName;
165: }
166:
167: @Override
168: protected void saveIndexForObject(Object instance) {
169: setIntForInt(System.identityHashCode(instance), objectCount++);
170: }
171:
172: @Override
173: protected void serialize(Object instance, String typeSignature)
174: throws SerializationException {
175: serializer.serialize(this , instance, typeSignature);
176: }
177:
178: private native int getIntForInt(int key) /*-{
179: var result = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::objectMap[key];
180: return (result == null) ? -1 : result;
181: }-*/;
182:
183: // prefix needed to prevent conflict with built-in JavaScript properties.
184: private native int getIntForString(String key) /*-{
185: var result = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::stringMap[':' + key];
186: return (result == null) ? 0 : result;
187: }-*/;
188:
189: private native void setIntForInt(int key, int value) /*-{
190: this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::objectMap[key] = value;
191: }-*/;
192:
193: // prefix needed to prevent conflict with built-in JavaScript properties.
194: private native void setIntForString(String key, int value) /*-{
195: this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::stringMap[':' + key] = value;
196: }-*/;
197:
198: private void writeHeader(StringBuffer buffer) {
199: append(buffer, String.valueOf(getVersion()));
200: append(buffer, String.valueOf(getFlags()));
201: }
202:
203: private void writePayload(StringBuffer buffer) {
204: buffer.append(encodeBuffer.toString());
205: }
206:
207: private StringBuffer writeStringTable(StringBuffer buffer) {
208: int stringTableSize = stringTable.size();
209: append(buffer, String.valueOf(stringTableSize));
210: for (int i = 0; i < stringTableSize; ++i) {
211: append(buffer, stringTable.get(i));
212: }
213: return buffer;
214: }
215:
216: }
|