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