UmlUtilities.java
package org.andromda.metafacades.emf.uml22;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.metafacade.MetafacadeConstants;
import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.UMLProfile;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.LiteralInteger;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.OpaqueExpression;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Slot;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.ValueSpecification;
import org.eclipse.uml2.uml.resource.UMLResource;
/**
* Contains utilities for the Eclipse/UML2 metafacades.
*
* @author Steve Jerman
* @author Chad Brandon
* @author Wouter Zoons
* @author Bob Fields
*/
public class UmlUtilities
{
/**
* The logger instance.
*/
private static final Logger LOGGER = Logger.getLogger(UmlUtilities.class);
private static List<Package> models = new ArrayList<Package>();
/**
* Utility method to return ALL loaded models, populated by RepositoryFacade
* @return models
*/
public static List<Package> getModels()
{
return UmlUtilities.models;
}
/**
* @param resources
*/
public static void setModels(final List<Package> resources)
{
models = Collections.synchronizedList(resources);
}
/**
* @param resource
*/
public static void addModel(final Package resource)
{
models.add(resource);
}
/**
* @param resource
*/
public static void removeModel(final Package resource)
{
models.remove(resource);
}
/**
* A transformer which transforms:
* <ul>
* <li>each property in an attribute or an association end to AssociationEnd or Attribute</li>
* <li>each slot in an attribute link or a link end to LinkEnd or AttributeLink</li>
* <li>each instance specification in an object instance or a link instance</li>
* </ul>
* This is needed because UML2 is an API in which there is no conceptual difference between
* fundamentally different elements (see list above); which makes it harder to map to metafacades
* geared towards UML 1.4
*/
protected static final Transformer ELEMENT_TRANSFORMER =
new Transformer()
{
public Object transform(final Object element)
{
final Object transformedObject;
if (element instanceof Property)
{
final Property property = (Property)element;
if (property instanceof AssociationEnd || property instanceof Attribute)
{
transformedObject = property;
}
else if (property.getAssociation() == null)
{
transformedObject = new AttributeImpl(property);
}
else
{
transformedObject = new AssociationEndImpl(property);
}
if (LOGGER.isDebugEnabled() && property.getName() != null && !property.getName().startsWith("andromda"))
{
LOGGER.debug("UMLUtilities.transform " + property.getName() + " "
+ property.getType().getName() + " " + property + " " + transformedObject);
}
}
else if (element instanceof Slot)
{
final Slot slot = (Slot)element;
// TODO: This mixes uml2 types and metafacade types, uml2 cannot be instanceof mf type
if (slot instanceof LinkEnd || slot instanceof AttributeLink)
{
transformedObject = slot;
}
else if (this.transform(slot.getDefiningFeature()) instanceof Attribute)
{
transformedObject = new AttributeLinkImpl(slot);
}
else
{
transformedObject = new LinkEndImpl(slot);
}
}
else if (element instanceof InstanceSpecification)
{
final InstanceSpecification instanceSpecification = (InstanceSpecification)element;
if (instanceSpecification instanceof LinkInstance ||
instanceSpecification instanceof ObjectInstance ||
instanceSpecification instanceof EnumerationLiteral)
{
transformedObject = instanceSpecification;
}
else if (!instanceSpecification.getClassifiers().isEmpty() &&
instanceSpecification.getClassifiers().iterator().next() instanceof org.eclipse.uml2.uml.Class)
{
transformedObject = new ObjectInstanceImpl(instanceSpecification);
}
else
{
transformedObject = new LinkInstanceImpl(instanceSpecification);
}
}
else
{
transformedObject = element;
}
return transformedObject;
}
};
private static final Map<String,List<EObject>> ALL_META_OBJECTS_CACHE =
Collections.synchronizedMap(new HashMap<String,List<EObject>>());
/**
* List all meta objects instances of a given meta class It's a way to
* achieve refAllOfType method in a JMI implementation. Please take care of the
* fact that properties are not transformed here.
*
* @param metaClass The meta class we're looking for its instances
* @param models The models where we're searching
* @return a list of objects owned by model, instance of metaClass
*/
public static List<? extends EObject> getAllMetaObjectsInstanceOf(
final Class metaClass,
final List<Package> models)
{
if (metaClass==null)
{
return new ArrayList<EObject>();
}
List<EObject> metaObjects = ALL_META_OBJECTS_CACHE.get(metaClass.getCanonicalName());
if (metaObjects == null)
{
metaObjects = new ArrayList<EObject>();
for (final Package model : models)
{
if (model!=null)
{
//for (Object metaObject : model.eAllContents())
for (final Iterator<EObject> it = model.eAllContents(); it.hasNext();)
{
final EObject metaObject = it.next();
if (metaClass.isInstance(metaObject))
{
metaObjects.add(metaObject);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("getAllMetaObjectsInstanceOf class: " + metaClass.getCanonicalName() + " " + metaClass.getClass() + " Found: " + metaObject.getClass());
}
}
}
}
}
}
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("getAllMetaObjectsInstanceOf class: " + metaClass.getCanonicalName() + ' ' + metaClass.getClass() + " Found: " + metaObjects.size());
}
ALL_META_OBJECTS_CACHE.put(metaClass.getCanonicalName(), metaObjects);
return metaObjects;
}
/**
* List all meta objects instances of a given meta class It's a way to
* achieve refAllOfType method in a JMI implementation. Please take care of the
* fact that properties are not transformed here.
*
* @param metaClass The meta class we're looking for its instances
* @param model The model where we're searching
* @return a list of objects owned by model, instance of metaClass
*/
private static List getAllMetaObjectsInstanceOf(
final Class metaClass,
final Package model)
{
if (metaClass==null)
{
return new ArrayList<EObject>();
}
final List<EObject> metaObjects = new ArrayList<EObject>();
if (model!=null)
{
//for (Object metaObject : model.eAllContents())
for (final Iterator<EObject> it = model.eAllContents(); it.hasNext();)
{
final EObject metaObject = it.next();
if (metaClass.isInstance(metaObject))
{
metaObjects.add(metaObject);
}
}
}
return metaObjects;
}
/**
* This clears the meta objects cache. Even though this
* isn't the "cleanest" way to handle things, we need this
* for performance reasons (getAllMetaObjectsInstanceOf is WAY
* to slow otherwise).
*/
public static void clearAllMetaObjectsCache()
{
ALL_META_OBJECTS_CACHE.clear();
}
/**
* Get the comments for a UML Element. This will be a string with each
* comment separated by a 2 newlines.
*
* @param element
* @return concatenated string
*/
public static String getComment(final Element element)
{
if (element==null)
{
return null;
}
final StringBuilder commentString = new StringBuilder();
final Collection<Comment> comments = element.getOwnedComments();
for (final Comment comment : comments)
{
if (commentString.length()>0)
{
commentString.append("\n\n");
}
commentString.append(comment.getBody());
}
return cleanText(commentString.toString());
}
/**
* Gets rid of all excess whitespace.
*
* @param text the text from which to remove the white space.
* @return the cleaned text.
*/
public static String cleanText(String text)
{
if (StringUtils.isBlank(text))
{
return text;
}
text =
text.replaceAll(
"[\\t\\n]*",
"");
text =
text.replaceAll(
"\\s+",
" ");
return text;
}
/**
* returns all owned properties of the given classifier with the right type:
* attribute if <code>isAssociation</code> is false and association end
* otherwise.
*
* @param classifier the classifier to inspect.
* @param follow whether to follow inheritance hierarchy upward.
* @param isAssociation Retrieve only AssociationEnd properties.
* @return all owned properties of the given classifier with the right type.
*/
public static List<Property> getOwnedProperty(
final Classifier classifier,
final boolean follow,
final boolean isAssociation)
{
if (classifier==null)
{
return new ArrayList<Property>();
}
final Map<String, Property> attributeMap = new LinkedHashMap<String, Property>(); // preserve ordering
final List<NamedElement> members = new ArrayList<NamedElement>(classifier.getOwnedMembers());
if (follow)
{
members.addAll(classifier.getInheritedMembers());
}
for (NamedElement nextCandidate : members)
{
if (nextCandidate instanceof Property)
{
final Property property = (Property)nextCandidate;
/*if (attributeMap.containsKey(property.getName()))
{
logger.warn(
"Attribute with this name has already been registered on " +
classifier.getQualifiedName() + ": " + property.getName());
}*/
if (isAssociation && property.getAssociation() != null)
{
/*if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Association found for " + classifier.getName() + ": " + property.getName());
}*/
// property represents an association end
attributeMap.put(
property.getName(),
property);
}
else if (!isAssociation && property.getAssociation() == null)
{
/*if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Attribute found for " + classifier.getName() + ": " + property.getName());
}*/
// property represents an attribute
attributeMap.put(
property.getName(),
property);
}
}
}
return new ArrayList<Property>(attributeMap.values());
}
/**
* Gets a collection containing all of the attributes for this
* class/interface. Superclass properties will included if
* <code>follow</code> is true. Overridden properties will be omitted.
*
* @param classifier the UML class instance from which to retrieve all properties
* @param follow whether or not the inheritance hierarchy should be followed upward
* @return all retrieved attributes. No associations or enumerations.
*/
public static List<Property> getAttributes(
final Classifier classifier,
final boolean follow)
{
final List<Property> attributeList = getOwnedProperty(classifier, follow, false);
CollectionUtils.transform(
attributeList,
ELEMENT_TRANSFORMER);
//Collections.sort(attributeList, new PropertyComparator());
if (LOGGER.isDebugEnabled())
{
for (Property property : attributeList)
{
if (!classifier.getQualifiedName().startsWith("andromda") && LOGGER.isDebugEnabled())
{
LOGGER.debug("UMLUtilities.getAttributes " + classifier.getQualifiedName()
+ " " + property.getName() + " " + property.getType().getName());
}
}
}
return attributeList;
}
/**
* Returns <code>true</code> if the given association end's type is an ancestor of the classifier, or just the
* argument classifier if follow is <code>false</code>.
* @param classifier
* @param property this method returns false if this argument is not an association end
* @param follow
* @return isAssociationEndAttachedToType((Classifier)parent, property, follow)
*/
public static boolean isAssociationEndAttachedToType(
final Classifier classifier,
final Property property,
final boolean follow)
{
boolean attachedToType = false;
if (property.getAssociation() != null)
{
attachedToType = classifier.equals(property.getType());
if (follow && !attachedToType)
{
for (Classifier parent : classifier.getGenerals())
{
//if (parent instanceof Classifier)
//{
attachedToType =
isAssociationEndAttachedToType(
parent,
property,
follow);
//}
}
}
if (LOGGER.isDebugEnabled() && attachedToType)
{
LOGGER.debug("isAssociationEndAttachedToType " + classifier.getQualifiedName() + ' ' + property + ' ' + property.getQualifiedName() + ' ' + property.getAssociation() + ' ' + property.getAssociationEnd() + ' ' + attachedToType);
}
}
return attachedToType;
}
/**
* Gets a collection containing all of the associationEnds for this
* class/interface. Superclass properties will be included if
* <code>follow</code> is true. Overridden properties will be omitted.
* Finds all Property classes in model and iterates through to see which are of type classifier.
* <p/>
* cejeanne: Changed the way association ends are found.
*
* @param classifier the UML class instance from which to retrieve all properties
* @param follow whether or not the inheritance hierarchy should be followed
* @return all retrieved attributes.
*/
public static List<Property> getAssociationEnds(
final Classifier classifier,
final boolean follow)
{
final Set<Property> associationEnds = new LinkedHashSet<Property>();
if (classifier==null)
{
return Collections.emptyList();
}
associationEnds.addAll(getOwnedProperty(classifier, follow, true));
CollectionUtils.transform(associationEnds, new Transformer()
{
public Object transform(final Object input) {
return getOppositeProperty((Property)input);
}
});
// TODO: Iterate through all referenced models, not just the model containing this classifier.
// TODO: UML2 bug? getModel returns null because UMLUtil.getOwningElement getBaseElement(owner.eContainer()) changes owningElement to null
final Package modelPackage = UmlUtilities.findModel(classifier);
/*if (modelPackage==null)
{
logger.error(classifier + " getModel was null: " + classifier.getOwner() + " " + classifier.getQualifiedName());
Element classifierOwner = classifier.getOwner();
Element owner = null;
while (classifierOwner!=null)
{
owner = classifierOwner;
classifierOwner = owner.getOwner();
}
// Find the last owner in the chain... Top level package.
modelPackage = (Package) owner;
}*/
final List<Property> allProperties = getAllMetaObjectsInstanceOf(
Property.class,
modelPackage);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("getAssociationEnds " + classifier.getQualifiedName() + ": getAllMetaObjectsInstanceOf=" + allProperties.size());
}
for (Property property : allProperties)
{
// only treat association ends, ignore attributes
if (property.getAssociation() != null && isAssociationEndAttachedToType(
classifier,
property,
follow))
{
/*int ownedSize = property.getAssociation().getOwnedEnds().size();
if (ownedSize==1)
{
associationEnds.add(property.getAssociation().getOwnedEnds().get(0));
logger.debug("getAssociationEnds " + classifier.getQualifiedName() + ": addedOwnedAssociationEnd " + property + " " + property.getType() + " " + property.getAssociation() + " AssociationEnd=" + property.getAssociationEnd() + " Qualifiers=" + property.getQualifiers() + " Opposite=" + property.getOpposite());
}
else if (ownedSize==0 || ownedSize>1)
{
logger.error("associationEnds ownedEnds=" + ownedSize);
}
else
{*/
// TODO: associationEnds always show up as non-navigable because the association property (not the end) is added.
associationEnds.add(property);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("getAssociationEnds " + classifier.getQualifiedName() + ": addedAssociation " + property + ' ' + property.getType() + ' ' + property.getAssociation() + " AssociationEnd=" + property.getAssociationEnd() + " OwnedEnds=" + property.getAssociation().getOwnedEnds() + " Qualifiers=" + property.getQualifiers() + " Navigable=" + property.isNavigable());
}
/* }*/
}
}
CollectionUtils.transform(
associationEnds,
ELEMENT_TRANSFORMER);
return new ArrayList<Property>(associationEnds);
}
/**
* Returns <code>true</code> if and only if the given operation would have an identical signature.
* This means:
* <ul>
* <li>the same name</li>
* <li>the same number of parameters</li>
* <li>matching parameter types (in that very same order)</li>
* </ul>
* @param first
* @param second
* @return isEqual(firstParameter.getType(), secondParameter.getType())
*/
public static boolean isSameSignature(
final Operation first,
final Operation second)
{
boolean sameSignature = true;
// test name
if (isEqual(
first.getName(),
second.getName()))
{
final List<Parameter> firstParameters = first.getOwnedParameters();
final List<Parameter> secondParameters = second.getOwnedParameters();
// test number of parameters
if (firstParameters.size() == secondParameters.size())
{
for (int i = 0; i < firstParameters.size() && sameSignature; i++)
{
final Parameter firstParameter = firstParameters.get(i);
final Parameter secondParameter = secondParameters.get(i);
// test each parameter's type
sameSignature =
isEqual(
firstParameter.getType(),
secondParameter.getType());
}
}
else
{
sameSignature = false;
}
}
else
{
sameSignature = false;
}
return sameSignature;
}
/**
* Returns <code>true</code> if and only if both arguments are equal, this method handles potential
* incoming <code>null</code> values.
*/
private static boolean isEqual(
final Object first,
final Object second)
{
return first == null ? second == null : first.equals(second);
}
/**
* Retrieves all specializations of the given <code>classifier</code>
* instance.
*
* @param classifier the classifier from which to retrieve the specializations.
* @return all specializations.
*/
public static List<Classifier> getSpecializations(final Classifier classifier)
{
final List<Classifier> specials = new ArrayList<Classifier>();
if (classifier==null)
{
return specials;
}
/*for(DirectedRelationship gen :
classifier.getTargetDirectedRelationships(UMLPackage.eINSTANCE.getGeneralization()))
{
for(Element elt : gen.getSources())
{
if (elt instanceof Classifier)
specials.add((Classifier)elt);
}
}*/
for (final TreeIterator<EObject> iterator = EcoreUtil.getRootContainer(classifier).eAllContents(); iterator.hasNext();)
{
final EObject object = iterator.next();
if (object instanceof Generalization)
{
final Generalization generalization = (Generalization)object;
if (generalization.getGeneral().equals(classifier))
{
specials.add(generalization.getSpecific());
}
iterator.prune();
}
}
return specials;
}
/**
* Retrieves the names of the stereotypes for the given <code>element</code>
*
* @param element the element for which to retrieve the stereotypes.
* @return all stereotype names
*/
public static List<String> getStereotypeNames(final Element element)
{
final List<String> names = new ArrayList<String>();
if (element==null)
{
return names;
}
final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
if (stereotypes != null)
{
for (Stereotype stereotype : stereotypes)
{
names.add(stereotype.getName());
}
}
return names;
}
/**
* Indicates whether or not the given <code>element</code> contains a
* stereotype with the given <code>stereotypeName</code>.
*
* @param element the element instance.
* @param stereotypeName the name of the stereotype
* @return true/false
*/
public static boolean containsStereotype(
final Element element,
final String stereotypeName)
{
if (element==null || StringUtils.isBlank(stereotypeName))
{
return false;
}
final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
boolean hasStereotype = StringUtils.isNotBlank(stereotypeName) && stereotypes != null &&
!stereotypes.isEmpty();
if (hasStereotype)
{
class StereotypeFilter
implements Predicate
{
public boolean evaluate(final Object object)
{
boolean valid;
final Stereotype stereotype = (Stereotype)object;
final String name = StringUtils.trimToEmpty(stereotype.getName());
valid = stereotypeName.equalsIgnoreCase(name);
for (Classifier itStereo : stereotype.allParents())
{
valid = valid || StringUtils.trimToEmpty(itStereo.getName()).equalsIgnoreCase(stereotypeName);
}
return valid;
}
}
hasStereotype =
CollectionUtils.find(
stereotypes,
new StereotypeFilter()) != null;
}
if (LOGGER.isDebugEnabled() && hasStereotype)
{
if (element instanceof NamedElement)
{
LOGGER.debug(
((NamedElement)element).getQualifiedName() + " has stereotype <<" + stereotypeName + ">> : " +
hasStereotype);
}
else
{
LOGGER.debug(element.toString() + " has stereotype <<" + stereotypeName + ">> : " + hasStereotype);
}
}
return hasStereotype;
}
/**
* @deprecated old way to handle tag values
* Note: The uml profile defines it as "AndroMdaTags" and not "AndroMDATags"
* Stores the tagged values that may be applied to an element.
*/
@Deprecated
private static final String TAGGED_VALUES_STEREOTYPE = "AndroMdaTags";
/**
* Retrieves the TagDefinitions for the given element.
*
* @param element the element from which to retrieve the tagged values.
* @return the collection of {@link TagDefinition} instances.
*/
public static Collection<TagDefinition> getTaggedValue(final Element element)
{
final Collection<TagDefinition> tags = new ArrayList<TagDefinition>();
if (element==null)
{
return tags;
}
String elementName = "";
if (element instanceof NamedElement)
{
elementName = ((NamedElement)element).getName();
}
else
{
elementName = element.toString();
}
/*if (logger.isDebugEnabled())
{
logger.debug("Searching Tagged Values for " + elementName);
}*/
final Collection<Stereotype> stereotypes = element.getAppliedStereotypes();
for (final Stereotype stereo : stereotypes)
{
if (TAGGED_VALUES_STEREOTYPE.equals(stereo.getName()))
{
final List tagNames = (List)element.getValue(
stereo,
"TagName");
final List tagValues = (List)element.getValue(
stereo,
"TagValue");
for (int ctr = 0; ctr < tagValues.size(); ctr++)
{
tags.add(new TagDefinitionImpl(
tagNames.get(ctr).toString(),
tagValues.get(ctr)));
}
}
else if (element.hasValue(
stereo,
"value"))
{
final Object value = element.getValue(
stereo,
"value");
tags.add(new TagDefinitionImpl(
stereo.getName(),
value));
}
else
{
for (final Property tagProperty : getAttributes(stereo, true))
{
final String tagName = tagProperty.getName();
// Some metafacades depend on an actual returned value. hasValue returns nothing if taggedValue=default for the attribute.
if (!tagName.startsWith("base$") && element.hasValue(stereo, tagName))
{
// Obtain its value
final Object tagValue = element.getValue(stereo, tagName);
if (tagValue instanceof Collection)
{
final Collection tagValues = (Collection)tagValue;
if (!tagValues.isEmpty())
{
final Collection tagValuesInString =
CollectionUtils.collect(
tagValues,
new Transformer()
{
public Object transform(final Object object)
{
return getTagValueAsString(object);
}
});
final TagDefinition tagDefinition = new TagDefinitionImpl(tagName, tagValuesInString);
tags.add(tagDefinition);
}
}
else
{
final String tagString = getTagValueAsString(tagValue);
if (!StringUtils.isBlank(tagString) && !"default".equalsIgnoreCase(tagString))
{
final TagDefinition tagDefinition =
new TagDefinitionImpl(tagName, tagString);
tags.add(tagDefinition);
}
}
}
}
}
}
if (LOGGER.isDebugEnabled() && !tags.isEmpty())
{
LOGGER.debug("Found " + tags.size() + " tagged values for " + elementName);
}
return tags;
}
/**
* The toString() method isn't suitable to transform the values of tagValue as String.
* @param tagValue
* @return the tag value as a string.
*/
static String getTagValueAsString(final Object tagValue)
{
String valueAsString = null;
if (tagValue != null)
{
valueAsString = tagValue.toString();
if (tagValue instanceof ValueSpecification)
{
final ValueSpecification literal = (ValueSpecification)tagValue;
valueAsString = literal.stringValue();
}
else if (tagValue instanceof NamedElement)
{
final NamedElement instance = (NamedElement)tagValue;
valueAsString = instance.getName();
}
}
return valueAsString;
}
/**
* Attempts to find the applied stereotype with the given name on the given
* <code>element</code>. First tries to find it with the fully qualified
* name, and then tries it with just the name.
* @param element
* @param name
* the name of the stereotype
* @return the found stereotype or null if not found.
*/
public static Stereotype findAppliedStereotype(
final Element element,
final String name)
{
if (element==null || StringUtils.isBlank(name))
{
return null;
}
Stereotype foundStereotype = element.getAppliedStereotype(name);
if (foundStereotype == null)
{
final EList<Stereotype> stereotypes = element.getAppliedStereotypes();
if (stereotypes != null)
{
for (Stereotype stereotype : stereotypes)
{
if (stereotype.getName().equals(name))
{
foundStereotype = stereotype;
break;
}
}
}
}
return foundStereotype;
}
/**
* Attempts to find the applicable stereotype with the given name on the
* given <code>element</code>. First tries to find it with the fully
* qualified name, and then tries it with just the name.
* @param element
* @param name the name of the stereotype
* @return the found stereotype or null if not found.
*/
public static Stereotype findApplicableStereotype(
final Element element,
final String name)
{
if (element==null || StringUtils.isBlank(name))
{
return null;
}
Stereotype foundStereotype = element.getApplicableStereotype(name);
if (foundStereotype == null)
{
final EList<Stereotype> stereotypes = element.getApplicableStereotypes();
if (stereotypes != null)
{
for (Stereotype stereotype : stereotypes)
{
if (stereotype.getName().equals(name))
{
foundStereotype = stereotype;
break;
}
}
}
}
return foundStereotype;
}
/**
* Retrieves the serial version UID by reading the tagged value
* {@link UMLProfile#TAGGEDVALUE_SERIALVERSION_UID} of the
* <code>classifier</code>.
*
* @param classifier the classifier to be inspected.
* @return the serial version UID of the classifier. Returns
* <code>null</code> if the tagged value cannot be found.
*/
static String getSerialVersionUID(final ClassifierFacade classifier)
{
ExceptionUtils.checkNull(
"classifer",
classifier);
final String serialVersionString = (String)classifier.findTaggedValue(UMLProfile.TAGGEDVALUE_SERIALVERSION_UID);
return StringUtils.trimToNull(serialVersionString);
}
/**
* Gets the opposite end of the given <code>associationEnd</code> if the
* property is indeed an association end, otherwise returns null.
*
* @param associationEnd the association end from which to retrieve the opposite end.
* @return the opposite association end or null.
*/
public static Property getOppositeProperty(final Property associationEnd)
{
if (associationEnd==null)
{
return null;
}
Property opposite = associationEnd.getOpposite();
if (opposite == null)
{
final Association association = associationEnd.getAssociation();
if (association != null)
{
final Collection<Property> ends = association.getMemberEnds();
for (final Property end : ends)
{
if (end != null && !associationEnd.equals(end))
{
opposite = end;
break;
}
}
}
}
return opposite;
}
/**
* Gets the opposite end of the given <code>associationEnd</code> if the
* property is indeed an association end, otherwise returns null.
*
* @param associationEnd the association end from which to retrieve the opposite end.
* @return the opposite association end or null.
*/
public static AssociationEnd getOppositeAssociationEnd(final Property associationEnd)
{
if (associationEnd==null)
{
return null;
}
return new AssociationEndImpl(getOppositeProperty(associationEnd));
}
/**
* Finds and returns the first model element having the given
* <code>name</code> in the <code>modelPackage</code>, returns
* <code>null</code> if not found.
* @param resourceSet
* @param pred
*
* @return the found model element.
*/
public static Object findByPredicate(
final ResourceSet resourceSet,
final Predicate pred)
{
Object modelElement = null;
if (resourceSet==null || pred==null)
{
return modelElement;
}
for (Resource resource : resourceSet.getResources())
{
final Package model =
(Package)EcoreUtil.getObjectByType(
resource.getContents(),
UMLPackage.eINSTANCE.getPackage());
if (model != null)
{
for (final TreeIterator<EObject> elementIterator = model.eAllContents();
elementIterator.hasNext() && modelElement == null;)
{
final Object object = elementIterator.next();
if (pred.evaluate(object))
{
modelElement = object;
}
}
}
if (modelElement != null)
{
break;
}
}
return modelElement;
}
/**
* Find the Model of a resource (UML2 Model)
* @param resource
* @return (Model)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.eINSTANCE.getEObject())
*/
public static Package findModel(final UMLResource resource)
{
if (resource==null)
{
return null;
}
final Package model = (Package)EcoreUtil.getObjectByType(
resource.getContents(),
EcorePackage.eINSTANCE.getEObject());
if (model==null)
{
LOGGER.error("getModel was null: " + resource);
}
else if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Model found: " + model);
}
return model;
}
/**
* Find the Model of a resource (UML2 Model)
* @param element
* @return (Model)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.eINSTANCE.getEObject())
*/
public static Package findModel(final Element element)
{
if (element==null)
{
return null;
}
Package modelPackage = element.getModel();
if (modelPackage==null)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.error("getModel was null: " + element + " OWNER: " + element.getOwner());
}
Element classifierOwner = element.getOwner();
Element owner = null;
while (classifierOwner!=null)
{
owner = classifierOwner;
classifierOwner = owner.getOwner();
}
// Find the last owner in the chain... Top level package.
modelPackage = (Package) owner;
}
return modelPackage;
}
/**
* Constructs the package name for the given <code>metaObject</code>,
* separating the package name by the given <code>separator</code>.
*
* @param metaObject the Model Element
* @param separator the PSM namespace separator, ignored if <code>modelName</code> is <code>true</code>
* @param modelName true/false on whether or not to get the model package name
* instead of the PSM package name.
* @return the package name.
*/
public static String getPackageName(
final NamedElement metaObject,
final String separator,
final boolean modelName)
{
if (metaObject==null || StringUtils.isBlank(separator))
{
return null;
}
final StringBuilder buffer = new StringBuilder();
final String usedSeparator = modelName ? MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR : separator;
for (Namespace namespace = metaObject.getNamespace(); namespace != null;
namespace = namespace.getNamespace())
{
if (namespace instanceof Package && !(namespace instanceof Model) && !(namespace instanceof Profile))
{
if (buffer.length() != 0)
{
buffer.insert(
0,
usedSeparator);
}
buffer.insert(
0,
namespace.getName());
}
}
String packageName = buffer.toString();
/* // TODO Remove Model Name from the package hierarchy
if (StringUtils.isBlank(packageName))
{
packageName =
getPackageName(
metaObject.getOwner(),
separator,
modelName);
}
if (StringUtils.isBlank(packageName) && metaObject instanceof Classifier)
{
packageName = ((Classifier)metaObject).getPackage().getQualifiedName();
}
// Allow for empty namespace - new after UML2 v3
if (StringUtils.isBlank(packageName))
{
Package modelPackage = metaObject.getModel();
String name = metaObject.getNearestPackage().getQualifiedName();
if (modelPackage instanceof Model)
{
// Remove model name from the front of Package Name string
String model = modelPackage.getName();
if (model != null && name.indexOf(model) > -1 && (name.length() >= model.length() + separator.length() + 1))
{
packageName = name.substring(model.length() + separator.length() + 1);
}
}
}*/
if (modelName && StringUtils.isNotBlank(packageName))
{
packageName =
StringUtils.replace(
packageName,
separator,
MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
}
return packageName;
}
/**
* Returns the package name of the closest ancestor that is an instance of <code>NamedElement</code>. If no such
* ancestor exists the empty String is returned.
* <p/>
* If the argument would be an instance of <code>NamedElement</code> then this method returns that object's
* package name.
* @param metaObject
* @param separator
* @param modelName
* @return packageName
*
* @see #getPackageName(org.eclipse.uml2.uml.NamedElement, String, boolean)
*/
public static String getPackageName(
final Element metaObject,
final String separator,
final boolean modelName)
{
if (metaObject==null || StringUtils.isBlank(separator))
{
return null;
}
String packageName = null;
if (metaObject instanceof NamedElement)
{
packageName =
getPackageName(
(NamedElement)metaObject,
separator,
modelName);
}
else if (metaObject.getOwner() == null)
{
packageName = "";
}
else
{
packageName =
getPackageName(
metaObject.getOwner(),
separator,
modelName);
}
/* if (StringUtils.isBlank(packageName))
{
packageName =
getPackageName(
metaObject.getOwner(),
separator,
modelName);
}
// TODO Remove model name from the front of the FQ package name
if (StringUtils.isBlank(packageName) && metaObject instanceof Classifier)
{
packageName = ((Classifier)metaObject).getPackage().getQualifiedName();
}
if (StringUtils.isBlank(packageName))
{
packageName = metaObject.getNearestPackage().getQualifiedName();
}*/
return packageName;
}
/**
* Returns the fully-qualified name of the model element by iterating up through the getOwner hierarchy.
* @param metaObject
* @param separator
* @param modelName
* @return packageName
*/
public static String getFullyQualifiedName(
final Element metaObject,
final String separator,
final boolean modelName)
{
if (metaObject==null || StringUtils.isBlank(separator) || !(metaObject instanceof NamedElement))
{
return "";
}
final NamedElement element = (NamedElement)metaObject;
String name = element.getName();
Element owner = element.getOwner();
String ownerName = null;
while (owner != null)
{
// Don't add the top level model name(s) to the FQN, unless the model is a Package.
if (owner instanceof NamedElement && !(owner instanceof Model) && !(owner instanceof Profile))
{
ownerName = ((NamedElement)owner).getName();
name = ownerName + separator + name;
}
owner = owner.getOwner();
}
/*// remove the top level model name, so that mapping works correctly...
if (ownerName != null)
{
name = name.substring(ownerName.length() + separator.length());
}*/
return name;
}
/**
* Finds and returns the first model element having the given
* <code>name</code> in the <code>modelPackage</code>, returns
* <code>null</code> if not found.
*
* @param rs the resource set to search in
* @param name the name to find.
* @return the found model element.
*/
public static Object findByName(
final ResourceSet rs,
final String name)
{
if (rs==null || StringUtils.isBlank(name))
{
return null;
}
Object modelElement = null;
if (StringUtils.isNotBlank(name))
{
modelElement =
findByPredicate(
rs,
new Predicate()
{
public boolean evaluate(final Object object)
{
if (object instanceof NamedElement)
{
return StringUtils.trimToEmpty(((NamedElement)object).getName()).equals(name);
}
return false;
}
});
}
return modelElement;
}
/**
* Finds a given model element in the model having the specified
* <code>fullyQualifiedName</code>. If the model element can <strong>NOT
* </strong> be found, <code>null</code> will be returned instead.
*
* @param resourceSet the resource set to search in
* @param fullyQualifiedName the fully qualified name of the element to search for.
* @param separator the PSM separator used for qualifying the name (example ".").
* @param modelName a flag indicating whether or not a search shall be performed
* using the fully qualified model name or fully qualified PSM
* name.
* @return the found model element
*/
public static Object findByFullyQualifiedName(
final ResourceSet resourceSet,
final String fullyQualifiedName,
final String separator,
final boolean modelName)
{
if (resourceSet==null || StringUtils.isBlank(fullyQualifiedName) || StringUtils.isBlank(separator))
{
return null;
}
Object modelElement;
modelElement =
findByPredicate(
resourceSet,
new Predicate()
{
public boolean evaluate(final Object object)
{
if (object instanceof NamedElement)
{
final NamedElement element = (NamedElement)object;
final StringBuilder fullName = new StringBuilder(getPackageName(
element,
separator,
modelName));
final String name = element.getName();
if (StringUtils.isNotBlank(name))
{
String namespaceSeparator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
if (!modelName)
{
namespaceSeparator = separator;
}
fullName.append(namespaceSeparator);
fullName.append(name);
}
return fullName.toString().equals(fullyQualifiedName);
}
return false;
}
});
return modelElement;
}
/**
* Multiplicity can be expressed as Value. String, integer... This method
* parses it. MD11.5 uses string, and RSM integers.
*
* @param multValue a ValueSpecification, which needs to be parsed
* @param type ClassifierFacade type used to determine default lower multiplicity (primitive=1, wrapped=0)
* @param defaultMultiplicity from ClassifierFacadeLogic.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)
* @return the parsed integer. Defaults to 1.
*/
public static int parseLowerMultiplicity(final ValueSpecification multValue, final ClassifierFacade type, final String defaultMultiplicity)
{
int value = 1;
if (multValue == null)
{
if (type.isWrappedPrimitive())
{
value = 0;
}
else if (!type.isPrimitive())
{
if (StringUtils.isNotBlank(defaultMultiplicity) && (defaultMultiplicity.charAt(0) == '0'))
{
value = 0;
}
else
{
value = 1;
}
}
// Defaults to 1 if a Primitive
}
else
{
value = parseMultiplicity(multValue, Integer.parseInt(defaultMultiplicity));
}
return value;
}
/**
* Multiplicity can be expressed as Value. String, integer... This method
* parses it. MD11.5 uses string, and RSM integers.
*
* @param multValue a ValueSpecification, which needs to be parsed
* @param defaultValue when null: 1 for upper multiplicity, 0 for lower multiplicity.
* @return the parsed integer. Defaults to 1.
*/
public static int parseMultiplicity(final ValueSpecification multValue, final int defaultValue)
{
int value = defaultValue;
if (multValue != null)
{
if (multValue instanceof LiteralInteger)
{
final LiteralInteger litInt = (LiteralInteger)multValue;
value = litInt.getValue();
}
else if (multValue instanceof LiteralUnlimitedNatural)
{
final LiteralUnlimitedNatural litInt = (LiteralUnlimitedNatural)multValue;
value = litInt.getValue();
}
else if (multValue instanceof LiteralString)
{
final LiteralString litStr = (LiteralString)multValue;
final String multString = litStr.getValue();
if ("*".equals(multString))
{
value = LiteralUnlimitedNatural.UNLIMITED;
}
else
{
value = Integer.parseInt(multString);
}
}
else
{
// Construct a String with the named element and default value
String multString = multValue.toString();
String forValue = "";
// property owns the upper/lower value OpaqueExpression which owns the multiplicity value body
final Element element = multValue.getOwner();
if (element instanceof Property)
{
final Property property = (Property)element;
forValue = " in property " + property.getQualifiedName();
}
if (multValue instanceof OpaqueExpression)
{
final OpaqueExpression expression = (OpaqueExpression)multValue;
final EList<String> bodies = expression.getBodies();
if (bodies != null && !bodies.isEmpty())
{
multString = bodies.get(0);
}
}
LOGGER.error("Invalid multiplicity value" + forValue + ", using default " + defaultValue + ": " + multString);
}
}
/*if (logger.isDebugEnabled())
{
logger.debug("Parsing multiplicity: intValue = " + value + " value: " + multValue);
}*/
return value;
}
/**
* There is an issue with EMF / XMI about tag value name (there should not be any @ or . inside)
* This method checks whether <code>tagValueName</code> can be seen as <code>requestedName</code>.
* <li>We compare them either:
* without name transformation
* removing initial '@' and replacing '.' by '_' (rsm / emf-uml2 profile)
* replacing initial '@' with '_' and removing '.' (EMF Normalization, RSM migration of MD 9.5 profiles)
* EMF normalization (for MD11.5 export)
*
* @param requestedName
* @param tagValueName
* @return Equals
*/
public static boolean doesTagValueNameMatch(
final String requestedName,
final String tagValueName)
{
if (StringUtils.isBlank(requestedName) || StringUtils.isBlank(tagValueName))
{
return false;
}
boolean result = requestedName.equals(tagValueName);
if (!result)
{
if (requestedName.charAt(0) == '@')
{
// let's try rsm guess
String rsmName = requestedName.substring(1);
rsmName =
rsmName.replace(
'.',
'_');
result = rsmName.equals(tagValueName);
if (!result)
{
// let's try emf normalization
final String emfName = EMFNormalizer.getEMFName(requestedName);
result = emfName.equals(tagValueName);
}
/*}
if (!result && tagValueName.startsWith("@"))
{
// let's try rsm guess
String rsmName = tagValueName.substring(1);
rsmName =
rsmName.replace(
'.',
'_');
result = requestedName.equals(rsmName);
if (!result)
{
// let's try emf normalization
String emfName = EMFNormalizer.getEMFName(tagValueName);
result = requestedName.equals(emfName);
}
}*/
// RSM converts @andromda.value to _andromdavalue when upgrading MD 9.5 profile
if (!result)
{
rsmName =
'_' + StringUtils.remove(requestedName.substring(1), '.');
result = rsmName.equals(tagValueName);
}
}
else
{
// MD converts to _andromdavalue when exporting to EMF UML2 (v2.x) XMI and
// the requestValue uses andromda_value
final String emfName = '_' + StringUtils.remove(requestedName, '_');
result = emfName.equals(tagValueName);
}
}
return result;
}
// hack to use a protected method
private static class EMFNormalizer
extends UML2Util
{
public static String getEMFName(final String name)
{
return getValidJavaIdentifier(name);
}
}
// Sort Attributes and AssociationEnds
@SuppressWarnings("unused")
private static class PropertyComparator implements Comparator<Property>
{
private static final long serialVersionUID = 1L;
public int compare(final Property property1, final Property property2)
{
return property1.getName().compareTo(property2.getName());
}
}
}