diff options
author | Gert Scholten <gscholt@gmail.com> | 2009-05-11 15:17:34 +0200 |
---|---|---|
committer | Gert Scholten <gscholt@gmail.com> | 2009-05-15 09:13:28 +0200 |
commit | f0f7d146fdcb303cd3312641517fbef8c65d5c1f (patch) | |
tree | bb2223edd98de868b742055599115361baaaf546 | |
parent | dce37e430c0fa2bbff9e2370249236e14e4064ca (diff) | |
download | gwtjsonrpc-f0f7d146fdcb303cd3312641517fbef8c65d5c1f.tar.gz |
Added suport for non-object return values using simple RPC.
(missing at the moment are HPC and CallbackHandle)
Signed-off-by: Gert Scholten <gscholt@gmail.com>
15 files changed, 579 insertions, 25 deletions
diff --git a/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java b/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java index 44ad157..a57cf8a 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java +++ b/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java @@ -40,7 +40,7 @@ public abstract class AbstractJsonProxy implements ServiceDefTarget { } protected <T> void doInvoke(final String methodName, final String reqData, - final JsonSerializer<T> ser, final AsyncCallback<T> cb) + final ResultDeserializer<T> ser, final AsyncCallback<T> cb) throws InvocationException { if (url == null) { throw new NoServiceEntryPointSpecifiedException(); diff --git a/src/main/java/com/google/gwtjsonrpc/client/ArrayResultDeserializer.java b/src/main/java/com/google/gwtjsonrpc/client/ArrayResultDeserializer.java new file mode 100644 index 0000000..55ae773 --- /dev/null +++ b/src/main/java/com/google/gwtjsonrpc/client/ArrayResultDeserializer.java @@ -0,0 +1,33 @@ +// 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.gwtjsonrpc.client; + +import com.google.gwt.core.client.JavaScriptObject; + +/** + * Base class for the {@link PrimitiveArrayResultDeserializer} and generated + * object array result deserializers. + */ +public abstract class ArrayResultDeserializer { + protected static native JavaScriptObject getResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + protected static native int getResultSize(JavaScriptObject responseObject) + /*-{ + return responseObject.result.length; + }-*/; +} diff --git a/src/main/java/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java index 90a3685..5a044ff 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JavaLangString_JsonSerializer.java @@ -14,9 +14,12 @@ package com.google.gwtjsonrpc.client; +import com.google.gwt.core.client.JavaScriptObject; + /** Default serialization for a String. */ public final class JavaLangString_JsonSerializer extends - JsonSerializer<java.lang.String> { + JsonSerializer<java.lang.String> implements + ResultDeserializer<java.lang.String> { public static final JavaLangString_JsonSerializer INSTANCE = new JavaLangString_JsonSerializer(); @@ -29,4 +32,9 @@ public final class JavaLangString_JsonSerializer extends public void printJson(final StringBuilder sb, final java.lang.String o) { sb.append(escapeString(o)); } + + @Override + public String fromResult(JavaScriptObject responseObject) { + return PrimitiveResultDeserializers.stringResult(responseObject); + } } diff --git a/src/main/java/com/google/gwtjsonrpc/client/JavaSqlDate_JsonSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/JavaSqlDate_JsonSerializer.java index fbf1ed9..cb26673 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JavaSqlDate_JsonSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JavaSqlDate_JsonSerializer.java @@ -14,9 +14,13 @@ package com.google.gwtjsonrpc.client; +import com.google.gwt.core.client.JavaScriptObject; + +import java.sql.Date; + /** Default serialization for a {@link java.sql.Date}. */ public final class JavaSqlDate_JsonSerializer extends - JsonSerializer<java.sql.Date> { + JsonSerializer<java.sql.Date> implements ResultDeserializer<java.sql.Date> { public static final JavaSqlDate_JsonSerializer INSTANCE = new JavaSqlDate_JsonSerializer(); @@ -69,4 +73,9 @@ public final class JavaSqlDate_JsonSerializer extends throw new IllegalArgumentException("Invalid escape format: " + s); } } + + @Override + public Date fromResult(JavaScriptObject responseObject) { + return fromJson(PrimitiveResultDeserializers.stringResult(responseObject)); + } } diff --git a/src/main/java/com/google/gwtjsonrpc/client/JavaSqlTimestamp_JsonSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/JavaSqlTimestamp_JsonSerializer.java index 719b692..01e6cf3 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JavaSqlTimestamp_JsonSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JavaSqlTimestamp_JsonSerializer.java @@ -14,11 +14,14 @@ package com.google.gwtjsonrpc.client; +import com.google.gwt.core.client.JavaScriptObject; + +import java.sql.Timestamp; import java.util.Date; /** Default serialization for a {@link java.sql.Timestamp}. */ public final class JavaSqlTimestamp_JsonSerializer extends - JsonSerializer<java.sql.Timestamp> { + JsonSerializer<java.sql.Timestamp> implements ResultDeserializer<java.sql.Timestamp> { public static final JavaSqlTimestamp_JsonSerializer INSTANCE = new JavaSqlTimestamp_JsonSerializer(); @@ -120,4 +123,9 @@ public final class JavaSqlTimestamp_JsonSerializer extends } } } + + @Override + public Timestamp fromResult(JavaScriptObject responseObject) { + return fromJson(PrimitiveResultDeserializers.stringResult(responseObject)); + } } diff --git a/src/main/java/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java index 384cb6d..da27685 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JavaUtilDate_JsonSerializer.java @@ -14,11 +14,13 @@ package com.google.gwtjsonrpc.client; +import com.google.gwt.core.client.JavaScriptObject; + import java.util.Date; /** Default serialization for a {@link java.util.Date}. */ public final class JavaUtilDate_JsonSerializer extends - JsonSerializer<java.util.Date> { + JsonSerializer<java.util.Date> implements ResultDeserializer<java.util.Date>{ public static final JavaUtilDate_JsonSerializer INSTANCE = new JavaUtilDate_JsonSerializer(); @@ -41,4 +43,9 @@ public final class JavaUtilDate_JsonSerializer extends private static Date parse(final String o) { return new java.util.Date(o); } + + @Override + public Date fromResult(JavaScriptObject responseObject) { + return fromJson(PrimitiveResultDeserializers.stringResult(responseObject)); + } } diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java index fe7d5bf..8ac7b3e 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java @@ -29,17 +29,17 @@ class JsonCall<T> implements RequestCallback { private final AbstractJsonProxy proxy; private final String methodName; private final String requestParams; - private final JsonSerializer<T> resultSerializer; + private final ResultDeserializer<T> resultDeserializer; private final AsyncCallback<T> callback; private int attempts; JsonCall(final AbstractJsonProxy abstractJsonProxy, final String methodName, - final String requestParams, final JsonSerializer<T> resultSerializer, + final String requestParams, final ResultDeserializer<T> resultDeserializer, final AsyncCallback<T> callback) { this.proxy = abstractJsonProxy; this.methodName = methodName; this.requestParams = requestParams; - this.resultSerializer = resultSerializer; + this.resultDeserializer = resultDeserializer; this.callback = callback; } @@ -115,7 +115,7 @@ class JsonCall<T> implements RequestCallback { if (sc == Response.SC_OK) { JsonUtil.fireOnCallEnd(); - JsonUtil.invoke(resultSerializer, callback, r.result()); + JsonUtil.invoke(resultDeserializer, callback, r); return; } } @@ -163,8 +163,6 @@ class JsonCall<T> implements RequestCallback { final native RpcError error()/*-{ return this.error; }-*/; - final native JavaScriptObject result()/*-{ return this.result; }-*/; - final native String xsrfKey()/*-{ return this.xsrfKey; }-*/; } diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java b/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java index 6ed732d..004cc59 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java +++ b/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java @@ -15,6 +15,7 @@ package com.google.gwtjsonrpc.client; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.InvocationException; import com.google.gwt.user.client.rpc.ServiceDefTarget; @@ -78,6 +79,20 @@ public class JsonUtil { } } + public static <T> void invoke(final ResultDeserializer<T> resultDeserializer, + final AsyncCallback<T> callback, final JavaScriptObject rpcResult) { + final T result; + try { + result = resultDeserializer.fromResult(rpcResult); + } catch (RuntimeException e) { + callback.onFailure(new InvocationException("Invalid JSON Response", e)); + return; + } + callback.onSuccess(result); + } + + // TODO: remove when the CallbackHandle supports primitive 'return' types? + // It is not called from anywhere in gwtjsonrpc, but it is a public method public static <T> void invoke(final JsonSerializer<T> resultSerializer, final AsyncCallback<T> callback, final Object encoded) { final T resobj; diff --git a/src/main/java/com/google/gwtjsonrpc/client/ObjectSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/ObjectSerializer.java index 211e76a..0c98cfc 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/ObjectSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/ObjectSerializer.java @@ -14,8 +14,11 @@ package com.google.gwtjsonrpc.client; +import com.google.gwt.core.client.JavaScriptObject; + /** Base class for generated JsonSerializer implementations. */ -public abstract class ObjectSerializer extends JsonSerializer<Object> { +public abstract class ObjectSerializer<T extends Object> extends + JsonSerializer<T> implements ResultDeserializer<T> { @Override public void printJson(final StringBuilder sb, final Object o) { sb.append("{"); @@ -24,4 +27,14 @@ public abstract class ObjectSerializer extends JsonSerializer<Object> { } protected abstract int printJsonImpl(int field, StringBuilder sb, Object o); + + @Override + public T fromResult(JavaScriptObject responseObject) { + final JavaScriptObject result = objectResult(responseObject); + return result == null ? null : fromJson(result); + } + + static native JavaScriptObject objectResult(JavaScriptObject responseObject) /*-{ + return responseObject.result; + }-*/; } diff --git a/src/main/java/com/google/gwtjsonrpc/client/PrimitiveArrayResultDeserializers.java b/src/main/java/com/google/gwtjsonrpc/client/PrimitiveArrayResultDeserializers.java new file mode 100644 index 0000000..ce22644 --- /dev/null +++ b/src/main/java/com/google/gwtjsonrpc/client/PrimitiveArrayResultDeserializers.java @@ -0,0 +1,90 @@ +// Copyright 2009 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.gwtjsonrpc.client; + +import com.google.gwt.core.client.JavaScriptObject; + +public class PrimitiveArrayResultDeserializers extends ArrayResultDeserializer { + public static ResultDeserializer<Boolean[]> BOOLEAN_INSTANCE = + new ResultDeserializer<Boolean[]>() { + @Override + public Boolean[] fromResult(JavaScriptObject responseObject) { + final Boolean[] tmp = new Boolean[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Byte[]> BYTE_INSTANCE = + new ResultDeserializer<Byte[]>() { + @Override + public Byte[] fromResult(JavaScriptObject responseObject) { + final Byte[] tmp = new Byte[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Character[]> CHARACTER_INSTANCE = + new ResultDeserializer<Character[]>() { + @Override + public Character[] fromResult(JavaScriptObject responseObject) { + final Character[] tmp = new Character[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Double[]> DOUBLE_INSTANCE = + new ResultDeserializer<Double[]>() { + @Override + public Double[] fromResult(JavaScriptObject responseObject) { + final Double[] tmp = new Double[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Float[]> FLOAT_INSTANCE = + new ResultDeserializer<Float[]>() { + @Override + public Float[] fromResult(JavaScriptObject responseObject) { + final Float[] tmp = new Float[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Integer[]> INTEGER_INSTANCE = + new ResultDeserializer<Integer[]>() { + @Override + public Integer[] fromResult(JavaScriptObject responseObject) { + final Integer[] tmp = new Integer[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; + public static ResultDeserializer<Short[]> SHORT_INSTANCE = + new ResultDeserializer<Short[]>() { + @Override + public Short[] fromResult(JavaScriptObject responseObject) { + final Short[] tmp = new Short[getResultSize(responseObject)]; + PrimitiveArraySerializer.INSTANCE.fromJson(getResult(responseObject), + tmp); + return tmp; + } + }; +} diff --git a/src/main/java/com/google/gwtjsonrpc/client/PrimitiveResultDeserializers.java b/src/main/java/com/google/gwtjsonrpc/client/PrimitiveResultDeserializers.java new file mode 100644 index 0000000..5725d08 --- /dev/null +++ b/src/main/java/com/google/gwtjsonrpc/client/PrimitiveResultDeserializers.java @@ -0,0 +1,108 @@ +// Copyright 2009 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.gwtjsonrpc.client; + +import com.google.gwt.core.client.JavaScriptObject; + +public class PrimitiveResultDeserializers { + static native boolean booleanResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static native byte byteResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static native String stringResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static char charResult(JavaScriptObject responseObject) { + return JsonSerializer.toChar(stringResult(responseObject)); + } + + static native double doubleResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static native float floatResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static native int intResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + static native short shortResult(JavaScriptObject responseObject) + /*-{ + return responseObject.result; + }-*/; + + public static final ResultDeserializer<Boolean> BOOLEAN_INSTANCE = + new ResultDeserializer<Boolean>() { + @Override + public Boolean fromResult(JavaScriptObject responseObject) { + return booleanResult(responseObject); + } + }; + public static final ResultDeserializer<Byte> BYTE_INSTANCE = + new ResultDeserializer<Byte>() { + @Override + public Byte fromResult(JavaScriptObject responseObject) { + return byteResult(responseObject); + } + }; + public static final ResultDeserializer<Character> CHARACTER_INSTANCE = + new ResultDeserializer<Character>() { + @Override + public Character fromResult(JavaScriptObject responseObject) { + return charResult(responseObject); + } + }; + public static final ResultDeserializer<Double> DOUBLE_INSTANCE = + new ResultDeserializer<Double>() { + @Override + public Double fromResult(JavaScriptObject responseObject) { + return doubleResult(responseObject); + } + }; + public static final ResultDeserializer<Float> FLOAT_INSTANCE = + new ResultDeserializer<Float>() { + @Override + public Float fromResult(JavaScriptObject responseObject) { + return floatResult(responseObject); + } + }; + public static final ResultDeserializer<Integer> INTEGER_INSTANCE = + new ResultDeserializer<Integer>() { + @Override + public Integer fromResult(JavaScriptObject responseObject) { + return intResult(responseObject); + } + }; + public static final ResultDeserializer<Short> SHORT_INSTANCE = + new ResultDeserializer<Short>() { + @Override + public Short fromResult(JavaScriptObject responseObject) { + return shortResult(responseObject); + } + }; +} diff --git a/src/main/java/com/google/gwtjsonrpc/client/ResultDeserializer.java b/src/main/java/com/google/gwtjsonrpc/client/ResultDeserializer.java new file mode 100644 index 0000000..87b0bbc --- /dev/null +++ b/src/main/java/com/google/gwtjsonrpc/client/ResultDeserializer.java @@ -0,0 +1,28 @@ +// Copyright 2009 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.gwtjsonrpc.client; + +import com.google.gwt.core.client.JavaScriptObject; + +/** + * Inteface class for deserializers of results from JSON RPC calls. Since + * primitive and array results need to be handled specially, not all results can + * be deserialized using the standard object serializers. + * + * @param <T> the result type of an RPC call. + */ +public interface ResultDeserializer<T> { + public T fromResult(JavaScriptObject responseObject); +} diff --git a/src/main/java/com/google/gwtjsonrpc/client/VoidResult_JsonSerializer.java b/src/main/java/com/google/gwtjsonrpc/client/VoidResult_JsonSerializer.java index 9bf444b..d0ab55b 100644 --- a/src/main/java/com/google/gwtjsonrpc/client/VoidResult_JsonSerializer.java +++ b/src/main/java/com/google/gwtjsonrpc/client/VoidResult_JsonSerializer.java @@ -14,7 +14,10 @@ package com.google.gwtjsonrpc.client; -public class VoidResult_JsonSerializer extends JsonSerializer<VoidResult> { +import com.google.gwt.core.client.JavaScriptObject; + +public class VoidResult_JsonSerializer extends JsonSerializer<VoidResult> + implements ResultDeserializer<VoidResult> { public static final VoidResult_JsonSerializer INSTANCE = new VoidResult_JsonSerializer(); @@ -30,4 +33,9 @@ public class VoidResult_JsonSerializer extends JsonSerializer<VoidResult> { public VoidResult fromJson(final Object o) { return VoidResult.INSTANCE; } + + @Override + public VoidResult fromResult(JavaScriptObject responseObject) { + return VoidResult.INSTANCE; + } } diff --git a/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java b/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java index 16b1f23..f5ad548 100644 --- a/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java +++ b/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java @@ -45,6 +45,7 @@ class ProxyCreator { private JClassType svcInf; private JClassType asyncCallbackClass; private SerializerCreator serializerCreator; + private ResultDeserializerCreator deserializerCreator; private int instanceField; ProxyCreator(final JClassType remoteService) { @@ -54,6 +55,7 @@ class ProxyCreator { String create(final TreeLogger logger, final GeneratorContext context) throws UnableToCompleteException { serializerCreator = new SerializerCreator(context); + deserializerCreator = new ResultDeserializerCreator(context, serializerCreator); final TypeOracle typeOracle = context.getTypeOracle(); try { asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName()); @@ -143,7 +145,8 @@ class ProxyCreator { logger.branch(TreeLogger.DEBUG, m.getName() + ", parameter " + p.getName()); serializerCreator.checkCanSerialize(branch, p.getType()); - if (p.getType().isPrimitive() == null && !SerializerCreator.isBoxedPrimitive(p.getType())) { + if (p.getType().isPrimitive() == null + && !SerializerCreator.isBoxedPrimitive(p.getType())) { serializerCreator.create((JClassType) p.getType(), branch); } } @@ -151,13 +154,25 @@ class ProxyCreator { final TreeLogger branch = logger.branch(TreeLogger.DEBUG, m.getName() + ", result " + resultType.getQualifiedSourceName()); - // For now, extra-check on result deserialisation - if (resultType.isArray() != null || SerializerCreator.isJsonPrimitive(resultType) || SerializerCreator.isBoxedPrimitive(resultType)) - invalid(branch, "(Boxed)Primitives and Arrays are not allowed as result values"); - else { - serializerCreator.checkCanSerialize(branch, resultType); - serializerCreator.create(resultType, branch); + // For now, extra-check on result deserialisation using CbHandles or HPC + if (returnsCallbackHandle(m) + || m.getAnnotation(HostPageCache.class) != null) { + if (resultType.isArray() != null + || SerializerCreator.isJsonPrimitive(resultType) + || SerializerCreator.isBoxedPrimitive(resultType)) + invalid(branch, "GwtJsonRpc does not support (Boxed)Primitives or " + + "Arrays as result values using a CallbackHandle " + + "or HostPageCache"); } + serializerCreator.checkCanSerialize(branch, resultType); + if (resultType.isArray() != null) { + // Arrays need a special deserializer + deserializerCreator.create(branch, resultType.isArray()); + } else if (resultType.isPrimitive() == null + && !SerializerCreator.isBoxedPrimitive(resultType)) + // Non primitives get deserialized by their normal serializer + serializerCreator.create(resultType, branch); + // (Boxed)Primitives are left, they are handled specially } } @@ -226,7 +241,7 @@ class ProxyCreator { w.println(";"); } } - if (SerializerCreator.needsTypeParameter(resultType)) { + if (resultType.isParameterized() != null) { serializerFields[params.length - 1] = "serializer_" + instanceField++; w.print("private static final "); w.print(JsonSerializer.class.getName()); @@ -264,6 +279,7 @@ class ProxyCreator { w.println(") {"); w.indent(); + // TODO: add support for non-object return values with a CallbackHandle if (returnsCallbackHandle(method)) { w.print("return new "); w.print(CallbackHandle.class.getName()); @@ -280,6 +296,7 @@ class ProxyCreator { return; } + // TODO: add support for non-object return values using HostPageCache if (hpc != null) { final String objName = nameFactory.createName("cached"); w.print("final Object " + objName + " = "); @@ -320,12 +337,14 @@ class ProxyCreator { final JType pType = params[i].getType(); final String pName = params[i].getName(); - if (pType == JPrimitiveType.CHAR || SerializerCreator.isBoxedCharacter(pType)) { + if (pType == JPrimitiveType.CHAR + || SerializerCreator.isBoxedCharacter(pType)) { w.println(reqData + ".append(\"\\\"\");"); w.println(reqData + ".append(" + JsonSerializer.class.getSimpleName() + ".escapeChar(" + pName + "));"); w.println(reqData + ".append(\"\\\"\");"); - } else if ((SerializerCreator.isJsonPrimitive(pType) || SerializerCreator.isBoxedPrimitive(pType)) + } else if ((SerializerCreator.isJsonPrimitive(pType) || SerializerCreator + .isBoxedPrimitive(pType)) && !SerializerCreator.isJsonString(pType)) { w.println(reqData + ".append(" + pName + ");"); } else { @@ -353,10 +372,10 @@ class ProxyCreator { w.print("\"" + method.getName() + "\""); w.print(", " + reqDataStr); w.print(", "); - if (SerializerCreator.needsTypeParameter(resultType)) { + if (resultType.isParameterized() != null) { w.print(serializerFields[params.length - 1]); } else { - serializerCreator.generateSerializerReference(resultType, w); + deserializerCreator.generateDeserializerReference(resultType, w); } w.print(", " + callback.getName()); w.println(");"); diff --git a/src/main/java/com/google/gwtjsonrpc/rebind/ResultDeserializerCreator.java b/src/main/java/com/google/gwtjsonrpc/rebind/ResultDeserializerCreator.java new file mode 100644 index 0000000..69477c0 --- /dev/null +++ b/src/main/java/com/google/gwtjsonrpc/rebind/ResultDeserializerCreator.java @@ -0,0 +1,210 @@ +// Copyright 2009 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.gwtjsonrpc.rebind; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JArrayType; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.google.gwtjsonrpc.client.ArrayResultDeserializer; +import com.google.gwtjsonrpc.client.JavaLangString_JsonSerializer; +import com.google.gwtjsonrpc.client.PrimitiveArrayResultDeserializers; +import com.google.gwtjsonrpc.client.PrimitiveResultDeserializers; +import com.google.gwtjsonrpc.client.ResultDeserializer; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * Creator of ResultDeserializers. Actually, only object arrays have created + * deserializers: + * <ul> + * <li>Boxed primitives are handled by {@link PrimitiveResultDeserializers} + * <li>Normal objects have their (generated) serializers extending + * {@link com.google.gwtjsonrpc.client.ObjectSerializer}, that handle result + * deserialisation as well. + * <li>Arrays of (boxed) primitives are handled by + * {@link PrimitiveArrayResultDeserializers}. + * <li>And object arrays get a generated deserializer extending + * {@link ArrayResultDeserializer} + * </ul> + * All object arrays that have a JSONSerializer for the array component can be + * generated, but they will need to live in the same package as the serializer. + * To do this, if the serializer lives in the + * <code>com.google.gwtjsonrpc.client</code> package (where custom object + * serializers live), the ResultDeserializer for it's array will be placed in + * this package as well. Else it will be placed with the serializer in the + * package the object lives. + */ +class ResultDeserializerCreator { + private static final String DSER_SUFFIX = "_ResultDeserializer"; + private static final String CUSTOM_SERIALIZER_PACKAGE = + JavaLangString_JsonSerializer.class.getPackage().getName(); + + private GeneratorContext context; + private HashMap<String, String> generatedDeserializers; + private SerializerCreator serializerCreator; + + private JArrayType targetType; + private JType componentType; + + ResultDeserializerCreator(GeneratorContext c, SerializerCreator sc) { + context = c; + generatedDeserializers = new HashMap<String, String>(); + serializerCreator = sc; + } + + void create(TreeLogger logger, JArrayType targetType) + throws UnableToCompleteException { + this.targetType = targetType; + this.componentType = targetType.getComponentType(); + + if (componentType.isPrimitive() != null + || SerializerCreator.isBoxedPrimitive(componentType)) { + logger.log(TreeLogger.DEBUG, + "No need to create array deserializer for primitive array " + + targetType); + return; + } + + if (deserializerFor(targetType) != null) { + return; + } + + logger.log(TreeLogger.DEBUG, "Creating result deserializer for " + + targetType.getSimpleSourceName()); + final SourceWriter srcWriter = getSourceWriter(logger, context); + if (srcWriter == null) { + return; + } + final String dsn = getDeserializerQualifiedName(targetType); + generatedDeserializers.put(targetType.getQualifiedSourceName(), dsn); + + generateSingleton(srcWriter); + generateInstanceMembers(srcWriter); + generateFromResult(srcWriter); + + srcWriter.commit(logger); + } + + private void generateSingleton(final SourceWriter w) { + w.print("public static final "); + w.print(getDeserializerSimpleName(targetType)); + w.print(" INSTANCE = new "); + w.print(getDeserializerSimpleName(targetType)); + w.println("();"); + w.println(); + } + + private void generateInstanceMembers(SourceWriter w) { + w.print("private final "); + w.print(serializerCreator.serializerFor(targetType)); + w.print(" "); + w.print("serializer"); + w.print(" = "); + serializerCreator.generateSerializerReference(targetType, w); + w.println(";"); + w.println(); + } + + private void generateFromResult(SourceWriter w) { + final String ctn = componentType.getQualifiedSourceName(); + + w.println("@Override"); + w.print("public " + ctn + "[] "); + w.println("fromResult(JavaScriptObject responseObject) {"); + w.indent(); + + w.print("final " + ctn + "[] tmp = new " + ctn); + w.println("[getResultSize(responseObject)];"); + + w.println("serializer.fromJson(getResult(responseObject), tmp);"); + w.println("return tmp;"); + w.outdent(); + + w.println("}"); + } + + private String getDeserializerQualifiedName(JArrayType targetType) { + final String pkgName = getDeserializerPackageName(targetType); + final String className = getDeserializerSimpleName(targetType); + return pkgName.length() == 0 ? className : pkgName + "." + className; + } + + private String getDeserializerPackageName(JArrayType targetType) { + // Place array deserializer in same package as the component deserializer + final String compSerializer = + serializerCreator.serializerFor(targetType.getComponentType()); + final int end = compSerializer.lastIndexOf('.'); + return end >= 0 ? compSerializer.substring(0, end) : ""; + } + + private static String getDeserializerSimpleName(JClassType targetType) { + return ProxyCreator.synthesizeTopLevelClassName(targetType, DSER_SUFFIX)[1]; + } + + private SourceWriter getSourceWriter(TreeLogger logger, + GeneratorContext context) { + String pkgName = getDeserializerPackageName(targetType); + final String simpleName = getDeserializerSimpleName(targetType); + final PrintWriter pw; + final ClassSourceFileComposerFactory cf; + + pw = context.tryCreate(logger, pkgName, simpleName); + if (pw == null) { + return null; + } + + cf = new ClassSourceFileComposerFactory(pkgName, simpleName); + cf.addImport(JavaScriptObject.class.getCanonicalName()); + cf.addImport(ResultDeserializer.class.getCanonicalName()); + + cf.setSuperclass(ArrayResultDeserializer.class.getCanonicalName()); + cf.addImplementedInterface(ResultDeserializer.class.getCanonicalName() + + "<" + targetType.getQualifiedSourceName() + ">"); + + return cf.createSourceWriter(context, pw); + } + + private String deserializerFor(JArrayType targetType) { + final JType componentType = targetType.getComponentType(); + // Custom primitive deserializers + if (SerializerCreator.isBoxedPrimitive(componentType)) + return PrimitiveArrayResultDeserializers.class.getCanonicalName() + "." + + componentType.getSimpleSourceName().toUpperCase() + "_INSTANCE"; + final String name = + generatedDeserializers.get(targetType.getQualifiedSourceName()); + + return name == null ? null : name + ".INSTANCE"; + } + + public void generateDeserializerReference(JType targetType, SourceWriter w) { + if (SerializerCreator.isBoxedPrimitive(targetType)) { + w.print(PrimitiveResultDeserializers.class.getCanonicalName()); + w.print("."); + w.print(targetType.getSimpleSourceName().toUpperCase()); + w.print("_INSTANCE"); + } else if (targetType.isArray() != null) { + w.print(deserializerFor(targetType.isArray())); + } else { + serializerCreator.generateSerializerReference(targetType, w); + } + } +} |