View Javadoc
1   package org.andromda.cartridges.ejb3.metafacades;
2   
3   import java.text.MessageFormat;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.Hashtable;
7   import java.util.Map;
8   import org.andromda.cartridges.ejb3.EJB3Globals;
9   import org.andromda.cartridges.ejb3.EJB3Profile;
10  import org.andromda.metafacades.uml.AssociationEndFacade;
11  import org.andromda.metafacades.uml.AttributeFacade;
12  import org.andromda.metafacades.uml.ClassifierFacade;
13  import org.andromda.metafacades.uml.Entity;
14  import org.andromda.metafacades.uml.EntityMetafacadeUtils;
15  import org.andromda.metafacades.uml.MetafacadeUtils;
16  import org.andromda.metafacades.uml.ModelElementFacade;
17  import org.andromda.metafacades.uml.TaggedValueFacade;
18  import org.andromda.metafacades.uml.TypeMappings;
19  import org.andromda.metafacades.uml.UMLMetafacadeProperties;
20  import org.andromda.metafacades.uml.UMLMetafacadeUtils;
21  import org.andromda.metafacades.uml.UMLProfile;
22  import org.apache.commons.lang.BooleanUtils;
23  import org.apache.commons.lang.ObjectUtils;
24  import org.apache.commons.lang.StringUtils;
25  
26  /**
27   * <p/>
28   * Represents an EJB association end. </p>
29   * MetafacadeLogic implementation for org.andromda.cartridges.ejb3.metafacades.EJB3AssociationEndFacade.
30   *
31   * @see EJB3AssociationEndFacade
32   */
33  public class EJB3AssociationEndFacadeLogicImpl
34      extends EJB3AssociationEndFacadeLogic
35  {
36      private static final long serialVersionUID = 34L;
37      /**
38       * The default composite association cascade property
39       */
40      private static final String ENTITY_DEFAULT_COMPOSITE_CASCADE = "entityCompositeCascade";
41  
42      /**
43       * The default aggregation association cascade property
44       */
45      private static final String ENTITY_DEFAULT_AGGREGATION_CASCADE = "entityAggregationCascade";
46  
47      /**
48       * The namespace property storing default collection type for associations
49       */
50      private static final String ASSOCIATION_COLLECTION_TYPE = "associationCollectionType";
51  
52      /**
53       * A flag indicating whether or not specific (java.util.Set, java.util.List,
54       * etc) collection interfaces should be used in assocation mutators and
55       * accessors or whether the generic java.util.Collection interface should be
56       * used.
57       */
58      private static final String SPECIFIC_COLLECTION_INTERFACES = "specificCollectionInterfaces";
59  
60      /**
61       * The property that defines the default collection interface, this is the
62       * interface used if the property defined by
63       * {@link #SPECIFIC_COLLECTION_INTERFACES} is false.
64       */
65      private static final String DEFAULT_COLLECTION_INTERFACE = "defaultCollectionInterface";
66  
67      /**
68       * Stores the default collection index name.
69       */
70      private static final String COLLECTION_INDEX_NAME = "associationEndCollectionIndexName";
71  
72      /**
73       * Stores the default collection index type.
74       */
75      private static final String COLLECTION_INDEX_TYPE = "associationEndCollectionIndexType";
76  
77      /**
78       * Represents the EJB3 <code>ALL</code> cascade option and fully qualified representation.
79       */
80      private static final String ENTITY_CASCADE_ALL = "ALL";
81      private static final String ENTITY_CASCADE_ALL_FQN = "javax.persistence.CascadeType.ALL";
82  
83      /**
84       * Represents the EJB3 <code>PERSIST</code> cascade option.
85       */
86      private static final String ENTITY_CASCADE_PERSIST = "PERSIST";
87      private static final String ENTITY_CASCADE_PERSIST_FQN = "javax.persistence.CascadeType.PERSIST";
88  
89      /**
90       * Represents the EJB3 <code>MERGE</code> cascade option.
91       */
92      private static final String ENTITY_CASCADE_MERGE = "MERGE";
93      private static final String ENTITY_CASCADE_MERGE_FQN = "javax.persistence.CascadeType.MERGE";
94  
95      /**
96       * Represents the EJB3 <code>REMOVE</code> cascade option.
97       */
98      private static final String ENTITY_CASCADE_REMOVE = "REMOVE";
99      private static final String ENTITY_CASCADE_REMOVE_FQN = "javax.persistence.CascadeType.REMOVE";
100 
101     /**
102      * Represents the EJB3 <code>REFRESH</code> cascade option.
103      */
104     private static final String ENTITY_CASCADE_REFRESH = "REFRESH";
105     private static final String ENTITY_CASCADE_REFRESH_FQN = "javax.persistence.CascadeType.REFRESH";
106 
107     /**
108      * Represents the value used to represents NO cascade option.
109      */
110     private static final String ENTITY_CASCADE_NONE = "NONE";
111 
112     /**
113      * Stores the cascade map of fully qualified cascade types
114      */
115     private static final Map<String, String> cascadeTable = new Hashtable<String, String>();
116 
117     static
118     {
119         cascadeTable.put(ENTITY_CASCADE_ALL, ENTITY_CASCADE_ALL_FQN);
120         cascadeTable.put(ENTITY_CASCADE_PERSIST, ENTITY_CASCADE_PERSIST_FQN);
121         cascadeTable.put(ENTITY_CASCADE_MERGE, ENTITY_CASCADE_MERGE_FQN);
122         cascadeTable.put(ENTITY_CASCADE_REMOVE, ENTITY_CASCADE_REMOVE_FQN);
123         cascadeTable.put(ENTITY_CASCADE_REFRESH, ENTITY_CASCADE_REFRESH_FQN);
124     }
125 
126     /**
127      * Value for set
128      */
129     private static final String COLLECTION_TYPE_SET = "set";
130 
131     /**
132      * Value for map
133      */
134     private static final String COLLECTION_TYPE_MAP = "map";
135 
136     /**
137      * Value for list
138      */
139     private static final String COLLECTION_TYPE_LIST = "list";
140 
141     /**
142      * Value for collections
143      */
144     private static final String COLLECTION_TYPE_COLLECTION = "bag";
145 
146     /**
147      * Stores the valid collection types
148      */
149     private static final Collection<String> collectionTypes = new ArrayList<String>();
150 
151     static
152     {
153         collectionTypes.add(COLLECTION_TYPE_SET);
154         collectionTypes.add(COLLECTION_TYPE_MAP);
155         collectionTypes.add(COLLECTION_TYPE_LIST);
156         collectionTypes.add(COLLECTION_TYPE_COLLECTION);
157     }
158 
159     /**
160      * Stores the property indicating whether or not composition should define
161      * the eager loading strategy and aggregation define lazy loading strategy.
162      */
163     private static final String COMPOSITION_DEFINES_EAGER_LOADING = "compositionDefinesEagerLoading";
164 
165     /**
166      * The property that stores whether relationship collection caching is enabled.
167      */
168     private static final String HIBERNATE_ASSOCIATION_ENABLE_CACHE = "hibernateEnableAssociationsCache";
169 
170     /**
171      * Stores the default cache strategy for relationship Collections.
172      */
173     private static final String HIBERNATE_ASSOCIATION_CACHE = "hibernateAssociationCache";
174 
175     /**
176      * The 'list' type implementation to use.
177      */
178     private static final String LIST_TYPE_IMPLEMENTATION = "listTypeImplementation";
179 
180     /**
181      * The 'set' type implementation to use.
182      */
183     private static final String SET_TYPE_IMPLEMENTATION = "setTypeImplementation";
184 
185     /**
186      * The 'map' type implementation to use.
187      */
188     private static final String MAP_TYPE_IMPLEMENTATION = "mapTypeImplementation";
189 
190     // ---------------- constructor -------------------------------
191 
192     /**
193      * @param metaObject
194      * @param context
195      */
196     public EJB3AssociationEndFacadeLogicImpl(final Object metaObject, final String context)
197     {
198         super (metaObject, context);
199     }
200 
201     // --------------- methods ---------------------
202 
203     /**
204      * @see AssociationEndFacade#getGetterSetterTypeName()
205      */
206     @Override public String getGetterSetterTypeName()
207     {
208         String getterSetterTypeName = null;
209 
210         if (this.isMany())
211         {
212             final boolean specificInterfaces =
213                 Boolean.valueOf(
214                     ObjectUtils.toString(this.getConfiguredProperty(SPECIFIC_COLLECTION_INTERFACES))).booleanValue();
215 
216             final TypeMappings mappings = this.getLanguageMappings();
217             if (mappings != null)
218             {
219                 if (this.isMap())
220                 {
221                     getterSetterTypeName = mappings.getTo(UMLProfile.MAP_TYPE_NAME);
222                 }
223                 else if (specificInterfaces)
224                 {
225                     if (this.isSet())
226                     {
227                         getterSetterTypeName = mappings.getTo(UMLProfile.SET_TYPE_NAME);
228                     }
229                     else if (this.isList())
230                     {
231                         getterSetterTypeName = mappings.getTo(UMLProfile.LIST_TYPE_NAME);
232                     }
233                     else if (this.isCollection())
234                     {
235                         getterSetterTypeName = mappings.getTo(UMLProfile.COLLECTION_TYPE_NAME);
236                     }
237                 }
238                 else
239                 {
240                     getterSetterTypeName = this.getDefaultCollectionInterface();
241                 }
242             }
243             else
244             {
245                 getterSetterTypeName = this.getDefaultCollectionInterface();
246             }
247         }
248         else
249         {
250             final ClassifierFacade type = this.getType();
251             if (type instanceof EJB3EntityFacade)
252             {
253                 final String typeName = ((EJB3EntityFacade)type).getFullyQualifiedEntityName();
254                 if (StringUtils.isNotBlank(typeName))
255                 {
256                     getterSetterTypeName = typeName;
257                 }
258             }
259         }
260 
261         if (getterSetterTypeName == null)
262         {
263             getterSetterTypeName = super.getGetterSetterTypeName();
264         }
265         else if (this.isMany())
266         {
267             /**
268              * Set this association end's type as a template parameter if required
269              */
270             if ("true".equals(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING)))
271             {
272                 getterSetterTypeName += '<'
273                     + (this.isMap() ? this.getCollectionIndexType() + ", " : "")
274                     + this.getType().getFullyQualifiedName() + '>';
275             }
276         }
277         return getterSetterTypeName;
278     }
279 
280     /**
281      * Overridden to provide handling of inheritance.
282      *
283      * @see AssociationEndFacade#isRequired()
284      */
285     @Override
286     public boolean isRequired()
287     {
288         // TODO: Fix UML Association isRequired to use getOtherEnd
289         boolean required = super.isRequired();
290         Object type = this.getOtherEnd().getType();
291 
292         if ((type != null) && EJB3EntityFacade.class.isAssignableFrom(type.getClass()))
293         {
294             EJB3EntityFacade entity = (EJB3EntityFacade)type;
295 
296             /**
297              * Exclude ONLY if single table inheritance exists
298              */
299             if (entity.isRequiresGeneralizationMapping() && entity.isInheritanceSingleTable()
300                     && !entity.isEmbeddableSuperclassGeneralizationExists())
301             {
302                 required = false;
303             }
304         }
305 
306         return required;
307     }
308 
309     /**
310      * @return fetch type
311      * @see EJB3AssociationEndFacade#getFetchType()
312      */
313     @Override
314     protected String handleGetFetchType()
315     {
316         String fetchType = (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_PERSISTENCE_FETCH_TYPE);
317 
318         if (StringUtils.isBlank(fetchType))
319         {
320             // check whether or not composition defines eager loading is turned on
321             final boolean compositionDefinesEagerLoading =
322                 Boolean.valueOf(String.valueOf(this.getConfiguredProperty(COMPOSITION_DEFINES_EAGER_LOADING)))
323                        .booleanValue();
324 
325             if (compositionDefinesEagerLoading)
326             {
327                 if (this.getOtherEnd().isComposition())
328                 {
329                     fetchType = EJB3Globals.FETCH_TYPE_EAGER;
330                 }
331                 else if (this.getOtherEnd().isAggregation())
332                 {
333                     fetchType = EJB3Globals.FETCH_TYPE_LAZY;
334                 }
335             }
336         }
337 
338         /**
339          * Go for defaults if blank
340          */
341         if (StringUtils.isBlank(fetchType))
342         {
343             if (this.getOtherEnd().isOne2Many() || this.getOtherEnd().isMany2Many())
344             {
345                 fetchType = EJB3Globals.FETCH_TYPE_LAZY;
346             }
347             else
348             {
349                 fetchType = EJB3Globals.FETCH_TYPE_EAGER;
350             }
351         }
352         return fetchType;
353     }
354 
355     /**
356      * @see EJB3AssociationEndFacadeLogic#handleIsEager()
357      */
358     @Override
359     protected boolean handleIsEager()
360     {
361         boolean isEager = false;
362         if (StringUtils.isNotBlank(this.getFetchType()))
363         {
364             if (EJB3Globals.FETCH_TYPE_EAGER.equalsIgnoreCase(this.getFetchType()))
365             {
366                 isEager = true;
367             }
368         }
369         return isEager;
370     }
371 
372     /**
373      * @see EJB3AssociationEndFacadeLogic#handleIsLazy()
374      */
375     @Override
376     protected boolean handleIsLazy()
377     {
378         boolean isLazy = false;
379         if (StringUtils.isNotBlank(this.getFetchType()))
380         {
381             if (EJB3Globals.FETCH_TYPE_LAZY.equalsIgnoreCase(this.getFetchType()))
382             {
383                 isLazy = true;
384             }
385         }
386         return isLazy;
387     }
388 
389     /**
390      * @see EJB3AssociationEndFacadeLogic#handleIsOwning()
391      */
392     @Override
393     protected boolean handleIsOwning()
394     {
395         return !UMLMetafacadeUtils.isOwningEnd(this);
396         /*boolean owning = false;
397         if (this.isAggregation() || this.isComposition() || !this.getOtherEnd().isNavigable()
398             || BooleanUtils.toBoolean(
399                 ObjectUtils.toString(this.findTaggedValue(
400                         EJB3Profile.TAGGEDVALUE_PERSISTENCE_ASSOCIATION_END_PRIMARY))))
401         {
402             owning = true;
403         }
404         // See if this end or the other end is tagged as the association owner
405         else if (BooleanUtils.toBoolean(
406             ObjectUtils.toString(this.getOtherEnd().findTaggedValue(
407                     EJB3Profile.TAGGEDVALUE_PERSISTENCE_ASSOCIATION_END_PRIMARY))))
408         {
409             owning = false;
410         }
411         // If other end not tagged, the many side of 1:M owns the bidirectional relationship
412         else if (this.isMany() && !this.getOtherEnd().isMany())
413         {
414             owning = true;
415         }
416         // Other side: aggregation/composition owns the bidirectional relationship
417         else if (this.getOtherEnd().isAggregation() || this.getOtherEnd().isComposition())
418         {
419             owning = false;
420         }
421         // Other side: the many side of 1:M owns the bidirectional relationship if no composition/aggregation
422         else if (!this.isMany() && this.getOtherEnd().isMany())
423         {
424             owning = false;
425         }
426         // If bidirectional 1:1 or M:M, choose the side with the longest type name because it typically indicates a composition relationship
427         //else if (this.getOtherEnd().getType().getName().length()
428         //        > this.getType().getName().length())
429         else if (this.getName().length()
430             > this.getOtherEnd().getName().length())
431         {
432             owning = true;
433         }
434         // If length is the same, alphabetically earliest is the owner
435         else if (this.getName().compareTo(
436                 this.getOtherEnd().getName()) < 0)
437         {
438             owning = true;
439         }
440         //System.out.println("handleIsOwning=" + owning + " for " + this.getName() + " OName=" + this.getOtherEnd().getName() + " Aggregation=" + this.isAggregation() + " Aggregation=" + this.isAggregation() + " Composition=" + this.isComposition() + " !Navigable=" + !this.isNavigable() + " Many=" + this.isMany() + " OMany=" + this.getOtherEnd().isMany() + " Upper=" + this.getUpper() + " OUpper=" + this.getOtherEnd().getUpper() + " OAggregation=" + this.getOtherEnd().isAggregation() + " OComposition=" + this.getOtherEnd().isComposition() + " ONavigable=" + this.getOtherEnd().isNavigable() + this);
441         return owning;*/
442     }
443 
444     /**
445      * @see EJB3AssociationEndFacadeLogic#handleIsOptional()
446      */
447     @Override
448     protected boolean handleIsOptional()
449     {
450         boolean optional = true;
451         String optionalString = (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_PERSISTENCE_OPTIONAL);
452 
453         if (StringUtils.isBlank(optionalString))
454         {
455             optional = !this.isRequired();
456         }
457         else
458         {
459             optional = Boolean.valueOf(optionalString).booleanValue();
460         }
461         return optional;
462     }
463 
464     /**
465      * @see EJB3AssociationEndFacadeLogic#handleGetOrderByClause()
466      */
467     @Override
468     protected String handleGetOrderByClause()
469     {
470         return (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_PERSISTENCE_ORDERBY);
471     }
472 
473     /**
474      * @see EJB3AssociationEndFacadeLogic#handleGetColumnDefinition()
475      */
476     @Override
477     protected String handleGetColumnDefinition()
478     {
479         return (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_PERSISTENCE_COLUMN_DEFINITION);
480     }
481 
482     /**
483      * Returns true if the tagged name exists for this association end.
484      *
485      * @param name The tagged name to lookup.
486      * @return boolean True if the tagged name exists.  False otherwise.
487      *
488      * @see EJB3AssociationEndFacadeLogic#handleHasTaggedValue(String)
489      */
490     @Override
491     protected boolean handleHasTaggedValue(String name)
492     {
493         boolean exists = false;
494         if (StringUtils.isNotBlank(name))
495         {
496             // trim to remove leading/trailing spaces
497             name = StringUtils.trimToEmpty(name);
498 
499             // loop over tagged values and match the argument tagged value name
500             for (TaggedValueFacade taggedValue : this.getTaggedValues())
501             {
502                 // return with true on the first match found
503                 //if (name.equals(taggedValue.getName()))
504                 String tagName = taggedValue.getName();
505                 if (name.equals(tagName) || MetafacadeUtils.getEmfTaggedValue(name).equals(tagName)
506                     || MetafacadeUtils.getUml14TaggedValue(name).equals(tagName))
507                 {
508                     exists = true;
509                     break;
510                 }
511             }
512         }
513         return exists;
514     }
515 
516     /**
517      * Resolves a comma separated list of cascade types from andromda.xml
518 
519      * @param cascadesString
520      * @return fully qualified cascade type sequence
521      */
522     private String getFullyQualifiedCascadeTypeList(final String cascadesString)
523     {
524         StringBuilder buf = null;
525         if (StringUtils.isNotBlank(cascadesString))
526         {
527             String[] ct = cascadesString.split(",");
528             for (int i = 0; i < ct.length; i++)
529             {
530                 final String value = ct[i].trim();
531                 if (StringUtils.isNotBlank(value))
532                 {
533                     if (buf == null)
534                     {
535                         buf = new StringBuilder();
536                     }
537                     else
538                     {
539                         buf.append(", ");
540                     }
541 
542                     buf.append(cascadeTable.get(value));
543                 }
544             }
545         }
546         return buf == null ? null : buf.toString();
547     }
548 
549     /**
550      * @see EJB3AssociationEndFacadeLogic#handleGetCascadeType()
551      */
552     @Override
553     protected String handleGetCascadeType()
554     {
555         String cascade = null;
556         final Collection<Object> taggedValues = this.findTaggedValues(EJB3Profile.TAGGEDVALUE_PERSISTENCE_CASCADE_TYPE);
557         if (taggedValues != null && !taggedValues.isEmpty())
558         {
559             StringBuilder buf = null;
560             for (Object value : taggedValues)
561             {
562                 if (buf == null)
563                 {
564                     buf = new StringBuilder();
565                 }
566                 else
567                 {
568                     buf.append(", ");
569                 }
570                 if (StringUtils.isNotBlank((String)value))
571                 {
572                     buf.append(cascadeTable.get(value));
573                 }
574             }
575             if (buf != null)
576             {
577                 cascade = buf.toString();
578             }
579         }
580         else if ((this.getOtherEnd() != null) &&
581                  (this.getOtherEnd().isAggregation() || this.getOtherEnd().isComposition()))
582         {
583             cascade = cascadeTable.get(ENTITY_CASCADE_REMOVE);
584             if (this.getOtherEnd().isComposition())
585             {
586                 if (StringUtils.isBlank(this.getCompositionCascadeType()))
587                 {
588                     if (this.getType() instanceof EJB3EntityFacade)
589                     {
590                         EJB3EntityFacade entity = (EJB3EntityFacade)this.getType();
591                         cascade = (ENTITY_CASCADE_NONE.equalsIgnoreCase(entity.getDefaultCascadeType()) ?
592                                 null : this.getFullyQualifiedCascadeTypeList(entity.getDefaultCascadeType()));
593                     }
594                 }
595                 else
596                 {
597                     cascade = (ENTITY_CASCADE_NONE.equalsIgnoreCase(this.getCompositionCascadeType()) ?
598                             null : this.getFullyQualifiedCascadeTypeList(this.getCompositionCascadeType()));
599                 }
600             }
601             else if (this.getOtherEnd().isAggregation())
602             {
603                 if (StringUtils.isBlank(this.getAggregationCascadeType()))
604                 {
605                     if (this.getType() instanceof EJB3EntityFacade)
606                     {
607                         EJB3EntityFacade entity = (EJB3EntityFacade)this.getType();
608                         cascade = (ENTITY_CASCADE_NONE.equalsIgnoreCase(entity.getDefaultCascadeType()) ?
609                                 null : this.getFullyQualifiedCascadeTypeList(entity.getDefaultCascadeType()));
610                     }
611                 }
612                 else
613                 {
614                     cascade = (ENTITY_CASCADE_NONE.equalsIgnoreCase(this.getAggregationCascadeType()) ?
615                             null : this.getFullyQualifiedCascadeTypeList(this.getAggregationCascadeType()));
616                 }
617             }
618         }
619         else if (this.isComposition())
620         {
621             /*
622              * On the composite side of the relationship, always enforce no cascade delete
623              * property indicating no cascadable propagation - overriding a default cascade
624              * value
625              */
626             // TODO cascade can only be null at this point - anything else to change?
627             //cascade = null;
628         }
629         else if (this.isAggregation())
630         {
631             /*
632              * On the aggregation side of the relationship, always enforce no cascade delete
633              * property indicating no cascadable propagation - overriding a default cascade
634              * value
635              */
636             // TODO cascade can only be null at this point - anything else to change?
637             //cascade = null;
638         }
639         return cascade;
640     }
641 
642     /**
643      * @see EJB3AssociationEndFacadeLogic#handleGetCompositionCascadeType()
644      */
645     @Override
646     protected String handleGetCompositionCascadeType()
647     {
648         return StringUtils.trimToEmpty(
649                 ObjectUtils.toString(this.getConfiguredProperty(ENTITY_DEFAULT_COMPOSITE_CASCADE)));
650     }
651 
652     /**
653      * @see EJB3AssociationEndFacadeLogic#handleGetAggregationCascadeType()
654      */
655     @Override
656     protected String handleGetAggregationCascadeType()
657     {
658         return StringUtils.trimToEmpty(
659                 ObjectUtils.toString(this.getConfiguredProperty(ENTITY_DEFAULT_AGGREGATION_CASCADE)));
660     }
661 
662     /**
663      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionType()
664      */
665     @Override
666     protected String handleGetCollectionType()
667     {
668         String collectionType = this.getSpecificCollectionType();
669         if (!collectionTypes.contains(collectionType))
670         {
671             if (this.isOrdered())
672             {
673                 collectionType = COLLECTION_TYPE_LIST;
674             }
675             else
676             {
677                 collectionType =
678                     (String)this.getConfiguredProperty(ASSOCIATION_COLLECTION_TYPE);
679             }
680         }
681         return collectionType;
682     }
683 
684     /**
685      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionTypeImplemenationClass()
686      */
687     @Override
688     protected String handleGetCollectionTypeImplemenationClass()
689     {
690         String collectionTypeImplementationClass = null;
691         if (this.isMany())
692         {
693             if (this.isSet())
694             {
695                 collectionTypeImplementationClass = String.valueOf(
696                         this.getConfiguredProperty(SET_TYPE_IMPLEMENTATION));
697             }
698             else if (this.isMap())
699             {
700                 collectionTypeImplementationClass = String.valueOf(
701                         this.getConfiguredProperty(MAP_TYPE_IMPLEMENTATION));
702             }
703             else if (this.isList() || this.isCollection())
704             {
705                 collectionTypeImplementationClass = String.valueOf(
706                         this.getConfiguredProperty(LIST_TYPE_IMPLEMENTATION));
707             }
708         }
709         return collectionTypeImplementationClass;
710     }
711 
712     /**
713      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionTypeImplementation()
714      */
715     @Override
716     protected String handleGetCollectionTypeImplementation()
717     {
718         return this.getCollectionTypeImplementation(null);
719     }
720 
721     /**
722      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionTypeImplementation(String)
723      */
724     @Override
725     protected String handleGetCollectionTypeImplementation(final String arg)
726     {
727         StringBuilder implementation = new StringBuilder();
728         if (this.isMany())
729         {
730             implementation.append("new ");
731             implementation.append(this.getCollectionTypeImplemenationClass());
732 
733             // set this association end's type as a template parameter if required
734             if (Boolean.valueOf(String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING)))
735                        .booleanValue())
736             {
737                 implementation.append("<");
738                 if (this.isMap())
739                 {
740                     implementation.append(this.getCollectionIndexType());
741                     implementation.append(", ");
742                 }
743                 implementation.append(this.getType().getFullyQualifiedName());
744                 implementation.append(">");
745             }
746             implementation.append("(");
747             if (StringUtils.isNotBlank(arg))
748             {
749                 implementation.append(arg);
750             }
751             implementation.append(")");
752         }
753 
754         return implementation.toString();
755     }
756 
757     /**
758      * Gets the collection type defined on this association end.
759      *
760      * @return the specific collection type.
761      */
762     private String getSpecificCollectionType()
763     {
764         return ObjectUtils.toString(
765             this.findTaggedValue(EJB3Profile.TAGGEDVALUE_ASSOCIATION_COLLECTION_TYPE));
766     }
767 
768     /**
769      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionIndexType()
770      */
771     @Override
772     protected String handleGetCollectionIndexType()
773     {
774         Object value = this.findTaggedValue(EJB3Profile.TAGGEDVALUE_ASSOCIATION_INDEX_TYPE);
775         if (value == null)
776         {
777             Object name = this.findTaggedValue(EJB3Profile.TAGGEDVALUE_ASSOCIATION_INDEX);
778             if (name == null)
779             {
780                 ClassifierFacade facade = this.getOtherEnd().getType();
781                 if (facade instanceof EJB3EntityFacade)
782                 {
783                     // Find the identifier
784                     ModelElementFacade identifier = ((EJB3EntityFacade)facade).getIdentifier();
785                     if (identifier instanceof AttributeFacade)
786                     {
787                         value = ((AttributeFacade)identifier).getType().getFullyQualifiedName();
788                         return value.toString();
789                     }
790                     else if (identifier instanceof AssociationEndFacade)
791                     {
792                         value = ((AssociationEndFacade)identifier).getType().getFullyQualifiedName();
793                         return value.toString();
794                     }
795                 }
796             }
797             // Find the attribute corresponding to name
798             Collection<AttributeFacade> attributes = ((EJB3EntityFacade)this.getOtherEnd().getType()).getAttributes();
799             for (AttributeFacade attrib : attributes)
800             {
801                 EJB3EntityAttributeFacade attribute = (EJB3EntityAttributeFacade)attrib;
802                 if (attribute.getName().equals(name))
803                 {
804                     value = attribute.getType().getFullyQualifiedName();
805                     return value.toString();
806                 }
807             }
808 
809             // value can only be null at this point
810             value = this.getConfiguredProperty(COLLECTION_INDEX_TYPE);
811             if (StringUtils.isBlank(ObjectUtils.toString(value)))
812             {
813                 value = null;
814             }
815          }
816 
817         if (value != null)
818         {
819             if (value instanceof String)
820             {
821                 value = this.getRootPackage().findModelElement((String)value);
822             }
823             if (value instanceof EJB3TypeFacade)
824             {
825                 value = ((EJB3TypeFacade)value).getFullyQualifiedEJB3Type();
826             }
827         }
828         return (value != null) ? ObjectUtils.toString(value) : null;
829     }
830 
831     /**
832      * @see EJB3AssociationEndFacadeLogic#handleGetCollectionIndexName()
833      */
834     @Override
835     protected String handleGetCollectionIndexName()
836     {
837         Object value = this.findTaggedValue(EJB3Profile.TAGGEDVALUE_ASSOCIATION_INDEX);
838         if ((value == null) && this.isConfiguredProperty(COLLECTION_INDEX_NAME))
839         {
840             value = this.getConfiguredProperty(COLLECTION_INDEX_NAME);
841             if (StringUtils.isBlank(ObjectUtils.toString(value)))
842             {
843                 value = null;
844             }
845         }
846 
847         if (value != null)
848         {
849             return ObjectUtils.toString(value);
850         }
851         final String otherEntityName = ((EJB3EntityFacade)this.getOtherEnd().getType()).getEntityName();
852         final Object separator = this.getConfiguredProperty(UMLMetafacadeProperties.SQL_NAME_SEPARATOR);
853         return EntityMetafacadeUtils.toSqlName(
854             otherEntityName,
855             separator) + separator + EntityMetafacadeUtils.toSqlName(
856             this.getName(),
857             separator) + separator + "IDX";
858     }
859 
860     /**
861      * @see EJB3AssociationEndFacadeLogic#handleIsMap()
862      */
863     @Override
864     protected boolean handleIsMap()
865     {
866         boolean isMap = COLLECTION_TYPE_MAP.equalsIgnoreCase(this.getCollectionType());
867         if (isMap && StringUtils.isBlank(this.getSpecificCollectionType()))
868         {
869             isMap = !this.isOrdered();
870         }
871         return isMap;
872     }
873 
874     /**
875      * @see EJB3AssociationEndFacadeLogic#handleIsList()
876      */
877     @Override
878     protected boolean handleIsList()
879     {
880         boolean isList = COLLECTION_TYPE_LIST.equalsIgnoreCase(this.getCollectionType());
881         if (!isList && StringUtils.isBlank(this.getSpecificCollectionType()))
882         {
883             isList = this.isOrdered();
884         }
885         return isList;
886     }
887 
888     /**
889      * @see EJB3AssociationEndFacadeLogic#handleIsSet()
890      */
891     @Override
892     protected boolean handleIsSet()
893     {
894         boolean isSet = COLLECTION_TYPE_SET.equalsIgnoreCase(this.getCollectionType());
895         if (isSet && StringUtils.isBlank(this.getSpecificCollectionType()))
896         {
897             isSet = !this.isOrdered();
898         }
899         return isSet;
900     }
901 
902     /**
903      * @see EJB3AssociationEndFacadeLogic#handleIsCollection()
904      */
905     @Override
906     protected boolean handleIsCollection()
907     {
908         boolean isCollection = COLLECTION_TYPE_COLLECTION.equalsIgnoreCase(this.getCollectionType());
909         if (!isCollection && StringUtils.isBlank(this.getSpecificCollectionType()))
910         {
911             isCollection = this.isOrdered();
912         }
913         return isCollection;
914     }
915 
916     /**
917      * @see EJB3AssociationEndFacadeLogic#handleGetLabelName()
918      */
919     @Override
920     protected String handleGetLabelName()
921     {
922         String labelNamePattern = (this.isMany() ?
923                 (String)this.getConfiguredProperty(EJB3Globals.LABEL_COLLECTION_NAME_PATTERN) :
924                     (String)this.getConfiguredProperty(EJB3Globals.LABEL_SINGLE_NAME_PATTERN));
925 
926         return MessageFormat.format(
927                 labelNamePattern,
928                 StringUtils.trimToEmpty(this.getName()));
929     }
930 
931     /**
932      * @see EJB3AssociationEndFacadeLogic#handleGetGetterLabelName()
933      */
934     @Override
935     protected String handleGetGetterLabelName()
936     {
937         return UMLMetafacadeUtils.getGetterPrefix(this.getType()) + StringUtils.capitalize(this.getLabelName());
938     }
939 
940     /**
941      * @see EJB3AssociationEndFacadeLogic#handleGetSetterLabelName()
942      */
943     @Override
944     protected String handleGetSetterLabelName()
945     {
946         return "set" + StringUtils.capitalize(this.getLabelName());
947     }
948 
949     /**
950      * @see EJB3AssociationEndFacadeLogic#handleGetCacheType()
951      */
952     @Override
953     protected String handleGetCacheType()
954     {
955         String cacheType = (String)super.findTaggedValue(EJB3Profile.TAGGEDVALUE_HIBERNATE_ASSOCIATION_CACHE);
956         if (StringUtils.isBlank(cacheType))
957         {
958             cacheType = String.valueOf(this.getConfiguredProperty(HIBERNATE_ASSOCIATION_CACHE));
959         }
960         return StringUtils.trimToEmpty(cacheType);
961     }
962 
963     /**
964      * @see EJB3AssociationEndFacadeLogic#handleIsAssociationCacheEnabled()
965      */
966     @Override
967     protected boolean handleIsAssociationCacheEnabled()
968     {
969         return BooleanUtils.toBoolean(String.valueOf(this.getConfiguredProperty(HIBERNATE_ASSOCIATION_ENABLE_CACHE)));
970     }
971 
972     /**
973      * @see EJB3AssociationEndFacadeLogic#handleIsForeignKeyConstraintDefined()
974      */
975     @Override
976     protected boolean handleIsForeignKeyConstraintDefined()
977     {
978         boolean fkConstraintDefined = false;
979         if (super.findTaggedValue(UMLProfile.TAGGEDVALUE_PERSISTENCE_FOREIGN_KEY_CONSTRAINT_NAME) != null)
980         {
981             fkConstraintDefined = true;
982         }
983         return fkConstraintDefined;
984     }
985 
986     /**
987      * @see EJB3AssociationEndFacadeLogic#handleGetForeignKeyConstraintName(String)
988      */
989     @Override
990     protected String handleGetForeignKeyConstraintName(final String suffix)
991     {
992         String constraintName;
993 
994         final Object taggedValueObject = super.findTaggedValue(
995                 UMLProfile.TAGGEDVALUE_PERSISTENCE_FOREIGN_KEY_CONSTRAINT_NAME);
996 
997         /**
998          * Construct our own foreign key constraint name here
999          */
1000         StringBuilder buffer = new StringBuilder();
1001 
1002         if (taggedValueObject == null)
1003         {
1004             final ClassifierFacade type = this.getOtherEnd().getType();
1005             if (type instanceof Entity)
1006             {
1007                 //Entity entity = (Entity)type;
1008                 //Instead of using the entity name, use the association end name to avoid duplication of
1009                 //FK constraint names which causes failures during table creation for some DBs (MySQL)
1010                 buffer.append(
1011                         EntityMetafacadeUtils.toSqlName(
1012                                 this.getOtherEnd().getName(),
1013                                 this.getConfiguredProperty(UMLMetafacadeProperties.SQL_NAME_SEPARATOR)));
1014             }
1015             else
1016             {
1017                 // should not happen
1018                 buffer.append(type.getName().toUpperCase());
1019             }
1020 
1021             buffer.append(this.getConfiguredProperty(UMLMetafacadeProperties.SQL_NAME_SEPARATOR));
1022 
1023             /**
1024              * Add the suffix - which is the name of the identifier pk column if not blank
1025              * otherwise use the column name of the relationship
1026              */
1027             if (StringUtils.isNotBlank(suffix))
1028             {
1029                 buffer.append(suffix);
1030             }
1031             else
1032             {
1033                 buffer.append(this.getColumnName());
1034             }
1035             constraintName = buffer.toString();
1036 
1037             final String constraintSuffix =
1038                 ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.CONSTRAINT_SUFFIX)).trim();
1039 
1040             /**
1041              * we take into consideration the maximum length allowed
1042              */
1043             final String maxLengthString = (String)super.getConfiguredProperty(UMLMetafacadeProperties.MAX_SQL_NAME_LENGTH);
1044             final short maxLength = (short)(Short.valueOf(maxLengthString).shortValue() - constraintSuffix.length());
1045             final String method = (String)super.getConfiguredProperty(UMLMetafacadeProperties.SHORTEN_SQL_NAMES_METHOD);
1046             buffer = new StringBuilder(
1047                     EntityMetafacadeUtils.ensureMaximumNameLength(constraintName, Short.valueOf(maxLength),method));
1048             buffer.append(constraintSuffix);
1049         }
1050         else
1051         {
1052             // use the tagged value
1053             buffer.append(taggedValueObject.toString());
1054         }
1055 
1056         return buffer.toString();
1057     }
1058 
1059     /**
1060      * @see EJB3AssociationEndFacadeLogic#handleGetForeignKeyName(String)
1061      */
1062     @Override
1063     protected String handleGetForeignKeyName(String suffix)
1064     {
1065         String columnName = null;
1066         // prevent ClassCastException if the association isn't an Entity
1067         if (this.getType() instanceof Entity)
1068         {
1069             if (StringUtils.isNotBlank(suffix))
1070             {
1071                 suffix = new String(
1072                         this.getConfiguredProperty(UMLMetafacadeProperties.SQL_NAME_SEPARATOR) +
1073                         suffix +
1074                         this.getForeignKeySuffix());
1075             }
1076             else
1077             {
1078                 suffix = this.getForeignKeySuffix();
1079             }
1080 
1081             final String columnNamePrefix =
1082                 this.isConfiguredProperty(UMLMetafacadeProperties.COLUMN_NAME_PREFIX)
1083                 ? ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.COLUMN_NAME_PREFIX)) : null;
1084             columnName =
1085                 EntityMetafacadeUtils.getSqlNameFromTaggedValue(
1086                     columnNamePrefix,
1087                     this,
1088                     UMLProfile.TAGGEDVALUE_PERSISTENCE_COLUMN,
1089                     ((Entity)this.getType()).getMaxSqlNameLength(),
1090                     suffix,
1091                     this.getConfiguredProperty(UMLMetafacadeProperties.SQL_NAME_SEPARATOR),
1092                     this.getConfiguredProperty(UMLMetafacadeProperties.SHORTEN_SQL_NAMES_METHOD));
1093         }
1094         return columnName;
1095     }
1096 
1097     /**
1098      * @see EJB3AssociationEndFacadeLogic#handleGetDefaultCollectionInterface()
1099      */
1100     @Override
1101     protected String handleGetDefaultCollectionInterface()
1102     {
1103         return ObjectUtils.toString(this.getConfiguredProperty(DEFAULT_COLLECTION_INTERFACE));
1104     }
1105 
1106     /**
1107      * @see EJB3AssociationEndFacadeLogic#handleIsCollectionInterfaceSortedSet()
1108      */
1109     @Override
1110     protected boolean handleIsCollectionInterfaceSortedSet()
1111     {
1112         boolean isInterfaceSortedSet = false;
1113         if (this.getGetterSetterTypeName().startsWith(EJB3Globals.COLLECTION_INTERFACE_SORTED_SET))
1114         {
1115             isInterfaceSortedSet = true;
1116         }
1117         return isInterfaceSortedSet;
1118     }
1119 
1120     /**
1121      * @see EJB3AssociationEndFacadeLogic#handleGetHibernateCascadeType()
1122      */
1123     @Override
1124     protected String handleGetHibernateCascadeType()
1125     {
1126         return (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_HIBERNATE_CASCADE);
1127     }
1128 
1129     /**
1130      * @see EJB3AssociationEndFacadeLogic#handleIsHibernateCascadeExists()
1131      */
1132     @Override
1133     protected boolean handleIsHibernateCascadeExists()
1134     {
1135         return StringUtils.isNotBlank(this.getHibernateCascadeType()) ? true : false;
1136     }
1137 
1138     /**
1139      * @see EJB3AssociationEndFacadeLogic#handleIsColumnNullable()
1140      */
1141     @Override
1142     protected boolean handleIsColumnNullable()
1143     {
1144         boolean nullable = true;
1145         String nullableString = (String)this.findTaggedValue(EJB3Profile.TAGGEDVALUE_PERSISTENCE_COLUMN_NULLABLE);
1146 
1147         if (StringUtils.isBlank(nullableString))
1148         {
1149             nullable = (this.isIdentifier() || this.isUnique()) ? false : !this.isRequired();
1150         }
1151         else
1152         {
1153             nullable = Boolean.valueOf(nullableString).booleanValue();
1154         }
1155         return nullable;
1156     }
1157 }