001    package org.andromda.core.metafacade;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Collection;
006    import java.util.LinkedHashMap;
007    import java.util.LinkedHashSet;
008    import java.util.List;
009    import java.util.Map;
010    import org.andromda.core.common.AndroMDALogger;
011    import org.andromda.core.common.ExceptionUtils;
012    import org.andromda.core.profile.Profile;
013    import org.apache.commons.collections.keyvalue.MultiKey;
014    import org.apache.commons.lang.StringUtils;
015    import org.apache.log4j.Logger;
016    
017    /**
018     * The factory in charge of constructing Metafacade instances. In order for a
019     * metafacade (i.e. a facade around a meta model element) to be constructed, it
020     * must be constructed through this factory.
021     *
022     * @author <a href="http://www.mbohlen.de">Matthias Bohlen </a>
023     * @author Chad Brandon
024     * @author Peter Friese
025     * @author Bob Fields
026     */
027    public final class MetafacadeFactory
028        implements Serializable
029    {
030        private static final long serialVersionUID = 34L;
031    
032        /**
033         * Caches the registered properties used within metafacades.
034         */
035        private final Map<String, Map<String, Map<String, Object>>> metafacadeNamespaces = new LinkedHashMap<String, Map<String, Map<String, Object>>>();
036    
037        /**
038         * The shared instance of this factory.
039         */
040        private static MetafacadeFactory instance = null;
041    
042        private MetafacadeFactory()
043        {
044            // make sure that nobody instantiates it
045        }
046    
047        /**
048         * Returns the facade factory singleton.
049         *
050         * @return the only instance
051         */
052        public static MetafacadeFactory getInstance()
053        {
054            if (instance == null)
055            {
056                instance = new MetafacadeFactory();
057            }
058            return instance;
059        }
060    
061        /**
062         * The metafacade cache for this factory.
063         */
064        private final MetafacadeCache cache = MetafacadeCache.newInstance();
065    
066        /**
067         * The metafacade mappings instance for this factory.
068         */
069        private final MetafacadeMappings mappings = MetafacadeMappings.newInstance();
070    
071        /**
072         * Performs any initialization required by the factory (i.e. discovering all
073         * <code>metafacade</code> mappings, etc).
074         */
075        public void initialize()
076        {
077            this.mappings.initialize();
078        }
079    
080        /**
081         * The shared profile instance.
082         */
083        private final Profile profile = Profile.instance();
084    
085        /**
086         * The namespace that is currently active (i.e. being used) within the factory
087         */
088        private String namespace;
089    
090        /**
091         * Sets the active namespace. The AndroMDA core and each cartridge have their own namespace for metafacade
092         * registration.
093         *
094         * @param namespace the name of the active namespace.
095         */
096        public void setNamespace(final String namespace)
097        {
098            this.namespace = namespace;
099            this.profile.setNamespace(namespace);
100            this.cache.setNamespace(this.namespace);
101        }
102    
103        /**
104         * Returns the name of the active namespace.
105         *
106         * @return String the namespace name
107         */
108        public String getNamespace()
109        {
110            if (this.namespace == null)
111            {
112                throw new MetafacadeFactoryException("This metafacade factory's namespace must be populated before " +
113                    "metafacade construction can occur");
114            }
115            return this.namespace;
116        }
117    
118        /**
119         * Returns a metafacade for a mappingObject, depending on its
120         * <code>mappingClass</code> and (optionally) its <code>stereotypes</code>
121         * and <code>context</code>.
122         *
123         * @param mappingObject the object used to map the metafacade (a meta model
124         *        object or a metafacade itself).
125         * @param context the name of the context the meta model element is
126         *        registered under.
127         * @return the new metafacade
128         */
129        public MetafacadeBase createMetafacade(
130            final Object mappingObject,
131            final String context)
132        {
133            return this.createMetafacade(
134                mappingObject,
135                context,
136                null);
137        }
138    
139        /**
140         * Creates a metafacade given the <code>mappingObject</code>,
141         * <code>contextName</code> and <code>metafacadeClass</code>.
142         *
143         * @param mappingObject the object used to map the metafacade (a meta model
144         *        object or a metafacade itself).
145         * @param context the name of the context the meta model element (if the
146         *        mappObject is a meta model element) is registered under.
147         * @param metafacadeClass if not null, it contains the name of the
148         *        metafacade class to be used. This is used ONLY when instantiating
149         *        super metafacades in an inheritance chain. The final metafacade
150         *        will NEVER have a metafacadeClass specified (it will ALWAYS be
151         *        null).
152         * @return the new metafacade
153         */
154        private MetafacadeBase createMetafacade(
155            final Object mappingObject,
156            final String context,
157            Class metafacadeClass)
158        {
159            final String methodName = "MetafacadeFactory.createMetafacade";
160            ExceptionUtils.checkNull(
161                "mappingObject",
162                mappingObject);
163    
164            // - register the namespace properties (if they haven't been)
165            this.registerNamespaceProperties();
166    
167            // if the mappingObject is REALLY a metafacade, just return it
168            if (mappingObject instanceof MetafacadeBase)
169            {
170                return (MetafacadeBase)mappingObject;
171            }
172            try
173            {
174                final Collection<String> stereotypes = this.getModel().getStereotypeNames(mappingObject);
175                if (this.getLogger().isDebugEnabled())
176                {
177                    this.getLogger().debug("mappingObject stereotypes --> '" + stereotypes + '\'');
178                }
179    
180                MetafacadeMapping mapping = null;
181                if (metafacadeClass == null)
182                {
183                    final MetafacadeMappings modelMetafacadeMappings = this.getModelMetafacadeMappings();
184                    mapping = modelMetafacadeMappings.getMetafacadeMapping(
185                        mappingObject,
186                        this.getNamespace(),
187                        context,
188                        stereotypes);
189                    if (this.getLogger().isDebugEnabled())
190                    {
191                        this.getLogger().debug("mappingObject getModelMetafacadeMappings for " + mappingObject + " namespace " + this.getNamespace() + " context " + context);
192                    }
193                    if (mapping != null)
194                    {
195                        metafacadeClass = mapping.getMetafacadeClass();
196                    }
197                    else
198                    {
199                        // get the default since no mapping was found.
200                        metafacadeClass = modelMetafacadeMappings.getDefaultMetafacadeClass(this.getNamespace());
201                        if (this.getLogger().isDebugEnabled())
202                        {
203                            this.getLogger().warn(
204                                "Meta object model class '" + mappingObject.getClass() +
205                                "' has no corresponding meta facade class, default is being used --> '" + metafacadeClass +
206                                        '\'');
207                        }
208                    }
209                }
210    
211                if (metafacadeClass == null)
212                {
213                    throw new MetafacadeMappingsException(methodName + " metafacadeClass was not retrieved from mappings" +
214                        " or specified as an argument in this method for mappingObject --> '" + mappingObject + '\'');
215                }
216                final MetafacadeBase metafacade = this.getMetafacade(
217                        metafacadeClass,
218                        mappingObject,
219                        context,
220                        mapping);
221    
222                // IMPORTANT: initialize each metafacade ONLY once (otherwise we
223                // get stack overflow errors)
224                if (metafacade != null && !metafacade.isInitialized())
225                {
226                    metafacade.setInitialized();
227                    metafacade.initialize();
228                }
229                return metafacade;
230            }
231            catch (final Throwable throwable)
232            {
233                final String message =
234                    "Failed to construct a meta facade of type '" + metafacadeClass + "' with mappingObject of type --> '" +
235                    mappingObject.getClass() + '\'';
236                this.getLogger().error(message);
237                throw new MetafacadeFactoryException(message, throwable);
238            }
239        }
240    
241        /**
242         * Gets the model metafacade mappings (the mappings that correspond
243         * to the current metafacade model namespace).
244         *
245         * @return the model metafacade mappings.
246         */
247        private MetafacadeMappings getModelMetafacadeMappings()
248        {
249            return this.mappings.getModelMetafacadeMappings(this.metafacadeModelNamespace);
250        }
251    
252        /**
253         * Validates all metafacades for the current namespace
254         * and collects the messages in the internal validation messages
255         * collection.
256         *
257         * @see #getValidationMessages()
258         */
259        public void validateAllMetafacades()
260        {
261            for (final MetafacadeBase metafacadeBase : this.getAllMetafacades())
262            {
263                metafacadeBase.validate(this.validationMessages);
264            }
265        }
266    
267        /**
268         * Creates a metafacade from the passed in <code>mappingObject</code>, and
269         * <code>mapping</code> instance.
270         *
271         * @param mappingObject the mapping object for which to create the
272         *        metafacade.
273         * @param mapping the mapping from which to create the metafacade
274         * @return the metafacade, or null if it can't be created.
275         */
276        protected MetafacadeBase createMetafacade(
277            final Object mappingObject,
278            final MetafacadeMapping mapping)
279        {
280            try
281            {
282                return this.getMetafacade(
283                    mapping.getMetafacadeClass(),
284                    mappingObject,
285                    mapping.getContext(),
286                    mapping);
287            }
288            catch (final Throwable throwable)
289            {
290                final String message =
291                    "Failed to construct a meta facade of type '" + mapping.getMetafacadeClass() +
292                    "' with mappingObject of type --> '" + mapping.getMappingClassNames() + '\'';
293                this.getLogger().error(message);
294                throw new MetafacadeFactoryException(message, throwable);
295            }
296        }
297    
298        /**
299         * Retrieves (if one has already been constructed) or constructs a new
300         * <code>metafacade</code> from the given <code>metafacadeClass</code>
301         * and <code>mappingObject</code>.
302         *
303         * @param metafacadeClass the metafacade class.
304         * @param mappingObject the object to which the metafacade is mapped.
305         * @param context the context to which the metafacade applies
306         * @param mapping the optional MetafacadeMapping instance from which the
307         *        metafacade is mapped.
308         * @return the new (or cached) metafacade.
309         * @throws Exception if any error occurs during metafacade creation
310         */
311        private MetafacadeBase getMetafacade(
312            final Class metafacadeClass,
313            final Object mappingObject,
314            final String context,
315            final MetafacadeMapping mapping)
316            throws Exception
317        {
318            MetafacadeBase metafacade = this.cache.get(
319                    mappingObject,
320                    metafacadeClass);
321            if (metafacade == null)
322            {
323                final MultiKey key = new MultiKey(mappingObject, metafacadeClass);
324                if (!this.metafacadesInCreation.contains(key))
325                {
326                    this.metafacadesInCreation.add(
327                        key);
328                    if (mapping != null && mapping.isContextRoot())
329                    {
330                        metafacade = MetafacadeUtils.constructMetafacade(
331                                metafacadeClass,
332                                mappingObject,
333                                null);
334                        // set whether or not this metafacade is a context root
335                        metafacade.setContextRoot(mapping.isContextRoot());
336                    }
337                    else
338                    {
339                        metafacade = MetafacadeUtils.constructMetafacade(
340                                metafacadeClass,
341                                mappingObject,
342                                context);
343                    }
344                    this.metafacadesInCreation.remove(key);
345    
346                    this.cache.add(
347                        mappingObject,
348                        metafacade);
349                }
350            }
351    
352            if (metafacade != null)
353            {
354                // if the requested metafacadeClass is different from the one in the mapping, contextRoot should be reset
355                if (mapping != null && !mapping.getMetafacadeClass().equals(metafacadeClass))
356                {
357                    metafacade.setContextRoot(false);
358                    metafacade.resetMetafacadeContext(context);
359                }
360                // we need to set some things each time
361                // we change a metafacade's namespace
362                final String metafacadeNamespace = metafacade.getMetafacadeNamespace();
363                if (metafacadeNamespace == null || !metafacadeNamespace.equals(this.getNamespace()))
364                {
365                    // assign the logger and active namespace
366                    metafacade.setNamespace(this.getNamespace());
367                    metafacade.setLogger(this.getLogger());
368                }
369            }
370            return metafacade;
371        }
372    
373        /**
374         * Stores the metafacades being created, so that we don't get stuck in
375         * endless recursion during creation.
376         */
377        private final Collection<MultiKey> metafacadesInCreation = new ArrayList<MultiKey>();
378    
379        /**
380         * Returns a metafacade for a mappingObject, depending on its <code>mappingClass</code>.
381         *
382         * @param mappingObject the object which is used to map to the metafacade
383         * @return MetafacadeBase the facade object (not yet attached to mappingClass object)
384         */
385        public MetafacadeBase createMetafacade(final Object mappingObject)
386        {
387            return this.createMetafacade(
388                mappingObject,
389                null,
390                null);
391        }
392    
393        /**
394         * Create a facade implementation object for a mappingObject. The facade
395         * implementation object must be found in a way that it implements the
396         * interface <code>interfaceName</code>.
397         *
398         * @param interfaceName the name of the interface that the implementation
399         *        object has to implement
400         * @param mappingObject the mappingObject for which a facade shall be
401         *        created
402         * @param context the context in which this metafacade will be created.
403         * @return MetafacadeBase the metafacade
404         */
405        public MetafacadeBase createFacadeImpl(
406            final String interfaceName,
407            final Object mappingObject,
408            final String context)
409        {
410            ExceptionUtils.checkEmpty(
411                "interfaceName",
412                interfaceName);
413            ExceptionUtils.checkNull(
414                "mappingObject",
415                mappingObject);
416    
417            Class metafacadeClass = null;
418            try
419            {
420                metafacadeClass = this.metafacadeImpls.getMetafacadeImplClass(interfaceName);
421                return this.createMetafacade(
422                    mappingObject,
423                    context,
424                    metafacadeClass);
425            }
426            catch (final Throwable throwable)
427            {
428                final String message =
429                    "Failed to construct a meta facade of type '" + metafacadeClass + "' with mappingObject of type --> '" +
430                    mappingObject.getClass().getName() + '\'';
431                this.getLogger().error(message);
432                throw new MetafacadeFactoryException(message, throwable);
433            }
434        }
435    
436        /**
437         * Returns a metafacade for each mappingObject, contained within the
438         * <code>mappingObjects</code> collection depending on its
439         * <code>mappingClass</code> and (optionally) its <code>stereotypes</code>,
440         * and <code>contextName</code>.
441         *
442         * @param mappingObjects the meta model element.
443         * @param contextName the name of the context the meta model element is
444         *        registered under.
445         * @return the Collection of newly created Metafacades.
446         */
447        protected Collection<MetafacadeBase> createMetafacades(
448            final Collection mappingObjects,
449            final String contextName)
450        {
451            final Collection<MetafacadeBase> metafacades = new ArrayList<MetafacadeBase>();
452            if (mappingObjects != null && !mappingObjects.isEmpty())
453            {
454                for (final Object mappingObject : mappingObjects)
455                {
456                    if (this.getLogger().isDebugEnabled())
457                    {
458                        this.getLogger().debug("MetafacadeFactory createMetafacade for namespace " + this.getNamespace() + " model " + this.getModel() + " contextName " + contextName + " mappingObject " + mappingObject);
459                    }
460                    metafacades.add(this.createMetafacade(
461                            mappingObject,
462                            contextName,
463                            null));
464                }
465            }
466            return metafacades;
467        }
468    
469        /**
470         * Returns a metafacade for each mappingObject, contained within the
471         * <code>mappingObjects</code> collection depending on its
472         * <code>mappingClass</code>.
473         *
474         * @param mappingObjects the objects used to map the metafacades (can be a
475         *        meta model element or an actual metafacade itself).
476         * @return Collection of metafacades
477         */
478        public Collection<MetafacadeBase> createMetafacades(final Collection mappingObjects)
479        {
480            return this.createMetafacades(
481                mappingObjects,
482                null);
483        }
484    
485        /**
486         * The model facade which provides access to the underlying meta model.
487         */
488        private ModelAccessFacade model;
489    
490        /**
491         * Gets the model which provides access to the underlying model and is used
492         * to construct metafacades.
493         *
494         * @return the model access facade.
495         */
496        public ModelAccessFacade getModel()
497        {
498            if (this.model == null)
499            {
500                throw new MetafacadeFactoryException("This metafacade factory's model must be populated before " +
501                    "metafacade construction can occur");
502            }
503            return this.model;
504        }
505    
506        /**
507         * The shared metafacade impls instance.
508         */
509        private MetafacadeImpls metafacadeImpls = MetafacadeImpls.instance();
510    
511        /**
512         * Stores the namespace that contains the metafacade model implementation.
513         */
514        private String metafacadeModelNamespace;
515    
516        /**
517         * The model access facade instance (provides access to the meta model).
518         *
519         * @param model the model
520         * @param metafacadeModelNamespace the namespace that contains the metafacade facade implementation.
521         */
522        public void setModel(
523            final ModelAccessFacade model,
524            final String metafacadeModelNamespace)
525        {
526            this.metafacadeModelNamespace = metafacadeModelNamespace;
527    
528            // - set the model type as the namespace for the metafacade impls so we have
529            //   access to the correct metafacade classes
530            this.metafacadeImpls.setMetafacadeModelNamespace(metafacadeModelNamespace);
531            this.model = model;
532        }
533    
534        /**
535         * Gets the correct logger based on whether or not an namespace logger should be used
536         *
537         * @return the logger
538         */
539        final Logger getLogger()
540        {
541            return AndroMDALogger.getNamespaceLogger(this.getNamespace());
542        }
543    
544        /**
545         * Registers a property with the specified <code>name</code> in the given
546         * <code>namespace</code>.
547         *
548         * @param namespace the namespace in which the property is stored.
549         * @param metafacadeName the name of the metafacade under which the property is registered
550         * @param name the name of the property
551         * @param value to give the property
552         */
553        final void registerProperty(
554            final String namespace,
555            final String metafacadeName,
556            final String name,
557            final Object value)
558        {
559            ExceptionUtils.checkEmpty(
560                "name",
561                name);
562            Map<String, Map<String, Object>> metafacadeNamespace = this.metafacadeNamespaces.get(namespace);
563            if (metafacadeNamespace == null)
564            {
565                metafacadeNamespace = new LinkedHashMap<String, Map<String, Object>>();
566                this.metafacadeNamespaces.put(
567                    namespace,
568                    metafacadeNamespace);
569            }
570            Map<String, Object> propertyNamespace = metafacadeNamespace.get(metafacadeName);
571            if (propertyNamespace == null)
572            {
573                propertyNamespace = new LinkedHashMap<String, Object>();
574                metafacadeNamespace.put(
575                    metafacadeName,
576                    propertyNamespace);
577            }
578            propertyNamespace.put(
579                name,
580                value);
581        }
582    
583        /**
584         * Registers a property with the specified <code>name</code> in the namespace
585         * that is currently set within the factory.
586         *
587         * @param metafacadeName the name of the metafacade under which the property is registered
588         * @param name the name of the property
589         * @param value to give the property
590         */
591        final void registerProperty(
592            final String metafacadeName,
593            final String name,
594            final Object value)
595        {
596            this.registerProperty(
597                this.getNamespace(),
598                metafacadeName,
599                name,
600                value);
601        }
602    
603        /**
604         * Gets the metafacade's property namespace (or returns null if hasn't be registered).
605         *
606         * @param metafacade the metafacade
607         * @return the metafacade's namespace
608         */
609        private Map<String, Object> getMetafacadePropertyNamespace(final MetafacadeBase metafacade)
610        {
611            Map<String, Object> metafacadeNamespace = null;
612            if (metafacade != null)
613            {
614                Map<String, Map<String, Object>> namespace = this.metafacadeNamespaces.get(this.getNamespace());
615                if (namespace != null)
616                {
617                    metafacadeNamespace = namespace.get(metafacade.getMetafacadeName());
618                }
619            }
620            return metafacadeNamespace;
621        }
622    
623        /**
624         * Returns true if this property is registered under the given
625         * <code>namespace</code>, false otherwise.
626         *
627         * @param metafacade the metafacade to search.
628         * @param name the name of the property.
629         * @return true if the property is registered, false otherwise.
630         */
631        final boolean isPropertyRegistered(
632            final MetafacadeBase metafacade,
633            final String name)
634        {
635            final Map<String, Object> propertyNamespace = this.getMetafacadePropertyNamespace(metafacade);
636            return propertyNamespace != null && propertyNamespace.containsKey(name);
637        }
638    
639        /**
640         * Finds the first property having the given <code>namespaces</code>, or
641         * <code>null</code> if the property can <strong>NOT </strong> be found.
642         *
643         * @param metafacade the metafacade to search.
644         * @param name the name of the property to find.
645         * @return the property or null if it can't be found.
646         */
647        private Object findProperty(
648            final MetafacadeBase metafacade,
649            final String name)
650        {
651            final Map<String, Object> propertyNamespace = this.getMetafacadePropertyNamespace(metafacade); //final Map<String, Map>
652            return propertyNamespace != null ? propertyNamespace.get(name) : null;
653        }
654    
655        /**
656         * Gets the registered property registered under the <code>namespace</code>
657         * with the <code>name</code>
658         *
659         * @param metafacade the metafacade to search
660         * @param name the name of the property to check.
661         * @return the registered property
662         */
663        final Object getRegisteredProperty(
664            final MetafacadeBase metafacade,
665            final String name)
666        {
667            final String methodName = "MetafacadeFactory.getRegisteredProperty";
668            final Object registeredProperty = this.findProperty(
669                    metafacade,
670                    name);
671            if (registeredProperty == null && !this.isPropertyRegistered(
672                    metafacade,
673                    name))
674            {
675                throw new MetafacadeFactoryException(methodName + " - no property '" + name +
676                    "' registered under metafacade '" + metafacade.getMetafacadeName() + "' for namespace '" + this.getNamespace() +
677                        '\'');
678            }
679            return registeredProperty;
680        }
681    
682        /**
683         * The validation messages that have been collected during the
684         * execution of this factory.
685         */
686        private final Collection<ModelValidationMessage> validationMessages = new LinkedHashSet<ModelValidationMessage>();
687    
688        /**
689         * Gets the list of all validation messages collection during model processing.
690         *
691         * @return Returns the validationMessages.
692         * @see #validateAllMetafacades()
693         */
694        public List<ModelValidationMessage> getValidationMessages()
695        {
696            return new ArrayList<ModelValidationMessage>(this.validationMessages);
697        }
698    
699        /**
700         * Stores the collection of all metafacades for
701         * each namespace.
702         */
703        private final Map<String, Collection<MetafacadeBase>> allMetafacades = new LinkedHashMap<String, Collection<MetafacadeBase>>();
704    
705        /**
706         * <p>
707         * Gets all metafacades for the entire model for the
708         * current namespace set within the factory.
709         * </p>
710         * <p>
711         * <strong>NOTE:</strong> The model package filter is applied
712         * before returning the results (if defined within the factory).
713         * </p>
714         *
715         * @return all metafacades
716         */
717        public Collection<MetafacadeBase> getAllMetafacades()
718        {
719            final String namespace = this.getNamespace();
720            Collection<MetafacadeBase> metafacades = null;
721            if (this.getModel() != null)
722            {
723                metafacades = allMetafacades.get(namespace);
724                if (metafacades == null)
725                {
726                    metafacades = this.createMetafacades(this.getModel().getModelElements());
727                    allMetafacades.put(
728                        namespace,
729                        metafacades);
730                }
731                if (metafacades != null)
732                {
733                    metafacades = new ArrayList<MetafacadeBase>(metafacades);
734                }
735            }
736            return metafacades;
737        }
738    
739        /**
740         * Caches the metafacades by stereotype.
741         */
742        private final Map<String, Map<String, Collection<MetafacadeBase>>> metafacadesByStereotype
743        = new LinkedHashMap<String, Map<String, Collection<MetafacadeBase>>>();
744    
745        /**
746         * <p>
747         * Gets all metafacades for the entire model having the given
748         * stereotype.
749         * </p>
750         * <p>
751         * <strong>NOTE:</strong> The model package filter is applied
752         * before returning the results (if defined within the factory).
753         * </p>
754         *
755         * @param stereotype the stereotype by which to perform the search.
756         * @return the metafacades having the given <code>stereotype</code>.
757         */
758        public Collection<MetafacadeBase> getMetafacadesByStereotype(final String stereotype)
759        {
760            final String namespace = this.getNamespace();
761            Collection<MetafacadeBase> metafacades = null;
762            if (this.getModel() != null)
763            {
764                Map<String, Collection<MetafacadeBase>> stereotypeMetafacades = this.metafacadesByStereotype.get(namespace);
765                if (stereotypeMetafacades == null)
766                {
767                    stereotypeMetafacades = new LinkedHashMap<String, Collection<MetafacadeBase>>();
768                }
769                metafacades = stereotypeMetafacades.get(stereotype);
770                if (metafacades == null)
771                {
772                    metafacades = this.createMetafacades(this.getModel().findByStereotype(stereotype));
773                    stereotypeMetafacades.put(
774                        stereotype,
775                        metafacades);
776                    this.metafacadesByStereotype.put(
777                        namespace,
778                        stereotypeMetafacades);
779                }
780                if (metafacades != null)
781                {
782                    metafacades = new ArrayList<MetafacadeBase>(metafacades);
783                }
784            }
785            return metafacades;
786        }
787    
788        /**
789         * Performs shutdown procedures for the factory. This should be called <strong>ONLY</code> when model processing has
790         * completed.
791         */
792        public void shutdown()
793        {
794            this.clearCaches();
795            this.metafacadeNamespaces.clear();
796            this.mappings.shutdown();
797            this.model = null;
798            MetafacadeFactory.instance = null;
799    
800            // - shutdown the profile instance
801            this.profile.shutdown();
802        }
803    
804        /**
805         * Registers all namespace properties (if required).
806         */
807        private void registerNamespaceProperties()
808        {
809            // - only register them if they already aren't registered
810            if (this.metafacadeNamespaces.isEmpty())
811            {
812                if (StringUtils.isNotBlank(this.metafacadeModelNamespace))
813                {
814                    final MetafacadeMappings modelMappings = this.getModelMetafacadeMappings();
815                    if (modelMappings != null)
816                    {
817                        modelMappings.registerAllProperties();
818                    }
819                }
820            }
821        }
822    
823        /**
824         * Entirely resets all the internal resources within this factory instance (such
825         * as the caches, etc).
826         */
827        public void reset()
828        {
829            // - refresh the profile
830            this.profile.refresh();
831    
832            // - clear out the namespace properties so we can re-register them next run
833            this.metafacadeNamespaces.clear();
834    
835            // - re-register the namespace properties (if we're running again)
836            this.registerNamespaceProperties();
837    
838            // - clear out the rest of the factory's caches
839            this.clearCaches();
840        }
841    
842        /**
843         * Clears out the factory's internal caches (other
844         * than namespace properties, which can be cleared by
845         * calling {@link org.andromda.core.configuration.Namespaces#clear()}.
846         */
847        public void clearCaches()
848        {
849            this.validationMessages.clear();
850            this.allMetafacades.clear();
851            this.metafacadesByStereotype.clear();
852            this.cache.clear();
853            this.metafacadesInCreation.clear();
854        }
855    }