001 /*
002 * Copyright 2006 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.rebind.rpc;
017
018 import com.google.gwt.core.ext.GeneratorContext;
019 import com.google.gwt.core.ext.TreeLogger;
020 import com.google.gwt.core.ext.typeinfo.JArrayType;
021 import com.google.gwt.core.ext.typeinfo.JClassType;
022 import com.google.gwt.core.ext.typeinfo.JField;
023 import com.google.gwt.core.ext.typeinfo.JType;
024 import com.google.gwt.core.ext.typeinfo.TypeOracle;
025 import com.google.gwt.user.client.rpc.SerializationStreamReader;
026 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
027 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
028 import com.google.gwt.user.rebind.SourceWriter;
029
030 import java.io.IOException;
031 import java.io.PrintWriter;
032
033 /**
034 * <p>
035 * This class creates field serializers for a particular class. If the class has
036 * a custom serializer then that class is used rather than creating one.
037 *
038 * <p>
039 * Generated field serializers are emitted into the same package as the class
040 * that they serialize.
041 *
042 * <p>
043 * Fields are considered serializable if:
044 * <ul>
045 * <li>Field is not static
046 * <li>Field is not transient
047 * </ul>
048 *
049 * TODO(mmendez): Need to make the generated field serializers final
050 * TODO(mmendez): Would be nice to be able to have imports, rather than using
051 * fully qualified type names everywhere
052 */
053 public class FieldSerializerCreator {
054 private JType fSerializableClass;
055 private JField[] fSerializableFields;
056 private SerializableTypeOracle fSerializationOracle;
057 private SourceWriter fSourceWriter;
058 private JClassType fObjectType;
059
060 public FieldSerializerCreator(SerializableTypeOracle serializationOracle,
061 JType requestedClass) {
062 setSerializationOracle(serializationOracle);
063 fSerializableClass = requestedClass;
064 }
065
066 public String realize(TreeLogger logger, GeneratorContext ctx)
067 throws IOException {
068 assert (ctx != null);
069 assert (fSerializationOracle.isSerializable(getSerializableClass()));
070
071 logger = logger.branch(TreeLogger.SPAM, "Generating a field serializer for type '"
072 + fSerializableClass.getQualifiedSourceName() + "'", null);
073 String fieldSerializerName = fSerializationOracle
074 .getFieldSerializerName(getSerializableClass());
075
076 fSourceWriter = getSourceWriter(logger, ctx);
077 if (fSourceWriter == null) {
078 return fieldSerializerName;
079 }
080 assert fSourceWriter != null;
081
082 writeFieldAccessors();
083
084 writeSerializeMethod(ctx);
085
086 writeDeserializeMethod(ctx);
087
088 fSourceWriter.commit(logger);
089
090 return fieldSerializerName;
091 }
092
093 private JType getSerializableClass() {
094 return fSerializableClass;
095 }
096
097 private String getSimpleSourceName(JType type) {
098 String sourceName = fSerializationOracle.getFieldSerializerName(type);
099
100 int qualifiedSourceNameStart = sourceName.lastIndexOf('.');
101 if (qualifiedSourceNameStart >= 0) {
102 sourceName = sourceName.substring(qualifiedSourceNameStart + 1);
103 }
104
105 return sourceName;
106 }
107
108 /**
109 * Returns true if we will need get/set method pair for a field. These are
110 * required when the field would not be accessible to the serializer class.
111 */
112 private boolean needsAccessorMethods(JField field) {
113 boolean needsAccessors = !field.isPublic();
114 return needsAccessors;
115 }
116
117 /**
118 * Return an array of fields declared by this class that are serializable
119 */
120 private JField[] applyFieldSerializationPolicy() {
121 if (fSerializableFields != null) {
122 return fSerializableFields;
123 }
124
125 fSerializableFields = fSerializationOracle
126 .applyFieldSerializationPolicy(getSerializableClass());
127 return fSerializableFields;
128 }
129
130 /**
131 * This method will generate a native JSNI accessor method for every field
132 * that is protected, private using the "Violator" pattern to allow an
133 * external class to access the field's value.
134 */
135 private void writeFieldAccessors() {
136 JField[] serializableFields = applyFieldSerializationPolicy();
137 int fieldCount = serializableFields.length;
138
139 for (int fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) {
140 JField serializableField = serializableFields[fieldIndex];
141
142 if (!needsAccessorMethods(serializableField)) {
143 continue;
144 }
145
146 writeFieldGet(serializableField);
147 writeFieldSet(serializableField);
148 }
149 }
150
151 /**
152 * Write a setter method for an instance field.
153 */
154 private void writeFieldSet(JField serializableField) {
155 JType fieldType = serializableField.getType();
156 String fieldTypeQualifiedSourceName = fieldType.getQualifiedSourceName();
157 String serializableClassQualifedName = getSerializableClass()
158 .getQualifiedSourceName();
159 String fieldName = serializableField.getName();
160
161 fSourceWriter.print("private static native void ");
162 fSourceWriter.print(" set");
163 fSourceWriter.print(capitalize(fieldName));
164 fSourceWriter.print("(");
165 fSourceWriter.print(serializableClassQualifedName);
166 fSourceWriter.print(" instance, ");
167 fSourceWriter.print(fieldTypeQualifiedSourceName);
168 fSourceWriter.println(" value) /*-{");
169 fSourceWriter.indent();
170
171 fSourceWriter.print("instance.@");
172 fSourceWriter.print(serializableClassQualifedName);
173 fSourceWriter.print("::");
174 fSourceWriter.print(fieldName);
175 fSourceWriter.println(" = value;");
176
177 fSourceWriter.outdent();
178 fSourceWriter.println("}-*/;");
179 fSourceWriter.println();
180 }
181
182 /**
183 * Write a getter method for an instance field.
184 */
185 private void writeFieldGet(JField serializableField) {
186 JType fieldType = serializableField.getType();
187 String fieldTypeQualifiedSourceName = fieldType.getQualifiedSourceName();
188 String serializableClassQualifedName = getSerializableClass()
189 .getQualifiedSourceName();
190 String fieldName = serializableField.getName();
191
192 fSourceWriter.print("private static native ");
193 fSourceWriter.print(fieldTypeQualifiedSourceName);
194 fSourceWriter.print(" get");
195 fSourceWriter.print(capitalize(fieldName));
196 fSourceWriter.print("(");
197 fSourceWriter.print(getSerializableClass().getQualifiedSourceName());
198 fSourceWriter.println(" instance) /*-{");
199 fSourceWriter.indent();
200
201 fSourceWriter.print("return instance.@");
202 fSourceWriter.print(serializableClassQualifedName);
203 fSourceWriter.print("::");
204 fSourceWriter.print(fieldName);
205 fSourceWriter.println(";");
206
207 fSourceWriter.outdent();
208 fSourceWriter.println("}-*/;");
209 fSourceWriter.println();
210 }
211
212 private String capitalize(String name) {
213 String s = name.substring(0, 1).toUpperCase() + name.substring(1);
214 return s;
215 }
216
217 private void writeDeserializeMethod(GeneratorContext ctx) {
218 fSourceWriter.print("public static void deserialize(");
219 fSourceWriter.print(SerializationStreamReader.class.getName());
220 fSourceWriter.print(" streamReader, ");
221 fSourceWriter.print(getSerializableClass().getQualifiedSourceName());
222 fSourceWriter.println(" instance) throws "
223 + Shared.SERIALIZATION_EXCEPTION_CLASS_NAME + "{");
224 fSourceWriter.indent();
225
226 writeFieldDeserializationStatements(ctx);
227
228 JClassType serializableClass = getSerializableClass().isClass();
229 if (serializableClass != null) {
230 JClassType serializableSuperClass = getSerializableSuperclass(serializableClass);
231 if (serializableSuperClass != null) {
232 String fieldSerializerName = fSerializationOracle
233 .getFieldSerializerName(serializableSuperClass);
234 fSourceWriter.print(fieldSerializerName);
235 fSourceWriter.println(".deserialize(streamReader, instance);");
236 }
237 }
238
239 fSourceWriter.outdent();
240 fSourceWriter.println("}");
241 fSourceWriter.println();
242 }
243
244 /**
245 * Given a class return the next superclass that is serializable.
246 *
247 * @param serializableClass
248 * @return
249 */
250 private JClassType getSerializableSuperclass(JClassType serializableClass) {
251 if (serializableClass == null) {
252 return null;
253 }
254
255 JClassType superClass = serializableClass.getSuperclass();
256 if (superClass != null) {
257 SerializableTypeOracle serializationOracle = getSerializationOracle();
258 if (serializationOracle.isSerializable(superClass)) {
259 return superClass;
260 }
261
262 return getSerializableSuperclass(superClass);
263 }
264
265 return null;
266 }
267
268 private void writeFieldDeserializationStatements(GeneratorContext ctx) {
269 JType type = getSerializableClass();
270 JArrayType isArray = type.isArray();
271 if (isArray != null) {
272 fSourceWriter
273 .println("for (int itemIndex = 0; itemIndex < instance.length; ++itemIndex) {");
274 fSourceWriter.indent();
275
276 JType componentType = isArray.getComponentType();
277 JType normalizedType = normalizeFieldType(ctx, componentType);
278 fSourceWriter.print("instance[itemIndex] = ");
279 if (normalizedType != componentType) {
280 fSourceWriter
281 .print("(" + componentType.getQualifiedSourceName() + ") ");
282 }
283 String readMethodName = "streamReader.read"
284 + capitalize(normalizedType.getSimpleSourceName());
285 fSourceWriter.println(readMethodName + "();");
286 fSourceWriter.outdent();
287 fSourceWriter.println("}");
288 } else {
289 JField[] serializableFields = applyFieldSerializationPolicy();
290 int fieldCount = serializableFields.length;
291
292 for (int fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) {
293 JField serializableField = serializableFields[fieldIndex];
294 JType fieldType = serializableField.getType();
295 JType normalizedFieldType = normalizeFieldType(ctx, fieldType);
296 boolean needsAccessor = needsAccessorMethods(serializableField);
297 boolean needsCast = fieldType != normalizedFieldType;
298
299 String readMethodName = "read"
300 + capitalize(normalizedFieldType.getSimpleSourceName());
301 String streamReadExpression = "streamReader." + readMethodName + "()";
302 if (needsCast) {
303 streamReadExpression = "(" + fieldType.getQualifiedSourceName()
304 + ") " + streamReadExpression;
305 }
306
307 if (needsAccessor) {
308 fSourceWriter.print("set");
309 fSourceWriter.print(capitalize(serializableField.getName()));
310 fSourceWriter.print("(instance, ");
311 fSourceWriter.print(streamReadExpression);
312 fSourceWriter.println(");");
313 } else {
314 fSourceWriter.print("instance.");
315 fSourceWriter.print(serializableField.getName());
316 fSourceWriter.print(" = ");
317 fSourceWriter.print(streamReadExpression);
318 fSourceWriter.println(";");
319 }
320 }
321 }
322
323 fSourceWriter.println();
324 }
325
326 private void writeSerializeMethod(GeneratorContext ctx) {
327 fSourceWriter.print("public static void serialize(");
328 fSourceWriter.print(SerializationStreamWriter.class.getName());
329 fSourceWriter.print(" streamWriter, ");
330 fSourceWriter.print(getSerializableClass().getQualifiedSourceName());
331 fSourceWriter.println(" instance) throws "
332 + Shared.SERIALIZATION_EXCEPTION_CLASS_NAME + " {");
333 fSourceWriter.indent();
334
335 writeFieldSerializationStatements(ctx);
336
337 JClassType serializableClass = getSerializableClass().isClass();
338 if (serializableClass != null) {
339 JClassType serializableSuperclass = getSerializableSuperclass(serializableClass);
340 if (serializableSuperclass != null) {
341 String fieldSerializerName = fSerializationOracle
342 .getFieldSerializerName(serializableSuperclass);
343 fSourceWriter.print(fieldSerializerName);
344 fSourceWriter.println(".serialize(streamWriter, instance);");
345 }
346 }
347
348 fSourceWriter.outdent();
349 fSourceWriter.println("}");
350 fSourceWriter.println();
351 }
352
353 private void writeFieldSerializationStatements(GeneratorContext ctx) {
354 JType type = getSerializableClass();
355 JArrayType isArray = type.isArray();
356 if (isArray != null) {
357 fSourceWriter.println("int itemCount = instance.length;");
358 fSourceWriter.println();
359 fSourceWriter.println("streamWriter.writeInt(itemCount);");
360 fSourceWriter.println();
361 fSourceWriter
362 .println("for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) {");
363 fSourceWriter.indent();
364 JType normalizedType = normalizeFieldType(ctx, isArray.getComponentType());
365 String writeMethodName = "write"
366 + capitalize(normalizedType.getSimpleSourceName());
367 fSourceWriter.print("streamWriter.");
368 fSourceWriter.print(writeMethodName);
369 fSourceWriter.println("(instance[itemIndex]);");
370 fSourceWriter.outdent();
371 fSourceWriter.println("}");
372 } else {
373 JField[] serializableFields = applyFieldSerializationPolicy();
374 int fieldCount = serializableFields.length;
375
376 for (int fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) {
377 JField serializableField = serializableFields[fieldIndex];
378 JType fieldType = serializableField.getType();
379 JType normalizedType = normalizeFieldType(ctx, fieldType);
380
381 String writeMethodName = "write"
382 + capitalize(normalizedType.getSimpleSourceName());
383 fSourceWriter.print("streamWriter.");
384 fSourceWriter.print(writeMethodName);
385 fSourceWriter.print("(");
386
387 if (needsAccessorMethods(serializableField)) {
388 fSourceWriter.print("get");
389 fSourceWriter.print(capitalize(serializableField.getName()));
390 fSourceWriter.println("(instance));");
391 } else {
392 fSourceWriter.print("instance.");
393 fSourceWriter.print(serializableField.getName());
394 fSourceWriter.println(");");
395 }
396 }
397 }
398
399 fSourceWriter.println();
400 }
401
402 /*
403 * Given a field's type return the type that can be handled via the
404 * SerializationStreamReader and SerializationStreamWriter interfaces.
405 */
406 private JType normalizeFieldType(GeneratorContext ctx, JType fieldType) {
407 assert (fieldType != null);
408
409 if (fieldType.isPrimitive() == null) {
410 TypeOracle typeOracle = ctx.getTypeOracle();
411 if (fObjectType == null) {
412 // Lazily init & cache JClassType for java.lang.Object
413 fObjectType = typeOracle.findType("java.lang.Object");
414
415 if (fObjectType == null) {
416 throw new RuntimeException("Unable to locate the definition of java.lang.Object");
417 }
418 }
419
420 fieldType = fObjectType;
421 }
422
423 return fieldType;
424 }
425
426 private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx)
427 throws IOException {
428 String packageName = getSerializableClassPackageName();
429 String className = getSimpleSourceName(getSerializableClass());
430
431 PrintWriter printWriter = ctx.tryCreate(logger, packageName, className);
432 if (printWriter == null) {
433 return null;
434 }
435
436 ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
437 packageName, className);
438
439 return composerFactory.createSourceWriter(ctx, printWriter);
440 }
441
442 private String getSerializableClassPackageName() {
443 JType type = getSerializableClass();
444 String packageName = fSerializationOracle.getFieldSerializerName(type);
445 int packageNameEnd = packageName.lastIndexOf('.');
446 if (packageNameEnd >= 0) {
447 packageName = packageName.substring(0, packageNameEnd);
448 }
449
450 return packageName;
451 }
452
453 private SerializableTypeOracle getSerializationOracle() {
454 return fSerializationOracle;
455 }
456
457 private void setSerializationOracle(SerializableTypeOracle serializationOracle) {
458 fSerializationOracle = serializationOracle;
459 }
460 }