fr.ird.observe.toolkit.eugene.templates.DtoReferenceTransformer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eugene-templates-extension Show documentation
Show all versions of eugene-templates-extension Show documentation
ObServe Eugene templates extension module
package fr.ird.observe.toolkit.eugene.templates;
/*
* #%L
* ObServe Toolkit :: Eugene Templates Extension
* %%
* Copyright (C) 2017 - 2018 IRD, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import fr.ird.observe.binder.DtoReferenceBinder;
import fr.ird.observe.binder.data.DataDtoReferenceBinder;
import fr.ird.observe.binder.referential.ReferentialDtoReferenceBinder;
import fr.ird.observe.dto.IdDto;
import fr.ird.observe.dto.IdHelper;
import fr.ird.observe.dto.data.DataDto;
import fr.ird.observe.dto.reference.DataDtoReference;
import fr.ird.observe.dto.reference.DataDtoReferenceDefinition;
import fr.ird.observe.dto.reference.DtoReference;
import fr.ird.observe.dto.reference.DtoReferenceAware;
import fr.ird.observe.dto.reference.DtoReferenceDefinition;
import fr.ird.observe.dto.reference.ReferentialDtoReference;
import fr.ird.observe.dto.reference.ReferentialDtoReferenceAware;
import fr.ird.observe.dto.reference.ReferentialDtoReferenceDefinition;
import fr.ird.observe.dto.referential.ReferentialDto;
import fr.ird.observe.spi.initializer.DtoReferencesInitializerSupport;
import io.ultreia.java4all.bean.spi.GenerateJavaBeanDefinition;
import io.ultreia.java4all.i18n.spi.builder.I18nKeySet;
import org.nuiton.eugene.TemplateConfiguration;
import org.nuiton.eugene.java.BeanTransformer;
import org.nuiton.eugene.java.BeanTransformerContext;
import org.nuiton.eugene.java.BeanTransformerTagValues;
import org.nuiton.eugene.java.EugeneJavaTagValues;
import org.nuiton.eugene.java.JavaGeneratorUtil;
import org.nuiton.eugene.java.ObjectModelTransformerToJava;
import org.nuiton.eugene.java.extension.ObjectModelAnnotation;
import org.nuiton.eugene.models.object.ObjectModel;
import org.nuiton.eugene.models.object.ObjectModelAttribute;
import org.nuiton.eugene.models.object.ObjectModelClass;
import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.eugene.models.object.ObjectModelPackage;
import org.nuiton.eugene.models.object.xml.ObjectModelAttributeImpl;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
/**
* Generates a reference of a bean.
*
* For example:
*
* GeneratedBoatReference
* BoatReference
*
*
* @author Tony Chemit - [email protected]
* @plexus.component role="org.nuiton.eugene.Template" role-hint="fr.ird.observe.toolkit.eugene.templates.DtoReferenceTransformer"
* @since 1.04
*/
public class DtoReferenceTransformer extends ObjectModelTransformerToJava {
private final EugeneJavaTagValues javaTemplatesTagValues;
private final BeanTransformerTagValues beanTagValues;
private final ObserveTagValues observeTagValues;
private BeanTransformerContext context;
public DtoReferenceTransformer() {
javaTemplatesTagValues = new EugeneJavaTagValues();
beanTagValues = new BeanTransformerTagValues();
observeTagValues = new ObserveTagValues();
}
@Override
public void transformFromModel(ObjectModel model) {
super.transformFromModel(model);
context = new BeanTransformerContext(model, javaTemplatesTagValues, beanTagValues, false, false, input -> {
ObjectModelPackage aPackage = model.getPackage(input.getPackageName());
boolean referential = IdHelper.isReferentialFromPackageName(aPackage.getName());
String referencesTagValue = observeTagValues.getReferencesTagValue(input);
return referencesTagValue != null || referential;
}, getLog());
context.report();
I18nKeySet i18nGetterFile = getConfiguration().getI18nGetterFile();
ImmutableSet.Builder withReferencesBuilder = ImmutableSet.builder();
for (ObjectModelClass input : context.selectedClasses) {
ObjectModelPackage aPackage = getPackage(input);
boolean referential = IdHelper.isReferentialFromPackageName(aPackage.getName());
String referencesTagValue = observeTagValues.getReferencesTagValue(input);
if (referencesTagValue == null && referential) {
referencesTagValue = "code,label,uri";
}
Objects.requireNonNull(referencesTagValue);
withReferencesBuilder.add(input.getQualifiedName());
Set availableProperties = new LinkedHashSet<>(Arrays.asList(referencesTagValue.split(",")));
Set properties = getProperties(input, availableProperties);
String prefix = getConstantPrefix(input);
setConstantPrefix(prefix);
String dtoName = context.classesNameTranslation.get(input);
String className = IdHelper.cleanId(dtoName) + "Reference";
String generatedClassName = "Generated" + className;
Class> superClass = referential ? ReferentialDtoReference.class : DataDtoReference.class;
generateGeneratedClass(input, className, generatedClassName, dtoName, String.format("%s<%s,%s>", superClass.getName(), dtoName, className), properties, referential);
boolean generateClass = notFoundInClassPath(input.getPackageName(), className);
if (generateClass) {
generateClass(input, className, generatedClassName, properties, referential);
}
}
ImmutableSet withReferenceFqns = withReferencesBuilder.build();
String initializerName = "DtoReferencesInitializer";
String generatedInitializerName = "Generated" + initializerName;
ObjectModelPackage aPackage = getModel().getPackage(getDefaultPackageName());
String binderPackageName = DtoReferenceBinder.class.getPackage().getName();
String initializerPackageName = DtoReferencesInitializerSupport.class.getPackage().getName();
generateGeneratedInitializer(initializerPackageName,aPackage, binderPackageName, generatedInitializerName, withReferenceFqns, i18nGetterFile);
boolean generateInitializer = notFoundInClassPath(binderPackageName, initializerName);
if (generateInitializer) {
generateInitializer(initializerPackageName,generatedInitializerName, initializerName);
}
// generate service loader file
generateMetaInfService(getConfiguration(), initializerPackageName + "." + initializerName, DtoReferencesInitializerSupport.class);
}
private void generateGeneratedInitializer(String initializerPackageName,ObjectModelPackage aPackage, String binderPackageName, String generatedInitializerName, ImmutableSet withReferenceFqns,I18nKeySet i18nGetterFile) {
String packageName = aPackage.getName() + ".";
int defaultPackageNameLength = packageName.length();
String modelName = getModel().getName();
ObjectModelClass output = createAbstractClass(generatedInitializerName, initializerPackageName);
setSuperClass(output, DtoReferencesInitializerSupport.class);
addImport(output, Map.class);
addImport(output, ImmutableMap.class);
addImport(output, ImmutableSet.class);
addImport(output, Set.class);
addImport(output, LinkedHashSet.class);
addImport(output, TreeMap.class);
addImport(output, ReferentialDto.class);
addImport(output, DataDto.class);
addImport(output, IdDto.class);
addImport(output, DtoReference.class);
addImport(output, ReferentialDtoReference.class);
addImport(output, DataDtoReference.class);
addImport(output, DtoReferenceDefinition.class);
// addImport(output, ImmutableClassMapping.class);
addImport(output, ReferentialDtoReferenceBinder.class);
addImport(output, DataDtoReferenceBinder.class);
// addImport(output, ImmutableClassMapping.class.getName() + ".Builder");
// addImport(output, I18n.class);
addImport(output, packageName + modelName + "ModelInitializer");
addImport(output, packageName + modelName + "ModelInitializerRunner");
addInterface(output, modelName + "ModelInitializer");
ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
setOperationBody(constructor, ""+"\n"
+" "+modelName+"ModelInitializerRunner.init(this);\n"
+" "
);
ObjectModelOperation startMethod = addOperation(output, "start", "void", ObjectModelJavaModifier.PUBLIC);
addAnnotation(output, startMethod, Override.class);
ObjectModelOperation endMethod = addOperation(output, "end", "void", ObjectModelJavaModifier.PUBLIC);
addAnnotation(output, endMethod, Override.class);
setOperationBody(endMethod, ""+"\n"
+" super.end();\n"
+" "
);
for (String fqn : context.classesFqn) {
ObjectModelClass beanClass = context.classesByFqn.get(fqn);
if (BeanTransformer.skipForInitializer(getModel().getPackage(beanClass), beanClass)) {
continue;
}
String dtoName = context.classesNameTranslation.get(beanClass);
ObjectModelOperation operation = addOperation(output, "init" + dtoName, "void", ObjectModelJavaModifier.PUBLIC);
addAnnotation(output, operation, Override.class);
if (context.selectedClasses.contains(beanClass)) {
addImport(output, fqn + "Reference");
boolean referential = IdHelper.isReferentialFromPackageName(beanClass.getPackageName());
String flushMethodName = referential ? "flushReferential" : "flushData";
String referenceName = beanClass.getName() + "Reference";
i18nGetterFile.addKey("observe.common." + dtoName + ".type");
i18nGetterFile.addKey("observe.common." + dtoName + ".types");
StringBuilder body = new StringBuilder();
body.append("" +"\n"
+" "+flushMethodName+"("+referenceName+".DEFINITION);");
if (withReferenceFqns.contains(fqn)) {
String binderMethodName = referential ? "registerReferentialBinder" : "registerDataBinder";
String binderName = BinderHelper.generateBinderName(defaultPackageNameLength, binderPackageName, fqn, dtoName + "Reference", referential);
addImport(output, binderName);
binderName = JavaGeneratorUtil.getSimpleName(binderName);
body.append("" +"\n"
+" "+binderMethodName+"(new "+binderName+"());\n"
+" ");
}
setOperationBody(operation, body.toString());
}
}
}
private void generateInitializer(String initializerPackageName, String generatedInitializerName, String initializerName) {
ObjectModelClass output = createClass(initializerName, initializerPackageName);
setSuperClass(output, generatedInitializerName);
getLog().debug("will generate " + output.getQualifiedName());
}
protected static void generateMetaInfService(TemplateConfiguration configuration, String outputName, Class type) {
Path outputDirectory = configuration.getProperty(TemplateConfiguration.PROP_OUTPUT_DIRECTORY, File.class).toPath();
Path file = outputDirectory.resolve("META-INF").resolve("services");
try {
if (!Files.exists(file)) {
Files.createDirectories(file);
}
Files.write(file.resolve(type.getName()), outputName.getBytes());
} catch (IOException e) {
throw new IllegalStateException("Could not create file: " + file);
}
}
private void generateClass(ObjectModelClass input, String className, String abstractClassName, Set properties, boolean referential) {
ObjectModelClass output = createClass(className, input.getPackageName());
setSuperClass(output, abstractClassName);
getLog().debug("will generate " + output.getQualifiedName());
ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
addParameter(constructor, referential ? ReferentialDtoReferenceAware.class : DtoReferenceAware.class, "dto");
StringBuilder body = new StringBuilder("\n super(dto, ");
for (ObjectModelAttribute attr : properties) {
addParameter(constructor, attr.getType(), attr.getName());
body.append(String.format("%s, ", attr.getName()));
}
String body1 = body.toString();
setOperationBody(constructor, body1.substring(0, body1.length() - 2) + ");\n");
}
private void generateGeneratedClass(ObjectModelClass input, String className, String abstractClassName, String dtoName, String superClass, Set properties, boolean referential) {
ObjectModelClass output = createAbstractClass(abstractClassName, input.getPackageName());
ObjectModelAnnotation objectModelAnnotation = addAnnotation(output, output, GenerateJavaBeanDefinition.class);
addAnnotationParameter(output, objectModelAnnotation, "types", "{\"" + input.getPackageName() + "." + className + "\"}");
setSuperClass(output, superClass);
Class builderMethodName = referential ?ReferentialDtoReferenceDefinition.class:DataDtoReferenceDefinition.class;
addImport(output, builderMethodName);
StringBuilder definition = new StringBuilder("" +"\n"
+" "+builderMethodName.getSimpleName()+"\n"
+" .builder("+dtoName+".class, "+className+".class) "
);
for (ObjectModelAttribute attr : properties) {
addImport(output, attr.getType());
String type = JavaGeneratorUtil.getSimpleName(attr.getType());
String name = attr.getName();
String getterName;
boolean booleanProperty = JavaGeneratorUtil.isBooleanPrimitive(attr);
if (booleanProperty) {
getterName = JavaGeneratorUtil.OPERATION_GETTER_BOOLEAN_PREFIX;
} else {
getterName = JavaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX;
}
getterName += JavaGeneratorUtil.capitalizeJavaBeanPropertyName(name);
definition.append("" +"\n"
+" .addProperty("+type+".class, \""+name+"\", "+className+"::"+getterName+")"
);
}
definition.append(""+"\n"
+" .build()"
);
String definitionType = String.format("%s<%s, %s>", builderMethodName.getName(), dtoName, className);
addAttribute(output, "DEFINITION", definitionType, definition.toString(), ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC, ObjectModelJavaModifier.FINAL);
getLog().debug("will generate " + output.getQualifiedName());
ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PROTECTED);
addParameter(constructor, referential ? ReferentialDtoReferenceAware.class : DtoReferenceAware.class, "dto");
StringBuilder body = new StringBuilder(""+"\n"
+" super(dto);");
for (ObjectModelAttribute attr : properties) {
createProperty(output, attr);
addParameter(constructor, attr.getType(), attr.getName());
String name = attr.getName();
body.append(""+"\n"
+" this."+name+" = "+name+";"
);
}
body.append(""+"\n"
+" ");
setOperationBody(constructor, body.toString());
ObjectModelOperation getDefinition = addOperation(output, "getDefinition", definitionType, ObjectModelJavaModifier.PUBLIC);
addAnnotation(output, getDefinition, Override.class);
setOperationBody(getDefinition, ""+"\n"
+" return DEFINITION;\n"
+" "
);
}
private String getAttributeType(ObjectModelAttribute attr) {
String attrType = attr.getType();
return getAttributeType(attrType);
}
private String getAttributeType(String attrType) {
if (!JavaGeneratorUtil.isPrimitiveType(attrType)) {
boolean hasClass = model.hasClass(attrType);
if (hasClass) {
ObjectModelClass attributeClass = model.getClass(attrType);
String attributeType = context.classesNameTranslation.get(attributeClass);
if (attributeType != null) {
attrType = attributeClass.getPackageName() + "." + attributeType;
}
}
}
return attrType;
}
private boolean notFoundInClassPath(String input, String className) {
String fqn = input + "." + className;
boolean inClassPath = getResourcesHelper().isJavaFileInClassPath(fqn);
return !inClassPath;
}
private void createProperty(ObjectModelClass output, ObjectModelAttribute attr) {
String attrName = attr.getName();
String attrType = getAttributeTypeWithGeneric(attr);
boolean booleanProperty = JavaGeneratorUtil.isBooleanPrimitive(attr);
if (booleanProperty) {
createGetMethod(output, attrName, attrType, JavaGeneratorUtil.OPERATION_GETTER_BOOLEAN_PREFIX);
} else {
createGetMethod(output, attrName, attrType, JavaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX);
}
addAttribute(output, attrName, attrType, "", ObjectModelJavaModifier.PRIVATE, ObjectModelJavaModifier.FINAL);
}
private Set getProperties(ObjectModelClass input, Set availableProperties) {
Collection attributes = new LinkedList<>(input.getAttributes());
attributes.addAll(input.getAllOtherAttributes());
Map properties = new TreeMap<>();
for (ObjectModelAttribute attr : attributes) {
if (attr.isNavigable()) {
String attrName = attr.getName();
String type = attr.getType();
if (context.selectedClassesFqn.contains(IdHelper.cleanId(type))) {
// Dto
if (availableProperties.contains(attrName + "Label")) {
ObjectModelAttributeImpl newAttr = new ObjectModelAttributeImpl();
newAttr.setName(attrName + "Label");
newAttr.setType("String");
// only keep navigable attributes
properties.put(newAttr.getName(), newAttr);
}
if (availableProperties.contains(attrName + "Id")) {
ObjectModelAttributeImpl newAttr = new ObjectModelAttributeImpl();
newAttr.setName(attrName + "Id");
newAttr.setType("String");
// only keep navigable attributes
properties.put(newAttr.getName(), newAttr);
}
// Get a ref
if (availableProperties.contains(attrName)) {
ObjectModelAttributeImpl newAttr = new ObjectModelAttributeImpl();
newAttr.setName(attrName);
if (!type.endsWith("Reference")) {
// not a ref, get a ref
type = IdHelper.cleanId(type) + "Reference";
}
newAttr.setType(getAttributeType(type));
properties.put(attrName, newAttr);
}
continue;
}
// Simple type
if (availableProperties.contains(attrName)) {
ObjectModelAttributeImpl newAttr = new ObjectModelAttributeImpl();
newAttr.setName(attrName);
newAttr.setType(type);
properties.put(attrName, newAttr);
}
}
}
if (availableProperties.contains("label") && properties.values().stream().noneMatch(p -> p.getName().equals("label"))) {
ObjectModelAttributeImpl attr = new ObjectModelAttributeImpl();
attr.setName("label");
attr.setType("String");
properties.put("label", attr);
}
Set result = new LinkedHashSet<>();
for (String availableProperty : availableProperties) {
ObjectModelAttribute e = properties.get(availableProperty);
if (e == null) {
ObjectModelAttributeImpl ee = new ObjectModelAttributeImpl();
ee.setName(availableProperty);
ee.setType("String");
e = ee;
}
Objects.requireNonNull(e, "Cant' find property " + availableProperty + " on " + input.getQualifiedName());
result.add(e);
}
return result;
}
private void createGetMethod(ObjectModelClass output, String attrName, String attrType, String methodPrefix) {
ObjectModelOperation operation = addOperation(
output,
getJavaBeanMethodName(methodPrefix, attrName),
attrType,
ObjectModelJavaModifier.PUBLIC
);
setOperationBody(operation, ""
+"\n"
+" return "+attrName+";\n"
+" "
);
}
private String getAttributeTypeWithGeneric(ObjectModelAttribute attr) {
String attrType = getAttributeType(attr);
String generic = eugeneTagValues.getAttributeGenericTagValue(attr);
if (generic != null) {
attrType += "<" + getAttributeType(generic) + ">";
}
return attrType;
}
}