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