View Javadoc
1   /*
2    * #%L
3    * ToPIA :: Persistence
4    * $Id$
5    * $HeadURL$
6    * %%
7    * Copyright (C) 2004 - 2014 CodeLutin
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as 
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Lesser Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * #L%
23   */
24  package org.nuiton.topia.generator;
25  
26  /*{generator option: parentheses = false}*/
27  /*{generator option: writeString = +}*/
28  
29  import org.apache.commons.collections4.CollectionUtils;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.codehaus.plexus.component.annotations.Component;
34  import org.nuiton.eugene.GeneratorUtil;
35  import org.nuiton.eugene.Template;
36  import org.nuiton.eugene.java.ObjectModelTransformerToJava;
37  import org.nuiton.eugene.models.object.ObjectModel;
38  import org.nuiton.eugene.models.object.ObjectModelAssociationClass;
39  import org.nuiton.eugene.models.object.ObjectModelAttribute;
40  import org.nuiton.eugene.models.object.ObjectModelClass;
41  import org.nuiton.eugene.models.object.ObjectModelClassifier;
42  import org.nuiton.eugene.models.object.ObjectModelDependency;
43  import org.nuiton.eugene.models.object.ObjectModelInterface;
44  import org.nuiton.eugene.models.object.ObjectModelJavaModifier;
45  import org.nuiton.eugene.models.object.ObjectModelOperation;
46  import org.nuiton.topia.TopiaException;
47  import org.nuiton.topia.framework.TopiaContextImplementor;
48  import org.nuiton.topia.persistence.TopiaDAO;
49  import org.nuiton.topia.persistence.TopiaDAOLegacy;
50  import org.nuiton.topia.persistence.TopiaEntity;
51  import org.nuiton.util.StringUtil;
52  
53  import java.security.Permission;
54  import java.util.ArrayList;
55  import java.util.Collection;
56  import java.util.HashMap;
57  import java.util.HashSet;
58  import java.util.List;
59  import java.util.Map;
60  import java.util.Set;
61  import java.util.TreeMap;
62  import java.util.regex.Matcher;
63  import java.util.regex.Pattern;
64  
65  /**
66   * To generate all <code>DAO</code> related classes for a given entity.
67   * 
68   * @author tchemit &lt;chemit@codelutin.com&gt;
69   * @since 2.5.4
70   */
71  @Component(role = Template.class, hint = "org.nuiton.topia.generator.EntityDAOTransformer")
72  public class EntityDAOTransformer extends ObjectModelTransformerToJava {
73  
74      /** Logger. */
75      private static final Log log =
76              LogFactory.getLog(EntityDAOTransformer.class);
77  
78      /**
79       * map of direct usages (values) for each entity (key).
80       * 
81       * This map is used to generate the findUsages methods for DAOAbstract.
82       */
83      protected Map<ObjectModelClass, Set<ObjectModelClass>> usages;
84  
85      /**
86       * All entities fqn of the model (used to detect if an attribute is not
87       * an entity).
88       */
89      Set<String> allEntitiesFqn;
90  
91      /**
92       * The class of abstract dao to use.
93       * @since 2.5
94       */
95      protected Class<?> daoImplementation;
96  
97      protected String entityEnumName;
98  
99      protected String entityEnumPackage;
100     /**
101      * Map of extra operations for DAO. The key of the map is the qualified
102      * name of the entity relative to the DAO.
103      */
104     Map<String, Collection<ObjectModelOperation>> extraOperations =
105             new HashMap<String, Collection<ObjectModelOperation>>();
106 
107     @Override
108     public void transformFromModel(ObjectModel model) {
109 
110         boolean generateStandaloneEnum =
111                 TopiaGeneratorUtil.shouldGenerateStandaloneEnumForDAOHelper(model);
112         String modelName = model.getName();
113 
114         entityEnumName = modelName + "EntityEnum";
115 
116         String packageName = getDefaultPackageName();
117 
118         if (generateStandaloneEnum) {
119             entityEnumPackage = packageName + "." + entityEnumName;
120         } else {
121             String daoHelperClazzName = modelName + "DAOHelper";
122             entityEnumPackage = packageName + "." + daoHelperClazzName + "." + entityEnumName;
123         }
124 
125         usages = TopiaGeneratorUtil.searchDirectUsages(model);
126         boolean extendLegacyDAO =
127                 Boolean.valueOf(model.getTagValue(TopiaTagValues.TAG_USE_LEGACY_DAO));
128         if (extendLegacyDAO) {
129             log.warn("Using a deprecated tag value " +
130                      TopiaTagValues.TAG_USE_LEGACY_DAO +
131                      ", prefer use the tag value " +
132                      TopiaTagValues.TAG_DAO_IMPLEMENTATION);
133             daoImplementation = TopiaDAOLegacy.class;
134         } else {
135             daoImplementation =
136                     TopiaGeneratorUtil.getDAOImplementation(model);
137         }
138 
139         // keep all classifiers on the model which are entities
140         List<ObjectModelClass> allEntities =
141                 TopiaGeneratorUtil.getEntityClasses(model, true);
142         allEntitiesFqn = new HashSet<String>(allEntities.size());
143         for (ObjectModelClass entity : allEntities) {
144             String fqn = entity.getQualifiedName();
145             allEntitiesFqn.add(fqn);
146             Collection<ObjectModelOperation> daoOperations =
147                     new HashSet<ObjectModelOperation>();
148             for (ObjectModelOperation op : entity.getOperations()) {
149                 if (TopiaGeneratorUtil.hasDaoStereotype(op)) {
150                     daoOperations.add(op);
151                 }
152             }
153 
154             if (daoOperations.isEmpty()) {
155 
156                 // found some dao operations
157                 extraOperations.put(fqn, daoOperations);
158             }
159         }
160     }
161     
162     @Override
163     public void transformFromInterface(ObjectModelInterface interfacez) {
164         if (!TopiaGeneratorUtil.hasDaoStereotype(interfacez)) {
165             return;
166         }
167         
168         /**
169          * EVO #636 : Manage extra operations for DAO from "dao" dependency
170          * between an interface with stereotype <<dao>> (dependency client) and
171          * a class with stereotype <<entity>> (dependency supplier).
172          */
173 
174         ObjectModelDependency dependency =
175                 interfacez.getDependency(TopiaGeneratorUtil.DEPENDENCIES_DAO);
176 
177         if (dependency == null) {
178             if (log.isWarnEnabled()) {
179                 log.warn("Could not find dependency " +
180                          TopiaGeneratorUtil.DEPENDENCIES_DAO +
181                          " but DAO stereotype was placed on the interface " +
182                          interfacez.getName());
183             }
184             return;
185         }
186 
187         ObjectModelClassifier classifier = dependency.getSupplier();
188 
189         if (!TopiaGeneratorUtil.isEntity(classifier)) {
190 
191             // dependency supplier is not an entity...
192             if (log.isWarnEnabled()) {
193                 log.warn("Dependency supplier " +
194                          classifier.getQualifiedName() +
195                          " is not an entity.");
196             }
197             return;
198         }
199     
200         // keep only direct operations
201         Collection<ObjectModelOperation> operations =
202                 interfacez.getOperations();
203 
204         if (CollectionUtils.isEmpty(operations)) {
205 
206             // no operations on interface, this is not normal
207             if (log.isWarnEnabled()) {
208                 log.warn("No operation found on interface with DAO " +
209                          "stereotype "+classifier.getQualifiedName());
210             }
211             return;
212         }
213         if (log.isDebugEnabled()) {
214             log.debug("add "+operations.size()+" extra operation(s) for DAO");
215         }
216 
217         extraOperations.put(classifier.getQualifiedName(), operations);
218     }
219 
220     @Override
221     public void transformFromClass(ObjectModelClass clazz) {
222         if (!TopiaGeneratorUtil.isEntity(clazz)) {
223             // not an entity
224             return;
225         }
226         String clazzName = clazz.getName();
227         String clazzFQN = clazz.getQualifiedName();
228 
229         if (isGenerateDAO(clazz)) {
230 
231             // generate DAO
232             generateDAOClass(clazz, clazzName, clazzFQN);
233 
234         }
235         if (isGenerateImpl(clazz)) {
236 
237             // generate DAOImpl
238             generateDAOImpl(clazz, clazzName, clazzFQN);
239         }
240 
241         if (isGenerateDAOAbstract(clazz)) {
242 
243             // generate DAOAbstract
244             generateDAOAbstract(clazz, clazzName, clazzFQN);
245         }
246     }
247 
248     protected boolean isGenerateDAO(ObjectModelClass input) {
249 
250         String fqn = input.getQualifiedName() + "DAO";
251 
252         if (isInClassPath(fqn)) {
253 
254             // already in class-path
255             return false;
256         }
257 
258         // can safely generate the dao impl
259         return true;
260     }
261 
262     protected boolean isGenerateDAOAbstract(ObjectModelClass input) {
263 
264         String fqn = input.getQualifiedName() + "DAOAbstract";
265 
266         if (isInClassPath(fqn)) {
267 
268             // already in class-path
269             return false;
270         }
271 
272         // can safely generate the dao impl
273         return true;
274     }
275 
276     protected boolean isGenerateImpl(ObjectModelClass input) {
277 
278         String fqn = input.getQualifiedName() + "DAOImpl";
279 
280         if (isInClassPath(fqn)) {
281 
282             // already in class-path
283             return false;
284         }
285 
286         Collection<ObjectModelOperation> moreOperations =
287                 extraOperations.get(input.getQualifiedName());
288 
289         if (CollectionUtils.isNotEmpty(moreOperations)) {
290 
291             // no user operations, can generate it
292             return false;
293         }
294 
295         // can safely generate the dao impl
296         return true;
297     }
298 
299     protected void generateDAOClass(ObjectModelClass clazz, String clazzName, String clazzFQN) {
300         ObjectModelClass daoClass = createClass(clazzName + "DAO", clazz.getPackageName());
301         setDocumentation(daoClass, "/**\n" +
302                                    " * Cette classe etend le DAOImpl pour parametrer la classe avec le bon type\n" +
303                                    " * Cette classe est marque finale car l'heritage entre les DAO se fait\n" +
304                                    " * sur les DOAImpl, c-a-d que DAOAbstract peut etendre le DAOImpl\n" +
305                                    " */");
306         setSuperClass(daoClass, clazzFQN + "DAOImpl<" + clazzName + ">");
307     }
308 
309     protected void generateDAOImpl(ObjectModelClass clazz,
310                                    String clazzName,
311                                    String clazzFQN) {
312 
313         Collection<ObjectModelOperation> moreOperations =
314                 extraOperations.get(clazz.getQualifiedName());
315 
316         if (CollectionUtils.isEmpty(moreOperations)) {
317 
318             // no business dao found, can safely generate the daoImpl class
319             
320             ObjectModelClass daoImplClass = createClass(clazzName + "DAOImpl<E extends " + clazzName + ">", clazz.getPackageName());
321             setDocumentation(daoImplClass, "/**\n" +
322                                            " Implantation du DAO pour l'entité " + clazzName + ".\n" +
323                                            " * L'utilisateur peut remplacer cette classe par la sienne en la mettant \n" +
324                                            " * simplement dans ces sources. Cette classe générée sera alors simplement\n" +
325                                            " * écrasée\n" +
326                                            " */");
327             setSuperClass(daoImplClass, clazzFQN + "DAOAbstract<E>");
328         }
329     }
330 
331     protected void generateDAOAbstract(ObjectModelClass clazz,
332                                         String clazzName,
333                                         String clazzFQN) {
334         ObjectModelClass daoAbstractClass = createAbstractClass(
335                 clazzName + "DAOAbstract<E extends " + clazzName + '>',
336                 clazz.getPackageName());
337 
338         // super class
339 
340         String extendClass = "";
341         for (ObjectModelClass parent : clazz.getSuperclasses()) {
342             extendClass = parent.getQualifiedName();
343             if (TopiaGeneratorUtil.isEntity(parent)) {
344                 extendClass += "DAOImpl<E>";
345                 // in java no multi-inheritance
346                 break;
347             }
348         }
349         if (extendClass.length() == 0) {
350             extendClass = daoImplementation.getName() + "<E>";
351         }
352         if (log.isDebugEnabled()) {
353             log.debug("super class = " + extendClass);
354         }
355         setSuperClass(daoAbstractClass, extendClass);
356 
357         String prefix = getConstantPrefix(clazz);
358         setConstantPrefix(prefix);
359 
360         // imports
361 
362         Collection<ObjectModelOperation> DAOoperations =
363                 getDAOOperations(clazz);
364 
365         if (TopiaGeneratorUtil.isCollectionNeeded(DAOoperations)) {
366             addImport(daoAbstractClass, Collection.class);
367         }
368         if (TopiaGeneratorUtil.isSetNeeded(DAOoperations)) {
369             addImport(daoAbstractClass, Set.class);
370         }
371         addImport(daoAbstractClass, List.class);
372         addImport(daoAbstractClass, TopiaException.class);
373 
374         boolean enableSecurity = TopiaGeneratorUtil.isClassWithSecurity(clazz);
375 
376         if (enableSecurity) {
377             addImport(daoAbstractClass, TopiaContextImplementor.class);
378             addImport(daoAbstractClass, ArrayList.class);
379             addImport(daoAbstractClass, Permission.class);
380             addImport(daoAbstractClass, "org.nuiton.topia.taas.entities.TaasAuthorizationImpl");
381             addImport(daoAbstractClass, "org.nuiton.topia.taas.jaas.TaasPermission");
382             addImport(daoAbstractClass, "org.nuiton.topia.taas.TaasUtil");
383             addImport(daoAbstractClass, TopiaDAO.class);
384 
385             //FIXME : how to do static imports ?
386 //import static org.nuiton.topia.taas.TaasUtil.CREATE;
387 //import static org.nuiton.topia.taas.TaasUtil.DELETE;
388 //import static org.nuiton.topia.taas.TaasUtil.LOAD;
389 //import static org.nuiton.topia.taas.TaasUtil.UPDATE;
390         }
391 
392         ObjectModelOperation op;
393 
394         // getEntityClass
395 
396         op = addOperation(daoAbstractClass,
397                 "getEntityClass",
398                 "Class<E>",
399                 ObjectModelJavaModifier.PUBLIC);
400         addAnnotation(daoAbstractClass, op,Override.class.getSimpleName());
401         setOperationBody(op, ""
402 /*{
403         return (Class<E>)<%=clazzName%>.class;
404     }*/
405        );
406 
407         // getTopiaEntityEnum
408         addImport(daoAbstractClass, entityEnumPackage);
409         op = addOperation(daoAbstractClass,
410                 "getTopiaEntityEnum",
411                 entityEnumName,
412                 ObjectModelJavaModifier.PUBLIC);
413         addAnnotation(daoAbstractClass, op,Override.class.getSimpleName());
414         setOperationBody(op, ""
415 /*{
416         return <%=entityEnumName%>.<%=clazzName%>;
417     }*/
418        );
419 
420        generateDAOOperations(daoAbstractClass, DAOoperations);
421 
422        generateDelete(clazz, daoAbstractClass);
423 
424        generateNaturalId(daoAbstractClass, clazz);
425 
426        generateNotNull(daoAbstractClass, clazz);
427 
428        for (ObjectModelAttribute attr : clazz.getAttributes()) {
429             if (!attr.isNavigable()) {
430                 continue;
431             }
432 
433             if (!GeneratorUtil.isNMultiplicity(attr)) {
434                 generateNoNMultiplicity(clazzName, daoAbstractClass, attr, false);
435             } else {
436                 generateNMultiplicity(clazzName, daoAbstractClass, attr);
437             }
438         }
439 
440         if (clazz instanceof ObjectModelAssociationClass) {
441             ObjectModelAssociationClass assocClass =
442                     (ObjectModelAssociationClass) clazz;
443             for (ObjectModelAttribute attr : assocClass.getParticipantsAttributes()) {
444                 if (attr != null) {
445                     if (!GeneratorUtil.isNMultiplicity(attr)) {
446                         generateNoNMultiplicity(clazzName, daoAbstractClass, attr, true);
447                     } else {
448                         generateNMultiplicity(clazzName, daoAbstractClass, attr);
449                     }
450                 }
451             }
452         }
453 
454         if (enableSecurity) {
455 
456             // getRequestPermission
457 
458             op = addOperation(daoAbstractClass,
459                     "getRequestPermission",
460                     "List<Permission>",
461                     ObjectModelJavaModifier.PUBLIC);
462 //            setDocumentation(op, "Retourne les permissions a verifier pour " +
463 //                    "l'acces a l'entite pour le service Taas");
464             addException(op, TopiaException.class);
465             addParameter(op, String.class, "topiaId");
466             addParameter(op, int.class, "actions");
467             StringBuilder buffer = new StringBuilder();
468             buffer.append(""
469 /*{
470         List<Permission> resultPermissions = new ArrayList<Permission>();
471         if ((actions & TaasUtil.CREATE) == TaasUtil.CREATE) {
472 }*/
473             );
474             buffer.append(generateSecurity(daoAbstractClass, clazz,
475                                            TopiaGeneratorUtil.getSecurityCreateTagValue(clazz)));
476             buffer.append(""
477 /*{
478         }
479         if ((actions & TaasUtil.LOAD) == TaasUtil.LOAD) {
480 }*/
481             );
482             buffer.append(generateSecurity(daoAbstractClass, clazz,
483                                            TopiaGeneratorUtil.getSecurityLoadTagValue(clazz)));
484             buffer.append(""
485 /*{
486         }
487         if ((actions & TaasUtil.UPDATE) == TaasUtil.UPDATE) {
488 }*/
489             );
490             buffer.append(generateSecurity(daoAbstractClass, clazz,
491                                            TopiaGeneratorUtil.getSecurityUpdateTagValue(clazz)));
492             buffer.append(""
493 /*{
494         }
495         if ((actions & TaasUtil.DELETE) == TaasUtil.DELETE) {
496 }*/
497             );
498             buffer.append(generateSecurity(daoAbstractClass, clazz,
499                                            TopiaGeneratorUtil.getSecurityDeleteTagValue(clazz)));
500             buffer.append(""
501 /*{
502         }
503         return resultPermissions;
504     }*/
505             );
506 
507             setOperationBody(op, buffer.toString());
508 
509             // THIMEL : Le code suivant doit pouvoir être déplacé dans DAODelegator ?
510 
511             // getRequestPermission
512 
513 
514             op = addOperation(daoAbstractClass,
515                     "getRequestPermission",
516                     "List<Permission>",
517                     ObjectModelJavaModifier.PROTECTED);
518             addParameter(op, String.class, "topiaId");
519             addParameter(op, int.class, "actions");
520             addParameter(op, String.class, "query");
521             addParameter(op, Class.class, "daoClass");
522             addException(op, TopiaException.class);
523 //            setDocumentation(op, "Retourne les permissions a verifier pour " +
524 //                    "l'acces a l'entite pour le service Taas");
525             setOperationBody(op, ""
526 /*{    TopiaContextImplementor context = getContext();
527     List<String> result = context.findAll(query, "id", topiaId);
528 
529     List<Permission> resultPermissions = new ArrayList<Permission>();
530     for (String topiaIdPermission : result) {
531         TopiaDAO dao = context.getDAO(daoClass);
532         List<Permission> permissions = dao.getRequestPermission(topiaIdPermission, actions);
533         if(permissions != null) {
534             resultPermissions.addAll(permissions);
535         } else {
536             TaasPermission permission = new TaasPermission(topiaIdPermission, actions);
537             resultPermissions.add(permission);
538         }
539     }
540     return resultPermissions;
541     }*/
542             );
543         }
544 
545         Set<ObjectModelClass> usagesForclass = usages.get(clazz);
546         generateFindUsages(clazz, daoAbstractClass, usagesForclass);
547     }
548 
549     protected void generateDelete(ObjectModelClass clazz,
550                                 ObjectModelClass result) {
551 
552         StringBuilder body = new StringBuilder();
553         String modelName = StringUtils.capitalize(model.getName());
554         String providerFQN = getDefaultPackageName() + '.' + modelName +
555                 "DAOHelper.getImplementationClass";
556 
557         for (ObjectModelAttribute attr : clazz.getAttributes()) {
558 
559             String attrType = GeneratorUtil.getSimpleName(attr.getType());
560 
561             String reverseAttrName = attr.getReverseAttributeName();
562             ObjectModelAttribute reverse = attr.getReverseAttribute();
563             if (attr.hasAssociationClass() ||
564                 reverse == null || !reverse.isNavigable()) {
565 
566                 // never treate a non reverse and navigable attribute
567                 // never treate an association class attribute
568                 continue;
569             }
570 
571             // at this point we are sure to have a attribute which is
572             // - reverse
573             // - navigable
574             // - not from an association class
575             if (!allEntitiesFqn.contains(attr.getType())) {
576 
577                 // this attribute is not from an entity, don't treate it
578                 if (log.isDebugEnabled()) {
579                     log.debug("[" + result.getName() + "] Skip attribute [" +
580                               attr.getName() + "] with type " + attr.getType());
581                 }
582                 continue;
583             }
584 
585             // At this point, the attribute type is a entity
586             if (GeneratorUtil.isNMultiplicity(attr) &&
587                 GeneratorUtil.isNMultiplicity(reverse)) {
588                 // On doit absolument supprimer pour les relations many-to-many
589                 // le this de la collection de l'autre cote
590 
591                 String attrDBName = TopiaGeneratorUtil.getDbName(attr);
592                 String attrClassifierDBName = TopiaGeneratorUtil.getDbName(attr.getClassifier());
593                 String attrJoinTableName = TopiaGeneratorUtil.getManyToManyTableName(attr);
594                 String schemaName = TopiaGeneratorUtil.getDbSchemaNameTagValue(attr.getClassifier(), model);
595                 if (schemaName != null) {
596                     attrJoinTableName = schemaName + "." + attrJoinTableName;
597                     attrClassifierDBName = schemaName + "." + attrClassifierDBName;
598                 }
599                 String attrReverseDBName = TopiaGeneratorUtil.getReverseDbName(attr);
600 
601                 //FIXME_-FC-20100413 Use a TopiaQuery (use HQLin elements)
602 //                // Add DAOHelper
603 //                String daoHelper = modelName + "DAOHelper";
604 //                String daoHelperFQN = getOutputProperties().
605 //                        getProperty(PROP_DEFAULT_PACKAGE) + '.' + daoHelper;
606 //                addImport(result, daoHelperFQN);
607 //
608 //                // Add import for TopiaQuery
609 //                addImport(result, TopiaQuery.class);
610 //
611 //                // Entity DAO and reversePropertyName
612 //                String entityDAO = attrType + "DAO";
613 //                String reverseAttrNameProperty =
614 //                        attrType + "." + getConstantName(reverseAttrName);
615 //
616 //
617 //             <%=entityDAO%> dao = <%=daoHelper%>.get<%=entityDAO%>(getContext());
618 //            TopiaQuery query = dao.createQuery("B").
619 //                    addFrom(entity.getClass(), "A").
620 //                    add("A", entity).
621 //                    addInElements("A", "B." + <%=reverseAttrNameProperty%>);
622 //
623 //            System.out.println("Query : " + query);
624 //            List<<%=attrType%>> list = dao.findAllByQuery(query);
625 
626                 String removeName = getJavaBeanMethodName("remove", reverseAttrName);
627                 body.append(""
628 /*{
629         {
630             List<<%=attrType%>> list = getContext().getHibernate().createNativeQuery(
631                     " SELECT main.*" +
632                     " FROM <%=attrClassifierDBName%> main, <%=attrJoinTableName%> secondary" +
633                     " WHERE main.topiaid = secondary.<%=attrDBName%>" +
634                     " AND secondary.<%=attrReverseDBName%> = :other")
635                     .addEntity("main", <%=providerFQN%>(<%=attrType%>.class))
636                     .setParameter("other", entity.getTopiaId())
637                     .list();
638 
639             for (<%=attrType%> item : list) {
640                 item.<%=removeName%>(entity);
641             }
642         }
643 }*/
644                 );
645             } else if (!GeneratorUtil.isNMultiplicity(reverse)) {
646                 // On doit mettre a null les attributs qui ont cet objet sur les
647                 // autres entites en one-to-*
648                 // TODO peut-etre qu'hibernate est capable de faire ca tout seul ?
649                 // THIMEL: J'ai remplacé reverse.getName() par reverseAttrName sans certitude
650                 addImport(result, attrType);
651                 String attrSimpleType = TopiaGeneratorUtil.getClassNameFromQualifiedName(attrType);
652                 String getName = getJavaBeanMethodName("get", reverseAttrName);
653                 String setName = getJavaBeanMethodName("set", reverseAttrName);
654 
655                 body.append(""
656                         /*{
657                                         {
658                                         List<<%=attrSimpleType%>> list = getContext()
659                                                     .getDAO(<%=attrSimpleType%>.class)
660                                                     .findAllByProperties(<%=attrSimpleType%>.<%=getConstantName(reverseAttrName)%>, entity);
661                                             for (<%=attrSimpleType%> item : list) {
662 
663                                                 // sletellier : Set null only if target is concerned by deletion
664                                                 if (entity.equals(item.<%=getName%>())) {
665                                                     item.<%=setName%>(null);
666                                                 }
667                         }*/
668                 );
669                 if (attr.isAggregate()) {
670                     body.append(""
671 /*{
672             			getContext().getDAO(<%=attrSimpleType%>.class).delete(item);
673             			//item.delete();
674 }*/
675                     );
676                 }
677                 body.append(""
678 /*{
679                     }
680                 }
681 }*/
682                 );
683 
684             }
685         }
686 
687         if (body.length()>0) {
688             // something specific was done, need to generate the method
689             ObjectModelOperation op;
690             op = addOperation(result, "delete", "void", ObjectModelJavaModifier.PUBLIC);
691             addAnnotation(result, op,Override.class.getSimpleName());
692             addException(op, TopiaException.class);
693             addParameter(op, "E", "entity");
694             body.append(""
695 /*{
696         super.delete(entity);
697     }*/
698         );
699             setOperationBody(op, body.toString());
700         }
701 
702 
703 
704     }
705 
706     protected void generateFindUsages(ObjectModelClass clazz,
707                                       ObjectModelClass result,
708                                       Set<ObjectModelClass> usagesForclass) {
709 
710         builder.addImport(result, ArrayList.class.getName());
711         builder.addImport(result, Map.class.getName());
712         builder.addImport(result, HashMap.class.getName());
713         builder.addImport(result, TopiaEntity.class.getName());
714 
715         if (clazz instanceof ObjectModelAssociationClass ||
716             usagesForclass.isEmpty()) {
717             // not for an association class
718             // just let a null method
719             ObjectModelOperation operation;
720             operation = addOperation(result,
721                     "findUsages",
722                     "<U extends TopiaEntity> List<U>",
723                     ObjectModelJavaModifier.PUBLIC);
724 
725             addParameter(operation, "Class<U>", "type");
726             addParameter(operation, "E", "entity");
727             addException(operation, TopiaException.class);
728             addAnnotation(result, operation, "Override");
729             setOperationBody(operation, ""
730 /*{
731         return new ArrayList<U>();
732     }*/
733             );
734 
735             operation = addOperation(result,
736                     "findAllUsages",
737                     "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>",
738                     ObjectModelJavaModifier.PUBLIC);
739 
740             addParameter(operation, "E", "entity");
741             addException(operation, TopiaException.class);
742             addAnnotation(result, operation, "Override");
743             setOperationBody(operation, ""
744 /*{
745         return new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>();
746     }*/
747             );
748 
749             return;
750         }
751         List<ObjectModelClass> allEntities;
752         Map<String, ObjectModelClass> allEntitiesByFQN;
753 
754         allEntities = TopiaGeneratorUtil.getEntityClasses(model, true);
755         allEntitiesByFQN = new TreeMap<String, ObjectModelClass>();
756 
757         // prepare usages map and fill allEntitiesByFQN map
758         for (ObjectModelClass klass : allEntities) {
759             allEntitiesByFQN.put(klass.getQualifiedName(), klass);
760         }
761 
762         ObjectModelOperation operation;
763         operation = addOperation(result,
764                 "findUsages",
765                 "<U extends TopiaEntity> List<U>",
766                 ObjectModelJavaModifier.PUBLIC);
767 
768         addParameter(operation, "Class<U>", "type");
769         addParameter(operation, "E", "entity");
770         addException(operation, TopiaException.class);
771         addAnnotation(result, operation, "Override");
772         StringBuilder buffer = new StringBuilder(300);
773         buffer.append(""
774 /*{
775         List<?> result = new ArrayList();
776         List tmp;
777 }*/
778         );
779 
780         for (ObjectModelClass usageClass : usagesForclass) {
781             String usageType = usageClass.getQualifiedName();
782             builder.addImport(result, usageType);
783             String usageSimpleType =
784                     TopiaGeneratorUtil.getClassNameFromQualifiedName(usageType);
785             String usageSimplePropertyMethod = "findAllBy" + usageSimpleType;
786             String usageCollectionPropertyMethod = "findAllContaining" + usageSimpleType;
787             for (ObjectModelAttribute attr : usageClass.getAttributes()) {
788                 if (!attr.isNavigable()) {
789                     // skip this case
790                     continue;
791                 }
792                 String type;
793                 String attrName = attr.getName();
794                 if (attr.hasAssociationClass()) {
795                     //FIXME-TC20100224 dont known how to do this ?
796                     continue;
797 //                    type = attr.getAssociationClass().getQualifiedName();
798 //                    //FIXME-TC20100224 : this is crazy ??? must find the good name
799 //                    // Perhaps need to make different cases?
800 //                    attrName = attrName + "_" + TopiaGeneratorUtil.toLowerCaseFirstLetter(attr.getAssociationClass().getName());
801                 } else {
802                     type = attr.getType();
803                 }
804                 if (!allEntitiesByFQN.containsKey(type)) {
805                     // not a entity, can skip for this attribute
806                     continue;
807                 }
808                 ObjectModelClass targetEntity = allEntitiesByFQN.get(type);
809 //                if (!type.equals(clazz.getQualifiedName())) {
810                 if (!targetEntity.equals(clazz)) {
811                     // not a good attribute reference
812                     continue;
813                 }
814                 // found something to seek
815 
816                 String methodName;
817                 if (TopiaGeneratorUtil.isNMultiplicity(attr)) {
818                     methodName = getJavaBeanMethodName("findAllContains", attrName);
819                 } else {
820                     methodName = getJavaBeanMethodName("findAllBy", attrName);
821                 }
822                 String daoName = StringUtils.capitalize(usageSimpleType) + "DAO";
823 
824                 builder.addImport(result, usageClass.getPackageName() + '.' + daoName);
825 
826                 buffer.append(""
827 /*{
828         if (type == <%=usageSimpleType%>.class) {
829             <%=daoName%> dao = (<%=daoName%>)
830                 getContext().getDAO(<%=usageSimpleType%>.class);
831             tmp = dao.<%=methodName%>(entity);
832             result.addAll(tmp);
833         }
834 }*/
835                 );
836             }
837         }
838 
839         buffer.append(""
840 /*{
841         return (List<U>) result;
842     }*/
843         );
844         setOperationBody(operation, buffer.toString());
845 
846         operation = addOperation(result,
847                 "findAllUsages",
848                 "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>",
849                 ObjectModelJavaModifier.PUBLIC);
850 
851         addParameter(operation, "E", "entity");
852         addException(operation, TopiaException.class);
853         addAnnotation(result, operation, "Override");
854 
855         buffer = new StringBuilder(300);
856         buffer.append(""
857 /*{
858         Map<Class<? extends TopiaEntity>,List<? extends TopiaEntity>> result;
859         result = new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(<%=usagesForclass.size()%>);
860 
861         List<? extends TopiaEntity> list;
862 }*/
863         );
864         for (ObjectModelClass usageClass : usagesForclass) {
865 
866             String fqn = usageClass.getName();
867             buffer.append(""
868 /*{
869         list = findUsages(<%=fqn%>.class, entity);
870         if (!list.isEmpty()) {
871             result.put(<%=fqn%>.class, list);
872         }
873 }*/
874             );
875 
876         }
877         buffer.append(""
878 /*{
879         return result;
880     }*/
881         );
882 
883         setOperationBody(operation, buffer.toString());
884     }
885 
886     /**
887      * Generation of DAO operations signatures from class. These operations are
888      * abstract and identified by <<dao>> stereotype in the model. The
889      * developper must defined these methods in the DAOImpl associated to this
890      * DAOAbstract.
891      *
892      * @param result     clazz where to add operations
893      * @param operations operations to generate
894      */
895     private void generateDAOOperations(ObjectModelClass result,
896                                        Collection<ObjectModelOperation>
897                                                operations) {
898         if (CollectionUtils.isEmpty(operations)) {
899 
900             // no extra operations to generate
901             return;
902         }
903         
904         for (ObjectModelOperation op : operations) {
905 
906             Set<String> exceptions = op.getExceptions();
907             exceptions.add(TopiaException.class.getName());
908             cloneOperation(op,
909                            result,
910                            true,
911                            ObjectModelJavaModifier.ABSTRACT,
912                            ObjectModelJavaModifier.fromVisibility(op.getVisibility())
913             );
914         }
915     }
916 
917 
918 
919 
920     private String generateSecurity(ObjectModelClass result,
921                                     ObjectModelClass clazz,
922                                     String tagValue) {
923         StringBuilder buffer = new StringBuilder();
924 
925         if (StringUtils.isNotEmpty(tagValue)) {
926             String security = tagValue;
927             Pattern propertiesPattern = Pattern
928                     .compile("((?:[_a-zA-Z0-9]+\\.)+(?:_?[A-Z][_a-zA-Z0-9]*\\.)+)attribute\\.(?:([_a-z0-9][_a-zA-Z0-9]*))#(?:(create|load|update|delete))");
929             String[] valuesSecurity = security.split(":");
930 
931             for (String valueSecurity : valuesSecurity) {
932                 Matcher matcher = propertiesPattern.matcher(valueSecurity);
933                 matcher.find();
934                 // className is fully qualified name of class
935                 String className = matcher.group(1);
936                 className = StringUtil.substring(className, 0, -1); // remove ended
937                 // .
938                 // target is class, attribute or operation
939                 String attributeName = matcher.group(2);
940                 String actions = matcher.group(3).toUpperCase();
941 
942                 String query = "";
943                 String daoClass = "";
944                 if (className.equals(clazz.getQualifiedName())) {
945                     query = "select " + attributeName + ".topiaId from " + clazz.getQualifiedName() + " where topiaId = :id";
946                     daoClass = clazz.getAttribute(attributeName).getClassifier().getQualifiedName();
947                 } else {
948                     query = "select at.topiaId from " + className + " at inner join at." + attributeName + " cl where cl.topiaId = :id";
949                     daoClass = className;
950                 }
951                 buffer.append(""
952 /*{
953               resultPermissions.addAll(getRequestPermission(topiaId,
954                                                             <%=actions%>,
955                                                             "<%=query%>",
956                                                             <%=daoClass%>.class));
957 }*/
958                 );
959             }
960         } else {
961             buffer.append(""
962 /*{            return null;
963     }*/
964             );
965         }
966         return buffer.toString();
967     }
968 
969     protected void generateNoNMultiplicity(String clazzName,
970                                            ObjectModelClass result,
971                                            ObjectModelAttribute attr,
972                                            boolean isAssoc) {
973         String attrName = attr.getName();
974         String attrType = attr.getType();
975         String propertyName = clazzName + "." + getConstantName(attrName);
976         if (!isAssoc && attr.hasAssociationClass()) {
977             String assocClassName = attr.getAssociationClass().getName();
978             String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr);
979             // It is about transitivity : use the property to access the
980             // associationClass + '.' + the property to access the expected
981             // attribute
982             // <class>.<attrAssoc> + '.' + <assocClass>.<attr>
983             propertyName =
984                     clazzName + '.' + getConstantName(assocAttrName) +
985                     " + '.' + " +
986                     assocClassName + '.' + getConstantName(attrName);
987         }
988 
989         ObjectModelOperation op;
990         op = addOperation(result,
991                 getJavaBeanMethodName("findBy", attrName),
992                 "E",
993                 ObjectModelJavaModifier.PUBLIC);
994         addException(op, TopiaException.class);
995         addParameter(op, attrType, "v");
996         setOperationBody(op, ""
997 /*{
998         E result = findByProperty(<%=propertyName%>, v);
999         return result;
1000     }*/
1001         );
1002 
1003         op = addOperation(result,
1004                 getJavaBeanMethodName("findAllBy", attrName),
1005                 "List<E>",
1006                 ObjectModelJavaModifier.PUBLIC);
1007         addException(op, TopiaException.class);
1008         addParameter(op, attrType, "v");
1009         setOperationBody(op, ""
1010 /*{
1011         List<E> result = findAllByProperty(<%=propertyName%>, v);
1012         return result;
1013     }*/
1014         );
1015 
1016         if (!isAssoc && attr.hasAssociationClass()) {
1017             String assocClassName = attr.getAssociationClass().getName();
1018             String assocClassFQN = attr.getAssociationClass().getQualifiedName();
1019             String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr);
1020             String assocPropertyConstantName = getConstantName(assocAttrName);
1021             op = addOperation(result,
1022                     getJavaBeanMethodName("findBy", assocClassName),
1023                     "E",
1024                     ObjectModelJavaModifier.PUBLIC);
1025             addException(op, TopiaException.class);
1026             addParameter(op, assocClassFQN, "value");
1027             setOperationBody(op, ""
1028 /*{
1029         E result = findByProperty(<%=clazzName + "." + assocPropertyConstantName%>, value);
1030         return result;
1031     }*/
1032             );
1033 
1034             op = addOperation(result,
1035                     getJavaBeanMethodName("findAllBy", assocClassName),
1036                     "List<E>",
1037                     ObjectModelJavaModifier.PUBLIC);
1038             addException(op, TopiaException.class);
1039             addParameter(op, assocClassFQN, "value");
1040             setOperationBody(op, ""
1041 /*{
1042         List<E> result = findAllByProperty(<%=clazzName + "." + assocPropertyConstantName%>, value);
1043         return result;
1044     }*/
1045             );
1046         }
1047     }
1048 
1049     protected void generateNMultiplicity(String clazzName,
1050                                          ObjectModelClass result,
1051                                          ObjectModelAttribute attr) {
1052         String attrName = attr.getName();
1053         String attrType = attr.getType();
1054         if (attr.hasAssociationClass()) {
1055             // do nothing for association class, too complex...
1056             return;
1057         }
1058         ObjectModelOperation op;
1059         // Since 2.4 do nothing, findContains and findAllContains are not generated anymore
1060         op = addOperation(result,
1061                     getJavaBeanMethodName("findContains", attrName),
1062                     "E",
1063                     ObjectModelJavaModifier.PUBLIC);
1064             addException(op, TopiaException.class);
1065             addParameter(op, attrType, "v");
1066             setOperationBody(op, ""
1067 /*{
1068         E result = findContains(<%=clazzName + "." + getConstantName(attrName)%>, v);
1069         return result;
1070     }*/
1071             );
1072 
1073         op = addOperation(result,
1074                     getJavaBeanMethodName("findAllContains", attrName),
1075                     "List<E>",
1076                     ObjectModelJavaModifier.PUBLIC);
1077             addException(op, TopiaException.class);
1078             addParameter(op, attrType, "v");
1079             setOperationBody(op, ""
1080 /*{
1081         List<E> result = findAllContains(<%=clazzName + "." + getConstantName(attrName)%>, v);
1082         return result;
1083     }*/
1084             );
1085     }
1086 
1087 
1088     /**
1089      * Obtain business operations of the DAO.
1090      *
1091      * This operations can not be generated, but must be written by developper.
1092      * 
1093      * @param clazz the clazz to test.
1094      * @return collections of extra operations, or empty collection if none found.
1095      */
1096     public Collection<ObjectModelOperation> getDAOOperations(
1097             ObjectModelClass clazz) {
1098 
1099 //        // Note : this collection will contains extra operations for DAO.
1100 //        // Overriding existing generated methods is not managed yet
1101 //        Collection<ObjectModelOperation> results =
1102 //                new ArrayList<ObjectModelOperation>();
1103 
1104 //        // This code will be deprecated
1105 //        for (ObjectModelOperation op : clazz.getOperations()) {
1106 //            if (TopiaGeneratorUtil.hasDaoStereotype(op)) {
1107 //                results.add(op);
1108 //            }
1109 //        }
1110         Collection<ObjectModelOperation> extra =
1111                 extraOperations.get(clazz.getQualifiedName());
1112         return extra;
1113 //        if (extra != null) {
1114 //            for (ObjectModelOperation op : extra) {
1115 //                results.add(op);
1116 //            }
1117 //        }
1118 
1119 //        return results;
1120     }
1121 
1122     private void generateNaturalId(ObjectModelClass result,
1123                                    ObjectModelClass clazz) {
1124         Set<ObjectModelAttribute> props =
1125                 TopiaGeneratorUtil.getNaturalIdAttributes(clazz);
1126 
1127         if (!props.isEmpty()) {
1128 
1129             if (log.isDebugEnabled()) {
1130                 log.debug("generateNaturalId for " + props);
1131             }
1132             ObjectModelOperation findByNaturalId = addOperation(result,
1133                     "findByNaturalId", "E", ObjectModelJavaModifier.PUBLIC);
1134             addException(findByNaturalId, TopiaException.class);
1135 
1136             ObjectModelOperation existByNaturalId = addOperation(result,
1137                     "existByNaturalId", "boolean", ObjectModelJavaModifier.PUBLIC);
1138             addException(existByNaturalId, TopiaException.class);
1139 
1140             // TODO sletellier 20120406 : remove method on 3.0
1141             ObjectModelOperation create = addOperation(result,
1142                     "create", "E", ObjectModelJavaModifier.PUBLIC);
1143 
1144             // sletellier : mark as Deprecated (http://nuiton.org/issues/2051)
1145             setDocumentation(create, "@deprecated since 2.6.10, prefer use {@link #createByNaturalId}\n");
1146             addAnnotation(result, create, "Deprecated");
1147             addException(create, TopiaException.class);
1148 
1149             ObjectModelOperation createByNaturalId = addOperation(result,
1150                     "createByNaturalId", "E", ObjectModelJavaModifier.PUBLIC);
1151 
1152             addException(createByNaturalId, TopiaException.class);
1153 
1154             // used for calling findByProperties in findByNaturalId
1155             String searchProperties = "";
1156             // used for calling findByNaturalId in existByNaturalId
1157 //            String params = "";
1158             String clazzName = clazz.getName();
1159             for (ObjectModelAttribute attr : props) {
1160                 String propName = attr.getName();
1161                 // add property as param in both methods
1162                 addParameter(findByNaturalId, attr.getType(), propName);
1163                 addParameter(existByNaturalId, attr.getType(), propName);
1164                 addParameter(create, attr.getType(), propName);
1165                 addParameter(createByNaturalId, attr.getType(), propName);
1166 
1167                 searchProperties +=
1168                         ", " + clazzName + '.' + getConstantName(propName) +
1169                                 ", " + propName;
1170                 //params += ", " + propName;
1171             }
1172             searchProperties = searchProperties.substring(2);
1173             //params = params.substring(2);
1174 
1175             setOperationBody(findByNaturalId, ""
1176 /*{
1177         return findByProperties(<%=searchProperties%>);
1178     }*/
1179             );
1180 
1181             setOperationBody(existByNaturalId, ""
1182 /*{
1183         return existByProperties(<%=searchProperties%>);
1184     }*/
1185             );
1186 
1187             setOperationBody(create, ""
1188 /*{
1189         return create(<%=searchProperties%>);
1190     }*/
1191             );
1192 
1193             setOperationBody(createByNaturalId, ""
1194 /*{
1195         return create(<%=searchProperties%>);
1196     }*/
1197             );
1198         }
1199     }
1200 
1201     private void generateNotNull(ObjectModelClass result,
1202                                  ObjectModelClass clazz) {
1203 
1204         Set<ObjectModelAttribute> props =
1205                 TopiaGeneratorUtil.getNotNullAttributes(clazz);
1206 
1207         if (!props.isEmpty()) {
1208 
1209             if (log.isDebugEnabled()) {
1210                 log.debug("generateNotNull for " + props);
1211             }
1212 
1213             ObjectModelOperation createByNotNull = addOperation(result,
1214                     "createByNotNull", "E", ObjectModelJavaModifier.PUBLIC);
1215 
1216             addException(createByNotNull, TopiaException.class);
1217 
1218             String searchProperties = "";
1219 //            String params = "";
1220             String clazzName = clazz.getName();
1221             for (ObjectModelAttribute attr : props) {
1222                 String propName = attr.getName();
1223                 // add property as param in both methods
1224                 addParameter(createByNotNull, attr.getType(), propName);
1225 
1226                 searchProperties +=
1227                         ", " + clazzName + '.' + getConstantName(propName) +
1228                                 ", " + propName;
1229                 //params += ", " + propName;
1230             }
1231             searchProperties = searchProperties.substring(2);
1232             //params = params.substring(2);
1233 
1234             setOperationBody(createByNotNull, ""
1235 /*{
1236         return create(<%=searchProperties%>);
1237     }*/
1238             );
1239         }
1240     }
1241 }