com.sap.cds.generator.writer.CreateEventContextInterfaceVisitor Maven / Gradle / Ivy
The newest version!
/************************************************************************
* © 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.generator.writer;
import static com.sap.cds.generator.util.CaseFormatHelper.lowercaseFirst;
import static com.sap.cds.generator.util.CaseFormatHelper.toUpperUnderscore;
import static com.sap.cds.generator.util.NamesUtils.className;
import static com.sap.cds.generator.util.NamesUtils.getterName;
import static com.sap.cds.generator.util.NamesUtils.methodName;
import static com.sap.cds.generator.util.NamesUtils.setterName;
import static com.sap.cds.generator.util.TypeUtils.addStaticFactoryMethods;
import static com.sap.cds.generator.util.TypeUtils.getArrayTypeName;
import static com.sap.cds.generator.util.TypeUtils.getAttributeType;
import static com.sap.cds.generator.writer.SpecWriterUtil.setJavaDoc;
import static com.sap.cds.generator.writer.Types.RESULT_ARGUMENT;
import static com.sap.cds.generator.writer.Types.RETURN_TYPE;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import com.sap.cds.generator.Configuration;
import com.sap.cds.generator.MethodStyle;
import com.sap.cds.generator.util.NamesUtils;
import com.sap.cds.generator.util.TypeUtils;
import com.sap.cds.generator.writer.ModelWriter.Context;
import com.sap.cds.ql.CdsName;
import com.sap.cds.ql.CdsOptionalArguments;
import com.sap.cds.reflect.CdsAction;
import com.sap.cds.reflect.CdsArrayedType;
import com.sap.cds.reflect.CdsDefinition;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEvent;
import com.sap.cds.reflect.CdsFunction;
import com.sap.cds.reflect.CdsKind;
import com.sap.cds.reflect.CdsOperation;
import com.sap.cds.reflect.CdsParameter;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.reflect.CdsVisitor;
import com.sap.cds.reflect.impl.CdsElementBuilder;
import com.sap.cds.reflect.impl.CdsParameterBuilder;
import com.sap.cds.reflect.impl.CdsStructuredTypeBuilder;
import com.sap.cds.util.CdsModelUtils;
import com.palantir.javapoet.AnnotationSpec;
import com.palantir.javapoet.ClassName;
import com.palantir.javapoet.CodeBlock;
import com.palantir.javapoet.FieldSpec;
import com.palantir.javapoet.MethodSpec;
import com.palantir.javapoet.ParameterizedTypeName;
import com.palantir.javapoet.TypeName;
import com.palantir.javapoet.TypeSpec;
import com.palantir.javapoet.WildcardTypeName;
public class CreateEventContextInterfaceVisitor implements CdsVisitor {
private static final ParameterizedTypeName MAP_STR2OBJ = ParameterizedTypeName.get(Types.MAP,
Types.STRING, WildcardTypeName.subtypeOf(Object.class));
private static final String PROXYMETHOD_ARG_NAME = "entityName";
private final TypeSpec.Builder builder;
private final Configuration cfg;
private final boolean bounded;
private final String boundEntityName;
private final Context context;
private final ClassName className;
CreateEventContextInterfaceVisitor(TypeSpec.Builder builder, ClassName className, String boundEntityName, Context context) {
this.builder = builder;
this.className = className;
this.cfg = context.config();
this.boundEntityName = boundEntityName;
this.builder.addSuperinterface(Types.EVENT_CONTEXT);
this.bounded = boundEntityName != null;
this.context = context;
}
@Override
public void visit(CdsAction action) {
addGetCqnMethod();
addSetCqnMethod();
addStaticQualifiedAttribute(action);
prepareDefinition(action);
}
@Override
public void visit(CdsFunction function) {
addGetCqnMethod();
addSetCqnMethod();
addStaticQualifiedAttribute(function);
prepareDefinition(function);
}
@Override
public void visit(CdsEvent event) {
// setData and getData uses event as the result type,
// not the event context that we have in this
ClassName eventClassName = className(cfg, event);
addGetter(eventClassName);
addSetter(eventClassName);
addStaticQualifiedAttribute(event);
addStaticProxyMethod();
builder.addAnnotation(eventNameAnnotation(event.getName(), "$S"));
}
private void addGetter(ClassName event) {
String methodName;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
methodName = "getData";
} else {
methodName = "data";
}
MethodSpec.Builder resultGetterBuilder = MethodSpec.methodBuilder(methodName)
.returns(event).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
builder.addMethod(resultGetterBuilder.build());
}
private void addSetter(ClassName event) {
String methodName;
TypeName returnType = TypeName.VOID;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
methodName = "setData";
} else {
methodName = "data";
returnType = className;
}
MethodSpec.Builder resultGetterBuilder = MethodSpec.methodBuilder(methodName).returns(returnType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(event, "event");
builder.addMethod(resultGetterBuilder.build());
}
private void addGetCqnMethod() {
if (bounded) {
String methodName;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
methodName = "getCqn";
} else {
methodName = "cqn";
}
MethodSpec.Builder resultGetterBuilder = MethodSpec.methodBuilder(methodName)
.returns(Types.CQN_SELECT)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
builder.addMethod(resultGetterBuilder.build());
}
}
private void addSetCqnMethod() {
if (bounded) {
String methodName = "cqn";
TypeName returnType;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
returnType = TypeName.VOID;
methodName = "setCqn";
} else {
returnType = className;
}
MethodSpec.Builder resultGetterBuilder = MethodSpec.methodBuilder(methodName).returns(returnType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(Types.CQN_SELECT, "select");
builder.addMethod(resultGetterBuilder.build());
}
}
private void addStaticQualifiedAttribute(CdsDefinition def) {
String name = def.getName();
FieldSpec staticField = FieldSpec.builder(String.class, "CDS_NAME")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("$S", name).build();
builder.addField(staticField);
}
private void prepareDefinition(CdsOperation operation) {
if (bounded) {
addStaticProxyMethodBound();
}
addResultMethod(operation);
addStaticProxyMethod();
builder.addAnnotation(eventNameAnnotation(operation.getName(), "$S"));
if (SpecWriterUtil.isGroupOptionalParameters(context.config(), operation)) {
List> optionalParameterBuilders = SpecWriterUtil.optionalParameters(operation)
.map(p -> new CdsElementBuilder<>(p.annotations().toList(), p.getName(), null, false, false,
p.isNotNull(), false, p.getDefaultValue().orElse(null), "").type(p.getType()))
.collect(Collectors.toList());
String qualifiedName = String.join(".", operation.getQualifier(), NamesUtils.OPTIONAL_ARGS_TYPE_NAME);
CdsStructuredTypeBuilder optParametersTypeBuilder = new CdsStructuredTypeBuilder<>(
Collections.emptyList(), qualifiedName, NamesUtils.OPTIONAL_ARGS_TYPE_NAME, CdsKind.TYPE, "");
optParametersTypeBuilder.addElements(optionalParameterBuilders);
CdsParameter optionalsParam = new CdsParameterBuilder(Collections.emptyList(),
NamesUtils.OPTIONAL_ARGS_PARAMETER_NAME, optParametersTypeBuilder, t -> null, "", false).build();
generateInnerInterface(className, optionalsParam.getType(), optionalsParam, true);
}
}
private void addResultMethod(CdsOperation operation) {
TypeName resultType = null;
TypeName setReturnType;
Optional opt = operation.returnType();
if (opt.isEmpty()) {
return;
}
CdsType returnType = opt.get();
if (TypeUtils.isAnonymousType(returnType, cfg)) {
resultType = generateInnerInterface(className, returnType, null);
} else {
resultType = getAttributeType(className, returnType, cfg);
}
if (resultType == null) {
resultType = TypeUtils.getOperationResultType(null, operation, returnType, cfg);
if (resultType == null) {
// Since actions might or might not return a value
return;
}
}
String setter;
String getter;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
setReturnType = TypeName.VOID;
setter = "setResult";
getter = "getResult";
} else {
setReturnType = className;
setter = getter = "result";
}
MethodSpec.Builder resultSetterBuilder = MethodSpec.methodBuilder(setter).returns(setReturnType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).addParameter(resultType, RESULT_ARGUMENT);
MethodSpec.Builder resultGetterBuilder = MethodSpec.methodBuilder(getter).returns(resultType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
builder.addMethod(resultSetterBuilder.build());
builder.addMethod(resultGetterBuilder.build());
}
private TypeName generateInnerInterface(ClassName parent, CdsType returnType, CdsParameter parameter, boolean isCdsOptionalArgument) {
Stream elements;
// Either fixed name return type or an inner class with raw name of the parameter
ClassName innerInterfaceName = parameter == null ? parent.nestedClass(RETURN_TYPE) : className(cfg, parent, parameter);
TypeName resultType = innerInterfaceName;
TypeSpec.Builder innerInterfaceBuilder = TypeSpec.interfaceBuilder(innerInterfaceName).addModifiers(Modifier.PUBLIC,
Modifier.STATIC).addSuperinterface(Types.CDS_DATA);
if (returnType.isStructured()) {
elements = returnType.as(CdsStructuredType.class).elements();
} else {
resultType = getArrayTypeName(resultType);
elements = returnType.as(CdsArrayedType.class).getItemsType().as(CdsStructuredType.class).elements();
}
elements.forEach(
e -> e.accept(new CreateConsumptionInterfaceVisitor(innerInterfaceBuilder, innerInterfaceName, context, Set.of())));
addStaticFactoryMethods(builder, innerInterfaceName, innerInterfaceBuilder);
// mark the inner interface as an {@link CdsOptionalArgument}
if (isCdsOptionalArgument) {
innerInterfaceBuilder.addAnnotation(CdsOptionalArguments.class);
}
builder.addType(innerInterfaceBuilder.build());
return resultType;
}
private TypeName generateInnerInterface(ClassName parent, CdsType returnType, CdsParameter parameter) {
return generateInnerInterface(parent, returnType, parameter, false);
}
private void addStaticProxyMethod() {
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("create").returns(className)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
if (bounded) {
methodBuilder.addParameter(TypeName.get(String.class), PROXYMETHOD_ARG_NAME);
}
CodeBlock.Builder codeBuilder = CodeBlock.builder();
codeBuilder.addStatement("return EventContext.create($T.class, $N)", className,
bounded ? PROXYMETHOD_ARG_NAME : "null");
methodBuilder.addCode(codeBuilder.build());
builder.addMethod(methodBuilder.build());
}
private void addStaticProxyMethodBound() {
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("create").returns(className)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
CodeBlock.Builder codeBuilder = CodeBlock.builder();
codeBuilder.addStatement("return EventContext.create($T.class, $S)", className, this.boundEntityName);
methodBuilder.addCode(codeBuilder.build());
builder.addMethod(methodBuilder.build());
}
private static AnnotationSpec eventNameAnnotation(String cdsName, String format) {
return AnnotationSpec.builder(Types.EVENT_NAME).addMember("value", format, cdsName).build();
}
@Override
public void visit(CdsParameter parameter) {
addStaticAttribute(parameter);
TypeName resultType;
CdsType returnType = parameter.getType();
if (TypeUtils.isAnonymousType(returnType, cfg)) {
resultType = generateInnerInterface(className, returnType, parameter);
addGetterMethod(parameter, resultType);
addSetterMethod(parameter, resultType);
} else {
addParamGetter(parameter);
addParamSetter(parameter);
}
}
private void addParamGetter(CdsParameter parameter) {
TypeName returnType = getAttributeType(className, parameter.getType(), cfg);
addGetterMethod(parameter, returnType);
}
private void addGetterMethod(CdsParameter parameter, TypeName returnType) {
if (returnType == null || TypeUtils.isIgnored(parameter)) {
return;
}
String getter;
if (cfg.getMethodStyle().equals(MethodStyle.BEAN)) {
getter = getterName(cfg, parameter);
} else {
getter = methodName(cfg, parameter);
}
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(getter).returns(returnType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
addJavaDoc(parameter, methodBuilder);
addCdsNameAnnotation(methodBuilder, parameter.getName(), getter);
builder.addMethod(methodBuilder.build());
}
private void addParamSetter(CdsParameter parameter) {
TypeName paramType = getSetterParam(parameter.getType());
addSetterMethod(parameter, paramType);
}
private void addSetterMethod(CdsParameter parameter, TypeName paramType) {
if (paramType == null || TypeUtils.isIgnored(parameter)) {
return;
}
String setter;
TypeName returnType;
if (cfg.getMethodStyle() == MethodStyle.BEAN) {
returnType = TypeName.VOID;
setter = setterName(cfg, parameter);
} else {
returnType = className;
setter = methodName(cfg, parameter);
}
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(setter).returns(returnType)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(paramType, methodName(cfg, parameter));
addJavaDoc(parameter, methodBuilder);
addCdsNameAnnotation(methodBuilder, parameter.getName(), setter);
builder.addMethod(methodBuilder.build());
}
private TypeName getSetterParam(CdsType type) {
if (type.isAssociation()) {
if (CdsModelUtils.isSingleValued(type)) {
return MAP_STR2OBJ;
}
return TypeUtils.listOf(WildcardTypeName.subtypeOf(MAP_STR2OBJ));
}
return getAttributeType(className, type, cfg);
}
private static AnnotationSpec cdsNameAnnotation(String cdsName, String format) {
return AnnotationSpec.builder(CdsName.class).addMember("value", format, cdsName).build();
}
private static MethodSpec.Builder addCdsNameAnnotation(MethodSpec.Builder builder, String cdsName,
String javaName) {
if (!cdsName.equals(propertyName(javaName))) {
builder.addAnnotation(cdsNameAnnotation(toUpperUnderscore(cdsName), "$L"));
}
return builder;
}
private static String propertyName(String javaName) {
if (javaName.startsWith("set") || javaName.startsWith("get")) {
javaName = lowercaseFirst(javaName.substring(3));
}
return javaName;
}
private void addStaticAttribute(CdsParameter parameter) {
String name = toUpperUnderscore(parameter.getName());
FieldSpec staticField = FieldSpec.builder(String.class, name)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("$S", parameter.getName())
.build();
builder.addField(staticField);
}
private void addJavaDoc(CdsParameter param, MethodSpec.Builder methodBuilder) {
if (cfg.getDocs()) {
setJavaDoc(param, methodBuilder);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy