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