throw new UnableToCompleteException();
}
returnRequest = true;
}
Json jsonAnnotation = getAnnotation(source, Json.class);
final Style classStyle = jsonAnnotation != null ? jsonAnnotation.style() : Style.DEFAULT;
Options classOptions = getAnnotation(source, Options.class);
Options options = getAnnotation(method, Options.class);
p(method.getReadableDeclaration(false, false, false, false, true) + " {").i(1);
{
String restMethod = getRestMethod(method);
LinkedList<JParameter> args = new LinkedList<JParameter>(Arrays.asList(method.getParameters()));
for (final JParameter arg : args.subList(0, args.size() - 1)) {
p("final "
+ arg.getType().getParameterizedQualifiedSourceName()
+ " final_" + arg.getName() + " = " + arg.getName()
+ ";");
}
// the last arg should be the callback.
if (args.isEmpty()) {
getLogger().log(ERROR, "Invalid rest method. Method must declare at least a callback argument: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
JParameter callbackArg = args.removeLast();
JClassType callbackType = callbackArg.getType().isClassOrInterface();
JClassType methodCallbackType = METHOD_CALLBACK_TYPE;
if (callbackType == null || !callbackType.isAssignableTo(methodCallbackType)) {
getLogger().log(ERROR, "Invalid rest method. Last argument must be a " + methodCallbackType.getName() + " type: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
JClassType resultType = getCallbackTypeGenericClass(callbackType);
String pathExpression = null;
Path pathAnnotation = getAnnotation(method, Path.class);
if (pathAnnotation != null) {
pathExpression = wrap(pathAnnotation.value());
}
JParameter contentArg = null;
HashMap<String, JParameter> queryParams = new HashMap<String, JParameter>();
HashMap<String, JParameter> formParams = new HashMap<String, JParameter>();
HashMap<String, JParameter> headerParams = new HashMap<String, JParameter>();
for (JParameter arg : args) {
PathParam paramPath = getAnnotation(arg, PathParam.class);
if (paramPath != null) {
if (pathExpression == null) {
getLogger().log(ERROR, "Invalid rest method. Invalid @PathParam annotation. Method is missing the @Path annotation: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
pathExpression = pathExpression(pathExpression, arg, paramPath);
//.replaceAll(Pattern.quote("{" + paramPath.value() + "}"), "\"+com.google.gwt.http.client.URL.encodePathSegment(" + toStringExpression(arg) + ")+\"");
if (getAnnotation(arg, Attribute.class) != null) {
// allow part of the arg-object participate in as PathParam and the object goes over the wire
contentArg = arg;
}
continue;
}
QueryParam queryParam = getAnnotation(arg, QueryParam.class);
if (queryParam != null) {
queryParams.put(queryParam.value(), arg);
continue;
}
FormParam formParam = getAnnotation(arg, FormParam.class);
if (formParam != null) {
formParams.put(formParam.value(), arg);
continue;
}
HeaderParam headerParam = getAnnotation(arg, HeaderParam.class);
if (headerParam != null) {
headerParams.put(headerParam.value(), arg);
continue;
}
if (!formParams.isEmpty()) {
getLogger().log(ERROR, "You can not have both @FormParam parameters and a content parameter: " +
method.getReadableDeclaration());
throw new UnableToCompleteException();
}
if (contentArg != null) {
getLogger().log(ERROR, "Invalid rest method. Only one content parameter is supported: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
contentArg = arg;
}
String acceptTypeBuiltIn = null;
if (callbackType.equals(TEXT_CALLBACK_TYPE)) {
acceptTypeBuiltIn = "CONTENT_TYPE_TEXT";
} else if (callbackType.equals(JSON_CALLBACK_TYPE)) {
acceptTypeBuiltIn = "CONTENT_TYPE_JSON";
} else if (callbackType.isAssignableTo(OVERLAY_CALLBACK_TYPE)) {
acceptTypeBuiltIn = "CONTENT_TYPE_JSON";
} else if (callbackType.equals(XML_CALLBACK_TYPE)) {
acceptTypeBuiltIn = "CONTENT_TYPE_XML";
}
p("final " + METHOD_CLASS + " __method =");
p("getResource()");
if (pathExpression != null) {
p(".resolve(" + pathExpression + ")");
}
for (Map.Entry<String, JParameter> entry : queryParams.entrySet()) {
String expr = entry.getValue().getName();
JClassType type = entry.getValue().getType().isClassOrInterface();
if (type != null && isQueryParamListType(type)) {
p(".addQueryParams(" + wrap(entry.getKey()) + ", " +
toIteratedStringExpression(entry.getValue()) + ")");
} else {
p(".addQueryParam(" + wrap(entry.getKey()) + ", " +
toStringExpression(entry.getValue().getType(), expr) + ")");
}
}
// example: .get()
p("." + restMethod + "();");
// Handle JSONP specific configuration...
JSONP jsonpAnnotation = getAnnotation(method, JSONP.class);
final boolean isJsonp = restMethod.equals(METHOD_JSONP) && jsonpAnnotation!=null;
if( isJsonp ) {
if (returnRequest && !method.getReturnType().getQualifiedSourceName().equals(JsonpRequest.class.getName())) {
getLogger().log(ERROR, "Invalid rest method. JSONP method must have void or JsonpRequest return types: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
if( jsonpAnnotation.callbackParam().length() > 0 ) {
p("(("+JSONP_METHOD_CLASS+")__method).callbackParam("+wrap(jsonpAnnotation.callbackParam())+");");
}
if( jsonpAnnotation.failureCallbackParam().length() > 0 ) {
p("(("+JSONP_METHOD_CLASS+")__method).failureCallbackParam("+wrap(jsonpAnnotation.failureCallbackParam())+");");
}
} else {
if (returnRequest && !method.getReturnType().getQualifiedSourceName().equals(Request.class.getName())) {
getLogger().log(ERROR, "Invalid rest method. Non JSONP method must have void or Request return types: " + method.getReadableDeclaration());
throw new UnableToCompleteException();
}
}
// configure the dispatcher
if( options!=null && options.dispatcher()!=Dispatcher.class ) {
// use the dispatcher configured for the method.
p("__method.setDispatcher("+options.dispatcher().getName()+".INSTANCE);");
} else {
// use the default dispatcher configured for the service..
p("__method.setDispatcher(this.dispatcher);");
}
// configure the expected statuses..
if( options!=null && options.expect().length!=0 ) {
// Using method level defined expected status
p("__method.expect("+join(options.expect(), ", ")+");");
} else if( classOptions!=null && classOptions.expect().length!=0 ) {
// Using class level defined expected status
p("__method.expect("+join(classOptions.expect(), ", ")+");");
}
// configure the timeout
if( options!=null && options.timeout() >= 0 ) {
// Using method level defined value
p("__method.timeout("+options.timeout()+");");
} else if( classOptions!=null && classOptions.timeout() >= 0 ) {
// Using class level defined value
p("__method.timeout("+classOptions.timeout()+");");
}
if(jsonpAnnotation == null) {
Produces producesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Produces.class);
if (producesAnnotation != null) {
p("__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, "+wrap(producesAnnotation.value()[0])+");");
} else {
// set the default accept header....
if (acceptTypeBuiltIn != null) {
p("__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + "." + acceptTypeBuiltIn + ");");
} else {
p("__method.header(" + RESOURCE_CLASS + ".HEADER_ACCEPT, " + RESOURCE_CLASS + ".CONTENT_TYPE_JSON);");
}
}
Consumes consumesAnnotation = findAnnotationOnMethodOrEnclosingType(method, Consumes.class);
if (consumesAnnotation != null) {
p("__method.header(" + RESOURCE_CLASS + ".HEADER_CONTENT_TYPE, "+wrap(consumesAnnotation.value()[0])+");");
}
// and set the explicit headers now (could override the accept header)
for (Map.Entry<String, JParameter> entry : headerParams.entrySet()) {
String expr = entry.getValue().getName();
p("__method.header(" + wrap(entry.getKey()) + ", " + toStringExpression(entry.getValue().getType(), expr) + ");");
}
}
if (! formParams.isEmpty()) {
p(FORM_POST_CONTENT_CLASS + " __formPostContent = new " + FORM_POST_CONTENT_CLASS + "();");
for (Map.Entry<String, JParameter> entry : formParams.entrySet()) {
JClassType type = entry.getValue().getType()
.isClassOrInterface();
if (type != null && isQueryParamListType(type)) {
p("__formPostContent.addParameters(" +
wrap(entry.getKey()) + ", " +
toIteratedFormStringExpression(entry.getValue(), classStyle) +
");");
} else {
p("__formPostContent.addParameter(" +
wrap(entry.getKey()) + ", " +
toFormStringExpression(entry.getValue(), classStyle) +
");");
}
}
p("__method.form(__formPostContent.getTextContent());");
}
if (contentArg != null) {
if (contentArg.getType() == STRING_TYPE) {
p("__method.text(" + contentArg.getName() + ");");
} else if (contentArg.getType() == JSON_VALUE_TYPE) {
p("__method.json(" + contentArg.getName() + ");");
} else if (contentArg.getType().isClass() != null &&
isOverlayArrayType(contentArg.getType().isClass())) {
p("__method.json(new " + JSON_ARRAY_CLASS + "(" + contentArg.getName() + "));");
} else if (contentArg.getType().isClass() != null &&
contentArg.getType().isClass().isAssignableTo(OVERLAY_VALUE_TYPE)) {
p("__method.json(new " + JSON_OBJECT_CLASS + "(" + contentArg.getName() + "));");
} else if (contentArg.getType() == DOCUMENT_TYPE) {
p("__method.xml(" + contentArg.getName() + ");");
} else {
JClassType contentClass = contentArg.getType().isClass();
if (contentClass == null) {
contentClass = contentArg.getType().isClassOrInterface();
if (!locator.isCollectionType(contentClass)) {
getLogger().log(ERROR, "Content argument must be a class.");
throw new UnableToCompleteException();
}
}
jsonAnnotation = getAnnotation(contentArg, Json.class);
Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle;
// example:
// .json(Listings$_Generated_JsonEncoder_$.INSTANCE.encode(arg0)
// )
p("__method.json(" + locator.encodeExpression(contentClass, contentArg.getName(), style) + ");");
}
}
List<AnnotationResolver> annotationResolvers = getAnnotationResolvers(context, getLogger());
getLogger().log(TreeLogger.DEBUG, "found " + annotationResolvers.size() + " additional AnnotationResolvers");
for (AnnotationResolver a : annotationResolvers) {
getLogger().log(TreeLogger.DEBUG, "(" + a.getClass().getName() + ") resolve `" + source.getName()
+ "#" + method.getName() + "ยด ...");
final Map<String, String[]> addDataParams = a.resolveAnnotation(getLogger(), source, method, restMethod);
if (addDataParams != null) {
for (String s : addDataParams.keySet()) {
final StringBuilder sb = new StringBuilder();
final List<String> classList = Arrays.asList(addDataParams.get(s));
sb.append("[");
for (int i = 0; i < classList.size(); ++i) {
sb.append("\\\"").append(classList.get(i)).append("\\\"");
if ((i+1) < classList.size()) {
sb.append(",");
}
}
sb.append("]");
getLogger().log(TreeLogger.DEBUG, "add call with (\"" + s + "\", \"" +
sb.toString() + "\")");
p("__method.addData(\"" + s + "\", \"" + sb.toString() + "\");");
}
}
}
if (acceptTypeBuiltIn != null) {
// TODO: shouldn't we also have a cach in here?
p(returnRequest(returnRequest,isJsonp) + "__method.send(" + callbackArg.getName() + ");");
} else if ( isJsonp ){
p(returnRequest(returnRequest,isJsonp) + "((" + JSONP_METHOD_CLASS + ")__method).send(new " + ABSTRACT_ASYNC_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">((" + JSONP_METHOD_CLASS + ")__method, "
+ callbackArg.getName() + ") {").i(1);
{
p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult(" + JSON_VALUE_CLASS + " result) throws Exception {").i(1);
{
if(resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) {
p("return (java.lang.Void) null;");
}
else {
p("try {").i(1);
{
if(resultType.isAssignableTo(locator.LIST_TYPE)){
p("result = new " + JSON_ARRAY_CLASS + "(result.getJavaScriptObject());");
}
jsonAnnotation = getAnnotation(method, Json.class);
Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle;
p("return " + locator.decodeExpression(resultType, "result", style) + ";");
}
i(-1).p("} catch (Throwable __e) {").i(1);
{
p("throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);");
}
i(-1).p("}");
}
}
i(-1).p("}");
}
i(-1).p("});");
} else {
p("try {").i(1);
{
p(returnRequest(returnRequest,isJsonp) + "__method.send(new " + ABSTRACT_REQUEST_CALLBACK_CLASS + "<" + resultType.getParameterizedQualifiedSourceName() + ">(__method, "
+ callbackArg.getName() + ") {").i(1);
{
p("protected " + resultType.getParameterizedQualifiedSourceName() + " parseResult() throws Exception {").i(1);
{
if(resultType.getParameterizedQualifiedSourceName().equals("java.lang.Void")) {
p("return (java.lang.Void) null;");
}
else {
p("try {").i(1);
{
jsonAnnotation = getAnnotation(method, Json.class);
Style style = jsonAnnotation != null ? jsonAnnotation.style() : classStyle;
p("return " + locator.decodeExpression(resultType, JSON_PARSER_CLASS + ".parse(__method.getResponse().getText())", style) + ";");
}
i(-1).p("} catch (Throwable __e) {").i(1);
{
p("throw new " + RESPONSE_FORMAT_EXCEPTION_CLASS + "(\"Response was NOT a valid JSON document\", __e);");