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  
25  /*{generator option: parentheses = true}*/
26  /*{generator option: writeString = output.write}*/
27  
28  /* *
29  * EntityHibernateMappingGenerator.java
30  *
31  * Created: 12 déc. 2005
32  *
33  * @author Arnaud Thimel &lt;thimel@codelutin.com&gt;
34  * @version $Revision$
35  *
36  */
37  
38  package org.nuiton.topia.generator;
39  
40  import org.apache.commons.lang3.BooleanUtils;
41  import org.apache.commons.lang3.StringUtils;
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.codehaus.plexus.component.annotations.Component;
45  import org.nuiton.eugene.Template;
46  import org.nuiton.eugene.models.object.ObjectModel;
47  import org.nuiton.eugene.models.object.ObjectModelAssociationClass;
48  import org.nuiton.eugene.models.object.ObjectModelAttribute;
49  import org.nuiton.eugene.models.object.ObjectModelClass;
50  import org.nuiton.eugene.models.object.ObjectModelGenerator;
51  
52  import java.beans.Introspector;
53  import java.io.File;
54  import java.io.IOException;
55  import java.io.Writer;
56  import java.sql.Types;
57  import java.util.ArrayList;
58  import java.util.HashMap;
59  import java.util.List;
60  import java.util.Map;
61  import java.util.TreeMap;
62  
63  import static org.nuiton.topia.generator.TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType;
64  
65  /**
66   *
67   * @author poussin &lt;poussin@codelutin.com&gt;
68   * @version $Id$
69   */
70  @Component(role = Template.class, hint = "org.nuiton.topia.generator.EntityHibernateMappingGenerator")
71  public class EntityHibernateMappingGenerator extends ObjectModelGenerator {
72  
73      /**
74       * Logger.
75       */
76      private static final Log log = LogFactory
77              .getLog(EntityHibernateMappingGenerator.class);
78  
79      private static final String HIBERNATE_ATTRIBUTE_DEFAULT = "default";
80  
81      private static final String HIBERNATE_ATTRIBUTE_SQL_TYPE = "sql-type";
82  
83      private static final String HIBERNATE_ATTRIBUTE_NAME = "name";
84  
85      private Map<String, String[]> columnNamesMap = new HashMap<String, String[]>();
86  
87      public static final String HIBERNATE_ATTRIBUTE_LAZY = "lazy";
88  
89      public static final String HIBERNATE_ATTRIBUTE_FETCH = "fetch";
90  
91      public static final String HIBERNATE_ATTRIBUTE_NOT_NULL = "not-null";
92  
93      public static final String HIBERNATE_ATTRIBUTE_SCHEMA = "schema";
94  
95      public static final String HIBERNATE_ATTRIBUTE_INDEX = "index";
96  
97      public static final String HIBERNATE_ATTRIBUTE_UNIQUE = "unique";
98  
99      public static final String HIBERNATE_ATTRIBUTE_LENGTH = "length";
100 
101     public static final String HIBERNATE_ATTRIBUTE_ORDER_BY = "order-by";
102 
103     public static final String HIBERNATE_ATTRIBUTE_FOREIGN_KEY = "foreign-key";
104 
105     static class ClassContext {
106 
107         private final ObjectModel model;
108 
109         private final ObjectModelClass input;
110 
111         private final boolean generateForeignKeyNames;
112 
113         private final String tableName;
114 
115         private final String schema;
116 
117         ClassContext(ObjectModel model, ObjectModelClass input) {
118             this.model = model;
119             this.input = input;
120             this.generateForeignKeyNames = TopiaGeneratorUtil.isGenerateForeignKeyNames(input, model);
121             this.tableName = TopiaGeneratorUtil.getDbName(input);
122             this.schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(input, model);
123         }
124 
125         public boolean isGenerateForeignKeyNames() {
126             return generateForeignKeyNames;
127         }
128 
129         public String getTableName() {
130             return tableName;
131         }
132 
133         public boolean isUseSchema() {
134             return schema != null;
135         }
136 
137         public String getSchema() {
138             return schema;
139         }
140 
141         public String getForeignKeyName(String attrColumn) {
142             return getForeignKeyName(tableName , attrColumn).toLowerCase();
143         }
144 
145         public String getForeignKeyName(String tableName, String attrColumn) {
146             return ("fk_" + tableName + "_" + attrColumn).toLowerCase();
147         }
148 
149         public ObjectModelClass getInput() {
150             return input;
151         }
152 
153     }
154 
155     @Override
156     public String getFilenameForClass(ObjectModelClass clazz) {
157         String DOName = TopiaGeneratorUtil.getDOType(clazz, model);
158         return DOName.replace('.', File.separatorChar) + ".hbm.xml";
159     }
160 
161     @Override
162     public void generateFromClass(Writer output,
163                                   ObjectModelClass input) throws IOException {
164         String persistenceType = TopiaGeneratorUtil.getPersistenceType(input);
165         if (!TopiaGeneratorUtil.isEntity(input) &&
166             TopiaGeneratorUtil.PERSISTENCE_TYPE_HIBERNATE.equals(persistenceType)) {
167             return;
168         }
169 /*{<?xml version="1.0" encoding="UTF-8"?>
170 <hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping"
171     xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping classpath://org/hibernate/hibernate-mapping-4.0.xsd" 
172     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
173     default-access="field" auto-import="true" package="<%=input.getPackageName()%>">
174 }*/
175 
176         ClassContext classContext = new ClassContext(model, input);
177 
178         boolean haveSuper = input.getSuperclasses().size() > 0;
179         // la liste des attributs faisant parti de la clef metier
180         List<ObjectModelAttribute> naturalAttributes = new ArrayList<ObjectModelAttribute>();
181         // la liste des autres attributs
182         List<ObjectModelAttribute> noneNaturalAttributes = new ArrayList<ObjectModelAttribute>();
183 
184         String clazzDOType = TopiaGeneratorUtil.getDOType(input, model);
185         String tableName = classContext.getTableName();
186         String isAbstract = BooleanUtils.toStringTrueFalse(input.isAbstract());
187         String clazzFQN = input.getQualifiedName();
188 
189         String optionalAttributes = "";
190         if (classContext.isUseSchema()) {
191             optionalAttributes += "schema=\"" + classContext.getSchema() + "\" ";
192         }
193 
194         //On précise au proxy de quelle interface hérite l'objet
195         String proxyTagValue = TopiaGeneratorUtil.getProxyInterfaceTagValue(input, model);
196         if (StringUtils.isEmpty(proxyTagValue) || !proxyTagValue.equals("none")) {
197             optionalAttributes += "proxy=\"" + clazzFQN + "\" ";
198         }
199 
200         if (!optionalAttributes.isEmpty()) {
201             optionalAttributes = " " + optionalAttributes.trim();
202         }
203         if (haveSuper) {
204             ObjectModelClass superClass = input.getSuperclasses().iterator().next();
205             String superClassname = superClass.getQualifiedName();
206             if (log.isDebugEnabled()) {
207             	log.debug("superClass for " + input.getQualifiedName() + " is " + superClassname);
208             }
209             String superClassDOType = TopiaGeneratorUtil.getDOType(superClassname, model);
210 
211 /*{    <union-subclass name="<%=clazzDOType%>" extends="<%=superClassDOType%>" table="<%=tableName%>" abstract="<%=isAbstract%>"<%=optionalAttributes%>>
212 }*/
213             // FIXME mieux gerer le cas haveSuper
214             noneNaturalAttributes.addAll(input.getAttributes());
215         } else {
216 /*{    <class name="<%=clazzDOType%>" table="<%=tableName%>" abstract="<%=isAbstract%>"<%=optionalAttributes%>>
217         <id name="topiaId" type="string" length="255"/>
218 }*/
219             // on detecte les attributs des clef metiers            
220             for (ObjectModelAttribute attr : input.getAttributes()) {
221                 if (TopiaGeneratorUtil.isNaturalId(attr)) {
222                     // attribut metier
223                     naturalAttributes.add(attr);
224                 } else {
225                     // attribut normal
226                     noneNaturalAttributes.add(attr);
227                 }
228             }
229             if (!naturalAttributes.isEmpty()) {
230                 // generation de la clef metier
231                 boolean mutable = TopiaGeneratorUtil.isNaturalIdMutable(input);
232                 String mutableStr = mutable ? " mutable=\"true\"" : "";
233                 if (log.isDebugEnabled()) {
234                     log.debug("natural-id detected for class " + input.getName() + " (" + mutableStr + ") attributes : " + naturalAttributes);
235                 }
236 /*{        <natural-id<%=mutableStr%>>
237 }*/
238                 generateAttributes(output, classContext, naturalAttributes, "    ");
239 /*{        </natural-id>
240 }*/
241             }
242 /*{        <version name="topiaVersion" type="long" />
243         <property name="topiaCreateDate" type="timestamp" />
244 }*/
245         }
246 
247         generateAttributes(output, classContext, noneNaturalAttributes, "");
248 
249         if (haveSuper) {
250 /*{    </union-subclass>
251 }*/
252         } else {
253 /*{    </class>
254 }*/
255         }
256 
257         generateDatabaseObjects(output, classContext, naturalAttributes);
258         generateDatabaseObjects(output, classContext, noneNaturalAttributes);
259 
260 /*{</hibernate-mapping>
261 }*/
262     }
263 
264     protected void generateDatabaseObjects(Writer output,
265                                            ClassContext classContext,
266                                            List<ObjectModelAttribute> attributes) throws IOException {
267 
268         for (ObjectModelAttribute attribute : attributes) {
269             if (!attribute.isNavigable() ||
270                 attribute.hasAssociationClass() ||
271                 !TopiaGeneratorUtil.isNMultiplicity(attribute) ||
272                     attribute.getClassifier() == null ||
273                     !TopiaGeneratorUtil.isEntity(attribute.getClassifier())
274                     ) {
275 
276                 // skip for this case (not a nm-multiplicity attribute)
277                 continue;
278             }
279 
280             String indexForeignKeys =
281                     TopiaGeneratorUtil.getIndexForeignKeys(attribute, model);
282 
283             if (StringUtils.isEmpty(indexForeignKeys) || !Boolean.valueOf(indexForeignKeys)) {
284 
285                 // no index to put of the attribute.
286                 continue;
287             }
288 
289             // add database-object to create and drop index
290 
291             // add schema if exist (http://nuiton.org/issues/2052)
292             String schema = classContext.getSchema();
293             boolean withSchema = classContext.isUseSchema();
294             String tableName;
295             String propertyName;
296 
297 
298             if (TopiaGeneratorUtil.isNMultiplicity(attribute.getReverseMaxMultiplicity())) {
299 
300                 // many to many
301                 tableName = TopiaGeneratorUtil.getManyToManyTableName(attribute);
302                 //propertyName = TopiaGeneratorUtil.getDbName(attribute.getReverseAttribute());
303                 // FIX https://forge.nuiton.org/issues/3674
304                 propertyName = TopiaGeneratorUtil.getReverseDbNameOnReverseAttribute(attribute);
305             } else {
306 
307                 // one to many
308                 tableName =TopiaGeneratorUtil.getDbName(attribute.getClassifier());
309                 //propertyName = TopiaGeneratorUtil.getDbName(attribute.getReverseAttribute());
310                 // FIX https://forge.nuiton.org/issues/3674
311                 propertyName = TopiaGeneratorUtil.getReverseDbNameOnReverseAttribute(attribute);
312             }
313 
314             String indexName = "idx";
315             if (withSchema) {
316                 indexName += '_' + schema;
317             }
318             indexName += '_' + tableName+ '_' + propertyName ;
319             indexName = indexName.toLowerCase();
320 
321             if (withSchema) {
322                 tableName = schema + "." + tableName;
323             }
324 /*{    <database-object>
325         <create>CREATE INDEX <%=indexName%> ON <%=tableName%>(<%=propertyName%>)</create>
326         <drop>DROP INDEX <%=indexName%></drop>
327     </database-object>
328 }*/
329 
330         }
331     }
332 
333     protected void generateAttributes(Writer output,
334                                       ClassContext classContext,
335                                       List<ObjectModelAttribute> attributes,
336                                       String prefix) throws IOException {
337         for (ObjectModelAttribute attr : attributes) {
338             ObjectModelAttribute reverse = attr.getReverseAttribute();
339 
340             // pour les asso quoi qu'il arrive il faut les lier des 2 cotes
341             // pour pouvoir supprimer en cascade l'asso lors de la suppression
342             // d'un des cotes
343             if (attr.isNavigable()
344                     || hasUnidirectionalRelationOnAbstractType(reverse, model)
345                     || attr.hasAssociationClass()) {
346                 if (!TopiaGeneratorUtil.isNMultiplicity(attr)) {
347                     if (attr.getClassifier() != null && TopiaGeneratorUtil.isEntity(attr.getClassifier())) {
348                         if (TopiaGeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) {
349                             generateHibernateManyToOne(output, classContext, attr, prefix);
350                         } else {
351                             generateHibernateOneToOne(output, classContext, attr, prefix);
352                         }
353                     } else {
354                         generateHibernateProperty(output, classContext, attr, prefix);
355                     }
356                 } else {
357                     if (attr.getClassifier() != null && TopiaGeneratorUtil.isEntity(attr.getClassifier())) {
358                         if (TopiaGeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) {
359                             generateHibernateManyToMany(output, classContext, attr, prefix);
360                         } else {
361                             generateHibernateOneToMany(output, classContext, attr, prefix);
362                         }
363                     } else {
364                         generateHibernateMany(output, classContext, attr, prefix);
365                     }
366                 }
367             }
368         }
369 
370         //Attributs pour les classes d'association
371         ObjectModelClass clazz = classContext.getInput();
372         if (clazz instanceof ObjectModelAssociationClass) {
373             ObjectModelAssociationClass assoc = (ObjectModelAssociationClass) clazz;
374             for (ObjectModelAttribute attr : assoc.getParticipantsAttributes()) {
375                 if (attr != null) {
376 
377 // Note(poussin) pour moi quoi qu'il arrive sur la classe d'association il faut
378 // un many-to-one, sinon on a des problemes.
379 //                    if ((!attr.getReverseAttribute().isNavigable()) || !Util.isNMultiplicity(attr.getReverseAttribute())) {
380 // / *{        <one-to-one name="<%=getName(attr, true)%>" class="<%=getType(attr, true)%>"<%=(TopiaGeneratorUtil.notEmpty(attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH))?(" length=\"" + attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH) + "\""):"")%><%=(attr.isComposite()?" cascade=\"delete\"":"")%>/>
381 // } */
382 //                    } else {
383                     String notNull = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr));
384                     String attrName = getName(attr, true);
385                     String attrType = getType(attr, true);
386                     String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr));
387                     String attrColumn = TopiaGeneratorUtil.getDbName(attr);
388                     String foreignKeyName ="";
389                     if (classContext.isGenerateForeignKeyNames()) {
390                         foreignKeyName = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_FOREIGN_KEY, classContext.getForeignKeyName(attrColumn)).trim();
391                     }
392 /*{<%=prefix%>        <many-to-one name="<%=attrName%>" class="<%=attrType%>" <%=lazy%>column="<%=attrColumn%>" <%=notNull%><%=foreignKeyName%>/>
393 }*/
394 //                    }
395                     //Ne sert plus grâce à l'utilisation de la navigabilité
396 //                    if (!attr.getReverseAttribute().isNavigable()) {
397 //                        String type = TopiaGeneratorUtil.getDOType(((ObjectModelClassifier)attr.getDeclaringElement()).getQualifiedName(), model);
398 //                        String name = Util.toLowerCaseFirstLetter(attr.getDeclaringElement().getName());
399 //                        if (log.isTraceEnabled()) {log.trace("reverse: " + type + " " + name);}
400 //                        if (!Util.isNMultiplicity(attr)) {
401 //{<!--        <one-to-one name="<%=name%>" class="<%=type%>"/>
402 //}
403 //                        } else {
404 //{        <many-to-one name="<%=name%>" class="<%=type%>" column="<%=name.toLowerCase()%>"/> -->
405 //}
406 //                        }
407 //                    }
408                 }
409             }
410         }
411     }
412 
413     protected String getName(ObjectModelAttribute attr) {
414         return getName(attr, false);
415     }
416 
417     protected String getName(ObjectModelAttribute attr, boolean isAssoc) {
418         String result = Introspector.decapitalize(attr.getName());
419         if (attr.hasAssociationClass() && !isAssoc) {
420             result = TopiaGeneratorUtil.getAssocAttrName(attr);
421         }
422         return result;
423     }
424 
425     protected String getType(ObjectModelAttribute attr) {
426         return getType(attr, false);
427     }
428 
429     protected String getType(ObjectModelAttribute attr, boolean isAssoc) {
430         String type = attr.getType();
431         String attrType = TopiaGeneratorUtil.getTypeTagValue(attr);
432         if (StringUtils.isNotEmpty(attrType)) {
433 
434             // tag value detected of the attribute
435             type = attrType;
436         } else {
437 
438             String modelType = model.getTagValue(type);
439             if (StringUtils.isNotEmpty(modelType)) {
440 
441                 // tag value detected of the model
442 
443                 //TODO tchemit 20100507 Explain What todes it do ? Dont understand the story of columnNamesMap
444                 int bracketIndex = modelType.indexOf('(');
445                 if (bracketIndex != -1) {
446                     type = modelType.substring(0, bracketIndex);
447                     int bracketEndIndex = modelType.indexOf(')', bracketIndex + 1);
448                     String colmunList;
449                     if (bracketEndIndex != -1) {
450                         colmunList = modelType.substring(bracketIndex + 1, bracketEndIndex);
451                     } else {
452                         colmunList = modelType.substring(bracketIndex);
453                     }
454                     columnNamesMap.put(type, colmunList.split(","));
455                 } else {
456                     type = modelType;
457                 }
458             }
459         }
460         if (attr.hasAssociationClass() && !isAssoc) {
461             type = attr.getAssociationClass().getQualifiedName();
462         }
463         return TopiaGeneratorUtil.getDOType(type, model);
464     }
465 
466     protected void generateHibernateProperty(Writer output,
467                                              ClassContext classContext,
468                                              ObjectModelAttribute attr,
469                                              String prefix) throws IOException {
470         String attrType = getType(attr);
471 
472         String accessField = "field";
473         String tagValue = TopiaGeneratorUtil.getAccessTagValue(attr);
474         if (StringUtils.isNotEmpty(tagValue)) {
475         	accessField = tagValue;
476         }
477         String attrName = attr.getName();
478         String declaringElementDBName = TopiaGeneratorUtil.getDbName(attr.getDeclaringElement());
479         String tableName = declaringElementDBName + "_" + attrName;
480 
481         boolean attrIsEnumeration = attr.getClassifier() != null
482                                  && attr.getClassifier().isEnum();
483 
484         if (attrType.trim().endsWith("[]")) {
485             attrType = attrType.trim().substring(0, attrType.trim().length()-2);
486 
487             String optionalAttributes = "";
488             if (classContext.isUseSchema()) {
489                 optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_SCHEMA, classContext.getSchema());
490             }
491 
492             if (TopiaGeneratorUtil.hasIndexedCollectionStereotype(attr)) {
493             	String indexName = tableName + "_idx";
494                 optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_INDEX, indexName);
495             }
496             if (!optionalAttributes.isEmpty()) {
497                 optionalAttributes = " " + optionalAttributes.trim();
498             }
499 
500 /*{<%=prefix%>        <primitive-array name="<%=attrName%>" table="<%=tableName%>" access="<%=accessField%>"<%=optionalAttributes%>>
501 <%=prefix%>          <key column="<%=declaringElementDBName%>"/>
502 <%=prefix%>          <list-index column="<%=attrName%>_idx"/>
503 <%=prefix%>          <element type="<%=attrType%>"/>
504 <%=prefix%>        </primitive-array>
505 }*/
506         } else {
507             String optionalAttributes = "";
508             if (TopiaGeneratorUtil.hasIndexedCollectionStereotype(attr)) {
509             	String indexName = tableName + "_idx";
510                 optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_INDEX, indexName);
511 //            	optionalAttributes += "index=\"" + indexName + "\"";
512             }
513 
514             if (TopiaGeneratorUtil.hasUniqueStereotype(attr)) {
515                 // the trim method is called on optionalAttributes after this set to suppress unusual space if no index is set on this attribute
516                 optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_UNIQUE, "true");
517 //                optionalAttributes += " unique=\"true\"";
518             }
519             optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr));
520 /*{<%=prefix%>        <property name="<%=attrName%>" access="<%=accessField%>"}*/
521             if ( ! attrIsEnumeration) {
522 /*{ type="<%=attrType%>"}*/
523             }
524             optionalAttributes = optionalAttributes.trim();
525             String[] columnNames = columnNamesMap.get(attrType);
526 
527             // contains all required attributes for a column node
528             Map<String,String> columnAttributes = new TreeMap<String, String>();
529             if (StringUtils.isNotEmpty(attr.getDefaultValue())) {
530                 //TC-20100129 with a default value we must use the column child tag
531 
532                 String defaultValue = attr.getDefaultValue().trim();
533                 columnAttributes.put(HIBERNATE_ATTRIBUTE_DEFAULT, defaultValue);
534             }
535             String sqlType = TopiaGeneratorUtil.getSqlTypeTagValue(attr);
536             if (!StringUtils.isEmpty(sqlType)) {
537 
538                 // an specific sql type was specified for the attribute, use it
539                 columnAttributes.put(HIBERNATE_ATTRIBUTE_SQL_TYPE, sqlType);
540             }
541 
542             // add length attribute if required
543             String lengthTagValue = TopiaGeneratorUtil.getLengthTagValue(attr);
544             if (!StringUtils.isEmpty(lengthTagValue)) {
545 
546                 optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_LENGTH, lengthTagValue);
547             }
548 
549             optionalAttributes = optionalAttributes.trim();
550             if (StringUtils.isNotEmpty(optionalAttributes)) {
551                 optionalAttributes = " " + optionalAttributes;
552             }
553 
554             // to know if specific column name mapping is given
555             boolean noSpecifiedColumn = columnNames == null || columnNames.length == 0;
556 
557             if (noSpecifiedColumn) {
558 
559                 String attrColumn = TopiaGeneratorUtil.getDbName(attr);
560 
561                 if (columnAttributes.isEmpty()) {
562 
563                     // simple case with no column node to generate
564 
565 /*{ column="<%=attrColumn%>"<%=optionalAttributes%>}*/
566                     if (attrIsEnumeration) {
567 /*{>
568 <%=prefix%>            <type name="org.hibernate.type.EnumType">
569 <%=prefix%>                <param name="<%=org.hibernate.type.EnumType.ENUM%>"><%=attrType%></param>}*/
570 
571                         // if the user tuned the model to use name instead of
572                         // ordinal to store the values, we must add a clause
573                         boolean useEnumerationName = TopiaGeneratorUtil.hasUseEnumerationNameTagValue(attr, model);
574                         if (useEnumerationName) {
575                             String enumSQLType = String.valueOf(Types.VARCHAR);
576 /*{
577 <%=prefix%>                <!-- using name instead of ordinal to store enumeration value -->
578 <%=prefix%>                <param name="<%=org.hibernate.type.EnumType.TYPE%>"><%=enumSQLType%></param>}*/
579                         }
580 
581 /*{
582 <%=prefix%>            </type>
583 <%=prefix%>        </property>
584 }*/
585                     } else {
586 /*{/>
587 }*/
588                     }
589                 } else {
590 
591                     // there is some attributes to write for the column node
592 
593                     columnAttributes.put(HIBERNATE_ATTRIBUTE_NAME, attrColumn);
594 
595                     String columnAttributesAsString ="";
596                     for (Map.Entry<String, String> entry :
597                             columnAttributes.entrySet()) {
598                         String name = entry.getKey();
599                         String value = entry.getValue();
600                         columnAttributesAsString += generateFromTagValue(name, value, null);
601                     }
602                     columnAttributesAsString = " " + columnAttributesAsString.trim();
603 /*{<%=optionalAttributes%>>
604 <%=prefix%>            <column<%=columnAttributesAsString%>/>
605 <%=prefix%>        </property>
606 }*/
607                 }
608             } else {
609 
610                 // there is a colum name mapping specified, must use it
611                 //FIXME tchemit 2010-12-29 Really don't know how to apply columnAttributes for multi-columns...
612 /*{<%=optionalAttributes%>>
613 }*/
614                 for (String columnName : columnNames) {
615                     columnName = attrName + "_" + columnName.trim();
616 /*{<%=prefix%>            <column name="<%=columnName%>"/>
617 }*/
618                 }
619 /*{<%=prefix%>        </property>
620 }*/
621             }
622         }
623     }
624 
625     protected void generateHibernateOneToOne(Writer output,
626                                              ClassContext classContext,
627                                              ObjectModelAttribute attr,
628                                              String prefix) throws IOException {
629 //      boolean accessField = hasUnidirectionalRelationOnAbstractType(attr.getReverseAttribute(), model);
630 /// *{        <one-to-one name="<%=getName(attr)%>" class="<%=getType(attr)%>"<%=(TopiaGeneratorUtil.notEmpty(attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH))?(" length=\"" + attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH) + "\""):"")%><%=((attr.isComposite() || attr.hasAssociationClass())?" cascade=\"delete\"":"")%><%=((accessField)?" access=\"field\"":"")%> />
631 //} */
632 
633         // for hibernate many-to-one with unique="true" => one-to-one
634         // but if it is one-to-zero-or-one unique contraints is violated
635         // with null values
636         boolean unique = TopiaGeneratorUtil.isOneMultiplicity(attr);
637         generateHibernateManyToOne(output, classContext, attr, unique, prefix);
638 
639     }
640 
641     protected void generateHibernateOneToMany(Writer output,
642                                               ClassContext classContext,
643                                               ObjectModelAttribute attr,
644                                               String prefix) throws IOException {
645         boolean needsIndex = TopiaGeneratorUtil.hasIndexedCollectionStereotype(attr);
646         boolean isInverse = attr.getReverseAttribute().isNavigable();
647         isInverse |= hasUnidirectionalRelationOnAbstractType(attr, model);
648 
649         String attrName = getName(attr); // ???
650         String attrType = getType(attr);
651         String reverseAttrDBName = TopiaGeneratorUtil.getReverseDbName(attr);
652         String orderBy = generateFromTagValue(HIBERNATE_ATTRIBUTE_ORDER_BY, TopiaGeneratorUtil.getOrderByTagValue(attr));
653 
654         String cascade = "";
655         if (attr.isComposite() || attr.hasAssociationClass()) {
656             cascade += "cascade=\"all,delete-orphan\" ";
657         }
658 
659         String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr), "true");
660 
661         String fetch = generateFromTagValue(HIBERNATE_ATTRIBUTE_FETCH, TopiaGeneratorUtil.getFetchTagValue(attr));
662 
663         String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr);
664         String inverse = "";
665         if (isInverse) {
666         	inverse = "inverse=\"true\" ";
667         }
668         String foreignKeyAttribute = "";
669         if (classContext.isGenerateForeignKeyNames()) {
670             String columnName = TopiaGeneratorUtil.getDbName(attr);
671             foreignKeyAttribute = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_FOREIGN_KEY, classContext.getForeignKeyName(columnName)).trim();
672         }
673         if (needsIndex) {
674 /*{<%=prefix%>        <<%=collType%> name="<%=attrName%>" <%=inverse%><%=lazy%><%=cascade%>>
675 <%=prefix%>            <key column="<%=reverseAttrDBName%>"<%=foreignKeyAttribute%>/>
676 <%=prefix%>            <list-index column="<%=reverseAttrDBName%>_idx"/>
677 <%=prefix%>            <one-to-many class="<%=attrType%>"/>
678 <%=prefix%>        </<%=collType%>>
679 }*/
680         } else {
681 /*{<%=prefix%>        <<%=collType%> name="<%=attrName%>" <%=inverse%><%=orderBy%><%=fetch%><%=lazy%><%=cascade%>>
682 <%=prefix%>            <key column="<%=reverseAttrDBName%>"<%=foreignKeyAttribute%>/>
683 <%=prefix%>            <one-to-many class="<%=attrType%>" />
684 <%=prefix%>        </<%=collType%>>
685 }*/
686         }
687     }
688 
689     private String generateFromTagValue(String attributeName, String tagValue) {
690 		return generateFromTagValue(attributeName, tagValue, null);
691 	}
692 
693     /**
694      * Generate hibernate xml attribute with a final space.
695      * @param attributeName
696      * @param tagValue
697      * @param defaultValue
698      * @return
699      */
700     private String generateFromTagValue(String attributeName, String tagValue, String defaultValue) {
701 		String result = "";
702         if (StringUtils.isNotEmpty(tagValue)) {
703             result+= attributeName + "=\"" + tagValue+"\" ";
704         } else if (defaultValue != null) {
705             result+= attributeName + "=\"" + defaultValue +"\" ";
706         }
707 //		if (attr.hasTagValue(tagName) || defaultValue != null) {
708 //			result+= attributeName + "=\"";
709 //			if (attr.hasTagValue(tagName)) {
710 //				result += attr.getTagValue(tagName);
711 //			} else {
712 //				result += defaultValue;
713 //			}
714 //			result += "\" ";
715 //		}
716 		return result;
717 	}
718 
719     protected void generateHibernateMany(Writer output,
720                                          ClassContext classContext,
721                                          ObjectModelAttribute attr,
722                                          String prefix) throws IOException {
723         boolean needsIndex = TopiaGeneratorUtil.hasIndexedCollectionStereotype(attr);
724         String attrName = getName(attr);
725         String attrType = getType(attr);
726         String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr);
727         String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr));
728         String attrColumn = TopiaGeneratorUtil.getDbName(attr);
729         String foreignKeyAttribute = "";
730         if (classContext.isGenerateForeignKeyNames()) {
731             foreignKeyAttribute = " " + HIBERNATE_ATTRIBUTE_FOREIGN_KEY + "=\"" + classContext.getTableName() + "_" + attrColumn + "\"";
732         }
733 
734 /*{<%=prefix%>        <<%=collType%> name="<%=attrName%>" <%=lazy%>>
735 <%=prefix%>            <key column="OWNER"<%=foreignKeyAttribute%>/>
736 }*/
737         if (needsIndex) {
738 /*{<%=prefix%>            <list-index/>
739 }*/
740         }
741 /*{<%=prefix%>            <element type="<%=attrType%>" column="<%=attrColumn%>" />
742 <%=prefix%>        </<%=collType%>>
743 }*/
744     }
745 
746     protected void generateHibernateManyToOne(Writer output,
747                                               ClassContext classContext,
748                                               ObjectModelAttribute attr,
749                                               String prefix) throws IOException {
750         generateHibernateManyToOne(output, classContext, attr, false, prefix);
751     }
752 
753     protected void generateHibernateManyToOne(Writer output,
754                                               ClassContext classContext,
755                                               ObjectModelAttribute attr,
756                                               boolean isUnique,
757                                               String prefix) throws IOException {
758     	String attrName = getName(attr);
759     	String attrType = getType(attr);
760     	String attrColumn = TopiaGeneratorUtil.getDbName(attr);
761 /*{<%=prefix%>        <many-to-one name="<%=attrName%>" class="<%=attrType%>" column="<%=attrColumn%>" }*/
762         if (attr.isComposite() || attr.hasAssociationClass()) {
763 /*{cascade="delete" }*/
764         }
765         if (classContext.isGenerateForeignKeyNames()) {
766             String foreignKeyName = generateFromTagValue(HIBERNATE_ATTRIBUTE_FOREIGN_KEY, classContext.getForeignKeyName(attrColumn));
767 /*{<%=foreignKeyName%>}*/
768         }
769         // Pour le test suivant, on verifie d'abord que l'attribut a un reverse.
770         // S'il n'en a pas, cela signifie qu'il ne s'agit pas d'un entite
771         // (au sens stereotype entity), donc a donc pas besoin de faire un access=field.
772         if (attr.getReverseAttribute() != null && hasUnidirectionalRelationOnAbstractType(attr.getReverseAttribute(), model)) {
773 /*{access="field" }*/
774         }
775         // vérifier si le tag lazy est defini par defaut dans le fichier de proprietes
776         String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr));
777 /*{<%=lazy%>}*/
778         String notNull = generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr));
779 /*{<%=notNull%>}*/
780         if (isUnique) {
781 /*{unique="true" }*/
782         }
783 /*{/>
784 }*/
785     }
786 
787     protected void generateHibernateManyToMany(Writer output,
788                                                ClassContext classContext,
789                                                ObjectModelAttribute attr,
790                                                String prefix) throws IOException {
791         // On ne met le inverse="true" uniquement pour un seul coté de la relation.
792         // Dans le cas contraire, les modifications dans la relation ne seront
793         // pas sauvegardées. Ceci n'est vrai que si les deux coté sont navigable
794         boolean isInverse = attr.isNavigable() && attr.getReverseAttribute().isNavigable();
795         //isInverse |= !Util.isFirstAttribute(attr);
796         //isInverse = false; // 20070117 poussin: pour du many, jamais de inverse
797 
798         // Modification FD-2010-04-01 :
799         // Le tagvalue "inverse" permet de spécifier qui possède le
800         // inverse="true". Il est impératif de l'utiliser sur les deux
801         // extrémités pour ne pas avoir de surprise.
802         String inverseValue = TopiaGeneratorUtil.getInverseTagValue(attr);
803         if (StringUtils.isNotEmpty(inverseValue)) {
804             isInverse &= Boolean.parseBoolean(inverseValue);
805         // Si aucun tagvalue n'est défini, le choix est arbitraire : le
806         // premier attribut dans l'ordre alphabétique sera choisi pour porter le
807         // inverse="true"
808         } else {
809             isInverse &= TopiaGeneratorUtil.isFirstAttribute(attr);
810         }
811 
812         boolean needsIndex = TopiaGeneratorUtil.hasIndexedCollectionStereotype(attr);
813         String cascade = "";
814         if (attr.isComposite() || attr.hasAssociationClass()) {
815             cascade = " cascade=\"delete,delete-orphan\"";
816         }
817 
818         String attrType = getType(attr);
819         String attrName = getName(attr);
820         String attrColumn = TopiaGeneratorUtil.getDbName(attr);
821         String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr), "true");
822         String orderBy = generateFromTagValue(HIBERNATE_ATTRIBUTE_ORDER_BY, TopiaGeneratorUtil.getOrderByTagValue(attr));
823         String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr);
824         String tableName = TopiaGeneratorUtil.getManyToManyTableName(attr);
825         String inverse = "";
826         if (isInverse) {
827         	inverse = "inverse=\"true\" ";
828         }
829         String reverseAttrDBName = TopiaGeneratorUtil.getReverseDbName(attr);
830         String optionalAttributes="";
831 
832         if (classContext.isUseSchema()) {
833             optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_SCHEMA, classContext.getSchema());
834         }
835         if (!optionalAttributes.isEmpty()) {
836             optionalAttributes = " " + optionalAttributes.trim();
837         }
838         String foreignKeyName = "";
839         String reverseForeignKeyName = "";
840         if (classContext.isGenerateForeignKeyNames()) {
841             foreignKeyName = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_FOREIGN_KEY, classContext.getForeignKeyName(tableName, reverseAttrDBName)).trim();
842             reverseForeignKeyName = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_FOREIGN_KEY, classContext.getForeignKeyName(tableName, attrColumn)).trim();
843         }
844 
845 /*{<%=prefix%>        <<%=collType%> name="<%=attrName%>" table="<%=tableName%>" <%=inverse%><%=lazy%><%=cascade%><%=optionalAttributes%>>
846 <%=prefix%>            <key column="<%=reverseAttrDBName%>"<%=foreignKeyName%>/>
847 }*/
848         if (needsIndex) {
849 /*{<%=prefix%>            <list-index column="<%=reverseAttrDBName%>_idx"/>
850 }*/
851         }
852 /*{<%=prefix%>            <many-to-many class="<%=attrType%>" column="<%=attrColumn%>" <%=orderBy%><%=reverseForeignKeyName%>/>
853 <%=prefix%>        </<%=collType%>>
854 }*/
855     }
856 
857 } //EntityHibernateMappingGenerator