001package org.andromda.core.metafacade;
002
003import java.io.Serializable;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.LinkedHashMap;
007import java.util.LinkedHashSet;
008import java.util.List;
009import java.util.Map;
010import org.andromda.core.common.AndroMDALogger;
011import org.andromda.core.common.ExceptionUtils;
012import org.andromda.core.profile.Profile;
013import org.apache.commons.collections.keyvalue.MultiKey;
014import org.apache.commons.lang.StringUtils;
015import 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 */
027public 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</strong> 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}