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

org.nuiton.topia.service.sql.metadata.TopiaMetadataEntity Maven / Gradle / Ivy

There is a newer version: 10.0.1
Show newest version
package org.nuiton.topia.service.sql.metadata;

/*
 * #%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.base.MoreObjects;
import io.ultreia.java4all.lang.Objects2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.topia.persistence.TopiaEntity;

import java.sql.Blob;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created on 03/01/16.
 *
 * @author Tony Chemit - [email protected]
 * @since 3.0.1
 */
public class TopiaMetadataEntity implements Comparable {

    private static final Logger log = LogManager.getLogger(TopiaMetadataEntity.class);

    /**
     * Optional parent type.
     */
    protected final String parent;
    /**
     * TopiaEntityEnum name.
     */
    protected final String type;
    /**
     * Fully qualified name of entity.
     */
    protected final String fullyQualifiedName;
    /**
     * Is abstract?
     */
    protected final boolean isAbstract;
    /**
     * Is entry point?
     */
    protected final boolean entryPoint;
    /**
     * Db schema name.
     */
    protected final String dbSchemaName;
    /**
     * Db table name.
     */
    protected final String dbTableName;
    /**
     * One to many associations (keys are property name, values are type).
     */
    protected final Map oneToManyAssociations = new LinkedHashMap<>();
    /**
     * One to one compositions (keys are property name, values are type).
     */
    protected final Map oneToOneCompositions = new LinkedHashMap<>();
    /**
     * Target types of one to many relations. (To get column, use getDbColumName from here).
     */
    protected final Set oneToOneCompositionInverses = new TreeSet<>();
    /**
     * Target types of one to many relations. (To get column, use getDbColumName from here).
     */
    protected final Set oneToManyAssociationInverses = new TreeSet<>();
    /**
     * Many to many associations (keys are property name, values are type).
     */
    protected final Map reversedAssociations = new LinkedHashMap<>();
    /**
     * Many to many associations (keys are property name, values are type).
     */
    protected final Map manyToManyAssociations = new LinkedHashMap<>();
    /**
     * Many to one associations (keys are property name, values are type).
     */
    protected final Map manyToOneAssociations = new LinkedHashMap<>();
    /**
     * Many associations (keys are property name, values are type).
     */
    protected final Map manyAssociations = new LinkedHashMap<>();
    /**
     * Properties (keys are property name, values are type).
     */
    protected final Map properties = new LinkedHashMap<>();
    /**
     * Decimal properties scales (keys are property name, values are scales).
     */
    protected final Map decimalPropertiesScales = new LinkedHashMap<>();
    /**
     * Enumeration properties using enumeration name.
     */
    protected final Set enumerationPropertiesUsingName = new LinkedHashSet<>();

    /**
     * Blob properties.
     */
    protected final Set blobProperties = new LinkedHashSet<>();
    /**
     * Extra columns.
     */
    protected final Set extraColumnNames = new LinkedHashSet<>();
    /**
     * Le nom des colunnes correspondants aux propriétés de l'entité.
     * Note:  On ne conserve que les correspondances qui diffèrent du nom de la propriété.
     *
     * @see #getDbColumnName(String)
     */
    protected final Map dbColumnsName = new LinkedHashMap<>();
    /**
     * Le nom des tables utilisées pour les associations nm.
     */
    protected final Map dbManyToManyAssociationsTableName = new LinkedHashMap<>();
    /**
     * Le nom des tables utilisées pour les associations simples nm.
     */
    protected final Map dbManyAssociationsTableName = new LinkedHashMap<>();
    private final boolean standalone;
    /**
     * Get all property we do not want to navigate on.
     */
    private final Set skipNavigation = new LinkedHashSet<>();
    protected Set allDbColumnNames;

    public static List toFqn(Collection entities) {
        return entities.stream().map(TopiaMetadataEntity::getFullyQualifiedName).collect(Collectors.toList());
    }

    public static List sortByFqn(Collection entities) {
        return sortByFqn(entities.stream());
    }

    public static List sortByFqn(Stream entities) {
        return streamSortByFqn(entities).collect(Collectors.toList());
    }

    public static Stream streamSortByFqn(Stream entities) {
        return entities.sorted(Comparator.comparing(TopiaMetadataEntity::getFullyQualifiedName));
    }

    public TopiaMetadataEntity(String parent, String type, String fullyQualifiedName, boolean isAbstract, boolean entryPoint, boolean standalone, String dbSchemaName, String dbTableName) {
        this.parent = parent;
        this.type = type;
        this.fullyQualifiedName = fullyQualifiedName;
        this.isAbstract = isAbstract;
        this.entryPoint = entryPoint;
        this.standalone = standalone;
        this.dbSchemaName = dbSchemaName;
        this.dbTableName = dbTableName;
    }

    public void putAll(TopiaMetadataEntity parent) {
        putAll(getOneToManyAssociations(), parent.getOneToManyAssociations());
        putAll(getOneToOneCompositions(), parent.getOneToOneCompositions());
        addAll(getOneToOneCompositionInverses(), parent.getOneToOneCompositionInverses());
        addAll(getOneToManyAssociationInverses(), parent.getOneToManyAssociationInverses());
        putAll(getReversedAssociations(), parent.getReversedAssociations());
        putAll(getManyToManyAssociations(), parent.getManyToManyAssociations());
        putAll(getManyToOneAssociations(), parent.getManyToOneAssociations());
        putAll(getManyAssociations(), parent.getManyAssociations());
        putAll(getProperties(), parent.getProperties());
        addAll(getExtraColumnNames(), parent.getExtraColumnNames());
        putAll(getDbColumnsName(), parent.getDbColumnsName());
        putAll(getDbColumnsName(), parent.getDbManyToManyAssociationsTableName());
        putAll(getDecimalPropertiesScales(), parent.getDecimalPropertiesScales());
        addAll(getSkipNavigation(), parent.getSkipNavigation());
    }

    private void addAll(Set current, Set parent) {
        Set result = new LinkedHashSet<>(parent);
        result.addAll(current);
        current.clear();
        current.addAll(result);
    }

    private  void putAll(Map current, Map parent) {
        Map result = new LinkedHashMap<>(parent);
        result.putAll(current);
        current.clear();
        current.putAll(result);
    }

    public String getType() {
        return type;
    }

    public String getFullyQualifiedName() {
        return fullyQualifiedName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TopiaMetadataEntity that = (TopiaMetadataEntity) o;
        return Objects.equals(type, that.type);
    }

    @Override
    public int hashCode() {
        return Objects.hash(type);
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("type", type)
                .add("dbName", dbSchemaName + "." + dbTableName)
                .toString();
    }

    public String getParent() {
        return parent;
    }

    public String getDbSchemaName() {
        return dbSchemaName;
    }

    public String getSchemaAndTableName() {
        return getDbSchemaName() + "." + getDbTableName();
    }

    public String getDbTableName() {
        return dbTableName;
    }

    public boolean isAbstract() {
        return isAbstract;
    }

    public boolean isEntryPoint() {
        return entryPoint;
    }

    public boolean isStandalone() {
        return standalone;
    }

    public Set getAllDbColumnNames() {
        if (allDbColumnNames == null) {
            allDbColumnNames = new LinkedHashSet<>();
            allDbColumnNames.add(TopiaEntity.PROPERTY_TOPIA_ID);
            allDbColumnNames.add(TopiaEntity.PROPERTY_TOPIA_CREATE_DATE);
            allDbColumnNames.add(TopiaEntity.PROPERTY_TOPIA_VERSION);
            allDbColumnNames.addAll(extraColumnNames);
            allDbColumnNames.addAll(getProperties().keySet().stream()
                                            .map(this::getDbColumnName)
                                            .collect(Collectors.toCollection(LinkedHashSet::new)));
            allDbColumnNames.addAll(getManyToOneAssociations().keySet().stream()
                                            .map(this::getDbColumnName)
                                            .collect(Collectors.toCollection(LinkedHashSet::new)));
            allDbColumnNames.addAll(getReversedAssociations().keySet().stream()
                                            .map(this::getDbColumnName)
                                            .collect(Collectors.toCollection(LinkedHashSet::new)));
            allDbColumnNames.addAll(getOneToManyAssociationInverses().stream()
                                            .map(this::getDbColumnName)
                                            .collect(Collectors.toCollection(LinkedHashSet::new)));
            allDbColumnNames.addAll(getOneToOneCompositionInverses().stream()
                                            .map(this::getDbColumnName)
                                            .collect(Collectors.toCollection(LinkedHashSet::new)));
        }
        return allDbColumnNames;
    }

    public Map getReversedAssociations() {
        return reversedAssociations;
    }

    public Map getManyToManyAssociations() {
        return manyToManyAssociations;
    }

    public Map getOneToManyAssociations() {
        return oneToManyAssociations;
    }

    public Map getOneToOneCompositions() {
        return oneToOneCompositions;
    }

    public Set getOneToOneCompositionInverses() {
        return oneToOneCompositionInverses;
    }

    public Map getManyToOneAssociations() {
        return manyToOneAssociations;
    }

    public Map getManyAssociations() {
        return manyAssociations;
    }

    public Set getOneToManyAssociationInverses() {
        return oneToManyAssociationInverses;
    }

    public Map getProperties() {
        return properties;
    }

    public Map getDecimalPropertiesScales() {
        return decimalPropertiesScales;
    }

    public Set getEnumerationPropertiesUsingName() {
        return enumerationPropertiesUsingName;
    }

    public Set getDbEnumerationPropertiesUsingName() {
        Set result = new TreeSet<>();
        for (String propertyName : getEnumerationPropertiesUsingName()) {
            String dbColumnName = getDbColumnName(propertyName);
            result.add(dbColumnName);
        }
        return result;
    }

    /**
     * @return dictionary of db columns with their types (keys are column names, values are fully qualified types)
     */
    public Map> getDbSimplePropertiesTypes() {
        Map> result = new TreeMap<>();
        for (Map.Entry entry : properties.entrySet()) {
            String dbColumnName = getDbColumnName(entry.getKey());
            String typeName = entry.getValue();
            Class type;
            //FIXME Move this to java-lang project
            if (typeName.equals("boolean")) {
                type = boolean.class;
            } else if (typeName.equals("int")) {
                type = int.class;
            } else {
                type = Objects2.forName(typeName);
            }
            result.put(dbColumnName, type);
        }
        return result;
    }

    /**
     * @return dictionary of decimal db columns with their precision (keys are column names and values are number of digits)
     */
    public Map getDbDecimalPrecisions() {
        Map result = new TreeMap<>();
        for (Map.Entry entry : decimalPropertiesScales.entrySet()) {
            String dbColumnName = getDbColumnName(entry.getKey());
            result.put(dbColumnName, entry.getValue());
        }
        return result;
    }

    public Set getExtraColumnNames() {
        return extraColumnNames;
    }

    public Set getSkipNavigation() {
        return skipNavigation;
    }

    public boolean withBlob() {
        return !blobProperties.isEmpty();
    }

    public Set getBlobProperties() {
        return blobProperties;
    }

    public Map getDbColumnsName() {
        return dbColumnsName;
    }

    public String getDbColumnName(String propertyName) {
        String dbColumnName = dbColumnsName.get(propertyName);
        if (dbColumnName == null) {
            dbColumnName = propertyName;
        }
        return dbColumnName;
    }

    public Optional getOptionalRecursiveProperty() {
        return getManyToOneAssociations().entrySet().stream().filter(f -> f.getValue().equals(getType())).map(Map.Entry::getKey).findFirst();
    }

    public Map getDbManyToManyAssociationsTableName() {
        return dbManyToManyAssociationsTableName;
    }

    public Map getDbManyAssociationsTableName() {
        return dbManyAssociationsTableName;
    }

    public String getBdManyToManyAssociationTableName(String propertyName) {
        return dbManyToManyAssociationsTableName.get(propertyName);
    }

    public String getBdManyAssociationTableName(String propertyName) {
        return dbManyAssociationsTableName.get(propertyName);
    }

    public void addOneToManyAssociation(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        oneToManyAssociations.put(name, associationClazz.getType());
        addDbColumnName(name, dbColumnName);
    }

    public void addExtraColumn(String extraColumn) {
        extraColumnNames.add(extraColumn);
    }

    public void addOneToManyAssociationInverse(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        oneToManyAssociationInverses.add(name);
        addDbColumnName(name, dbColumnName);
    }


    public void addOneToOneAssociationInverse(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        oneToOneCompositionInverses.add(name);
        addDbColumnName(name, dbColumnName);
    }

    public void addOneToOneAssociation(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        oneToOneCompositions.put(name, associationClazz.getType());
//        addDbColumnName(name, dbColumnName);
    }

    public void addReversedAssociation(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        reversedAssociations.put(name, associationClazz.getType());
        addDbColumnName(name, dbColumnName);
    }

    public void addManyToManyAssociation(TopiaMetadataEntity associationClazz, String name, String dbColumnName, String dbManyToManyAssociationTableName, String reverseDbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbManyToManyAssociationTableName + ") →" + associationClazz.getType());
        manyToManyAssociations.put(name, associationClazz.getType());
        addDbColumnName(name, dbColumnName);
        if (reverseDbColumnName != null) {
            addDbColumnName(dbManyToManyAssociationTableName, reverseDbColumnName);
        }
        dbManyToManyAssociationsTableName.put(name, dbManyToManyAssociationTableName);
    }

    public void addManyAssociation(String name, String dbColumnName, String dbManyToManyAssociationTableName, String reverseDbColumnName, String type) {
        log.debug(getType() + "/" + name + "(" + dbManyToManyAssociationTableName + ") →" + type);
        manyAssociations.put(name, type);
        addDbColumnName(name, dbColumnName);
        if (reverseDbColumnName != null) {
            addDbColumnName(dbManyToManyAssociationTableName, reverseDbColumnName);
        }
        dbManyAssociationsTableName.put(name, dbManyToManyAssociationTableName);
    }

    public void addManyToOneAssociation(TopiaMetadataEntity associationClazz, String name, String dbColumnName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + associationClazz.getType());
        manyToOneAssociations.put(name, associationClazz.getType());
        addDbColumnName(name, dbColumnName);
    }

    public void addProperty(String name, String type, String dbColumnName, Integer precision, Boolean useEnumerationName) {
        log.debug(getType() + "/" + name + "(" + dbColumnName + ") →" + type);
        properties.put(name, type);
        if (Blob.class.getName().equals(type)) {
            blobProperties.add(name);
        }
        if (precision != null) {
            decimalPropertiesScales.put(name, precision);
        }
        if (useEnumerationName != null && useEnumerationName) {
            enumerationPropertiesUsingName.add(name);
        }
        addDbColumnName(name, dbColumnName);
    }

    public void accept(TopiaMetadataModelVisitor visitor, TopiaMetadataModel metadataModel) {
        visitor.visitEntityStart(metadataModel, this);
        for (Map.Entry entry : reversedAssociations.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitReversedAssociation(metadataModel, this, propertyName, metadataModel.getEntity(propertyType));
        }
        for (Map.Entry entry : oneToManyAssociations.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitOneToManyAssociation(metadataModel, this, propertyName, metadataModel.getEntity(propertyType));
        }
        for (Map.Entry entry : oneToOneCompositions.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitOneToOneAssociation(metadataModel, this, propertyName, metadataModel.getEntity(propertyType));
        }
        for (String propertyType : oneToManyAssociationInverses) {
            visitor.visitOneToManyAssociationInverse(metadataModel, this, propertyType, metadataModel.getEntity(propertyType));
        }
        //FIXME
//        for (String propertyType : oneToOneCompositionInverses) {
//            visitor.visitOneToManyAssociationInverse(metadataModel, this, propertyType, metadataModel.getEntity(propertyType));
//        }
        for (Map.Entry entry : manyToManyAssociations.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitManyToManyAssociation(metadataModel, this, propertyName, metadataModel.getEntity(propertyType));
        }
        for (Map.Entry entry : manyAssociations.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitManyAssociation(metadataModel, this, propertyName, propertyType);
        }
        for (Map.Entry entry : manyToOneAssociations.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitManyToOneAssociation(metadataModel, this, propertyName, metadataModel.getEntity(propertyType));
        }
        for (Map.Entry entry : properties.entrySet()) {
            String propertyName = entry.getKey();
            String propertyType = entry.getValue();
            visitor.visitProperty(metadataModel, this, propertyName, propertyType);
        }
        visitor.visitEntityEnd(metadataModel, this);
    }

    private void addDbColumnName(String name, String dbColumnName) {
        if (!name.equals(dbColumnName)) {
            dbColumnsName.put(name, dbColumnName);
        }
    }

    public void addSkipNavigation(String name) {
        skipNavigation.add(name);
    }

    public boolean isSkipNavigation(String name) {
        return skipNavigation.contains(name);
    }

    @Override
    public int compareTo(TopiaMetadataEntity o) {
        return getFullyQualifiedName().compareTo(o.getFullyQualifiedName());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy