org.nuiton.topia.templates.sql.TopiaMetadataModelBuilder Maven / Gradle / Ivy
package org.nuiton.topia.templates.sql;
/*-
* #%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.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.eugene.EugeneCoreTagValues;
import org.nuiton.eugene.GeneratorUtil;
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.ObjectModelEnumeration;
import org.nuiton.eugene.models.object.ObjectModelPackage;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntity;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModel;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModelVisitor;
import org.nuiton.topia.templates.TopiaExtensionTagValues;
import org.nuiton.topia.templates.TopiaHibernateTagValues;
import org.nuiton.topia.templates.TopiaTemplateHelper;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class TopiaMetadataModelBuilder {
private static final Logger log = LogManager.getLogger(TopiaMetadataModelBuilder.class);
private static TopiaMetadataModel CACHE;
private static TopiaMetadataModel FULL_CACHE;
private final boolean verbose;
private final ObjectModel model;
private final TopiaTemplateHelper templateHelper;
private final TopiaExtensionTagValues topiaExtensionTagValues;
private final TopiaHibernateTagValues topiaHibernateTagValues;
/**
* Metadata for all entities of the model.
*/
private final Map entities;
public static TopiaMetadataModel build(boolean verbose, ObjectModel model, TopiaTemplateHelper templateHelper) {
if (CACHE != null) {
return CACHE;
}
log.info("Build topia metadata model for: " + Objects.requireNonNull(model).getName());
return CACHE = new TopiaMetadataModelBuilder(verbose, model, templateHelper).build0(false);
}
public static TopiaMetadataModel buildFull(boolean verbose, ObjectModel model, TopiaTemplateHelper templateHelper) {
if (FULL_CACHE != null) {
return FULL_CACHE;
}
log.info("Build topia metadata model for: " + Objects.requireNonNull(model).getName());
return FULL_CACHE = new TopiaMetadataModelBuilder(verbose, model, templateHelper).build0(true);
}
public TopiaMetadataModelBuilder(boolean verbose, ObjectModel model, TopiaTemplateHelper templateHelper) {
this.verbose = verbose;
this.model = model;
this.templateHelper = templateHelper;
this.topiaExtensionTagValues = new TopiaExtensionTagValues();
this.topiaHibernateTagValues = new TopiaHibernateTagValues();
this.entities = new LinkedHashMap<>();
}
public TopiaMetadataModel build0(boolean full) {
List entityClasses = templateHelper.getEntityClasses(model, true);
Multimap> oneToManyAssociationInverses = ArrayListMultimap.create();
Multimap> oneToOneAssociationInverses = ArrayListMultimap.create();
// Fist round to build all entities
for (ObjectModelClass entityClass : entityClasses) {
buildMetadataEntity(entityClass, oneToManyAssociationInverses, oneToOneAssociationInverses);
}
// Second round to fill inverses
for (TopiaMetadataEntity metadataEntity : entities.values()) {
if (oneToManyAssociationInverses.containsKey(metadataEntity.getType())) {
for (Pair pair : oneToManyAssociationInverses.get(metadataEntity.getType())) {
String typeName = pair.getRight();
String columnName = pair.getLeft();
metadataEntity.addOneToManyAssociationInverse(metadataEntity, typeName, columnName);
}
}
if (oneToOneAssociationInverses.containsKey(metadataEntity.getType())) {
for (Pair pair : oneToOneAssociationInverses.get(metadataEntity.getType())) {
String typeName = pair.getRight();
String columnName = pair.getLeft();
metadataEntity.addOneToOneAssociationInverse(metadataEntity, typeName, columnName);
}
}
}
TopiaMetadataModel metadataModel = new TopiaMetadataModel(entities);
if (full) {
metadataModel.applyInheritance();
}
if (verbose || log.isDebugEnabled()) {
TopiaMetadataModelVisitor.PrintVisitor visitor = new TopiaMetadataModelVisitor.PrintVisitor(true, "\n");
metadataModel.accept(visitor);
log.info("MetadataModel:\n" + visitor);
}
return metadataModel;
}
public TopiaMetadataEntity getEntity(String type) {
return entities.get(type);
}
public Optional getOptionalEntity(String type) {
return Optional.ofNullable(getEntity(type));
}
public TopiaMetadataEntity newEntity(String parent, String packageName, String type, boolean isAbstract, boolean entryPoint, boolean standalone, String dbSchemaName, String dbTableName) {
String literalName = (dbSchemaName == null ? "" : (dbSchemaName + "_")) + type;
if (entities.containsKey(literalName)) {
throw new IllegalStateException(literalName + " already in cache");
}
TopiaMetadataEntity clazz = new TopiaMetadataEntity(parent, literalName, packageName + "." + type, isAbstract, entryPoint, standalone, dbSchemaName, dbTableName);
entities.put(literalName, clazz);
log.debug("create new entity: " + clazz.getType());
return clazz;
}
private TopiaMetadataEntity buildMetadataEntity(ObjectModelClass entityClass, Multimap> oneToManyAssociationInverses, Multimap> oneToOneAssociationInverses) {
TopiaMetadataEntity metadataEntity;
String entityClassName = entityClass.getName();
String literalName = templateHelper.getEntityEnumLiteralName(entityClass);
Optional optionalClazz = getOptionalEntity(literalName);
if (optionalClazz.isPresent()) {
metadataEntity = optionalClazz.get();
} else {
if (verbose) {
log.info("Start " + literalName);
}
ObjectModelPackage aPackage = model.getPackage(entityClass);
String dbSchemaName = templateHelper.getSchema(entityClass);
boolean entryPoint = topiaExtensionTagValues.isEntryPoint(entityClass);
boolean standalone = topiaExtensionTagValues.isStandalone(model, aPackage, entityClass);
if (entryPoint && standalone) {
throw new IllegalStateException("An entity " + entityClassName + " can't be an entry point and standalone at the same time, make your choice");
}
String dbTableName = templateHelper.getDbName(entityClass);
boolean isAbstract = entityClass.isAbstract();
boolean haveSuper = entityClass.getSuperclasses().size() > 0;
String parent = null;
if (haveSuper) {
ObjectModelClass superClass = entityClass.getSuperclasses().iterator().next();
parent = templateHelper.getEntityEnumLiteralName(superClass);
}
metadataEntity = newEntity(parent, aPackage.getName(), entityClassName, isAbstract, entryPoint, standalone, dbSchemaName, dbTableName);
Collection attributes = entityClass.getAttributes();
for (ObjectModelAttribute attr : attributes) {
ObjectModelAttribute reverse = attr.getReverseAttribute();
if (!attr.isNavigable()
&& !templateHelper.hasUnidirectionalRelationOnAbstractType(reverse, model)
&& !attr.hasAssociationClass()) {
continue;
}
String name = attr.getName();
String attrColumn = templateHelper.getDbName(attr);
if (attr.getClassifier() == null || !templateHelper.isEntity(attr.getClassifier())) {
String attrType = attr.getType();
Integer precision = null;
Boolean useEnumerationName = null;
switch (attrType) {
case "String":
attrType = "java.lang.String";
break;
case "Boolean":
attrType = "java.lang.Boolean";
break;
case "Byte":
attrType = "java.lang.Byte";
break;
case "Character":
attrType = "java.lang.Character";
break;
case "Short":
attrType = "java.lang.Short";
break;
case "Integer":
attrType = "java.lang.Integer";
break;
case "Long":
attrType = "java.lang.Long";
break;
case "Float":
case "java.lang.Float":
attrType = "java.lang.Float";
precision = topiaExtensionTagValues.getDigits(aPackage, entityClass, attr);
Objects.requireNonNull(precision, String.format("No digits found on decimal attribute %s.%s", entityClass.getQualifiedName(), attr.getName()));
break;
case "Double":
case "java.lang.Double":
attrType = "java.lang.Double";
precision = topiaExtensionTagValues.getDigits(aPackage, entityClass, attr);
Objects.requireNonNull(precision, String.format("No digits found on decimal attribute %s.%s", entityClass.getQualifiedName(), attr.getName()));
break;
case "java.util.Date":
String type = Objects.requireNonNull(topiaHibernateTagValues.getHibernateAttributeType(attr, entityClass, aPackage, model));
switch (type) {
case "time":
attrType = Time.class.getName();
break;
case "timestamp":
attrType = Timestamp.class.getName();
break;
case "date":
attrType = Date.class.getName();
break;
}
break;
default:
useEnumerationName = topiaHibernateTagValues.hasUseEnumerationNameTagValue(attr, entityClass, aPackage, model);
if (useEnumerationName) {
// check we are on an enumeration
ObjectModelEnumeration enumeration = model.getEnumeration(attrType);
if (enumeration==null) {
throw new IllegalStateException(String.format("Using tag-value useEnumerationName on %s.%s which is not an enumeration", entityClass.getQualifiedName(), attr.getName()));
}
}
break;
}
if (GeneratorUtil.isNMultiplicity(attr)) {
String tableName = templateHelper.getManyToManyTableName(attr);
metadataEntity.addManyAssociation(name, attrColumn, tableName, attr.getReverseAttributeName(), attrType);
} else {
metadataEntity.addProperty(name, attrType, attrColumn, precision, useEnumerationName);
}
continue;
}
ObjectModelClass attributeClass = model.getClass(attr.getType());
boolean skipModelNavigation = topiaExtensionTagValues.isSkipModelNavigation(model.getPackage(attributeClass), attributeClass, attr);
if (skipModelNavigation) {
metadataEntity.addSkipNavigation(name);
}
Optional optionalAttributeClass = getOptionalEntity(templateHelper.getEntityEnumLiteralName(attributeClass));
TopiaMetadataEntity attributeClazz = optionalAttributeClass.orElseGet(() -> buildMetadataEntity(attributeClass, oneToManyAssociationInverses, oneToOneAssociationInverses));
String reverseColumnName = templateHelper.getReverseDbName(attr);
if (GeneratorUtil.isNMultiplicity(attr)) {
if (GeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) {
// many to many
String tableName = templateHelper.getManyToManyTableName(attr);
metadataEntity.addManyToManyAssociation(attributeClazz, name, attrColumn, tableName, reverseColumnName);
} else {
// one to many
if (reverseColumnName == null) {
reverseColumnName = entityClassName;
}
boolean ordered = EugeneCoreTagValues.isOrdered(attr);
if (ordered) {
String extraColumn = reverseColumnName + "_idx";
if (verbose) {
log.info(String.format("Found ordered %s -> %s", attr, extraColumn));
}
attributeClazz.addExtraColumn(extraColumn);
}
metadataEntity.addOneToManyAssociation(attributeClazz, name, attrColumn);
oneToManyAssociationInverses.put(attributeClazz.getType(), Pair.of(reverseColumnName, metadataEntity.getType()));
}
} else {
if (GeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) {
// many to one
metadataEntity.addManyToOneAssociation(attributeClazz, name, attrColumn);
} else {
// one to one
if (attr.isComposite()) {
// one to one composition (child table has foreign key to parent)
if (reverseColumnName == null) {
reverseColumnName = entityClassName;
}
metadataEntity.addOneToOneAssociation(attributeClazz, name, attrColumn);
oneToOneAssociationInverses.put(attributeClazz.getType(), Pair.of(reverseColumnName, metadataEntity.getType()));
} else {
// one to one association (parent table has foreign key to child)
metadataEntity.addReversedAssociation(attributeClazz, name, attrColumn);
}
}
}
}
if (verbose) {
log.info(String.format("End %s", entityClassName));
}
}
return metadataEntity;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy