All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sap.cds.generator.writer.ModelWriter Maven / Gradle / Ivy
/*******************************************************************
* © 2019 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.generator.writer;
import static com.google.common.collect.Streams.concat;
import static com.sap.cds.generator.util.NamesUtils.checkForJavaKeyword;
import static com.sap.cds.generator.util.NamesUtils.getResolvedWrapperName;
import static com.sap.cds.generator.util.NamesUtils.isValidTechnicalEntity;
import static com.sap.cds.generator.util.NamesUtils.qualifiedContextname;
import static com.sap.cds.generator.util.NamesUtils.qualifiedWrapperBuilderName;
import static com.sap.cds.generator.util.NamesUtils.unqualifiedName;
import static com.sap.cds.generator.util.TypeUtils.builderClassName;
import static com.sap.cds.generator.util.TypeUtils.className;
import static com.sap.cds.generator.util.TypeUtils.logWarningForManyToManyWithStructElement;
import static com.sap.cds.generator.writer.SpecWriterUtil.getJavaDoc;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import javax.lang.model.element.Modifier;
import javax.tools.JavaFileObject;
import com.sap.cds.generator.Cds4jCodegen;
import com.sap.cds.generator.Configuration;
import com.sap.cds.generator.util.EntityWriterException;
import com.sap.cds.generator.util.GeneratedAnnotationUtil;
import com.sap.cds.generator.util.GeneratedFile;
import com.sap.cds.generator.util.GeneratedFile.Consumer;
import com.sap.cds.generator.util.NamesUtils;
import com.sap.cds.generator.util.UnSupportedModelException;
import com.sap.cds.impl.util.Pair;
import com.sap.cds.reflect.CdsAction;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsDefinition;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsEvent;
import com.sap.cds.reflect.CdsFunction;
import com.sap.cds.reflect.CdsKind;
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsVisitor;
import com.sap.cds.reflect.impl.CdsEventBuilder.EventProxy;
import com.sap.cds.reflect.impl.reader.model.CdsConstants;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
public class ModelWriter implements CdsVisitor {
private static final String CONTEXT_SUFFIX = "Context";
private final Consumer consumer;
private final Configuration config;
private final CdsModel model;
private final Map wrapperMap;
private final Set builderInterfaces;
private final boolean isEventContext;
private final boolean readDocs;
private final NamesUtils namesUtils;
private final GeneratedAnnotationUtil generated;
public ModelWriter(Consumer consumer, Configuration config, CdsModel model) {
this.consumer = consumer;
this.config = config;
this.model = model;
this.wrapperMap = new HashMap<>();
this.builderInterfaces = new HashSet<>();
this.isEventContext = Boolean.parseBoolean(config.getEventContext());
this.namesUtils = new NamesUtils(config);
this.generated = new GeneratedAnnotationUtil(config);
this.readDocs = config.getDocs();
}
@Override
public void visit(CdsModel model) {
wrapperMap.values().forEach(this::generateWrapperInterface);
}
@Override
public void visit(CdsEntity entity) {
checkForJavaKeyword(entity.getQualifiedName());
if (!namesUtils.isExcluded(entity.getQualifiedName())
&& isValidTechnicalEntity(model, entity.getQualifiedName())) {
collectWrapperInterfaces(entity);
generateBuilderInterface(entity);
generateConsumptionInterface(entity);
if (isEventContext) {
concat(entity.actions(), entity.functions()).forEach(a -> generateEventContextInterface(a, entity));
}
}
}
@Override
public void visit(CdsEvent event) {
checkForJavaKeyword(event.getQualifiedName());
if (!namesUtils.isExcluded(event.getQualifiedName())) {
collectWrapperInterfaces(event);
generateBuilderInterface(event);
generateConsumptionInterface(event);
if (isEventContext) {
generateEventContextInterface(event, null);
}
}
}
@Override
public void visit(CdsAction action) {
if (isEventContext && !namesUtils.isExcluded(action.getQualifiedName())) {
collectWrapperInterfaces(action);
generateEventContextInterface(action, null);
}
}
@Override
public void visit(CdsFunction function) {
if (isEventContext && !namesUtils.isExcluded(function.getQualifiedName())) {
collectWrapperInterfaces(function);
generateEventContextInterface(function, null);
}
}
@Override
public void visit(CdsStructuredType struct) {
if (!struct.getName().isEmpty()) {
checkForJavaKeyword(struct.getQualifiedName());
if (!namesUtils.isExcluded(struct.getQualifiedName())) {
generateConsumptionInterface(struct);
if (isTargetAspect(struct)) {
// For every TargetAspect which contains structured elements and is target of
// many to many composition, throw warning
logWarningForManyToManyWithStructElement(model, struct);
generateBuilderInterface(struct);
}
}
}
}
private void collectWrapperInterfaces(CdsDefinition def) {
String packageName = namesUtils.packageName(def);
wrapperMap.put(packageName, def);
}
private void generateWrapperInterface(CdsDefinition def) {
String qualifiedName = def.getQualifiedName();
String name = def.getName();
String packageName = namesUtils.packageName(def);
String qualifiedWrapperName = qualifiedWrapperBuilderName(def, config.getClassNameSuffix(), true);
while (builderInterfaces.contains(qualifiedWrapperName.toLowerCase(Locale.US))) {
qualifiedWrapperName = getResolvedWrapperName(qualifiedWrapperName, config.getClassNameSuffix());
}
TypeSpec.Builder builder = TypeSpec.interfaceBuilder(unqualifiedName(qualifiedWrapperName))
.addModifiers(Modifier.PUBLIC);
generated.addTo(builder);
if (WrapperInterfaceCreator.create(builder, model, config)
.generateInterface(qualifiedContextname(qualifiedName, name))) {
writeType(packageName, builder.build());
}
}
private void generateConsumptionInterface(CdsDefinition def) {
TypeSpec.Builder builder = createInterfaceSpecBuilder(className(def.getName()));
addJavadoc(builder, def, () -> !(def instanceof EventProxy) && readDocs);
String qualifiedTypeName = namesUtils.qualifiedJavaClassName(def);
def.accept(CreateConsumptionInterfaceVisitor.create(builder, config, qualifiedTypeName));
def.getAnnotationValue(CdsConstants.ANNOTATION_JAVA_EXTENDS.substring(1), Collections.emptyList())
.stream().map(model::getStructuredType).map(this::throwErrorOnExtendEntity)
.map(namesUtils::qualifiedJavaClassName).map(ClassName::bestGuess).forEach(builder::addSuperinterface);
generated.addTo(builder);
TypeSpec typeSpec = builder.build();
String packageName = namesUtils.packageName(def);
writeType(packageName, typeSpec);
}
private boolean isFuncOrAction(CdsDefinition def) {
return def.getKind().equals(CdsKind.ACTION) || def.getKind().equals(CdsKind.FUNCTION);
}
private void generateEventContextInterface(CdsDefinition def, CdsDefinition boundEntity) {
if (Cds4jCodegen.isIgnored(def)) {
return;
}
String defName = def.getName();
String defQualifiedName = def.getQualifiedName();
if (isFuncOrAction(def)) {
Pair qualifiedName = NamesUtils.getEffectiveNames(def);
defName = qualifiedName.left;
defQualifiedName = qualifiedName.right;
}
TypeSpec.Builder builder = createInterfaceSpecBuilder(className(defName + CONTEXT_SUFFIX));
addJavadoc(builder, def, () -> readDocs);
String boundEntityName = boundEntity == null ? null : boundEntity.getQualifiedName();
def.accept(CreateEventContextInterfaceVisitor.create(builder, config, boundEntityName, defQualifiedName, namesUtils));
generated.addTo(builder);
TypeSpec typeSpec = builder.build();
String packageName = boundEntity != null ? namesUtils.packageName(boundEntity) : namesUtils.packageName(def);
writeType(packageName, typeSpec);
}
private CdsStructuredType throwErrorOnExtendEntity(CdsStructuredType struct) {
if (struct.getKind().equals(CdsKind.ENTITY) && !struct.as(CdsEntity.class).isAbstract()) {
throw new UnSupportedModelException(
"The '@cds.java.extends' annotation does not support extending an entity.");
}
return struct;
}
private void generateBuilderInterface(CdsDefinition def) {
TypeSpec.Builder builder = createInterfaceSpecBuilder(builderClassName(def));
addJavadoc(builder, def, () -> readDocs);
def.accept(CreateBuilderInterfaceVisitor.create(builder, config, namesUtils));
builderInterfaces
.add(qualifiedWrapperBuilderName(def, config.getClassNameSuffix(), false).toLowerCase(Locale.US));
generated.addTo(builder);
TypeSpec typeSpec = builder.build();
String packageName = namesUtils.packageName(def);
writeType(packageName, typeSpec);
}
private void addJavadoc(TypeSpec.Builder builder, CdsDefinition def, Supplier doRead) {
if (doRead.get()) {
getJavaDoc(def).ifPresent(a -> builder.addJavadoc(a.replace("$", "$$")));
}
}
private static TypeSpec.Builder createInterfaceSpecBuilder(ClassName type) {
return TypeSpec.interfaceBuilder(type).addModifiers(Modifier.PUBLIC);
}
private boolean isTargetAspect(CdsStructuredType struct) {
return model.entities().flatMap(CdsStructuredType::associations)
.map(a -> a.getType().as(CdsAssociationType.class).getTargetAspect())
.anyMatch(ta -> ta.isPresent() && ta.get().getName().equals(struct.getName()));
}
private void writeType(String packageName, TypeSpec typeSpec) {
final JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();
final JavaFileObject fileObject = javaFile.toJavaFileObject();
GeneratedFile jpaFile = new GeneratedFile() {
@Override
public URI getUri() {
return fileObject.toUri();
}
@Override
public InputStream getContent() throws IOException {
return fileObject.openInputStream();
}
};
try {
consumer.accept(jpaFile);
} catch (IOException e) {
String message = String.format("Exception while writing to file %s.", jpaFile.getUri());
throw new EntityWriterException(message, e);
}
}
}