001package org.andromda.core.metafacade; 002 003import java.io.Serializable; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collection; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.Iterator; 010import java.util.LinkedHashMap; 011import java.util.LinkedHashSet; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015import org.andromda.core.common.AndroMDALogger; 016import org.andromda.core.common.ClassUtils; 017import org.andromda.core.common.ComponentContainer; 018import org.andromda.core.common.ExceptionUtils; 019import org.andromda.core.configuration.Namespace; 020import org.andromda.core.configuration.Namespaces; 021import org.andromda.core.namespace.BaseNamespaceComponent; 022import org.apache.commons.lang.StringUtils; 023import org.apache.log4j.Logger; 024 025/** 026 * The Metafacade mapping class. Used to map <code>metafacade</code> objects 027 * to <code>metamodel</code> objects. 028 * 029 * @author Chad Brandon 030 * @author Bob Fields 031 * @see MetafacadeMapping 032 * @see org.andromda.core.common.XmlObjectFactory 033 */ 034public class MetafacadeMappings 035 extends BaseNamespaceComponent 036 implements Serializable 037{ 038 private static final long serialVersionUID = 34L; 039 040 /** 041 * Holds the references to the child MetafacadeMapping instances. 042 */ 043 private final Collection<MetafacadeMapping> mappings = new ArrayList<MetafacadeMapping>(); 044 045 /** 046 * Holds the namespace MetafacadeMappings. This are child MetafacadeMappings 047 * keyed by namespace name. 048 */ 049 private final Map<String, MetafacadeMappings> namespaceMetafacadeMappings = new HashMap<String, MetafacadeMappings>(); 050 051 /** 052 * The default meta facade to use when there isn't a mapping found. 053 */ 054 private Class defaultMetafacadeClass = null; 055 056 /** 057 * Constructs a new instance of this class. 058 * 059 * @return MetafacadeMappings 060 */ 061 public static MetafacadeMappings newInstance() 062 { 063 return new MetafacadeMappings(); 064 } 065 066 /** 067 * Adds a MetafacadeMapping instance to the set of current mappings. 068 * 069 * @param mapping the MetafacadeMapping instance. 070 */ 071 public void addMapping(final MetafacadeMapping mapping) 072 { 073 ExceptionUtils.checkNull( 074 "mapping", 075 mapping); 076 ExceptionUtils.checkNull( 077 "mapping.metafacadeClass", 078 mapping.getMetafacadeClass()); 079 mapping.setMetafacadeMappings(this); 080 081 // find any mappings that match, if they do we add the properties 082 // from that mapping to the existing matched mapping (so we only 083 // have one mapping containing properties that can be 'OR'ed together). 084 final MetafacadeMapping foundMapping = 085 this.findMapping( 086 new Condition() 087 { 088 public boolean evaluate(final MetafacadeMapping object) 089 { 090 return mapping.match(object); 091 } 092 }); 093 if (foundMapping != null) 094 { 095 foundMapping.addMappingPropertyGroup(mapping.getMappingProperties()); 096 } 097 else 098 { 099 this.mappings.add(mapping); 100 this.mappingsByMetafacadeClass.put( 101 this.getMetafacadeInterface(mapping.getMetafacadeClass()), 102 mapping); 103 } 104 } 105 106 /** 107 * Gets the class of the metafacade interface that belongs to the given 108 * <code>metafacadeClass</code>. 109 * @param metafacadeClass 110 * 111 * @return the metafacade interface Class. 112 */ 113 public Class getMetafacadeInterface(final Class metafacadeClass) 114 { 115 Class metafacadeInterface = null; 116 if (metafacadeClass != null) 117 { 118 metafacadeInterface = metafacadeClass; 119 final List<Class> interfaces = ClassUtils.getAllInterfaces(metafacadeClass); 120 if (interfaces != null && !interfaces.isEmpty()) 121 { 122 metafacadeInterface = interfaces.iterator().next(); 123 } 124 } 125 return metafacadeInterface; 126 } 127 128 /** 129 * Stores mappings by the metafacade class so that we can retrieve the 130 * inherited metafacade classes. 131 */ 132 private final Map<Class, MetafacadeMapping> mappingsByMetafacadeClass = new HashMap<Class, MetafacadeMapping>(); 133 134 /** 135 * Copies all data from <code>mappings<code> to this instance. 136 * 137 * @param mappings the mappings to add 138 */ 139 private void copyMappings(final MetafacadeMappings mappings) 140 { 141 ExceptionUtils.checkNull( 142 "mappings", 143 mappings); 144 for (final MetafacadeMapping mapping : mappings.mappings) 145 { 146 this.addMapping(mapping); 147 } 148 final Collection<String> propertyReferences = mappings.getPropertyReferences(); 149 if (propertyReferences != null && !propertyReferences.isEmpty()) 150 { 151 this.propertyReferences.addAll(propertyReferences); 152 } 153 this.defaultMetafacadeClass = mappings.defaultMetafacadeClass; 154 } 155 156 /** 157 * Contains references to properties populated in the Namespaces. 158 */ 159 private final Collection<String> propertyReferences = new LinkedHashSet<String>(); 160 161 /** 162 * Gets all property references defined in this mappings instance. 163 * 164 * @return the map of property references (names and values). 165 */ 166 public Collection<String> getPropertyReferences() 167 { 168 return this.propertyReferences; 169 } 170 171 /** 172 * <p> Retrieves the MetafacadeMapping belonging to the unique 173 * <code>key</code> created from the <code>mappingObject</code>'s 174 * class, <code>context</code> and given <code>stereotypes</code>. It's 175 * <strong>IMPORTANT </strong> to note that contexts have a higher priority 176 * than stereotypes. This allows us to retrieve mappings based on the 177 * following combinations: 178 * <ul> 179 * <li>A single stereotype no context</li> 180 * <li>A single stereotype with a context</li> 181 * <li>metafacade properties no context</li> 182 * <li>metafacade properties with a context</code> 183 * <li>multiple stereotypes no context</li> 184 * <li>multiple stereotypes with a context</li> 185 * </ul> 186 * </p> 187 * <p> NOTE: mapping properties are inherited from super metafacades. 188 * </p> 189 * 190 * @param mappingObject an instance of the class to which the mapping 191 * applies. 192 * @param stereotypes the stereotypes to check. 193 * @param context the context within the namespace for which the mapping 194 * applies (has 'root' in the name because of the fact that we also 195 * search the context inheritance hierarchy started with this 'root' 196 * context). 197 * @return MetafacadeMapping (or null if none was found matching the 198 * criteria). 199 */ 200 protected MetafacadeMapping getMapping( 201 final Object mappingObject, 202 final String context, 203 final Collection<String> stereotypes) 204 { 205 MetafacadeMapping mapping = this.getMapping( 206 null, 207 mappingObject, 208 context, 209 stereotypes); 210 if (mapping == null) 211 { 212 final Collection<String> hierarchy = this.getMappingObjectHierarchy(mappingObject); 213 if (hierarchy != null && !hierarchy.isEmpty()) 214 { 215 for (final Iterator<String> iterator = hierarchy.iterator(); iterator.hasNext() && mapping == null;) 216 { 217 mapping = 218 this.getMapping( 219 iterator.next(), 220 mappingObject, 221 context, 222 stereotypes); 223 } 224 } 225 } 226 return mapping; 227 } 228 229 /** 230 * The cache containing the hierarchies for each mapping object so that we 231 * don't need to retrieve more than once. 232 */ 233 private final Map<Object, Collection<String>> mappingObjectHierarchyCache = new HashMap<Object, Collection<String>>(); 234 235 /** 236 * The pattern used for substituting the package name. 237 */ 238 private static final String METAFACADE_PACKAGE_REPLACE_PATTERN = "\\{0\\}"; 239 240 /** 241 * The pattern used for substituting the metafacade name. 242 */ 243 private static final String METAFACADE_NAME_REPLACE_PATTERN = "\\{1\\}"; 244 245 /** 246 * Retrieves the hierarchy of class names of the given 247 * <code>mappingObject</code>. 248 * 249 * @param mappingObject the object from which to retrieve the hierarchy. 250 * @return a list containing all inherited class names. 251 */ 252 protected Collection<String> getMappingObjectHierarchy(final Object mappingObject) 253 { 254 Collection<String> hierarchy = this.mappingObjectHierarchyCache.get(mappingObject); 255 if (hierarchy == null) 256 { 257 // - we construct the mapping object name from the metafacade interface 258 // (using the underlying UML implementation name pattern). 259 final String pattern = this.getMetaclassPattern(); 260 if (StringUtils.isNotBlank(pattern)) 261 { 262 hierarchy = new ArrayList<String>(); 263 List<Class> metafacadeInterfaces = ClassUtils.getAllInterfaces(mappingObject.getClass()); 264 for (final Class metafacadeInterface : metafacadeInterfaces) 265 { 266 final String packageName = ClassUtils.getPackageName(metafacadeInterface); 267 final String name = ClassUtils.getShortClassName(metafacadeInterface); 268 269 // - replace references {0} with the package name and 270 // references of {1} with the name of the class 271 final String metafacadeImplementationName = 272 pattern != null 273 ? pattern.replaceAll( 274 METAFACADE_PACKAGE_REPLACE_PATTERN, 275 packageName).replaceAll( 276 METAFACADE_NAME_REPLACE_PATTERN, 277 name) : metafacadeInterface.getName(); 278 hierarchy.add(metafacadeImplementationName); 279 } 280 this.mappingObjectHierarchyCache.put( 281 mappingObject, 282 hierarchy); 283 } 284 } 285 return hierarchy; 286 } 287 288 /** 289 * <p> 290 * Stores the mappings which are currently "in process" (within the 291 * {@link #getMapping(Object, String, Collection)}. This means the mapping 292 * is being processed by the {@link #getMapping(Object, String, Collection)} 293 * operation. We store these "in process" mappings in order to keep track of 294 * the mappings currently being evaluated so we avoid stack over flow errors 295 * {@link #getMapping(Object, String, Collection)}when finding mappings 296 * that are mapped to super metafacade properties. 297 * </p> 298 * <p> 299 * Note: visibility is defined as <code>protected</code> in order to 300 * improve inner class access performance. 301 * </p> 302 */ 303 protected final Collection<MetafacadeMapping> inProcessMappings = new ArrayList<MetafacadeMapping>(); 304 305 /** 306 * <p> 307 * Stores the metafacades which are currently "in process" (within the 308 * {@link #getMapping(Object, String, Collection)}. This means the 309 * metafacade being processed by the {@link #getMapping(Object, String, 310 * Collection)}operation. We store these "in process" metafacades in order 311 * to keep track of the metafacades currently being evaluated so we avoid 312 * stack over flow errors {@link #getMapping(Object, String, Collection)}when 313 * finding metafacades that are mapped to super metafacade properties. 314 * </p> 315 * <p> 316 * Note: visibility is defined as <code>protected</code> in order to 317 * improve inner class access performance. 318 * </p> 319 */ 320 protected final Collection<MetafacadeBase> inProcessMetafacades = new ArrayList<MetafacadeBase>(); 321 322 /** 323 * <p> 324 * Retrieves the MetafacadeMapping belonging to the unique <code>key</code> 325 * created from the <code>mappingObject</code>'s class, 326 * <code>context</code> and given <code>stereotypes</code>. It's 327 * <strong>IMPORTANT </strong> to note that contexts have a higher priority 328 * than stereotypes. This allows us to retrieve mappings based on the 329 * following combinations: 330 * <ul> 331 * <li>A single stereotype no context</li> 332 * <li>A single stereotype with a context</li> 333 * <li>metafacade properties no context</li> 334 * <li>metafacade properties with a context</li> 335 * <li>multiple stereotypes no context</li> 336 * <li>multiple stereotypes with a context</li> 337 * </ul> 338 * </p> 339 * <p> 340 * NOTE: mapping properties are inherited from super metafacades. 341 * </p> 342 * 343 * @param mappingClassName the name of the mapping class to use instead of 344 * the actual class name taken from the <code>mappingObject</code>. 345 * If null then the class name from the <code>mappingObject</code> 346 * is used. 347 * @param mappingObject an instance of the class to which the mapping 348 * applies. 349 * @param stereotypes the stereotypes to check. 350 * @param context the context within the namespace for which the mapping 351 * applies (has 'root' in the name because of the fact that we also 352 * search the context inheritance hierarchy started with this 'root' 353 * context). 354 * @return MetafacadeMapping (or null if none was found matching the 355 * criteria). 356 */ 357 private MetafacadeMapping getMapping( 358 final String mappingClassName, 359 final Object mappingObject, 360 final String context, 361 final Collection<String> stereotypes) 362 { 363 final String metaclassName = mappingClassName != null ? mappingClassName : mappingObject.getClass().getName(); 364 365 // - Verify we can at least find the meta class, so we don't perform the 366 // rest of the search for nothing 367 final boolean validMetaclass = 368 this.findMapping( 369 new Condition() 370 { 371 public boolean evaluate(final MetafacadeMapping mapping) 372 { 373 return mapping.getMappingClassNames().contains(metaclassName); 374 } 375 }) != null; 376 MetafacadeMapping mapping = null; 377 if (validMetaclass) 378 { 379 final boolean emptyStereotypes = stereotypes == null || stereotypes.isEmpty(); 380 381 // - first try to find the mapping by context and stereotypes 382 if (context != null && !emptyStereotypes) 383 { 384 mapping = 385 this.findMapping( 386 new Condition() 387 { 388 public boolean evaluate(final MetafacadeMapping mapping) 389 { 390 boolean valid = false; 391 if (mapping.getMappingClassNames().contains(metaclassName) && mapping.hasContext() && 392 mapping.hasStereotypes() && !mapping.hasMappingProperties()) 393 { 394 valid = 395 getContextHierarchy(context).contains(mapping.getContext()) && 396 stereotypes!=null && stereotypes.containsAll(mapping.getStereotypes()); 397 } 398 return valid; 399 } 400 }); 401 } 402 403 // - check for context and metafacade properties 404 if (mapping == null && context != null) 405 { 406 mapping = 407 this.findMapping( 408 new Condition() 409 { 410 public boolean evaluate(final MetafacadeMapping mapping) 411 { 412 boolean valid = false; 413 if (mapping.getMappingClassNames().contains(metaclassName) && !mapping.hasStereotypes() && 414 mapping.hasContext() && mapping.hasMappingProperties() && 415 !inProcessMappings.contains(mapping)) 416 { 417 if (getContextHierarchy(context).contains(mapping.getContext())) 418 { 419 inProcessMappings.add(mapping); 420 final MetafacadeBase metafacade = 421 MetafacadeFactory.getInstance().createMetafacade( 422 mappingObject, 423 mapping); 424 inProcessMetafacades.add(metafacade); 425 426 // reset the "in process" mappings 427 inProcessMappings.clear(); 428 valid = 429 MetafacadeUtils.propertiesValid( 430 metafacade, 431 mapping); 432 } 433 } 434 return valid; 435 } 436 }); 437 } 438 439 // - check just the context alone 440 if (mapping == null && context != null) 441 { 442 mapping = 443 this.findMapping( 444 new Condition() 445 { 446 public boolean evaluate(final MetafacadeMapping mapping) 447 { 448 boolean valid = false; 449 if (mapping.getMappingClassNames().contains(metaclassName) && mapping.hasContext() && 450 !mapping.hasStereotypes() && !mapping.hasMappingProperties()) 451 { 452 valid = getContextHierarchy(context).contains(mapping.getContext()); 453 } 454 return valid; 455 } 456 }); 457 } 458 459 // check only stereotypes 460 if (mapping == null && !emptyStereotypes) 461 { 462 mapping = 463 this.findMapping( 464 new Condition() 465 { 466 public boolean evaluate(final MetafacadeMapping mapping) 467 { 468 boolean valid = false; 469 if (mapping.getMappingClassNames().contains(metaclassName) && mapping.hasStereotypes() && 470 !mapping.hasContext() && !mapping.hasMappingProperties()) 471 { 472 valid = stereotypes!=null && stereotypes.containsAll(mapping.getStereotypes()); 473 } 474 return valid; 475 } 476 }); 477 } 478 479 // - now check for metafacade properties 480 if (mapping == null) 481 { 482 mapping = 483 this.findMapping( 484 new Condition() 485 { 486 public boolean evaluate(final MetafacadeMapping mapping) 487 { 488 boolean valid = false; 489 if (mapping.getMappingClassNames().contains(metaclassName) && !mapping.hasStereotypes() && 490 !mapping.hasContext() && mapping.hasMappingProperties() && 491 (!inProcessMappings.contains(mapping))) 492 { 493 inProcessMappings.add(mapping); 494 final MetafacadeBase metafacade = 495 MetafacadeFactory.getInstance().createMetafacade( 496 mappingObject, 497 mapping); 498 inProcessMetafacades.add(metafacade); 499 500 // reset the "in process" mappings 501 inProcessMappings.clear(); 502 valid = 503 MetafacadeUtils.propertiesValid( 504 metafacade, 505 mapping); 506 } 507 return valid; 508 } 509 }); 510 } 511 512 // - finally find the mapping with just the class 513 if (mapping == null) 514 { 515 mapping = 516 this.findMapping( 517 new Condition() 518 { 519 public boolean evaluate(final MetafacadeMapping mapping) 520 { 521 return mapping.getMappingClassNames().contains(metaclassName) && !mapping.hasContext() && 522 !mapping.hasStereotypes() && !mapping.hasMappingProperties(); 523 } 524 }); 525 } 526 } 527 528 // - if it's still null, try with the parent 529 if (mapping == null && this.getParent() != null) 530 { 531 mapping = 532 this.getParent().getMapping( 533 metaclassName, 534 mappingObject, 535 context, 536 stereotypes); 537 } 538 539 // - reset the "in process" metafacades 540 this.inProcessMetafacades.clear(); 541 return mapping; 542 } 543 544 /** 545 * Finds the first mapping in the internal {@link #mappings} collection that 546 * matches the given condition. 547 * 548 * @param condition the condition 549 * @return the found mapping instance 550 */ 551 private MetafacadeMapping findMapping(final Condition condition) 552 { 553 MetafacadeMapping found = null; 554 for (final MetafacadeMapping mapping : this.mappings) 555 { 556 if (condition.evaluate(mapping)) 557 { 558 found = mapping; 559 break; 560 } 561 } 562 return found; 563 } 564 565 /** 566 * Provides a means to evaluate whether or not a condition is true. 567 */ 568 static interface Condition 569 { 570 /** 571 * @param mapping 572 * @return true/false 573 */ 574 public boolean evaluate(final MetafacadeMapping mapping); 575 } 576 577 /** 578 * <p> 579 * Loads all property references into the given <code>mapping</code> 580 * inherited from any super metafacade of the given mapping's metafacade. 581 * </p> 582 * 583 * @param mapping the MetafacadeMapping to which we'll add the inherited 584 * property references. 585 */ 586 private void loadInheritedPropertyReferences(final MetafacadeMapping mapping) 587 { 588 if (mapping != null) 589 { 590 final Class[] interfaces = this.getInterfacesReversed(mapping.getMetafacadeClass().getName()); 591 if (interfaces != null && interfaces.length > 0) 592 { 593 for (final Class metafacadeClass : interfaces) 594 { 595 final MetafacadeMapping contextMapping = 596 this.mappingsByMetafacadeClass.get(metafacadeClass); 597 if (contextMapping != null) 598 { 599 // add all property references 600 mapping.addPropertyReferences(contextMapping.getPropertyReferences()); 601 } 602 } 603 } 604 } 605 } 606 607 /** 608 * The cache containing the hierarchies for each context so that we don't 609 * need to retrieve more than once. 610 */ 611 private final Map<String, List<String>> contextHierarchyCache = new HashMap<String, List<String>>(); 612 613 /** 614 * Retrieves all inherited contexts (including the root <code>context</code>) 615 * from the given <code>context</code> and returns a list containing all 616 * of them. Note that the visibility of this operation is protected to 617 * improve inner class access performance. 618 * 619 * @param context the root contexts 620 * @return a list containing all inherited contexts 621 */ 622 protected final List<String> getContextHierarchy(final String context) 623 { 624 List<String> contexts = this.contextHierarchyCache.get(context); 625 if (contexts == null) 626 { 627 final List<Class> interfaces = ClassUtils.getInterfaces(context); 628 contexts = new ArrayList<String>(interfaces.size()); 629 for (Class anInterface : interfaces) 630 { 631 contexts.add(anInterface.getName()); 632 } 633 this.contextHierarchyCache.put( 634 context, 635 contexts); 636 } 637 return contexts; 638 } 639 640 /** 641 * The cache of interfaces for the given className in reversed order. 642 */ 643 private final Map<String, Class[]> reversedInterfaceArrayCache = new HashMap<String, Class[]>(); 644 645 /** 646 * Gets the interfaces for the given <code>className</code> in reverse 647 * order. 648 * 649 * @param className the name of the class for which to retrieve the 650 * interfaces 651 * @return the array containing the reversed interfaces. 652 */ 653 private Class[] getInterfacesReversed(final String className) 654 { 655 Class[] interfaces = this.reversedInterfaceArrayCache.get(className); 656 if (interfaces == null) 657 { 658 interfaces = ClassUtils.getInterfacesReversed(className); 659 this.reversedInterfaceArrayCache.put( 660 className, 661 interfaces); 662 } 663 return interfaces; 664 } 665 666 /** 667 * Adds a language mapping reference. This are used to populate metafacade 668 * impl classes with mapping files (such as those that map from model types 669 * to Java, JDBC, SQL types, etc). If its added here as opposed to each 670 * child MetafacadeMapping, then the reference will apply to all mappings. 671 * 672 * @param reference the name of the reference. 673 */ 674 public void addPropertyReference(final String reference) 675 { 676 this.propertyReferences.add(reference); 677 } 678 679 /** 680 * <p> Attempts to get the MetafacadeMapping identified by the given 681 * <code>mappingClass</code>,<code>context</code> and 682 * <code>stereotypes<code>, from the mappings for the given <code>namespace</code>. If it can <strong>not</strong> 683 * be found, it will search the default mappings and return that instead. </p> 684 * <p> 685 * <strong>IMPORTANT:</strong> The <code>context</code> will take precedence over any <code>stereotypes</code> with 686 * the mapping. </p> 687 * 688 * @param mappingObject the meta object for the mapping we are trying to find. 689 * @param namespace the namespace (i.e. a cartridge, name, etc.) 690 * @param context to which the mapping applies (note this takes precedence over stereotypes). 691 * @param stereotypes collection of stereotype names. We'll check to see if the mapping for the given 692 * <code>mappingClass</code> is defined for it. 693 * @return mapping 694 */ 695 public MetafacadeMapping getMetafacadeMapping( 696 final Object mappingObject, 697 final String namespace, 698 final String context, 699 final Collection<String> stereotypes) 700 { 701 if (this.getLogger().isDebugEnabled()) 702 { 703 this.getLogger().debug( 704 "performing 'MetafacadeMappings.getMetafacadeMapping' with mappingObject '" + mappingObject + 705 "', stereotypes '" + stereotypes + "', namespace '" + namespace + "' and context '" + context + '\''); 706 } 707 708 MetafacadeMapping mapping = null; 709 710 final MetafacadeMappings mappings = this.getNamespaceMappings(namespace); 711 712 // first try the namespace mappings 713 if (mappings != null) 714 { 715 // - set the parent namespace 716 mappings.parentNamespace = this.getNamespace(); 717 mapping = 718 mappings.getMapping( 719 mappingObject, 720 context, 721 stereotypes); 722 } 723 724 // - if we've found a namespace mapping, try to get any shared mappings 725 // that this namespace mapping may extend and copy over any property 726 // references from the shared mapping to the namespace mapping. 727 if (mapping != null) 728 { 729 final Collection<String> propertyReferences = mapping.getPropertyReferences(); 730 final MetafacadeMapping defaultMapping = this.getMapping( 731 mappingObject, 732 context, 733 stereotypes); 734 if (defaultMapping != null) 735 { 736 Collection<String> defaultPropertyReferences = defaultMapping.getPropertyReferences(); 737 final Class metafacadeInterface = 738 this.metafacadeClasses.getMetafacadeClass(mapping.getMetafacadeClass().getName()); 739 final Class defaultMetafacadeInterface = 740 this.metafacadeClasses.getMetafacadeClass(defaultMapping.getMetafacadeClass().getName()); 741 if (defaultMetafacadeInterface.isAssignableFrom(metafacadeInterface)) 742 { 743 mapping.addPropertyReferences(defaultPropertyReferences); 744 745 // add the namespace property references back so 746 // that the default ones don't override the 747 // namespace specific ones. 748 mapping.addPropertyReferences(propertyReferences); 749 } 750 } 751 } 752 753 // if the namespace mappings weren't found, try the default 754 if (mapping == null) 755 { 756 if (this.getLogger().isDebugEnabled()) 757 { 758 this.getLogger().debug("namespace mapping not found --> finding default"); 759 } 760 mapping = 761 this.getMapping( 762 mappingObject, 763 context, 764 stereotypes); 765 } 766 767 if (this.getLogger().isDebugEnabled()) 768 { 769 this.getLogger().debug("found mapping --> '" + mapping + '\''); 770 } 771 return mapping; 772 } 773 774 /** 775 * Gets the MetafacadeMappings instance belonging to the 776 * <code>namespace</code>. 777 * 778 * @param namespace the namespace name to check. 779 * @return the found MetafacadeMappings. 780 */ 781 private MetafacadeMappings getNamespaceMappings(final String namespace) 782 { 783 return this.namespaceMetafacadeMappings.get(namespace); 784 } 785 786 /** 787 * Stores the possible parents of this metafacade mappings instance (i.e. mappings for uml-1.4, emf-uml2, etc). 788 */ 789 private Map<String, MetafacadeMappings> parents = new HashMap<String, MetafacadeMappings>(); 790 791 /** 792 * Retrieves the appropriate parent based on the current {@link #getNamespace()}. 793 * 794 * @return the parent metafacade mappings. 795 */ 796 private MetafacadeMappings getParent() 797 { 798 return this.parents.get(this.parentNamespace); 799 } 800 801 /** 802 * Adds a MetafacadeMappings instance to the namespace metafacade mappings 803 * of this instance. 804 * 805 * @param namespace the namespace name to which the <code>mappings</code> 806 * will belong. 807 * @param mappings the MetafacadeMappings instance to add. 808 */ 809 private void addNamespaceMappings( 810 final String namespace, 811 final MetafacadeMappings mappings) 812 { 813 if (mappings != null) 814 { 815 // - set the parent by its namespace (the parent is different depending on the current metafacade model namespace) 816 mappings.parents.put( 817 this.getNamespace(), 818 this); 819 this.namespaceMetafacadeMappings.put( 820 namespace, 821 mappings); 822 } 823 } 824 825 /** 826 * Initializes this mappings instance, which includes discovery of all 827 * metafacade mappings instances on the classpath. 828 */ 829 public void initialize() 830 { 831 final List<String> modelTypeNamespaces = new ArrayList<String>(); 832 final Collection<MetafacadeMappings> metafacades = ComponentContainer.instance().findComponentsOfType(MetafacadeMappings.class); 833 for (final MetafacadeMappings mappings : metafacades) 834 { 835 final String namespace = mappings.getNamespace(); 836 if (MetafacadeUtils.isMetafacadeModelPresent(namespace)) 837 { 838 modelTypeNamespaces.add(namespace); 839 } 840 } 841 842 final String[] modelNamespaces = modelTypeNamespaces.toArray(new String[modelTypeNamespaces.size()]); 843 MetafacadeImpls.instance().discover(modelNamespaces); 844 this.initializeMappings(modelNamespaces); 845 } 846 847 /** 848 * Registers all namespace properties in the shared {@link MetafacadeFactory} instance. 849 */ 850 final void registerAllProperties() 851 { 852 // - register all namespace property references defined in the descriptors 853 final Namespaces namespaces = Namespaces.instance(); 854 for (Namespace namespace1 : namespaces.getNamespaces()) 855 { 856 final String mappingsNamespace = namespace1.getName(); 857 858 // - add the default mappings 859 final Collection<MetafacadeMapping> mappings = new ArrayList<MetafacadeMapping>(this.mappings); 860 final MetafacadeMappings metafacadeMappings = this.getNamespaceMappings(mappingsNamespace); 861 862 // - add all the references from the default namespace 863 final Collection<String> propertyReferences = new ArrayList<String>(this.propertyReferences); 864 865 // - if we have namespace mappings, add them 866 if (metafacadeMappings != null) 867 { 868 mappings.addAll(metafacadeMappings.mappings); 869 propertyReferences.addAll(metafacadeMappings.propertyReferences); 870 } 871 872 for (final MetafacadeMapping mapping : mappings) 873 { 874 final String metafacadeInterface = 875 this.metafacadeClasses.getMetafacadeClass(mapping.getMetafacadeClass().getName()).getName(); 876 877 // - first register the references defined globally in the 878 // descriptor for each interface 879 // in the hierarchy 880 final Class[] interfaces = this.getInterfacesReversed(metafacadeInterface); 881 for (final Class anInterface : interfaces) 882 { 883 this.registerProperties( 884 mappingsNamespace, 885 propertyReferences, 886 anInterface.getName()); 887 } 888 889 // - next register the references defined only within each mapping 890 // - remember to first load the inherited property references 891 // into the mapping 892 this.loadInheritedPropertyReferences(mapping); 893 this.registerProperties( 894 mappingsNamespace, 895 mapping.getPropertyReferences(), 896 metafacadeInterface); 897 } 898 } 899 } 900 901 /** 902 * The name of the metaclass pattern. 903 */ 904 private String metaclassPattern; 905 906 /** 907 * First attempts to retrieve the metaclass pattern from this instance, and 908 * if not found, attempts to retrieve it from the parent instance (since the 909 * parent instance should always have been set at least once from a shared 910 * metafacades instance). 911 * 912 * @return the metaclass pattern. 913 */ 914 private String getMetaclassPattern() 915 { 916 if (this.metaclassPattern == null && this.getParent() != null) 917 { 918 this.metaclassPattern = this.getParent().metaclassPattern; 919 } 920 return this.metaclassPattern; 921 } 922 923 /** 924 * Sets the pattern of the metaclass implementations based on a metaclass 925 * interface name. This should only be set on a metafacade mappings 926 * instances that is marked as shared. 927 * 928 * @param metaclassPattern the pattern for the meta classes. 929 */ 930 public void setMetaclassPattern(final String metaclassPattern) 931 { 932 this.metaclassPattern = metaclassPattern; 933 } 934 935 /** 936 * Initializes all the metafacade mapping instances under the appropriate model type (defined 937 * in the <code>modelTypes</code> collection. 938 * 939 * @param metafacadeModelNamespaces a list of each namespace containing a metafacade model facade implementation. 940 */ 941 private void initializeMappings(final String[] metafacadeModelNamespaces) 942 { 943 ExceptionUtils.checkNull( 944 "modelTypes", 945 metafacadeModelNamespaces); 946 final Collection<MetafacadeMappings> metafacades = ComponentContainer.instance().findComponentsOfType(MetafacadeMappings.class); 947 948 // - we need to load up the allMetafacadeMappingInstances before we do 949 // anything else 950 for (final MetafacadeMappings mappings : metafacades) 951 { 952 for (final MetafacadeMapping mapping : mappings.mappings) 953 { 954 if (mapping.isMappingClassNamePresent()) 955 { 956 Set<String> mappingClassNames = MetafacadeMappings.allMetafacadeMappingInstances.get(mapping.getMetafacadeClass()); 957 if (mappingClassNames == null) 958 { 959 mappingClassNames = new HashSet<String>(); 960 MetafacadeMappings.allMetafacadeMappingInstances.put(mapping.getMetafacadeClass(), mappingClassNames); 961 } 962 963 mappingClassNames.addAll(mapping.getMappingClassNames()); 964 } 965 } 966 } 967 968 final List<String> modelNamespaces = new ArrayList<String>(Arrays.asList(metafacadeModelNamespaces)); 969 try 970 { 971 final Namespaces namespaces = Namespaces.instance(); 972 for (final String modelNamespace : metafacadeModelNamespaces) 973 { 974 if (modelNamespace != null) 975 { 976 // - remove the current model type so that we don't keep out the namespace 977 // that stores the metafacade model 978 modelNamespaces.remove(modelNamespace); 979 980 MetafacadeMappings modelMetafacadeMappings = 981 this.modelMetafacadeMappings.get(modelNamespace); 982 if (modelMetafacadeMappings == null) 983 { 984 modelMetafacadeMappings = MetafacadeMappings.newInstance(); 985 986 // - set the namespace 987 modelMetafacadeMappings.setNamespace(modelNamespace); 988 this.modelMetafacadeMappings.put( 989 modelNamespace, 990 modelMetafacadeMappings); 991 } 992 993 for (final MetafacadeMappings mappings : metafacades) 994 { 995 final String namespace = mappings.getNamespace(); 996 997 if (!modelNamespaces.contains(namespace)) 998 { 999 // - if we have 'shared' mappings or only a single set available, they are copied 1000 // to this mappings instance. 1001 if (namespaces.isShared(namespace) || metafacades.size() == 1) 1002 { 1003 // - copy over any 'shared' mappings to this root instance 1004 modelMetafacadeMappings.copyMappings(mappings); 1005 1006 // - set the metaclass pattern from the 'shared' or single 1007 // instance of metafacades 1008 final String metaclassPattern = mappings.metaclassPattern; 1009 if (StringUtils.isNotBlank(metaclassPattern)) 1010 { 1011 modelMetafacadeMappings.setMetaclassPattern(mappings.metaclassPattern); 1012 } 1013 } 1014 else 1015 { 1016 // add all others as namespace mappings 1017 modelMetafacadeMappings.addNamespaceMappings( 1018 namespace, 1019 mappings); 1020 } 1021 } 1022 } 1023 1024 // - add the metafacade model namespace back 1025 modelNamespaces.add(modelNamespace); 1026 if (StringUtils.isBlank(modelMetafacadeMappings.getNamespace())) 1027 { 1028 throw new MetafacadeMappingsException( 1029 "No shared metafacades found, please check your classpath, at least " + 1030 "one set of metafacades must be marked as 'shared'"); 1031 } 1032 if (StringUtils.isBlank(modelMetafacadeMappings.metaclassPattern)) 1033 { 1034 throw new MetafacadeMappingsException("At least one set of metafacades marked as shared " + 1035 "must have the 'metaclassPattern' attribute defined"); 1036 } 1037 } 1038 } 1039 } 1040 catch (final Throwable throwable) 1041 { 1042 throw new MetafacadeMappingsException(throwable); 1043 } 1044 this.getLogger().debug("initializeMappings " + " size=" + MetafacadeMappings.allMetafacadeMappingInstances.size()); 1045 } 1046 1047 /** 1048 * Stores all metafacade mapping instances 1049 */ 1050 private static final Map<Class, Set<String>> allMetafacadeMappingInstances = new HashMap<Class, Set<String>>(); 1051 1052 /** 1053 * Stores every metafacade mapping instance, this is used from 1054 * {@link MetafacadeUtils#getInheritedMappingClassNames(MetafacadeMapping)}. 1055 * 1056 * @return all metafacade mapping instances. 1057 */ 1058 static Map<Class, Set<String>> getAllMetafacadeMappingInstances() 1059 { 1060 return allMetafacadeMappingInstances; 1061 } 1062 1063 /** 1064 * The shared metafacade impls instance. 1065 */ 1066 private MetafacadeImpls metafacadeClasses = MetafacadeImpls.instance(); 1067 1068 /** 1069 * Stores the metafacadeMapping instances by model type. 1070 */ 1071 private Map<String, MetafacadeMappings> modelMetafacadeMappings = new LinkedHashMap<String, MetafacadeMappings>(); 1072 1073 /** 1074 * Should be used used instead of "this", retrieves the appropriate 1075 * metafacade mappings instance based on the current model type. 1076 * 1077 * @param metafacadeModelNamespace the namespace that contains a metafacade model facade implementation. 1078 * @return the {@link MetafacadeMappings} instance. 1079 */ 1080 public MetafacadeMappings getModelMetafacadeMappings(final String metafacadeModelNamespace) 1081 { 1082 final MetafacadeMappings instance = 1083 this.modelMetafacadeMappings.get(metafacadeModelNamespace); 1084 if (instance == null) 1085 { 1086 throw new MetafacadeMappingsException("Namespace '" + metafacadeModelNamespace + 1087 "' is not a registered metafacade model namespace"); 1088 } 1089 return instance; 1090 } 1091 1092 /** 1093 * Stores the namespace of the parent mappings. 1094 */ 1095 private String parentNamespace; 1096 1097 /** 1098 * Gets the defaultMetafacadeClass, first looks for it in the namespace 1099 * mapping, if it can't find it it then takes the default mappings, setting. 1100 * @param namespace mapping to check for defaultMetafacadeClass 1101 * @return Returns the defaultMetafacadeClass. 1102 */ 1103 final Class getDefaultMetafacadeClass(final String namespace) 1104 { 1105 Class defaultMetafacadeClass = null; 1106 MetafacadeMappings mappings = this.getNamespaceMappings(namespace); 1107 if (mappings != null) 1108 { 1109 defaultMetafacadeClass = mappings.defaultMetafacadeClass; 1110 } 1111 if (defaultMetafacadeClass == null) 1112 { 1113 defaultMetafacadeClass = this.defaultMetafacadeClass; 1114 } 1115 return defaultMetafacadeClass; 1116 } 1117 1118 /** 1119 * Sets the default metafacade class to use if no other is found for the 1120 * mapping class. 1121 * 1122 * @param defaultMetafacadeClass the default metafacade class. 1123 */ 1124 public void setDefaultMetafacadeClass(final String defaultMetafacadeClass) 1125 { 1126 try 1127 { 1128 this.defaultMetafacadeClass = ClassUtils.loadClass(StringUtils.trimToEmpty(defaultMetafacadeClass)); 1129 } 1130 catch (final Throwable throwable) 1131 { 1132 throw new MetafacadeMappingsException(throwable); 1133 } 1134 } 1135 1136 /** 1137 * Retrieves all child {@link MetafacadeMapping} instances belonging to this 1138 * metafacade mappings instance. 1139 * 1140 * @return the collection of {@link MetafacadeMapping} instances 1141 */ 1142 protected Collection<MetafacadeMapping> getMappings() 1143 { 1144 return this.mappings; 1145 } 1146 1147 /** 1148 * Registers the defined property references properties in the metafacade 1149 * factory. 1150 * 1151 * @param propertyReferences the property references to register. 1152 * @param metafacadeName the name of the metafacade under which to register 1153 * the properties. 1154 * @param namespace the namespace of the property reference. 1155 */ 1156 final void registerProperties( 1157 final String namespace, 1158 final Collection<String> propertyReferences, 1159 final String metafacadeName) 1160 { 1161 final MetafacadeFactory factory = MetafacadeFactory.getInstance(); 1162 for (final String reference : propertyReferences) 1163 { 1164 final String value = Namespaces.instance().getPropertyValue( 1165 namespace, 1166 reference); 1167 if (value != null) 1168 { 1169 if (this.getLogger().isDebugEnabled()) 1170 { 1171 this.getLogger().debug( 1172 "setting context property '" + reference + "' with value '" + value + "' for namespace '" + 1173 namespace + "' on metafacade '" + metafacadeName + '\''); 1174 } 1175 } 1176 factory.registerProperty( 1177 namespace, 1178 metafacadeName, 1179 reference, 1180 value); 1181 } 1182 } 1183 1184 /** 1185 * Performs shutdown procedures for the factory. This should be called 1186 * <strong>ONLY</code> when {@link MetafacadeFactory#shutdown()}is called. 1187 */ 1188 final void shutdown() 1189 { 1190 this.mappings.clear(); 1191 this.inProcessMappings.clear(); 1192 this.inProcessMetafacades.clear(); 1193 this.namespaceMetafacadeMappings.clear(); 1194 this.propertyReferences.clear(); 1195 this.mappingObjectHierarchyCache.clear(); 1196 this.mappingsByMetafacadeClass.clear(); 1197 this.contextHierarchyCache.clear(); 1198 this.reversedInterfaceArrayCache.clear(); 1199 for (final MetafacadeMappings metafacadeMappings : this.modelMetafacadeMappings.values()) 1200 { 1201 metafacadeMappings.shutdown(); 1202 } 1203 this.modelMetafacadeMappings.clear(); 1204 } 1205 1206 /** 1207 * Returns the logger instance to be used for logging within this class. 1208 * 1209 * @return the plugin logger 1210 */ 1211 private Logger getLogger() 1212 { 1213 return AndroMDALogger.getNamespaceLogger(this.getNamespace()); 1214 } 1215 1216 /** 1217 * @see Object#toString() 1218 */ 1219 public String toString() 1220 { 1221 return super.toString() + '[' + this.getNamespace() + ']'; 1222 } 1223}