View Javadoc
1   package org.andromda.cartridges.meta.metafacades;
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.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  import org.andromda.cartridges.meta.MetaProfile;
13  import org.andromda.core.metafacade.MetafacadeException;
14  import org.andromda.metafacades.uml.AssociationEndFacade;
15  import org.andromda.metafacades.uml.AttributeFacade;
16  import org.andromda.metafacades.uml.ClassifierFacade;
17  import org.andromda.metafacades.uml.DependencyFacade;
18  import org.andromda.metafacades.uml.GeneralizableElementFacade;
19  import org.andromda.metafacades.uml.GeneralizationFacade;
20  import org.andromda.metafacades.uml.ModelElementFacade;
21  import org.andromda.metafacades.uml.OperationFacade;
22  import org.apache.commons.collections.CollectionUtils;
23  import org.apache.commons.collections.Predicate;
24  import org.apache.commons.collections.Transformer;
25  import org.apache.commons.lang.ObjectUtils;
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.log4j.Logger;
28  
29  /**
30   * Metaclass facade implementation.
31   *
32   * @see org.andromda.cartridges.meta.metafacades.Metafacade
33   * @author Bob Fields
34   */
35  public class MetafacadeLogicImpl
36      extends MetafacadeLogic
37  {
38      private static final long serialVersionUID = 34L;
39      /**
40       * This defines the metamodel version package name (i.e.
41       * org.andromda.metafacades.uml14, org.andromda.metafacades.um22, etc) used
42       * by this cartridge to create the generated impl package name, if left
43       * empty then the impl package will be the same as the metafacade package
44       * (therefore we default to an empty name)
45       */
46      private static final String METAMODEL_VERSION_PACKAGE = "metamodelVersionPackage";
47      private Map<ClassifierFacade, Collection<MethodData>> featureMap = null;
48      /**
49       * The logger instance.
50       */
51      private static final Logger logger = Logger.getLogger(MetafacadeLogicImpl.class);
52  
53      /**
54       * @param metaObjectIn
55       * @param context
56       */
57      public MetafacadeLogicImpl(
58          Object metaObjectIn,
59          String context)
60      {
61          super(metaObjectIn, context);
62      }
63  
64      /**
65       * Returns the class tagged with &lt;&lt;metaclass&gt;&gt;&gt; that is
66       * connected to the metaobject via a dependency. If no metaclass is directly
67       * connected, the method walks up the supertype hierarchy.
68       *
69       * @return the metaclass object
70       */
71      @Override
72      protected ClassifierFacade handleGetMetaclass()
73      {
74          // delegate to recursive method
75          return getMetaclass(this);
76      }
77  
78      /**
79       * Returns the class tagged with &lt;&lt;metaclass&gt;&gt; that is connected
80       * to classifier via a dependency.
81       *
82       * @param classifier the source classifier
83       * @return the metaclass object
84       */
85      private ClassifierFacade getMetaclass(ClassifierFacade classifier)
86      {
87          for (DependencyFacade dep : classifier.getSourceDependencies())
88          {
89              ClassifierFacade target = (ClassifierFacade)dep.getTargetElement();
90              Collection<String> stereotypes = target.getStereotypeNames();
91              if ((stereotypes != null) && (!stereotypes.isEmpty()))
92              {
93                  String stereotypeName = stereotypes.iterator().next();
94                  if (stereotypeName.equals(MetaProfile.STEREOTYPE_METACLASS))
95                  {
96                      return target;
97                  }
98              }
99          }
100 
101         ClassifierFacade superclass = (ClassifierFacade)classifier.getGeneralization();
102         return (superclass != null) ? getMetaclass(superclass) : null;
103     }
104 
105     /**
106      * @see Metafacade#isMetaclassDirectDependency()
107      */
108     @Override
109     protected boolean handleIsMetaclassDirectDependency()
110     {
111         boolean isMetaClassDirectDependency = false;
112         Collection<DependencyFacade> dependencies = this.getSourceDependencies();
113         if ((dependencies != null) && !dependencies.isEmpty())
114         {
115             // there should be only one.
116             DependencyFacade dependency = dependencies.iterator().next();
117             if (dependency != null)
118             {
119                 ModelElementFacade targetElement = dependency.getTargetElement();
120                 if (targetElement != null)
121                 {
122                     isMetaClassDirectDependency = targetElement.hasStereotype(MetaProfile.STEREOTYPE_METACLASS);
123                 }
124             }
125         }
126         return isMetaClassDirectDependency;
127     }
128 
129     /**
130      * @see Metafacade#getLogicName()
131      */
132     @Override
133     protected String handleGetLogicName()
134     {
135         return this.getName() + "Logic";
136     }
137 
138     /**
139      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getLogicImplName()
140      */
141     @Override
142     protected String handleGetLogicImplName()
143     {
144         return this.getName() + "LogicImpl";
145     }
146 
147     /**
148      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getFullyQualifiedLogicImplName()
149      */
150     @Override
151     protected String handleGetFullyQualifiedLogicImplName()
152     {
153         return this.getMetafacadeSupportClassName(this.getLogicImplName());
154     }
155 
156     /**
157      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getFullyQualifiedLogicName()
158      */
159     @Override
160     protected String handleGetFullyQualifiedLogicName()
161     {
162         return this.getMetafacadeSupportClassName(this.getLogicName());
163     }
164 
165     /**
166      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getLogicFile()
167      */
168     @Override
169     protected String handleGetLogicFile()
170     {
171         return this.getFullyQualifiedLogicName().replace('.', '/') + ".java";
172     }
173 
174     /**
175      * Gets the metamodel version package name (i.e.
176      * org.andromda.metafacades.uml14, org.andromda.metafacades.um20, etc) used
177      * by this cartridge to create the generated impl package name, if left
178      * empty then the impl package will be the same as the metafacade package
179      * (therefore we default to an empty name)
180      */
181     private String getMetaModelVersionPackage()
182     {
183         return ObjectUtils.toString(this.getConfiguredProperty(METAMODEL_VERSION_PACKAGE));
184     }
185 
186     /**
187      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getLogicPackageName()
188      */
189     @Override
190     protected String handleGetLogicPackageName()
191     {
192         String packageName = this.getMetaModelVersionPackage();
193         if (StringUtils.isEmpty(packageName))
194         {
195             packageName = this.getPackageName();
196         }
197         return packageName;
198     }
199 
200     /**
201      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getLogicImplFile()
202      */
203     @Override
204     protected String handleGetLogicImplFile()
205     {
206         return this.getFullyQualifiedLogicImplName().replace('.', '/') + ".java";
207     }
208 
209     /**
210      * Creates a metafacade support class name from the given
211      * <code>metamodelVersionPackage</code> (i.e. the package for the specific
212      * meta model version). Support classes are the 'Logic' classes.
213      *
214      * @param name the name of the class to append to the package.
215      * @return the new metafacade support class name.
216      */
217     private String getMetafacadeSupportClassName(String name)
218     {
219         StringBuilder fullyQualifiedName = new StringBuilder(this.getLogicPackageName());
220         if (StringUtils.isNotBlank(fullyQualifiedName.toString()))
221         {
222             fullyQualifiedName.append('.');
223             fullyQualifiedName.append(name);
224         }
225         return fullyQualifiedName.toString();
226     }
227 
228     /**
229      * @see org.andromda.cartridges.meta.metafacades.MetafacadeLogic#handleGetMethodDataForPSM(org.andromda.metafacades.uml.ClassifierFacade)
230      */
231     @Override
232     protected Collection<MethodData> handleGetMethodDataForPSM(ClassifierFacade facade)
233     {
234         return this.getMethodDataForPSM(facade, true);
235     }
236 
237     /**
238      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getMethodDataForPSM()
239      */
240     @Override
241     protected Collection<MethodData> handleGetMethodDataForPSM()
242     {
243         return this.getMethodDataForPSM(null, false);
244     }
245 
246     /**
247      * Return collection of all methods of all generalization classes for the classifier
248      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getMethodDataForPSM(boolean)
249      */
250     private final Collection<MethodData> getMethodDataForPSM(
251         final ClassifierFacade facade,
252         final boolean includeSuperclasses)
253     {
254         try
255         {
256             final Set<String> declarationSet = new LinkedHashSet<String>();
257             if (this.featureMap == null)
258             {
259                 this.featureMap = new HashMap<ClassifierFacade, Collection<MethodData>>();
260                 if (includeSuperclasses && this.getGeneralizations() != null)
261                 {
262                     for (GeneralizableElementFacade general : this.getGeneralizations())
263                     {
264                         final Map<String, MethodData> methodDataMap = new HashMap<String, MethodData>();
265                         final ClassifierFacade metafacade = (ClassifierFacade)general;
266                         for (ClassifierFacade classifier = metafacade; classifier instanceof Metafacade;
267                              classifier = (ClassifierFacade)classifier.getGeneralization())
268                         {
269                             this.getAllFeatures(methodDataMap, declarationSet, (Metafacade)classifier);
270                         }
271                         this.featureMap.put(metafacade, methodDataMap.values());
272                     }
273                 }
274             }
275             final List<MethodData> result = new ArrayList<MethodData>();
276             if (this.featureMap != null)
277             {
278                 Collection<MethodData> features = this.featureMap.get(facade);
279                 if (features != null)
280                 {
281                     result.addAll(features);
282                 }
283             }
284             if (!includeSuperclasses)
285             {
286                 final Map<String, MethodData> methodDataMap = new HashMap<String, MethodData>();
287                 this.getAllFeatures(methodDataMap, declarationSet, this);
288                 result.addAll(methodDataMap.values());
289             }
290             Collections.sort(result);
291             return result;
292         }
293         catch (Throwable th)
294         {
295             throw new RuntimeException(th);
296         }
297     }
298 
299     /**
300      * Returns method data (name, visibility, type, doc) for each property and operation in the Metafacade
301      * @param methodDataMap
302      * @param declarationSet
303      * @param facade
304      */
305     private final void getAllFeatures(
306         final Map<String, MethodData> methodDataMap,
307         final Set<String> declarationSet,
308         final Metafacade facade)
309     {
310         try
311         {
312             final String methodVisibility = "public";
313             final String indendation = "     * ";
314             final String fullyQualifiedName = facade.getFullyQualifiedName();
315 
316             // translate UML attributes and association ends to getter methods
317             for (final Object obj : facade.getProperties())
318             {
319                 final ModelElementFacade property = (ModelElementFacade)obj;
320                 MethodData method = null;
321                 if (property instanceof AttributeFacade)
322                 {
323                     final AttributeFacade attribute = (AttributeFacade)property;
324                     method =
325                         new MethodData(
326                             fullyQualifiedName,
327                             methodVisibility,
328                             false,
329                             attribute.getGetterSetterTypeName(),
330                             attribute.getGetterName(),
331                             attribute.getDocumentation(indendation));
332                 }
333                 else
334                 {
335                     final AssociationEndFacade association = (AssociationEndFacade)property;
336                     method =
337                         new MethodData(
338                             fullyQualifiedName,
339                             methodVisibility,
340                             false,
341                             association.getGetterSetterTypeName(),
342                             association.getGetterName(),
343                             association.getDocumentation(indendation));
344                 }
345                 final String declaration = method.buildMethodDeclaration(true);
346 
347                 // don't add the new method data if we already have the
348                 // declaration from a previous generalization.
349                 if (!declarationSet.contains(declaration))
350                 {
351                     methodDataMap.put(
352                         method.buildCharacteristicKey(),
353                         method);
354                     declarationSet.add(declaration);
355                 }
356             }
357 
358             // translate UML operations to methods
359             for (OperationFacade operation : facade.getOperations())
360             {
361                 final UMLOperationData method = new UMLOperationData(fullyQualifiedName, operation);
362 
363                 // don't add the new method data if we already have the
364                 // declaration from a previous generalization.
365                 final String declaration = method.buildMethodDeclaration(true);
366                 if (!declarationSet.contains(declaration))
367                 {
368                     methodDataMap.put(
369                         method.buildCharacteristicKey(),
370                         method);
371                     declarationSet.add(declaration);
372                 }
373             }
374         }
375         catch (final Throwable throwable)
376         {
377             MetafacadeLogicImpl.logger.error(throwable);
378             throw new MetafacadeException(throwable);
379         }
380     }
381 
382     /**
383      * @see org.andromda.cartridges.meta.metafacades.Metafacade#isRequiresInheritanceDelegatation()
384      */
385     @Override
386     protected boolean handleIsRequiresInheritanceDelegatation()
387     {
388         boolean requiresInheritanceDelegation = false;
389         final ModelElementFacade superMetafacade = this.getGeneralization();
390         if (superMetafacade != null)
391         {
392             requiresInheritanceDelegation =
393                 !superMetafacade.getPackageName().equals(this.getPackageName()) ||
394                 (this.getGeneralizations().size() > 1);
395         }
396         return requiresInheritanceDelegation;
397     }
398 
399     /**
400      * @see org.andromda.cartridges.meta.metafacades.Metafacade#isConstructorRequiresMetaclassCast()
401      */
402     @Override
403     protected boolean handleIsConstructorRequiresMetaclassCast()
404     {
405         boolean requiresCast = false;
406         final Metafacade superMetafacade = (Metafacade)this.getGeneralization();
407         if (superMetafacade != null)
408         {
409             requiresCast = superMetafacade.isMetaclassDirectDependency() && !this.isRequiresInheritanceDelegatation();
410         }
411         return requiresCast;
412     }
413 
414     /**
415      * @see org.andromda.metafacades.uml.GeneralizableElementFacade#getGeneralizations()
416      */
417     @Override
418     public Collection<GeneralizableElementFacade> getGeneralizations()
419     {
420         final List generalizations = new ArrayList(super.getGeneralizationLinks());
421         Collections.sort(
422             generalizations,
423             new GeneralizationPrecedenceComparator());
424         CollectionUtils.transform(
425             generalizations,
426             new Transformer()
427             {
428                 public Object transform(final Object object)
429                 {
430                     return ((GeneralizationFacade)object).getParent();
431                 }
432             });
433         CollectionUtils.filter(generalizations,
434             new Predicate()
435             {
436                 public boolean evaluate(final Object object)
437                 {
438                     return object instanceof Metafacade;
439                 }
440             });
441         return generalizations;
442     }
443 
444     /**
445      * @see org.andromda.cartridges.meta.metafacades.Metafacade#getGeneralizationCount()
446      */
447     @Override
448     protected int handleGetGeneralizationCount()
449     {
450         int count = 0;
451         final Collection<GeneralizableElementFacade> generalizations = this.getGeneralizations();
452         if (generalizations != null)
453         {
454             count = generalizations.size();
455         }
456         return count;
457     }
458 
459     /**
460      * Used to sort metafacade generalizations by precedence.
461      */
462     static final class GeneralizationPrecedenceComparator
463         implements Comparator
464     {
465         /**
466          * @see java.util.Comparator#compare(Object, Object)
467          */
468         public int compare(
469             Object objectA,
470             Object objectB)
471         {
472             MetafacadeGeneralization a = (MetafacadeGeneralization)objectA;
473             MetafacadeGeneralization b = (MetafacadeGeneralization)objectB;
474             return a.getPrecedence().compareTo(b.getPrecedence());
475         }
476     }
477 
478     /**
479      * @see org.andromda.cartridges.meta.metafacades.MetafacadeLogic#getAllParents()
480      */
481     @Override
482     protected Collection<GeneralizableElementFacade> handleGetAllParents()
483     {
484         Set<GeneralizableElementFacade> allParents = new LinkedHashSet<GeneralizableElementFacade> ();
485         final Collection<GeneralizableElementFacade> parents = this.getGeneralizations();
486         allParents.addAll(parents);
487         for (Object object : parents)
488         {
489             if (object instanceof Metafacade)
490             {
491                 final Metafacade metafacade = (Metafacade)object;
492                 allParents.addAll(metafacade.getAllParents());
493             }
494         }
495         return allParents;
496     }
497 }