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
 * ToPIA :: Templates
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2004 - 2014 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */




import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.java.ObjectModelTransformerToJava;
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.ObjectModelClassifier;
import org.nuiton.eugene.models.object.ObjectModelDependency;
import org.nuiton.eugene.models.object.ObjectModelInterface;
import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.topia.persistence.TopiaDao;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaException;
import org.nuiton.topia.persistence.TopiaQueryBuilderAddCriteriaOrRunQueryStep;
import org.nuiton.topia.persistence.TopiaQueryBuilderRunQueryWithUniqueResultStep;
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.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * To generate all DAO related classes for a given entity.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.5.4
 * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.templates.EntityDaoTransformer"
 */
public class EntityDaoTransformer extends ObjectModelTransformerToJava {

    /** Logger. */
    private static final Log log =
            LogFactory.getLog(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 String entityEnumName; protected String entityEnumPackage; protected TopiaTemplateHelper templateHelper; protected TopiaTagValues topiaTagValues; /** * Map of extra operations for DAO. The key of the map is the qualified * name of the entity relative to the DAO. */ protected Map> extraOperations = new HashMap>(); @Override public void transformFromModel(ObjectModel model) { if (templateHelper == null) { templateHelper = new TopiaTemplateHelper(model); } if (topiaTagValues == null) { topiaTagValues = templateHelper.getTopiaTagValues(); } String modelName = model.getName(); entityEnumName = modelName + "EntityEnum"; String packageName = getDefaultPackageName(); entityEnumPackage = packageName + "." + entityEnumName; 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) { String fqn = entity.getQualifiedName(); allEntitiesFqn.add(fqn); Collection daoOperations = new HashSet(); for (ObjectModelOperation op : entity.getOperations()) { if (TopiaStereoTypes.hasDaoStereotype(op)) { daoOperations.add(op); } } if (daoOperations.isEmpty()) { // found some dao operations extraOperations.put(fqn, daoOperations); } } } @Override public void transformFromInterface(ObjectModelInterface interfacez) { if (!TopiaStereoTypes.hasDaoStereotype(interfacez)) { return; } /** * EVO #636 : Manage extra operations for DAO from "dao" dependency * between an interface with stereotype <> (dependency client) and * a class with stereotype <> (dependency supplier). */ ObjectModelDependency dependency = interfacez.getDependency(TopiaTemplateHelper.DEPENDENCIES_DAO); if (dependency == null) { if (log.isWarnEnabled()) { log.warn("Could not find dependency " + TopiaTemplateHelper.DEPENDENCIES_DAO + " but DAO stereotype was placed on the interface " + interfacez.getName()); } return; } ObjectModelClassifier classifier = dependency.getSupplier(); if (!templateHelper.isEntity(classifier)) { // dependency supplier is not an entity... if (log.isWarnEnabled()) { log.warn("Dependency supplier " + classifier.getQualifiedName() + " is not an entity."); } return; } // keep only direct operations Collection operations = interfacez.getOperations(); if (CollectionUtils.isEmpty(operations)) { // no operations on interface, this is not normal if (log.isWarnEnabled()) { log.warn("No operation found on interface with DAO " + "stereotype "+classifier.getQualifiedName()); } return; } if (log.isDebugEnabled()) { log.debug("add "+operations.size()+" extra operation(s) for DAO"); } extraOperations.put(classifier.getQualifiedName(), operations); } @Override public void transformFromClass(ObjectModelClass clazz) { if (!templateHelper.isEntity(clazz)) { // not an entity return; } 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 (isInClassPath(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 (isInClassPath(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); if (isInClassPath(fqn)) { // already in class-path return false; } Collection moreOperations = extraOperations.get(input.getQualifiedName()); if (CollectionUtils.isNotEmpty(moreOperations)) { // no user operations, can not generate it return false; } // can safely generate the dao impl return true; } 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) { Collection moreOperations = extraOperations.get(clazz.getQualifiedName()); if (CollectionUtils.isEmpty(moreOperations)) { // no business Dao found, can safely generate the abstract Dao class 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 ( ! isInClassPath(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) { ObjectModelClass daoAbstractClass = createAbstractClass(templateHelper.getGeneratedDaoName(clazz) + "', clazz.getPackageName()); // super class String superClassName = null; for (ObjectModelClass parent : clazz.getSuperclasses()) { if (templateHelper.isEntity(parent)) { superClassName = templateHelper.getAbstractDaoFqn(parent) + ""; // in java no multi-inheritance break; } } if (superClassName == null) { superClassName = topiaTagValues.getDaoSuperClassTagValue(clazz, getPackage(clazz), model); if (superClassName == null) { superClassName = templateHelper.getParentDaoFqn(this, model); addImport(daoAbstractClass, superClassName); } superClassName += ""; } if (log.isDebugEnabled()) { log.debug("super class = " + superClassName); } setSuperClass(daoAbstractClass, superClassName); String prefix = getConstantPrefix(clazz, ""); setConstantPrefix(prefix); // imports Collection daoOperations = getDaoOperations(clazz); if (templateHelper.isCollectionNeeded(daoOperations)) { addImport(daoAbstractClass, Collection.class); } if (templateHelper.isSetNeeded(daoOperations)) { addImport(daoAbstractClass, Set.class); } addImport(daoAbstractClass, List.class); addImport(daoAbstractClass, TopiaQueryBuilderAddCriteriaOrRunQueryStep.class); ObjectModelOperation op; // getEntityClass op = addOperation(daoAbstractClass, "getEntityClass", "Class", ObjectModelJavaModifier.PUBLIC); addAnnotation(daoAbstractClass, op,Override.class); setOperationBody(op, "" +"\n" +" return (Class) "+clazzName+".class;\n" +" " ); // getTopiaEntityEnum addImport(daoAbstractClass, entityEnumPackage); op = addOperation(daoAbstractClass, "getTopiaEntityEnum", entityEnumName, ObjectModelJavaModifier.PUBLIC); addAnnotation(daoAbstractClass, op,Override.class); setOperationBody(op, "" +"\n" +" return "+entityEnumName+"."+clazzName+";\n" +" " ); generateDAOOperations(daoAbstractClass, daoOperations); generateDelete(clazz, daoAbstractClass); generateNaturalId(daoAbstractClass, clazz); generateNotNull(daoAbstractClass, clazz); for (ObjectModelAttribute attr : clazz.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); } } } } Set usagesForclass = usages.get(clazz); generateFindUsages(clazz, daoAbstractClass, usagesForclass); generateAggregateOperation(daoAbstractClass, clazz); generateCompositeOperation(daoAbstractClass, clazz); } protected void generateDelete(ObjectModelClass clazz, ObjectModelClass result) { StringBuilder body = new StringBuilder(); String modelName = StringUtils.capitalize(model.getName()); String providerFQN = getDefaultPackageName() + '.' + modelName + "DAOHelper.getImplementationClass"; 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 : clazz.getAttributes()) { String attrType = GeneratorUtil.getSimpleName(attr.getType()); String reverseAttrName = attr.getReverseAttributeName(); ObjectModelAttribute reverse = attr.getReverseAttribute(); if (attr.hasAssociationClass() || reverse == null || !reverse.isNavigable()) { // never treate a non reverse and navigable attribute // never treate 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 treate it if (log.isDebugEnabled()) { log.debug("[" + result.getName() + "] Skip attribute [" + attr.getName() + "] with type " + attr.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 = templateHelper.getDbName(attr); String attrClassifierDBName = templateHelper.getDbName(attr.getClassifier()); String attrJoinTableName = templateHelper.getManyToManyTableName(attr); String attrReverseDBName = templateHelper.getReverseDbName(attr); //FIXME_-FC-20100413 Use a TopiaQuery (use HQLin elements) // // Add DAOHelper // String daoHelper = modelName + "DAOHelper"; // String daoHelperFQN = getOutputProperties(). // getProperty(PROP_DEFAULT_PACKAGE) + '.' + daoHelper; // addImport(result, daoHelperFQN); // // // Add import for TopiaQuery // addImport(result, TopiaQuery.class); // // // Entity DAO and reversePropertyName // String entityDAO = attrType + "DAO"; // String reverseAttrNameProperty = // attrType + "." + getConstantName(reverseAttrName); // // // <%=entityDAO%> dao = <%=daoHelper%>.get<%=entityDAO%>(getTopiaContext()); // TopiaQuery query = dao.createQuery("B"). // addFrom(entity.getClass(), "A"). // add("A", entity). // addInElements("A", "B." + <%=reverseAttrNameProperty%>); // // System.out.println("Query : " + query); // List<<%=attrType%>> list = dao.findAllByQuery(query); if (!hibernateSupportGenerated) { hibernateSupportGenerated = true; addImport(result, TopiaHibernateSupport.class); addImport(result, HibernateTopiaJpaSupport.class); addImport(result, Session.class); body.append("" +"\n" +" TopiaHibernateSupport hibernateSupport = ((HibernateTopiaJpaSupport) topiaJpaSupport).getHibernateSupport();\n" +" Session hibernateSession = hibernateSupport.getHibernateSession();\n" +"" ); } String removeName = getJavaBeanMethodName("remove", reverseAttrName); body.append("" +"\n" +" {\n" +" String sql = \"SELECT main.topiaid \" +\n" +" \" FROM "+attrClassifierDBName+" main, "+attrJoinTableName+" secondary \" +\n" +" \" WHERE main.topiaid=secondary."+attrDBName+" \" +\n" +" \" AND secondary."+attrReverseDBName+"='\" + entity.getTopiaId() + \"'\";\n" +" List<"+attrType+"> list = hibernateSession\n" +" .createSQLQuery(sql)\n" +" .addEntity(\"main\", "+entityEnumName+"."+attrType+".getImplementation())\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 addImport(result, attrType); addImport(result, attr.getType() + "TopiaDao"); // AThimel 30/10/13 Not using attrType because we need FQN // Can use TopiaTemplateHelper.getConcreteDaoFqn(...) ? String attrSimpleType = GeneratorUtil.getClassNameFromQualifiedName(attrType); // 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 generateFindUsages(ObjectModelClass clazz, ObjectModelClass result, Set usagesForclass) { builder.addImport(result, LinkedList.class.getName()); builder.addImport(result, Map.class.getName()); builder.addImport(result, HashMap.class.getName()); builder.addImport(result, TopiaEntity.class.getName()); if (clazz instanceof ObjectModelAssociationClass || usagesForclass.isEmpty()) { // not for an association class // just let a null method ObjectModelOperation operation; operation = addOperation(result, "findUsages", " List", ObjectModelJavaModifier.PUBLIC); addParameter(operation, "Class", "type"); addParameter(operation, "E", "entity"); addAnnotation(result, operation, Override.class); setOperationBody(operation, "" +"\n" +" return new LinkedList();\n" +" " ); operation = addOperation(result, "findAllUsages", "Map, List>", ObjectModelJavaModifier.PUBLIC); addParameter(operation, "E", "entity"); addAnnotation(result, operation, Override.class); setOperationBody(operation, "" +"\n" +" return new HashMap, List>();\n" +" " ); return; } List allEntities; Map allEntitiesByFQN; allEntities = templateHelper.getEntityClasses(model, true); allEntitiesByFQN = new TreeMap(); // prepare usages map and fill allEntitiesByFQN map for (ObjectModelClass klass : allEntities) { allEntitiesByFQN.put(klass.getQualifiedName(), klass); } ObjectModelOperation operation; operation = addOperation(result, "findUsages", " List", ObjectModelJavaModifier.PUBLIC); addParameter(operation, "Class", "type"); addParameter(operation, "E", "entity"); addAnnotation(result, operation, Override.class); StringBuilder buffer = new StringBuilder(300); buffer.append("" +"\n" +" List result = new LinkedList();\n" +" List tmp;\n" +"" ); for (ObjectModelClass usageClass : usagesForclass) { String usageType = usageClass.getQualifiedName(); builder.addImport(result, usageType); builder.addImport(result, templateHelper.getConcreteDaoFqn(usageClass)); String usageSimpleType = GeneratorUtil.getClassNameFromQualifiedName(usageType); String usageSimplePropertyMethod = "findAllBy" + usageSimpleType; String usageCollectionPropertyMethod = "findAllContaining" + usageSimpleType; for (ObjectModelAttribute attr : usageClass.getAttributes()) { if (!attr.isNavigable()) { // skip this case continue; } String type; String attrName = attr.getName(); if (attr.hasAssociationClass()) { //FIXME-TC20100224 dont known how to do this ? continue; // type = attr.getAssociationClass().getQualifiedName(); // //FIXME-TC20100224 : this is crazy ??? must find the good name // // Perhaps need to make different cases? // attrName = attrName + "_" + templateHelper.toLowerCaseFirstLetter(attr.getAssociationClass().getName()); } else { type = attr.getType(); } if (!allEntitiesByFQN.containsKey(type)) { // not a entity, can skip for this attribute continue; } ObjectModelClass targetEntity = allEntitiesByFQN.get(type); // if (!type.equals(clazz.getQualifiedName())) { if (!targetEntity.equals(clazz)) { // not a good attribute reference continue; } // found something to seek String methodName; if (GeneratorUtil.isNMultiplicity(attr)) { methodName = getJavaBeanMethodName("for", attrName , "Contains"); } else { methodName = getJavaBeanMethodName("for", attrName, "Equals"); } String daoName = templateHelper.getConcreteDaoName(usageClass); builder.addImport(result, usageClass.getPackageName() + '.' + daoName); buffer.append("" +"\n" +" if (type == "+usageSimpleType+".class) {\n" +" "+daoName+" dao =\n" +" topiaDaoSupplier.getDao("+usageSimpleType+".class, "+daoName+".class);\n" +" tmp = dao."+methodName+"(entity).findAll();\n" +" result.addAll(tmp);\n" +" }\n" +"" ); } } buffer.append("" +"\n" +" return (List) result;\n" +" " ); setOperationBody(operation, buffer.toString()); operation = addOperation(result, "findAllUsages", "Map, List>", ObjectModelJavaModifier.PUBLIC); addParameter(operation, "E", "entity"); addAnnotation(result, operation, Override.class); buffer = new StringBuilder(300); buffer.append("" +"\n" +" Map,List> result;\n" +" result = new HashMap, List>("+usagesForclass.size()+");\n" +"\n" +" List list;\n" +"" ); for (ObjectModelClass usageClass : usagesForclass) { String fqn = usageClass.getName(); buffer.append("" +"\n" +" list = findUsages("+fqn+".class, entity);\n" +" if (!list.isEmpty()) {\n" +" result.put("+fqn+".class, list);\n" +" }\n" +"" ); } buffer.append("" +"\n" +" return result;\n" +" " ); setOperationBody(operation, buffer.toString()); } /** * Generation of DAO operations signatures from class. These operations are * abstract and identified by <> stereotype in the model. The * developper must defined these methods in the DAOImpl associated to this * DAOAbstract. * * @param result clazz where to add operations * @param operations operations to generate * @deprecated will be removed ASAP in topia 3.0 */ @Deprecated protected void generateDAOOperations(ObjectModelClass result, Collection operations) { if (CollectionUtils.isEmpty(operations)) { // no extra operations to generate return; } for (ObjectModelOperation op : operations) { Set exceptions = op.getExceptions(); cloneOperation(op, result, true, ObjectModelJavaModifier.ABSTRACT, ObjectModelJavaModifier.fromVisibility(op.getVisibility()) ); } } 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" +" " ); String methodToDelegateName = op.getName(); op = addOperation(result, getJavaBeanMethodName("findBy", attrName), "E", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" return "+methodToDelegateName+"(v).findAnyOrNull();\n" +" " ); addAnnotation(result, op, Deprecated.class); op = addOperation(result, getJavaBeanMethodName("findAllBy", attrName), "List", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" return "+methodToDelegateName+"(v).findAll();\n" +" " ); addAnnotation(result, op, Deprecated.class); if (!isAssoc && attr.hasAssociationClass()) { String assocClassName = attr.getAssociationClass().getName(); String assocClassFQN = attr.getAssociationClass().getQualifiedName(); String assocAttrName = GeneratorUtil.getAssocAttrName(attr); String assocPropertyConstantName = getConstantName(assocAttrName); op = addOperation(result, getJavaBeanMethodName("findBy", assocClassName), "E", ObjectModelJavaModifier.PUBLIC); addParameter(op, assocClassFQN, "value"); setOperationBody(op, "" +"\n" +" E result = forProperties("+clazzName + "." + assocPropertyConstantName+", value).findAnyOrNull();\n" +" return result;\n" +" " ); op = addOperation(result, getJavaBeanMethodName("findAllBy", assocClassName), "List", ObjectModelJavaModifier.PUBLIC); addParameter(op, assocClassFQN, "value"); setOperationBody(op, "" +"\n" +" List result = forProperties("+clazzName + "." + assocPropertyConstantName+", value).findAll();\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" +" " ); String methodToDelegateName = op.getName(); op = addOperation(result, getJavaBeanMethodName("findContains", attrName), "E", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" return "+methodToDelegateName+"(v).findAnyOrNull();\n" +" " ); addAnnotation(result, op, Deprecated.class); op = addOperation(result, getJavaBeanMethodName("findAllContains", attrName), "List", ObjectModelJavaModifier.PUBLIC); addParameter(op, attrType, "v"); setOperationBody(op, "" +"\n" +" return "+methodToDelegateName+"(v).findAll();\n" +" " ); addAnnotation(result, op, Deprecated.class); } /** * Obtain business operations of the DAO. * * This operations can not be generated, but must be written by developper. * * @param clazz the clazz to test. * @return collections of extra operations, or empty collection if none found. * @deprecated Dao operation will not be generated anymore in a very close future */ @Deprecated public Collection getDaoOperations( ObjectModelClass clazz) { // // Note : this collection will contains extra operations for DAO. // // Overriding existing generated methods is not managed yet // Collection results = // new ArrayList(); // // This code will be deprecated // for (ObjectModelOperation op : clazz.getOperations()) { // if (templateHelper.hasDaoStereotype(op)) { // results.add(op); // } // } Collection extra = extraOperations.get(clazz.getQualifiedName()); if (extra != null && !extra.isEmpty() && log.isWarnEnabled()) { log.warn("Dao contract in model will not be supported in topia 3.0"); } return extra; // if (extra != null) { // for (ObjectModelOperation op : extra) { // results.add(op); // } // } // return results; } private void generateNaturalId(ObjectModelClass result, ObjectModelClass clazz) { Set props = templateHelper.getNaturalIdAttributes(clazz); if (!props.isEmpty()) { if (log.isDebugEnabled()) { log.debug("generateNaturalId for " + props); } ObjectModelOperation findByNaturalId = addOperation(result, "findByNaturalId", "E", ObjectModelJavaModifier.PUBLIC); addAnnotation(result, findByNaturalId, Deprecated.class); ObjectModelOperation existByNaturalId = addOperation(result, "existByNaturalId", "boolean", ObjectModelJavaModifier.PUBLIC); addAnnotation(result, existByNaturalId, Deprecated.class); ObjectModelOperation createByNaturalId = addOperation(result, "createByNaturalId", "E", ObjectModelJavaModifier.PUBLIC); addImport(result, TopiaQueryBuilderRunQueryWithUniqueResultStep.class); ObjectModelOperation forNaturalId = addOperation(result, "forNaturalId", "TopiaQueryBuilderRunQueryWithUniqueResultStep", ObjectModelJavaModifier.PUBLIC); Set < String > properties = Sets.newLinkedHashSet(); String clazzName = clazz.getName(); for (ObjectModelAttribute attr : props) { String propName = attr.getName(); String type = attr.getType(); addParameter(findByNaturalId, type, propName); addParameter(existByNaturalId, type, propName); addParameter(createByNaturalId, type, propName); addParameter(forNaturalId, type, propName); String property = clazzName + '.' + getConstantName(propName) + ", " + propName; properties.add(property); } String arguments = StringUtils.join(properties, ", "); setOperationBody(findByNaturalId, "" +"\n" +" return forProperties("+arguments+").findUnique();\n" +" " ); setOperationBody(existByNaturalId, "" +"\n" +" return forProperties("+arguments+").exists();\n" +" " ); setOperationBody(createByNaturalId, "" +"\n" +" return create("+arguments+");\n" +" " ); setOperationBody(forNaturalId, "" +"\n" +" return forProperties("+arguments+");\n" +" " ); } } protected void generateNotNull(ObjectModelClass result, ObjectModelClass clazz) { Set props = templateHelper.getNotNullAttributes(clazz); if (!props.isEmpty()) { if (log.isDebugEnabled()) { log.debug("generateNotNull for " + props); } ObjectModelOperation createByNotNull = addOperation(result, "createByNotNull", "E", ObjectModelJavaModifier.PUBLIC); String createProperties = ""; // String params = ""; String clazzName = clazz.getName(); for (ObjectModelAttribute attr : props) { String propName = attr.getName(); // add property as param in both methods addParameter(createByNotNull, attr.getType(), propName); createProperties += ", " + clazzName + '.' + getConstantName(propName) + ", " + propName; //params += ", " + propName; } createProperties = createProperties.substring(2); //params = params.substring(2); setOperationBody(createByNotNull, "" +"\n" +" return create("+createProperties+");\n" +" " ); } } protected void generateCompositeOperation(ObjectModelClass outputAbstract, ObjectModelClass input) { ObjectModelOperation operation = addOperation(outputAbstract, "getComposite", List.class.getName() + '<' + TopiaEntity.class.getName() + '>'); addParameter(operation, "E", "entity"); addException(operation, TopiaException.class); addAnnotation(outputAbstract, operation, Override.class); StringBuilder body; addImport(outputAbstract, ArrayList.class); addImport(outputAbstract, List.class); addImport(outputAbstract, TopiaDao.class); body = new StringBuilder("" +"\n" +" List tmp = new ArrayList();\n" +"\n" +" // pour tous les attributs rechecher les composites et les class d'asso\n" +" // on les ajoute dans tmp\n" +"" ); for (ObjectModelAttribute attr : input.getAttributes()) { if (attr.referenceClassifier() && templateHelper.isEntity(attr.getClassifier())) { if (attr.isComposite() && attr.isNavigable()) { String attrName = attr.getName(); String getterName = getJavaBeanMethodName("get", attrName); if (GeneratorUtil.isNMultiplicity(attr)) { body.append("" +" if (entity."+getterName+"() != null) {\n" +" tmp.addAll(entity."+getterName+"());\n" +" }\n" +"" ); } else { body.append("" +" tmp.add(entity."+getterName+"());\n" +"" ); } } else if (attr.hasAssociationClass()) { if (!GeneratorUtil.isNMultiplicity(attr)) { String assocAttrName = GeneratorUtil.getAssocAttrName(attr); String entityAbstractName = templateHelper.getEntityAbstractName(input); // Use the protected access to entity field body.append("" +"\n" +" "+entityAbstractName+" entityAbstract = ("+entityAbstractName+")entity;\n" +" if (entityAbstract."+assocAttrName+" != null) {\n" +" tmp.add(entityAbstract."+assocAttrName+");\n" +" }\n" +"" ); } else { ObjectModelAttribute reverse = attr.getReverseAttribute(); String reverseAttrName = reverse.getName(); // On utilise pas l'attribut car il n'est potentiellement // pas à jour, car pour les asso avec cardinalité // personne ne fait de add. Ce qui est normal, mais // pour pouvoir faire tout de même des delete en cascade // sur les asso, le champs est dans le mapping // hibernate et donc il le faut aussi dans la classe // sinon hibernate râle lorsqu'il charge l'objet // if (<%=ref%> != null) { // tmp.addAll(<%=ref%>); // } String assocClassFQN = attr.getAssociationClass().getQualifiedName(); String assocClassName = GeneratorUtil.getSimpleName(assocClassFQN); String assocConcreteDaoClassFQN = templateHelper.getConcreteDaoFqn(attr.getAssociationClass()); String assocConcreteDaoClassName = GeneratorUtil.getSimpleName(assocConcreteDaoClassFQN); addImport(outputAbstract, assocClassFQN); addImport(outputAbstract, assocConcreteDaoClassFQN); body.append("" +"\n" +" {\n" +" "+assocConcreteDaoClassName+" dao = topiaDaoSupplier\n" +" .getDao("+assocClassName+".class, "+assocConcreteDaoClassName+".class);\n" +" List<"+assocClassName+"> findAllByProperties = dao.forProperties(\""+reverseAttrName+"\", entity).findAll();\n" +" if (findAllByProperties != null) {\n" +" tmp.addAll(findAllByProperties);\n" +" }\n" +" }\n" +"" ); } } } } body.append("" +"\n" +" // on refait un tour sur chaque entity de tmp pour recuperer leur\n" +" // composite\n" +" List result = new ArrayList();\n" +" for (TopiaEntity e : tmp) {\n" +" if (e != null) {\n" +" result.add(e);\n" +" TopiaDao dao = (TopiaDao) topiaDaoSupplier.getDao(e.getClass());\n" +" result.addAll(dao.getComposite(e));\n" +" }\n" +" }\n" +" return result;\n" +" " ); setOperationBody(operation, body.toString()); } protected void generateAggregateOperation(ObjectModelClass outputAbstract, ObjectModelClass input) { ObjectModelOperation operation = addOperation(outputAbstract, "getAggregate", List.class.getName() + '<' + TopiaEntity.class.getName() + '>'); addParameter(operation, "E", "entity"); addException(operation, TopiaException.class); addAnnotation(outputAbstract, operation, Override.class); StringBuilder body = new StringBuilder(); addImport(outputAbstract, ArrayList.class); addImport(outputAbstract, List.class); addImport(outputAbstract, TopiaDao.class); body .append("" +"\n" +" List tmp = new ArrayList();\n" +"\n" +" // pour tous les attributs rechecher les composites et les class d'asso\n" +" // on les ajoute dans tmp\n" +"" ); for (ObjectModelAttribute attr : input.getAttributes()) { if (attr.referenceClassifier() && templateHelper.isEntity(attr.getClassifier()) && attr.isAggregate()) { String attrName = attr.getName(); String getterName = getJavaBeanMethodName("get", attrName); if (GeneratorUtil.isNMultiplicity(attr)) { body.append("" +" tmp.addAll(entity."+getterName+"());\n" +"" ); } else { body.append("" +" tmp.add(entity."+getterName+"());\n" +"" ); } } } body.append("" +"\n" +" // on refait un tour sur chaque entity de tmp pour recuperer leur\n" +" // composite\n" +" List result = new ArrayList();\n" +" for (TopiaEntity e : tmp) {\n" +" result.add(e);\n" +" TopiaDao dao = (TopiaDao) topiaDaoSupplier.getDao(e.getClass());\n" +" result.addAll(dao.getAggregate(e));\n" +" }\n" +" return result;\n" +" " ); setOperationBody(operation, body.toString()); } }