001package org.andromda.metafacades.uml14;
002
003import java.util.Collection;
004import org.andromda.metafacades.uml.ClassifierFacade;
005import org.andromda.metafacades.uml.NameMasker;
006import org.andromda.metafacades.uml.TypeMappings;
007import org.andromda.metafacades.uml.UMLMetafacadeProperties;
008import org.andromda.metafacades.uml.UMLMetafacadeUtils;
009import org.andromda.metafacades.uml.UMLProfile;
010import org.andromda.utils.StringUtilsHelper;
011import org.apache.commons.lang.BooleanUtils;
012import org.apache.commons.lang.ObjectUtils;
013import org.apache.commons.lang.StringUtils;
014import org.omg.uml.foundation.core.AssociationEnd;
015import org.omg.uml.foundation.core.Classifier;
016import org.omg.uml.foundation.core.UmlAssociation;
017import org.omg.uml.foundation.datatypes.AggregationKindEnum;
018import org.omg.uml.foundation.datatypes.ChangeableKindEnum;
019import org.omg.uml.foundation.datatypes.Multiplicity;
020import org.omg.uml.foundation.datatypes.MultiplicityRange;
021import org.omg.uml.foundation.datatypes.OrderingKind;
022import org.omg.uml.foundation.datatypes.OrderingKindEnum;
023
024/**
025 * Metaclass facade implementation.
026 * @author Bob Fields
027 */
028public class AssociationEndFacadeLogicImpl
029    extends AssociationEndFacadeLogic
030{
031    private static final long serialVersionUID = 34L;
032    /**
033     * @param metaObject
034     * @param context
035     */
036    public AssociationEndFacadeLogicImpl(
037        org.omg.uml.foundation.core.AssociationEnd metaObject,
038        String context)
039    {
040        super(metaObject, context);
041    }
042
043    /**
044     * @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner()
045     */
046    public ClassifierFacade getValidationOwner()
047    {
048        return this.getType();
049    }
050
051    /**
052     * @see org.andromda.metafacades.uml.AssociationEndFacade#getOtherEnd()
053     */
054    @Override
055    protected AssociationEnd handleGetOtherEnd()
056    {
057        final Collection<AssociationEnd> ends = metaObject.getAssociation().getConnection();
058        for (final AssociationEnd end : ends)
059        {
060            if (!metaObject.equals(end))
061            {
062                return end;
063            }
064        }
065        return null;
066    }
067
068    /**
069     * @see org.andromda.metafacades.uml14.ModelElementFacadeLogic#handleGetName()
070     */
071    protected String handleGetName()
072    {
073        String name = super.handleGetName();
074
075        // if name is empty, then get the name from the type
076        if (StringUtils.isEmpty(name))
077        {
078            final ClassifierFacade type = this.getType();
079            if (type != null)
080            {
081                name = StringUtils.uncapitalize(StringUtils.trimToEmpty(type.getName()));
082            }
083        }
084        if (this.isMany() && this.isPluralizeAssociationEndNames())
085        {
086            name = StringUtilsHelper.pluralize(name);
087        }
088        final String nameMask =
089            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.CLASSIFIER_PROPERTY_NAME_MASK));
090        return NameMasker.mask(
091            name,
092            nameMask);
093    }
094
095    /**
096     * Indicates whether or not we should pluralize association end names.
097     *
098     * @return true/false
099     */
100    private boolean isPluralizeAssociationEndNames()
101    {
102        final Object value = this.getConfiguredProperty(UMLMetafacadeProperties.PLURALIZE_ASSOCIATION_END_NAMES);
103        return value != null && Boolean.valueOf(String.valueOf(value));
104    }
105
106    /**
107     * @return metaObject.getAggregation().toString()
108     * @see org.andromda.metafacades.uml.AssociationEndFacade#getAggregationKind()
109     */
110    protected String handleGetAggregationKind()
111    {
112        return metaObject.getAggregation().toString();
113    }
114
115    /**
116     * NOT IMPLEMENTED - UML2 only
117     * @return "" always
118     * @see org.andromda.metafacades.uml.AssociationEndFacade#getDefault()
119     */
120    protected String handleGetDefault()
121    {
122        return "";
123    }
124
125    /**
126     * @see org.andromda.metafacades.uml.AssociationEndFacade#getType()
127     */
128    @Override
129    protected Classifier handleGetType()
130    {
131        return metaObject.getParticipant();
132    }
133
134    /**
135     * @see org.andromda.metafacades.uml.AssociationEndFacade#isOne2Many()
136     */
137    @Override
138    protected boolean handleIsOne2Many()
139    {
140        return !this.isMany() && this.getOtherEnd().isMany();
141    }
142
143    /**
144     * @see org.andromda.metafacades.uml.AssociationEndFacade#isMany2Many()
145     */
146    @Override
147    protected boolean handleIsMany2Many()
148    {
149        return this.isMany() && this.getOtherEnd().isMany();
150    }
151
152    /**
153     * @see org.andromda.metafacades.uml.AssociationEndFacade#isOne2One()
154     */
155    @Override
156    protected boolean handleIsOne2One()
157    {
158        return !this.isMany() && !this.getOtherEnd().isMany();
159    }
160
161    /**
162     * @see org.andromda.metafacades.uml.AssociationEndFacade#isMany2One()
163     */
164    @Override
165    protected boolean handleIsMany2One()
166    {
167        return this.isMany() && !this.getOtherEnd().isMany();
168    }
169
170    /**
171     * @see org.andromda.metafacades.uml.AssociationEndFacade#isMany()
172     */
173    @Override
174    protected boolean handleIsMany()
175    {
176        boolean isMany = false;
177        final Multiplicity multiplicity = this.metaObject.getMultiplicity();
178
179        // we'll say a null multiplicity is 1
180        if (multiplicity != null)
181        {
182            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
183            if (ranges != null && !ranges.isEmpty())
184            {
185                for (MultiplicityRange multiplicityRange : ranges)
186                {
187                    final int upper = multiplicityRange.getUpper();
188                    isMany = upper > 1 || upper < 0;
189                }
190            }
191        }
192        if (null!=this.getType() && !isMany)
193        {
194            // isCollectionType causes too many problems with the metafacades
195            isMany = this.getType().isArrayType();
196        }
197        return isMany;
198    }
199
200    /**
201     * UML2 Only: Returns false always.
202     * @return false
203     * @see org.andromda.metafacades.uml.AssociationEndFacade#isLeaf()
204     */
205    @Override
206    public boolean handleIsLeaf()
207    {
208        return false;
209    }
210
211    /**
212     * @see org.andromda.metafacades.uml.AssociationEndFacade#isOrdered()
213     */
214    @Override
215    protected boolean handleIsOrdered()
216    {
217        boolean ordered = false;
218
219        final OrderingKind ordering = metaObject.getOrdering();
220
221        // no ordering is 'unordered'
222        if (ordering != null)
223        {
224            ordered = ordering.equals(OrderingKindEnum.OK_ORDERED);
225        }
226
227        return ordered;
228    }
229
230    /**
231     * UML2 Only: Returns false always.
232     * @return hasStereotype(UMLProfile.STEREOTYPE_UNIQUE)
233     * @see org.andromda.metafacades.uml.AssociationEndFacade#isUnique()
234     */
235    @Override
236    public boolean handleIsUnique()
237    {
238        return this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE);
239    }
240
241    /**
242     * @see org.andromda.metafacades.uml.AssociationEndFacade#isAggregation()
243     */
244    @Override
245    protected boolean handleIsAggregation()
246    {
247        return AggregationKindEnum.AK_AGGREGATE.equals(metaObject.getAggregation());
248    }
249
250    /**
251     * @see org.andromda.metafacades.uml.AssociationEndFacade#isComposition()
252     */
253    @Override
254    protected boolean handleIsComposition()
255    {
256        return AggregationKindEnum.AK_COMPOSITE.equals(metaObject.getAggregation());
257    }
258
259    /**
260     * @see org.andromda.metafacades.uml.AssociationEndFacade#isReadOnly()
261     */
262    @Override
263    protected boolean handleIsReadOnly()
264    {
265        return ChangeableKindEnum.CK_FROZEN.equals(metaObject.getChangeability());
266    }
267
268    /**
269     * @see org.andromda.metafacades.uml.AssociationEndFacade#isNavigable()
270     */
271    @Override
272    protected boolean handleIsNavigable()
273    {
274        return metaObject.isNavigable();
275    }
276
277    /**
278     * @see org.andromda.metafacades.uml.AssociationEndFacade#getGetterName()
279     */
280    @Override
281    protected String handleGetGetterName()
282    {
283        return UMLMetafacadeUtils.getGetterPrefix(this.getType()) + StringUtilsHelper.capitalize(this.getName());
284    }
285
286    /**
287     * @see org.andromda.metafacades.uml.AssociationEndFacade#getSetterName()
288     */
289    @Override
290    protected String handleGetSetterName()
291    {
292        return "set" + StringUtils.capitalize(this.getName());
293    }
294
295    /**
296     * @see org.andromda.metafacades.uml.AssociationEndFacade#getAdderName()
297     */
298    @Override
299    protected String handleGetAdderName()
300    {
301        return "add" + StringUtils.capitalize(this.getName());
302    }
303
304    /**
305     * @see org.andromda.metafacades.uml.AssociationEndFacade#getRemoverName()
306     */
307    @Override
308    protected String handleGetRemoverName()
309    {
310        return "remove" + StringUtils.capitalize(this.getName());
311    }
312
313    /**
314     * @see org.andromda.metafacades.uml.AssociationEndFacade#isBindingDependenciesPresent()
315     */
316    @Override
317    protected boolean handleIsBidirectional()
318    {
319        return isNavigable() && getOtherEnd().isNavigable();
320    }
321
322    /**
323     * @see org.andromda.metafacades.uml.AssociationEndFacade#getAssociation()
324     */
325    @Override
326    protected UmlAssociation handleGetAssociation()
327    {
328        return metaObject.getAssociation();
329    }
330
331    /**
332     * @see org.andromda.metafacades.uml.AssociationEndFacade#getGetterSetterTypeName()
333     */
334    @Override
335    protected String handleGetGetterSetterTypeName()
336    {
337        String name = null;
338        if (this.isMany() && this.getType()!=null && !this.getType().isArrayType() && !this.getType().isCollectionType())
339        {
340            final TypeMappings mappings = this.getLanguageMappings();
341            if (mappings != null)
342            {
343                name =
344                    this.isOrdered() ? mappings.getTo(UMLProfile.LIST_TYPE_NAME)
345                                     : mappings.getTo(UMLProfile.COLLECTION_TYPE_NAME);
346            }
347
348            // set this association end's type as a template parameter if required
349            if (BooleanUtils.toBoolean(
350                    ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING)))
351                    && this.getType() != null)
352            {
353                String type = this.getType().getFullyQualifiedName();
354                /*Collection<GeneralizableElementFacade> specializations = this.getType().getAllSpecializations();
355                if ((specializations != null && !specializations.isEmpty()))
356                {
357                    name += "<? extends " + type + '>';
358                }
359                else
360                {*/
361                    name += '<' + type + '>';
362                //}
363            }
364        }
365        if (name == null && this.getType() != null)
366        {
367            name = this.getType().getFullyQualifiedName();
368        }
369        return name;
370    }
371
372    /**
373     * @see org.andromda.metafacades.uml.AssociationEndFacade#isRequired()
374     */
375    @Override
376    protected boolean handleIsRequired()
377    {
378        final int lower = this.getMultiplicityRangeLower();
379        return lower >= 1;
380    }
381
382    /**
383     * @see org.andromda.metafacades.uml.AssociationEndFacade#isChild()
384     */
385    @Override
386    protected boolean handleIsChild()
387    {
388        return this.getOtherEnd() != null && this.getOtherEnd().isComposition();
389    }
390
391    /**
392     * Returns the upper range of the multiplicity for the passed in attribute
393     *
394     * @return int the upper range of the multiplicity or 1 if it isn't defined.
395     */
396    private int getMultiplicityRangeUpper()
397    {
398        Integer upper = null;
399        final Multiplicity multiplicity = metaObject.getMultiplicity();
400        if (multiplicity != null)
401        {
402            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
403            if (ranges != null)
404            {
405                for (MultiplicityRange multiplicityRange  : ranges)
406                {
407                    upper = Integer.valueOf(multiplicityRange.getUpper());
408                }
409            }
410        }
411        if (upper == null)
412        {
413            upper = Integer.valueOf(1);
414        }
415        return upper.intValue();
416    }
417
418    /**
419     * Returns the lower range of the multiplicity for the passed in associationEnd
420     *
421     * @return int the lower range of the multiplicity or 1 if it isn't defined.
422     */
423    private int getMultiplicityRangeLower()
424    {
425        Integer lower = null;
426        final Multiplicity multiplicity = this.metaObject.getMultiplicity();
427        if (multiplicity != null)
428        {
429            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
430            if (ranges != null)
431            {
432                for (MultiplicityRange multiplicityRange : ranges)
433                {
434                    lower = Integer.valueOf(multiplicityRange.getLower());
435                }
436            }
437        }
438        if (lower == null)
439        {
440            final String defaultMultiplicity = this.getDefaultMultiplicity();
441            if (defaultMultiplicity.startsWith("0"))
442            {
443                lower = Integer.valueOf(0);
444            }
445            else
446            {
447                lower = Integer.valueOf(1);
448            }
449        }
450        return lower.intValue();
451    }
452
453    /**
454     * Gets the default multiplicity for this attribute (the
455     * multiplicity if none is defined).
456     *
457     * @return the default multiplicity as a String.
458     */
459    private String getDefaultMultiplicity()
460    {
461        return ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY));
462    }
463
464    /**
465     * Get the UML upper multiplicity
466     * Not implemented for UML1.4
467     */
468    @Override
469    protected int handleGetUpper()
470    {
471        return this.getMultiplicityRangeUpper();
472     }
473
474    /**
475     * Get the UML lower multiplicity
476     */
477    @Override
478    protected int handleGetLower()
479    {
480        return this.getMultiplicityRangeLower();
481    }
482
483    /**
484     * @see org.andromda.metafacades.uml14.AssociationEndFacadeLogic#handleIsDerived()
485     */
486    @Override
487    protected boolean handleIsDerived()
488    {
489        // UML 1.4 does not have derived association ends.
490        return false;
491    }
492
493    /**
494     * UML2 only. Can't determine association scope in UML14.
495     * @see org.andromda.metafacades.uml.AttributeFacade#isStatic()
496     */
497    @Override
498    public boolean handleIsStatic()
499    {
500        return false;
501    }
502}