001package org.andromda.metafacades.uml14;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Collections;
006import java.util.HashMap;
007import java.util.Iterator;
008import java.util.Map;
009import java.util.regex.Pattern;
010import org.andromda.core.metafacade.MetafacadeBase;
011import org.andromda.core.metafacade.MetafacadeConstants;
012import org.andromda.core.metafacade.MetafacadeFactory;
013import org.andromda.metafacades.uml.BindingFacade;
014import org.andromda.metafacades.uml.ConstraintFacade;
015import org.andromda.metafacades.uml.DependencyFacade;
016import org.andromda.metafacades.uml.EnumerationLiteralFacade;
017import org.andromda.metafacades.uml.MetafacadeUtils;
018import org.andromda.metafacades.uml.ModelElementFacade;
019import org.andromda.metafacades.uml.NameMasker;
020import org.andromda.metafacades.uml.ParameterFacade;
021import org.andromda.metafacades.uml.RedefinableTemplateSignatureFacade;
022import org.andromda.metafacades.uml.StereotypeFacade;
023import org.andromda.metafacades.uml.TaggedValueFacade;
024import org.andromda.metafacades.uml.TemplateArgumentFacade;
025import org.andromda.metafacades.uml.TemplateParameterFacade;
026import org.andromda.metafacades.uml.TypeMappings;
027import org.andromda.metafacades.uml.UMLMetafacadeProperties;
028import org.andromda.metafacades.uml.UMLMetafacadeUtils;
029import org.andromda.metafacades.uml.UMLProfile;
030import org.andromda.translation.ocl.ExpressionKinds;
031import org.andromda.utils.StringUtilsHelper;
032import org.apache.commons.collections.CollectionUtils;
033import org.apache.commons.collections.Predicate;
034import org.apache.commons.lang.BooleanUtils;
035import org.apache.commons.lang.ObjectUtils;
036import org.apache.commons.lang.StringUtils;
037import org.apache.commons.lang.SystemUtils;
038import org.apache.log4j.Logger;
039import org.omg.uml.behavioralelements.statemachines.StateMachine;
040import org.omg.uml.foundation.core.Abstraction;
041import org.omg.uml.foundation.core.Classifier;
042import org.omg.uml.foundation.core.Comment;
043import org.omg.uml.foundation.core.Dependency;
044import org.omg.uml.foundation.core.Feature;
045import org.omg.uml.foundation.core.ModelElement;
046import org.omg.uml.foundation.core.Namespace;
047import org.omg.uml.foundation.datatypes.VisibilityKind;
048import org.omg.uml.foundation.datatypes.VisibilityKindEnum;
049import org.omg.uml.modelmanagement.UmlPackage;
050
051/**
052 * Metaclass facade implementation.
053 * @author Bob Fields
054 */
055public class ModelElementFacadeLogicImpl
056    extends ModelElementFacadeLogic
057{
058    private static final long serialVersionUID = 34L;
059    /**
060     * @param metaObject
061     * @param context
062     */
063    public ModelElementFacadeLogicImpl(
064        org.omg.uml.foundation.core.ModelElement metaObject,
065        String context)
066    {
067        super(metaObject, context);
068    }
069
070    /**
071     * The logger instance.
072     */
073    private static final Logger logger = Logger.getLogger(ModelElementFacadeLogicImpl.class);
074
075    /**
076     * @see org.andromda.metafacades.uml.ModelElementFacade#getTaggedValues()
077     */
078    @Override
079    protected Collection handleGetTaggedValues()
080    {
081        return metaObject.getTaggedValue();
082    }
083
084    /**
085     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName()
086     */
087    @Override
088    protected String handleGetPackageName()
089    {
090        final boolean modelName = false;
091        String packageName = UML14MetafacadeUtils.getPackageName(
092            this.metaObject,
093            this.getNamespaceScope(modelName),
094            modelName);
095        
096        //package names are treated differently so we have to apply the name mask here
097        //since there isn't a packageNameMask, we're using the modelElementNameMask
098        String nameMask = null;
099        try
100        {
101            nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.MODEL_ELEMENT_NAME_MASK));
102        }
103        catch (Exception ignore)
104        {
105            logger.warn("modelElementNameMask not found in " + this.toString() + " (getPackageName)");
106            nameMask = "none";
107        }
108
109        return NameMasker.mask(packageName, nameMask);
110        
111    }
112
113    /**
114     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName(boolean)
115     */
116    @Override
117    protected String handleGetPackageName(boolean modelName)
118    {
119        String packageName = this.handleGetPackageName();
120        if (modelName)
121        {
122            packageName =
123                StringUtils.replace(
124                    packageName,
125                    ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
126                    MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
127        }
128        return packageName;
129    }
130
131    /**
132     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedName(boolean)
133     */
134    @Override
135    protected String handleGetFullyQualifiedName(boolean modelName)
136    {
137        return handleGetBindedFullyQualifiedName(modelName, Collections.<BindingFacade>emptyList());
138    }
139
140    /**
141     * Gets the appropriate namespace property for retrieving the namespace scope operation (depending on the given
142     * <code>modelName</code> flag.
143     *
144     * @param modelName whether or not the scope operation for the model should be retrieved as opposed to the mapped
145     *                  scope operator.
146     * @return the scope operator.
147     */
148    private String getNamespaceScope(boolean modelName)
149    {
150        String namespaceScope = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
151        if (!modelName)
152        {
153            namespaceScope =
154                ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR));
155        }
156        return namespaceScope;
157    }
158
159    /**
160     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedName()
161     */
162    @Override
163    protected String handleGetFullyQualifiedName()
164    {
165        return this.handleGetFullyQualifiedName(false);
166    }
167
168    /**
169     * @see org.andromda.metafacades.uml.ModelElementFacade#findTaggedValues(String)
170     */
171    @Override
172    protected Collection<Object> handleFindTaggedValues(String name)
173    {
174        final Collection<Object> values = new ArrayList<Object>();
175
176        // only search a tagged value when it actually has a name
177        if (StringUtils.isNotBlank(name))
178        {
179            // trim the name, we don't want leading/trailing spaces
180            name = StringUtils.trimToEmpty(name);
181
182            for (final TaggedValueFacade taggedValue: this.getTaggedValues())
183            {
184                // does this name match the argument tagged value name ?
185                // Check both the UML14 format name @andromda.value and EMF Format andromda_whatever
186                String tagName = taggedValue.getName();
187                if (name.equals(tagName) || MetafacadeUtils.getEmfTaggedValue(name).equals(tagName)
188                    || MetafacadeUtils.getUml14TaggedValue(name).equals(tagName))
189                {
190                    for (final Object value : taggedValue.getValues())
191                    {
192                        // only process tagged values when they actually have a non-empty value
193                        if (value != null)
194                        {
195                            // if an enumeration literal is referenced we assume its name
196                            if (value instanceof EnumerationLiteralFacade)
197                            {
198                                values.add(((EnumerationLiteralFacade)value).getValue(true));
199                            }
200                            else if (value instanceof String)
201                            {
202                                // only add String when they are not blank
203                                String valueString = (String)value;
204                                if (StringUtils.isNotBlank(valueString))
205                                {
206                                    values.add(value);
207                                }
208                            }
209                            else
210                            {
211                                values.add(value);
212                            }
213                        }
214                    }
215                }
216            }
217        }
218        return values;
219    }
220
221    /**
222     * @see org.andromda.metafacades.uml.ModelElementFacade#findTaggedValue(String)
223     */
224    @Override
225    protected Object handleFindTaggedValue(String name)
226    {
227        Collection taggedValues = findTaggedValues(name);
228        return taggedValues.isEmpty() ? null : taggedValues.iterator().next();
229    }
230
231    /**
232     * @see org.andromda.metafacades.uml.ModelElementFacade#hasStereotype(String)
233     */
234    @Override
235    protected boolean handleHasStereotype(final String stereotypeName)
236    {
237        Collection<StereotypeFacade> stereotypes = this.getStereotypes();
238
239        boolean hasStereotype = StringUtils.isNotBlank(stereotypeName) && stereotypes != null &&
240            !stereotypes.isEmpty();
241
242        if (hasStereotype)
243        {
244            class StereotypeFilter
245                implements Predicate
246            {
247                public boolean evaluate(Object object)
248                {
249                    boolean valid;
250                    StereotypeFacade facade = (StereotypeFacade)object;
251                    String name = StringUtils.trimToEmpty(facade.getName());
252                    valid = stereotypeName.equals(name);
253                    while (!valid && facade != null)
254                    {
255                        facade = (StereotypeFacade)facade.getGeneralization();
256                        valid = facade != null && StringUtils.trimToEmpty(facade.getName()).equals(stereotypeName);
257                    }
258                    return valid;
259                }
260            }
261            hasStereotype = CollectionUtils.find(
262                    this.getStereotypes(),
263                    new StereotypeFilter()) != null;
264        }
265        return hasStereotype;
266    }
267
268    /**
269     * @see org.andromda.metafacades.uml.ModelElementFacade#getId()
270     */
271    @Override
272    protected String handleGetId()
273    {
274        return this.metaObject.refMofId();
275    }
276
277    /**
278     * @see org.andromda.metafacades.uml.ModelElementFacade#hasExactStereotype(String)
279     */
280    @Override
281    protected boolean handleHasExactStereotype(String stereotypeName)
282    {
283        return this.handleGetStereotypeNames().contains(StringUtils.trimToEmpty(stereotypeName));
284    }
285
286    /**
287     * @see org.andromda.metafacades.uml.ModelElementFacade#getVisibility()
288     */
289    @Override
290    protected String handleGetVisibility()
291    {
292        String visibility;
293
294        final VisibilityKind visibilityKind = metaObject.getVisibility();
295        if (visibilityKind == null || VisibilityKindEnum.VK_PRIVATE.equals(visibilityKind))
296        {
297            visibility = "private";
298        }
299        else if (VisibilityKindEnum.VK_PROTECTED.equals(visibilityKind))
300        {
301            visibility = "protected";
302        }
303        else if (VisibilityKindEnum.VK_PUBLIC.equals(visibilityKind))
304        {
305            visibility = "public";
306        }
307        else // VisibilityKindEnum.VK_PACKAGE
308        {
309            visibility = "package";
310        }
311
312        final TypeMappings languageMappings = this.getLanguageMappings();
313        if (languageMappings != null)
314        {
315            visibility = languageMappings.getTo(visibility);
316        }
317
318        return visibility;
319    }
320
321    /**
322     * @see org.andromda.metafacades.uml.ModelElementFacade#getStereotypeNames()
323     */
324    @Override
325    protected Collection<String> handleGetStereotypeNames()
326    {
327        Collection<String> stereotypeNames = new ArrayList<String>();
328
329        Collection<ModelElement> stereotypes = metaObject.getStereotype();
330        for (final ModelElement stereotype : stereotypes)
331        {
332            if (stereotype != null)
333            {
334                stereotypeNames.add(StringUtils.trimToEmpty(stereotype.getName()));
335            }
336        }
337        return stereotypeNames;
338    }
339
340    /**
341     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedNamePath()
342     */
343    @Override
344    protected String handleGetFullyQualifiedNamePath()
345    {
346        return StringUtils.replace(
347            this.handleGetFullyQualifiedName(),
348            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
349            "/");
350    }
351
352    /**
353     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackagePath()
354     */
355    @Override
356    protected String handleGetPackagePath()
357    {
358        return StringUtils.replace(
359            this.handleGetPackageName(),
360            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
361            "/");
362    }
363
364    /**
365     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String)
366     */
367    @Override
368    protected String handleGetDocumentation(String indent)
369    {
370        return getDocumentation(
371            indent,
372            100 - indent.length());
373    }
374
375    /**
376     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String, int)
377     */
378    @Override
379    protected String handleGetDocumentation(
380        String indent,
381        int lineLength)
382    {
383        return getDocumentation(
384            indent,
385            lineLength,
386            true);
387    }
388
389    /**
390     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String, int, boolean)
391     */
392    @Override
393    protected String handleGetDocumentation(
394        String indent,
395        int lineLength,
396        boolean htmlStyle)
397    {
398        final StringBuilder documentation = new StringBuilder();
399
400        if (lineLength < 1)
401        {
402            lineLength = Integer.MAX_VALUE;
403        }
404
405        final Collection<Comment> comments = this.metaObject.getComment();
406        if (comments != null && !comments.isEmpty())
407        {
408            for (Comment comment : comments)
409            {
410                String commentString = StringUtils.trimToEmpty(comment.getBody());
411
412                // if there isn't anything in the body, try the name
413                if (StringUtils.isBlank(commentString))
414                {
415                    commentString = StringUtils.trimToEmpty(comment.getName());
416                }
417                documentation.append(StringUtils.trimToEmpty(commentString));
418                documentation.append(SystemUtils.LINE_SEPARATOR);
419            }
420        }
421
422        // if there still isn't anything, try a tagged value
423        if (StringUtils.isBlank(documentation.toString()))
424        {
425            documentation.append(
426                StringUtils.trimToEmpty((String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_DOCUMENTATION)));
427        }
428        // Add toDoTag if doc is empty: make modelers do the documentation.
429        if (StringUtils.isEmpty(documentation.toString()))
430        {
431            if (Boolean.valueOf((String)this.getConfiguredProperty(UMLMetafacadeProperties.TODO_FOR_MISSING_DOCUMENTATION)))
432            {
433                String todoTag = (String)this.getConfiguredProperty(UMLMetafacadeProperties.TODO_TAG);
434                documentation.append(todoTag).append(": Model Documentation for " + this.handleGetFullyQualifiedName());
435            }
436        }
437
438        // Only add paragraph tags if doc is not empty
439        String rtn = StringUtilsHelper.format(
440            StringUtils.trimToEmpty(documentation.toString()),
441            indent,
442            lineLength,
443            htmlStyle);
444
445        return rtn;
446    }
447
448    /**
449     * If documentation is present, i.e. to add toDoTag or skip a line if not
450     * @return true is documentation comment or Documentation stereotype is present
451     * @see org.andromda.metafacades.uml.ModelElementFacade#isDocumentationPresent()
452     */
453    @Override
454    protected boolean handleIsDocumentationPresent()
455    {
456        boolean rtn = false;
457        final Collection<Comment> comments = this.metaObject.getComment();
458        if (comments != null && !comments.isEmpty())
459        {
460            for (Comment comment : comments)
461            {
462                // if there isn't anything in the body, try the name
463                if (StringUtils.isNotBlank(comment.getBody())|| StringUtils.isNotBlank(comment.getName()))
464                {
465                    rtn = true;
466                    break;
467                }
468            }
469        }
470
471        if (!rtn && StringUtils.isNotBlank((String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_DOCUMENTATION)))
472        {
473            rtn = true;
474        }
475
476        return rtn;
477    }
478
479    /**
480     * @return NOT IMPLEMENTED: UML2 only
481     * @see org.andromda.metafacades.uml.ModelElementFacade#getLabel()
482     */
483    protected String handleGetLabel()
484    {
485        return null;
486    }
487
488    /**
489     * @return NOT IMPLEMENTED: UML2 only
490     * @see org.andromda.metafacades.uml.ModelElementFacade#getKeywords()
491     */
492    protected Collection<String> handleGetKeywords()
493    {
494        return new ArrayList<String>();
495    }
496
497    /*
498     * @return NOT IMPLEMENTED: UML2 only
499     * @see org.andromda.metafacades.uml.ModelElementFacade#getModelNamespace()
500    protected ModelElementFacade handleGetModelNamespace()
501    {
502        return null;
503    }
504     */
505
506    /*
507     * @return NOT IMPLEMENTED: UML2 only
508     * @see org.andromda.metafacades.uml.ModelElementFacade#getModelNamespace()
509    protected ClassifierFacade handleGetOwner()
510    {
511        return null;
512    }
513     */
514
515    /**
516     * @return NOT IMPLEMENTED: UML2 only
517     * @see org.andromda.metafacades.uml.ModelElementFacade#getQualifiedName()
518     */
519    protected String handleGetQualifiedName()
520    {
521        return null;
522    }
523
524    /**
525     * @param keyword hasExactStereotype(keyword)
526     * @return hasExactStereotype(keyword)
527     * @see org.andromda.metafacades.uml.ModelElementFacade#getQualifiedName()
528     */
529    protected boolean handleHasKeyword(String keyword)
530    {
531        return this.handleHasExactStereotype(keyword);
532    }
533
534    /**
535     * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
536     */
537    @Override
538    protected String handleGetName()
539    {
540        String name=metaObject.getName();
541        
542        if(name != null)
543        {
544            String nameMask = null;
545            try
546            {
547                nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.MODEL_ELEMENT_NAME_MASK));
548            }
549            catch (Exception ignore)
550            {
551                logger.warn("modelElementNameMask not found in " + this.toString());
552                nameMask = "none";
553            }
554            name = NameMasker.mask(name, nameMask);
555        }
556
557        return name;
558    }
559
560    /**
561     * Gets the array suffix from the configured metafacade properties.
562     *
563     * @return the array suffix.
564     */
565    protected String getArraySuffix()
566    {
567        return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.ARRAY_NAME_SUFFIX));
568    }
569
570    /**
571     * @see org.andromda.metafacades.uml.ModelElementFacade#getLanguageMappings()
572     */
573    @Override
574    protected TypeMappings handleGetLanguageMappings()
575    {
576        final String propertyName = UMLMetafacadeProperties.LANGUAGE_MAPPINGS_URI;
577        Object property = this.getConfiguredProperty(propertyName);
578        TypeMappings mappings = null;
579        String uri;
580        if (property instanceof String)
581        {
582            uri = (String)property;
583            try
584            {
585                mappings = TypeMappings.getInstance(uri);
586                mappings.setArraySuffix(this.getArraySuffix());
587                this.setProperty(
588                    propertyName,
589                    mappings);
590            }
591            catch (Throwable throwable)
592            {
593                final String message = "Error getting '" + propertyName + "' --> '" + uri + '\'';
594                logger.error(
595                    message,
596                    throwable);
597
598                // don't throw the exception
599            }
600        }
601        else
602        {
603            mappings = (TypeMappings)property;
604        }
605        return mappings;
606    }
607
608    /*
609     * UML2 Only
610     * @see org.andromda.metafacades.uml.ModelElementFacade#getOwnedMembers()
611    protected Collection<ModelElementFacade> handleGetOwnedElements()
612    {
613        return new ArrayList<ModelElementFacade>();
614    }
615     */
616
617    /**
618     * @return Feature owner, or namespace
619     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackage()
620     */
621    //@Override
622    protected Object handleGetOwner()
623    {
624        if (this.metaObject instanceof Feature)
625        {
626            Feature feature = (Feature)this.metaObject;
627            return feature.getOwner();
628        }
629        return metaObject.getNamespace();
630    }
631
632    /**
633     * @return Collection of Features or Namespace elements
634     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackage()
635     */
636    //@Override
637    protected Collection handleGetOwnedElements()
638    {
639        if (this.metaObject instanceof Classifier)
640        {
641            Classifier classifier = (Classifier)this.metaObject;
642            return classifier.getFeature();
643        }
644        if (this.metaObject instanceof Namespace)
645        {
646            Namespace namespace = (Namespace)this.metaObject;
647            return namespace.getOwnedElement();
648        }
649        return null;
650    }
651
652    /**
653     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackage()
654     */
655    @Override
656    protected Namespace handleGetPackage()
657    {
658        return metaObject.getNamespace();
659    }
660
661    /**
662     * @see org.andromda.metafacades.uml.ModelElementFacade#getRootPackage()
663     */
664    @Override
665    protected UmlPackage handleGetRootPackage()
666    {
667        return UML14MetafacadeUtils.getRootPackage();
668    }
669
670    /**
671     * @see org.andromda.metafacades.uml.ModelElementFacade#getSourceDependencies()
672     */
673    @Override
674    protected Collection<DependencyFacade> handleGetSourceDependencies()
675    {
676        Collection<DependencyFacade> dependencies = new ArrayList();
677        Collection<DependencyFacade> clientDependencies =
678            UML14MetafacadeUtils.getCorePackage().getAClientClientDependency().getClientDependency(this.metaObject);
679        if (clientDependencies != null)
680        {
681            dependencies.addAll(clientDependencies);
682        }
683        CollectionUtils.filter(
684            dependencies,
685            new Predicate()
686            {
687                public boolean evaluate(Object object)
688                {
689                    return (object instanceof Dependency) && !(object instanceof Abstraction);
690                }
691            });
692        return dependencies;
693    }
694
695    /**
696     * @see org.andromda.metafacades.uml.ModelElementFacade#getTargetDependencies()
697     */
698    @Override
699    protected Collection<DependencyFacade> handleGetTargetDependencies()
700    {
701        Collection<DependencyFacade> dependencies = new ArrayList();
702        Collection<DependencyFacade> supplierDependencies =
703            UML14MetafacadeUtils.getCorePackage().getASupplierSupplierDependency().getSupplierDependency(
704                this.metaObject);
705        if (supplierDependencies != null)
706        {
707            dependencies.addAll(supplierDependencies);
708        }
709        CollectionUtils.filter(
710            dependencies,
711            new Predicate()
712            {
713                public boolean evaluate(Object object)
714                {
715                    return (object instanceof Dependency) && !(object instanceof Abstraction);
716                }
717            });
718        return dependencies;
719    }
720
721    /**
722     * @see org.andromda.metafacades.uml.ModelElementFacade#getStereotypes()
723     */
724    @Override
725    protected Collection<StereotypeFacade> handleGetStereotypes()
726    {
727        return this.metaObject.getStereotype();
728    }
729
730    /**
731     * @see org.andromda.metafacades.uml.ModelElementFacade#getModel()
732     */
733    @Override
734    protected Object handleGetModel()
735    {
736        return MetafacadeFactory.getInstance().getModel().getModel();
737    }
738
739    /**
740     * @see org.andromda.metafacades.uml.ModelElementFacade#getConstraints()
741     */
742    @Override
743    protected Collection<ConstraintFacade> handleGetConstraints()
744    {
745        return this.metaObject.getConstraint();
746    }
747
748    /**
749     * @see org.andromda.metafacades.uml.ModelElementFacade#getConstraints(String)
750     */
751    @Override
752    protected Collection<ConstraintFacade> handleGetConstraints(final String kind)
753    {
754        return CollectionUtils.select(
755            getConstraints(),
756            new Predicate()
757            {
758                public boolean evaluate(Object object)
759                {
760                    if (object instanceof ConstraintFacade)
761                    {
762                        ConstraintFacade constraint = (ConstraintFacade)object;
763                        return ((ExpressionKinds.BODY.equals(kind) && constraint.isBodyExpression()) ||
764                        (ExpressionKinds.DEF.equals(kind) && constraint.isDefinition()) ||
765                        (ExpressionKinds.INV.equals(kind) && constraint.isInvariant()) ||
766                        (ExpressionKinds.PRE.equals(kind) && constraint.isPreCondition()) ||
767                        (ExpressionKinds.POST.equals(kind) && constraint.isPostCondition()));
768                    }
769                    return false;
770                }
771            });
772    }
773
774    /**
775     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraint(String, String)
776     */
777    @Override
778    protected String handleTranslateConstraint(
779        final String name,
780        String translation)
781    {
782        String translatedExpression = "";
783        ConstraintFacade constraint =
784            (ConstraintFacade)CollectionUtils.find(
785                this.getConstraints(),
786                new Predicate()
787                {
788                    public boolean evaluate(Object object)
789                    {
790                        ConstraintFacade constraint = (ConstraintFacade)object;
791                        return StringUtils.trimToEmpty(constraint.getName()).equals(StringUtils.trimToEmpty(name));
792                    }
793                });
794
795        if (constraint != null)
796        {
797            translatedExpression = constraint.getTranslation(translation);
798        }
799        return translatedExpression;
800    }
801
802    /**
803     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraints(String)
804     */
805    @Override
806    protected String[] handleTranslateConstraints(String translation)
807    {
808        return this.translateConstraints(
809            this.getConstraints(),
810            translation);
811    }
812
813    /**
814     * Private helper that translates all the expressions contained in the <code>constraints</code>, and returns an
815     * array of the translated expressions.
816     *
817     * @param constraints the constraints to translate
818     * @param translation the translation to translate <code>to</code>.
819     * @return String[] the translated expressions, or null if no constraints were found
820     */
821    private String[] translateConstraints(
822        Collection<ConstraintFacade> constraints,
823        String translation)
824    {
825        String[] translatedExpressions = null;
826        if (constraints != null && !constraints.isEmpty())
827        {
828            translatedExpressions = new String[constraints.size()];
829            Iterator<ConstraintFacade> constraintIt = constraints.iterator();
830            for (int ctr = 0; constraintIt.hasNext(); ctr++)
831            {
832                ConstraintFacade constraint = constraintIt.next();
833                translatedExpressions[ctr] = constraint.getTranslation(translation);
834            }
835        }
836        return translatedExpressions;
837    }
838
839    /**
840     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraints(String, String)
841     */
842    @Override
843    protected String[] handleTranslateConstraints(
844        final String kind,
845        String translation)
846    {
847        Collection<ConstraintFacade> constraints = this.getConstraints();
848        CollectionUtils.filter(
849            constraints,
850            new Predicate()
851            {
852                public boolean evaluate(Object object)
853                {
854                    ConstraintFacade constraint = (ConstraintFacade)object;
855                    return UMLMetafacadeUtils.isConstraintKind(
856                        constraint.getBody(),
857                        kind);
858                }
859            });
860        return this.translateConstraints(
861            constraints,
862            translation);
863    }
864
865    /**
866     * @see org.andromda.metafacades.uml.ModelElementFacade#getStateMachineContext()
867     */
868    @Override
869    protected StateMachine handleGetStateMachineContext()
870    {
871        StateMachine machineContext = null;
872
873        final Collection<StateMachine> machines = UML14MetafacadeUtils.getModel().getStateMachines().getStateMachine().refAllOfType();
874        for (final StateMachine machine : machines)
875        {
876            final ModelElement contextElement = machine.getContext();
877            if (metaObject.equals(contextElement))
878            {
879                machineContext = machine;
880            }
881        }
882
883        return machineContext;
884    }
885
886    /**
887     * @see org.andromda.metafacades.uml.ModelElementFacade#getTemplateParameters()
888     */
889    @Override
890    protected Collection handleGetTemplateParameters()
891    {
892        return metaObject.getTemplateParameter();
893    }
894
895    /**
896     * @see org.andromda.core.metafacade.MetafacadeBase#getValidationName()
897     */
898    public String getValidationName()
899    {
900        final StringBuilder validationName = new StringBuilder();
901        final String seperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
902        for (ModelElement namespace = metaObject.getNamespace(); namespace != null;
903            namespace = namespace.getNamespace())
904        {
905            if (validationName.length() == 0)
906            {
907                validationName.append(namespace.getName());
908            }
909            else
910            {
911                validationName.insert(
912                    0,
913                    seperator);
914                validationName.insert(
915                    0,
916                    namespace.getName());
917            }
918        }
919        if (validationName.length() > 0)
920        {
921            validationName.append(seperator);
922        }
923        if (StringUtils.isNotBlank(this.handleGetName()))
924        {
925            validationName.append(this.handleGetName());
926        }
927        else
928        {
929            validationName.append(this.getConfiguredProperty(UMLMetafacadeProperties.UNDEFINED_NAME));
930        }
931        return validationName.toString();
932    }
933
934    /**
935     * @see org.andromda.metafacades.uml.ModelElementFacade#isConstraintsPresent()
936     */
937    @Override
938    protected boolean handleIsConstraintsPresent()
939    {
940        return this.getConstraints() != null && !this.getConstraints().isEmpty();
941    }
942
943    /**
944     * @see org.andromda.metafacades.uml.ModelElementFacade#isBindingDependenciesPresent()
945     */
946    @Override
947    protected boolean handleIsBindingDependenciesPresent()
948    {
949        Collection<DependencyFacade> dependencies = this.getSourceDependencies();
950        CollectionUtils.filter(
951            dependencies,
952            new Predicate()
953            {
954                public boolean evaluate(Object object)
955                {
956                    return object instanceof BindingFacade;
957                }
958            });
959        return !dependencies.isEmpty();
960    }
961
962    /**
963     * @see org.andromda.metafacades.uml.ModelElementFacade#isReservedWord()
964     */
965    @Override
966    protected boolean handleIsReservedWord()
967    {
968        return UMLMetafacadeUtils.isReservedWord(metaObject.getName());
969    }
970
971    /**
972     * @see org.andromda.metafacades.uml.ModelElementFacade#isTemplateParametersPresent()
973     */
974    @Override
975    protected boolean handleIsTemplateParametersPresent()
976    {
977        final Collection<TemplateParameterFacade> params = this.getTemplateParameters();
978        return params != null && !params.isEmpty();
979    }
980
981    /**
982     * @see org.andromda.metafacades.uml.ModelElementFacade#copyTaggedValues(org.andromda.metafacades.uml.ModelElementFacade)
983     */
984    @Override
985    protected void handleCopyTaggedValues(ModelElementFacade element)
986    {
987        org.omg.uml.foundation.core.ModelElement elementMetaObject;
988        if (element instanceof MetafacadeBase)
989        {
990            final MetafacadeBase metafacade = (MetafacadeBase)element;
991            final Object metaObject = metafacade.getMetaObject();
992            if (metaObject instanceof ModelElement)
993            {
994                elementMetaObject = (ModelElement)metaObject;
995                this.metaObject.getTaggedValue().addAll(elementMetaObject.getTaggedValue());
996            }
997        }
998    }
999
1000    /**
1001     * @see org.andromda.metafacades.uml.ModelElementFacade#getTemplateParameter(String)
1002     */
1003    @Override
1004    protected TemplateParameterFacade handleGetTemplateParameter(String parameterName)
1005    {
1006        TemplateParameterFacade templateParameter = null;
1007        if (StringUtils.isNotBlank(parameterName))
1008        {
1009            parameterName = StringUtils.trimToEmpty(parameterName);
1010            final Collection<TemplateParameterFacade> parameters = this.getTemplateParameters();
1011            if (parameters != null && !parameters.isEmpty())
1012            {
1013                for (final TemplateParameterFacade currentTemplateParameter : parameters)
1014                {
1015                    if (currentTemplateParameter.getParameter() != null)
1016                    {
1017                        final ModelElementFacade parameter = currentTemplateParameter.getParameter();
1018
1019                        // there should not be two template parameters with the same parameter name, but nothing
1020                        // prevents the model from allowing that.  So return the first instance if found.
1021                        if (parameterName.equals(parameter.getName()))
1022                        {
1023                            templateParameter = currentTemplateParameter;
1024                            break;
1025                        }
1026                    }
1027                }
1028            }
1029        }
1030
1031        return templateParameter;
1032    }
1033
1034    /**
1035     * {@inheritDoc}
1036     */
1037    @Override
1038    protected String handleGetBindedFullyQualifiedName(ModelElementFacade bindedElement)
1039    {
1040        // This cast is not safe yet, but will be as soon as the filtering will be done
1041        @SuppressWarnings("unchecked")
1042        final Collection<BindingFacade> bindingFacades = new ArrayList(bindedElement.getSourceDependencies());
1043        CollectionUtils.filter(
1044            bindingFacades,
1045            new Predicate()
1046            {
1047                public boolean evaluate(Object object)
1048                {
1049                    return object instanceof BindingFacade;
1050                }
1051            });
1052        return handleGetBindedFullyQualifiedName(false, bindingFacades);
1053    }
1054
1055    /**
1056     * <p>
1057     * Returns the fully qualified name of the model element. The fully
1058     * qualified name includes complete package qualified name of the
1059     * underlying model element.  If modelName is true, then the
1060     * original name of the model element (the name contained within
1061     * the model) will be the name returned, otherwise a name from a
1062     * language mapping will be returned. Moreover use the given collection
1063     * of {@link BindingFacade} to bind templates parameters to their actual
1064     * type.
1065     * </p>
1066     * @param modelName boolean
1067     * @param bindingFacades Collection
1068     * @return String
1069     */
1070    private String handleGetBindedFullyQualifiedName(boolean modelName, Collection<BindingFacade> bindingFacades)
1071    {
1072        String fullName = StringUtils.trimToEmpty(this.handleGetName());
1073        final String packageName = this.handleGetPackageName(true);
1074        final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
1075        if (StringUtils.isNotBlank(packageName))
1076        {
1077            fullName = packageName + metafacadeNamespaceScopeOperator + fullName;
1078        }
1079        if (!modelName)
1080        {
1081            final TypeMappings languageMappings = this.getLanguageMappings();
1082            if (languageMappings != null)
1083            {
1084                fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName));
1085
1086                // now replace the metafacade scope operators
1087                // with the mapped scope operators
1088                final String namespaceScopeOperator =
1089                    String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR));
1090                fullName = StringUtils.replace(
1091                        fullName,
1092                        metafacadeNamespaceScopeOperator,
1093                        namespaceScopeOperator);
1094            }
1095        }
1096
1097        if (this.isTemplateParametersPresent() &&
1098            BooleanUtils.toBoolean(
1099                ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))))
1100        {
1101            // Retrieve all template parameters
1102            final Collection<TemplateParameterFacade> templateParameters = this.getTemplateParameters();
1103
1104            // Construct a map of the TemplateParameterFacade to replace
1105            Map<TemplateParameterFacade, ModelElementFacade> bindedParameters = new HashMap<TemplateParameterFacade, ModelElementFacade>();
1106            for(BindingFacade bindingFacade : bindingFacades)
1107            {
1108                ModelElementFacade targetElement = bindingFacade.getTargetElement();
1109                if(targetElement instanceof RedefinableTemplateSignatureFacade)
1110                {
1111                    targetElement = ((RedefinableTemplateSignatureFacade) targetElement).getClassifier();
1112                }
1113                if(this.equals(targetElement))
1114                {
1115                    final Collection<TemplateArgumentFacade> arguments = bindingFacade.getArguments();
1116                    if(arguments.size() != getTemplateParameters().size())
1117                    {
1118                        throw new IllegalStateException("The size of the arguments of the BindingFacace must be equals to the size of the TemplateParameter collection of this element.");
1119                    }
1120
1121                    Iterator<TemplateParameterFacade> templateParametersIterator = templateParameters.iterator();
1122                    Iterator<TemplateArgumentFacade> templateArgumentsIterator = arguments.iterator();
1123
1124                    while(templateParametersIterator.hasNext())
1125                    {
1126                        final TemplateParameterFacade templateParameter = templateParametersIterator.next();
1127                        final TemplateArgumentFacade templateArgument =  templateArgumentsIterator.next();
1128                        bindedParameters.put(templateParameter, templateArgument.getElement());
1129                    }
1130                }
1131            }
1132            if(bindedParameters.isEmpty())
1133            {
1134                for(TemplateParameterFacade templateParameterFacade : templateParameters)
1135                {
1136                    bindedParameters.put(templateParameterFacade, templateParameterFacade.getParameter());
1137                }
1138            }
1139
1140            // we'll be constructing the parameter list in this buffer
1141            // add the name we've constructed so far
1142            final StringBuilder buffer = new StringBuilder(fullName + '<');
1143
1144            // loop over the parameters, we are so to have at least one (see
1145            // outer condition)
1146            for (final Iterator<TemplateParameterFacade> parameterIterator = templateParameters.iterator(); parameterIterator.hasNext();)
1147            {
1148                final ModelElementFacade modelElement = bindedParameters.get(parameterIterator.next());
1149
1150                // TODO: UML14 returns ParameterFacade, UML2 returns ModelElementFacade, so types are wrong from fullyQualifiedName
1151                // Mapping from UML2 should return ParameterFacade, with a getType method.
1152                // Add TemplateParameterFacade.getType method - need to access this in vsl templates.
1153                if (modelElement instanceof ParameterFacade)
1154                {
1155                    buffer.append(((ParameterFacade)modelElement).getType().getFullyQualifiedName());
1156                }
1157                else
1158                {
1159                    buffer.append(modelElement.getFullyQualifiedName());
1160                }
1161
1162                if (parameterIterator.hasNext())
1163                {
1164                    buffer.append(", ");
1165                }
1166            }
1167
1168            // we're finished listing the parameters
1169            buffer.append('>');
1170
1171            // we have constructed the full name in the buffer
1172            fullName = buffer.toString();
1173        }
1174
1175        return fullName;
1176    }
1177
1178    // Valid identifier starts with alphanum or _$ and contains only alphanum or _$ or digits
1179    private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_$][a-zA-Z\\d_$]*");
1180    /**
1181     * Return true if name matches the pattern [a-zA-Z_$][a-zA-Z\\d_$]*
1182     * @see org.andromda.metafacades.uml14.ModelElementFacadeLogic#handleIsValidIdentifierName()
1183     */
1184    @Override
1185    protected boolean handleIsValidIdentifierName()
1186    {
1187        final String name = this.handleGetName();
1188        return IDENTIFIER_PATTERN.matcher(name).matches();
1189    }
1190}