package io.vertx.serviceproxy.generator.model;
import io.vertx.codegen.*;
import io.vertx.codegen.annotations.ProxyClose;
import io.vertx.codegen.annotations.ProxyIgnore;
import io.vertx.codegen.doc.Doc;
import io.vertx.codegen.doc.Text;
import io.vertx.codegen.type.*;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ProxyModel extends ClassModel {
public ProxyModel(ProcessingEnvironment env, TypeElement modelElt) {
super(env, modelElt);
}
@Override
public String getKind() {
return "proxy";
}
@Override
protected void checkParamType(ExecutableElement elem, TypeMirror type, TypeInfo typeInfo, int pos, int numParams, boolean allowAnyJavaType) {
if (typeInfo.getKind().basic || typeInfo.getKind().json) {
return;
}
if (typeInfo.getKind() == ClassKind.ENUM) {
return;
}
if (isLegalContainerParam(typeInfo)) {
return;
}
if (typeInfo.getKind() == ClassKind.DATA_OBJECT) {
if (type instanceof DeclaredType) {
if (Helper.isJsonifiable(elementUtils, typeUtils, (TypeElement) ((DeclaredType) type).asElement())) {
return;
}
throw new GenException(elem, "type " + typeInfo + " does not have a valid 'public JsonObject toJson()' method.");
}
}
if (isLegalHandlerAsyncResultType(typeInfo)) {
if (pos != numParams - 1) {
throw new GenException(elem, "Handler<AsyncResult<T>> must be the last parameter if present in a proxied method");
}
return;
}
if (elem.getModifiers().contains(Modifier.STATIC)) {
return;
}
throw new GenException(elem, "type " + typeInfo + " is not legal for use for a parameter in proxy");
}
@Override
protected void checkReturnType(ExecutableElement elem, TypeInfo type, TypeMirror typeMirror, boolean allowAnyJavaType) {
if (elem.getModifiers().contains(Modifier.STATIC)) {
return;
}
if (type.isVoid()) {
return;
}
throw new GenException(elem, "Proxy methods must have void or Fluent returns");
}
@Override
protected void checkMethod(MethodInfo methodInfo) {
List<MethodInfo> methodsByName = methodMap.get(methodInfo.getName());
if (methodsByName != null) {
throw new GenException(this.modelElt, "Overloaded methods are not allowed in ProxyGen interfaces " + methodInfo.getName());
}
}
@Override
protected MethodInfo createMethodInfo(Set<ClassTypeInfo> ownerTypes, String methodName, String comment, Doc doc, TypeInfo returnType, Text returnDescription, boolean isFluent, boolean isCacheReturn, List<ParamInfo> mParams, ExecutableElement methodElt, boolean isStatic, boolean isDefault, ArrayList<TypeParamInfo.Method> typeParams, TypeElement declaringElt, boolean methodDeprecated, Text methodDeprecatedDesc) {
AnnotationMirror proxyIgnoreAnnotation = Helper.resolveMethodAnnotation(ProxyIgnore.class, elementUtils, typeUtils, declaringElt, methodElt);
boolean isProxyIgnore = proxyIgnoreAnnotation != null;
AnnotationMirror proxyCloseAnnotation = Helper.resolveMethodAnnotation(ProxyClose.class, elementUtils, typeUtils, declaringElt, methodElt);
boolean isProxyClose = proxyCloseAnnotation != null;
ProxyMethodInfo proxyMeth = new ProxyMethodInfo(ownerTypes, methodName, returnType, returnDescription,
isFluent, isCacheReturn, mParams, comment, doc, isStatic, isDefault, typeParams, isProxyIgnore,
isProxyClose, methodDeprecated, methodDeprecatedDesc);
if (isProxyClose && mParams.size() > 0) {
if (mParams.size() > 1) {
throw new GenException(this.modelElt, "@ProxyClose methods can't have more than one parameter");
}
if (proxyMeth.getKind() != MethodKind.FUTURE) {
throw new GenException(this.modelElt, "@ProxyClose parameter must be Handler<AsyncResult<Void>>");
}
TypeInfo type = mParams.get(0).getType();
TypeInfo arg = ((ParameterizedTypeInfo) ((ParameterizedTypeInfo) type).getArgs().get(0)).getArgs().get(0);
if (arg.getKind() != ClassKind.VOID) {
throw new GenException(this.modelElt, "@ProxyClose parameter must be " +
"Handler<AsyncResult<Void>> instead of " + type);
}
}
return proxyMeth;
}
private boolean isLegalHandlerAsyncResultType(TypeInfo type) {
if (type.getErased().getKind() == ClassKind.HANDLER) {
TypeInfo eventType = ((ParameterizedTypeInfo) type).getArgs().get(0);
if (eventType.getErased().getKind() == ClassKind.ASYNC_RESULT) {
TypeInfo resultType = ((ParameterizedTypeInfo) eventType).getArgs().get(0);
if (resultType.getKind().json || resultType.getKind().basic ||
isLegalListSetMapResult(resultType) || resultType.getKind() == ClassKind.VOID ||
resultType.getKind() == ClassKind.ENUM || resultType.getKind() == ClassKind.DATA_OBJECT) {
return true;
}
if (resultType.getKind() == ClassKind.API) {
ApiTypeInfo cla = (ApiTypeInfo)resultType;
if (cla.isProxyGen()) {
return true;
}
}
}
}
return false;
}
private boolean isLegalListSetMapResult(TypeInfo type) {
if (type instanceof ParameterizedTypeInfo) {
if (type.getKind() == ClassKind.LIST || type.getKind() == ClassKind.SET) {
TypeInfo elementType = ((ParameterizedTypeInfo) type).getArgs().get(0);
if (elementType.getKind().basic || elementType.getKind().json || elementType.getKind() == ClassKind.DATA_OBJECT) {
return true;
}
}
}
return false;
}
protected boolean isLegalContainerParam(TypeInfo type) {
TypeInfo raw = type.getRaw();
if (raw.getName().equals(List.class.getName()) || raw.getName().equals(Set.class.getName())) {
TypeInfo argument = ((ParameterizedTypeInfo) type).getArgs().get(0);
if (argument.getKind().basic || argument.getKind().json || argument.getKind() == ClassKind.DATA_OBJECT) {
return true;
}
} else if (raw.getName().equals(Map.class.getName())) {
TypeInfo argument0 = ((ParameterizedTypeInfo) type).getArgs().get(0);
if (!argument0.getName().equals(String.class.getName())) {
return false;
}
TypeInfo argument1 = ((ParameterizedTypeInfo) type).getArgs().get(1);
if (argument1.getKind().basic || argument1.getKind().json) {
return true;
}
}
return false;
}
}