package org.n3r.eql.eqler.generators;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.n3r.eql.EqlPage;
import org.n3r.eql.EqlTran;
import org.n3r.eql.config.EqlConfig;
import org.n3r.eql.eqler.annotations.Dynamic;
import org.n3r.eql.eqler.annotations.Param;
import org.n3r.eql.eqler.annotations.ReturnType;
import org.n3r.eql.eqler.annotations.SqlId;
import org.n3r.eql.impl.EqlBatch;
import org.n3r.eql.map.EqlRowMapper;
import org.n3r.eql.pojo.annotations.EqlId;
import org.objectweb.asm.Type;
import java.lang.annotation.Annotation;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class MethodAllParam {
private List<MethodParam> methodParams = Lists.<MethodParam>newArrayList();
@Getter private int seqParamsCount;
@Getter private int seqDynamicsCount;
@Getter private int namedParamsCount;
@Getter private int namedDynamicCount;
@Getter private MethodParam paramEqlId;
@Getter private MethodParam paramReturnType;
@Getter private MethodParam eqlTran;
@Getter private MethodParam eqlBatch;
@Getter private MethodParam eqlPage;
@Getter private MethodParam eqlConfig;
@Getter private MethodParam eqlRowMapper;
@Getter private int methodParamsCount = 0;
@Getter private int asmLocalVarNamedParamIndex = -1;
@Getter int asmLocalVarNamedDynamicIndex = -1;
@Setter private EqlId methodEqlId;
public void compute() {
int offset = 0;
for (MethodParam methodParam : methodParams) {
computeMethodParam(methodParam);
methodParam.setOffset(offset);
if (isWildType(methodParam)) ++offset;
}
if (seqParamsCount > 0 && namedParamsCount > 0)
throw new RuntimeException("@Param should be used to annotate parameters all or none");
if (seqDynamicsCount > 0 && namedDynamicCount > 0)
throw new RuntimeException("@Dynamic(name=\"sth.\" should be used to annotate dynamics all or none");
if (namedParamsCount > 0) {
asmLocalVarNamedParamIndex = 1 /* this */ + methodParamsCount + 1 /* named param hashmap */;
}
if (namedDynamicCount > 0) {
asmLocalVarNamedDynamicIndex = 1 /* this */ + methodParamsCount + (namedParamsCount > 0 ? 2 : 1) /* named dynamic hashmap */;
}
}
private boolean isWildType(MethodParam methodParam) {
Type tp = Type.getType(methodParam.getParamType());
return tp.equals(Type.LONG_TYPE) || tp.equals(Type.DOUBLE_TYPE);
}
private void computeMethodParam(MethodParam methodParam) {
val eqlTranNew = parseNonAnnotationsMethodParam(methodParam, EqlTran.class, eqlTran);
if (eqlTranNew != null) {
eqlTran = eqlTranNew;
return;
}
val eqlBatchNew = parseNonAnnotationsMethodParam(methodParam, EqlBatch.class, eqlBatch);
if (eqlBatchNew != null) {
eqlBatch = eqlBatchNew;
return;
}
val eqlPageNew = parseNonAnnotationsMethodParam(methodParam, EqlPage.class, eqlPage);
if (eqlPageNew != null) {
eqlPage = eqlPageNew;
return;
}
val eqlConfigNew = parseNonAnnotationsMethodParam(methodParam, EqlConfig.class, eqlConfig);
if (eqlConfigNew != null) {
eqlConfig = eqlConfigNew;
return;
}
val eqlRowMapperNew = parseNonAnnotationsMethodParam(methodParam, EqlRowMapper.class, eqlRowMapper);
if (eqlRowMapperNew != null) {
eqlRowMapper = eqlRowMapperNew;
return;
}
if (isSqlIdAnnotated(methodParam)) return;
if (isReturnTypeAnnotated(methodParam)) return;
++methodParamsCount;
Param param = methodParam.getParam();
if (param != null) methodParam.setSeqParamIndex(namedParamsCount++);
Dynamic dynamic = methodParam.getDynamic();
if (dynamic == null) {
if (param == null) methodParam.setSeqParamIndex(seqParamsCount++);
} else {
if (dynamic.sole() && param != null)
throw new RuntimeException(
"@DynamicParam(sole=true) and @NamedParam can not co-exists");
if (isBlank(dynamic.name()))
methodParam.setSeqDynamicIndex(seqDynamicsCount++);
else methodParam.setSeqDynamicIndex(namedDynamicCount++);
if (!dynamic.sole() && param == null) {
methodParam.setSeqParamIndex(seqParamsCount++);
}
}
}
private boolean isReturnTypeAnnotated(MethodParam methodParam) {
ReturnType returnType = methodParam.getReturnType();
if (returnType == null) return false;
if (paramReturnType != null)
throw new RuntimeException("more than one @ReturnType defined");
if (methodParam.getParamType() != Class.class)
throw new RuntimeException("bad @ReturnType parameter type, required Class");
paramReturnType = methodParam;
return true;
}
private boolean isSqlIdAnnotated(MethodParam methodParam) {
SqlId paramEqlIdThis = methodParam.getSqlId();
if (paramEqlIdThis == null) return false;
if (methodEqlId != null || paramEqlId != null)
throw new RuntimeException("more than one @EqlId defined");
if (methodParam.getParamType() != String.class)
throw new RuntimeException("bad @EqlId parameter type, required String");
paramEqlId = methodParam;
return true;
}
private MethodParam parseNonAnnotationsMethodParam(
MethodParam methodParam, Class<?> type, MethodParam lastMethodParam) {
if (type.isAssignableFrom(methodParam.getParamType())) {
checkNull(lastMethodParam, "only one " + type + " parameter supported");
checkNonAnnotations(methodParam);
return methodParam;
}
return null;
}
private void checkNonAnnotations(MethodParam methodParam) {
Annotation[] paramAnnotations = methodParam.getParamAnnotations();
if (paramAnnotations.length == 0) return;
throw new RuntimeException("Annotations are not supported for type "
+ methodParam.getParamType());
}
private void checkNull(Object object, String msg) {
if (object == null) return;
throw new RuntimeException(msg);
}
public void addMethodParam(MethodParam methodParam) {
this.methodParams.add(methodParam);
}
public MethodParam getMethodParam(int index) {
return methodParams.get(index);
}
public int getParamsSize() {
return methodParams.size();
}
}