View Javadoc
1   package org.andromda.metafacades.uml;
2   
3   import java.security.MessageDigest;
4   import java.security.NoSuchAlgorithmException;
5   import java.text.Collator;
6   import java.util.Collection;
7   import java.util.Collections;
8   import java.util.Comparator;
9   import java.util.List;
10  import org.apache.commons.collections.CollectionUtils;
11  import org.apache.commons.collections.Predicate;
12  import org.apache.commons.lang.StringUtils;
13  
14  /**
15   * A class containing utilities for metafacade manipulation.
16   *
17   * @author Chad Brandon
18   * @author Wouter Zoons
19   * @author Bob Fields
20   */
21  public class MetafacadeUtils
22  {
23      /**
24       * Checks to see if the element is the specified type and if so casts it to the object and returns it, otherwise it
25       * returns null.
26       *
27       * @param element the element to check.
28       * @param type the Class type.
29       * @return the element has the given type or null.
30       */
31      public static Object getElementAsType(
32          final Object element,
33          final Class type)
34      {
35          Object elementAsType = null;
36          if (element != null && type != null)
37          {
38              final Class elementClass = element.getClass();
39              if (type.isAssignableFrom(elementClass))
40              {
41                  elementAsType = element;
42              }
43          }
44          return elementAsType;
45      }
46  
47      /**
48       * Filters out the model elements from the <code>modelElements</code> collection that don't have the specified
49       * <code>stereotype</code>
50       *
51       * @param modelElements the model elements to filter.
52       * @param stereotype    the stereotype that a model element must have in order to stay remain within the
53       *                      <code>modelElements</code> collection.
54       */
55      public static void filterByStereotype(
56          final Collection modelElements,
57          final String stereotype)
58      {
59          // Should be able to type the Collection as <ModelElementFacade>, but compilation failure results.
60          if (StringUtils.isNotBlank(stereotype))
61          {
62              CollectionUtils.filter(
63                  modelElements,
64                  new Predicate()
65                  {
66                      public boolean evaluate(Object object)
67                      {
68                          return ((ModelElementFacade)object).hasStereotype(stereotype);
69                      }
70                  });
71          }
72      }
73  
74      /**
75       * Filters out the model elements from the <code>modelElements</code> collection that are not of (or do not inherit
76       * from) the specified type <code>type</code>
77       *
78       * @param modelElements the model elements to filter.
79       * @param type          the type of Class.
80       */
81      public static void filterByType(
82          final Collection modelElements,
83          final Class type)
84      {
85          if (type != null)
86          {
87              CollectionUtils.filter(
88                  modelElements,
89                  new Predicate()
90                  {
91                      public boolean evaluate(Object object)
92                      {
93                          return type.isAssignableFrom(object.getClass());
94                      }
95                  });
96          }
97      }
98  
99      /**
100      * Filters out the model elements from the <code>modelElements</code> collection that are of (or inherit from) the
101      * specified type <code>type</code>
102      *
103      * @param modelElements the model elements to filter.
104      * @param type the type of Class.
105      */
106     public static void filterByNotType(
107         final Collection modelElements,
108         final Class type)
109     {
110         if (type != null)
111         {
112             CollectionUtils.filter(
113                 modelElements,
114                 new Predicate()
115                 {
116                     public boolean evaluate(Object object)
117                     {
118                         return !type.isAssignableFrom(object.getClass());
119                     }
120                 });
121         }
122     }
123 
124     /**
125      * <p/>Returns a consistent name for a relation, independent from the end of the relation one is looking at. </p>
126      * <p/>In order to guarantee consistency with relation names, they must appear the same whichever angle (ie entity)
127      * that you come from. For example, if you are at Customer end of a relationship to an Address then your relation
128      * may appear with the name Customer-Address. But if you are in the Address entity looking at the Customer then you
129      * will get an error because the relation will be called Address-Customer. A simple way to guarantee that both ends
130      * of the relationship have the same name is merely to use alphabetical ordering. </p>
131      *
132      * @param roleName       name of role in relation
133      * @param targetRoleName name of target role in relation
134      * @param separator      character used to separate words
135      * @return uniform mapping name (in alphabetical order)
136      */
137     public static String toRelationName(
138         final String roleName,
139         final String targetRoleName,
140         final String separator)
141     {
142         if (roleName.compareTo(targetRoleName) <= 0)
143         {
144             return (roleName + separator + targetRoleName);
145         }
146         return (targetRoleName + separator + roleName);
147     }
148 
149     /**
150      * Sorts given metafacades by their fully qualified name.
151      *
152      * @param metafacades the collection of model elements to sort.
153      */
154     public static void sortByFullyQualifiedName(final List metafacades)
155     {
156         Collections.sort(
157             metafacades,
158             new FullyQualifiedNameComparator());
159     }
160 
161     /**
162      * Used to sort operations by <code>fullyQualifiedName</code>.
163      */
164     private static final class FullyQualifiedNameComparator
165         implements Comparator
166     {
167         private final Collator collator = Collator.getInstance();
168 
169         /** */
170         FullyQualifiedNameComparator()
171         {
172             collator.setStrength(Collator.PRIMARY);
173         }
174 
175         public int compare(
176             final Object objectA,
177             final Object objectB)
178         {
179             final ModelElementFacade a = (ModelElementFacade)objectA;
180             final ModelElementFacade b = (ModelElementFacade)objectB;
181             return collator.compare(
182                 a.getFullyQualifiedName() != null ? a.getFullyQualifiedName() : "",
183                 b.getFullyQualifiedName() != null ? b.getFullyQualifiedName() : "");
184         }
185     }
186 
187     /**
188      * Creates a typed argument list with the given <code>arguments</code>.  If the <code>withArgumentNames</code>
189      * flag is true, the argument names are included in the list.
190      *
191      * @param arguments the arguments from which to create the list.
192      * @param withArgumentNames whether or not to include the argument names.
193      * @param modifier
194      * @return arguments.iterator().getGetterSetterTypeName()
195      */
196     public static String getTypedArgumentList(
197         final Collection<ParameterFacade> arguments,
198         final boolean withArgumentNames,
199         final String modifier)
200     {
201         final StringBuilder buffer = new StringBuilder();
202         boolean commaNeeded = false;
203         for (ParameterFacade parameter : arguments)
204         {
205             String type = null;
206             ClassifierFacade classifier = parameter.getType();
207             if (classifier != null)
208             {
209                 // Takes multiplicity and templating into account
210                 type = parameter.getGetterSetterTypeName();
211             }
212 
213             if (commaNeeded)
214             {
215                 buffer.append(", ");
216             }
217             if (StringUtils.isNotBlank(modifier))
218             {
219                 buffer.append(modifier);
220                 buffer.append(' ');
221             }
222             buffer.append(type);
223             if (withArgumentNames)
224             {
225                 buffer.append(' ');
226                 buffer.append(parameter.getName());
227             }
228             commaNeeded = true;
229         }
230         return buffer.toString();
231     }
232 
233     /**
234      * Creates a typed argument list with the given <code>arguments</code>.  If the <code>withArgumentNames</code>
235      * flag is true, the argument names are included in the list.
236      *
237      * @param name
238      * @param arguments the arguments from which to create the list.
239      * @param withArgumentNames whether or not to include the argument names.
240      * @param argumentModifier
241      * @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
242      */
243     public static String getSignature(
244         final String name,
245         Collection<ParameterFacade> arguments,
246         final boolean withArgumentNames,
247         final String argumentModifier)
248     {
249         final StringBuilder signature = new StringBuilder(name);
250         signature.append('(');
251         signature.append(getTypedArgumentList(
252                 arguments,
253                 withArgumentNames,
254                 argumentModifier));
255         signature.append(')');
256         return signature.toString();
257     }
258 
259     private static final String at = "@";
260     private static final char period = '.';
261     private static final char underscore = '_';
262     /**
263      * Changes andromda standard tag format Strings to EMF standard format Strings
264      * (must be a valid Java identifier). Used for backwards compatibility with UML14 conventions.
265      * For example, @andromda.whatever becomes andromda_whatever.
266      *
267      * @param name
268      * @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
269      */
270     public static String getEmfTaggedValue(String name)
271     {
272         if (name==null)
273         {
274             return name;
275         }
276         if (name.startsWith(at))
277         {
278             name = name.substring(1);
279         }
280         name = name.replace(period, underscore);
281         return name;
282     }
283 
284     /**
285      * Changes EMF standard tag format Strings to AndroMDA standard format Strings.
286      * Used for backwards compatibility with UML14 conventions.
287      * For example, andromda_whatever becomes @andromda.whatever.
288      *
289      * @param name
290      * @return getTypedArgumentList(arguments, withArgumentNames, argumentModifier)
291      */
292     public static String getUml14TaggedValue(String name)
293     {
294         if (name==null)
295         {
296             return name;
297         }
298         if (!name.startsWith(at))
299         {
300             name = at+name;
301         }
302         name = name.replace(underscore, period);
303         return name;
304     }
305 
306     /**
307      * Calculates the serial version UID of this classifier based on the
308      * signature of the classifier (name, visibility, attributes and methods).
309      * The algorithm is inspired by
310      * {@link java.io.ObjectStreamClass#getSerialVersionUID()}.
311      *
312      * The value should be stable as long as the classifier remains unchanged
313      * and should change as soon as there is any change in the signature of the
314      * classifier.
315      * @param object
316      *
317      * @return the serial version UID of this classifier.
318      */
319     public static long calculateDefaultSUID(ClassifierFacade object)
320     {
321         // class name
322         StringBuilder buffer = new StringBuilder(object.getName());
323 
324         // generalizations
325         for (GeneralizableElementFacade generalizableElementFacade : object.getAllGeneralizations())
326         {
327             ClassifierFacade classifier = (ClassifierFacade) generalizableElementFacade;
328             buffer.append(classifier.getName());
329         }
330 
331         // declared fields
332         for (AttributeFacade attribute : object.getAttributes())
333         {
334             buffer.append(attribute.getName());
335             buffer.append(attribute.getVisibility());
336             buffer.append(attribute.getType().getName());
337         }
338 
339         // operations
340         for (OperationFacade operation : object.getOperations())
341         {
342             buffer.append(operation.getName());
343             buffer.append(operation.getVisibility());
344             buffer.append(operation.getReturnType().getName());
345             for (final ParameterFacade parameter : operation.getArguments())
346             {
347                 buffer.append(parameter.getName());
348                 buffer.append(parameter.getType().getName());
349             }
350         }
351         final String signature = buffer.toString();
352 
353         long serialVersionUID = 0L;
354         try
355         {
356             MessageDigest md = MessageDigest.getInstance("SHA");
357             byte[] hashBytes = md.digest(signature.getBytes());
358 
359             long hash = 0;
360             for (int ctr = Math.min(hashBytes.length, 8) - 1; ctr >= 0; ctr--)
361             {
362                 hash = (hash << 8) | (hashBytes[ctr] & 0xFF);
363             }
364             serialVersionUID = hash;
365         }
366         catch (final NoSuchAlgorithmException ignore)
367         {
368             // ignore exception
369         }
370 
371         return serialVersionUID;
372     }
373 }