View Javadoc
1   package org.andromda.metafacades.emf.uml22;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.HashMap;
8   import java.util.Iterator;
9   import java.util.LinkedHashMap;
10  import java.util.LinkedHashSet;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  import org.andromda.core.common.ExceptionUtils;
15  import org.andromda.core.metafacade.MetafacadeConstants;
16  import org.andromda.metafacades.uml.ClassifierFacade;
17  import org.andromda.metafacades.uml.UMLProfile;
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.collections.Predicate;
20  import org.apache.commons.collections.Transformer;
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.log4j.Logger;
23  import org.eclipse.emf.common.util.EList;
24  import org.eclipse.emf.common.util.TreeIterator;
25  import org.eclipse.emf.ecore.EObject;
26  import org.eclipse.emf.ecore.EcorePackage;
27  import org.eclipse.emf.ecore.resource.Resource;
28  import org.eclipse.emf.ecore.resource.ResourceSet;
29  import org.eclipse.emf.ecore.util.EcoreUtil;
30  import org.eclipse.uml2.common.util.UML2Util;
31  import org.eclipse.uml2.uml.Association;
32  import org.eclipse.uml2.uml.Classifier;
33  import org.eclipse.uml2.uml.Comment;
34  import org.eclipse.uml2.uml.Element;
35  import org.eclipse.uml2.uml.EnumerationLiteral;
36  import org.eclipse.uml2.uml.Generalization;
37  import org.eclipse.uml2.uml.InstanceSpecification;
38  import org.eclipse.uml2.uml.LiteralInteger;
39  import org.eclipse.uml2.uml.LiteralString;
40  import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
41  import org.eclipse.uml2.uml.Model;
42  import org.eclipse.uml2.uml.NamedElement;
43  import org.eclipse.uml2.uml.Namespace;
44  import org.eclipse.uml2.uml.OpaqueExpression;
45  import org.eclipse.uml2.uml.Operation;
46  import org.eclipse.uml2.uml.Package;
47  import org.eclipse.uml2.uml.Parameter;
48  import org.eclipse.uml2.uml.Profile;
49  import org.eclipse.uml2.uml.Property;
50  import org.eclipse.uml2.uml.Slot;
51  import org.eclipse.uml2.uml.Stereotype;
52  import org.eclipse.uml2.uml.UMLPackage;
53  import org.eclipse.uml2.uml.ValueSpecification;
54  import org.eclipse.uml2.uml.resource.UMLResource;
55  
56  /**
57   * Contains utilities for the Eclipse/UML2 metafacades.
58   *
59   * @author Steve Jerman
60   * @author Chad Brandon
61   * @author Wouter Zoons
62   * @author Bob Fields
63   */
64  public class UmlUtilities
65  {
66      /**
67       * The logger instance.
68       */
69      private static final Logger LOGGER = Logger.getLogger(UmlUtilities.class);
70  
71      private static List<Package> models = new ArrayList<Package>();
72  
73      /**
74       * Utility method to return ALL loaded models, populated by RepositoryFacade
75       * @return models
76       */
77      public static List<Package> getModels()
78      {
79          return UmlUtilities.models;
80      }
81  
82      /**
83       * @param resources
84       */
85      public static void setModels(final List<Package> resources)
86      {
87          models = Collections.synchronizedList(resources);
88      }
89  
90      /**
91       * @param resource
92       */
93      public static void addModel(final Package resource)
94      {
95          models.add(resource);
96      }
97  
98      /**
99       * @param resource
100      */
101     public static void removeModel(final Package resource)
102     {
103         models.remove(resource);
104     }
105 
106     /**
107      * A transformer which transforms:
108      * <ul>
109      *   <li>each property in an attribute or an association end to AssociationEnd or Attribute</li>
110      *   <li>each slot in an attribute link or a link end to LinkEnd or AttributeLink</li>
111      *   <li>each instance specification in an object instance or a link instance</li>
112      * </ul>
113      * This is needed because UML2 is an API in which there is no conceptual difference between
114      * fundamentally different elements (see list above); which makes it harder to map to metafacades
115      * geared towards UML 1.4
116      */
117     protected static final Transformer ELEMENT_TRANSFORMER =
118         new Transformer()
119         {
120             public Object transform(final Object element)
121             {
122                 final Object transformedObject;
123 
124                 if (element instanceof Property)
125                 {
126                     final Property property = (Property)element;
127                     if (property instanceof AssociationEnd || property instanceof Attribute)
128                     {
129                         transformedObject = property;
130                     }
131                     else if (property.getAssociation() == null)
132                     {
133                         transformedObject = new AttributeImpl(property);
134                     }
135                     else
136                     {
137                         transformedObject = new AssociationEndImpl(property);
138                     }
139                     if (LOGGER.isDebugEnabled() && property.getName() != null && !property.getName().startsWith("andromda"))
140                     {
141                         LOGGER.debug("UMLUtilities.transform " + property.getName() + " "
142                             + property.getType().getName() + " " + property + " " + transformedObject);
143                     }
144                 }
145                 else if (element instanceof Slot)
146                 {
147                     final Slot slot = (Slot)element;
148 
149                     // TODO: This mixes uml2 types and metafacade types, uml2 cannot be instanceof mf type
150                     if (slot instanceof LinkEnd || slot instanceof AttributeLink)
151                     {
152                         transformedObject = slot;
153                     }
154                     else if (this.transform(slot.getDefiningFeature()) instanceof Attribute)
155                     {
156                         transformedObject = new AttributeLinkImpl(slot);
157                     }
158                     else
159                     {
160                         transformedObject = new LinkEndImpl(slot);
161                     }
162                 }
163                 else if (element instanceof InstanceSpecification)
164                 {
165                     final InstanceSpecification instanceSpecification = (InstanceSpecification)element;
166 
167                     if (instanceSpecification instanceof LinkInstance ||
168                         instanceSpecification instanceof ObjectInstance ||
169                         instanceSpecification instanceof EnumerationLiteral)
170                     {
171                         transformedObject = instanceSpecification;
172                     }
173                     else if (!instanceSpecification.getClassifiers().isEmpty() &&
174                         instanceSpecification.getClassifiers().iterator().next() instanceof org.eclipse.uml2.uml.Class)
175                     {
176                         transformedObject = new ObjectInstanceImpl(instanceSpecification);
177                     }
178                     else
179                     {
180                         transformedObject = new LinkInstanceImpl(instanceSpecification);
181                     }
182                 }
183                 else
184                 {
185                     transformedObject = element;
186                 }
187 
188                 return transformedObject;
189             }
190         };
191 
192     private static final Map<String,List<EObject>> ALL_META_OBJECTS_CACHE =
193         Collections.synchronizedMap(new HashMap<String,List<EObject>>());
194 
195     /**
196      * List all meta objects instances of a given meta class It's a way to
197      * achieve refAllOfType method in a JMI implementation. Please take care of the
198      * fact that properties are not transformed here.
199      *
200      * @param metaClass The meta class we're looking for its instances
201      * @param models     The models where we're searching
202      * @return a list of objects owned by model, instance of metaClass
203      */
204     public static List<? extends EObject> getAllMetaObjectsInstanceOf(
205         final Class metaClass,
206         final List<Package> models)
207     {
208         if (metaClass==null)
209         {
210             return new ArrayList<EObject>();
211         }
212         List<EObject> metaObjects = ALL_META_OBJECTS_CACHE.get(metaClass.getCanonicalName());
213         if (metaObjects == null)
214         {
215             metaObjects = new ArrayList<EObject>();
216 
217             for (final Package model : models)
218             {
219                 if (model!=null)
220                 {
221                     //for (Object metaObject : model.eAllContents())
222                     for (final Iterator<EObject> it = model.eAllContents(); it.hasNext();)
223                     {
224                         final EObject metaObject = it.next();
225                         if (metaClass.isInstance(metaObject))
226                         {
227                             metaObjects.add(metaObject);
228                             if (LOGGER.isDebugEnabled())
229                             {
230                                 LOGGER.debug("getAllMetaObjectsInstanceOf class: " + metaClass.getCanonicalName() + " " + metaClass.getClass() + " Found: " + metaObject.getClass());
231                             }
232                         }
233                     }
234                 }
235             }
236         }
237 
238         if (LOGGER.isDebugEnabled())
239         {
240             LOGGER.debug("getAllMetaObjectsInstanceOf class: " + metaClass.getCanonicalName() + ' ' + metaClass.getClass() + " Found: " + metaObjects.size());
241         }
242         ALL_META_OBJECTS_CACHE.put(metaClass.getCanonicalName(), metaObjects);
243 
244         return metaObjects;
245     }
246 
247     /**
248      * List all meta objects instances of a given meta class It's a way to
249      * achieve refAllOfType method in a JMI implementation. Please take care of the
250      * fact that properties are not transformed here.
251      *
252      * @param metaClass The meta class we're looking for its instances
253      * @param model     The model where we're searching
254      * @return a list of objects owned by model, instance of metaClass
255      */
256     private static List getAllMetaObjectsInstanceOf(
257         final Class metaClass,
258         final Package model)
259     {
260         if (metaClass==null)
261         {
262             return new ArrayList<EObject>();
263         }
264         final List<EObject> metaObjects = new ArrayList<EObject>();
265 
266         if (model!=null)
267         {
268             //for (Object metaObject : model.eAllContents())
269             for (final Iterator<EObject> it = model.eAllContents(); it.hasNext();)
270             {
271                 final EObject metaObject = it.next();
272                 if (metaClass.isInstance(metaObject))
273                 {
274                     metaObjects.add(metaObject);
275                 }
276             }
277         }
278 
279         return metaObjects;
280     }
281 
282     /**
283      * This clears the meta objects cache.  Even though this
284      * isn't the "cleanest" way to handle things, we need this
285      * for performance reasons (getAllMetaObjectsInstanceOf is WAY
286      * to slow otherwise).
287      */
288     public static void clearAllMetaObjectsCache()
289     {
290         ALL_META_OBJECTS_CACHE.clear();
291     }
292 
293     /**
294      * Get the comments for a UML Element. This will be a string with each
295      * comment separated by a 2 newlines.
296      *
297      * @param element
298      * @return concatenated string
299      */
300     public static String getComment(final Element element)
301     {
302         if (element==null)
303         {
304             return null;
305         }
306         final StringBuilder commentString = new StringBuilder();
307         final Collection<Comment> comments = element.getOwnedComments();
308 
309         for (final Comment comment : comments)
310         {
311             if (commentString.length()>0)
312             {
313                 commentString.append("\n\n");
314             }
315             commentString.append(comment.getBody());
316         }
317         return cleanText(commentString.toString());
318     }
319 
320     /**
321      * Gets rid of all excess whitespace.
322      *
323      * @param text the text from which to remove the white space.
324      * @return the cleaned text.
325      */
326     public static String cleanText(String text)
327     {
328         if (StringUtils.isBlank(text))
329         {
330             return text;
331         }
332         text =
333             text.replaceAll(
334                 "[\\t\\n]*",
335                 "");
336         text =
337             text.replaceAll(
338                 "\\s+",
339                 " ");
340 
341         return text;
342     }
343 
344     /**
345      * returns all owned properties of the given classifier with the right type:
346      * attribute if <code>isAssociation</code> is false and association end
347      * otherwise.
348      *
349      * @param classifier the classifier to inspect.
350      * @param follow whether to follow inheritance hierarchy upward.
351      * @param isAssociation Retrieve only AssociationEnd properties.
352      * @return all owned properties of the given classifier with the right type.
353      */
354     public static List<Property> getOwnedProperty(
355         final Classifier classifier,
356         final boolean follow,
357         final boolean isAssociation)
358     {
359         if (classifier==null)
360         {
361             return new ArrayList<Property>();
362         }
363         final Map<String, Property> attributeMap = new LinkedHashMap<String, Property>(); // preserve ordering
364         final List<NamedElement> members = new ArrayList<NamedElement>(classifier.getOwnedMembers());
365 
366         if (follow)
367         {
368             members.addAll(classifier.getInheritedMembers());
369         }
370 
371         for (NamedElement nextCandidate : members)
372         {
373             if (nextCandidate instanceof Property)
374             {
375                 final Property property = (Property)nextCandidate;
376                 /*if (attributeMap.containsKey(property.getName()))
377                 {
378                     logger.warn(
379                         "Attribute with this name has already been registered on " +
380                         classifier.getQualifiedName() + ": " + property.getName());
381                 }*/
382 
383                 if (isAssociation && property.getAssociation() != null)
384                 {
385                     /*if (LOGGER.isDebugEnabled())
386                     {
387                         LOGGER.debug("Association found for " + classifier.getName() + ": " + property.getName());
388                     }*/
389                     // property represents an association end
390                     attributeMap.put(
391                         property.getName(),
392                         property);
393                 }
394                 else if (!isAssociation && property.getAssociation() == null)
395                 {
396                     /*if (LOGGER.isDebugEnabled())
397                     {
398                         LOGGER.debug("Attribute found for " + classifier.getName() + ": " + property.getName());
399                     }*/
400                     // property represents an attribute
401                     attributeMap.put(
402                         property.getName(),
403                         property);
404                 }
405             }
406         }
407 
408         return new ArrayList<Property>(attributeMap.values());
409     }
410 
411     /**
412      * Gets a collection containing all of the attributes for this
413      * class/interface. Superclass properties will included if
414      * <code>follow</code> is true. Overridden properties will be omitted.
415      *
416      * @param classifier the UML class instance from which to retrieve all properties
417      * @param follow whether or not the inheritance hierarchy should be followed upward
418      * @return all retrieved attributes. No associations or enumerations.
419      */
420     public static List<Property> getAttributes(
421         final Classifier classifier,
422         final boolean follow)
423     {
424         final List<Property> attributeList = getOwnedProperty(classifier, follow, false);
425         CollectionUtils.transform(
426             attributeList,
427             ELEMENT_TRANSFORMER);
428         //Collections.sort(attributeList, new PropertyComparator());
429         if (LOGGER.isDebugEnabled())
430         {
431             for (Property property : attributeList)
432             {
433                 if (!classifier.getQualifiedName().startsWith("andromda") && LOGGER.isDebugEnabled())
434                 {
435                     LOGGER.debug("UMLUtilities.getAttributes " + classifier.getQualifiedName()
436                         + " " + property.getName() + " " + property.getType().getName());
437                 }
438             }
439         }
440         return attributeList;
441     }
442 
443     /**
444      * Returns <code>true</code> if the given association end's type is an ancestor of the classifier, or just the
445      * argument classifier if follow is <code>false</code>.
446      * @param classifier
447      * @param property this method returns false if this argument is not an association end
448      * @param follow
449      * @return isAssociationEndAttachedToType((Classifier)parent, property, follow)
450      */
451     public static boolean isAssociationEndAttachedToType(
452         final Classifier classifier,
453         final Property property,
454         final boolean follow)
455     {
456         boolean attachedToType = false;
457 
458         if (property.getAssociation() != null)
459         {
460             attachedToType = classifier.equals(property.getType());
461             if (follow && !attachedToType)
462             {
463 
464                 for (Classifier parent : classifier.getGenerals())
465                 {
466                     //if (parent instanceof Classifier)
467                     //{
468                     attachedToType =
469                         isAssociationEndAttachedToType(
470                             parent,
471                             property,
472                             follow);
473                     //}
474                 }
475             }
476             if (LOGGER.isDebugEnabled() && attachedToType)
477             {
478                 LOGGER.debug("isAssociationEndAttachedToType " + classifier.getQualifiedName() + ' ' + property + ' ' + property.getQualifiedName() + ' ' + property.getAssociation() + ' ' + property.getAssociationEnd() + ' ' + attachedToType);
479             }
480         }
481         return attachedToType;
482     }
483 
484     /**
485      * Gets a collection containing all of the associationEnds for this
486      * class/interface. Superclass properties will be included if
487      * <code>follow</code> is true. Overridden properties will be omitted.
488      * Finds all Property classes in model and iterates through to see which are of type classifier.
489      * <p/>
490      * cejeanne: Changed the way association ends are found.
491      *
492      * @param classifier the UML class instance from which to retrieve all properties
493      * @param follow     whether or not the inheritance hierarchy should be followed
494      * @return all retrieved attributes.
495      */
496     public static List<Property> getAssociationEnds(
497         final Classifier classifier,
498         final boolean follow)
499     {
500         final Set<Property> associationEnds = new LinkedHashSet<Property>();
501         if (classifier==null)
502         {
503             return Collections.emptyList();
504         }
505         associationEnds.addAll(getOwnedProperty(classifier, follow, true));
506         CollectionUtils.transform(associationEnds, new Transformer()
507         {
508             public Object transform(final Object input) {
509                 return getOppositeProperty((Property)input);
510             }
511         });
512         // TODO: Iterate through all referenced models, not just the model containing this classifier.
513         // TODO: UML2 bug? getModel returns null because UMLUtil.getOwningElement getBaseElement(owner.eContainer()) changes owningElement to null
514         final Package modelPackage = UmlUtilities.findModel(classifier);
515         /*if (modelPackage==null)
516         {
517             logger.error(classifier + " getModel was null: " + classifier.getOwner() + " " + classifier.getQualifiedName());
518             Element classifierOwner = classifier.getOwner();
519             Element owner = null;
520             while (classifierOwner!=null)
521             {
522                 owner = classifierOwner;
523                 classifierOwner = owner.getOwner();
524             }
525             // Find the last owner in the chain... Top level package.
526             modelPackage = (Package) owner;
527         }*/
528         final List<Property> allProperties = getAllMetaObjectsInstanceOf(
529                 Property.class,
530                 modelPackage);
531         if (LOGGER.isDebugEnabled())
532         {
533             LOGGER.debug("getAssociationEnds " + classifier.getQualifiedName() + ": getAllMetaObjectsInstanceOf=" + allProperties.size());
534         }
535 
536         for (Property property : allProperties)
537         {
538             // only treat association ends, ignore attributes
539             if (property.getAssociation() != null && isAssociationEndAttachedToType(
540                     classifier,
541                     property,
542                     follow))
543             {
544                 /*int ownedSize = property.getAssociation().getOwnedEnds().size();
545                 if (ownedSize==1)
546                 {
547                     associationEnds.add(property.getAssociation().getOwnedEnds().get(0));
548                     logger.debug("getAssociationEnds " + classifier.getQualifiedName() + ": addedOwnedAssociationEnd " + property + " " + property.getType() + " " + property.getAssociation() + " AssociationEnd=" + property.getAssociationEnd() + " Qualifiers=" + property.getQualifiers() + " Opposite=" + property.getOpposite());
549                 }
550                 else if (ownedSize==0 || ownedSize>1)
551                 {
552                     logger.error("associationEnds ownedEnds=" + ownedSize);
553                 }
554                 else
555                 {*/
556                     // TODO: associationEnds always show up as non-navigable because the association property (not the end) is added.
557                     associationEnds.add(property);
558                     if (LOGGER.isDebugEnabled())
559                     {
560                         LOGGER.debug("getAssociationEnds " + classifier.getQualifiedName() + ": addedAssociation " + property + ' ' + property.getType() + ' ' + property.getAssociation() + " AssociationEnd=" + property.getAssociationEnd() + " OwnedEnds=" + property.getAssociation().getOwnedEnds() + " Qualifiers=" + property.getQualifiers() + " Navigable=" + property.isNavigable());
561                     }
562                /* }*/
563             }
564         }
565 
566         CollectionUtils.transform(
567             associationEnds,
568             ELEMENT_TRANSFORMER);
569         return new ArrayList<Property>(associationEnds);
570     }
571 
572     /**
573      * Returns <code>true</code> if and only if the given operation would have an identical signature.
574      * This means:
575      * <ul>
576      *  <li>the same name</li>
577      *  <li>the same number of parameters</li>
578      *  <li>matching parameter types (in that very same order)</li>
579      * </ul>
580      * @param first
581      * @param second
582      * @return isEqual(firstParameter.getType(), secondParameter.getType())
583      */
584     public static boolean isSameSignature(
585         final Operation first,
586         final Operation second)
587     {
588         boolean sameSignature = true;
589 
590         // test name
591         if (isEqual(
592                 first.getName(),
593                 second.getName()))
594         {
595             final List<Parameter> firstParameters = first.getOwnedParameters();
596             final List<Parameter> secondParameters = second.getOwnedParameters();
597 
598             // test number of parameters
599             if (firstParameters.size() == secondParameters.size())
600             {
601                 for (int i = 0; i < firstParameters.size() && sameSignature; i++)
602                 {
603                     final Parameter firstParameter = firstParameters.get(i);
604                     final Parameter secondParameter = secondParameters.get(i);
605 
606                     // test each parameter's type
607                     sameSignature =
608                         isEqual(
609                             firstParameter.getType(),
610                             secondParameter.getType());
611                 }
612             }
613             else
614             {
615                 sameSignature = false;
616             }
617         }
618         else
619         {
620             sameSignature = false;
621         }
622 
623         return sameSignature;
624     }
625 
626     /**
627      * Returns <code>true</code> if and only if both arguments are equal, this method handles potential
628      * incoming <code>null</code> values.
629      */
630     private static boolean isEqual(
631         final Object first,
632         final Object second)
633     {
634         return first == null ? second == null : first.equals(second);
635     }
636 
637     /**
638      * Retrieves all specializations of the given <code>classifier</code>
639      * instance.
640      *
641      * @param classifier the classifier from which to retrieve the specializations.
642      * @return all specializations.
643      */
644     public static List<Classifier> getSpecializations(final Classifier classifier)
645     {
646         final List<Classifier> specials = new ArrayList<Classifier>();
647         if (classifier==null)
648         {
649             return specials;
650         }
651         /*for(DirectedRelationship gen :
652             classifier.getTargetDirectedRelationships(UMLPackage.eINSTANCE.getGeneralization()))
653         {
654            for(Element elt : gen.getSources())
655            {
656              if (elt instanceof Classifier)
657                  specials.add((Classifier)elt);
658            }
659         }*/
660 
661         for (final TreeIterator<EObject> iterator = EcoreUtil.getRootContainer(classifier).eAllContents(); iterator.hasNext();)
662         {
663             final EObject object = iterator.next();
664             if (object instanceof Generalization)
665             {
666                 final Generalization generalization = (Generalization)object;
667                 if (generalization.getGeneral().equals(classifier))
668                 {
669                     specials.add(generalization.getSpecific());
670                 }
671                 iterator.prune();
672             }
673         }
674         return specials;
675     }
676 
677     /**
678      * Retrieves the names of the stereotypes for the given <code>element</code>
679      *
680      * @param element the element for which to retrieve the stereotypes.
681      * @return all stereotype names
682      */
683     public static List<String> getStereotypeNames(final Element element)
684     {
685         final List<String> names = new ArrayList<String>();
686         if (element==null)
687         {
688             return names;
689         }
690         final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
691         if (stereotypes != null)
692         {
693             for (Stereotype stereotype : stereotypes)
694             {
695                 names.add(stereotype.getName());
696             }
697         }
698         return names;
699     }
700 
701     /**
702      * Indicates whether or not the given <code>element</code> contains a
703      * stereotype with the given <code>stereotypeName</code>.
704      *
705      * @param element the element instance.
706      * @param stereotypeName the name of the stereotype
707      * @return true/false
708      */
709     public static boolean containsStereotype(
710         final Element element,
711         final String stereotypeName)
712     {
713         if (element==null || StringUtils.isBlank(stereotypeName))
714         {
715             return false;
716         }
717         final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
718 
719         boolean hasStereotype = StringUtils.isNotBlank(stereotypeName) && stereotypes != null &&
720             !stereotypes.isEmpty();
721 
722         if (hasStereotype)
723         {
724             class StereotypeFilter
725                 implements Predicate
726             {
727                 public boolean evaluate(final Object object)
728                 {
729                     boolean valid;
730                     final Stereotype stereotype = (Stereotype)object;
731                     final String name = StringUtils.trimToEmpty(stereotype.getName());
732                     valid = stereotypeName.equalsIgnoreCase(name);
733                     for (Classifier itStereo : stereotype.allParents())
734                     {
735                         valid = valid || StringUtils.trimToEmpty(itStereo.getName()).equalsIgnoreCase(stereotypeName);
736                     }
737                     return valid;
738                 }
739             }
740             hasStereotype =
741                 CollectionUtils.find(
742                     stereotypes,
743                     new StereotypeFilter()) != null;
744         }
745         if (LOGGER.isDebugEnabled() && hasStereotype)
746         {
747             if (element instanceof NamedElement)
748             {
749                 LOGGER.debug(
750                     ((NamedElement)element).getQualifiedName() + " has stereotype <<" + stereotypeName + ">> : " +
751                     hasStereotype);
752             }
753             else
754             {
755                 LOGGER.debug(element.toString() + " has stereotype <<" + stereotypeName + ">> : " + hasStereotype);
756             }
757         }
758         return hasStereotype;
759     }
760 
761     /**
762      * @deprecated old way to handle tag values
763      *             Note: The uml profile defines it as "AndroMdaTags" and not "AndroMDATags"
764      *             Stores the tagged values that may be applied to an element.
765      */
766     @Deprecated
767     private static final String TAGGED_VALUES_STEREOTYPE = "AndroMdaTags";
768 
769     /**
770      * Retrieves the TagDefinitions for the given element.
771      *
772      * @param element the element from which to retrieve the tagged values.
773      * @return the collection of {@link TagDefinition} instances.
774      */
775     public static Collection<TagDefinition> getTaggedValue(final Element element)
776     {
777         final Collection<TagDefinition> tags = new ArrayList<TagDefinition>();
778         if (element==null)
779         {
780             return tags;
781         }
782         String elementName = "";
783 
784         if (element instanceof NamedElement)
785         {
786             elementName = ((NamedElement)element).getName();
787         }
788         else
789         {
790             elementName = element.toString();
791         }
792 
793         /*if (logger.isDebugEnabled())
794         {
795             logger.debug("Searching Tagged Values for " + elementName);
796         }*/
797         final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
798         for (final Stereotype stereo : stereotypes)
799         {
800             if (TAGGED_VALUES_STEREOTYPE.equals(stereo.getName()))
801             {
802                 final List tagNames = (List)element.getValue(
803                         stereo,
804                         "TagName");
805                 final List tagValues = (List)element.getValue(
806                         stereo,
807                         "TagValue");
808                 for (int ctr = 0; ctr < tagValues.size(); ctr++)
809                 {
810                     tags.add(new TagDefinitionImpl(
811                             tagNames.get(ctr).toString(),
812                             tagValues.get(ctr)));
813                 }
814             }
815             else if (element.hasValue(
816                     stereo,
817                     "value"))
818             {
819                 final Object value = element.getValue(
820                         stereo,
821                         "value");
822                 tags.add(new TagDefinitionImpl(
823                         stereo.getName(),
824                         value));
825             }
826             else
827             {
828                 for (final Property tagProperty : getAttributes(stereo, true))
829                 {
830                     final String tagName = tagProperty.getName();
831                     // Some metafacades depend on an actual returned value. hasValue returns nothing if taggedValue=default for the attribute.
832                     if (!tagName.startsWith("base$") && element.hasValue(stereo, tagName))
833                     {
834                         // Obtain its value
835                         final Object tagValue = element.getValue(stereo, tagName);
836                         if (tagValue instanceof Collection)
837                         {
838                             final Collection tagValues = (Collection)tagValue;
839                             if (!tagValues.isEmpty())
840                             {
841                                 final Collection tagValuesInString =
842                                     CollectionUtils.collect(
843                                         tagValues,
844                                         new Transformer()
845                                         {
846                                             public Object transform(final Object object)
847                                             {
848                                                 return getTagValueAsString(object);
849                                             }
850                                         });
851                                 final TagDefinition tagDefinition = new TagDefinitionImpl(tagName, tagValuesInString);
852                                 tags.add(tagDefinition);
853                             }
854                         }
855                         else
856                         {
857                             final String tagString = getTagValueAsString(tagValue);
858                             if (!StringUtils.isBlank(tagString) && !"default".equalsIgnoreCase(tagString))
859                             {
860                                 final TagDefinition tagDefinition =
861                                     new TagDefinitionImpl(tagName, tagString);
862                                 tags.add(tagDefinition);
863                             }
864                         }
865                     }
866                 }
867             }
868         }
869 
870         if (LOGGER.isDebugEnabled() && !tags.isEmpty())
871         {
872             LOGGER.debug("Found " + tags.size() + " tagged values for " + elementName);
873         }
874 
875         return tags;
876     }
877 
878     /**
879      * The toString() method isn't suitable to transform the values of tagValue as String.
880      * @param tagValue
881      * @return the tag value as a string.
882      */
883     static String getTagValueAsString(final Object tagValue)
884     {
885         String valueAsString = null;
886         if (tagValue != null)
887         {
888             valueAsString = tagValue.toString();
889             if (tagValue instanceof ValueSpecification)
890             {
891                 final ValueSpecification literal = (ValueSpecification)tagValue;
892                 valueAsString = literal.stringValue();
893             }
894             else if (tagValue instanceof NamedElement)
895             {
896                 final NamedElement instance = (NamedElement)tagValue;
897                 valueAsString = instance.getName();
898             }
899         }
900         return valueAsString;
901     }
902 
903     /**
904      * Attempts to find the applied stereotype with the given name on the given
905      * <code>element</code>. First tries to find it with the fully qualified
906      * name, and then tries it with just the name.
907      * @param element
908      * @param name
909      *            the name of the stereotype
910      * @return the found stereotype or null if not found.
911      */
912     public static Stereotype findAppliedStereotype(
913         final Element element,
914         final String name)
915     {
916         if (element==null || StringUtils.isBlank(name))
917         {
918             return null;
919         }
920         Stereotype foundStereotype = element.getAppliedStereotype(name);
921         if (foundStereotype == null)
922         {
923             final EList<Stereotype> stereotypes = element.getAppliedStereotypes();
924             if (stereotypes != null)
925             {
926                 for (Stereotype stereotype : stereotypes)
927                 {
928                     if (stereotype.getName().equals(name))
929                     {
930                         foundStereotype = stereotype;
931                         break;
932                     }
933                 }
934             }
935         }
936         return foundStereotype;
937     }
938 
939     /**
940      * Attempts to find the applicable stereotype with the given name on the
941      * given <code>element</code>. First tries to find it with the fully
942      * qualified name, and then tries it with just the name.
943      * @param element
944      * @param name the name of the stereotype
945      * @return the found stereotype or null if not found.
946      */
947     public static Stereotype findApplicableStereotype(
948         final Element element,
949         final String name)
950     {
951         if (element==null || StringUtils.isBlank(name))
952         {
953             return null;
954         }
955         Stereotype foundStereotype = element.getApplicableStereotype(name);
956         if (foundStereotype == null)
957         {
958             final EList<Stereotype> stereotypes = element.getApplicableStereotypes();
959             if (stereotypes != null)
960             {
961                 for (Stereotype stereotype : stereotypes)
962                 {
963                     if (stereotype.getName().equals(name))
964                     {
965                         foundStereotype = stereotype;
966                         break;
967                     }
968                 }
969             }
970         }
971         return foundStereotype;
972     }
973 
974     /**
975      * Retrieves the serial version UID by reading the tagged value
976      * {@link UMLProfile#TAGGEDVALUE_SERIALVERSION_UID} of the
977      * <code>classifier</code>.
978      *
979      * @param classifier the classifier to be inspected.
980      * @return the serial version UID of the classifier. Returns
981      *         <code>null</code> if the tagged value cannot be found.
982      */
983     static String getSerialVersionUID(final ClassifierFacade classifier)
984     {
985         ExceptionUtils.checkNull(
986             "classifer",
987             classifier);
988         final String serialVersionString = (String)classifier.findTaggedValue(UMLProfile.TAGGEDVALUE_SERIALVERSION_UID);
989         return StringUtils.trimToNull(serialVersionString);
990     }
991 
992     /**
993      * Gets the opposite end of the given <code>associationEnd</code> if the
994      * property is indeed an association end, otherwise returns null.
995      *
996      * @param associationEnd the association end from which to retrieve the opposite end.
997      * @return the opposite association end or null.
998      */
999     public static Property getOppositeProperty(final Property associationEnd)
1000     {
1001         if (associationEnd==null)
1002         {
1003             return null;
1004         }
1005         Property opposite = associationEnd.getOpposite();
1006         if (opposite == null)
1007         {
1008             final Association association = associationEnd.getAssociation();
1009             if (association != null)
1010             {
1011                 final Collection<Property> ends = association.getMemberEnds();
1012                 for (final Property end : ends)
1013                 {
1014                     if (end != null && !associationEnd.equals(end))
1015                     {
1016                         opposite = end;
1017                         break;
1018                     }
1019                 }
1020             }
1021         }
1022         return opposite;
1023     }
1024 
1025 
1026     /**
1027      * Gets the opposite end of the given <code>associationEnd</code> if the
1028      * property is indeed an association end, otherwise returns null.
1029      *
1030      * @param associationEnd the association end from which to retrieve the opposite end.
1031      * @return the opposite association end or null.
1032      */
1033     public static AssociationEnd getOppositeAssociationEnd(final Property associationEnd)
1034     {
1035         if (associationEnd==null)
1036         {
1037             return null;
1038         }
1039         return new AssociationEndImpl(getOppositeProperty(associationEnd));
1040     }
1041 
1042     /**
1043      * Finds and returns the first model element having the given
1044      * <code>name</code> in the <code>modelPackage</code>, returns
1045      * <code>null</code> if not found.
1046      * @param resourceSet
1047      * @param pred
1048      *
1049      * @return the found model element.
1050      */
1051     public static Object findByPredicate(
1052         final ResourceSet resourceSet,
1053         final Predicate pred)
1054     {
1055         Object modelElement = null;
1056         if (resourceSet==null || pred==null)
1057         {
1058             return modelElement;
1059         }
1060         for (Resource resource : resourceSet.getResources())
1061         {
1062             final Package model =
1063                 (Package)EcoreUtil.getObjectByType(
1064                     resource.getContents(),
1065                     UMLPackage.eINSTANCE.getPackage());
1066             if (model != null)
1067             {
1068                 for (final TreeIterator<EObject> elementIterator = model.eAllContents();
1069                     elementIterator.hasNext() && modelElement == null;)
1070                 {
1071                     final Object object = elementIterator.next();
1072                     if (pred.evaluate(object))
1073                     {
1074                         modelElement = object;
1075                     }
1076                 }
1077             }
1078             if (modelElement != null)
1079             {
1080                 break;
1081             }
1082         }
1083 
1084         return modelElement;
1085     }
1086 
1087     /**
1088      * Find the Model of a resource (UML2 Model)
1089      * @param resource
1090      * @return (Model)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.eINSTANCE.getEObject())
1091      */
1092     public static Package findModel(final UMLResource resource)
1093     {
1094         if (resource==null)
1095         {
1096             return null;
1097         }
1098         final Package model = (Package)EcoreUtil.getObjectByType(
1099                 resource.getContents(),
1100                 EcorePackage.eINSTANCE.getEObject());
1101         if (model==null)
1102         {
1103             LOGGER.error("getModel was null: " + resource);
1104         }
1105         else if (LOGGER.isDebugEnabled())
1106         {
1107             LOGGER.debug("Model found: " + model);
1108         }
1109         return model;
1110     }
1111 
1112     /**
1113      * Find the Model of a resource (UML2 Model)
1114      * @param element
1115      * @return (Model)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.eINSTANCE.getEObject())
1116      */
1117     public static Package findModel(final Element element)
1118     {
1119         if (element==null)
1120         {
1121             return null;
1122         }
1123         Package modelPackage = element.getModel();
1124         if (modelPackage==null)
1125         {
1126             if (LOGGER.isDebugEnabled())
1127             {
1128                 LOGGER.error("getModel was null: " + element + " OWNER: " + element.getOwner());
1129             }
1130             Element classifierOwner = element.getOwner();
1131             Element owner = null;
1132             while (classifierOwner!=null)
1133             {
1134                 owner = classifierOwner;
1135                 classifierOwner = owner.getOwner();
1136             }
1137             // Find the last owner in the chain... Top level package.
1138             modelPackage = (Package) owner;
1139         }
1140         return modelPackage;
1141     }
1142 
1143     /**
1144      * Constructs the package name for the given <code>metaObject</code>,
1145      * separating the package name by the given <code>separator</code>.
1146      *
1147      * @param metaObject the Model Element
1148      * @param separator  the PSM namespace separator, ignored if <code>modelName</code> is <code>true</code>
1149      * @param modelName  true/false on whether or not to get the model package name
1150      *                   instead of the PSM package name.
1151      * @return the package name.
1152      */
1153     public static String getPackageName(
1154         final NamedElement metaObject,
1155         final String separator,
1156         final boolean modelName)
1157     {
1158         if (metaObject==null || StringUtils.isBlank(separator))
1159         {
1160             return null;
1161         }
1162         final StringBuilder buffer = new StringBuilder();
1163 
1164         final String usedSeparator = modelName ? MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR : separator;
1165 
1166         for (Namespace namespace = metaObject.getNamespace(); namespace != null;
1167             namespace = namespace.getNamespace())
1168         {
1169             if (namespace instanceof Package && !(namespace instanceof Model) && !(namespace instanceof Profile))
1170             {
1171                 if (buffer.length() != 0)
1172                 {
1173                     buffer.insert(
1174                         0,
1175                         usedSeparator);
1176                 }
1177 
1178                 buffer.insert(
1179                     0,
1180                     namespace.getName());
1181             }
1182         }
1183         String packageName = buffer.toString();
1184         /* // TODO Remove Model Name from the package hierarchy
1185         if (StringUtils.isBlank(packageName))
1186         {
1187             packageName =
1188                 getPackageName(
1189                     metaObject.getOwner(),
1190                     separator,
1191                     modelName);
1192         }
1193         if (StringUtils.isBlank(packageName) && metaObject instanceof Classifier)
1194         {
1195             packageName = ((Classifier)metaObject).getPackage().getQualifiedName();
1196         }
1197         // Allow for empty namespace - new after UML2 v3
1198         if (StringUtils.isBlank(packageName))
1199         {
1200             Package modelPackage = metaObject.getModel();
1201             String name = metaObject.getNearestPackage().getQualifiedName();
1202             if (modelPackage instanceof Model)
1203             {
1204                 // Remove model name from the front of Package Name string
1205                 String model = modelPackage.getName();
1206                 if (model != null && name.indexOf(model) > -1 && (name.length() >= model.length() + separator.length() + 1))
1207                 {
1208                     packageName = name.substring(model.length() + separator.length() + 1);
1209                 }
1210             }
1211         }*/
1212         if (modelName && StringUtils.isNotBlank(packageName))
1213         {
1214             packageName =
1215                 StringUtils.replace(
1216                     packageName,
1217                     separator,
1218                     MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
1219         }
1220 
1221         return packageName;
1222     }
1223 
1224     /**
1225      * Returns the package name of the closest ancestor that is an instance of <code>NamedElement</code>. If no such
1226      * ancestor exists the empty String is returned.
1227      * <p/>
1228      * If the argument would be an instance of <code>NamedElement</code> then this method returns that object's
1229      * package name.
1230      * @param metaObject
1231      * @param separator
1232      * @param modelName
1233      * @return packageName
1234      *
1235      * @see #getPackageName(org.eclipse.uml2.uml.NamedElement, String, boolean)
1236      */
1237     public static String getPackageName(
1238         final Element metaObject,
1239         final String separator,
1240         final boolean modelName)
1241     {
1242         if (metaObject==null || StringUtils.isBlank(separator))
1243         {
1244             return null;
1245         }
1246         String packageName = null;
1247 
1248         if (metaObject instanceof NamedElement)
1249         {
1250             packageName =
1251                 getPackageName(
1252                     (NamedElement)metaObject,
1253                     separator,
1254                     modelName);
1255         }
1256         else if (metaObject.getOwner() == null)
1257         {
1258             packageName = "";
1259         }
1260         else
1261         {
1262             packageName =
1263                 getPackageName(
1264                     metaObject.getOwner(),
1265                     separator,
1266                     modelName);
1267         }
1268         /* if (StringUtils.isBlank(packageName))
1269         {
1270             packageName =
1271                 getPackageName(
1272                     metaObject.getOwner(),
1273                     separator,
1274                     modelName);
1275         }
1276         // TODO Remove model name from the front of the FQ package name
1277         if (StringUtils.isBlank(packageName) && metaObject instanceof Classifier)
1278         {
1279             packageName = ((Classifier)metaObject).getPackage().getQualifiedName();
1280         }
1281         if (StringUtils.isBlank(packageName))
1282         {
1283             packageName = metaObject.getNearestPackage().getQualifiedName();
1284         }*/
1285 
1286         return packageName;
1287     }
1288 
1289     /**
1290      * Returns the fully-qualified name of the model element by iterating up through the getOwner hierarchy.
1291      * @param metaObject
1292      * @param separator
1293      * @param modelName
1294      * @return packageName
1295      */
1296     public static String getFullyQualifiedName(
1297         final Element metaObject,
1298         final String separator,
1299         final boolean modelName)
1300     {
1301         if (metaObject==null || StringUtils.isBlank(separator) || !(metaObject instanceof NamedElement))
1302         {
1303             return "";
1304         }
1305         final NamedElement element = (NamedElement)metaObject;
1306         String name = element.getName();
1307         Element owner = element.getOwner();
1308         String ownerName = null;
1309 
1310         while (owner != null)
1311         {
1312             // Don't add the top level model name(s) to the FQN, unless the model is a Package.
1313             if (owner instanceof NamedElement && !(owner instanceof Model) && !(owner instanceof Profile))
1314             {
1315                 ownerName = ((NamedElement)owner).getName();
1316                 name = ownerName + separator + name;
1317             }
1318             owner = owner.getOwner();
1319         }
1320         /*// remove the top level model name, so that mapping works correctly...
1321         if (ownerName != null)
1322         {
1323             name = name.substring(ownerName.length() + separator.length());
1324         }*/
1325 
1326         return name;
1327     }
1328 
1329     /**
1330      * Finds and returns the first model element having the given
1331      * <code>name</code> in the <code>modelPackage</code>, returns
1332      * <code>null</code> if not found.
1333      *
1334      * @param rs   the resource set to search in
1335      * @param name the name to find.
1336      * @return the found model element.
1337      */
1338     public static Object findByName(
1339         final ResourceSet rs,
1340         final String name)
1341     {
1342         if (rs==null || StringUtils.isBlank(name))
1343         {
1344             return null;
1345         }
1346         Object modelElement = null;
1347         if (StringUtils.isNotBlank(name))
1348         {
1349             modelElement =
1350                 findByPredicate(
1351                     rs,
1352                     new Predicate()
1353                     {
1354                         public boolean evaluate(final Object object)
1355                         {
1356                             if (object instanceof NamedElement)
1357                             {
1358                                 return StringUtils.trimToEmpty(((NamedElement)object).getName()).equals(name);
1359                             }
1360                             return false;
1361                         }
1362                     });
1363         }
1364         return modelElement;
1365     }
1366 
1367     /**
1368      * Finds a given model element in the model having the specified
1369      * <code>fullyQualifiedName</code>. If the model element can <strong>NOT
1370      * </strong> be found, <code>null</code> will be returned instead.
1371      *
1372      * @param resourceSet        the resource set to search in
1373      * @param fullyQualifiedName the fully qualified name of the element to search for.
1374      * @param separator          the PSM separator used for qualifying the name (example ".").
1375      * @param modelName          a flag indicating whether or not a search shall be performed
1376      *                           using the fully qualified model name or fully qualified PSM
1377      *                           name.
1378      * @return the found model element
1379      */
1380     public static Object findByFullyQualifiedName(
1381         final ResourceSet resourceSet,
1382         final String fullyQualifiedName,
1383         final String separator,
1384         final boolean modelName)
1385     {
1386         if (resourceSet==null || StringUtils.isBlank(fullyQualifiedName) || StringUtils.isBlank(separator))
1387         {
1388             return null;
1389         }
1390         Object modelElement;
1391         modelElement =
1392             findByPredicate(
1393                 resourceSet,
1394                 new Predicate()
1395                 {
1396                     public boolean evaluate(final Object object)
1397                     {
1398                         if (object instanceof NamedElement)
1399                         {
1400                             final NamedElement element = (NamedElement)object;
1401                             final StringBuilder fullName = new StringBuilder(getPackageName(
1402                                         element,
1403                                         separator,
1404                                         modelName));
1405                             final String name = element.getName();
1406                             if (StringUtils.isNotBlank(name))
1407                             {
1408                                 String namespaceSeparator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
1409                                 if (!modelName)
1410                                 {
1411                                     namespaceSeparator = separator;
1412                                 }
1413                                 fullName.append(namespaceSeparator);
1414                                 fullName.append(name);
1415                             }
1416                             return fullName.toString().equals(fullyQualifiedName);
1417                         }
1418                         return false;
1419                     }
1420                 });
1421         return modelElement;
1422     }
1423 
1424     /**
1425      * Multiplicity can be expressed as Value. String, integer... This method
1426      * parses it. MD11.5 uses string, and RSM integers.
1427      *
1428      * @param multValue a ValueSpecification, which needs to be parsed
1429      * @param type ClassifierFacade type used to determine default lower multiplicity (primitive=1, wrapped=0)
1430      * @param defaultMultiplicity from ClassifierFacadeLogic.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)
1431      * @return the parsed integer. Defaults to 1.
1432      */
1433     public static int parseLowerMultiplicity(final ValueSpecification multValue, final ClassifierFacade type, final String defaultMultiplicity)
1434     {
1435         int value = 1;
1436         if (multValue == null)
1437         {
1438             if (type.isWrappedPrimitive())
1439             {
1440                 value = 0;
1441             }
1442             else if (!type.isPrimitive())
1443             {
1444                 if (StringUtils.isNotBlank(defaultMultiplicity) && (defaultMultiplicity.charAt(0) == '0'))
1445                 {
1446                     value = 0;
1447                 }
1448                 else
1449                 {
1450                     value = 1;
1451                 }
1452             }
1453             // Defaults to 1 if a Primitive
1454         }
1455         else
1456         {
1457             value = parseMultiplicity(multValue, Integer.parseInt(defaultMultiplicity));
1458         }
1459         return value;
1460     }
1461 
1462     /**
1463      * Multiplicity can be expressed as Value. String, integer... This method
1464      * parses it. MD11.5 uses string, and RSM integers.
1465      *
1466      * @param multValue a ValueSpecification, which needs to be parsed
1467      * @param defaultValue when null: 1 for upper multiplicity, 0 for lower multiplicity.
1468      * @return the parsed integer. Defaults to 1.
1469      */
1470     public static int parseMultiplicity(final ValueSpecification multValue, final int defaultValue)
1471     {
1472         int value = defaultValue;
1473         if (multValue != null)
1474         {
1475             if (multValue instanceof LiteralInteger)
1476             {
1477                 final LiteralInteger litInt = (LiteralInteger)multValue;
1478                 value = litInt.getValue();
1479             }
1480             else if (multValue instanceof LiteralUnlimitedNatural)
1481             {
1482                 final LiteralUnlimitedNatural litInt = (LiteralUnlimitedNatural)multValue;
1483                 value = litInt.getValue();
1484             }
1485 
1486             else if (multValue instanceof LiteralString)
1487             {
1488                 final LiteralString litStr = (LiteralString)multValue;
1489                 final String multString = litStr.getValue();
1490                 if ("*".equals(multString))
1491                 {
1492                     value = LiteralUnlimitedNatural.UNLIMITED;
1493                 }
1494                 else
1495                 {
1496                     value = Integer.parseInt(multString);
1497                 }
1498             }
1499             else
1500             {
1501                 // Construct a String with the named element and default value
1502                 String multString = multValue.toString();
1503                 String forValue = "";
1504                 // property owns the upper/lower value OpaqueExpression which owns the multiplicity value body
1505                 final Element element = multValue.getOwner();
1506                 if (element instanceof Property)
1507                 {
1508                     final Property property = (Property)element;
1509                     forValue = " in property " + property.getQualifiedName();
1510                 }
1511                 if (multValue instanceof OpaqueExpression)
1512                 {
1513                     final OpaqueExpression expression = (OpaqueExpression)multValue;
1514                     final EList<String> bodies = expression.getBodies();
1515                     if (bodies != null && !bodies.isEmpty())
1516                     {
1517                         multString = bodies.get(0);
1518                     }
1519                 }
1520                 LOGGER.error("Invalid multiplicity value" + forValue + ", using default " + defaultValue + ": " + multString);
1521             }
1522         }
1523         /*if (logger.isDebugEnabled())
1524         {
1525             logger.debug("Parsing multiplicity: intValue = " + value + " value: " + multValue);
1526         }*/
1527         return value;
1528     }
1529 
1530     /**
1531      * There is an issue with EMF / XMI about tag value name (there should not be any @ or . inside)
1532      * This method checks whether <code>tagValueName</code> can be seen as <code>requestedName</code>.
1533      * <li>We compare them either:
1534      *   without name transformation
1535      *   removing initial '@' and replacing '.' by '_' (rsm / emf-uml2 profile)
1536      *   replacing initial '@' with '_' and removing '.' (EMF Normalization, RSM migration of MD 9.5 profiles)
1537      * EMF normalization (for MD11.5 export)
1538      *
1539      * @param requestedName
1540      * @param tagValueName
1541      * @return Equals
1542      */
1543     public static boolean doesTagValueNameMatch(
1544         final String requestedName,
1545         final String tagValueName)
1546     {
1547         if (StringUtils.isBlank(requestedName) || StringUtils.isBlank(tagValueName))
1548         {
1549             return false;
1550         }
1551         boolean result = requestedName.equals(tagValueName);
1552         if (!result)
1553         {
1554             if (requestedName.charAt(0) == '@')
1555             {
1556                 // let's try rsm guess
1557                 String rsmName = requestedName.substring(1);
1558                 rsmName =
1559                     rsmName.replace(
1560                         '.',
1561                         '_');
1562                 result = rsmName.equals(tagValueName);
1563                 if (!result)
1564                 {
1565                     // let's try emf normalization
1566                     final String emfName = EMFNormalizer.getEMFName(requestedName);
1567                     result = emfName.equals(tagValueName);
1568                 }
1569                 /*}
1570                 if (!result && tagValueName.startsWith("@"))
1571                 {
1572                     // let's try rsm guess
1573                     String rsmName = tagValueName.substring(1);
1574                     rsmName =
1575                         rsmName.replace(
1576                             '.',
1577                             '_');
1578                     result = requestedName.equals(rsmName);
1579                     if (!result)
1580                     {
1581                         // let's try emf normalization
1582                         String emfName = EMFNormalizer.getEMFName(tagValueName);
1583                         result = requestedName.equals(emfName);
1584                     }
1585                 }*/
1586                 // RSM converts @andromda.value to _andromdavalue when upgrading MD 9.5 profile
1587                 if (!result)
1588                 {
1589                     rsmName =
1590                         '_' + StringUtils.remove(requestedName.substring(1), '.');
1591                     result = rsmName.equals(tagValueName);
1592                 }
1593             }
1594             else
1595             {
1596                 // MD converts to _andromdavalue when exporting to EMF UML2 (v2.x) XMI and
1597                 // the requestValue uses andromda_value
1598                 final String emfName = '_' + StringUtils.remove(requestedName, '_');
1599                 result = emfName.equals(tagValueName);
1600             }
1601         }
1602         return result;
1603     }
1604 
1605     // hack to use a protected method
1606     private static class EMFNormalizer
1607         extends UML2Util
1608     {
1609         public static String getEMFName(final String name)
1610         {
1611             return getValidJavaIdentifier(name);
1612         }
1613     }
1614 
1615     // Sort Attributes and AssociationEnds
1616     @SuppressWarnings("unused")
1617     private static class PropertyComparator implements Comparator<Property>
1618     {
1619         private static final long serialVersionUID = 1L;
1620         public int compare(final Property property1, final Property property2)
1621         {
1622             return property1.getName().compareTo(property2.getName());
1623         }
1624     }
1625 }