ModelElementFacadeLogicImpl.java

package org.andromda.metafacades.emf.uml22;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.andromda.core.metafacade.MetafacadeConstants;
import org.andromda.metafacades.uml.BindingFacade;
import org.andromda.metafacades.uml.ConstraintFacade;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.NameMasker;
import org.andromda.metafacades.uml.RedefinableTemplateSignatureFacade;
import org.andromda.metafacades.uml.TaggedValueFacade;
import org.andromda.metafacades.uml.TemplateArgumentFacade;
import org.andromda.metafacades.uml.TemplateParameterFacade;
import org.andromda.metafacades.uml.TypeMappings;
import org.andromda.metafacades.uml.UMLMetafacadeProperties;
import org.andromda.metafacades.uml.UMLMetafacadeUtils;
import org.andromda.metafacades.uml.UMLProfile;
import org.andromda.translation.ocl.ExpressionKinds;
import org.andromda.utils.StringUtilsHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMIHelperImpl;
import org.eclipse.uml2.uml.Abstraction;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.Deployment;
import org.eclipse.uml2.uml.DirectedRelationship;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Manifestation;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Realization;
import org.eclipse.uml2.uml.StateMachine;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Substitution;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateSignature;
import org.eclipse.uml2.uml.TemplateableElement;
import org.eclipse.uml2.uml.Usage;
import org.eclipse.uml2.uml.VisibilityKind;

/**
 * MetafacadeLogic implementation for
 * org.andromda.metafacades.uml.ModelElementFacade.
 *
 * @see org.andromda.metafacades.uml.ModelElementFacade
 * @author Bob Fields
 */
public class ModelElementFacadeLogicImpl
    extends ModelElementFacadeLogic
{
    private static final long serialVersionUID = 34L;
    /**
     * Helps to get the xmi:id value from the model
     */
    static XMIHelperImpl xmiHelper = new XMIHelperImpl();

    /**
     * @param metaObjectIn
     * @param context
     */
    public ModelElementFacadeLogicImpl(
        final Element metaObjectIn,
        final String context)
    {
        super(metaObjectIn, context);
    }

    /**
     * The logger instance.
     */
    private static final Logger LOGGER = Logger.getLogger(ModelElementFacadeLogicImpl.class);

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getVisibility()
     */
    @Override
    protected String handleGetVisibility()
    {
        if (this.metaObject instanceof NamedElement)
        {
            final NamedElement element = (NamedElement)this.metaObject;
            final VisibilityKind kind = element.getVisibility();
            String visibility = null;
            if (kind.equals(VisibilityKind.PUBLIC_LITERAL))
            {
                visibility = "public";
            }
            else if (kind.equals(VisibilityKind.PRIVATE_LITERAL))
            {
                visibility = "private";
            }
            else if (kind.equals(VisibilityKind.PROTECTED_LITERAL))
            {
                visibility = "protected";
            }
            else if (kind.equals(VisibilityKind.PACKAGE_LITERAL))
            {
                // So that we can use $attribute.visibility without having to remove the 'package' qualifier
                //visibility = "package";
                visibility = "";
            }
            else
            {
                visibility = "public";
            }
            final TypeMappings languageMappings = this.handleGetLanguageMappings();
            if (languageMappings != null)
            {
                visibility = languageMappings.getTo(visibility);
            }
            // Attribute and Associations change visibility from private to public, instead of throwing an error. Get/Set should still be public.
            return visibility;
        }
        return null;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackagePath()
     */
    @Override
    protected String handleGetPackagePath()
    {
        return StringUtils.replace(
            this.handleGetPackageName(),
            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
            "/");
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
     */
    @Override
    protected String handleGetName()
    {
        // In UML2, model elements need not have a name,
        // only when they are an instance of NamedElement.
        if (this.metaObject instanceof NamedElement)
        {
            final NamedElement namedElement = (NamedElement) this.metaObject;

            String nameMask = null;
            try
            {
                nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.MODEL_ELEMENT_NAME_MASK));
            }
            catch (Exception ignore)
            {
                LOGGER.warn("modelElementNameMask not found in " + this.toString());
                nameMask = "none";
            }

            return NameMasker.mask(namedElement.getName(), nameMask);
        }
        return "";
    }

    /**
     * Gets the appropriate namespace property for retrieve the namespace scope
     * operation (depending on the given <code>modelName</code> flag.
     *
     * @param modelName
     *            whether or not the scope operation for the model should be
     *            retrieved as opposed to the mapped scope operator.
     * @return the scope operator.
     */
    private String getNamespaceScope(final boolean modelName)
    {
        return modelName
            ? MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR
            : ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR));
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName()
     */
    @Override
    protected String handleGetPackageName()
    {
        String packageName = UmlUtilities.getPackageName(this.metaObject, this.getNamespaceScope(false), false);

        //package names are treated differently so we have to apply the name mask here
        //since there isn't a packageNameMask, we're using the modelElementNameMask
        String nameMask = null;
        try
        {
            nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.MODEL_ELEMENT_NAME_MASK));
        }
        catch (Exception ignore)
        {
            LOGGER.warn("modelElementNameMask not found in " + this.toString() + " (getPackageName)");
            nameMask = "none";
        }

        return NameMasker.mask(packageName, nameMask);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedName()
     */
    @Override
    protected String handleGetFullyQualifiedName()
    {
        return this.handleGetFullyQualifiedName(false);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedNamePath()
     */
    @Override
    protected String handleGetFullyQualifiedNamePath()
    {
        return StringUtils.replace(
            this.handleGetFullyQualifiedName(),
            String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
            "/");
    }

    /**
     * Gets the array suffix from the configured metafacade properties.
     *
     * @return the array suffix.
     */
    protected String getArraySuffix()
    {
        return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.ARRAY_NAME_SUFFIX));
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getLanguageMappings()
     */
    @Override
    protected org.andromda.metafacades.uml.TypeMappings handleGetLanguageMappings()
    {
        final String propertyName = UMLMetafacadeProperties.LANGUAGE_MAPPINGS_URI;
        final Object property = this.getConfiguredProperty(propertyName);
        TypeMappings mappings = null;
        if (String.class.isAssignableFrom(property.getClass()))
        {
            final String uri = (String)property;
            try
            {
                mappings = TypeMappings.getInstance(uri);
                mappings.setArraySuffix(this.getArraySuffix());
                this.setProperty(
                    propertyName,
                    mappings);
            }
            catch (Throwable th)
            {
                final String errMsg = "Error getting '" + propertyName + "' --> '" + uri + '\'';
                ModelElementFacadeLogicImpl.LOGGER.error(
                    errMsg,
                    th);

                // don't throw the exception
            }
        }
        else
        {
            mappings = (TypeMappings)property;
        }
        return mappings;
    }

    /**
     * @return Collection String of org.eclipse.uml2.uml.Stereotype.getName()
     * @see org.andromda.metafacades.uml.ModelElementFacade#getStereotypeNames()
     */
    @Override
    protected Collection<String> handleGetStereotypeNames()
    {
        return UmlUtilities.getStereotypeNames(this.metaObject);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getId()
     */
    @Override
    protected String handleGetId()
    {
        if (this.metaObject == null || this.metaObject.eResource() == null)
            return null;
        // From http://www.openarchitectureware.org/forum/viewtopic.php?showtopic=6653
        Resource xmiResource = this.metaObject.eResource();
        String rtn = ((XMLResource) xmiResource).getID(this.metaObject);
        /*if (rtn == null)
        {
            // From http://www.eclipse.org/forums/index.php?t=msg&th=130555/
            rtn = xmiResource.getURIFragment(this.metaObject);
            if (rtn == null)
            {
                // This always returns null for UML2, but just in case something changes, try anyway
                rtn = xmiHelper.getID(this.metaObject);
            }
        }*/
        return rtn;
    }

    /**
     * @return isReservedWord
     * @see org.andromda.metafacades.uml.ModelElementFacade#isReservedWord()
     */
    @Override
    protected boolean handleIsReservedWord()
    {
        return UMLMetafacadeUtils.isReservedWord(this.handleGetName());
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#isConstraintsPresent()
     */
    @Override
    protected boolean handleIsConstraintsPresent()
    {
        return this.handleGetConstraints() != null && !this.handleGetConstraints().isEmpty();
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#findTaggedValue(String)
     */
    @Override
    protected Object handleFindTaggedValue(final String name)
    {
        final Collection taggedValues = this.findTaggedValues(name);
        return taggedValues.isEmpty() ? null : taggedValues.iterator().next();
    }

    /**
     * Assumes no stereotype inheritance
     *
     * @see org.andromda.metafacades.uml.ModelElementFacade#hasStereotype(String)
     */
    @Override
    protected boolean handleHasStereotype(final String stereotypeName)
    {
        return UmlUtilities.containsStereotype(
            this.metaObject,
            stereotypeName);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String)
     */
    @Override
    protected String handleGetDocumentation(final String indent)
    {
        return this.handleGetDocumentation(
            indent,
            100 - indent.length());
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getFullyQualifiedName(boolean)
     */
    @Override
    protected String handleGetFullyQualifiedName(final boolean modelName)
    {
        return handleGetBindedFullyQualifiedName(modelName, Collections.<BindingFacade>emptyList());
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String,
     *      int)
     */
    @Override
    protected String handleGetDocumentation(
        final String indent,
        final int lineLength)
    {
        // We really don't want the start/end paragraph returns on the first/last lines. Very annoying. Makes the docs too long with mixed html.
        // That's the only thing that htmlStyle does: convert each line to <p>line</p>. Not necessary.
        return this.handleGetDocumentation(
            indent,
            lineLength,
            false);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#hasExactStereotype(String)
     */
    @Override
    protected boolean handleHasExactStereotype(final String stereotypeName)
    {
        return this.handleGetStereotypeNames().contains(StringUtils.trimToEmpty(stereotypeName));
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraint(String,
     *      String)
     */
    @Override
    protected String handleTranslateConstraint(
        final String name,
        final String translation)
    {
        String translatedExpression = "";
        final ConstraintFacade constraint =
            (ConstraintFacade)CollectionUtils.find(
                this.getConstraints(),
                new Predicate()
                {
                    public boolean evaluate(final Object object)
                    {
                        final ConstraintFacade constraintEval = (ConstraintFacade)object;
                        return StringUtils.trimToEmpty(constraintEval.getName()).equals(StringUtils.trimToEmpty(name));
                    }
                });

        if (constraint != null)
        {
            translatedExpression = constraint.getTranslation(translation);
        }
        return translatedExpression;
    }

    /**
     * Private helper that translates all the expressions contained in the
     * <code>constraints</code>, and returns an array of the translated
     * expressions.
     *
     * @param constraints
     *            the constraints to translate
     * @param translation
     *            the translation to translate <code>to</code>.
     * @return String[] the translated expressions, or null if no constraints
     *         were found
     */
    // TODO: Possible covariant of the method 'translateConstraints' defined in the class 'ModelElementFacadeLogic'
    private String[] translateConstraints(
        final Collection<ConstraintFacade> constraints,
        final String translation)
    {
        String[] translatedExpressions = null;
        if (constraints != null && !constraints.isEmpty())
        {
            translatedExpressions = new String[constraints.size()];
            final Iterator<ConstraintFacade> constraintIt = constraints.iterator();
            for (int ctr = 0; constraintIt.hasNext(); ctr++)
            {
                final ConstraintFacade constraint = constraintIt.next();
                translatedExpressions[ctr] = constraint.getTranslation(translation);
            }
        }
        return translatedExpressions;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraints(String,
     *      String)
     */
    @Override
    protected String[] handleTranslateConstraints(
        final String kind,
        final String translation)
    {
        final Collection<ConstraintFacade> constraints = this.getConstraints();
        CollectionUtils.filter(
            constraints,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    final ConstraintFacade constraint = (ConstraintFacade)object;
                    return UMLMetafacadeUtils.isConstraintKind(
                        constraint.getBody(),
                        kind);
                }
            });
        return this.translateConstraints(
            constraints,
            translation);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#translateConstraints(String)
     */
    @Override
    protected String[] handleTranslateConstraints(final String translation)
    {
        return this.translateConstraints(
            this.getConstraints(),
            translation);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getConstraints(String)
     */
    @Override
    protected Collection<ConstraintFacade> handleGetConstraints(final String kind)
    {
        final Collection<ConstraintFacade> constraints = new ArrayList<ConstraintFacade>();
        for (ConstraintFacade constraint : this.getConstraints())
        {
            if ((ExpressionKinds.BODY.equals(kind) && constraint.isBodyExpression()) ||
                (ExpressionKinds.DEF.equals(kind) && constraint.isDefinition()) ||
                (ExpressionKinds.INV.equals(kind) && constraint.isInvariant()) ||
                (ExpressionKinds.PRE.equals(kind) && constraint.isPreCondition()) ||
                (ExpressionKinds.POST.equals(kind) && constraint.isPostCondition()))
            {
                constraints.add(constraint);
            }
        }
        return constraints;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#findTaggedValues(String)
     */
    @Override
    protected Collection<Object> handleFindTaggedValues(String name)
    {
        final Collection<Object> values = new ArrayList<Object>();

        // only search a tagged value when it actually has a name
        if (StringUtils.isNotBlank(name))
        {
            // trim the name, we don't want leading/trailing spaces
            name = StringUtils.trimToEmpty(name);

            // loop over the tagged values
            final Collection<TaggedValueFacade> taggedValues = this.getTaggedValues();
            for (final TaggedValueFacade taggedValue : taggedValues)
            {
                // does this name match the argument tagged value name ?
                if (UmlUtilities.doesTagValueNameMatch(name, taggedValue.getName()))
                {
                    // 'tagged values' can have arrays of strings as well as
                    // strings as values.
                    /* http://www.andromda.org/jira/browse/UMLMETA-89
                    Object value = taggedValue.getValue();
                    if (value instanceof Collection)
                    {
                        values.addAll((Collection) taggedValue.getValue());
                    }
                    else
                    {
                        values.add(taggedValue.getValue());
                    } */
                    //
                    if (taggedValue.getValues() != null && !taggedValue.getValues().isEmpty())
                    {
                        values.addAll(taggedValue.getValues());
                    }
                    // If it matches this taggedValue, and taggedValues are unique, no need to check the rest.
                    break;
                }
            }
        }
        return values;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getDocumentation(String,
     *      int, boolean)
     */
    @Override
    protected String handleGetDocumentation(
        final String indent,
        int lineLength,
        final boolean htmlStyle)
    {
        final StringBuilder documentation = new StringBuilder();

        if (lineLength < 1)
        {
            lineLength = Integer.MAX_VALUE;
        }

        final Collection<Comment> comments = this.metaObject.getOwnedComments();
        if (comments != null && !comments.isEmpty())
        {
            for (final Comment comment : comments)
            {
                final String commentString = StringUtils.trimToEmpty(comment.getBody());

                // Comment.toString returns org.eclipse.uml2.uml.internal.impl.CommentImpl@95c90f4 (body: )
                /*if (StringUtils.isBlank(commentString))
                {
                    commentString = StringUtils.trimToEmpty(comment.toString());
                }*/
                documentation.append(StringUtils.trimToEmpty(commentString));
                documentation.append(SystemUtils.LINE_SEPARATOR);
            }
        }

        // if there still isn't anything, try a tagged value
        if (StringUtils.isBlank(documentation.toString()))
        {
            documentation.append(
                StringUtils.trimToEmpty((String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_DOCUMENTATION)));
        }

        // For associations, use the type class documentation if association documentation is missing.
        if (StringUtils.isBlank(documentation.toString())
        && this instanceof AssociationEndFacadeLogicImpl)
        {
            AssociationEndFacadeLogicImpl end = (AssociationEndFacadeLogicImpl)this;
            documentation.append(end.getType().getDocumentation(""));
        }

        // if there still isn't anything, create a todo tag.
        if (StringUtils.isBlank(documentation.toString())
        && Boolean.valueOf((String)this.getConfiguredProperty(UMLMetafacadeProperties.TODO_FOR_MISSING_DOCUMENTATION)))
        {
            final String todoTag = (String)this.getConfiguredProperty(UMLMetafacadeProperties.TODO_TAG);
            documentation.append(todoTag).append(": Model Documentation for " + this.handleGetFullyQualifiedName());
        }

        return StringUtilsHelper.format(
            StringUtils.trimToEmpty(documentation.toString()),
            indent,
            lineLength,
            htmlStyle);
    }

    /**
     * If documentation is present, i.e. to add toDoTag or skip a line if not
     * @return true is documentation comment or Documentation stereotype is present
     * @see org.andromda.metafacades.uml.ModelElementFacade#isDocumentationPresent()
     */
    @Override
    protected boolean handleIsDocumentationPresent()
    {
        boolean rtn = false;
        final Collection<Comment> comments = this.metaObject.getOwnedComments();
        if (comments != null && !comments.isEmpty())
        {
            for (Comment comment : comments)
            {
                final String commentString = StringUtils.trimToEmpty(comment.getBody());

                // if there isn't anything in the body, try the name
                if (StringUtils.isNotBlank(commentString))
                {
                    rtn = true;
                    break;
                }
            }
        }

        if (!rtn && StringUtils.isNotBlank((String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_DOCUMENTATION)))
        {
            rtn = true;
        }

        return rtn;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackageName(boolean)
     */
    @Override
    protected String handleGetPackageName(final boolean modelName)
    {
        String packageName = this.handleGetPackageName();
        if (modelName)
        {
            packageName =
                StringUtils.replace(
                    packageName,
                    ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR)),
                    MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR);
        }
        return packageName;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getTaggedValues()
     */
    @Override
    protected Collection<TagDefinition> handleGetTaggedValues()
    {
        // TODO: UmlUtilities returns TagDefinition, not TaggedValueFacade
        return UmlUtilities.getTaggedValue(this.metaObject);
    }

    /*
     * @see org.andromda.metafacades.uml.ModelElementFacade#getOwnedElements()
    protected Collection<Element> handleGetOwnedElements()
    {
        return this.metaObject.getOwnedElements();
    }
     */

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getPackage()
     */
    @Override
    protected Package handleGetPackage()
    {
        return this.metaObject.getNearestPackage();
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getRootPackage()
     */
    @Override
    protected Package handleGetRootPackage()
    {
        // UML2 Model extends Package, is mapped to a PackageFacade -
        // RootPackage. Need to map Model to UMLResource, not to modelFacade, in metafacade.xml
        return UmlUtilities.findModel(this.metaObject);
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getTargetDependencies()
     */
    @Override
    protected Collection<DirectedRelationship> handleGetTargetDependencies()
    {
        final List<DirectedRelationship> dependencies = new ArrayList<DirectedRelationship>();
        dependencies.addAll((Collection<? extends DirectedRelationship>) UmlUtilities.getAllMetaObjectsInstanceOf(
                DirectedRelationship.class,
                UmlUtilities.getModels()));
        CollectionUtils.filter(
            dependencies,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    final DirectedRelationship relation = (DirectedRelationship) object;
                    if(isAUml14Dependency(relation))
                    {
                        // we only check first, see dependency facade for more detail.
                        return ModelElementFacadeLogicImpl.this.metaObject.equals(relation.getTargets().get(0));
                    }
                    return false;
                }
            });
        return dependencies;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getModel()
     */
    @Override
    protected Resource handleGetModel()
    {
        // Be careful here, Model Facade is mapped to resource
        // TODO map ModelFacade to UML2 Model or Package?
        Resource resource = this.metaObject.getModel().eResource();
        if (resource==null)
        {
            LOGGER.error("ModelElementFacadeLogicImpl.handleGetModel: " + this.metaObject + " Model: " + this.metaObject.getModel());
            resource = (Resource) this.metaObject.getModel();
        }
        return resource;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getStereotypes()
     */
    @Override
    protected Collection<Stereotype> handleGetStereotypes()
    {
        return this.metaObject.getAppliedStereotypes();
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getConstraints()
     */
    @Override
    protected Collection<Constraint> handleGetConstraints()
    {
        final List<Constraint> constraints = new ArrayList<Constraint>();
        constraints.addAll((Collection<? extends Constraint>) UmlUtilities.getAllMetaObjectsInstanceOf(Constraint.class, UmlUtilities.getModels()));

        CollectionUtils.filter(
                constraints,
                new Predicate()
                {
                    public boolean evaluate(final Object object)
                    {
                        final Constraint constraint = (Constraint) object;
                        return constraint.getConstrainedElements().contains(ModelElementFacadeLogicImpl.this.metaObject);
                    }
                });
        return constraints;
    }

    /**
     * @see org.andromda.metafacades.uml.ModelElementFacade#getSourceDependencies()
     */
    @Override
    protected Collection<DirectedRelationship> handleGetSourceDependencies()
    {
        // A more efficient implementation of this would have been to use getClientDependencies() and getTemplateBindings()
        // But it would have required the same filtering
        // This way, the code is the "same" as getTargetDependencies
        final List<DirectedRelationship> dependencies = new ArrayList<DirectedRelationship>();
        dependencies.addAll((Collection<? extends DirectedRelationship>) UmlUtilities.getAllMetaObjectsInstanceOf(
                DirectedRelationship.class, UmlUtilities.getModels()));
        CollectionUtils.filter(
            dependencies,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    final DirectedRelationship relation = (DirectedRelationship) object;
                    if(relation != null && isAUml14Dependency(relation) && relation.getSources() != null && !relation.getSources().isEmpty())
                    {
                        // we only check first, see dependency facade for more detail.
                        return ModelElementFacadeLogicImpl.this.metaObject.equals(relation.getSources().get(0));
                    }
                    return false;
                }
            });
        return dependencies;
    }

    /**
     * This function tests if the given relation is a dependency in UML1.4 sense of term.
     * @param relation The relation to test
     * @return instanceof Abstraction, Deployment, Manifestation, realization, Substitution, Usage
     */
    static boolean isAUml14Dependency(final DirectedRelationship relation)
    {
        // this ensure that this relation is either a dependency or a template binding
        boolean isAUml14Dependency = (relation instanceof Dependency) || (relation instanceof TemplateBinding);

        // but we don't want subclass of dependency
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Abstraction); // present in uml 1.4 (but filter in uml14 facade)
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Deployment);
        //isAUml14Dependency = isAUml14Dependency && !(relation instanceof Implementation);
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Manifestation);
        //isAUml14Dependency = isAUml14Dependency && !(relation instanceof Permission); // present in uml 1.4
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Realization);
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Substitution);
        isAUml14Dependency = isAUml14Dependency && !(relation instanceof Usage);// present in uml 1.4

        return isAUml14Dependency;
    }

    /**
     * @return stateMachine org.eclipse.uml2.uml.StateMachine
     * @see org.andromda.metafacades.uml.ModelElementFacade#getStateMachineContext()
     */
    @Override
    protected StateMachine handleGetStateMachineContext()
    {
        // TODO: What should this method return ?
        // As I've seen in uml1.4 impl, it should return the statemachine which this element is the context for.
        // Let's say for UML2: Return the owner if the latter is a StateMachine
        StateMachine stateMachine = null;
        final Element owner = this.metaObject.getOwner();
        if(owner instanceof StateMachine)
        {
            stateMachine = (StateMachine) owner;
        }
        return stateMachine;
    }

    /**
     * @see org.andromda.core.metafacade.MetafacadeBase#getValidationName()
     */
    @Override
    public String getValidationName()
    {
        final StringBuilder validationName = new StringBuilder();
        final Object separator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
        for (NamedElement namespace = (NamedElement)this.metaObject.getOwner(); namespace != null;
            namespace = (NamedElement)namespace.getOwner())
        {
            if (validationName.length() == 0)
            {
                validationName.append(namespace.getName());
            }
            else
            {
                validationName.insert(
                    0,
                    separator);
                validationName.insert(
                    0,
                    namespace.getName());
            }
        }
        if (validationName.length() > 0)
        {
            validationName.append(separator);
        }
        if (StringUtils.isNotBlank(this.handleGetName()))
        {
            validationName.append(this.handleGetName());
        }
        else
        {
            validationName.append(this.getConfiguredProperty(UMLMetafacadeProperties.UNDEFINED_NAME));
        }
        return validationName.toString();
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleIsBindingDependenciesPresent()
     */
    @Override
    protected boolean handleIsBindingDependenciesPresent()
    {
        final Collection<DirectedRelationship> dependencies = this.handleGetSourceDependencies();
        CollectionUtils.filter(
            dependencies,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    return object instanceof BindingFacade;
                }
            });
        return !dependencies.isEmpty();
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleIsTemplateParametersPresent()
     */
    @Override
    protected boolean handleIsTemplateParametersPresent()
    {
        final Collection<TemplateParameter> params = this.handleGetTemplateParameters();
        return params != null && !params.isEmpty();
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleCopyTaggedValues(org.andromda.metafacades.uml.ModelElementFacade)
     */
    @Override
    protected void handleCopyTaggedValues(final ModelElementFacade element)
    {
        // TODO What to do with this ?
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleGetTemplateParameter(String)
     */
    @Override
    protected Object handleGetTemplateParameter(String parameterName)
    {
        TemplateParameterFacade templateParameter = null;
        if (StringUtils.isNotBlank(parameterName))
        {
            parameterName = StringUtils.trimToEmpty(parameterName);
            final Collection<TemplateParameterFacade> parameters = this.getTemplateParameters();
            if (parameters != null && !parameters.isEmpty())
            {
                for (final TemplateParameterFacade currentTemplateParameter : parameters)
                {
                    if (currentTemplateParameter.getParameter() != null)
                    {
                        final ModelElementFacade parameter = currentTemplateParameter.getParameter();

                        // there should not be two template parameters with the same parameter name, but nothing
                        // prevents the model from allowing that.  So return the first instance if found.
                        if (parameterName.equals(parameter.getName()))
                        {
                            templateParameter = currentTemplateParameter;
                            break;
                        }
                    }
                }
            }
        }

        return templateParameter;
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleGetTemplateParameters()
     */
    @Override
    protected Collection<TemplateParameter> handleGetTemplateParameters()
    {
        final Collection<TemplateParameter> templateParameters = new ArrayList<TemplateParameter>();
        if (this.metaObject instanceof TemplateableElement)
        {
            final TemplateableElement templateableElement = (TemplateableElement)this.metaObject;
            final TemplateSignature templateSignature = templateableElement.getOwnedTemplateSignature();
            if (templateSignature != null)
            {
                templateParameters.addAll(templateSignature.getParameters());
            }
        }
        return templateParameters;
    }

    /**
     * @return this.metaObject.getKeywords()
     * @see org.andromda.metafacades.uml.ModelElementFacade#getKeywords()
     */
    @Override
    protected Collection<String> handleGetKeywords()
    {
        return this.metaObject.getKeywords();
    }

    /**
     * @return element.getLabel()
     * @see org.andromda.metafacades.uml.ModelElementFacade#getLabel()
     */
    @Override
    protected String handleGetLabel()
    {
        final NamedElement element = (NamedElement)this.metaObject;
        return element.getLabel();
    }

    /**
     * @return element.getNamespace()
     */
    // * @see org.andromda.metafacades.uml.ModelElementFacade#getModelNamespace()
    //@Override
    protected ModelElementFacade handleGetModelNamespace()
    {
        final NamedElement element = (NamedElement)this.metaObject;
        //return (ModelElementFacade) element.getNamespace();
        return (ModelElementFacade)this.shieldedElement(element.getNamespace());
    }

    /*
     * @return this.metaObject.getOwner()
     * @see org.eclipse.uml2.uml.Element#getOwner()
    //@Override
    protected Element handleGetOwner()
    {
        return this.metaObject.getOwner();
    }
     */

    /**
     * @return element.getQualifiedName()
     * @see org.andromda.metafacades.uml.ModelElementFacade#getQualifiedName()
     */
    protected String handleGetQualifiedName()
    {
        final NamedElement element = (NamedElement)this.metaObject;
        return element.getQualifiedName();
    }

    /**
     * @param keywordName
     * @return this.metaObject.hasKeyword(keywordName)
     * @see org.andromda.metafacades.uml.ModelElementFacade#hasKeyword(String)
     */
    //@Override
    protected boolean handleHasKeyword(final String keywordName)
    {
        return this.metaObject.hasKeyword(keywordName);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String handleGetBindedFullyQualifiedName(final ModelElementFacade bindedElement)
    {
        // This cast is not safe yet, but will be as soon as the filtering will be done
        @SuppressWarnings("unchecked")
        final Collection<BindingFacade> bindingFacades = new ArrayList(bindedElement.getSourceDependencies());
        CollectionUtils.filter(
            bindingFacades,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    return object instanceof BindingFacade;
                }
            });
        return handleGetBindedFullyQualifiedName(false, bindingFacades);
    }

    /**
     * <p>
     * Returns the fully qualified name of the model element. The fully
     * qualified name includes complete package qualified name of the
     * underlying model element.  If modelName is true, then the
     * original name of the model element (the name contained within
     * the model) will be the name returned, otherwise a name from a
     * language mapping will be returned. Moreover use the given collection
     * of {@link BindingFacade} to bind templates parameters to their actual
     * type.
     * </p>
     * @param modelName boolean
     * @param bindingFacades Collection
     * @return String
     */
    private String handleGetBindedFullyQualifiedName(final boolean modelName, final Collection<BindingFacade> bindingFacades)
    {
        final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR;
        String fullName = StringUtils.trimToEmpty(this.handleGetName());
        final String packageName = this.handleGetPackageName(true);
        // Don't fully qualify other UML types such as Action, UseCase etc. Some cartridges require package.name as the result
        // For Parameter, return Classifier.Operation(name)
        if (this.metaObject instanceof Parameter)
        {
            // Add ClassifierName.operationName(ParameterName)
            final Object owner = this.metaObject.getOwner();
            if (owner != null && owner instanceof Operation)
            {
                final Operation operation = (Operation)owner;
                fullName = operation.getName() + '(' + fullName + ')';
                final Object classifier = operation.getOwner();
                if (classifier != null && classifier instanceof NamedElement)
                {
                    fullName = ((NamedElement)classifier).getName() + metafacadeNamespaceScopeOperator + fullName;
                }
            }
        }
        // For Attribute/AssociationEnd, return Classifier.name
        else if (this.metaObject instanceof Property || this.metaObject instanceof Operation)
        {
            final Object classifier = this.metaObject.getOwner();
            if (classifier != null && classifier instanceof NamedElement)
            {
                fullName = ((NamedElement)classifier).getName() + metafacadeNamespaceScopeOperator + fullName;
            }
        }
        if (StringUtils.isNotBlank(packageName))
        {
            fullName = packageName + metafacadeNamespaceScopeOperator + fullName;
        }
        if (!modelName)
        {
            final TypeMappings languageMappings = this.getLanguageMappings();
            if (languageMappings != null)
            {
                fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName));

                // now replace the metafacade scope operators
                // with the mapped scope operators
                final String namespaceScopeOperator =
                    String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.NAMESPACE_SEPARATOR));
                fullName = StringUtils.replace(
                        fullName,
                        metafacadeNamespaceScopeOperator,
                        namespaceScopeOperator);
            }
        }

        if (this.isTemplateParametersPresent() &&
            BooleanUtils.toBoolean(
                ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))))
        {
            // Retrieve all template parameters
            final Collection<TemplateParameterFacade> templateParameters = this.getTemplateParameters();

            // Construct a map of the TemplateParameterFacade to replace
            final Map<TemplateParameterFacade, ModelElementFacade> bindedParameters = new HashMap<TemplateParameterFacade, ModelElementFacade>();
            for(final BindingFacade bindingFacade : bindingFacades)
            {
                ModelElementFacade targetElement = bindingFacade.getTargetElement();
                if(targetElement instanceof RedefinableTemplateSignatureFacade)
                {
                    targetElement = ((RedefinableTemplateSignatureFacade) targetElement).getClassifier();
                }
                if(this.equals(targetElement))
                {
                    final Collection<TemplateArgumentFacade> arguments = bindingFacade.getArguments();
                    if(arguments.size() != getTemplateParameters().size())
                    {
                        throw new IllegalStateException("The size of the arguments of the BindingFacace must be equals to the size of the TemplateParameter collection of this element.");
                    }

                    final Iterator<TemplateArgumentFacade> templateArgumentsIterator = arguments.iterator();
                    for (final TemplateParameterFacade templateParameter : templateParameters)
                    {
                        bindedParameters.put(templateParameter, templateArgumentsIterator.next().getElement());
                    }
                }
            }
            if(bindedParameters.isEmpty())
            {
                for(TemplateParameterFacade templateParameterFacade : templateParameters)
                {
                    if (templateParameterFacade != null)
                    {
                        bindedParameters.put(templateParameterFacade, templateParameterFacade.getParameter());
                    }
                }
            }

            // we'll be constructing the parameter list in this buffer
            // add the name we've constructed so far
            final StringBuilder buffer = new StringBuilder(fullName).append('<');

            // loop over the parameters, we are so to have at least one (see
            // outer condition)
            for (final Iterator<TemplateParameterFacade> parameterIterator = templateParameters.iterator(); parameterIterator.hasNext();)
            {
                TemplateParameterFacadeLogicImpl parameter = (TemplateParameterFacadeLogicImpl)parameterIterator.next();
                if (parameter == null)
                {
                    ModelElementFacadeLogicImpl.LOGGER.error("TemplateParameter was null");
                }
                if (parameter != null && parameter.getType() != null)
                {
                    String typeName = parameter.getType().getFullyQualifiedName();
                    /*final ModelElementFacade modelElement = bindedParameters.get(parameterIterator.next());

                    // TODO: UML14 returns ParameterFacade, UML2 returns ModelElementFacade, so types are wrong from fullyQualifiedName
                    // Mapping from UML2 should return ParameterFacade, with a getType method.
                    // Add TemplateParameterFacade.getType method - need to access this in vsl templates.
                    if (modelElement instanceof ParameterFacade)
                    {
                        buffer.append(((ParameterFacade)modelElement).getType().getFullyQualifiedName());
                    }
                    else
                    {*/
                        buffer.append(typeName);
                    //}

                    if (parameterIterator.hasNext())
                    {
                        buffer.append(", ");
                    }
                }
                else
                {
                    ModelElementFacadeLogicImpl.LOGGER.error(
                    ((NamedElement)super.metaObject.getOwner()).getName() + "." + parameter + " parameter type NULL");
                }
            }

            // we're finished listing the parameters
            buffer.append('>');

            // we have constructed the full name in the buffer
            fullName = buffer.toString();
        }

        return fullName;
    }

    // Valid identifier starts with alphanum or _$ and contains only alphanum or _$ or digits
    private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_$][a-zA-Z\\d_$]*");
    /**
     * Return true if name matches the pattern [a-zA-Z_$][a-zA-Z\\d_$]*
     * @see org.andromda.metafacades.emf.uml22.ModelElementFacadeLogic#handleIsValidIdentifierName()
     */
    @Override
    protected boolean handleIsValidIdentifierName()
    {
        final String name = this.handleGetName();
        return IDENTIFIER_PATTERN.matcher(name).matches();
    }
}