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

fr.ird.observe.toolkit.templates.dto.DtoTransformer Maven / Gradle / Ivy

There is a newer version: 4.34
Show newest version
package fr.ird.observe.toolkit.templates.dto;

/*-
 * #%L
 * ObServe Toolkit :: Templates
 * %%
 * Copyright (C) 2017 - 2020 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 fr.ird.observe.dto.DtoToReference;
import fr.ird.observe.dto.IdDto;
import fr.ird.observe.dto.form.FormDefinition;
import fr.ird.observe.dto.referential.ReferentialLocale;
import fr.ird.observe.spi.ProjectPackagesDefinition;
import fr.ird.observe.spi.mapping.DtoToFormDtoMapping;
import fr.ird.observe.spi.mapping.DtoToMainDtoClassMapping;
import fr.ird.observe.toolkit.templates.ObserveTagValues;
import fr.ird.observe.toolkit.templates.TemplateContract;
import io.ultreia.java4all.classmapping.ImmutableClassMapping;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.plexus.component.annotations.Component;
import org.nuiton.eugene.EugeneCoreTagValues;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.Template;
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.JavaBuilder;
import org.nuiton.eugene.java.JavaGeneratorUtil;
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.ObjectModelClassifier;
import org.nuiton.eugene.models.object.ObjectModelElement;
import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.eugene.models.object.ObjectModelPackage;
import org.nuiton.eugene.models.object.ObjectModelParameter;
import org.nuiton.eugene.models.object.xml.ObjectModelAttributeImpl;
import org.nuiton.eugene.models.object.xml.ObjectModelClassifierImpl;
import org.nuiton.eugene.models.object.xml.ObjectModelOperationImpl;
import org.nuiton.eugene.models.object.xml.ObjectModelParameterImpl;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;

import static fr.ird.observe.toolkit.templates.TemplateContract.isReferentialFromPackageName;





/**
 * @author Tony Chemit - [email protected]
 * @since ?
 */
@Component(role = Template.class, hint = "fr.ird.observe.toolkit.templates.dto.DtoTransformer")
public class DtoTransformer extends BeanTransformer implements TemplateContract {

    private final EugeneCoreTagValues coreTagValues;
    private final EugeneJavaTagValues javaTemplatesTagValues;
    private final BeanTransformerTagValues beanTagValues;
    private final ObserveTagValues observeTagValues;

    private BeanTransformerContext context;
    private final Map dtoFormMapping = new TreeMap<>();
    private final Map dtoMainMapping = new TreeMap<>();

    public DtoTransformer() {
        coreTagValues = new EugeneCoreTagValues();
        javaTemplatesTagValues = new EugeneJavaTagValues();
        beanTagValues = new BeanTransformerTagValues();
        observeTagValues = new ObserveTagValues();
    }

    @Override
    public void transformFromModel(ObjectModel model) {
        super.transformFromModel(model);

        context = new BeanTransformerContext(model, coreTagValues, javaTemplatesTagValues, beanTagValues, false, false, input -> {

            ObjectModelPackage aPackage = model.getPackage(input.getPackageName());

            boolean referential = isReferentialFromPackageName(aPackage.getName());

            String referencesTagValue = observeTagValues.getReferencesTagValue(input);
            return referencesTagValue != null || referential;
        }, getLog());

        context.report();

    }

    @Override
    protected void debugOutputModel() {
        super.debugOutputModel();

        generateClassMapping(true, model.getName() + " dto → form dto mapping", DtoToFormDtoMapping.class, "Class", FormDefinition.class.getName() + "", ImmutableMap.class, "build", dtoFormMapping);
        generateClassMapping(true, model.getName() + " dto → main dto class mapping", DtoToMainDtoClassMapping.class, IdDto.class.getName(), IdDto.class.getName(), ImmutableClassMapping.class, "getMappingBuilder", dtoMainMapping);

    }

    @Override
    protected ObjectModelClass generateGeneratedClass(ObjectModelPackage aPackage, ObjectModelClass input, String className, String mainClassName) {
        ObjectModelClass output = super.generateGeneratedClass(aPackage, input, className, mainClassName);
        boolean referential = TemplateContract.isReferentialFromPackageName(aPackage.getName());
        if (context.selectedClasses.contains(input)) {

            String referencesTagValue = observeTagValues.getReferencesTagValue(input);
            if (referencesTagValue == null && referential) {
                referencesTagValue = "code,label,uri";
            }

            Objects.requireNonNull(referencesTagValue);

            Set availableProperties = new LinkedHashSet<>(Arrays.asList(referencesTagValue.split(",")));

            Map binderProperties = DtoReferenceTransformer.getReferenceProperties(context.selectedClassesFqn, input, availableProperties, this::getAttributeType);

            String dtoName = context.classesNameTranslation.get(input);
            String referenceName = ProjectPackagesDefinition.cleanType(dtoName) + "Reference";

            addInterface(output, String.format("%s<%s, %s>", DtoToReference.class.getName(), mainClassName, referenceName));
            addToReferenceMethod(output, binderProperties, referenceName);
        }
        if (!input.isAbstract()) {
            addFormDefinitionAttribute(input, output, referential);
            addMainDtoMapping(input, referential);

        }
        return output;
    }

    private void addMainDtoMapping(ObjectModelClass input, boolean referential) {
        String mainDtoTagValue = observeTagValues.getMainDtoTagValue(input);
        if (Objects.equals("skip", mainDtoTagValue)) {
            return;
        }
        String dtoType = input.getPackageName() + "." + context.classesNameTranslation.get(input);
        String mainDtoType;

        if (mainDtoTagValue == null) {
            // this is a main dto
            mainDtoType = dtoType;
        } else {
            mainDtoType = getDefaultPackageName() + "." + mainDtoTagValue;
            ObjectModelClass mainDtoClass = context.classesByFqn.get(mainDtoType);
            mainDtoType = mainDtoClass.getPackageName() + "." + context.classesNameTranslation.get(mainDtoClass);
        }
        dtoMainMapping.put(dtoType, mainDtoType + ".class");
    }

    private void addFormDefinitionAttribute(ObjectModelClass input, ObjectModelClass output, boolean referential) {
        ObjectModelPackage thisPackage = getPackage(input);

        String formTagValue = observeTagValues.getFormTagValue(input, thisPackage);
        getLog().debug("FormTagValue: " + formTagValue);

        ObjectModelPackage defaultPackage = getModel().getPackage(getDefaultPackageName());

        String packageName = defaultPackage.getName() + ".";

        String formType;
        String dtoType = thisPackage.getName() + "." + context.classesNameTranslation.get(input);
        String dtoTypeSimpleName = GeneratorUtil.getSimpleName(thisPackage.getName() + "." + context.classesNameTranslation.get(input));

        boolean addForm = false;

        Map properties;
        if (formTagValue != null && formTagValue.equals("self")) {

            formType = dtoType;
            properties = getFormProperties(input, output);

            getLog().debug(String.format("form: %s, found %d properties.", formType, properties.size()));

            addForm = referential || !properties.isEmpty();
            if (addForm) {
                StringBuilder bodyBuilder = new StringBuilder();
                bodyBuilder.append(""+"\n"
+"        new FormDefinition.Builder<>("+dtoTypeSimpleName+".class)");


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

                bodyBuilder.append(""+"\n"
+"                .build()");

                addImport(output, formType);
                addAttribute(output, "FORM_DEFINITION", "FormDefinition<" + GeneratorUtil.getSimpleName(formType) + ">", bodyBuilder.toString(), ObjectModelJavaModifier.STATIC, ObjectModelJavaModifier.FINAL, ObjectModelJavaModifier.PUBLIC);
            }
        } else {
            ObjectModelClass aClass = getModel().getClass(packageName + formTagValue);
            formType = aClass.getPackageName() + "." + context.classesNameTranslation.get(aClass);
//            addImport(output, formType);
            addForm = true;
        }


        if (addForm) {
            addImport(output, FormDefinition.class);
            dtoFormMapping.put(dtoType, formType + ".FORM_DEFINITION");
        }
    }

    private void addToReferenceMethod(ObjectModelClass output, Map properties, String referenceName) {

        boolean useRelativeName = context.useRelativeName;

        ObjectModelOperation operation = addOperation(output, "toReference", referenceName, ObjectModelJavaModifier.PUBLIC);
        addAnnotation(output, operation, Override.class);
        addParameter(operation, ReferentialLocale.class, "referentialLocale");

        int prefixLength = ("        return new " + referenceName + "(").length();
        String prefix = StringUtils.leftPad("", prefixLength, " ");

        StringBuilder body = new StringBuilder(""+"\n"
+"        return new "+referenceName+"(this,");

        Iterator> iterator = properties.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            ObjectModelAttribute attr = entry.getKey();
            ObjectModelAttribute dtoAttr = entry.getValue();
            String content = "";
            if (dtoAttr != null) {
                String getterName;
                String type = useRelativeName ? dtoAttr.getType() : JavaGeneratorUtil.getSimpleName(dtoAttr.getType());
                String dtoAttrname = dtoAttr.getName();
                boolean booleanProperty = JavaGeneratorUtil.isBooleanPrimitive(dtoAttr);

                if (booleanProperty) {
                    getterName = JavaGeneratorUtil.OPERATION_GETTER_BOOLEAN_PREFIX;
                } else {
                    getterName = JavaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX;
                }
                getterName += JavaGeneratorUtil.capitalizeJavaBeanPropertyName(dtoAttrname);
                content = getterName + "()";
                if (context.selectedClassesFqn.contains(ProjectPackagesDefinition.cleanType(dtoAttr.getType()))) {

                    // Dto

                    if (!type.endsWith("Reference")) {

                        // not a ref, get a ref
                        type = ProjectPackagesDefinition.cleanType(type) + "Reference";
                    }

                    if (attr.getName().equals(dtoAttrname + "Id")) {
                        addImport(output, Optional.class);
                        addImport(output, IdDto.class);
                        content = String.format("Optional.ofNullable(%s).map(%s::getId).orElse(null)", content, type);
                    } else if (attr.getName().equals(dtoAttrname + "Label")) {
                        addImport(output, Optional.class);
                        addImport(output, IdDto.class);
                        String method = "getLabel";
                        if (type.equals("SpeciesReference")) {
                            method = "getScientificLabel";
                        }
                        content = String.format("Optional.ofNullable(%s).map(%s::%s).orElse(null)", content, type, method);
                    } else if (attr.getName().equals(dtoAttrname + "Code")) {
                        addImport(output, Optional.class);
                        addImport(output, IdDto.class);
                        content = String.format("Optional.ofNullable(%s).map(%s::getCode).orElse(null)", content, type);
                    }
                }

            } else {

                if (attr.getName().equals("label")) {
                    content = "getLabel(referentialLocale)";
                } else {
                    content = "null";
                }
            }
            content = prefix + content;
            String coma = iterator.hasNext() ? "," : "";
            body.append(""+"\n"
+""+content+""+coma+""
            );
        }
        body.append(""+");\n"
+"    ");

        setOperationBody(operation, body.toString());

    }

    @Override
    protected ObjectModel initOutputModel() {

        //FIXME Override builder to avoid bad imports when using synonyms in model...
        builder = new JavaBuilder(getModel().getName()){
            @Override
            public ObjectModelOperation addOperation(ObjectModelClassifier classifier, String name, String type, ObjectModelModifier... modifiers) {
                ObjectModelOperationImpl result = new ObjectModelOperationImpl();
                result.setName(name);

                if (type != null) {
                    ObjectModelParameterImpl returnParameter =
                            new ObjectModelParameterImpl();
                    returnParameter.setType(type);
                    result.setReturnParameter(returnParameter);
                }

                result.addModifier(modifiers);
                ((ObjectModelClassifierImpl) classifier).addOperation(result);
                return result;
            }

            @Override
            public ObjectModelParameter addParameter(ObjectModelOperation operation, String type, String name) {
                ObjectModelOperationImpl impl = (ObjectModelOperationImpl) operation;
                ObjectModelParameterImpl param = new ObjectModelParameterImpl();
                param.setType(type);
                param.setName(name);
                impl.addParameter(param);
                return param;
            }

            @Override
            public ObjectModelAttribute addAttribute(ObjectModelClassifier classifier, String name, String type, String value, ObjectModelModifier... modifiers) {
                ObjectModelAttributeImpl attribute = new ObjectModelAttributeImpl();
                attribute.setName(name);
                attribute.setType(type);
                attribute.setDefaultValue(value);

                attribute.addModifier(modifiers);
                ObjectModelClassifierImpl classifierImpl = (ObjectModelClassifierImpl) classifier;
                classifierImpl.addAttribute(attribute);
                return attribute;
            }
        };
        setConstantPrefix("");
        return builder.getModel();
    }

    private Map getFormProperties(ObjectModelClass input, ObjectModelClass output) {
        Collection attributes = new LinkedList<>(input.getAttributes());
        attributes.addAll(input.getAllOtherAttributes());
        Map properties = new TreeMap<>();
        for (ObjectModelAttribute attr : attributes) {

            if (!attr.isNavigable()) {
                continue;
            }

            String type = attr.getType();

            if (!type.endsWith("Reference") || !type.contains("referential")) {
                continue;
            }
//            if (true) {
//                properties.put(attr.getName(), type);
//            } else {
            addImport(output, type);
            properties.put(attr.getName(), JavaGeneratorUtil.getSimpleName(type));
//            }

        }

        return properties;
    }

    @Override
    public ObjectModelClass createClass(String className, String packageName) {
        return super.createClass(className, packageName);
    }

    @Override
    public void addImport(ObjectModelClass output, Class type) {
        super.addImport(output, type);
    }

    @Override
    public void addImport(ObjectModelClass output, String type) {
        super.addImport(output, type);
    }

    @Override
    public void setSuperClass(ObjectModelClass output, Class superClass) {
        super.setSuperClass(output, superClass);
    }

    @Override
    public ObjectModelAnnotation addAnnotation(ObjectModelClass output, ObjectModelElement output1, Class annotationType) {
        return super.addAnnotation(output, output1, annotationType);
    }

    @Override
    public void addAnnotationParameter(ObjectModelClass output, ObjectModelAnnotation annotation, String name, String value) {
        super.addAnnotationParameter(output, annotation, name, value);
    }

    @Override
    public ObjectModelOperation addConstructor(ObjectModelClass output, ObjectModelJavaModifier modifiers) {
        return super.addConstructor(output, modifiers);
    }

    @Override
    public void setOperationBody(ObjectModelOperation constructor, String body) {
        super.setOperationBody(constructor, body);
    }

    @Override
    public ObjectModelParameter addParameter(ObjectModelOperation operation, Class type, String name) {
        return super.addParameter(operation, type, name);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy