All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.ird.observe.toolkit.eugene.templates.dto.DtoDefinitionTransformer Maven / Gradle / Ivy

The newest version!
/*-
 * #%L
 * ObServe Toolkit :: Eugene Templates
 * %%
 * Copyright (C) 2008 - 2017 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%
 */

package fr.ird.observe.toolkit.eugene.templates.dto;

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.IdHelper;
import fr.ird.observe.dto.form.FormDefinition;
import fr.ird.observe.dto.reference.DataDtoReferenceDefinition;
import fr.ird.observe.dto.reference.ReferentialDtoReferenceDefinition;
import fr.ird.observe.spi.dto.DataDtoDefinition;
import fr.ird.observe.spi.dto.DataDtoWithNoReferenceDefinition;
import fr.ird.observe.spi.dto.DtoModuleHelper;
import fr.ird.observe.spi.dto.ReferentialDtoDefinition;
import fr.ird.observe.toolkit.eugene.templates.DtoHelper;
import fr.ird.observe.toolkit.eugene.templates.DtoModel;
import fr.ird.observe.toolkit.eugene.templates.DtoTransformerContext;
import fr.ird.observe.toolkit.eugene.templates.ToolkitTagValues;
import org.nuiton.eugene.EugeneCoreTagValues;
import org.nuiton.eugene.GeneratorUtil;
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.models.object.ObjectModel;
import org.nuiton.eugene.models.object.ObjectModelAttribute;
import org.nuiton.eugene.models.object.ObjectModelClass;
import org.nuiton.eugene.models.object.ObjectModelClassifier;
import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.eugene.models.object.ObjectModelPackage;
import org.nuiton.i18n.spi.GetterFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;




/**
 * @author Tony Chemit - [email protected]
 * @plexus.component role="org.nuiton.eugene.Template" role-hint="fr.ird.observe.toolkit.eugene.templates.dto.DtoDefinitionTransformer"
 * @since 3.0
 */
public class DtoDefinitionTransformer extends ObjectModelTransformerToJava {

    private DtoModel dtoModel;
    private GetterFile i18nGetterFile;

    @Override
    public void transformFromModel(ObjectModel model) {

        super.transformFromModel(model);

        i18nGetterFile = getConfiguration().getProperty(PROP_I18N_GETTER_FILE, GetterFile.class);

        dtoModel = new DtoModel(
                new EugeneCoreTagValues(),
                new EugeneJavaTagValues(),
                new BeanTransformerTagValues(),
                new ToolkitTagValues(),
                getLog(),
                model,
                getDefaultPackageName());

        DtoTransformerContext context = dtoModel.context;
        context.report();

        List referentialDefinitionClasses = new LinkedList<>();
        List dataDefinitionClasses = new LinkedList<>();
        List dataWithoutReferenceDefinitionClasses = new LinkedList<>();

        for (ObjectModelClass input : context.selectedClasses) {

            ObjectModelPackage aPackage = getPackage(input);

            if (EugeneCoreTagValues.isSkip(input, aPackage) || input.isAbstract()) {
                continue;
            }

            if (IdHelper.isReferentialFromPackageName(input.getPackageName())) {
                referentialDefinitionClasses.add(input);
            } else {

                String referencesTagValue = dtoModel.toolkitTagValues.getReferencesTagValue(input);
                boolean withoutReference = referencesTagValue == null;
                if (withoutReference) {
                    dataWithoutReferenceDefinitionClasses.add(input);
                } else {
                    dataDefinitionClasses.add(input);
                }
            }
        }

        getLog().info(String.format("Found %d referential dto.", referentialDefinitionClasses.size()));
        for (ObjectModelClass input : referentialDefinitionClasses) {
            generateReferentialDtoDefinitionClass(context, input);
        }

        getLog().info(String.format("Found %d data dto.", dataDefinitionClasses.size()));
        for (ObjectModelClass input : dataDefinitionClasses) {
            generateDataDtoDefinitionClass(context, input);
        }
        getLog().info(String.format("Found %d data (without reference) dto.", dataWithoutReferenceDefinitionClasses.size()));
        for (ObjectModelClass input : dataWithoutReferenceDefinitionClasses) {
            generateDataDtoWithoutReferenceDefinitionClass(context, input);
        }

        Function function = i -> i.getQualifiedName() + "DtoDefinition";
        storeServiceLoaderFile(DataDtoDefinition.class, dataDefinitionClasses, function);
        storeServiceLoaderFile(DataDtoWithNoReferenceDefinition.class, dataWithoutReferenceDefinitionClasses, function);
        storeServiceLoaderFile(ReferentialDtoDefinition.class, referentialDefinitionClasses, function);
    }

    private void generateDataDtoDefinitionClass(DtoTransformerContext context, ObjectModelClass input) {

        String prefix = getConstantPrefix(input);
        setConstantPrefix(prefix);

        String dtoType = input.getPackageName() + "." + input.getName() + "Dto";
        String dtoTypeSimpleName = GeneratorUtil.getSimpleName(dtoType);

        i18nGetterFile.addKey("observe.common." + dtoTypeSimpleName + ".type");
        i18nGetterFile.addKey("observe.common." + dtoTypeSimpleName + ".types");

        String referenceType = dtoType.replace("Dto", "Reference");
        String referenceTypeSimpleName = GeneratorUtil.getSimpleName(referenceType);

        List properties = getAllProperties(input);

        String referencesTagValue = dtoModel.toolkitTagValues.getReferencesTagValue(input);

        String superClass = String.format("%s<%s, %s>", DataDtoDefinition.class.getName(), dtoType, referenceType);

        String className = input.getName() + "DtoDefinition";
        ObjectModelClass output = createClass(className, input.getPackageName());
        setSuperClass(output, superClass);

        getLog().debug("will generate " + output.getQualifiedName());

        addInstance(output, DataDtoDefinition.class.getSimpleName(), dtoTypeSimpleName, "fromDataDto");

        int selectedPriority = dtoModel.toolkitTagValues.getSelectedTagValue(input);
        int openPriority = dtoModel.toolkitTagValues.getOpenTagValue(input);
        String parentDtoType;
        if (selectedPriority > 0) {
            String selectedParentTagValue = dtoModel.toolkitTagValues.getSelectedParentTagValue(input);
            Objects.requireNonNull(selectedParentTagValue, "Can't find a parent for a selected data " + input.getQualifiedName());
            parentDtoType = JavaGeneratorUtil.getSimpleName(selectedParentTagValue) + ".class";
            addImport(output, selectedParentTagValue);
        } else {
            parentDtoType = "null";
        }

        ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
        String referenceParam = referenceTypeSimpleName + ".class";
        setOperationBody(constructor, ""+"\n"
+"        super("+parentDtoType+", "+dtoTypeSimpleName+".class, "+referenceParam+", "+selectedPriority+", "+openPriority+");\n"
+"    ");

        addComputeDtoGettersMethod(output, dtoTypeSimpleName, properties);
        addComputeDtoSettersMethod(output, dtoTypeSimpleName, properties);

        Set availableReferenceProperties = new LinkedHashSet<>(Arrays.asList(referencesTagValue.split(",")));
        Set referenceProperties = DtoHelper.getReferenceProperties(context, input, availableReferenceProperties);
        addComputeReferenceGettersMethod(output, referenceTypeSimpleName, referenceProperties);
        addComputeReferenceBinder(output, dtoType, referenceType, false);
        addComputeReferenceDefinition(output, dtoType, referenceTypeSimpleName, referenceProperties, false);
        addComputeFormDefinition(input, output, dtoTypeSimpleName, referenceTypeSimpleName, false);
    }

    private void generateDataDtoWithoutReferenceDefinitionClass(DtoTransformerContext context, ObjectModelClass input) {

        String prefix = getConstantPrefix(input);
        setConstantPrefix(prefix);

        String dtoType = input.getPackageName() + "." + input.getName() + "Dto";
        String dtoTypeSimpleName = GeneratorUtil.getSimpleName(dtoType);

        String referenceType = dtoType.replace("Dto", "Reference");
        String referenceTypeSimpleName = GeneratorUtil.getSimpleName(referenceType);

        List properties = getAllProperties(input);

        String superClass = String.format("%s<%s>", DataDtoWithNoReferenceDefinition.class.getName(), dtoType);

        String className = input.getName() + "DtoDefinition";
        ObjectModelClass output = createClass(className, input.getPackageName());
        setSuperClass(output, superClass);
        addImport(output, DtoModuleHelper.class);

        getLog().debug("will generate " + output.getQualifiedName());

        addInstance(output, DataDtoWithNoReferenceDefinition.class.getSimpleName(), dtoTypeSimpleName, "fromDataDtoWithoutReference");

        ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
        setOperationBody(constructor, ""+"\n"
+"        super("+dtoTypeSimpleName+".class);\n"
+"    ");

        addComputeDtoGettersMethod(output, dtoTypeSimpleName, properties);
        addComputeDtoSettersMethod(output, dtoTypeSimpleName, properties);
        addComputeFormDefinition(input, output, dtoTypeSimpleName, referenceTypeSimpleName, false);

    }

    private void generateReferentialDtoDefinitionClass(DtoTransformerContext context, ObjectModelClass input) {

        String prefix = getConstantPrefix(input);
        setConstantPrefix(prefix);

        String dtoType = input.getPackageName() + "." + input.getName() + "Dto";
        String dtoTypeSimpleName = GeneratorUtil.getSimpleName(dtoType);

        i18nGetterFile.addKey("observe.common." + dtoTypeSimpleName + ".type");
        i18nGetterFile.addKey("observe.common." + dtoTypeSimpleName + ".types");

        String referenceType = dtoType.replace("Dto", "Reference");
        String referenceTypeSimpleName = GeneratorUtil.getSimpleName(referenceType);
        List properties = getAllProperties(input);

        String referencesTagValue = dtoModel.toolkitTagValues.getReferencesTagValue(input);
        if (referencesTagValue == null) {
            referencesTagValue = "code,label";
        }

        String superClass = String.format("%s<%s, %s>", ReferentialDtoDefinition.class.getName(), dtoType, referenceType);

        String className = input.getName() + "DtoDefinition";
        ObjectModelClass output = createClass(className, input.getPackageName());
        setSuperClass(output, superClass);
        getLog().debug("will generate " + output.getQualifiedName());
        addInstance(output, ReferentialDtoDefinition.class.getSimpleName(), dtoTypeSimpleName, "fromReferentialDto");

        int selectedPriority = dtoModel.toolkitTagValues.getSelectedTagValue(input);
        int openPriority = dtoModel.toolkitTagValues.getOpenTagValue(input);

        ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
        String referenceParam = referenceTypeSimpleName + ".class";
        setOperationBody(constructor, ""+"\n"
+"        super(null, "+dtoTypeSimpleName+".class, "+referenceParam+", "+selectedPriority+", "+openPriority+");\n"
+"    ");

        addComputeDtoGettersMethod(output, dtoTypeSimpleName, properties);
        addComputeDtoSettersMethod(output, dtoTypeSimpleName, properties);

        Set referenceProperties;

        Set availableReferenceProperties = new LinkedHashSet<>(Arrays.asList(referencesTagValue.split(",")));
        referenceProperties = DtoHelper.getReferenceProperties(context, input, availableReferenceProperties);

        addComputeReferenceGettersMethod(output, referenceTypeSimpleName, referenceProperties);
        addComputeReferenceBinder(output, dtoType, referenceType, true);
        addComputeReferenceDefinition(output, dtoType, referenceTypeSimpleName, referenceProperties, true);

        addComputeFormDefinition(input, output, dtoTypeSimpleName, referenceTypeSimpleName, true);

    }

    private void addInstance(ObjectModelClass output, String type, String dtoTypeSimpleName, String method) {
        addImport(output, DtoModuleHelper.class);


        addAttribute(output, "INSTANCE", output.getName(), null, ObjectModelJavaModifier.PRIVATE, ObjectModelJavaModifier.STATIC);
        ObjectModelOperation get = addOperation(output, "get", output.getName(), ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC, ObjectModelJavaModifier.FINAL);
        setOperationBody(get, ""+"\n"
+"        if (INSTANCE == null) {\n"
+"            "+type+" context = DtoModuleHelper."+method+"("+dtoTypeSimpleName+".class);\n"
+"            INSTANCE = ("+output.getName()+") context;\n"
+"        }\n"
+"        return INSTANCE;\n"
+"    ");
    }


    private void addComputeDtoGettersMethod(ObjectModelClass output, String dtoType, List properties) {
        ObjectModelOperation computeDtoGetters = addOperation(output, "computeDtoGetters", getGettersMapType(dtoType), ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, computeDtoGetters, Override.class);
        String dtoGettersMapCode = generateGettersMap(output, dtoType, properties);
        setOperationBody(computeDtoGetters, ""+"\n"
+"        return "+dtoGettersMapCode+";\n"
+"    ");
    }

    private void addComputeDtoSettersMethod(ObjectModelClass output, String dtoType, List properties) {
        ObjectModelOperation computeDtoSetters = addOperation(output, "computeDtoSetters", getSettersMapType(dtoType), ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, computeDtoSetters, Override.class);
        String dtoSettersMapCode = generateSettersMap(output, dtoType, properties, e -> getPropertyType(output, e));
        setOperationBody(computeDtoSetters, ""+"\n"
+"        return "+dtoSettersMapCode+";\n"
+"    ");
    }

    private void addComputeFormDefinition(ObjectModelClass input, ObjectModelClass output, String dtoType, String referenceType, boolean referential) {

        String formTagValue = null;
        boolean withForm = dtoModel.formContext.selectedClasses.contains(input);
        if (withForm) {
            formTagValue = dtoModel.toolkitTagValues.getFormTagValue(input, getPackage(input.getPackageName())).trim();
        }
        StringBuilder bodyBuilder = new StringBuilder();

        if (formTagValue != null) {
            Map properties;
            boolean mainForm = formTagValue.equals("self");
            boolean withProperties;

            if (referential || mainForm) {

                properties = DtoHelper.getFormProperties(input);
                getLog().debug(String.format("form: %s, found %d properties.", dtoType, properties.size()));

                withProperties = !properties.isEmpty();
                if (referential || withProperties) {

                    if (referential) {
                        bodyBuilder.append(""+"\n"
+"        return () -> FormDefinition.referentialBuilder("+dtoType+".class, "+referenceType+".class)");

                    } else {
                        bodyBuilder.append(""+"\n"
+"        return () -> FormDefinition.builder("+dtoType+".class)");
                        i18nGetterFile.addKey("observe.common." + dtoType + ".title");
                    }

                    for (Map.Entry entry : properties.entrySet()) {
                        String propertyName = entry.getKey();
                        String type = entry.getValue();
                        propertyName = JavaGeneratorUtil.convertVariableNameToConstantName("property" + JavaGeneratorUtil.capitalizeJavaBeanPropertyName(propertyName));
                        bodyBuilder.append(""+"\n"
+"                .addProperty("+dtoType+"."+propertyName+", "+type+".class)");
                    }
                    bodyBuilder.append(""+"\n"
+"                .build();\n"
+"    ");
                }
            }
        }

        if (bodyBuilder.length() == 0) {
            bodyBuilder.append(""+"\n"
+"        return () -> null;\n"
+"    ");
        }

        String methodType = "Supplier>";
        addImport(output, Supplier.class);
        addImport(output, FormDefinition.class);
        ObjectModelOperation computeFormDefinition = addOperation(output, "computeFormDefinition", methodType, ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, computeFormDefinition, Override.class);
        setOperationBody(computeFormDefinition, bodyBuilder.toString());
    }

    private void addComputeReferenceBinder(ObjectModelClass output, String dtoType, String referenceType, boolean referential) {

        String binderPackageName = DtoReferenceBinder.class.getPackage().getName();
        String binderFieldType = referenceType == null ? (referential ? ReferentialDtoReferenceBinder.class.getName() : DataDtoReferenceBinder.class.getName()) : DtoHelper.generateBinderName(dtoModel.context.defaultPackage.getName().length(), binderPackageName, dtoType, GeneratorUtil.getSimpleName(dtoType) + "Reference", referential);
        addImport(output, binderFieldType);
        binderFieldType = JavaGeneratorUtil.getSimpleName(binderFieldType);

        ObjectModelOperation computeReferenceBinder = addOperation(output, "computeReferenceBinder", binderFieldType, ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, computeReferenceBinder, Override.class);
        String binderInstance = referenceType == null ? "null" : "new " + binderFieldType + "()";
        setOperationBody(computeReferenceBinder, ""+"\n"
+"        return "+binderInstance+";\n"
+"    ");
        if (referenceType != null) {
            ObjectModelOperation getReferenceBinder = addOperation(output, "toReferenceBinder", binderFieldType, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
            addAnnotation(output, getReferenceBinder, Override.class);
            setOperationBody(getReferenceBinder, ""+"\n"
+"        return ("+binderFieldType+") super.toReferenceBinder();\n"
+"    ");
        }

    }

    private void addComputeReferenceDefinition(ObjectModelClass output, String dtoType, String referenceType, Set referenceProperties, boolean referential) {

        Class builderMethodName = referential ? ReferentialDtoReferenceDefinition.class : DataDtoReferenceDefinition.class;
        addImport(output, builderMethodName);
        StringBuilder definition;
        if (referenceType == null) {
            definition = new StringBuilder("" +"\n"
+"        return null;\n"
+"     "
            );

        } else {

            definition = new StringBuilder("" +"\n"
+"        return "+builderMethodName.getSimpleName()+"\n"
+"                .builder("+dtoType+".class, "+referenceType+".class)"
            );

            for (ObjectModelAttribute attr : referenceProperties) {
                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);
                name = JavaGeneratorUtil.convertVariableNameToConstantName("property" + JavaGeneratorUtil.capitalizeJavaBeanPropertyName(name));
                definition.append("" +"\n"
+"                .addProperty("+type+".class, "+referenceType+"."+name+", "+referenceType+"::"+getterName+")"
                );
            }
            definition.append(""+"\n"
+"                .build();"
            );

        }

        String definitionType = builderMethodName.getName();
        if (referenceType != null) {
            definitionType = String.format("%s<%s, %s>", definitionType, dtoType, referenceType);
        }

        ObjectModelOperation computeReferenceDefinition = addOperation(output, "computeReferenceDefinition", definitionType, ObjectModelJavaModifier.FINAL, ObjectModelJavaModifier.PROTECTED);
        addAnnotation(output, computeReferenceDefinition, Override.class);
        setOperationBody(computeReferenceDefinition, definition.toString());
    }

    private void addComputeReferenceGettersMethod(ObjectModelClass output, String referenceType, Set referenceProperties) {

        String methodType = referenceType == null ? "ImmutableMap>" : getGettersMapType(referenceType);
        ObjectModelOperation computeReferenceGetters = addOperation(output, "computeReferenceGetters", methodType, ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, computeReferenceGetters, Override.class);
        String referenceGettersMapCode = referenceType == null ? "null" : generateGettersMap(output, referenceType, referenceProperties);
        setOperationBody(computeReferenceGetters, ""+"\n"
+"        return "+referenceGettersMapCode+";\n"
+"    ");
    }

    private List getAllProperties(ObjectModelClass input) {
        List attributes =
                (List) input.getAllOtherAttributes();

        List result = new ArrayList<>();
        for (ObjectModelAttribute attr : attributes) {
            if (attr.isNavigable()) {
                // only keep navigable attributes
                result.add(attr);
            }
        }
        result.addAll(getProperties(input));
        return result;
    }

    private String getAttributeTypeWithGeneric(ObjectModelAttribute attr) {
        String attrType = dtoModel.context.getAttributeType(attr);
        String generic = eugeneTagValues.getAttributeGenericTagValue(attr);
        if (generic != null) {
            attrType += String.format("<%s>", dtoModel.context.getAttributeType(generic));
        }
        return attrType;
    }

    private List getProperties(ObjectModelClass input) {
        List attributes =
                (List) input.getAttributes();

        List attrs =
                new ArrayList<>();
        for (ObjectModelAttribute attr : attributes) {
            if (attr.isNavigable()) {

                // only keep navigable attributes
                attrs.add(attr);
            }
        }
        return attrs;
    }

    private String getPropertyType(ObjectModelClass output, ObjectModelAttribute attr) {
        boolean multiple = JavaGeneratorUtil.isNMultiplicity(attr);
        String attrType = getAttributeTypeWithGeneric(attr);
        if (multiple) {
            attrType = JavaGeneratorUtil.getAttributeInterfaceType(attr, getAttributeTypeWithGeneric(attr), true);
        }
        addImport(output, attrType);
        return JavaGeneratorUtil.getSimpleName(attrType);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy