/*
* Copyright 2013 cruxframework.org.
*
* 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 org.cruxframework.crux.core.rebind.rest;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.net.URLEncoder;
import java.util.Date;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.client.utils.StringUtils;
import org.cruxframework.crux.core.rebind.AbstractProxyCreator.SourcePrinter;
import org.cruxframework.crux.core.rebind.CruxGeneratorException;
import org.cruxframework.crux.core.rebind.context.RebindContext;
import org.cruxframework.crux.core.server.rest.util.HttpHeaderNames;
import org.cruxframework.crux.core.shared.rest.annotation.CookieParam;
import org.cruxframework.crux.core.shared.rest.annotation.FormParam;
import org.cruxframework.crux.core.shared.rest.annotation.HeaderParam;
import org.cruxframework.crux.core.utils.JClassUtils;
import org.cruxframework.crux.core.utils.JClassUtils.PropertyInfo;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.user.client.Cookies;
/**
* @author Thiago da Rosa de Bustamante
*
*/
class BodyParameterHandler extends AbstractParameterHelper
{
private final RebindContext context;
public BodyParameterHandler(RebindContext context)
{
super(context);
this.context = context;
}
public void generateMethodParamToBodyCode(SourcePrinter srcWriter, RestMethodInfo methodInfo, String builder, String httpMethod)
{
JParameter[] parameters = methodInfo.method.getParameters();
boolean formEncoded = false;
boolean hasBodyObject = false;
String formString = getFormString(methodInfo);
if (!StringUtils.isEmpty(formString))
{
srcWriter.println("String requestData = "+EscapeUtils.quote(formString)+";");
}
for (int i = 0; i< methodInfo.parameterAnnotations.length; i++)
{
Annotation[] annotations = methodInfo.parameterAnnotations[i];
if (annotations == null || annotations.length == 0)
{ // JSON on body
if(hasBodyObject)
{
throw new CruxGeneratorException("Invalid Method: " + methodInfo.method.getEnclosingType().getName() + "." + methodInfo.method.getName() + "(). " +
"Request body can not contain more than one body parameter (JSON serialized object).");
}
hasBodyObject = true;
String serializerName = new JSonSerializerProxyCreator(context, parameters[i].getType()).create();
srcWriter.println(builder+".setHeader(\""+HttpHeaderNames.CONTENT_TYPE+"\", \"application/json\");");
srcWriter.println("JSONValue serialized = new "+serializerName+"().encode("+parameters[i].getName()+");");
if (parameters[i].getType().getQualifiedSourceName().equals("java.lang.String"))
{
srcWriter.println("String requestData = (serialized==null||serialized.isNull()!=null)?null:serialized.isString().stringValue();");
}
else
{
srcWriter.println("String requestData = (serialized==null||serialized.isNull()!=null)?null:serialized.toString();");
}
}
else
{
for (Annotation annotation : annotations)
{
JParameter parameter = parameters[i];
JType parameterType = parameter.getType();
String parameterName = parameter.getName();
formEncoded = generateMethodParamToBodyCodeForAnnotatedParameter(srcWriter, builder, parameters, formEncoded, i, annotation, parameterType, parameterName);
}
}
}
if (hasBodyObject && formEncoded)
{
throw new CruxGeneratorException("Invalid Method: " + methodInfo.method.getEnclosingType().getName() + "." + methodInfo.method.getName() + "(). " +
"Request body can not contain form parameters and a JSON serialized object.");
}
if (hasBodyObject || formEncoded)
{
if (httpMethod.equals("GET"))
{
throw new CruxGeneratorException("Invalid Method: " + methodInfo.method.getEnclosingType().getName() + "." + methodInfo.method.getName() + "(). " +
"Can not use request body parameters on a GET operation.");
}
srcWriter.println(builder+".setRequestData(requestData);");
}
}
private String getFormString(RestMethodInfo methodInfo)
{
StringBuilder str = new StringBuilder();
boolean first = true;
JParameter[] parameters = methodInfo.method.getParameters();
try
{
for (int i = 0; i< methodInfo.parameterAnnotations.length; i++)
{
Annotation[] annotations = methodInfo.parameterAnnotations[i];
for (Annotation annotation : annotations)
{
if (annotation instanceof FormParam)
{
if (!first)
{
str.append("&");
}
first = false;
if (JClassUtils.isSimpleType(parameters[i].getType()))
{
buildFormStringForSimpleType(str, ((FormParam)annotation).value());
}
else
{
buildFormStringForComplexType(str, parameters[i].getType(), ((FormParam)annotation).value());
}
}
}
}
}
catch (UnsupportedEncodingException e)
{
throw new CruxGeneratorException("Unsupported encoding for parameter name on method ["+methodInfo.method.toString()+"]");
}
return str.toString();
}
private boolean generateMethodParamToBodyCodeForAnnotatedParameter(SourcePrinter srcWriter, String builder,
JParameter[] parameters, boolean formEncoded, int i, Annotation annotation, JType parameterType, String parameterName)
{
if (annotation instanceof FormParam)
{
if (!formEncoded)
{
srcWriter.println(builder+".setHeader(\""+HttpHeaderNames.CONTENT_TYPE+"\", \"application/x-www-form-urlencoded\");");
formEncoded = true;
}
}
if (JClassUtils.isSimpleType(parameterType))
{
if (annotation instanceof FormParam)
{
generateMethodParamToCodeForSimpleType(srcWriter, "requestData", parameterType, ((FormParam) annotation).value(),
parameterName, (parameterType.isPrimitive() != null?"true":parameterName+"!=null"), annotation);
}
if (annotation instanceof HeaderParam)
{
generateMethodParamToHeaderCodeForSimpleType(srcWriter, builder, ((HeaderParam) annotation).value(), parameterType,
parameterName, (parameterType.isPrimitive() != null?"true":parameterName+"!=null"));
}
if (annotation instanceof CookieParam)
{
generateMethodParamToCookieCodeForSimpleType(srcWriter, ((CookieParam) annotation).value(), parameterType,
parameterName, (parameterType.isPrimitive() != null?"true":parameterName+"!=null"));
}
}
else
{
if (annotation instanceof FormParam)
{
generateMethodParamToCodeForComplexType(srcWriter, "requestData", parameterType,
((FormParam) annotation).value(), parameterName, parameterName+"!=null", annotation);
}
if (annotation instanceof HeaderParam)
{
generateMethodParamToHeaderCodeForComplexType(srcWriter, builder, ((HeaderParam) annotation).value(), parameterType,
parameterName, parameterName+"!=null");
}
if (annotation instanceof CookieParam)
{
generateMethodParamToCookieCodeForComplexType(srcWriter, ((CookieParam) annotation).value(), parameterType,
parameterName, parameterName+"!=null");
}
}
return formEncoded;
}
private void generateMethodParamToCookieCodeForComplexType(SourcePrinter srcWriter, String cookieName, JType parameterType,
String parameterExpression, String parameterCheckExpression)
{
PropertyInfo[] propertiesInfo = JClassUtils.extractBeanPropertiesInfo(parameterType.isClassOrInterface());
for (PropertyInfo propertyInfo : propertiesInfo)
{
if (JClassUtils.isSimpleType(propertyInfo.getType()))
{
generateMethodParamToCookieCodeForSimpleType(srcWriter, cookieName+"."+propertyInfo.getName(), propertyInfo.getType(),
parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()",
(propertyInfo.getType().isPrimitive()!=null?
parameterCheckExpression:
parameterCheckExpression + " && " + parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()!=null"));
}
else
{
generateMethodParamToCookieCodeForComplexType(srcWriter, cookieName+"."+propertyInfo.getName(), propertyInfo.getType(),
parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()",
parameterCheckExpression + " && " + parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()!=null");
}
}
}
private void generateMethodParamToCookieCodeForSimpleType(SourcePrinter srcWriter, String cookieName, JType parameterType,
String parameterexpression, String parameterCheckExpression)
{
JClassType jClassType = parameterType.isClassOrInterface();
if (jClassType != null)
{
if (jClassType.isAssignableTo(stringType))
{
srcWriter.println(Cookies.class.getCanonicalName()+".setCookie("+EscapeUtils.quote(cookieName) +
", "+"("+parameterCheckExpression+"?"+parameterexpression+":\"\"), new "+Date.class.getCanonicalName()+"(2240532000000L), null, \"/\", false);");
}
else if (jClassType.isAssignableTo(dateType))
{
srcWriter.println(Cookies.class.getCanonicalName()+".setCookie("+EscapeUtils.quote(cookieName) +
", "+"("+parameterCheckExpression+"?Long.toString("+parameterexpression+".getTime()):\"\"), new "+Date.class.getCanonicalName()+"(2240532000000L), null, \"/\", false);");
}
else
{
srcWriter.println(Cookies.class.getCanonicalName()+".setCookie("+EscapeUtils.quote(cookieName) +
", "+"("+parameterCheckExpression+"?(\"\"+"+parameterexpression+"):\"\"), new "+Date.class.getCanonicalName()+"(2240532000000L), null, \"/\", false);");
}
}
else
{
srcWriter.println(Cookies.class.getCanonicalName()+".setCookie("+EscapeUtils.quote(cookieName) +
", "+"("+parameterCheckExpression+"?(\"\"+"+parameterexpression+"):\"\"), new "+Date.class.getCanonicalName()+"(2240532000000L), null, \"/\", false);");
}
}
private void generateMethodParamToHeaderCodeForComplexType(SourcePrinter srcWriter, String builder, String headerName, JType parameterType,
String parameterExpression, String parameterCheckExpression)
{
PropertyInfo[] propertiesInfo = JClassUtils.extractBeanPropertiesInfo(parameterType.isClassOrInterface());
for (PropertyInfo propertyInfo : propertiesInfo)
{
if (JClassUtils.isSimpleType(propertyInfo.getType()))
{
generateMethodParamToHeaderCodeForSimpleType(srcWriter, builder, headerName+"."+propertyInfo.getName(), propertyInfo.getType(),
parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()",
(propertyInfo.getType().isPrimitive()!=null?
parameterCheckExpression:
parameterCheckExpression + " && " + parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()!=null"));
}
else
{
generateMethodParamToHeaderCodeForComplexType(srcWriter, builder, headerName+"."+propertyInfo.getName(), propertyInfo.getType(),
parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()",
parameterCheckExpression + " && " + parameterExpression+"."+propertyInfo.getReadMethod().getName()+"()!=null");
}
}
}
private void generateMethodParamToHeaderCodeForSimpleType(SourcePrinter srcWriter, String builderVarName, String headerName,
JType parameterType, String parameterexpression, String parameterCheckExpression)
{
JClassType jClassType = parameterType.isClassOrInterface();
srcWriter.println("if ("+parameterCheckExpression+"){");
if (jClassType != null)
{
if (jClassType.isAssignableTo(stringType))
{
srcWriter.println(builderVarName+".setHeader("+EscapeUtils.quote(headerName)+", "+
parameterexpression+");");
}
else if (jClassType.isAssignableTo(dateType))
{
srcWriter.println(builderVarName+".setHeader("+EscapeUtils.quote(headerName)+", "+
"Long.toString("+parameterexpression+".getTime()));");
}
else
{
srcWriter.println(builderVarName+".setHeader("+EscapeUtils.quote(headerName)+", "+
"\"\"+"+parameterexpression+");");
}
}
else
{
srcWriter.println(builderVarName+".setHeader("+EscapeUtils.quote(headerName)+", "+
"\"\"+"+parameterexpression+");");
}
srcWriter.println("}");
}
private void buildFormStringForComplexType(StringBuilder str, JType parameterType, String value) throws UnsupportedEncodingException
{
PropertyInfo[] propertiesInfo = JClassUtils.extractBeanPropertiesInfo(parameterType.isClassOrInterface());
boolean first = true;
for (PropertyInfo propertyInfo : propertiesInfo)
{
if (!first)
{
str.append("&");
}
first = false;
String parameterName = (StringUtils.isEmpty(value)?propertyInfo.getName():value+"."+propertyInfo.getName());
if (JClassUtils.isSimpleType(propertyInfo.getType()))
{
buildFormStringForSimpleType(str, parameterName);
}
else
{
buildFormStringForComplexType(str, propertyInfo.getType(), parameterName);
}
}
}
private void buildFormStringForSimpleType(StringBuilder str, String parameterName) throws UnsupportedEncodingException
{
str.append(URLEncoder.encode(parameterName, "UTF-8")+"={"+parameterName+"}");
}
}