UMLModelAccessFacade.java

package org.andromda.metafacades.emf.uml22;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.configuration.Filters;
import org.andromda.core.metafacade.MetafacadeBase;
import org.andromda.core.metafacade.MetafacadeConstants;
import org.andromda.core.metafacade.MetafacadeFactory;
import org.andromda.core.metafacade.ModelAccessFacade;
import org.andromda.metafacades.uml.EntityMetafacadeUtils;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.PackageFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.resource.UMLResource;

/**
 * Model access facade implementation for the EMF/UML2 metafacades
 *
 * @author Steve Jerman
 * @author Chad Brandon
 * @author Bob Fields (Multiple model support)
 */
public class UMLModelAccessFacade
    implements ModelAccessFacade
{
    /**
     * The logger instance.
     */
    private static final Logger LOGGER = Logger.getLogger(UMLModelAccessFacade.class);

    /**
     * Protected to improve performance.
     */
    protected Filters modelPackages = new Filters();

    private static final String MODEL_ELEMENT = "modelElement";
    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#setModel(Object)
     */
    public void setModel(final Object modelIn)
    {
        ExceptionUtils.checkNull(
            "model",
            modelIn);
        ExceptionUtils.checkAssignable(
            List.class,
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelIn.getClass());
        // TODO Find models that are already in the models list, don't clear, remove the rest.
        if (this.model!=null)
        {
            // Always clear out models from any previous andromda run
            this.model.clear(); // Ensure garbage collection of referenced resources
        }
        this.model = new ArrayList<UMLResource>();
        for (UMLResource modelResource : (ArrayList<UMLResource>)modelIn)
        {
            if (!this.model.contains(modelResource))
            {
                this.model.add(modelResource);
            }
        }
        // TODO: - clear the meta objects cache (yes this is a performance
        //       hack that at some point should be improved).
        UmlUtilities.clearAllMetaObjectsCache();
        // TODO: - clears out the foreign key cache (again probably not
        //         the cleanest way, but the easiest at this point).
        EntityMetafacadeUtils.clearForeignKeyConstraintNameCache();
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#getModel()
     */
    public Object getModel()
    {
        return this.model;
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#getName(Object)
     */
    public String getName(final Object modelElement)
    {
        ExceptionUtils.checkNull(
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelElement);
        ExceptionUtils.checkAssignable(
            ModelElementFacade.class,
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelElement.getClass());
        return ((ModelElementFacade)modelElement).getName();
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#getPackageName(Object)
     */
    public String getPackageName(final Object modelElement)
    {
        ExceptionUtils.checkNull(
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelElement);
        ExceptionUtils.checkAssignable(
            ModelElementFacade.class,
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelElement.getClass());
        final ModelElementFacade modelElementFacade = (ModelElementFacade)modelElement;
        final StringBuilder packageName = new StringBuilder(modelElementFacade.getPackageName(true));

        // - if the model element is a package then the package name will be the
        // name of the package with its package name
        if (modelElement instanceof PackageFacade)
        {
            final String name = modelElementFacade.getName();
            if (StringUtils.isNotBlank(name))
            {
                packageName.append(MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
                packageName.append(name);
            }
        }
        return packageName.toString();
    }

    /**
     * The actual underlying model instance.
     */
    private List<UMLResource> model;

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#setPackageFilter(org.andromda.core.configuration.Filters)
     */
    public void setPackageFilter(final Filters modelPackagesIn)
    {
        this.modelPackages = modelPackagesIn;
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#getStereotypeNames(Object)
     */
    public Collection<String> getStereotypeNames(final Object modelElement)
    {
        ExceptionUtils.checkNull(
            UMLModelAccessFacade.MODEL_ELEMENT,
            modelElement);

        Collection<String> stereotypeNames = Collections.emptyList();
        if (modelElement instanceof Element)
        {
            final Element element = (Element)modelElement;
            stereotypeNames = UmlUtilities.getStereotypeNames(element);
        }
        else if (modelElement instanceof ModelElementFacade)
        {
            stereotypeNames = ((ModelElementFacade)modelElement).getStereotypeNames();
        }
        return stereotypeNames;
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#findByStereotype(String)
     */
    public Collection<MetafacadeBase> findByStereotype(final String name)
    {
        final List<NamedElement> elements = new ArrayList<NamedElement>();
        for (final UMLResource modelResource : this.model)
        {
            // TODO UmlUtilities.findModel() can return null. Check for null return value.
            for (final TreeIterator<EObject> iterator = UmlUtilities.findModel(modelResource).eAllContents(); iterator.hasNext();)
            {
                final EObject object = iterator.next();
                if (object instanceof NamedElement)
                {
                    final NamedElement element = (NamedElement)object;
                    if (UmlUtilities.containsStereotype(
                            element,
                            name))
                    {
                        elements.add(element);
                    }
                }
            }
        }
        final int elementSize = elements.size();
        if (LOGGER.isDebugEnabled())
        {
            LOGGER.debug("Finding stereotype <<" + name + ">> in elements " +
                elements.size());
        }
        // - filter any elements before we turn them into metafacades (for performance reasons)
        this.filterElements(elements);
        if (LOGGER.isDebugEnabled() && elements.size()<elementSize)
        {
            LOGGER.debug("Filtered out " + (elementSize-elements.size()) + " elements");
        }
        return MetafacadeFactory.getInstance().createMetafacades(elements);
    }

    /**
     * @see org.andromda.core.metafacade.ModelAccessFacade#getModelElements()
     */
    public Collection<MetafacadeBase> getModelElements()
    {
        final List<NamedElement> elements = new ArrayList<NamedElement>();

        for (UMLResource modelResource : this.model)
        {
            // TODO UmlUtilities.findModel() can return null. Check for null return value.
            for (final Iterator<EObject> iterator = UmlUtilities.findModel(modelResource).eAllContents(); iterator.hasNext();)
            {
                final EObject object = iterator.next();
                if (object instanceof NamedElement)
                {
                    elements.add((NamedElement) object);
                }
            }
        }

        // - filter any elements before we turn them into metafacades (for performance reasons)
        this.filterElements(elements);

        // Don't forget to transform properties
        CollectionUtils.transform(
            elements,
            UmlUtilities.ELEMENT_TRANSFORMER);

        final Collection<MetafacadeBase> metafacades;

        if (elements.isEmpty())
        {
            metafacades = new ArrayList<MetafacadeBase>();
        }
        else
        {
            metafacades = MetafacadeFactory.getInstance().createMetafacades(elements);
        }

        return metafacades;
    }

    /**
     * Filters out those metafacades which <strong>should</strong> be
     * processed.
     *
     * @param metafacades the Collection of metafacades.
     */
    private void filterElements(final Collection<NamedElement> metafacades)
    {
        if (this.modelPackages != null && !this.modelPackages.isEmpty())
        {
            CollectionUtils.filter(
                metafacades,
                new Predicate()
                {
                    public boolean evaluate(final Object element)
                    {
                        boolean valid = false;
                        if (element instanceof NamedElement)
                        {
                            final NamedElement modelElement = (NamedElement)element;
                            final StringBuilder packageName =
                                new StringBuilder(
                                    UmlUtilities.getPackageName(
                                        modelElement,
                                        MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR,
                                        true));

                            // - if the model element is a package then the package name will be the name
                            // of the package with its package name
                            if (element instanceof Package)
                            {
                                final String name = modelElement.getName();
                                if (StringUtils.isNotBlank(name))
                                {
                                    packageName.append(MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
                                    packageName.append(name);
                                }
                            }
                            valid = UMLModelAccessFacade.this.modelPackages.isApply(packageName.toString());
                        }

                        return valid;
                    }
                });
        }
    }
}