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

org.nuiton.topia.templates.EntityDaoTransformer Maven / Gradle / Ivy

package org.nuiton.topia.templates;

/*
 * #%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.Strings;
import fr.ird.observe.toolkit.templates.entity.query.SqlQueryDefinition;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.plexus.component.annotations.Component;
import org.nuiton.eugene.EugeneCoreTagValues;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.Template;
import org.nuiton.eugene.java.ObjectModelTransformerToJava;
import org.nuiton.eugene.java.extension.ObjectModelAnnotation;
import org.nuiton.eugene.models.object.ObjectModel;
import org.nuiton.eugene.models.object.ObjectModelAssociationClass;
import org.nuiton.eugene.models.object.ObjectModelAttribute;
import org.nuiton.eugene.models.object.ObjectModelClass;
import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.eugene.models.object.ObjectModelPackage;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaQueryBuilderAddCriteriaOrRunQueryStep;
import org.nuiton.topia.persistence.internal.AbstractTopiaDao;
import org.nuiton.topia.persistence.internal.support.HibernateTopiaJpaSupport;
import org.nuiton.topia.persistence.support.TopiaHibernateSupport;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * To generate all {@code DAO} related classes for a given entity.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.5.4
 */
@Component(role = Template.class, hint = "org.nuiton.topia.templates.EntityDaoTransformer")
public class EntityDaoTransformer extends ObjectModelTransformerToJava {
    private static final Logger log = LogManager.getLogger(EntityDaoTransformer.class);
    /**
     * map of direct usages (values) for each entity (key).
     * 

* This map is used to generate the findUsages methods for DAOAbstract. */ protected Map> usages; /** * All entities fqn of the model (used to detect if an attribute is not * an entity). */ protected Set allEntitiesFqn; protected TopiaTemplateHelper templateHelper; protected final TopiaCoreTagValues topiaCoreTagValues; protected final TopiaHibernateTagValues topiaHibernateTagValues; private boolean useRelativeName; private EntityClassContext classContext; public EntityDaoTransformer() { this.topiaCoreTagValues = new TopiaCoreTagValues(); this.topiaHibernateTagValues = new TopiaHibernateTagValues(); } @Override public void transformFromModel(ObjectModel model) { if (templateHelper == null) { templateHelper = new TopiaTemplateHelper(model); } EugeneCoreTagValues coreTagValues = new EugeneCoreTagValues(); useRelativeName = coreTagValues.isUseRelativeName(model); usages = templateHelper.searchDirectUsages(model); generateParentDao(); // keep all classifiers on the model which are entities List allEntities = templateHelper.getEntityClasses(model, true); allEntitiesFqn = new HashSet<>(allEntities.size()); for (ObjectModelClass entity : allEntities) { if (!templateHelper.isAbstract(entity)) { String fqn = entity.getQualifiedName(); allEntitiesFqn.add(fqn); } } } @Override public void transformFromClass(ObjectModelClass clazz) { if (!templateHelper.isEntity(clazz)) { // not an entity return; } if (templateHelper.isAbstract(clazz)) { return; } classContext = new EntityClassContext(templateHelper, model, model.getPackage(clazz), clazz); String clazzName = clazz.getName(); String clazzFQN = clazz.getQualifiedName(); if (isGenerateGeneratedDao(clazz)) { generateGeneratedDao(clazz, clazzName, clazzFQN); } if (isGenerateAbstractDao(clazz)) { generateAbstractDao(clazz, clazzName, clazzFQN); } if (isGenerateConcreteDao(clazz)) { generateConcreteDao(clazz, clazzName, clazzFQN); } } protected boolean isGenerateConcreteDao(ObjectModelClass input) { String daoConcreteFqn = templateHelper.getConcreteDaoFqn(input); if (getResourcesHelper().isJavaFileInClassPath(daoConcreteFqn)) { // already in class-path return false; } // can safely generate the dao impl return true; } protected boolean isGenerateGeneratedDao(ObjectModelClass input) { String daoGeneratedFqn = templateHelper.getGeneratedDaoFqn(input); if (getResourcesHelper().isJavaFileInClassPath(daoGeneratedFqn)) { // already in class-path return false; } // can safely generate the dao impl return true; } protected boolean isGenerateAbstractDao(ObjectModelClass input) { String fqn = templateHelper.getAbstractDaoFqn(input); return !getResourcesHelper().isJavaFileInClassPath(fqn); } protected void generateConcreteDao(ObjectModelClass clazz, String clazzName, String clazzFQN) { String concreteDaoName = templateHelper.getConcreteDaoName(clazz); ObjectModelClass daoClass = createClass(concreteDaoName, clazz.getPackageName()); setDocumentation(daoClass, "/**\n" + " * Cette classe etend le DAOImpl pour parametrer la classe avec le bon type\n" + " * Cette classe est marque finale car l'heritage entre les DAO se fait\n" + " * sur les DOAImpl, c-a-d que DAOAbstract peut etendre le DAOImpl\n" + " */"); String superclassQualifiedName = templateHelper.getAbstractDaoFqn(clazz) + "<" + clazzName + ">"; setSuperClass(daoClass, superclassQualifiedName); // TODO AThimel 25/10/13 Remove the next lines in ToPIA 3.1 // Look for legacy Dao class, then warn user if found in classpath String legacyConcreteDaoName = templateHelper.getLegacyDaoName(clazz); warnOnLegacyClassDetected(clazz.getPackageName(), legacyConcreteDaoName, concreteDaoName, null, templateHelper.getAbstractDaoFqn(clazz) + "<" + clazzName + ">"); } protected void generateAbstractDao(ObjectModelClass clazz, String clazzName, String clazzFQN) { String abstractDaoName = templateHelper.getAbstractDaoName(clazz); String daoGenerics = ""; ObjectModelClass abstractDaoClass = createClass(abstractDaoName + daoGenerics, clazz.getPackageName()); String documentation = String.format("/**%n" + " * Implantation du Dao pour l'entité '%s'.%n" + " * L'utilisateur peut remplacer cette classe par la sienne en la mettant%n" + " * simplement dans ses sources. Cette classe générée sera alors simplement%n" + " * ignorée à la génération suivante.%n" + " */", clazzName); setDocumentation(abstractDaoClass, documentation); String superclassQualifiedName = templateHelper.getGeneratedDaoFqn(clazz) + ""; setSuperClass(abstractDaoClass, superclassQualifiedName); // TODO AThimel 25/10/13 Remove the next lines in ToPIA 3.1 // Look for legacy Dao class, then warn user if found in classpath String legacyDaoImplName = templateHelper.getLegacyDaoName(clazz) + "Impl"; warnOnLegacyClassDetected(clazz.getPackageName(), legacyDaoImplName, abstractDaoName, daoGenerics, superclassQualifiedName); } // TODO AThimel 25/10/13 Remove this method in ToPIA 3.1 protected void warnOnLegacyClassDetected(String packageName, String legacyDaoName, String daoName, String daoGenerics, String superclassQualifiedName) { String legacyDaoFqn = String.format("%s.%s", packageName, legacyDaoName); // AThimel 25/10/13 Not using isInClassPath(fqn) because this method logs that file won't be generated if (log.isWarnEnabled() && getFileInClassPath(legacyDaoFqn) != null) { String format = "public class %s%s extends %s {"; String superclassName = superclassQualifiedName.substring(superclassQualifiedName.lastIndexOf('.') + 1); String daoDeclaration = String.format(format, daoName, Strings.nullToEmpty(daoGenerics), superclassName); String warnMessage = "Legacy DAO detected : %s !%n" + " - You should consider renaming '%s' to '%s'%n" + " - Expected class declaration is : %s%n" + "%n"; // AThimel 29/10/13 Add a new line for log clearness log.warn(String.format(warnMessage, legacyDaoFqn, legacyDaoName, daoName, daoDeclaration)); } } protected void generateParentDao() { String parentDaoFqn = templateHelper.getParentDaoFqn(this, model); if (!getResourcesHelper().isJavaFileInClassPath(parentDaoFqn)) { ObjectModelClass parentDao = createAbstractClass( templateHelper.getParentDaoName(model) + "", templateHelper.getDaoPackage(this, model) ); addImport(parentDao, TopiaEntity.class); // super class setSuperClass(parentDao, AbstractTopiaDao.class.getName() + ""); } } protected void generateGeneratedDao(ObjectModelClass clazz, String clazzName, String clazzFQN) { String outputName = templateHelper.getGeneratedDaoName(clazz) + "'; ObjectModelClass daoAbstractClass = createAbstractClass(outputName, clazz.getPackageName()); addImport(daoAbstractClass, clazz.getQualifiedName()); // super class String superClassName = topiaCoreTagValues.getDaoSuperClassTagValue(clazz, getPackage(clazz), model); if (superClassName == null) { superClassName = templateHelper.getParentDaoFqn(this, model); addImport(daoAbstractClass, superClassName); } superClassName += ""; setSuperClass(daoAbstractClass, superClassName); String prefix = getConstantPrefix(clazz); setConstantPrefix(prefix); // imports addImport(daoAbstractClass, List.class); addImport(daoAbstractClass, TopiaQueryBuilderAddCriteriaOrRunQueryStep.class); ObjectModelOperation op; // getEntityClass op = addOperation(daoAbstractClass, "getEntityClass", "Class", ObjectModelJavaModifier.PUBLIC); addAnnotation(daoAbstractClass, op, Override.class); ObjectModelAnnotation annotation = addAnnotation(daoAbstractClass, op, SuppressWarnings.class); addAnnotationParameter(daoAbstractClass, annotation, "value", "unchecked"); setOperationBody(op, "" +"\n" +" return (Class) "+clazzName+".class;\n" +" " ); op = addOperation(daoAbstractClass, "newInstance0", "E", ObjectModelJavaModifier.PUBLIC); addAnnotation(daoAbstractClass, op, Override.class); setOperationBody(op, "" +"\n" +" return getEntityClass().cast(new "+clazzName+"Impl());\n" +" " ); generateDelete(clazz, daoAbstractClass); for (ObjectModelAttribute attr : classContext.getAttributes()) { if (!attr.isNavigable()) { continue; } if (!GeneratorUtil.isNMultiplicity(attr)) { generateNoNMultiplicity(clazzName, daoAbstractClass, attr, false); } else { generateNMultiplicity(clazzName, daoAbstractClass, attr); } } if (clazz instanceof ObjectModelAssociationClass) { ObjectModelAssociationClass assocClass = (ObjectModelAssociationClass) clazz; for (ObjectModelAttribute attr : assocClass.getParticipantsAttributes()) { if (attr != null) { if (!GeneratorUtil.isNMultiplicity(attr)) { generateNoNMultiplicity(clazzName, daoAbstractClass, attr, true); } else { generateNMultiplicity(clazzName, daoAbstractClass, attr); } } } } generateUserSqlQueries(daoAbstractClass, classContext.getUserQueryDefinitions()); } private void generateUserSqlQueries(ObjectModelClass clazz, Set userQueryDefinitions) { for (SqlQueryDefinition userQueryDefinition : userQueryDefinitions) { String queryName = userQueryDefinition.getName(); addImport(clazz, "org.hibernate.query.NativeQuery"); String queryMethodName = "query" + queryName; boolean withParameters = userQueryDefinition.withParameters(); ObjectModelOperation op = addOperation(clazz, queryMethodName, " NativeQuery", withParameters?ObjectModelJavaModifier.PROTECTED:ObjectModelJavaModifier.PUBLIC); setOperationBody(op, "" +"\n" +" return getSqlQuery(\""+queryName+"\");\n" +" " ); if (withParameters) { op = addOperation(clazz, queryMethodName, " NativeQuery", ObjectModelJavaModifier.PUBLIC); StringBuilder content = new StringBuilder(""+"\n" +" return this."+queryMethodName+"()"); int index = 1; for (Map.Entry entry : userQueryDefinition.getParameters().entrySet()) { String name = entry.getKey(); String type = entry.getValue(); addImport(clazz, type); addParameter(op, type, name); content.append("" +"\n" +" .setParameter("+index+++", "+name+")"); } content.append(""+";\n" +" "); setOperationBody(op, content.toString()); } } } protected void generateDelete(ObjectModelClass clazz, ObjectModelClass result) { StringBuilder body = new StringBuilder(); String modelName = io.ultreia.java4all.lang.Strings.capitalize(model.getName()); String providerFQN = getDefaultPackageName() + '.' + modelName + "DAOHelper.getImplementationClass"; ObjectModelPackage aPackage = getPackage(clazz); body.append("" +"\n" +" if (!entity.isPersisted()) {\n" +" throw new IllegalArgumentException(\"entity \" + entity + \" is not persisted, you can't delete it\");\n" +" }\n" +"" ); boolean hibernateSupportGenerated = false; for (ObjectModelAttribute attr : classContext.getAttributes()) { String attrType = useRelativeName ? attr.getType() : GeneratorUtil.getSimpleName(attr.getType()); String reverseAttrName = attr.getReverseAttributeName(); ObjectModelAttribute reverse = attr.getReverseAttribute(); if (attr.hasAssociationClass() || reverse == null || !reverse.isNavigable()) { // never treat a non reverse and navigable attribute // never treat an association class attribute continue; } // at this point we are sure to have a attribute which is // - reverse // - navigable // - not from an association class if (!allEntitiesFqn.contains(attr.getType())) { // this attribute is not from an entity, don't treat it if (log.isDebugEnabled()) { log.debug("[" + result.getName() + "] Skip attribute [" + attr.getName() + "] with type " + attr.getType()); } continue; } if (!allEntitiesFqn.contains(reverse.getType())) { // reverse attribute is not from an entity, don't treat it if (log.isDebugEnabled()) { log.debug("[" + result.getName() + "] Skip attribute [" + reverse.getName() + "] with type " + reverse.getType()); } continue; } // At this point, the attribute type is a entity if (GeneratorUtil.isNMultiplicity(attr) && GeneratorUtil.isNMultiplicity(reverse)) { // On doit absolument supprimer pour les relations many-to-many // le this de la collection de l'autre cote String attrDBName = attr.isNavigable() ? templateHelper.getDbName(attr) : templateHelper.getReverseDbName(attr.getReverseAttribute()); String attrClassifierDBNameSchemaName = templateHelper.getSchema(attr.getClassifier()); String attrClassifierDBName = templateHelper.getDbName(attr.getClassifier()); String attrJoinTableName = attr.isNavigable() ? templateHelper.getManyToManyTableName(attr) : templateHelper.getManyToManyTableName(attr.getReverseAttribute()); if (attrClassifierDBNameSchemaName != null) { attrClassifierDBName = attrClassifierDBNameSchemaName + "." + attrClassifierDBName; attrJoinTableName = attrClassifierDBNameSchemaName + "." + attrJoinTableName; } String attrReverseDBName = templateHelper.getReverseDbName(attr); if (!hibernateSupportGenerated) { hibernateSupportGenerated = true; addImport(result, TopiaHibernateSupport.class); addImport(result, HibernateTopiaJpaSupport.class); body.append("" +"\n" +" TopiaHibernateSupport hibernateSupport = ((HibernateTopiaJpaSupport) topiaJpaSupport).getHibernateSupport();\n" +"" ); } String implementation = attr.getClassifier().getQualifiedName() + "Impl"; String removeName = getJavaBeanMethodName("remove", reverseAttrName); body.append("" +"\n" +" {\n" +" String sql = \"SELECT main.* \" +\n" +" \" FROM "+attrClassifierDBName+" main, "+attrJoinTableName+" secondary \" +\n" +" \" WHERE main.topiaId=secondary."+attrDBName+" \" +\n" +" \" AND secondary."+attrReverseDBName+"='\" + entity.getTopiaId() + \"'\";\n" +" List<"+attrType+"> list = hibernateSupport.getHibernateSession()\n" +" .createNativeQuery(sql)\n" +" .addEntity(\"main\", \""+implementation+"\")\n" +" .list();\n" +"\n" +" for ("+attrType+" item : list) {\n" +" item."+removeName+"(entity);\n" +" }\n" +" }\n" +"" ); } else if (!GeneratorUtil.isNMultiplicity(reverse)) { // On doit mettre a null les attributs qui ont cet objet sur les // autres entites en one-to-* // TODO peut-etre qu'hibernate est capable de faire ca tout seul ? // THIMEL: J'ai remplacé reverse.getName() par reverseAttrName sans certitude String attrSimpleType = GeneratorUtil.getClassNameFromQualifiedName(attrType); if (useRelativeName) { attrSimpleType = attrType; } else { addImport(result, attrType); addImport(result, attr.getType() + "TopiaDao"); // AThimel 30/10/13 Not using attrType because we need FQN // Can use TopiaTemplateHelper.getConcreteDaoFqn(...) ? } // XXX brendan 04/10/13 do not hard code concrete dao name String attrConcreteDaoClassName = attrSimpleType + "TopiaDao"; String getName = getJavaBeanMethodName("get", reverseAttrName); String setName = getJavaBeanMethodName("set", reverseAttrName); body.append("" +"\n" +" {\n" +" "+attrConcreteDaoClassName+" dao = topiaDaoSupplier\n" +" .getDao("+attrSimpleType+".class, "+attrConcreteDaoClassName+".class);\n" +" List<"+attrSimpleType+"> list = dao\n" +" .forProperties("+attrSimpleType+"."+getConstantName(reverseAttrName)+", entity)\n" +" .findAll();\n" +" for ("+attrSimpleType+" item : list) {\n" +"\n" +" // sletellier : Set null only if target is concerned by deletion\n" +" if (entity.equals(item."+getName+"())) {\n" +" item."+setName+"(null);\n" +" }\n" +" " ); if (attr.isAggregate()) { body.append("" +"\n" +" topiaDaoSupplier.getDao("+attrSimpleType+".class).delete(item);\n" +"" ); } body.append("" +"\n" +" }\n" +" }\n" +"" ); } } if (body.length() > 0) { // something specific was done, need to generate the method ObjectModelOperation op; op = addOperation(result, "delete", "void", ObjectModelJavaModifier.PUBLIC); addAnnotation(result, op, Override.class); addParameter(op, "E", "entity"); body.append("" +"\n" +" super.delete(entity);\n" +" " ); setOperationBody(op, body.toString()); } } protected void generateNoNMultiplicity(String clazzName, ObjectModelClass result, ObjectModelAttribute attr, boolean isAssoc) { String attrName = attr.getName(); String attrType = attr.getType(); String propertyName = clazzName + "." + getConstantName(attrName); String attrTypeForGeneric; if (GeneratorUtil.isPrimitiveType(attrType)) { attrTypeForGeneric = templateHelper.getClassForPrimitiveType(attr); } else { attrTypeForGeneric = attrType; } if (!isAssoc && attr.hasAssociationClass()) { String assocClassName = attr.getAssociationClass().getName(); String assocAttrName = GeneratorUtil.getAssocAttrName(attr); // It is about transitivity : use the property to access the // associationClass + '.' + the property to access the expected // attribute // . + '.' + . propertyName = clazzName + '.' + getConstantName(assocAttrName) + " + '.' + " + assocClassName + '.' + getConstantName(attrName); } addImport(result, Collection.class); ObjectModelOperation op; op = addOperation(result, getJavaBeanMethodName("for", attrName, "In"), "TopiaQueryBuilderAddCriteriaOrRunQueryStep", ObjectModelJavaModifier.PUBLIC); addParameter(op, "Collection<" + attrTypeForGeneric + ">", "v"); setOperationBody(op, "" +"\n" +" TopiaQueryBuilderAddCriteriaOrRunQueryStep result = forIn("+propertyName+", (Collection) v);\n" +" return result;\n" +" " ); op = addOperation(result, getJavaBeanMethodName("for", attrName, "Equals"), "TopiaQueryBuilderAddCriteriaOrRunQueryStep", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" TopiaQueryBuilderAddCriteriaOrRunQueryStep result = forEquals("+propertyName+", v);\n" +" return result;\n" +" " ); } protected void generateNMultiplicity(String clazzName, ObjectModelClass result, ObjectModelAttribute attr) { String attrName = attr.getName(); String attrType = attr.getType(); if (attr.hasAssociationClass()) { // do nothing for association class, too complex... return; } ObjectModelOperation op; op = addOperation(result, getJavaBeanMethodName("for", attrName, "Contains"), "TopiaQueryBuilderAddCriteriaOrRunQueryStep", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" return forContains("+clazzName + "." + getConstantName(attrName)+", v);\n" +" " ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy