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

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

The newest version!
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.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.plexus.component.annotations.Component;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.Template;
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.ObjectModelJavaModifier;
import org.nuiton.eugene.models.object.ObjectModelOperation;
import org.nuiton.eugene.models.object.ObjectModelPackage;
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 {@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 {

    /** 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 final TopiaCoreTagValues topiaCoreTagValues;
    protected final TopiaHibernateTagValues topiaHibernateTagValues;

    public EntityDaoTransformer() {
        this.topiaCoreTagValues = new TopiaCoreTagValues();
        this.topiaHibernateTagValues = new TopiaHibernateTagValues();
    }

    @Override
    public void transformFromModel(ObjectModel model) {

        if (templateHelper == null) {
            templateHelper = new TopiaTemplateHelper(model);
        }

        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);
        }
    }

    @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 (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) {
        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 = topiaCoreTagValues.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

        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"
+"    "
       );

       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";
        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 : 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 attrClassifierDBNameSchemaName = topiaHibernateTagValues.getDbSchemaNameTagValue(attr.getClassifier(), getPackage(attr.getClassifier()), model);
                if (attrClassifierDBNameSchemaName != null) {
                    attrClassifierDBName = attrClassifierDBNameSchemaName + "." + attrClassifierDBName;
                }
                String attrJoinTableName = templateHelper.getManyToManyTableName(attr);
                if (attrClassifierDBNameSchemaName != null) {
                    attrJoinTableName = attrClassifierDBNameSchemaName + "." + attrJoinTableName;
                }
                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);
                    body.append(""
+"\n"
+"        TopiaHibernateSupport hibernateSupport = ((HibernateTopiaJpaSupport) topiaJpaSupport).getHibernateSupport();\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 listIds = hibernateSupport.getHibernateSession()\n"
+"                    .createNativeQuery(sql, String.class)\n"
+"                    .list();\n"
+"\n"
+"            TopiaDao<"+attr.getType()+"> dao = topiaDaoSupplier.getDao("+attr.getType()+".class);\n"
+"            List<"+attr.getType()+"> list = dao.forTopiaIdIn(listIds).findAll();\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());
    }

    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);

    }

    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 propertiesAndArguments = Sets.newLinkedHashSet();
            Set argumentsOnly = 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;
                propertiesAndArguments.add(property);
                argumentsOnly.add(propName);

            }

            String arguments = StringUtils.join(propertiesAndArguments, ", ");
            String naturalIdArguments = StringUtils.join(argumentsOnly, ", ");

            setOperationBody(findByNaturalId, ""
+"\n"
+"        return forNaturalId("+naturalIdArguments+").findUnique();\n"
+"    "
            );

            setOperationBody(existByNaturalId, ""
+"\n"
+"        return forNaturalId("+naturalIdArguments+").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 clazzName = clazz.getName();
            List createPropertiesList = Lists.newArrayList();
            for (ObjectModelAttribute attr : props) {
                String propName = attr.getName();
                // add property as param in both methods
                addParameter(createByNotNull, attr.getType(), propName);

                // First parameter is the property identifier : .PROPERTY_XXX
                createPropertiesList.add(clazzName + '.' + getConstantName(propName));

                // Second parameter is the property value : 
                createPropertiesList.add(propName);
            }
            String createProperties = Joiner.on(", ").join(createPropertiesList);

            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());
    }
}