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

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

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

/*-
 * #%L
 * Toolkit :: Templates
 * %%
 * Copyright (C) 2017 - 2024 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.BusinessDto;
import fr.ird.observe.dto.CoordinateHelper;
import fr.ird.observe.dto.DtoToReference;
import fr.ird.observe.dto.ToolkitIdDtoBean;
import fr.ird.observe.dto.data.ContainerChildDto;
import fr.ird.observe.dto.data.ContainerChildWithDataFileDto;
import fr.ird.observe.dto.data.ContainerChildWithIndexDto;
import fr.ird.observe.dto.data.ContainerChildWithOptionalProportionDto;
import fr.ird.observe.dto.data.ContainerChildWithProportionDto;
import fr.ird.observe.dto.data.ContainerDto;
import fr.ird.observe.dto.data.DataGroupByDto;
import fr.ird.observe.dto.data.DataGroupByDtoDefinition;
import fr.ird.observe.dto.data.DataGroupByType;
import fr.ird.observe.dto.data.EditableDto;
import fr.ird.observe.dto.data.InlineDataDto;
import fr.ird.observe.dto.data.LayoutAware;
import fr.ird.observe.dto.data.NoValidationDto;
import fr.ird.observe.dto.data.OpenableDto;
import fr.ird.observe.dto.data.RootOpenableDto;
import fr.ird.observe.dto.data.UsingLayout;
import fr.ird.observe.dto.data.WithIndex;
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.DataGroupByDtoToDefinitionMapping;
import fr.ird.observe.spi.mapping.DtoToFormDtoMapping;
import fr.ird.observe.spi.mapping.DtoToMainDtoClassMapping;
import fr.ird.observe.spi.navigation.model.tree.TreeProjectModelBuilder;
import fr.ird.observe.toolkit.templates.ConsolidateDtoModel;
import fr.ird.observe.toolkit.templates.TemplateContract;
import fr.ird.observe.toolkit.templates.ToolkitTagValues;
import fr.ird.observe.toolkit.templates.dto.stats.StatisticDefinitionModel;
import fr.ird.observe.toolkit.templates.dto.stats.StatisticsHelper;
import fr.ird.observe.toolkit.templates.validation.ValidationTagValues;
import io.ultreia.java4all.bean.spi.GenerateJavaBeanDefinition;
import io.ultreia.java4all.classmapping.ImmutableClassMapping;
import io.ultreia.java4all.i18n.spi.bean.RegisterI18nLabel;
import io.ultreia.java4all.lang.Strings;
import io.ultreia.java4all.util.Dates;
import org.apache.commons.lang3.tuple.Pair;
import org.nuiton.eugene.EugeneCoreTagValues;
import org.nuiton.eugene.GeneratorUtil;
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.ImportsManager;
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.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.ObjectModelImpl;
import org.nuiton.eugene.models.object.xml.ObjectModelOperationImpl;
import org.nuiton.eugene.models.object.xml.ObjectModelParameterImpl;
import org.nuiton.eugene.models.tagvalue.ObjectModelTagValuesStore;
import org.nuiton.eugene.models.tagvalue.TagValuesStore;
import org.nuiton.topia.templates.TopiaExtensionTagValues;

import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
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.TreeMap;

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





/**
 * @author Tony Chemit - [email protected]
 * @since ?
 */
@SuppressWarnings({"unused", "StringOperationCanBeSimplified", "UnusedAssignment"})
public class DtoTransformer extends BeanTransformer implements TemplateContract {

    private final EugeneCoreTagValues coreTagValues;
    private final EugeneJavaTagValues javaTemplatesTagValues;
    private final BeanTransformerTagValues beanTagValues;
    private final ToolkitTagValues toolkitTagValues;
    private final ValidationTagValues validationTagValues;
    private final Map dtoFormMapping = new TreeMap<>();
    private final Map groupByDtoDefinitionMapping = new LinkedHashMap<>();
    private final Map dtoMainMapping = new TreeMap<>();
    private final TagValuesStore persistenceTagValues = new TagValuesStore();
    private BeanTransformerContext context;
    private Map> statistics;

    private List decomposeTimestamps;
    private Map decomposeTimes;
    private Map decomposeDates;
    private String currentTimestampPropertyName;
    private AutoTrimGenerator autoTrimGenerator;

    public static String getCoordinateField(boolean before, String coordinatePrefix, String type) {
        return Introspector.decapitalize(before ? (type + coordinatePrefix) : (coordinatePrefix + type));
    }

    public static Pair getCoordinateMainAndPrefix(boolean before, String coordinatePrefix) {
        String main;
        if (before) {
            coordinatePrefix = coordinatePrefix.substring(1);
        }
        if (coordinatePrefix.equals("default")) {
            coordinatePrefix = "";
            main = "coordinate";
        } else {
            if (before) {
                main = "coordinate" + coordinatePrefix;
            } else {
                main = coordinatePrefix;
            }
        }
        return Pair.of(main, coordinatePrefix);
    }

    public DtoTransformer() {
        coreTagValues = new EugeneCoreTagValues();
        javaTemplatesTagValues = new EugeneJavaTagValues();
        beanTagValues = new BeanTransformerTagValues();
        toolkitTagValues = new ToolkitTagValues();
        validationTagValues = new ValidationTagValues();
    }

    public void addRegisterI18nLabels(boolean inlineDto, boolean containerChildDto, boolean containerChildWithDataFileDto, boolean containerChildWithProportionDto, boolean containerChildWithOptionalProportionDto, ObjectModelClass input, ObjectModelClassifier output, ImportsManager importManager, Set i18nLabelsTagValue, String mainClassName, String i18nOverrideLabelsTagValue) {
        ObjectModelAnnotation annotation = addAnnotation(output, output, RegisterI18nLabel.class);
        addAnnotationClassParameter(importManager, output, annotation, "target", mainClassName);
        StringBuilder content = new StringBuilder("{");
        Set i18nProperties = new LinkedHashSet<>(i18nLabelsTagValue);
        if (inlineDto) {
            String propertiesTagValue = toolkitTagValues.getInlineDataDtoPropertiesTagValue(input);
            Arrays.stream(propertiesTagValue.split("\\s*,\\s*")).map(property -> property + ".short").forEach(i18nProperties::add);
        }
        ConsolidateDtoModel
                .loadContainerChildProperties(toolkitTagValues,
                                              input,
                                              containerChildDto,
                                              containerChildWithDataFileDto,
                                              containerChildWithProportionDto,
                                              containerChildWithOptionalProportionDto,
                                              null)
                .stream().map(property -> property + ".short").forEach(i18nProperties::add);
        Iterator iterator = i18nProperties.stream().sorted().iterator();
        while (iterator.hasNext()) {
            String value = iterator.next();
            content.append("\"").append(value).append("\"");
            if (iterator.hasNext()) {
                content.append(", ");
            }
        }
        content.append("}");
        addAnnotationParameter(output, annotation, "properties", content.toString());
        if (i18nOverrideLabelsTagValue != null) {
            StringBuilder contentOverride = new StringBuilder("{");
            iterator = Arrays.asList(i18nOverrideLabelsTagValue.split("\\s*,\\s*")).iterator();
            while (iterator.hasNext()) {
                String value = iterator.next();
                contentOverride.append("\"").append(value).append("\"");
                if (iterator.hasNext()) {
                    contentOverride.append(", ");
                }
            }
            contentOverride.append("}");
            addAnnotationParameter(output, annotation, "overrides", contentOverride.toString());
        }
    }

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

        persistenceTagValues.getStore().putAll(TemplateContract.loadPersistenceTagValues(getClassLoader(), model.getName()));

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

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

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

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

        context.report();
        statistics = StatisticsHelper.toModel(model, ProjectPackagesDefinition.of(getClassLoader()));
    }

    @Override
    protected void generateInitializers() {
        // do not generate global model initializer
    }

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

        generateClassMapping(true, DtoToFormDtoMapping.class, "Class", FormDefinition.class.getName() + "", ImmutableMap.class, "build", dtoFormMapping);
        generateClassMapping(true, DataGroupByDtoToDefinitionMapping.class, "Class>", DataGroupByDtoDefinition.class.getName() + "", ImmutableMap.class, "build", groupByDtoDefinitionMapping);
        generateClassMapping(true, DtoToMainDtoClassMapping.class, BusinessDto.class.getName(), BusinessDto.class.getName(), ImmutableClassMapping.class, "getMappingBuilder", dtoMainMapping);
    }

    @Override
    protected boolean canGenerateAbstractClass(ObjectModelClass aClass, String abstractClassName) {
        boolean result = super.canGenerateAbstractClass(aClass, abstractClassName);
        if (result) {
            result = !EugeneCoreTagValues.isSkip(aClass, model.getPackage(aClass));
        }
        return result;
    }

    @Override
    protected void addSerializable(ObjectModelClass input,
                                   ObjectModelClass output,
                                   boolean interfaceFound) {
        // Nope!
    }

    public Set getI18nLabelsTagValues(ObjectModelClass input,
                                              boolean referential,
                                              boolean rootOpenableDto,
                                              boolean openableDto,
                                              boolean editableDto,
                                              boolean inlineDto,
                                              boolean containerDto,
                                              boolean containerChildDto,
                                              boolean containerChildWithIndexDto,
                                              boolean containerChildWithDataFileDto,
                                              boolean containerChildWithProportionDto,
                                              boolean containerChildWithOptionalProportionDto,
                                              boolean usingLayout) {
        Set result = new LinkedHashSet<>();
        String i18nLabelsTagValue = toolkitTagValues.getI18nLabelsTagValue(input);
        if (i18nLabelsTagValue != null) {
            result.addAll(List.of(i18nLabelsTagValue.split("\\s*,\\s*")));
        }
        if (referential) {
            result.add("label");
        }
        if (input.isAbstract() || input.isInterface() || input.getName().endsWith("Stub")) {
            return result;
        }
        result.add("type");
        if (usingLayout) {
            result.add("action.move.all");
            result.add("action.move.all.choose.parent.message");
            result.add("action.move.all.choose.parent.title");
        }
        if (containerDto) {
            return result;
        }
        result.add("label");
        if (inlineDto) {
            return result;
        }
        if (containerChildWithIndexDto) {
            result.add(WithIndex.PROPERTY_INDEX);
            result.add(WithIndex.PROPERTY_INDEX + ".short");
        }
        if (containerChildWithIndexDto || containerChildWithOptionalProportionDto || containerChildWithProportionDto || containerChildDto || containerChildWithDataFileDto) {
            result.add("action.create");
            result.add("action.save");
            result.add("action.save.tip");
            if (!input.getName().endsWith("Composition")) {
                result.add("title");
            }
            return result;
        }
        if (rootOpenableDto) {
            result.add("list.title");
            result.add("action.create");
            result.add("list.navigation.node");
            result.add("root.list.message.none");
            return result;
        }
        if (openableDto) {
            result.add("action.create");
            result.add("action.move");
            result.add("action.move.choose.parent.message");
            result.add("action.move.choose.parent.title");
            result.add("action.move.tip");
            result.add("list.message.none");
            result.add("list.navigation.node");
            result.add("list.title");
            result.add("message.not.open");
            result.add("navigation.unsaved");
            result.add("title");
            return result;
        }
        if (editableDto) {
            result.add("action.add");
            result.add("action.add.tip");
            result.add("navigation.unsaved");
            result.add("title");
            return result;
        }
        return result;
    }

    @Override
    protected ObjectModelClass generateGeneratedClass(ObjectModelPackage aPackage, ObjectModelClass input, String className, String mainClassName) {
        withIndexProperties.clear();
        String decomposeTimestampTagValue = toolkitTagValues.getDecomposeTimestamp(input);
        if (decomposeTimestampTagValue != null) {
            decomposeTimestamps = new ArrayList<>();
            decomposeTimes = new LinkedHashMap<>();
            decomposeDates = new LinkedHashMap<>();
            for (String timestampPropertyName : decomposeTimestampTagValue.split("\\s*,\\s*")) {

                // add timestamp → date
                // add timestamp → time
                String datePropertyName;
                String timePropertyName;
                if (timestampPropertyName.equals("timeStamp")) {
                    datePropertyName = "date";
                    timePropertyName = "time";
                } else {
                    if (timestampPropertyName.endsWith("TimeStamp")) {
                        datePropertyName = timestampPropertyName.replace("TimeStamp", "Date");
                        timePropertyName = timestampPropertyName.replace("TimeStamp", "Time");
                    } else if (timestampPropertyName.contains(":")) {
                        String[] split = timestampPropertyName.split("\\s*:\\s*");
                        timestampPropertyName = split[0];
                        datePropertyName = split[1];
                        timePropertyName = split[2];
                    } else {
                        throw new IllegalStateException("Can't manage decomposeTimestamp TagValue with value: " + timestampPropertyName);
                    }
                }
                decomposeTimestamps.add(timestampPropertyName);
                decomposeDates.put(timestampPropertyName, datePropertyName);
                decomposeTimes.put(timestampPropertyName, timePropertyName);
            }
        } else {
            decomposeTimestamps = null;
            decomposeTimes = decomposeDates = null;
        }
        ObjectModelClass output = super.generateGeneratedClass(aPackage, input, className, mainClassName);
        List statisticDefinitions = statistics.get(input);
        StatisticsHelper.addWithStatistics(this, input.getPackageName(), input, output, null, statisticDefinitions);
        ObjectModelClass firstSuperClass = output.getSuperclasses().iterator().next();
        if (firstSuperClass.getName().equals(ContainerDto.class.getSimpleName())) {
            ObjectModelAttribute mainAttribute = input.getAttributes().iterator().next();
            if (mainAttribute == null) {
                throw new IllegalStateException(String.format("Can't find main attribute on %s", input.getName()));
            }
            setSuperClass(output, String.format("%s<%sDto>", firstSuperClass.getQualifiedName(), mainAttribute.getType()));
            String propertyNameValue = getConstantName(mainAttribute.getName());
            if (propertyNameValue != null) {
                ObjectModelOperation constructor = addConstructor(output, ObjectModelJavaModifier.PUBLIC);
                setOperationBody(constructor, ""+"\n"
+"        super("+propertyNameValue+");\n"
+"    ");
            }
        }
        boolean referential = TemplateContract.isReferentialFromPackageName(aPackage.getName());
        addWithNoCodeInterface(referential, input, output);
        boolean notAbstract = !input.isAbstract();
        boolean inlineDto = input.getInterfaces().stream().anyMatch(i -> i.getQualifiedName().equals(InlineDataDto.class.getName()));
        boolean businessDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(BusinessDto.class.getName()));
        boolean containerChildWithIndexDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildWithIndexDto.class.getName()));
        boolean containerChildWithDataFileDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildWithDataFileDto.class.getName()));
        boolean containerChildWithProportionDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildWithProportionDto.class.getName()));
        boolean containerChildWithOptionalProportionDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildWithOptionalProportionDto.class.getName()));
        boolean containerChildDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildDto.class.getName()));
        boolean containerDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerDto.class.getName()));
        boolean openableDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(OpenableDto.class.getName()));
        boolean rootOpenableDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(RootOpenableDto.class.getName()));
        boolean editableDto = input.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(EditableDto.class.getName()));
        boolean usingLayout = input.getInterfaces().stream().anyMatch(i -> (i.getQualifiedName()).equals(UsingLayout.class.getName()))
                || input.getInterfaces().stream().anyMatch(i -> (i.getQualifiedName()).equals(LayoutAware.class.getName()));

        String dtoName = TemplateContract.getDtoName(context, input);

        Set i18nLabelsTagValues = getI18nLabelsTagValues(input,
                                                                 referential,
                                                                 rootOpenableDto,
                                                                 openableDto,
                                                                 editableDto,
                                                                 inlineDto,
                                                                 containerDto,
                                                                 containerChildDto,
                                                                 containerChildWithIndexDto,
                                                                 containerChildWithDataFileDto,
                                                                 containerChildWithProportionDto,
                                                                 containerChildWithOptionalProportionDto,
                                                                 usingLayout);
        String i18nOverrideLabelsTagValue = toolkitTagValues.getI18nOverrideLabelsTagValue(input);

        addRegisterI18nLabels(inlineDto, containerChildDto, containerChildWithDataFileDto, containerChildWithProportionDto, containerChildWithOptionalProportionDto, input, output, getImportManager(output), i18nLabelsTagValues, mainClassName, i18nOverrideLabelsTagValue);
        if (notAbstract) {
            addStaticFactoryMethod(output, mainClassName);
            TemplateContract.addToLabel(this, output, dtoName);
        }
        if (context.selectedClasses.contains(input)) {

            String referencesTagValue = toolkitTagValues.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 referenceName = ProjectPackagesDefinition.cleanType(dtoName) + "Reference";

            addInterface(output, String.format("%s<%s>", DtoToReference.class.getName(), referenceName));
            addImport(output, ToolkitIdDtoBean.class);
            ObjectModelOperation toShortDto = addOperation(output, "toShortDto", ToolkitIdDtoBean.class.getSimpleName(), ObjectModelJavaModifier.PUBLIC);
            addAnnotation(output, toShortDto, Override.class);
            setOperationBody(toShortDto, ""+"\n"
+"        return ToolkitIdDtoBean.of("+dtoName+".class, this);\n"
+"    "
            );
            addToReferenceMethod(output, dtoName, referenceName);
        }

        if (notAbstract) {
            addFormDefinitionAttribute(input, output, referential);
            addMainDtoMapping(input, referential);
            if (inlineDto) {
                addInlineDataDtoMethod(input, output);
            }
        }
        ObjectModelTagValuesStore tagValuesStore = model.getTagValuesStore();
        String mandatoryCoordinate = validationTagValues.getNotNullCoordinate(tagValuesStore, input);
        String mayNotNullCoordinate = validationTagValues.getMayNotNullCoordinate(tagValuesStore, input);
        if (mandatoryCoordinate != null || mayNotNullCoordinate != null) {
            StringBuilder postInitContent = new StringBuilder(""+"\n"
+"        super.postInit();");
            addImport(output, CoordinateHelper.class);
            if (mandatoryCoordinate != null) {
                String[] split = mandatoryCoordinate.split("\\s*,\\s*");
                for (String coordinatePrefix : split) {
                    boolean before = coordinatePrefix.startsWith(":");
                    Pair pair = getCoordinateMainAndPrefix(before, coordinatePrefix);
                    String main = pair.getLeft();
                    coordinatePrefix = pair.getRight();

                    String latitude = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Latitude"));
                    String longitude = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Longitude"));
                    String quadrant = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Quadrant"));

                    postInitContent.append(""+"\n"
+"        set"+quadrant+"(CoordinateHelper.getQuadrant(get"+longitude+"(), get"+latitude+"()));");
                }
            }
            if (mayNotNullCoordinate != null) {
                boolean warning = mayNotNullCoordinate.startsWith("+");
                if (warning) {
                    mayNotNullCoordinate = mayNotNullCoordinate.substring(1);
                }
                String[] split = mayNotNullCoordinate.split("\\s*,\\s*");
                for (String coordinatePrefix : split) {
                    boolean before = coordinatePrefix.startsWith(":");
                    Pair pair = getCoordinateMainAndPrefix(before, coordinatePrefix);
                    String main = pair.getLeft();
                    coordinatePrefix = pair.getRight();

                    String latitude = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Latitude"));
                    String longitude = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Longitude"));
                    String quadrant = Strings.capitalize(getCoordinateField(before, coordinatePrefix, "Quadrant"));

                    postInitContent.append(""+"\n"
+"        set"+quadrant+"(CoordinateHelper.getQuadrant(get"+longitude+"(), get"+latitude+"()));");
                }
            }

            ObjectModelOperation postInit = addOperation(output, "postInit", void.class, ObjectModelJavaModifier.PUBLIC);
            addAnnotation(output, postInit, Override.class);
            setOperationBody(postInit, postInitContent.append(""+"\n"
+"    ").toString());
        }
        String entryPoint = persistenceTagValues.getClassifierTagValue(input.getQualifiedName().replace("dto", "entities"), TopiaExtensionTagValues.Store.entryPoint.name());
        if ("true".equals(entryPoint)) {
            String groupByList = persistenceTagValues.getClassifierTagValue(input.getQualifiedName().replace("dto", "entities"), TopiaExtensionTagValues.Store.groupBy.name());
            if (groupByList != null) {
                addGroupBy(input, groupByList.split("\\s*,\\s*"));
            }
        }
        String atLeastOneSelected = validationTagValues.getAtLeastOneSelected(input);
        if (atLeastOneSelected != null) {
            if (atLeastOneSelected.startsWith(":")) {
                atLeastOneSelected = atLeastOneSelected.substring(1);
            }
            ObjectModelOperation isAtLeastOneSelectedOperation = addOperation(output, "isAtLeastOneSelected", boolean.class, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
            addAnnotation(output, isAtLeastOneSelectedOperation, Override.class);
            List contentBuilder = new LinkedList<>();
            String[] split = atLeastOneSelected.split("\\s*,\\s*");
            if (split.length == 0) {
                throw new IllegalStateException("Can't have a atLeastOneSelected tagValue without value on: " + input);
            }
            for (String s : split) {
                String getterName = getJavaBeanMethodName("is", s);
                contentBuilder.add(getterName + "()");
            }
            String content = String.join(" || ", contentBuilder);
            setOperationBody(isAtLeastOneSelectedOperation, ""+"\n"
+"        return "+content+";\n"
+"    ");
        }
        if (autoTrimGenerator == null) {
            autoTrimGenerator = new AutoTrimGenerator(validationTagValues, this, model, (c, a) -> {
                ObjectModelClass aClass = model.getClass(a.getType());
                return aClass != null
                        && aClass.getSuperclasses().stream().anyMatch(i -> model.getClass(i.getQualifiedName()) != null)
                        && aClass.getInterfaces().stream().noneMatch(i -> i.getQualifiedName().equals(NoValidationDto.class.getName()));
            });
        }
        autoTrimGenerator.generate(input, output);
        if (!referential && !businessDto && !withIndexProperties.isEmpty()) {
            generateInstallIndexMethod(output, withIndexProperties);
        }
        return output;
    }

    private void generateInstallIndexMethod(ObjectModelClass output, List withIndexProperties) {
        ObjectModelOperation installIndexOperation = addOperation(output, "installIndex", void.class, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
        addAnnotation(output, installIndexOperation, Override.class);
        StringBuilder builder = new StringBuilder();
        for (String withIndexProperty : withIndexProperties) {
            String methodName = getJavaBeanMethodName("get", withIndexProperty);
            builder.append(""+"\n"
+"        WithIndex.installOrder("+methodName+"());\n"
+"    ");
        }
        if (builder.length() > 0) {
            addImport(output, WithIndex.class);
        }
        setOperationBody(installIndexOperation, builder.toString());
    }

    void addGroupBy(ObjectModelClass input, String... groupByList) {
        for (String groupByPattern : groupByList) {
            String groupBy = groupByPattern;
            String groupBySecond = null;
            int index = groupByPattern.indexOf(":");
            if (index > -1) {
                groupBy = groupByPattern.substring(0, index);
            } else {
                index = groupByPattern.indexOf(".");
                if (index > -1) {
                    groupBy = groupByPattern.substring(0, index);
                    groupBySecond = groupByPattern.substring(index + 1);
                }
            }
            ObjectModelAttribute parentAttribute = Objects.requireNonNull(input.getAttribute(groupBy));
            boolean mandatory = validationTagValues.isNotNull(model.getTagValuesStore(), input, parentAttribute);
            if (groupBySecond == null) {
                addGroupBy(input, groupBy, null, parentAttribute.getType(), mandatory, null);
            } else {
                ObjectModelClass classifier = model.getClass(parentAttribute.getType().replace("Reference", ""));
                model.getClass(classifier.getQualifiedName().replace("Reference", ""));
                ObjectModelAttribute secondClassifier = classifier.getAttribute(groupBySecond);
                mandatory &= validationTagValues.isNotNull(model.getTagValuesStore(), classifier, secondClassifier);
                addGroupBy(input, groupBy, groupBySecond, secondClassifier.getType(), mandatory, classifier);
            }
        }
    }

    private void addGroupBy(ObjectModelClass input, String groupByPropertyName, String groupBySecond, String type, boolean mandatory, ObjectModelClass secondClassifier) {

        String capitalize = Strings.capitalize(groupByPropertyName);
        if (groupBySecond != null) {
            capitalize += Strings.capitalize(groupBySecond);
        }
        String inputName = input.getName() + "Dto";
        String formClassName = input.getName() + "GroupBy" + capitalize + "Dto";
        String definitionClassName = formClassName + "Definition";
        String simplifyGroupType = GeneratorUtil.getSimpleName(type);
        String dtoType = input.getQualifiedName() + "Dto";

        // generate Definition
        ObjectModelClass definitionClass = createClass(definitionClassName, input.getPackageName());

        addImport(definitionClass, type);
        addImport(definitionClass, dtoType);
        addImport(definitionClass, Strings.class);
        addImport(definitionClass, dtoType.replace("Dto", "Reference"));
        String businessProject = "fr.ird.observe.spi.module.ObserveBusinessProject";
        String moduleName = ProjectPackagesDefinition.extractFirstPackage(ProjectPackagesDefinition.removeFirstPackage(dtoType.replace(getDefaultPackageName() + ".", "")));
        String moduleMethodName = String.format("() -> %s.get().get%sBusinessModule()", importAndSimplify(definitionClass, businessProject), Strings.capitalize(moduleName));
        setSuperClass(definitionClass, String.format("%1$s<%2$s, %3$s>", DataGroupByDtoDefinition.class.getName(), inputName, formClassName));
        addImport(definitionClass, DataGroupByType.class);
        String definitionId = Introspector.decapitalize(TreeProjectModelBuilder.toCamelCase(input.getQualifiedName().substring(getDefaultPackageName().length()) + ".groupBy." + groupByPropertyName));
        if (groupBySecond != null) {
            definitionId += Strings.capitalize(groupBySecond);
        }
        addConstant(definitionClass, "NAME", "String", "\"" + definitionId + "\"", ObjectModelJavaModifier.PUBLIC);
        DataGroupByType groupByType = model.getClassifier(type.replace("Reference", "")) == null ? DataGroupByType.TEMPORAL : DataGroupByType.QUALITATIVE;
        ObjectModelOperation definitionConstructor = addConstructor(definitionClass, ObjectModelJavaModifier.PACKAGE);
        String constantName = inputName + "." + getConstantName(groupByPropertyName);
        if (groupBySecond != null) {
            constantName += " + \".\" + " + secondClassifier.getQualifiedName() + "Dto." + getConstantName(groupBySecond);
        }
        setOperationBody(definitionConstructor, ""+"\n"
+"        super(NAME, DataGroupByType."+groupByType+", "+constantName+", "+mandatory+", "+simplifyGroupType+".class, "+inputName+".class, "+formClassName+".class, "+formClassName+"::new, "+moduleMethodName+");\n"
+"    ");
        ObjectModelOperation toGroupByValueOperation = addOperation(definitionClass, "toGroupByObjectValue", type, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
        addParameter(toGroupByValueOperation, inputName, "data");
        addAnnotation(definitionClass, toGroupByValueOperation, Override.class);
        String getterName = getJavaBeanMethodName("get", groupByPropertyName);
        if (groupBySecond != null) {
            getterName += "()." + getJavaBeanMethodName("get", groupBySecond);
        }
        setOperationBody(toGroupByValueOperation, ""+"\n"
+"        return data."+getterName+"();\n"
+"    ");
        ObjectModelOperation setGroupByValueOperation = addOperation(definitionClass, "setGroupByValue", void.class, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
        addParameter(setGroupByValueOperation, inputName, "data");
        addParameter(setGroupByValueOperation, Object.class, "value");
        addAnnotation(definitionClass, setGroupByValueOperation, Override.class);
        if (groupBySecond == null) {
            String setterName = getJavaBeanMethodName("set", groupByPropertyName);
            setOperationBody(setGroupByValueOperation, ""+"\n"
+"        data."+setterName+"(("+type+") value);\n"
+"    ");
        } else {
            setOperationBody(setGroupByValueOperation, ""+"\n"
+"    ");
        }

        // generate Dto
        ObjectModelClass dtoClass = createClass(formClassName, input.getPackageName());
        addAnnotation(dtoClass, dtoClass, GenerateJavaBeanDefinition.class);
        ObjectModelAnnotation annotation = addAnnotation(dtoClass, dtoClass, RegisterI18nLabel.class);
        addAnnotationClassParameter(getImportManager(dtoClass), dtoClass, annotation, "target", formClassName);
        StringBuilder content = new StringBuilder("{");
        Set i18nProperties = new LinkedHashSet<>(List.of("filterText", "count"));

        Iterator iterator = i18nProperties.stream().sorted().iterator();
        while (iterator.hasNext()) {
            String value = iterator.next();
            content.append("\"").append(value).append("\"");
            if (iterator.hasNext()) {
                content.append(", ");
            }
        }
        content.append("}");
        addAnnotationParameter(dtoClass, annotation, "properties", content.toString());

        groupByDtoDefinitionMapping.put(dtoClass.getQualifiedName(), dtoClass.getQualifiedName() + ".DEFINITION");

        addImport(dtoClass, DataGroupByDtoDefinition.class);
        addImport(dtoClass, type);
        addImport(dtoClass, dtoType);
        addImport(dtoClass, dtoType.replace("Dto", "Reference"));

        setSuperClass(dtoClass, String.format("%1$s<%2$s>", DataGroupByDto.class.getName(), inputName));
        addConstant(dtoClass, "DEFINITION", definitionClassName, "new " + definitionClassName + "()", ObjectModelJavaModifier.PUBLIC);

        ObjectModelOperation definitionOperation = addOperation(dtoClass, "definition", definitionClassName, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.FINAL);
        addAnnotation(dtoClass, definitionOperation, Override.class);
        setOperationBody(definitionOperation, ""+"\n"
+"        return DEFINITION;\n"
+"        ");
    }

    @Override
    protected void generateI18nBlockAndConstants(ObjectModelPackage aPackage, ObjectModelClass input, ObjectModelClassifier output) {
        super.generateI18nBlockAndConstants(aPackage, input, output);
        if (decomposeDates != null) {
            decomposeDates.values().forEach(value -> addConstant(output, getConstantName(value), String.class, "\"" + value + "\"", ObjectModelJavaModifier.PUBLIC));
        }
        if (decomposeTimes != null) {
            decomposeTimes.values().forEach(value -> addConstant(output, getConstantName(value), String.class, "\"" + value + "\"", ObjectModelJavaModifier.PUBLIC));
        }
    }

    private final List withIndexProperties = new LinkedList<>();

    @Override
    protected void createProperty(ObjectModelClass output,
                                  ObjectModelAttribute attr,
                                  boolean usePCS,
                                  boolean generateBooleanGetMethods,
                                  boolean generateNotEmptyCollections) {
        String attrName = getAttributeName(attr);
        String attrType = attr.getType();
        if (decomposeTimestamps != null && decomposeTimestamps.contains(attrName)) {
            // need to add date and time getters
            String datePropertyName = Objects.requireNonNull(decomposeDates.get(attrName));
            String timePropertyName = Objects.requireNonNull(decomposeTimes.get(attrName));

            addImport(output, Dates.class);
            currentTimestampPropertyName = attrName;
            createDecomposeTimestampAttribute(output, attr, usePCS, generateBooleanGetMethods, generateNotEmptyCollections, datePropertyName, attrType);
            createDecomposeTimestampAttribute(output, attr, usePCS, generateBooleanGetMethods, generateNotEmptyCollections, timePropertyName, attrType);

        }
        super.createProperty(output, attr, usePCS, generateBooleanGetMethods, generateNotEmptyCollections);
        boolean multiple = JavaGeneratorUtil.isNMultiplicity(attr);
        if (multiple) {
            // check out if we are using a WithIndex type
            if (attrType.endsWith("Dto")) {
                attrType = attrType.substring(0, attrType.length() - "Dto".length());
            }
            ObjectModelClass classifier = model.getClass(attrType);
            if (classifier != null) {
                boolean containerChildWithIndexDto = classifier.getSuperclasses().stream().anyMatch(i -> (i.getQualifiedName() + "Dto").equals(ContainerChildWithIndexDto.class.getName()));
                if (containerChildWithIndexDto) {
                    withIndexProperties.add(attrName);
                }
            }
        }
        currentTimestampPropertyName = null;
    }

    private void createDecomposeTimestampAttribute(ObjectModelClass output,
                                                   ObjectModelAttribute attr,
                                                   boolean usePCS,
                                                   boolean generateBooleanGetMethods,
                                                   boolean generateNotEmptyCollections,
                                                   String attrName, String attrType) {
        ObjectModelAttributeImpl dateProperty = new ObjectModelAttributeImpl();
        dateProperty.setType(attrType);
        dateProperty.setName(attrName);
        dateProperty.setObjectModelImpl((ObjectModelImpl) model);
        dateProperty.postInit();
        super.createProperty(output, dateProperty, usePCS, generateBooleanGetMethods, generateNotEmptyCollections);
    }

    @Override
    protected void createGetMethod(ObjectModelClass output, String attrName, String attrType, String methodPrefix) {

        if (currentTimestampPropertyName == null || attrName.equals(currentTimestampPropertyName)) {
            // default getter
            super.createGetMethod(output, attrName, attrType, methodPrefix);
            return;
        }
        attrType = importAndSimplify(output, attrType);
        if (decomposeDates != null && attrName.equals(decomposeDates.get(currentTimestampPropertyName))) {
            // date getter
            ObjectModelOperation getter = addOperation(output, getJavaBeanMethodName(methodPrefix, attrName), attrType, ObjectModelJavaModifier.PUBLIC);
            setOperationBody(getter, ""+"\n"
+"        return "+currentTimestampPropertyName+" == null ? null : Dates.getDay("+currentTimestampPropertyName+");\n"
+"    ");
            return;
        }

        if (decomposeTimes != null && attrName.equals(decomposeTimes.get(currentTimestampPropertyName))) {
            // time getter
            ObjectModelOperation getter = addOperation(output, getJavaBeanMethodName(methodPrefix, attrName), attrType, ObjectModelJavaModifier.PUBLIC);
            setOperationBody(getter, ""+"\n"
+"        return "+currentTimestampPropertyName+" == null ? null : Dates.getTime("+currentTimestampPropertyName+", false, false);\n"
+"    ");
        }
    }

    @Override
    protected void createSetMethod(ObjectModelClass output, String attrName, String attrType, String simpleType, String constantName, boolean usePCS) {
        if (currentTimestampPropertyName == null) {
            // default setter
            super.createSetMethod(output, attrName, attrType, simpleType, constantName, usePCS);
            return;
        }
        attrType = importAndSimplify(output, attrType);

        if (attrName.equals(currentTimestampPropertyName)) {
            // timestamp setter
            String timestampGetterMethodName = getJavaBeanMethodName("get", attrName);
            String datePropertyName = Objects.requireNonNull(decomposeDates.get(attrName));
            String dateGetterMethodNameName = getJavaBeanMethodName("get", datePropertyName);
            String timePropertyName = Objects.requireNonNull(decomposeTimes.get(attrName));
            String timeGetterMethodName = getJavaBeanMethodName("get", timePropertyName);

            String dateConstantName = getConstantName(datePropertyName);
            String timeConstantName = getConstantName(timePropertyName);
            ObjectModelOperation setter = createSetter(output, attrName, attrType);

            setOperationBody(setter, ""+"\n"
+"        Date oldValue = "+timestampGetterMethodName+"();\n"
+"        Date oldDate = "+dateGetterMethodNameName+"();\n"
+"        Date oldTime = "+timeGetterMethodName+"();\n"
+"        this."+attrName+"= "+attrName+";\n"
+"        firePropertyChange("+constantName+", oldValue, "+timestampGetterMethodName+"());\n"
+"        firePropertyChange("+dateConstantName+", oldDate, "+dateGetterMethodNameName+"());\n"
+"        firePropertyChange("+timeConstantName+", oldTime, "+timeGetterMethodName+"());\n"
+"    ");
            return;
        }
        String timestampPropertyName = "set" + Strings.capitalize(currentTimestampPropertyName);

        if (decomposeDates != null && attrName.equals(decomposeDates.get(currentTimestampPropertyName))) {
            // date setter
            ObjectModelOperation setter = createSetter(output, attrName, attrType);

            setOperationBody(setter, ""+"\n"
+"        if ("+currentTimestampPropertyName+" != null) {\n"
+"            Date dateAndTime = "+attrName+" == null ? "+currentTimestampPropertyName+" : Dates.getDateAndTime("+attrName+", "+currentTimestampPropertyName+", true, false);\n"
+"            "+timestampPropertyName+"(dateAndTime);\n"
+"        }\n"
+"    ");
            return;
        }
        if (decomposeTimes != null && attrName.equals(decomposeTimes.get(currentTimestampPropertyName))) {
            // time setter
            ObjectModelOperation setter = createSetter(output, attrName, attrType);
            setOperationBody(setter, ""+"\n"
+"        if ("+currentTimestampPropertyName+" != null) {\n"
+"            Date dateAndTime = "+attrName+" == null ? "+currentTimestampPropertyName+" : Dates.getDateAndTime("+currentTimestampPropertyName+", "+attrName+", false, false);\n"
+"            "+timestampPropertyName+"(dateAndTime);\n"
+"        }\n"
+"    ");
        }
    }

    @Override
    protected void createGetChildMethod(ObjectModelClass output, String attrName, String attrType, String simpleType) {
        //super.createGetChildMethod(output, attrName, attrType, simpleType);
    }

    @Override
    protected void createAddChildMethod(ObjectModelClass output,
                                        String attrName,
                                        String attrType,
                                        String constantName,
                                        boolean usePCS) {
    }

    @Override
    protected void createAddAllChildrenMethod(ObjectModelClass output, String attrName, String attrType, String constantName, boolean usePCS) {
        //super.createAddAllChildrenMethod(output, attrName, attrType, constantName, usePCS);
    }

    @Override
    protected void createRemoveChildMethod(ObjectModelClass output, String attrName, String attrType, String constantName, boolean usePCS) {
        //super.createRemoveChildMethod(output, attrName, attrType, constantName, usePCS);
    }

    @Override
    protected void createRemoveAllChildrenMethod(ObjectModelClass output, String attrName, String attrType, String constantName, boolean usePCS) {
        //super.createRemoveAllChildrenMethod(output, attrName, attrType, constantName, usePCS);
    }

    @Override
    protected void createContainsChildMethod(ObjectModelClass output, String attrName, String attrType, String constantName, boolean usePCS) {
        //super.createContainsChildMethod(output, attrName, attrType, constantName, usePCS);
    }


    @Override
    protected void createContainsAllChildrenMethod(ObjectModelClass output, String attrName, String attrType, String constantName) {
        //super.createContainsAllChildrenMethod(output, attrName, attrType, constantName);
    }

    private ObjectModelOperation createSetter(ObjectModelClass output, String attrName, String attrType) {
        ObjectModelOperation setter = addOperation(output, getJavaBeanMethodName("set", attrName), "void", ObjectModelJavaModifier.PUBLIC);
        addParameter(setter, attrType, attrName);
        return setter;
    }

    private void addInlineDataDtoMethod(ObjectModelClass input, ObjectModelClass output) {
        //FIXME Generate I18n
        List expressions = new LinkedList<>();
        String propertiesTagValue = toolkitTagValues.getInlineDataDtoPropertiesTagValue(input);
        for (String attributeName : propertiesTagValue.split("\\s*,\\s*")) {
            ObjectModelAttribute attribute = input.getAttribute(attributeName);
            if (attribute == null) {
                addInlineDataDtoExpression(expressions, attributeName, "String");
            } else {
                addInlineDataDtoExpression(expressions, attributeName, GeneratorUtil.getSimpleName(attribute.getType()));
            }
        }
        if (expressions.isEmpty()) {
            return;
        }
        ObjectModelOperation operation = addOperation(output, "isDataEmpty", boolean.class, ObjectModelJavaModifier.PUBLIC);
        addAnnotation(output, operation, Override.class);
        String bodyContent = String.join(" && ", expressions);
        setOperationBody(operation, ""+"\n"
+"        return "+bodyContent+";\n"
+"    ");
    }

    private void addInlineDataDtoExpression(List expressions, String attributeName, String type) {
        String methodPrefix = JavaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX;
        String getter = getJavaBeanMethodName(methodPrefix, attributeName) + "()";
        String expression = getter + " == null";
        if (type.equals(String.class.getSimpleName())) {
            expression = "(" + expression + " || " + getter + ".trim().isBlank())";
        }
        expressions.add(expression);
    }

    private void addStaticFactoryMethod(ObjectModelClass output, String className) {
        ObjectModelOperation operation = addOperation(output, "newDto", className, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC);
        addParameter(operation, importAndSimplify(output, Date.class.getName()), "createDate");
        setOperationBody(operation, ""+"\n"
+"        return newDto("+className+".class, createDate);\n"
+"    ");
    }

    private void addMainDtoMapping(ObjectModelClass input, boolean referential) {
        String mainDtoType = toolkitTagValues.getMainDtoTagValue(input);
        if (toolkitTagValues.notSkip(mainDtoType) == null) {
            return;
        }
        String dtoType = input.getPackageName() + "." + context.classesNameTranslation.get(input);
        dtoMainMapping.put(dtoType, mainDtoType + ".class");
    }

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

        String formTagValue = toolkitTagValues.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;

        Map properties;
        if (formTagValue.equals(dtoType)) {

            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)");
                if (referential) {
                    String referenceType = dtoTypeSimpleName.replace("Dto", "Reference");
                    bodyBuilder.append(""+"\n"
+"                .addProperty(FormDefinition.REFERENTIAL_LIST_HEADER, "+referenceType+".DEFINITION)");
                }

                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 {
            formType = formTagValue;
            addForm = true;
        }

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

    private void addToReferenceMethod(ObjectModelClass output, String dtoName, String referenceName) {

        boolean useRelativeName = context.useRelativeName;

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

        setOperationBody(operation, ""+"\n"
+"        return "+referenceName+".of(referentialLocale, ("+dtoName+") this);\n"
+"    ");
    }

    @Override
    public 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 LinkedHashMap<>();
        for (ObjectModelAttribute attr : attributes) {
            if (!attr.isNavigable()) {
                continue;
            }
            String type = attr.getType();
            if (!type.endsWith("Reference") || !type.contains("referential")) {
                continue;
            }
            addImport(output, type);
            properties.put(attr.getName(), JavaGeneratorUtil.getSimpleName(type));
        }

        return properties;
    }

    @Override
    protected void createSizeMethod(ObjectModelClass output, String attrName) {
        ObjectModelOperation operation = addOperation(
                output,
                getJavaBeanMethodName("get", attrName + "Size"),
                int.class,
                ObjectModelJavaModifier.PUBLIC
        );
        setOperationBody(operation, ""+"\n"
+"        return "+attrName+" == null ? 0 : "+attrName+".size();\n"
+"    "
        );
    }

    @Override
    protected void createIsEmptyMethod(ObjectModelClass output, String attrName) {
        super.createIsEmptyMethod(output, attrName);
        ObjectModelOperation isNotEmptyOperationImpl = addOperation(
                output,
                getJavaBeanMethodName("isNot", attrName + "Empty"),
                boolean.class,
                ObjectModelJavaModifier.PUBLIC
        );
        String isEmptyMethodName = getJavaBeanMethodName("is", attrName + "Empty");
        setOperationBody(isNotEmptyOperationImpl, ""+"\n"
+"        return !"+isEmptyMethodName+"();\n"
+"    "
        );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy