001package org.andromda.cartridges.meta.metafacades; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.HashMap; 008import java.util.LinkedHashSet; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012import org.andromda.cartridges.meta.MetaProfile; 013import org.andromda.core.metafacade.MetafacadeException; 014import org.andromda.metafacades.uml.AssociationEndFacade; 015import org.andromda.metafacades.uml.AttributeFacade; 016import org.andromda.metafacades.uml.ClassifierFacade; 017import org.andromda.metafacades.uml.DependencyFacade; 018import org.andromda.metafacades.uml.GeneralizableElementFacade; 019import org.andromda.metafacades.uml.GeneralizationFacade; 020import org.andromda.metafacades.uml.ModelElementFacade; 021import org.andromda.metafacades.uml.OperationFacade; 022import org.apache.commons.collections.CollectionUtils; 023import org.apache.commons.collections.Predicate; 024import org.apache.commons.collections.Transformer; 025import org.apache.commons.lang.ObjectUtils; 026import org.apache.commons.lang.StringUtils; 027import org.apache.log4j.Logger; 028 029/** 030 * Metaclass facade implementation. 031 * 032 * @see org.andromda.cartridges.meta.metafacades.Metafacade 033 * @author Bob Fields 034 */ 035public class MetafacadeLogicImpl 036 extends MetafacadeLogic 037{ 038 private static final long serialVersionUID = 34L; 039 /** 040 * This defines the metamodel version package name (i.e. 041 * org.andromda.metafacades.uml14, org.andromda.metafacades.um22, etc) used 042 * by this cartridge to create the generated impl package name, if left 043 * empty then the impl package will be the same as the metafacade package 044 * (therefore we default to an empty name) 045 */ 046 private static final String METAMODEL_VERSION_PACKAGE = "metamodelVersionPackage"; 047 private Map<ClassifierFacade, Collection<MethodData>> featureMap = null; 048 /** 049 * The logger instance. 050 */ 051 private static final Logger logger = Logger.getLogger(MetafacadeLogicImpl.class); 052 053 /** 054 * @param metaObjectIn 055 * @param context 056 */ 057 public MetafacadeLogicImpl( 058 Object metaObjectIn, 059 String context) 060 { 061 super(metaObjectIn, context); 062 } 063 064 /** 065 * Returns the class tagged with <<metaclass>>> that is 066 * connected to the metaobject via a dependency. If no metaclass is directly 067 * connected, the method walks up the supertype hierarchy. 068 * 069 * @return the metaclass object 070 */ 071 @Override 072 protected ClassifierFacade handleGetMetaclass() 073 { 074 // delegate to recursive method 075 return getMetaclass(this); 076 } 077 078 /** 079 * Returns the class tagged with <<metaclass>> that is connected 080 * to classifier via a dependency. 081 * 082 * @param classifier the source classifier 083 * @return the metaclass object 084 */ 085 private ClassifierFacade getMetaclass(ClassifierFacade classifier) 086 { 087 for (DependencyFacade dep : classifier.getSourceDependencies()) 088 { 089 ClassifierFacade target = (ClassifierFacade)dep.getTargetElement(); 090 Collection<String> stereotypes = target.getStereotypeNames(); 091 if ((stereotypes != null) && (!stereotypes.isEmpty())) 092 { 093 String stereotypeName = stereotypes.iterator().next(); 094 if (stereotypeName.equals(MetaProfile.STEREOTYPE_METACLASS)) 095 { 096 return target; 097 } 098 } 099 } 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}