001package org.andromda.metafacades.uml14;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Iterator;
006import java.util.LinkedHashMap;
007import java.util.List;
008import java.util.Map;
009import org.andromda.core.common.ExceptionUtils;
010import org.andromda.core.metafacade.MetafacadeConstants;
011import org.andromda.core.metafacade.MetafacadeFactory;
012import org.andromda.metafacades.uml.ActivityGraphFacade;
013import org.andromda.metafacades.uml.ClassifierFacade;
014import org.andromda.metafacades.uml.EventFacade;
015import org.andromda.metafacades.uml.MetafacadeUtils;
016import org.andromda.metafacades.uml.ModelElementFacade;
017import org.andromda.metafacades.uml.ParameterFacade;
018import org.andromda.metafacades.uml.UMLProfile;
019import org.andromda.metafacades.uml.UseCaseFacade;
020import org.apache.commons.collections.CollectionUtils;
021import org.apache.commons.collections.Predicate;
022import org.apache.commons.lang.StringUtils;
023import org.omg.uml.behavioralelements.activitygraphs.ActivityGraph;
024import org.omg.uml.behavioralelements.statemachines.Event;
025import org.omg.uml.behavioralelements.statemachines.FinalState;
026import org.omg.uml.behavioralelements.usecases.UseCase;
027import org.omg.uml.foundation.core.Attribute;
028import org.omg.uml.foundation.core.Classifier;
029import org.omg.uml.foundation.core.CorePackage;
030import org.omg.uml.foundation.core.ModelElement;
031import org.omg.uml.foundation.core.Parameter;
032import org.omg.uml.foundation.core.Stereotype;
033import org.omg.uml.foundation.core.TaggedValue;
034import org.omg.uml.foundation.core.UmlClass;
035import org.omg.uml.foundation.datatypes.VisibilityKind;
036import org.omg.uml.foundation.datatypes.VisibilityKindEnum;
037import org.omg.uml.modelmanagement.Model;
038import org.omg.uml.modelmanagement.UmlPackage;
039
040/**
041 * Utilities for dealing with UML 1.4 metafacades
042 *
043 * @author Chad Brandon
044 * @author Bob Fields
045 */
046public class UML14MetafacadeUtils
047{
048    /**
049     * Finds a given model element in the model having the specified
050     * <code>fullyQualifiedName</code>. If the model element can <strong>NOT
051     * </strong> be found, <code>null</code> will be returned instead.
052     *
053     * @param fullyQualifiedName the fully qualified name of the element to
054     *        search for.
055     * @param separator the PSM separator used for qualifying the name (example
056     *        ".").
057     * @param modelName a flag indicating whether or not a search shall be performed using
058     *        the fully qualified model name or fully qualified PSM name.
059     * @return the found model element
060     */
061    static Object findByFullyQualifiedName(final String fullyQualifiedName, final String separator, final boolean modelName)
062    {
063        Object modelElement;
064        Collection elements = ((org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel()).getCore()
065                .getModelElement()
066                .refAllOfType();
067        modelElement = CollectionUtils.find(elements, new Predicate()
068        {
069            public boolean evaluate(Object object)
070            {
071                ModelElement element = (ModelElement)object;
072                StringBuilder fullName = new StringBuilder(getPackageName(element, separator, modelName));
073                String name = element.getName();
074                if (StringUtils.isNotBlank(name))
075                {
076                    String namespaceSeparator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
077                    if (!modelName)
078                    {
079                        namespaceSeparator = separator;
080                    }
081                    fullName.append(namespaceSeparator);
082                    fullName.append(name);
083                }
084                return fullName.toString().equals(fullyQualifiedName);
085            }
086        });
087        return modelElement;
088    }
089
090    private static String empty = "";
091    /**
092     * Constructs the package name for the given <code>metaObject</code>, separating the package name by the given
093     * <code>separator</code>.
094     *
095     * @param metaObject the Model Element
096     * @param separator the PSM namespace separator
097     * @param modelName true/false on whether or not to get the model package name instead
098     *        of the PSM package name.
099     * @return the package name.
100     */
101    static String getPackageName(ModelElement metaObject, String separator, boolean modelName)
102    {
103        String packageName = empty;
104        for (ModelElement namespace = metaObject.getNamespace(); (namespace instanceof UmlPackage) &&
105                !(namespace instanceof Model); namespace = namespace.getNamespace())
106        {
107            packageName = packageName.equals(empty) ? namespace.getName() : namespace.getName() + separator + packageName;
108        }
109        if (modelName && StringUtils.isNotBlank(packageName))
110        {
111            packageName = StringUtils.replace(packageName, separator, MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
112        }
113        return packageName;
114    }
115
116    /**
117     * Basically just checks to make sure the <code>model</code> is of type <code>org.omg.uml.UmlPackage</code> and
118     * retrieves the <code>CorePackage</code> from it.
119     *
120     * @return the <code>model</code> as a <code>org.omg.uml.UmlPackage</code>
121     */
122    static CorePackage getCorePackage()
123    {
124        return ((org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel()).getCore();
125    }
126
127    /**
128     * Finds and returns the first model element having the given <code>name</code> in the <code>modelPackage</code>,
129     * returns <code>null</code> if not found.
130     *
131     * @param name the name to find.
132     * @return the found model element.
133     */
134    static Object findByName(final String name)
135    {
136        Object modelElement = null;
137        if (StringUtils.isNotBlank(name))
138        {
139            modelElement = CollectionUtils.find(getModel().getCore().getModelElement().refAllOfType(), new Predicate()
140            {
141                public boolean evaluate(Object object)
142                {
143                    return StringUtils.trimToEmpty(((ModelElement)object).getName()).equals(name);
144                }
145            });
146        }
147        return modelElement;
148    }
149
150    /**
151     * Gets the root package in the model.
152     *
153     * @return the root package as a UmlPackage.
154     */
155    static UmlPackage getRootPackage()
156    {
157        Object result = null;
158        Collection rootPackages = UML14MetafacadeUtils.getModel().getModelManagement().getModel().refAllOfType();
159        for (Object rootPackage : rootPackages)
160        {
161            // get the first package that's a ModelElement instance
162            // Note: UML2 allows top level ModelElement to be a Package.
163            if (rootPackage instanceof ModelElement)
164            {
165                result = rootPackage;
166                break;
167            }
168        }
169        return (UmlPackage)result;
170    }
171
172    /**
173     * Returns the entire model.
174     *
175     * @return org.omg.uml.UmlPackage model instance.
176     */
177    static org.omg.uml.UmlPackage getModel()
178    {
179        return (org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel();
180    }
181
182    /**
183     * Gets the correct meta model visibility kind for the given <code>visibility</code> string.
184     *
185     * @param visibility the visibility to retrieve.
186     * @return the VisibilityKind
187     */
188    static VisibilityKind getVisibilityKind(String visibility)
189    {
190        VisibilityKind visibilityKind = null;
191        visibility = StringUtils.trimToEmpty(visibility);
192        if ("public".equals(visibility))
193        {
194            visibilityKind = VisibilityKindEnum.VK_PUBLIC;
195        }
196        else if ("private".equals(visibility))
197        {
198            visibilityKind = VisibilityKindEnum.VK_PRIVATE;
199        }
200        else if (StringUtils.isEmpty(visibility))
201        {
202            visibilityKind = VisibilityKindEnum.VK_PACKAGE;
203        }
204        else if ("protected".equals(visibility))
205        {
206            visibilityKind = VisibilityKindEnum.VK_PROTECTED;
207        }
208        return visibilityKind;
209    }
210
211    /**
212     * Creates an attribute having the give <code>name</code> and the type
213     * having the given <code>fullyQualifiedTypeName</code>, with the
214     * specified visibility, if no type can be found with the given name, no
215     * type is set.
216     *
217     * @param name the new name
218     * @param fullyQualifiedTypeName the name of the fully qualified type
219     * @param visibility the visibility name
220     * @param separator the separator used for qualifying the name.
221     * @return the new Attribute.
222     */
223    static Attribute createAttribute(String name, String fullyQualifiedTypeName, String visibility, String separator)
224    {
225        Attribute attribute = UML14MetafacadeUtils.getCorePackage().getAttribute().createAttribute();
226        attribute.setName(name);
227        attribute.setVisibility(UML14MetafacadeUtils.getVisibilityKind(visibility));
228        Object type = UML14MetafacadeUtils.findByFullyQualifiedName(fullyQualifiedTypeName, separator, false);
229        if (type != null && Classifier.class.isAssignableFrom(type.getClass()))
230        {
231            attribute.setType((Classifier)type);
232        }
233        return attribute;
234    }
235
236    /**
237     * Indicates whether or not the attribute exists on the given </code>classifier</code>.
238     *
239     * @param classifier the classifier to check
240     * @param name the name of the attribute
241     * @return true/false
242     */
243    static boolean attributeExists(Object classifier, String name)
244    {
245        boolean exists = false;
246        if (Classifier.class.isAssignableFrom(classifier.getClass()))
247        {
248            List features = ((Classifier)classifier).getFeature();
249            if (features != null && !features.isEmpty())
250            {
251                for (final Iterator featureIterator = features.iterator(); featureIterator.hasNext();)
252                {
253                    Object feature = featureIterator.next();
254                    if (feature != null && Attribute.class.isAssignableFrom(feature.getClass()))
255                    {
256                        exists = StringUtils.trimToEmpty(((Attribute)feature).getName()).equals(name);
257                        if(exists)
258                        {
259                            break;
260                        }
261                    }
262                }
263            }
264        }
265        return exists;
266    }
267
268    /**
269     * Finds or creates a stereotype with the given name. If the stereotype isn't found, it will be created.
270     *
271     * @param name the name of the stereotype.
272     * @return the new Stereotype.
273     */
274    static Stereotype findOrCreateStereotype(String name)
275    {
276        Object stereotype = UML14MetafacadeUtils.findByName(name);
277        if (stereotype == null || !Stereotype.class.isAssignableFrom(stereotype.getClass()))
278        {
279            stereotype = UML14MetafacadeUtils.getCorePackage().getStereotype().createStereotype();
280            ((Stereotype)stereotype).setName(name);
281        }
282        return (Stereotype)stereotype;
283    }
284
285    /**
286     * Returns the first use-case it can find with the given name.
287     * @param name
288     * @return findFirstUseCaseWithNameAndStereotype(name, null)
289     */
290    static UseCase findFirstUseCaseWithName(String name)
291    {
292        return findFirstUseCaseWithNameAndStereotype(name, null);
293    }
294
295    /**
296     * Returns the first use-case it can find with the given name and stereotype, if the stereotype is not specified (it
297     * is null) it will be ignored and the returned use-case may have any arbitrary stereotype.
298     * @param name
299     * @param stereotypeName
300     * @return useCaseWithNameAndStereotype
301     */
302    static UseCase findFirstUseCaseWithNameAndStereotype(String name, String stereotypeName)
303    {
304        UseCase useCaseWithNameAndStereotype = null;
305
306        Collection<UseCase> useCases = getModel().getUseCases().getUseCase().refAllOfType();
307        for (final Iterator<UseCase> useCaseIterator = useCases.iterator(); useCaseIterator.hasNext() && useCaseWithNameAndStereotype ==
308                null;)
309        {
310            UseCase useCase = useCaseIterator.next();
311            if (name.equals(useCase.getName()))
312            {
313                if (stereotypeName == null || isStereotypePresent(useCase, stereotypeName))
314                {
315                    useCaseWithNameAndStereotype = useCase;
316                }
317            }
318        }
319
320        return useCaseWithNameAndStereotype;
321    }
322
323    /**
324     * Returns the first activity graph it can find with the given name.
325     * @param name
326     * @return findFirstActivityGraphWithNameAndStereotype(name, null)
327     */
328    static ActivityGraph findFirstActivityGraphWithName(String name)
329    {
330        return findFirstActivityGraphWithNameAndStereotype(name, null);
331    }
332
333    /**
334     * Returns the first activity graph it can find with the given name and stereotype, if the stereotype is not
335     * specified (it is null) it will be ignored and the returned activity graph may have any arbitrary stereotype.
336     * @param name
337     * @param stereotypeName
338     * @return graphWithNameAndStereotype
339     */
340    static ActivityGraph findFirstActivityGraphWithNameAndStereotype(String name, String stereotypeName)
341    {
342        ActivityGraph graphWithNameAndStereotype = null;
343
344        Collection<ActivityGraph> graphs = getModel().getActivityGraphs().getActivityGraph().refAllOfType();
345        for (final Iterator<ActivityGraph> graphIterator = graphs.iterator();
346             graphIterator.hasNext() && graphWithNameAndStereotype == null;)
347        {
348            ActivityGraph graph = graphIterator.next();
349            if (name.equals(graph.getName()))
350            {
351                if (stereotypeName == null || isStereotypePresent(graph, stereotypeName))
352                {
353                    graphWithNameAndStereotype = graph;
354                }
355            }
356        }
357
358        return graphWithNameAndStereotype;
359    }
360
361    /**
362     * Returns true if the given model element has a tag with the given name and value, returns false otherwise.
363     * @param element
364     * @param tag
365     * @param value
366     * @return tagPresent
367     */
368    static boolean isTagPresent(ModelElement element, String tag, Object value)
369    {
370        boolean tagPresent = false;
371
372        Collection<TaggedValue> taggedValues = element.getTaggedValue();
373        for (final Iterator<TaggedValue> taggedValueIterator = taggedValues.iterator(); taggedValueIterator.hasNext() && !tagPresent;)
374        {
375            TaggedValue taggedValue = taggedValueIterator.next();
376            // does this name match the argument tagged value name ?
377            // Check both the UML14 format name @andromda.value and EMF Format andromda_whatever
378            String tagName = taggedValue.getName();
379            if (tag.equals(tagName) || MetafacadeUtils.getEmfTaggedValue(tag).equals(tagName)
380                || MetafacadeUtils.getUml14TaggedValue(tag).equals(tagName))
381            {
382                for (final Iterator valueIterator = taggedValue.getDataValue().iterator(); valueIterator.hasNext() &&
383                        !tagPresent;)
384                {
385                    Object dataValue = valueIterator.next();
386                    if (value.equals(dataValue))
387                    {
388                        tagPresent = true;
389                    }
390                }
391                for (final Iterator valueIterator = taggedValue.getReferenceValue().iterator(); valueIterator.hasNext() &&
392                        !tagPresent;)
393                {
394                    Object referenceValue = valueIterator.next();
395                    if (value.equals(referenceValue))
396                    {
397                        tagPresent = true;
398                    }
399                }
400            }
401        }
402        return tagPresent;
403    }
404
405    /**
406     * Returns true if the given model element has a hyperlink with the given value, returns false otherwise.
407     * @param element
408     * @param value
409     * @return isTagPresent(element, "hyperlinkModel", value)
410     */
411    static boolean isHyperlinkPresent(ModelElement element, Object value)
412    {
413        return isTagPresent(element, "hyperlinkModel", value);
414    }
415
416    /**
417     * @param element
418     * @param stereotypeName
419     * @return stereotypePresent
420     */
421    static boolean isStereotypePresent(ModelElement element, String stereotypeName)
422    {
423        boolean stereotypePresent = false;
424
425        Collection<Stereotype> stereotypes = element.getStereotype();
426        for (final Iterator<Stereotype> stereotypeIterator = stereotypes.iterator();
427             stereotypeIterator.hasNext() && !stereotypePresent;)
428        {
429            Stereotype stereotype = stereotypeIterator.next();
430            if (stereotypeName.equals(stereotype.getName()))
431            {
432                stereotypePresent = true;
433            }
434        }
435        return stereotypePresent;
436    }
437
438    /**
439     * Returns the first use-case this method can find with the given tagged value or hyperlink. Both arguments are used
440     * to look for the tagged value but only <code>value</code> is used to search for the hyperlink.
441     * @param tag
442     * @param value
443     * @return useCaseWithTaggedValue
444     */
445    static UseCase findUseCaseWithTaggedValueOrHyperlink(String tag, String value)
446    {
447        UseCase useCaseWithTaggedValue = null;
448
449        Collection<UseCase> useCases = getModel().getUseCases().getUseCase().refAllOfType();
450        for (final Iterator<UseCase> useCaseIterator = useCases.iterator(); useCaseIterator.hasNext() && useCaseWithTaggedValue ==
451                null;)
452        {
453            // loop over all use-cases
454            UseCase useCase = useCaseIterator.next();
455            if (isTagPresent(useCase, tag, value) || isHyperlinkPresent(useCase, value))
456            {
457                useCaseWithTaggedValue = useCase;
458            }
459        }
460
461        return useCaseWithTaggedValue;
462    }
463
464    /**
465     * Returns the first class this method can find with the given tagged value or hyperlink. Both arguments are used to
466     * look for the tagged value but only <code>value</code> is used to search for the hyperlink.
467     * @param tag
468     * @param value
469     * @return classWithTaggedValue
470     */
471    static UmlClass findClassWithTaggedValueOrHyperlink(String tag, String value)
472    {
473        UmlClass classWithTaggedValue = null;
474
475        Collection<UmlClass> classes = getModel().getCore().getUmlClass().refAllOfType();
476        for (final Iterator<UmlClass> classIterator = classes.iterator(); classIterator.hasNext() && classWithTaggedValue == null;)
477        {
478            // loop over all classes
479            UmlClass clazz = classIterator.next();
480            if (isTagPresent(clazz, tag, value) || isHyperlinkPresent(clazz, value))
481            {
482                classWithTaggedValue = clazz;
483            }
484        }
485
486        return classWithTaggedValue;
487    }
488
489    /**
490     * @param useCase
491     * @return finalStates
492     */
493    static Collection<FinalState> findFinalStatesWithNameOrHyperlink(UseCase useCase)
494    {
495        List finalStates = new ArrayList();
496
497        if (useCase != null && useCase.getName() != null)
498        {
499            String useCaseName = useCase.getName();
500            Collection<FinalState> allFinalStates = getModel().getStateMachines().getFinalState().refAllOfType();
501            for (final Iterator<FinalState> iterator = allFinalStates.iterator(); iterator.hasNext();)
502            {
503                FinalState finalState = iterator.next();
504                if (useCaseName != null)
505                {
506                    if (useCaseName.equals(finalState.getName()))
507                    {
508                        finalStates.add(finalState);
509                    }
510                    else
511                    {
512                        if (isHyperlinkPresent(finalState, useCase))
513                        {
514                            finalStates.add(finalState);
515                        }
516                    }
517                }
518                else
519                {
520                    if (isHyperlinkPresent(finalState, useCase))
521                    {
522                        finalStates.add(finalState);
523                    }
524                }
525            }
526        }
527
528        return finalStates;
529    }
530
531    /**
532     * Finds the given metafacade class for the passed in <code>facade</code>.
533     *
534     * @param facade the model element facade for which to find the meta class.
535     * @return the meta model element
536     */
537    static ActivityGraph getMetaClass(ActivityGraphFacade facade)
538    {
539        ActivityGraph activityGraph = null;
540
541        if (facade != null)
542        {
543            String id = facade.getId();
544            Collection<ModelElement> graphs = getModel().getActivityGraphs().getActivityGraph().refAllOfType();
545            for (final Iterator<ModelElement> iterator = graphs.iterator(); iterator.hasNext() && activityGraph == null;)
546            {
547                ModelElement element = iterator.next();
548                if (id.equals(element.refMofId()))
549                {
550                    activityGraph = (ActivityGraph)element;
551                }
552            }
553        }
554        return activityGraph;
555    }
556
557    /**
558     * Finds the given metafacade class for the passed in <code>facade</code>.
559     *
560     * @param facade the model element facade for which to find the meta class.
561     * @return the meta model element
562     */
563    static UseCase getMetaClass(UseCaseFacade facade)
564    {
565        UseCase useCase = null;
566
567        if (facade != null)
568        {
569            String id = facade.getId();
570            Collection<ModelElement> useCases = getModel().getUseCases().getUseCase().refAllOfType();
571            for (final Iterator<ModelElement> iterator = useCases.iterator(); iterator.hasNext() && useCase == null;)
572            {
573                ModelElement element = iterator.next();
574                if (id.equals(element.refMofId()))
575                {
576                    useCase = (UseCase)element;
577                }
578            }
579        }
580        return useCase;
581    }
582
583    /**
584     * Finds the given metafacade class for the passed in <code>facade</code>.
585     *
586     * @param facade the model element facade for which to find the meta class.
587     * @return the meta model element
588     */
589    static Parameter getMetaClass(ParameterFacade facade)
590    {
591        Parameter parameter = null;
592
593        if (facade != null)
594        {
595            String id = facade.getId();
596            Collection<ModelElement> parameters = getModel().getCore().getParameter().refAllOfType();
597            for (final Iterator<ModelElement> iterator = parameters.iterator(); iterator.hasNext() && parameter == null;)
598            {
599                ModelElement element = iterator.next();
600                if (id.equals(element.refMofId()))
601                {
602                    parameter = (Parameter)element;
603                }
604            }
605        }
606        return parameter;
607    }
608
609    /**
610     * Finds the given metafacade class for the passed in <code>facade</code>.
611     *
612     * @param facade the model element facade for which to find the meta class.
613     * @return the meta model element
614     */
615    static Event getMetaClass(EventFacade facade)
616    {
617        Event event = null;
618
619        if (facade != null)
620        {
621            String id = facade.getId();
622            Collection<ModelElement> events = getModel().getStateMachines().getEvent().refAllOfType();
623            for (final Iterator<ModelElement> iterator = events.iterator(); iterator.hasNext() && event == null;)
624            {
625                ModelElement element = iterator.next();
626                if (id.equals(element.refMofId()))
627                {
628                    event = (Event)element;
629                }
630            }
631        }
632        return event;
633    }
634
635    /**
636     * Finds the given metafacade class for the passed in <code>facade</code>.
637     *
638     * @param facade the model element facade for which to find the meta class.
639     * @return the meta model element
640     */
641    static ModelElement getMetaClass(ModelElementFacade facade)
642    {
643        ModelElement modelElement = null;
644
645        if (facade != null)
646        {
647            String id = facade.getId();
648            Collection<ModelElement> modelElements = getModel().getCore().getModelElement().refAllOfType();
649            for (final Iterator<ModelElement> iterator = modelElements.iterator(); iterator.hasNext() && modelElement == null;)
650            {
651                ModelElement element = iterator.next();
652                if (id.equals(element.refMofId()))
653                {
654                    modelElement = element;
655                }
656            }
657        }
658        return modelElement;
659    }
660
661    /**
662     * Retrieves the serial version UID by reading the tagged value
663     * {@link UMLProfile#TAGGEDVALUE_SERIALVERSION_UID} of the
664     * <code>classifier</code>.
665     *
666     * @param classifier the classifier to be inspected.
667     * @return the serial version UID of the classifier. Returns
668     *         <code>null</code> if the tagged value cannot be found.
669     */
670    public static String getSerialVersionUID(ClassifierFacade classifier)
671    {
672        ExceptionUtils.checkNull("classifer", classifier);
673        String serialVersionString = (String)classifier
674                .findTaggedValue(UMLProfile.TAGGEDVALUE_SERIALVERSION_UID);
675        return StringUtils.trimToNull(serialVersionString);
676    }
677
678    /**
679     * This method removes all duplicates within the <code>elements</code> collection while at the same
680     * time copying tagged values from duplicates to the one remaining element with the given name.
681     *
682     * @param elements the elements to remove duplicates and copy tagged values to.
683     * @return the elements with duplicates removed.
684     */
685    public static List<ModelElementFacade> removeDuplicatesAndCopyTaggedValues(final Collection<ModelElementFacade> elements)
686    {
687        final Map<String, ModelElementFacade> map = new LinkedHashMap<String, ModelElementFacade>();
688        if (elements != null)
689        {
690            for (final Iterator<ModelElementFacade> iterator = elements.iterator(); iterator.hasNext();)
691            {
692                ModelElementFacade element = iterator.next();
693                final String name = element.getName();
694                final ModelElementFacade existingVariable = map.get(name);
695                // - copy over any tagged values from the existing variable to the new one.
696                if (existingVariable != null)
697                {
698                    element.copyTaggedValues(existingVariable);
699                }
700                map.put(
701                    name,
702                    element);
703            }
704        }
705        return new ArrayList<ModelElementFacade>(map.values());
706    }
707}