MetafacadeUtils.java
package org.andromda.metafacades.uml;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
/**
* A class containing utilities for metafacade manipulation.
*
* @author Chad Brandon
* @author Wouter Zoons
* @author Bob Fields
*/
public class MetafacadeUtils
{
/**
* Checks to see if the element is the specified type and if so casts it to the object and returns it, otherwise it
* returns null.
*
* @param element the element to check.
* @param type the Class type.
* @return the element has the given type or null.
*/
public static Object getElementAsType(
final Object element,
final Class type)
{
Object elementAsType = null;
if (element != null && type != null)
{
final Class elementClass = element.getClass();
if (type.isAssignableFrom(elementClass))
{
elementAsType = element;
}
}
return elementAsType;
}
/**
* Filters out the model elements from the <code>modelElements</code> collection that don't have the specified
* <code>stereotype</code>
*
* @param modelElements the model elements to filter.
* @param stereotype the stereotype that a model element must have in order to stay remain within the
* <code>modelElements</code> collection.
*/
public static void filterByStereotype(
final Collection modelElements,
final String stereotype)
{
// Should be able to type the Collection as <ModelElementFacade>, but compilation failure results.
if (StringUtils.isNotBlank(stereotype))
{
CollectionUtils.filter(
modelElements,
new Predicate()
{
public boolean evaluate(Object object)
{
return ((ModelElementFacade)object).hasStereotype(stereotype);
}
});
}
}
/**
* Filters out the model elements from the <code>modelElements</code> collection that are not of (or do not inherit
* from) the specified type <code>type</code>
*
* @param modelElements the model elements to filter.
* @param type the type of Class.
*/
public static void filterByType(
final Collection modelElements,
final Class type)
{
if (type != null)
{
CollectionUtils.filter(
modelElements,
new Predicate()
{
public boolean evaluate(Object object)
{
return type.isAssignableFrom(object.getClass());
}
});
}
}
/**
* Filters out the model elements from the <code>modelElements</code> collection that are of (or inherit from) the
* specified type <code>type</code>
*
* @param modelElements the model elements to filter.
* @param type the type of Class.
*/
public static void filterByNotType(
final Collection modelElements,
final Class type)
{
if (type != null)
{
CollectionUtils.filter(
modelElements,
new Predicate()
{
public boolean evaluate(Object object)
{
return !type.isAssignableFrom(object.getClass());
}
});
}
}
/**
* <p/>Returns a consistent name for a relation, independent from the end of the relation one is looking at. </p>
* <p/>In order to guarantee consistency with relation names, they must appear the same whichever angle (ie entity)
* that you come from. For example, if you are at Customer end of a relationship to an Address then your relation
* may appear with the name Customer-Address. But if you are in the Address entity looking at the Customer then you
* will get an error because the relation will be called Address-Customer. A simple way to guarantee that both ends
* of the relationship have the same name is merely to use alphabetical ordering. </p>
*
* @param roleName name of role in relation
* @param targetRoleName name of target role in relation
* @param separator character used to separate words
* @return uniform mapping name (in alphabetical order)
*/
public static String toRelationName(
final String roleName,
final String targetRoleName,
final String separator)
{
if (roleName.compareTo(targetRoleName) <= 0)
{
return (roleName + separator + targetRoleName);
}
return (targetRoleName + separator + roleName);
}
/**
* Sorts given metafacades by their fully qualified name.
*
* @param metafacades the collection of model elements to sort.
*/
public static void sortByFullyQualifiedName(final List metafacades)
{
Collections.sort(
metafacades,
new FullyQualifiedNameComparator());
}
/**
* Used to sort operations by <code>fullyQualifiedName</code>.
*/
private static final class FullyQualifiedNameComparator
implements Comparator
{
private final Collator collator = Collator.getInstance();
/** */
FullyQualifiedNameComparator()
{
collator.setStrength(Collator.PRIMARY);
}
public int compare(
final Object objectA,
final Object objectB)
{
final ModelElementFacade a = (ModelElementFacade)objectA;
final ModelElementFacade b = (ModelElementFacade)objectB;
return collator.compare(
a.getFullyQualifiedName() != null ? a.getFullyQualifiedName() : "",
b.getFullyQualifiedName() != null ? b.getFullyQualifiedName() : "");
}
}
/**
* Creates a typed argument list with the given <code>arguments</code>. If the <code>withArgumentNames</code>
* flag is true, the argument names are included in the list.
*
* @param arguments the arguments from which to create the list.
* @param withArgumentNames whether or not to include the argument names.
* @param modifier
* @return arguments.iterator().getGetterSetterTypeName()
*/
public static String getTypedArgumentList(
final Collection<ParameterFacade> arguments,
final boolean withArgumentNames,
final String modifier)
{
final StringBuilder buffer = new StringBuilder();
boolean commaNeeded = false;
for (ParameterFacade parameter : arguments)
{
String type = null;
ClassifierFacade classifier = parameter.getType();
if (classifier != null)
{
// Takes multiplicity and templating into account
type = parameter.getGetterSetterTypeName();
}
if (commaNeeded)
{
buffer.append(", ");
}
if (StringUtils.isNotBlank(modifier))
{
buffer.append(modifier);
buffer.append(' ');
}
buffer.append(type);
if (withArgumentNames)
{
buffer.append(' ');
buffer.append(parameter.getName());
}
commaNeeded = true;
}
return buffer.toString();
}
/**
* Creates a typed argument list with the given <code>arguments</code>. If the <code>withArgumentNames</code>
* flag is true, the argument names are included in the list.
*
* @param name
* @param arguments the arguments from which to create the list.
* @param withArgumentNames whether or not to include the argument names.
* @param argumentModifier
* @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
*/
public static String getSignature(
final String name,
Collection<ParameterFacade> arguments,
final boolean withArgumentNames,
final String argumentModifier)
{
final StringBuilder signature = new StringBuilder(name);
signature.append('(');
signature.append(getTypedArgumentList(
arguments,
withArgumentNames,
argumentModifier));
signature.append(')');
return signature.toString();
}
private static final String at = "@";
private static final char period = '.';
private static final char underscore = '_';
/**
* Changes andromda standard tag format Strings to EMF standard format Strings
* (must be a valid Java identifier). Used for backwards compatibility with UML14 conventions.
* For example, @andromda.whatever becomes andromda_whatever.
*
* @param name
* @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
*/
public static String getEmfTaggedValue(String name)
{
if (name==null)
{
return name;
}
if (name.startsWith(at))
{
name = name.substring(1);
}
name = name.replace(period, underscore);
return name;
}
/**
* Changes EMF standard tag format Strings to AndroMDA standard format Strings.
* Used for backwards compatibility with UML14 conventions.
* For example, andromda_whatever becomes @andromda.whatever.
*
* @param name
* @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
*/
public static String getUml14TaggedValue(String name)
{
if (name==null)
{
return name;
}
if (!name.startsWith(at))
{
name = at+name;
}
name = name.replace(underscore, period);
return name;
}
/**
* Calculates the serial version UID of this classifier based on the
* signature of the classifier (name, visibility, attributes and methods).
* The algorithm is inspired by
* {@link java.io.ObjectStreamClass#getSerialVersionUID()}.
*
* The value should be stable as long as the classifier remains unchanged
* and should change as soon as there is any change in the signature of the
* classifier.
* @param object
*
* @return the serial version UID of this classifier.
*/
public static long calculateDefaultSUID(ClassifierFacade object)
{
// class name
StringBuilder buffer = new StringBuilder(object.getName());
// generalizations
for (GeneralizableElementFacade generalizableElementFacade : object.getAllGeneralizations())
{
ClassifierFacade classifier = (ClassifierFacade) generalizableElementFacade;
buffer.append(classifier.getName());
}
// declared fields
for (AttributeFacade attribute : object.getAttributes())
{
buffer.append(attribute.getName());
buffer.append(attribute.getVisibility());
buffer.append(attribute.getType().getName());
}
// operations
for (OperationFacade operation : object.getOperations())
{
buffer.append(operation.getName());
buffer.append(operation.getVisibility());
buffer.append(operation.getReturnType().getName());
for (final ParameterFacade parameter : operation.getArguments())
{
buffer.append(parameter.getName());
buffer.append(parameter.getType().getName());
}
}
final String signature = buffer.toString();
long serialVersionUID = 0L;
try
{
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] hashBytes = md.digest(signature.getBytes());
long hash = 0;
for (int ctr = Math.min(hashBytes.length, 8) - 1; ctr >= 0; ctr--)
{
hash = (hash << 8) | (hashBytes[ctr] & 0xFF);
}
serialVersionUID = hash;
}
catch (final NoSuchAlgorithmException ignore)
{
// ignore exception
}
return serialVersionUID;
}
}