001package org.andromda.metafacades.uml14;
002
003import java.util.Collection;
004import org.andromda.metafacades.uml.ClassifierFacade;
005import org.andromda.metafacades.uml.EnumerationFacade;
006import org.andromda.metafacades.uml.NameMasker;
007import org.andromda.metafacades.uml.TypeMappings;
008import org.andromda.metafacades.uml.UMLMetafacadeProperties;
009import org.andromda.metafacades.uml.UMLMetafacadeUtils;
010import org.andromda.metafacades.uml.UMLProfile;
011import org.andromda.utils.StringUtilsHelper;
012import org.apache.commons.lang.BooleanUtils;
013import org.apache.commons.lang.ObjectUtils;
014import org.apache.commons.lang.StringUtils;
015import org.omg.uml.foundation.core.Attribute;
016import org.omg.uml.foundation.core.Classifier;
017import org.omg.uml.foundation.datatypes.ChangeableKindEnum;
018import org.omg.uml.foundation.datatypes.Multiplicity;
019import org.omg.uml.foundation.datatypes.MultiplicityRange;
020import org.omg.uml.foundation.datatypes.OrderingKind;
021import org.omg.uml.foundation.datatypes.OrderingKindEnum;
022import org.omg.uml.foundation.datatypes.ScopeKindEnum;
023
024/**
025 * Metaclass facade implementation.
026 * @author Bob Fields
027 */
028public class AttributeFacadeLogicImpl
029    extends AttributeFacadeLogic
030{
031    private static final long serialVersionUID = 34L;
032    /**
033     * @param metaObject
034     * @param context
035     */
036    public AttributeFacadeLogicImpl(
037        Attribute metaObject,
038        String context)
039    {
040        super(metaObject, context);
041    }
042
043    /**
044     * @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner()
045    public ClassifierFacade getValidationOwner()
046    {
047        return this.getOwner();
048    }
049     */
050
051    /**
052     * @see org.andromda.metafacades.uml.AttributeFacade#getGetterName()
053     */
054    @Override
055    public String handleGetGetterName()
056    {
057        return UMLMetafacadeUtils.getGetterPrefix(this.getType()) + StringUtilsHelper.capitalize(this.getName());
058    }
059
060    /**
061     * @see org.andromda.metafacades.uml.AttributeFacade#getSetterName()
062     */
063    @Override
064    public String handleGetSetterName()
065    {
066        return "set" + StringUtils.capitalize(this.getName());
067    }
068
069    /**
070     * @see org.andromda.metafacades.uml.AttributeFacade#getDefaultValue()
071     */
072    @Override
073    public String handleGetDefaultValue()
074    {
075        String defaultValue = null;
076        if (this.metaObject.getInitialValue() != null)
077        {
078            defaultValue = this.metaObject.getInitialValue().getBody();
079        }
080        // Put single or double quotes around default in case modeler forgot to do it. Most templates
081        // declare Type attribute = $attribute.defaultValue, requiring quotes around the value
082        if (StringUtils.isNotBlank(defaultValue) && !this.isMany() && this.metaObject.getType() != null && defaultValue != null)
083        {
084            String typeName = this.metaObject.getType().getName();
085            if ("String".equals(typeName) && defaultValue.indexOf('"')<0)
086            {
087                defaultValue = '"' + defaultValue + '"';
088            }
089            else if (("char".equals(typeName) || "Character".equals(typeName))
090                && defaultValue.indexOf('\'')<0)
091            {
092                defaultValue = "'" + defaultValue.charAt(0) + '\'';
093            }
094        }
095        if (defaultValue==null) defaultValue="";
096        return defaultValue;
097    }
098
099    /**
100     * @see org.andromda.metafacades.uml.AttributeFacade#isChangeable()
101     */
102    @Override
103    public boolean handleIsChangeable()
104    {
105        return ChangeableKindEnum.CK_CHANGEABLE.equals(metaObject.getChangeability());
106    }
107
108    /**
109     * @see org.andromda.metafacades.uml.AttributeFacade#isAddOnly()
110     */
111    @Override
112    public boolean handleIsAddOnly()
113    {
114        return ChangeableKindEnum.CK_ADD_ONLY.equals(metaObject.getChangeability());
115    }
116
117    /**
118     * @see org.andromda.metafacades.uml.AttributeFacade#getType()
119     */
120    @Override
121    protected Classifier handleGetType()
122    {
123        return metaObject.getType();
124    }
125
126    /**
127     * @see org.andromda.metafacades.uml.AttributeFacade#getOwner()
128     */
129    @Override
130    public Classifier handleGetOwner()
131    {
132        return this.metaObject.getOwner();
133    }
134
135    /**
136     * @see org.andromda.metafacades.uml.AttributeFacade#isReadOnly()
137     */
138    @Override
139    public boolean handleIsReadOnly()
140    {
141        return ChangeableKindEnum.CK_FROZEN.equals(metaObject.getChangeability());
142    }
143
144    /**
145     * @see org.andromda.metafacades.uml.AttributeFacade#isStatic()
146     */
147    @Override
148    public boolean handleIsStatic()
149    {
150        return ScopeKindEnum.SK_CLASSIFIER.equals(this.metaObject.getOwnerScope());
151    }
152
153    /**
154     * @see org.andromda.metafacades.uml.AttributeFacade#findTaggedValue(String, boolean)
155     */
156    @Override
157    public Object handleFindTaggedValue(
158        String name,
159        boolean follow)
160    {
161        name = StringUtils.trimToEmpty(name);
162        Object value = findTaggedValue(name);
163        if (follow)
164        {
165            ClassifierFacade type = this.getType();
166            while (value == null && type != null)
167            {
168                value = type.findTaggedValue(name);
169                type = (ClassifierFacade)type.getGeneralization();
170            }
171        }
172        return value;
173    }
174
175    /**
176     * @see org.andromda.metafacades.uml.AttributeFacade#isRequired()
177     */
178    @Override
179    public boolean handleIsRequired()
180    {
181        int lower = this.getMultiplicityRangeLower();
182        return lower >= 1;
183    }
184
185    /**
186     * @see org.andromda.metafacades.uml.AttributeFacade#isMany()
187     */
188    @Override
189    public boolean handleIsMany()
190    {
191        boolean isMany = false;
192        final Multiplicity multiplicity = this.metaObject.getMultiplicity();
193
194        // assume no multiplicity is 1
195        if (multiplicity != null)
196        {
197            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
198            if (ranges != null && !ranges.isEmpty())
199            {
200                for (MultiplicityRange multiplicityRange : ranges)
201                {
202                    final int upper = multiplicityRange.getUpper();
203                    isMany = upper > 1 || upper < 0;
204                }
205            }
206        }
207        if (null!=this.getType() && !isMany)
208        {
209            // isCollectionType causes too many problems with metafacades
210            isMany = this.getType().isArrayType();
211        }
212        return isMany;
213    }
214
215    /**
216     * Returns the lower range of the multiplicity for the passed in attribute. If not specified, default is based on
217     * primitive or wrappedPrimitive type, or the default multiplicity.
218     *
219     * @return int the lower range of the multiplicity or the default multiplicity or 1 if it isn't defined.
220     */
221    private int getMultiplicityRangeLower()
222    {
223        Integer lower = null;
224        final Multiplicity multiplicity = metaObject.getMultiplicity();
225        if (multiplicity != null)
226        {
227            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
228            if (ranges != null && !ranges.isEmpty())
229            {
230                for (MultiplicityRange multiplicityRange : ranges)
231                {
232                    lower = Integer.valueOf(multiplicityRange.getLower());
233                }
234            }
235        }
236        if (lower == null)
237        {
238            if (this.getType().isPrimitive())
239            {
240                lower = Integer.valueOf(1);
241            }
242            else if (this.getType().isWrappedPrimitive())
243            {
244                lower = Integer.valueOf(0);
245            }
246            else
247            {
248                final String defaultMultiplicity = this.getDefaultMultiplicity();
249                if (defaultMultiplicity.startsWith("0"))
250                {
251                    lower = Integer.valueOf(0);
252                }
253                else
254                {
255                    lower = Integer.valueOf(1);
256                }
257            }
258        }
259        return lower.intValue();
260    }
261
262    /**
263     * Returns the upper range of the multiplicity for the passed in attribute
264     *
265     * @return int the upper range of the multiplicity or 1 if it isn't defined.
266     */
267    private int getMultiplicityRangeUpper()
268    {
269        Integer upper = null;
270        final Multiplicity multiplicity = metaObject.getMultiplicity();
271        if (multiplicity != null)
272        {
273            final Collection<MultiplicityRange> ranges = multiplicity.getRange();
274            if (ranges != null && !ranges.isEmpty())
275            {
276                for (MultiplicityRange multiplicityRange : ranges)
277                {
278                    upper = Integer.valueOf(multiplicityRange.getUpper());
279                }
280            }
281        }
282        if (upper == null)
283        {
284            upper = Integer.valueOf(1);
285        }
286        return upper.intValue();
287    }
288
289    /**
290     * Gets the default multiplicity for this attribute (the
291     * multiplicity if none is defined).
292     *
293     * @return the default multiplicity as a String.
294     */
295    private String getDefaultMultiplicity()
296    {
297        return ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY));
298    }
299
300    /**
301     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumeration()
302     */
303    @Override
304    protected EnumerationFacade handleGetEnumeration()
305    {
306        return (EnumerationFacade)(this.isEnumerationLiteral() ? this.getOwner() : null);
307    }
308
309    /**
310     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteral()
311     */
312    @Override
313    protected boolean handleIsEnumerationLiteral()
314    {
315        final ClassifierFacade owner = this.getOwner();
316        return (owner != null) && owner.isEnumeration();
317    }
318
319    /**
320     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationValue()
321     */
322    @Override
323    protected String handleGetEnumerationValue()
324    {
325        String value = null;
326        if (this.isEnumerationLiteral())
327        {
328            value = this.getDefaultValue();
329            value = StringUtils.isEmpty(value) ? this.getName() : String.valueOf(value);
330        }
331        if (this.getType().isStringType() && value!=null && value.indexOf('"')<0)
332        {
333            value = '\"' + value + '\"';
334        }
335        return value;
336    }
337
338    /**
339     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationMember()
340     */
341    @Override
342    protected boolean handleIsEnumerationMember()
343    {
344        boolean isMemberVariable = false;
345        final String isMemberVariableAsString = (String)this.findTaggedValue(
346                UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_MEMBER_VARIABLE);
347        if (StringUtils.isNotBlank(isMemberVariableAsString) && BooleanUtils.toBoolean(isMemberVariableAsString))
348        {
349            isMemberVariable = true;
350        }
351        return isMemberVariable;
352    }
353
354    /**
355     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationLiteralParameters()
356     */
357    @Override
358    protected String handleGetEnumerationLiteralParameters()
359    {
360        return (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_LITERAL_PARAMETERS);
361    }
362
363    /**
364     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteralParametersExist()
365     */
366    @Override
367    protected boolean handleIsEnumerationLiteralParametersExist()
368    {
369        boolean parametersExist = false;
370        if (StringUtils.isNotBlank(this.getEnumerationLiteralParameters()))
371        {
372            parametersExist = true;
373        }
374        return parametersExist;
375    }
376
377    /**
378     * @see org.andromda.metafacades.uml.AttributeFacade#isDefaultValuePresent()
379     */
380    @Override
381    public boolean handleIsDefaultValuePresent()
382    {
383        return StringUtils.isNotBlank(this.getDefaultValue());
384    }
385
386    /**
387     * Overridden to provide different handling of the name if this attribute represents a literal.
388     *
389     * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
390     */
391    @Override
392    protected String handleGetName()
393    {
394        String name = null;
395        if (this.isEnumerationMember())
396        {
397            name = super.handleGetName();
398        }
399        else
400        {
401            final String mask = String.valueOf(this.getConfiguredProperty(
402                this.getOwner() instanceof EnumerationFacade
403                    ? UMLMetafacadeProperties.ENUMERATION_LITERAL_NAME_MASK
404                    : UMLMetafacadeProperties.CLASSIFIER_PROPERTY_NAME_MASK ));
405
406            name = NameMasker.mask(super.handleGetName(), mask);
407            final boolean templating = Boolean.parseBoolean(String.valueOf(
408                this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING)));
409            // May be null during the validation process.
410            if (this.getType() != null)
411            {
412                final boolean arrayType = this.getType().isArrayType();
413                if (this.isPluralizeAttributeNames() && ((this.isMany() && templating) || arrayType))
414                {
415                    name = StringUtilsHelper.pluralize(name);
416                }
417            }
418        }
419
420        return name;
421    }
422
423    /**
424     * Indicates whether or not we should pluralize association end names.
425     *
426     * @return true/false
427     */
428    private boolean isPluralizeAttributeNames()
429    {
430        final Object value = this.getConfiguredProperty(UMLMetafacadeProperties.PLURALIZE_ATTRIBUTE_NAMES);
431        return value != null && Boolean.valueOf(String.valueOf(value)).booleanValue();
432    }
433
434    /**
435     * UML2 Only: Returns false always.
436     * @return false
437     * @see org.andromda.metafacades.uml.AttributeFacade#isLeaf()
438     */
439    @Override
440    public boolean handleIsLeaf()
441    {
442        return false;
443    }
444
445    /**
446     * @see org.andromda.metafacades.uml.AttributeFacade#isOrdered()
447     */
448    @Override
449    public boolean handleIsOrdered()
450    {
451        boolean ordered = false;
452
453        final OrderingKind ordering = metaObject.getOrdering();
454
455        // no ordering is 'unordered'
456        if (ordering != null)
457        {
458            ordered = ordering.equals(OrderingKindEnum.OK_ORDERED);
459        }
460
461        return ordered;
462    }
463
464    /**
465     * @return hasStereotype(UMLProfile.STEREOTYPE_UNIQUE)
466     * @see org.andromda.metafacades.uml.AttributeFacade#isUnique()
467     */
468    protected boolean handleIsUnique()
469    {
470        return this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE);
471    }
472
473    /**
474     * @see org.andromda.metafacades.uml.AttributeFacade#getGetterSetterTypeName()
475     */
476    @Override
477    public String handleGetGetterSetterTypeName()
478    {
479        String name = null;
480        // TODO: Change to check upperBound > 1, not isMany or Array/Collection types
481        if (this.isMany() && !this.getType().isArrayType() && !this.getType().isCollectionType())
482        {
483            final TypeMappings mappings = this.getLanguageMappings();
484            if (this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE))
485            {
486                name =
487                    isOrdered() ? mappings.getTo(UMLProfile.ORDERED_SET_TYPE_NAME) : mappings.getTo(
488                        UMLProfile.SET_TYPE_NAME);
489            }
490            else
491            {
492                name =
493                    isOrdered() ? mappings.getTo(UMLProfile.LIST_TYPE_NAME) : mappings.getTo(
494                        UMLProfile.COLLECTION_TYPE_NAME);
495            }
496
497            // set this attribute's type as a template parameter if required
498            if (BooleanUtils.toBoolean(
499                    ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING)))
500                    && this.getType() != null)
501            {
502                String type = this.getType().getFullyQualifiedName();
503                /*Collection<GeneralizableElementFacade> specializations = this.getType().getAllSpecializations();
504                if ((specializations != null && !specializations.isEmpty()))
505                {
506                    name += "<? extends " + type + '>';
507                }
508                else
509                {*/
510                    name += '<' + type + '>';
511                //}
512            }
513        }
514        if (name == null && this.getType() != null)
515        {
516            name = this.getType().getFullyQualifiedName();
517        }
518        return name;
519    }
520
521    /**
522     * Get the UML upper multiplicity
523     */
524    @Override
525    protected int handleGetUpper()
526    {
527        return this.getMultiplicityRangeUpper();
528     }
529
530    /**
531     * Get the UML lower multiplicity
532     */
533    @Override
534    protected int handleGetLower()
535    {
536        return this.getMultiplicityRangeLower();
537    }
538
539    /**
540     * @see org.andromda.metafacades.uml.AttributeFacade#isDerived()
541     */
542    @Override
543    protected boolean handleIsDerived()
544    {
545        // UML 1.4 does not have derived attribute.
546        return false;
547    }
548}