0001: package org.andromda.core.metafacade;
0002:
0003: import java.util.ArrayList;
0004: import java.util.Arrays;
0005: import java.util.Collection;
0006: import java.util.HashMap;
0007: import java.util.Iterator;
0008: import java.util.LinkedHashMap;
0009: import java.util.LinkedHashSet;
0010: import java.util.List;
0011: import java.util.ListIterator;
0012: import java.util.Map;
0013:
0014: import org.andromda.core.common.AndroMDALogger;
0015: import org.andromda.core.common.ClassUtils;
0016: import org.andromda.core.common.ComponentContainer;
0017: import org.andromda.core.common.ExceptionUtils;
0018: import org.andromda.core.configuration.Namespace;
0019: import org.andromda.core.configuration.Namespaces;
0020: import org.andromda.core.namespace.BaseNamespaceComponent;
0021: import org.apache.commons.lang.StringUtils;
0022: import org.apache.log4j.Logger;
0023:
0024: /**
0025: * The Metafacade mapping class. Used to map <code>metafacade</code> objects
0026: * to <code>metamodel</code> objects.
0027: *
0028: * @author Chad Brandon
0029: * @see MetafacadeMapping
0030: * @see org.andromda.core.common.XmlObjectFactory
0031: */
0032: public class MetafacadeMappings extends BaseNamespaceComponent {
0033: /**
0034: * Holds the references to the child MetafacadeMapping instances.
0035: */
0036: private final Collection mappings = new ArrayList();
0037:
0038: /**
0039: * Holds the namespace MetafacadeMappings. This are child MetafacadeMappings
0040: * keyed by namespace name.
0041: */
0042: private final Map namespaceMetafacadeMappings = new HashMap();
0043:
0044: /**
0045: * The default meta facade to use when there isn't a mapping found.
0046: */
0047: private Class defaultMetafacadeClass = null;
0048:
0049: /**
0050: * Constructs a new instance of this class.
0051: *
0052: * @return MetafacadeMappings
0053: */
0054: public static MetafacadeMappings newInstance() {
0055: return new MetafacadeMappings();
0056: }
0057:
0058: /**
0059: * Adds a MetafacadeMapping instance to the set of current mappings.
0060: *
0061: * @param mapping the MetafacadeMapping instance.
0062: */
0063: public void addMapping(final MetafacadeMapping mapping) {
0064: ExceptionUtils.checkNull("mapping", mapping);
0065: ExceptionUtils.checkNull("mapping.metafacadeClass", mapping
0066: .getMetafacadeClass());
0067: mapping.setMetafacadeMappings(this );
0068:
0069: // find any mappings that match, if they do we add the properties
0070: // from that mapping to the existing matched mapping (so we only
0071: // have one mapping containing properties that can be 'OR'ed together).
0072: final MetafacadeMapping foundMapping = this
0073: .findMapping(new Condition() {
0074: public boolean evaluate(
0075: final MetafacadeMapping object) {
0076: return mapping.match(object);
0077: }
0078: });
0079: if (foundMapping != null) {
0080: foundMapping.addMappingPropertyGroup(mapping
0081: .getMappingProperties());
0082: } else {
0083: this .mappings.add(mapping);
0084: this .mappingsByMetafacadeClass.put(this
0085: .getMetafacadeInterface(mapping
0086: .getMetafacadeClass()), mapping);
0087: }
0088: }
0089:
0090: /**
0091: * Gets the class of the metafacade interface that belongs to the given
0092: * <code>metafacadeClass</code>.
0093: *
0094: * @return the metafacade interface Class.
0095: */
0096: public Class getMetafacadeInterface(final Class metafacadeClass) {
0097: Class metafacadeInterface = null;
0098: if (metafacadeClass != null) {
0099: metafacadeInterface = metafacadeClass;
0100: final List interfaces = ClassUtils
0101: .getAllInterfaces(metafacadeClass);
0102: if (interfaces != null && !interfaces.isEmpty()) {
0103: metafacadeInterface = (Class) interfaces.iterator()
0104: .next();
0105: }
0106: }
0107: return metafacadeInterface;
0108: }
0109:
0110: /**
0111: * Stores mappings by the metafacade class so that we can retrieve the
0112: * inherited metafacade classes.
0113: */
0114: private final Map mappingsByMetafacadeClass = new HashMap();
0115:
0116: /**
0117: * Copies all data from <code>mappings<code> to this instance.
0118: *
0119: * @param mappings the mappings to add
0120: */
0121: private void copyMappings(final MetafacadeMappings mappings) {
0122: ExceptionUtils.checkNull("mappings", mappings);
0123: for (final Iterator iterator = mappings.mappings.iterator(); iterator
0124: .hasNext();) {
0125: final MetafacadeMapping mapping = (MetafacadeMapping) iterator
0126: .next();
0127: this .addMapping(mapping);
0128: }
0129: final Collection propertyReferences = mappings
0130: .getPropertyReferences();
0131: if (propertyReferences != null && !propertyReferences.isEmpty()) {
0132: this .propertyReferences.addAll(propertyReferences);
0133: }
0134: this .defaultMetafacadeClass = mappings.defaultMetafacadeClass;
0135: }
0136:
0137: /**
0138: * Contains references to properties populated in the Namespaces.
0139: */
0140: private final Collection propertyReferences = new LinkedHashSet();
0141:
0142: /**
0143: * Gets all property references defined in this mappings instance.
0144: *
0145: * @return the map of property references (names and values).
0146: */
0147: public Collection getPropertyReferences() {
0148: return this .propertyReferences;
0149: }
0150:
0151: /**
0152: * <p/> Retrieves the MetafacadeMapping belonging to the unique
0153: * <code>key</code> created from the <code>mappingObject</code>'s
0154: * class, <code>context</code> and given <code>stereotypes</code>. It's
0155: * <strong>IMPORTANT </strong> to note that contexts have a higher priority
0156: * than stereotypes. This allows us to retrieve mappings based on the
0157: * following combinations:
0158: * <ul>
0159: * <li>A single stereotype no context</li>
0160: * <li>A single stereotype with a context</li>
0161: * <li>metafacade properties no context</li>
0162: * <li>metafacade properties with a context</code>
0163: * <li>multiple stereotypes no context</li>
0164: * <li>multiple stereotypes with a context</li>
0165: * </ul>
0166: * </p>
0167: * <p/> NOTE: mapping properties are inherited from super metafacades.
0168: * </p>
0169: *
0170: * @param mappingObject an instance of the class to which the mapping
0171: * applies.
0172: * @param stereotypes the stereotypes to check.
0173: * @param context the context within the namespace for which the mapping
0174: * applies (has 'root' in the name because of the fact that we also
0175: * search the context inheritance hiearchy started with this 'root'
0176: * context).
0177: * @return MetafacadeMapping (or null if none was found matching the
0178: * criteria).
0179: */
0180: protected MetafacadeMapping getMapping(final Object mappingObject,
0181: final String context, final Collection stereotypes) {
0182: MetafacadeMapping mapping = this .getMapping(null,
0183: mappingObject, context, stereotypes);
0184: if (mapping == null) {
0185: final Collection hierarchy = this
0186: .getMappingObjectHierarchy(mappingObject);
0187: if (hierarchy != null && !hierarchy.isEmpty()) {
0188: for (final Iterator iterator = hierarchy.iterator(); iterator
0189: .hasNext()
0190: && mapping == null;) {
0191: mapping = this .getMapping((String) iterator.next(),
0192: mappingObject, context, stereotypes);
0193: }
0194: }
0195: }
0196: return mapping;
0197: }
0198:
0199: /**
0200: * The cache containing the hierarchies for each mapping object so that we
0201: * don't need to retrieve more than once.
0202: */
0203: private final Map mappingObjectHierachyCache = new HashMap();
0204:
0205: /**
0206: * The pattern used for substituting the package name.
0207: */
0208: private static final String METAFACADE_PACKAGE_REPLACE_PATTERN = "\\{0\\}";
0209:
0210: /**
0211: * The pattern used for substituting the metafacade name.
0212: */
0213: private static final String METAFACADE_NAME_REPLACE_PATTERN = "\\{1\\}";
0214:
0215: /**
0216: * Retrieves the hiearchy of class names of the given
0217: * <code>mappingObject</code>.
0218: *
0219: * @param mappingObject the object from which to retrieve the hierarchy.
0220: * @return a list containing all inherited class names.
0221: */
0222: protected List getMappingObjectHierarchy(final Object mappingObject) {
0223: List hierarchy = (List) this .mappingObjectHierachyCache
0224: .get(mappingObject);
0225: if (hierarchy == null) {
0226: // - we construct the mapping object name from the interface
0227: // (using the implementation name pattern).
0228: final String pattern = this .getMetaclassPattern();
0229: if (StringUtils.isNotBlank(pattern)) {
0230: hierarchy = new ArrayList(ClassUtils
0231: .getAllInterfaces(mappingObject.getClass()));
0232: for (final ListIterator iterator = hierarchy
0233: .listIterator(); iterator.hasNext();) {
0234: final Class metafacadeInterface = (Class) iterator
0235: .next();
0236: final String packageName = ClassUtils
0237: .getPackageName(metafacadeInterface);
0238: final String name = ClassUtils
0239: .getShortClassName(metafacadeInterface);
0240:
0241: // - replace references {0} with the package name and
0242: // references of {1} with the name of the class
0243: final String metafacadeImplementationName = pattern != null ? pattern
0244: .replaceAll(
0245: METAFACADE_PACKAGE_REPLACE_PATTERN,
0246: packageName).replaceAll(
0247: METAFACADE_NAME_REPLACE_PATTERN,
0248: name)
0249: : metafacadeInterface.getName();
0250: iterator.set(metafacadeImplementationName);
0251: }
0252: this .mappingObjectHierachyCache.put(mappingObject,
0253: hierarchy);
0254: }
0255: }
0256: return hierarchy;
0257: }
0258:
0259: /**
0260: * <p>
0261: * Stores the mappings which are currently "in process" (within the
0262: * {@link #getMapping(Object, String, Collection)}. This means the mapping
0263: * is being processed by the {@link #getMapping(Object, String, Collection)}
0264: * operation. We store these "in process" mappings in order to keep track of
0265: * the mappings currently being evaluated so we avoid stack over flow errors
0266: * {@link #getMapping(Object, String, Collection)}when finding mappings
0267: * that are mapped to super metafacade properties.
0268: * </p>
0269: * <p>
0270: * Note: visibility is defined as <code>protected</code> in order to
0271: * improve inner class access performance.
0272: * </p>
0273: */
0274: protected final Collection inProcessMappings = new ArrayList();
0275:
0276: /**
0277: * <p>
0278: * Stores the metafacades which are currently "in process" (within the
0279: * {@link #getMapping(Object, String, Collection)}. This means the
0280: * metafacade being processed by the {@link #getMapping(Object, String,
0281: * Collection)}operation. We store these "in process" metafacades in order
0282: * to keep track of the metafacades currently being evaluated so we avoid
0283: * stack over flow errors {@link #getMapping(Object, String, Collection)}when
0284: * finding metafacades that are mapped to super metafacade properties.
0285: * </p>
0286: * <p>
0287: * Note: visibility is defined as <code>protected</code> in order to
0288: * improve inner class access performance.
0289: * </p>
0290: */
0291: protected final Collection inProcessMetafacades = new ArrayList();
0292:
0293: /**
0294: * <p>
0295: * Retrieves the MetafacadeMapping belonging to the unique <code>key</code>
0296: * created from the <code>mappingObject</code>'s class,
0297: * <code>context</code> and given <code>stereotypes</code>. It's
0298: * <strong>IMPORTANT </strong> to note that contexts have a higher priority
0299: * than stereotypes. This allows us to retrieve mappings based on the
0300: * following combinations:
0301: * <ul>
0302: * <li>A single stereotype no context</li>
0303: * <li>A single stereotype with a context</li>
0304: * <li>metafacade properties no context</li>
0305: * <li>metafacade properties with a context</li>
0306: * <li>multiple stereotypes no context</li>
0307: * <li>multiple stereotypes with a context</li>
0308: * </ul>
0309: * </p>
0310: * <p>
0311: * NOTE: mapping properties are inherited from super metafacades.
0312: * </p>
0313: *
0314: * @param mappingClassName the name of the mapping class to use instead of
0315: * the actual class name taken from the <code>mappingObject</code>.
0316: * If null then the class name from the <code>mappingObject</code>
0317: * is used.
0318: * @param mappingObject an instance of the class to which the mapping
0319: * applies.
0320: * @param stereotypes the stereotypes to check.
0321: * @param context the context within the namespace for which the mapping
0322: * applies (has 'root' in the name because of the fact that we also
0323: * search the context inheritance hiearchy started with this 'root'
0324: * context).
0325: * @return MetafacadeMapping (or null if none was found matching the
0326: * criteria).
0327: */
0328: private MetafacadeMapping getMapping(final String mappingClassName,
0329: final Object mappingObject, final String context,
0330: final Collection stereotypes) {
0331: final String metaclassName = mappingClassName != null ? mappingClassName
0332: : mappingObject.getClass().getName();
0333:
0334: // - verfiy we can at least find the meta class, so we don't perform the
0335: // rest of the search for nothing
0336: final boolean validMetaclass = this
0337: .findMapping(new Condition() {
0338: public boolean evaluate(
0339: final MetafacadeMapping mapping) {
0340: return mapping.getMappingClassName().equals(
0341: metaclassName);
0342: }
0343: }) != null;
0344: MetafacadeMapping mapping = null;
0345: if (validMetaclass) {
0346: final boolean emptyStereotypes = stereotypes == null
0347: || stereotypes.isEmpty();
0348:
0349: // - first try to find the mapping by context and stereotypes
0350: if (context != null && !emptyStereotypes) {
0351: mapping = this .findMapping(new Condition() {
0352: public boolean evaluate(
0353: final MetafacadeMapping mapping) {
0354: boolean valid = false;
0355: if (metaclassName.equals(mapping
0356: .getMappingClassName())
0357: && mapping.hasContext()
0358: && mapping.hasStereotypes()
0359: && !mapping.hasMappingProperties()) {
0360: valid = getContextHierarchy(context)
0361: .contains(mapping.getContext())
0362: && stereotypes.containsAll(mapping
0363: .getStereotypes());
0364: }
0365: return valid;
0366: }
0367: });
0368: }
0369:
0370: // - check for context and metafacade properties
0371: if (mapping == null && context != null) {
0372: mapping = this .findMapping(new Condition() {
0373: public boolean evaluate(
0374: final MetafacadeMapping mapping) {
0375: boolean valid = false;
0376: if (metaclassName.equals(mapping
0377: .getMappingClassName())
0378: && !mapping.hasStereotypes()
0379: && mapping.hasContext()
0380: && mapping.hasMappingProperties()
0381: && (!inProcessMappings
0382: .contains(mapping))) {
0383: if (getContextHierarchy(context).contains(
0384: mapping.getContext())) {
0385: inProcessMappings.add(mapping);
0386: final MetafacadeBase metafacade = MetafacadeFactory
0387: .getInstance()
0388: .createMetafacade(
0389: mappingObject, mapping);
0390: inProcessMetafacades.add(metafacade);
0391:
0392: // reset the "in process" mappings
0393: inProcessMappings.clear();
0394: valid = MetafacadeUtils
0395: .propertiesValid(metafacade,
0396: mapping);
0397: }
0398: }
0399: return valid;
0400: }
0401: });
0402: }
0403:
0404: // - check just the context alone
0405: if (mapping == null && context != null) {
0406: mapping = this .findMapping(new Condition() {
0407: public boolean evaluate(
0408: final MetafacadeMapping mapping) {
0409: boolean valid = false;
0410: if (metaclassName.equals(mapping
0411: .getMappingClassName())
0412: && mapping.hasContext()
0413: && !mapping.hasStereotypes()
0414: && !mapping.hasMappingProperties()) {
0415: valid = getContextHierarchy(context)
0416: .contains(mapping.getContext());
0417: }
0418: return valid;
0419: }
0420: });
0421: }
0422:
0423: // check only stereotypes
0424: if (mapping == null && !emptyStereotypes) {
0425: mapping = this .findMapping(new Condition() {
0426: public boolean evaluate(
0427: final MetafacadeMapping mapping) {
0428: boolean valid = false;
0429: if (metaclassName.equals(mapping
0430: .getMappingClassName())
0431: && mapping.hasStereotypes()
0432: && !mapping.hasContext()
0433: && !mapping.hasMappingProperties()) {
0434: valid = stereotypes.containsAll(mapping
0435: .getStereotypes());
0436: }
0437: return valid;
0438: }
0439: });
0440: }
0441:
0442: // - now check for metafacade properties
0443: if (mapping == null) {
0444: mapping = this .findMapping(new Condition() {
0445: public boolean evaluate(
0446: final MetafacadeMapping mapping) {
0447: boolean valid = false;
0448: if (metaclassName.equals(mapping
0449: .getMappingClassName())
0450: && !mapping.hasStereotypes()
0451: && !mapping.hasContext()
0452: && mapping.hasMappingProperties()
0453: && (!inProcessMappings
0454: .contains(mapping))) {
0455: inProcessMappings.add(mapping);
0456: final MetafacadeBase metafacade = MetafacadeFactory
0457: .getInstance().createMetafacade(
0458: mappingObject, mapping);
0459: inProcessMetafacades.add(metafacade);
0460:
0461: // reset the "in process" mappings
0462: inProcessMappings.clear();
0463: valid = MetafacadeUtils.propertiesValid(
0464: metafacade, mapping);
0465: }
0466: return valid;
0467: }
0468: });
0469: }
0470:
0471: // - finally find the mapping with just the class
0472: if (mapping == null) {
0473: mapping = this .findMapping(new Condition() {
0474: public boolean evaluate(
0475: final MetafacadeMapping mapping) {
0476: return metaclassName.equals(mapping
0477: .getMappingClassName())
0478: && !mapping.hasContext()
0479: && !mapping.hasStereotypes()
0480: && !mapping.hasMappingProperties();
0481: }
0482: });
0483: }
0484: }
0485:
0486: // - if it's still null, try with the parent
0487: if (mapping == null && this .getParent() != null) {
0488: mapping = this .getParent().getMapping(metaclassName,
0489: mappingObject, context, stereotypes);
0490: }
0491:
0492: // - reset the "in process" metafacades
0493: this .inProcessMetafacades.clear();
0494: return mapping;
0495: }
0496:
0497: /**
0498: * Finds the first mapping in the internal {@link #mappings} collection that
0499: * matches the given condition.
0500: *
0501: * @param condition the condition
0502: * @return the found mapping instance
0503: */
0504: private MetafacadeMapping findMapping(final Condition condition) {
0505: MetafacadeMapping found = null;
0506: for (final Iterator iterator = this .mappings.iterator(); iterator
0507: .hasNext();) {
0508: final MetafacadeMapping mapping = (MetafacadeMapping) iterator
0509: .next();
0510: if (condition.evaluate(mapping)) {
0511: found = mapping;
0512: break;
0513: }
0514: }
0515: return found;
0516: }
0517:
0518: /**
0519: * Provides a means to evaluate whether or not a condition is true.
0520: */
0521: static interface Condition {
0522: public boolean evaluate(final MetafacadeMapping mapping);
0523: }
0524:
0525: /**
0526: * <p>
0527: * Loads all property references into the given <code>mapping</code>
0528: * inherited from any super metafacade of the given mapping's metafacade.
0529: * </p>
0530: *
0531: * @param mapping the MetafacadeMapping to which we'll add the inherited
0532: * property references.
0533: */
0534: private void loadInheritedPropertyReferences(
0535: final MetafacadeMapping mapping) {
0536: if (mapping != null) {
0537: final Class[] interfaces = this
0538: .getInterfacesReversed(mapping.getMetafacadeClass()
0539: .getName());
0540: if (interfaces != null && interfaces.length > 0) {
0541: for (int ctr = 0; ctr < interfaces.length; ctr++) {
0542: final Class metafacadeClass = interfaces[ctr];
0543: final MetafacadeMapping contextMapping = (MetafacadeMapping) this .mappingsByMetafacadeClass
0544: .get(metafacadeClass);
0545: if (contextMapping != null) {
0546: // add all property references
0547: mapping.addPropertyReferences(contextMapping
0548: .getPropertyReferences());
0549: }
0550: }
0551: }
0552: }
0553: }
0554:
0555: /**
0556: * The cache containing the hierachies for each context so that we don't
0557: * need to retrieve more than once.
0558: */
0559: private final Map contextHierachyCache = new HashMap();
0560:
0561: /**
0562: * Retrieves all inherited contexts (including the root <code>context</code>)
0563: * from the given <code>context</code> and returns a list containing all
0564: * of them. Note that the visibilty of this operation is protected to
0565: * improve inner class access performance.
0566: *
0567: * @param context the root contexts
0568: * @return a list containing all inherited contexts
0569: */
0570: protected final List getContextHierarchy(final String context) {
0571: List contexts = (List) this .contextHierachyCache.get(context);
0572: if (contexts == null) {
0573: contexts = ClassUtils.getInterfaces(context);
0574: if (contexts != null) {
0575: for (final ListIterator iterator = contexts
0576: .listIterator(); iterator.hasNext();) {
0577: iterator.set(((Class) iterator.next()).getName());
0578: }
0579: }
0580: this .contextHierachyCache.put(context, contexts);
0581: }
0582: return contexts;
0583: }
0584:
0585: /**
0586: * The cache of interfaces for the given className in reversed order.
0587: */
0588: private final Map reversedInterfaceArrayCache = new HashMap();
0589:
0590: /**
0591: * Gets the interfaces for the given <code>className</code> in reverse
0592: * order.
0593: *
0594: * @param className the name of the class for which to retrieve the
0595: * interfaces
0596: * @return the array containing the reversed interfaces.
0597: */
0598: private Class[] getInterfacesReversed(final String className) {
0599: Class[] interfaces = (Class[]) this .reversedInterfaceArrayCache
0600: .get(className);
0601: if (interfaces == null) {
0602: interfaces = ClassUtils.getInterfacesReversed(className);
0603: this .reversedInterfaceArrayCache.put(className, interfaces);
0604: }
0605: return interfaces;
0606: }
0607:
0608: /**
0609: * Adds a language mapping reference. This are used to populate metafacade
0610: * impl classes with mapping files (such as those that map from model types
0611: * to Java, JDBC, SQL types, etc). If its added here as opposed to each
0612: * child MetafacadeMapping, then the reference will apply to all mappings.
0613: *
0614: * @param reference the name of the reference.
0615: */
0616: public void addPropertyReference(final String reference) {
0617: this .propertyReferences.add(reference);
0618: }
0619:
0620: /**
0621: * <p/> Attempts to get the MetafacadeMapping identified by the given
0622: * <code>mappingClass</code>,<code>context</code> and
0623: * <code>stereotypes<code>, from the mappings for the given <code>namespace</code>. If it can <strong>not</strong>
0624: * be found, it will search the default mappings and return that instead. </p>
0625: * <p/>
0626: * <strong>IMPORTANT:</strong> The <code>context</code> will take precedence over any <code>stereotypes</code> with
0627: * the mapping. </p>
0628: *
0629: * @param mappingObject the meta object for the mapping we are trying to find.
0630: * @param namespace the namespace (i.e. a cartridge, name, etc.)
0631: * @param context to which the mapping applies (note this takes precendence over stereotypes).
0632: * @param stereotypes collection of sterotype names. We'll check to see if the mapping for the given
0633: * <code>mappingClass</code> is defined for it.
0634: */
0635: public MetafacadeMapping getMetafacadeMapping(
0636: final Object mappingObject, final String namespace,
0637: final String context, final Collection stereotypes) {
0638: if (this .getLogger().isDebugEnabled()) {
0639: this .getLogger().debug(
0640: "performing 'MetafacadeMappings.getMetafacadeMapping' with mappingObject '"
0641: + mappingObject + "', stereotypes '"
0642: + stereotypes + "', namespace '"
0643: + namespace + "' and context '" + context
0644: + "'");
0645: }
0646:
0647: MetafacadeMapping mapping = null;
0648:
0649: final MetafacadeMappings mappings = this
0650: .getNamespaceMappings(namespace);
0651:
0652: // first try the namespace mappings
0653: if (mappings != null) {
0654: // - set the parent namespace
0655: mappings.parentNamespace = this .getNamespace();
0656: mapping = mappings.getMapping(mappingObject, context,
0657: stereotypes);
0658: }
0659:
0660: // - if we've found a namespace mapping, try to get any shared mappings
0661: // that this namespace mapping may extend and copy over any property
0662: // references from the shared mapping to the namespace mapping.
0663: if (mapping != null) {
0664: final Collection propertyReferences = mapping
0665: .getPropertyReferences();
0666: final MetafacadeMapping defaultMapping = this .getMapping(
0667: mappingObject, context, stereotypes);
0668: if (defaultMapping != null) {
0669: Collection defaultPropertyReferences = defaultMapping
0670: .getPropertyReferences();
0671: final Class metafacadeInterface = this .metafacadeClasses
0672: .getMetafacadeClass(mapping
0673: .getMetafacadeClass().getName());
0674: final Class defaultMetafacadeInterface = this .metafacadeClasses
0675: .getMetafacadeClass(defaultMapping
0676: .getMetafacadeClass().getName());
0677: if (defaultMetafacadeInterface
0678: .isAssignableFrom(metafacadeInterface)) {
0679: mapping
0680: .addPropertyReferences(defaultPropertyReferences);
0681:
0682: // add the namespace property references back so
0683: // that the default ones don't override the
0684: // namespace specific ones.
0685: mapping.addPropertyReferences(propertyReferences);
0686: }
0687: }
0688: }
0689:
0690: // if the namespace mappings weren't found, try the default
0691: if (mapping == null) {
0692: if (this .getLogger().isDebugEnabled()) {
0693: this
0694: .getLogger()
0695: .debug(
0696: "namespace mapping not found --> finding default");
0697: }
0698: mapping = this .getMapping(mappingObject, context,
0699: stereotypes);
0700: }
0701:
0702: if (this .getLogger().isDebugEnabled()) {
0703: this .getLogger().debug(
0704: "found mapping --> '" + mapping + "'");
0705: }
0706: return mapping;
0707: }
0708:
0709: /**
0710: * Gets the MetafacadeMappings instance belonging to the
0711: * <code>namespace</code>.
0712: *
0713: * @param namespace the namespace name to check.
0714: * @return the found MetafacadeMappings.
0715: */
0716: private MetafacadeMappings getNamespaceMappings(
0717: final String namespace) {
0718: return (MetafacadeMappings) this .namespaceMetafacadeMappings
0719: .get(namespace);
0720: }
0721:
0722: /**
0723: * Stores the possible parents of this metafacade mappings instance (i.e. mappings for uml-1.4, emf-uml2, etc).
0724: */
0725: private Map parents = new HashMap();
0726:
0727: /**
0728: * Retrieves the appropriate parent based on the current {@link #getNamespace()}.
0729: *
0730: * @return the parent metafacade mappings.
0731: */
0732: private MetafacadeMappings getParent() {
0733: return (MetafacadeMappings) this .parents
0734: .get(this .parentNamespace);
0735: }
0736:
0737: /**
0738: * Adds a MetafacadeMappings instance to the namespace metafacade mappings
0739: * of this instance.
0740: *
0741: * @param namespace the namespace name to which the <code>mappings</code>
0742: * will belong.
0743: * @param mappings the MetafacadeMappings instance to add.
0744: */
0745: private void addNamespaceMappings(final String namespace,
0746: final MetafacadeMappings mappings) {
0747: if (mappings != null) {
0748: // - set the parent by its namespace (the parent is different depending on the current metafacade model namespace)
0749: mappings.parents.put(this .getNamespace(), this );
0750: this .namespaceMetafacadeMappings.put(namespace, mappings);
0751: }
0752: }
0753:
0754: /**
0755: * Initializes this mappings instance, which includes discovery of all
0756: * metafacade mappings instances on the classpath.
0757: */
0758: public void initialize() {
0759: final List modelTypeNamespaces = new ArrayList();
0760: final Collection metafacades = ComponentContainer.instance()
0761: .findComponentsOfType(MetafacadeMappings.class);
0762: for (final Iterator iterator = metafacades.iterator(); iterator
0763: .hasNext();) {
0764: final MetafacadeMappings mappings = (MetafacadeMappings) iterator
0765: .next();
0766: final String namespace = mappings.getNamespace();
0767: if (MetafacadeUtils.isMetafacadeModelPresent(namespace)) {
0768: modelTypeNamespaces.add(namespace);
0769: }
0770: }
0771:
0772: final String[] modelNamespaces = (String[]) modelTypeNamespaces
0773: .toArray(new String[0]);
0774: MetafacadeImpls.instance().discover(modelNamespaces);
0775: this .initializeMappings(modelNamespaces);
0776: }
0777:
0778: /**
0779: * Registers all namespace properties in the shared {@link MetafacadeFactory} instance.
0780: */
0781: final void registerAllProperties() {
0782: // - register all namespace property references defined in the descriptors
0783: final Namespaces namespaces = Namespaces.instance();
0784: for (final Iterator iterator = namespaces.getNamespaces()
0785: .iterator(); iterator.hasNext();) {
0786: final String mappingsNamespace = ((Namespace) iterator
0787: .next()).getName();
0788:
0789: // - add the default mappings
0790: final Collection mappings = new ArrayList(this .mappings);
0791: final MetafacadeMappings metafacadeMappings = this
0792: .getNamespaceMappings(mappingsNamespace);
0793:
0794: // - add all the references from the default namespace
0795: final Collection propertyReferences = new ArrayList(
0796: this .propertyReferences);
0797:
0798: // - if we have namespace mappings, add them
0799: if (metafacadeMappings != null) {
0800: mappings.addAll(metafacadeMappings.mappings);
0801: propertyReferences
0802: .addAll(metafacadeMappings.propertyReferences);
0803: }
0804:
0805: for (final Iterator mappingIterator = mappings.iterator(); mappingIterator
0806: .hasNext();) {
0807: final MetafacadeMapping mapping = (MetafacadeMapping) mappingIterator
0808: .next();
0809: final String metafacadeInterface = this .metafacadeClasses
0810: .getMetafacadeClass(
0811: mapping.getMetafacadeClass().getName())
0812: .getName();
0813:
0814: // - first register the references defined globally in the
0815: // descriptor for each interface
0816: // in the hierarchy
0817: final Class[] interfaces = this
0818: .getInterfacesReversed(metafacadeInterface);
0819: for (int ctr = 0; ctr < interfaces.length; ctr++) {
0820: this .registerProperties(mappingsNamespace,
0821: propertyReferences, interfaces[ctr]
0822: .getName());
0823: }
0824:
0825: // - next register the references defined only within each mapping
0826: // - remember to first load the inherited property references
0827: // into the mapping
0828: this .loadInheritedPropertyReferences(mapping);
0829: this .registerProperties(mappingsNamespace, mapping
0830: .getPropertyReferences(), metafacadeInterface);
0831: }
0832: }
0833: }
0834:
0835: /**
0836: * The name of the metaclass pattern.
0837: */
0838: private String metaclassPattern;
0839:
0840: /**
0841: * First attempts to retrieve the metaclass pattern from this instance, and
0842: * if not found, attempts to retrieve it from the parent instance (since the
0843: * parent instance should always have been set at least once from a shared
0844: * metafacades instance).
0845: *
0846: * @return the metaclass pattern.
0847: */
0848: private String getMetaclassPattern() {
0849: if (this .metaclassPattern == null && this .getParent() != null) {
0850: this .metaclassPattern = this .getParent().metaclassPattern;
0851: }
0852: return this .metaclassPattern;
0853: }
0854:
0855: /**
0856: * Sets the pattern of the metaclass implementations based on a metaclass
0857: * interface name. This should only be set on a metafacade mappings
0858: * instances that is marked as shared.
0859: *
0860: * @param metaclassPattern the pattern for the meta classes.
0861: */
0862: public void setMetaclassPattern(final String metaclassPattern) {
0863: this .metaclassPattern = metaclassPattern;
0864: }
0865:
0866: /**
0867: * Initializes all the metafacade mapping instances under the appropriate model type (defined
0868: * in the <code>modelTypes</code> collection.
0869: *
0870: * @param metafacadeModelNamespaces a list of each namespace containing a metafacade model facade implementation.
0871: */
0872: private void initializeMappings(
0873: final String[] metafacadeModelNamespaces) {
0874: ExceptionUtils.checkNull("modelTypes",
0875: metafacadeModelNamespaces);
0876: final Collection metafacades = ComponentContainer.instance()
0877: .findComponentsOfType(MetafacadeMappings.class);
0878:
0879: // - we need to load up the allMetafacadeMappingInstances before we do
0880: // anything else
0881: for (final Iterator iterator = metafacades.iterator(); iterator
0882: .hasNext();) {
0883: final MetafacadeMappings mappings = (MetafacadeMappings) iterator
0884: .next();
0885: for (final Iterator mappingIterator = mappings.mappings
0886: .iterator(); mappingIterator.hasNext();) {
0887: final MetafacadeMapping mapping = (MetafacadeMapping) mappingIterator
0888: .next();
0889: if (mapping.isMappingClassNamePresent()) {
0890: allMetafacadeMappingInstances.put(mapping
0891: .getMetafacadeClass(), mapping
0892: .getMappingClassName());
0893: }
0894: }
0895: }
0896:
0897: final List modelNamespaces = new ArrayList(Arrays
0898: .asList(metafacadeModelNamespaces));
0899: try {
0900: final Namespaces namespaces = Namespaces.instance();
0901: final int numberOfModelTypes = metafacadeModelNamespaces.length;
0902: for (int ctr = 0; ctr < numberOfModelTypes; ctr++) {
0903: final String modelNamespace = metafacadeModelNamespaces[ctr];
0904: if (modelNamespace != null) {
0905: // - remove the current model type so that we don't keep out the namespace
0906: // that stores the metafacade model
0907: modelNamespaces.remove(modelNamespace);
0908:
0909: MetafacadeMappings modelMetafacadeMappings = (MetafacadeMappings) this .modelMetafacadeMappings
0910: .get(modelNamespace);
0911: if (modelMetafacadeMappings == null) {
0912: modelMetafacadeMappings = MetafacadeMappings
0913: .newInstance();
0914:
0915: // - set the namespace
0916: modelMetafacadeMappings
0917: .setNamespace(modelNamespace);
0918: this .modelMetafacadeMappings
0919: .put(modelNamespace,
0920: modelMetafacadeMappings);
0921: }
0922:
0923: for (final Iterator iterator = metafacades
0924: .iterator(); iterator.hasNext();) {
0925: final MetafacadeMappings mappings = (MetafacadeMappings) iterator
0926: .next();
0927: final String namespace = mappings
0928: .getNamespace();
0929:
0930: if (!modelNamespaces.contains(namespace)) {
0931: // - if we have 'shared' mappings or only a single set available, they are copied
0932: // to this mappings instance.
0933: if (namespaces.isShared(namespace)
0934: || metafacades.size() == 1) {
0935: // - copy over any 'shared' mappings to this root instance
0936: modelMetafacadeMappings
0937: .copyMappings(mappings);
0938:
0939: // - set the metaclass pattern from the 'shared' or single
0940: // instance of metafacades
0941: final String metaclassPattern = mappings.metaclassPattern;
0942: if (metaclassPattern != null
0943: && metaclassPattern.trim()
0944: .length() > 0) {
0945: modelMetafacadeMappings
0946: .setMetaclassPattern(mappings.metaclassPattern);
0947: }
0948: } else {
0949: // add all others as namespace mappings
0950: modelMetafacadeMappings
0951: .addNamespaceMappings(
0952: namespace, mappings);
0953: }
0954: }
0955: }
0956:
0957: // - add the metafacade model namespace back
0958: modelNamespaces.add(modelNamespace);
0959: if (modelMetafacadeMappings.getNamespace() == null
0960: || modelMetafacadeMappings.getNamespace()
0961: .trim().length() == 0) {
0962: throw new MetafacadeMappingsException(
0963: "No shared metafacades found, please check your classpath, at least "
0964: + "one set of metafacades must be marked as 'shared'");
0965: }
0966: if (modelMetafacadeMappings.metaclassPattern == null
0967: || modelMetafacadeMappings.metaclassPattern
0968: .trim().length() == 0) {
0969: throw new MetafacadeMappingsException(
0970: "At least one set of metafacades marked as shared "
0971: + "must have the 'metaclassPattern' attribute defined");
0972: }
0973: }
0974: }
0975: } catch (final Throwable throwable) {
0976: throw new MetafacadeMappingsException(throwable);
0977: }
0978: }
0979:
0980: /**
0981: * Stores all metafacade mapping instances
0982: */
0983: private static Map allMetafacadeMappingInstances = new HashMap();
0984:
0985: /**
0986: * Stores every metafacade mapping instance, this is used from
0987: * {@link MetafacadeUtils#getInheritedMappingClassName(MetafacadeMapping)}.
0988: *
0989: * @return all metafacade mapping instances.
0990: */
0991: static Map getAllMetafacadeMappingInstances() {
0992: return allMetafacadeMappingInstances;
0993: }
0994:
0995: /**
0996: * The shared metafacade impls instance.
0997: */
0998: private MetafacadeImpls metafacadeClasses = MetafacadeImpls
0999: .instance();
1000:
1001: /**
1002: * Stores the metafacadeMapping instances by model type.
1003: */
1004: private Map modelMetafacadeMappings = new LinkedHashMap();
1005:
1006: /**
1007: * Should be used used instead of "this", retrieves the appropriate
1008: * metafacade mappings instance based on the current model type.
1009: *
1010: * @param metafacadeModelNamespace the namespace that contains a metafacade model facade implementation.
1011: * @return the {@link MetafacadeMappings} instance.
1012: */
1013: public MetafacadeMappings getModelMetafacadeMappings(
1014: final String metafacadeModelNamespace) {
1015: final MetafacadeMappings instance = (MetafacadeMappings) this .modelMetafacadeMappings
1016: .get(metafacadeModelNamespace);
1017: if (instance == null) {
1018: throw new MetafacadeMappingsException(
1019: "Namespace '"
1020: + metafacadeModelNamespace
1021: + "' is not a registered metafacade model namespace");
1022: }
1023: return instance;
1024: }
1025:
1026: /**
1027: * Stores the namespace of the parent mappings.
1028: */
1029: private String parentNamespace;
1030:
1031: /**
1032: * Gets the defaultMetafacadeClass, first looks for it in the namespace
1033: * mapping, if it can't find it it then takes the default mappings, setting.
1034: *
1035: * @return Returns the defaultMetafacadeClass.
1036: */
1037: final Class getDefaultMetafacadeClass(final String namespace) {
1038: Class defaultMetafacadeClass = null;
1039: MetafacadeMappings mappings = this
1040: .getNamespaceMappings(namespace);
1041: if (mappings != null) {
1042: defaultMetafacadeClass = mappings.defaultMetafacadeClass;
1043: }
1044: if (defaultMetafacadeClass == null) {
1045: defaultMetafacadeClass = this .defaultMetafacadeClass;
1046: }
1047: return defaultMetafacadeClass;
1048: }
1049:
1050: /**
1051: * Sets the default metafacade class to use if no other is found for the
1052: * mapping class.
1053: *
1054: * @param defaultMetafacadeClass the default metafacade class.
1055: */
1056: public void setDefaultMetafacadeClass(
1057: final String defaultMetafacadeClass) {
1058: try {
1059: this .defaultMetafacadeClass = ClassUtils
1060: .loadClass(StringUtils
1061: .trimToEmpty(defaultMetafacadeClass));
1062: } catch (final Throwable throwable) {
1063: throw new MetafacadeMappingsException(throwable);
1064: }
1065: }
1066:
1067: /**
1068: * Retrieves all child {@link MetafacadeMapping} instances belonging to this
1069: * metafacade mappings instance.
1070: *
1071: * @return the collection of {@link MetafacadeMapping} instances
1072: */
1073: protected Collection getMappings() {
1074: return this .mappings;
1075: }
1076:
1077: /**
1078: * Registers the defined property references properties in the metafacade
1079: * factory.
1080: *
1081: * @param propertyReferences the property references to register.
1082: * @param metafacadeName the name of the metafacade under which to register
1083: * the properties.
1084: * @param namespace the namespace of the property reference.
1085: */
1086: final void registerProperties(final String namespace,
1087: final Collection propertyReferences,
1088: final String metafacadeName) {
1089: final MetafacadeFactory factory = MetafacadeFactory
1090: .getInstance();
1091: for (final Iterator iterator = propertyReferences.iterator(); iterator
1092: .hasNext();) {
1093: final String reference = (String) iterator.next();
1094: final String value = Namespaces.instance()
1095: .getPropertyValue(namespace, reference);
1096: if (value != null) {
1097: if (this .getLogger().isDebugEnabled()) {
1098: this .getLogger().debug(
1099: "setting context property '" + reference
1100: + "' with value '" + value
1101: + "' for namespace '" + namespace
1102: + "' on metafacade '"
1103: + metafacadeName + "'");
1104: }
1105: }
1106: factory.registerProperty(namespace, metafacadeName,
1107: reference, value);
1108: }
1109: }
1110:
1111: /**
1112: * Performs shutdown procedures for the factory. This should be called
1113: * <strong>ONLY</code> when {@link MetafacadeFactory#shutdown()}is called.
1114: */
1115: final void shutdown() {
1116: this .mappings.clear();
1117: this .inProcessMappings.clear();
1118: this .inProcessMetafacades.clear();
1119: this .namespaceMetafacadeMappings.clear();
1120: this .propertyReferences.clear();
1121: this .mappingObjectHierachyCache.clear();
1122: this .mappingsByMetafacadeClass.clear();
1123: this .contextHierachyCache.clear();
1124: this .reversedInterfaceArrayCache.clear();
1125: for (final Iterator iterator = this .modelMetafacadeMappings
1126: .values().iterator(); iterator.hasNext();) {
1127: final MetafacadeMappings metafacadeMappings = (MetafacadeMappings) iterator
1128: .next();
1129: metafacadeMappings.shutdown();
1130: }
1131: this .modelMetafacadeMappings.clear();
1132: }
1133:
1134: /**
1135: * Returns the logger instance to be used for logging within this class.
1136: *
1137: * @return the plugin logger
1138: */
1139: private Logger getLogger() {
1140: return AndroMDALogger.getNamespaceLogger(this .getNamespace());
1141: }
1142:
1143: /**
1144: * @see java.lang.Object#toString()
1145: */
1146: public String toString() {
1147: return super .toString() + "[" + this .getNamespace() + "]";
1148: }
1149: }
|