EJB3MetafacadeUtils.java

package org.andromda.cartridges.ejb3.metafacades;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.andromda.cartridges.ejb3.EJB3Globals;
import org.andromda.cartridges.ejb3.EJB3Profile;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.metafacades.uml.AttributeFacade;
import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.EntityMetafacadeUtils;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.OperationFacade;
import org.andromda.metafacades.uml.UMLProfile;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;

/**
 * Contains utilities for use with EJB metafacades.
 *
 * @author Chad Brandon
 * @author Vance Karimi
 */
class EJB3MetafacadeUtils
{
    /**
     * Gets all create methods for the given <code>classifier</code>.
     *
     * @param classifier The classifier from which to retries the create methods
     * @param follow if true, all super type create methods are also retrieved
     * @return collection of create methods found.
     */
    static Collection<OperationFacade> getCreateMethods(
            ClassifierFacade classifier,
            boolean follow)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        Collection<OperationFacade> retval = new ArrayList<OperationFacade>();
        ClassifierFacade entity = classifier;
        do
        {
            for (final OperationFacade op : entity.getOperations())
            {
                if (op.hasStereotype(EJB3Profile.STEREOTYPE_CREATE_METHOD))
                {
                    retval.add(op);
                }
            }
            if (follow)
            {
                entity = (ClassifierFacade)entity.getGeneralization();
            }
        }
        while (follow && entity != null);
        return retval;
    }

    /**
     * Gets the interface name for the passed in <code>classifier</code>. Returns 'LocalHome' if the mode element has
     * the entity stereotype, returns 'Home' otherwise.
     * @param classifier
     * @return the interface name.
     */
    static String getHomeInterfaceName(ClassifierFacade classifier)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        String homeInterfaceName;
        if (classifier.hasStereotype(UMLProfile.STEREOTYPE_ENTITY))
        {
            homeInterfaceName = classifier.getName() + "LocalHome";
        }
        else
        {
            homeInterfaceName = classifier.getName() + "Home";
        }
        return homeInterfaceName;
    }

    /**
     * Gets the view type for the passed in <code>classifier</code>. If no
     * view type can be retrieved from the <code>classifier</code>, then the
     * <code>defaultViewType</code> is returned.
     *
     * If session ejb pojo then checks the ejb tagged value and if there is
     * no value defined, returns 'remote'.
     *
     * @param classifier The classifier to lookup the view type tagged value
     * @param defaultViewType The default view type if one is not found
     * @return String the view type name.
     */
    static String getViewType(
            ClassifierFacade classifier,
            String defaultViewType)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        String viewType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_EJB_VIEWTYPE);
        if (StringUtils.isBlank(viewType))
        {
            if (classifier.hasStereotype(EJB3Profile.STEREOTYPE_SERVICE))
            {
                // if the view type wasn't found, search all super classes
                if (StringUtils.isBlank(viewType))
                {
                    viewType = (String)CollectionUtils.find(
                            classifier.getAllGeneralizations(),
                            new Predicate()
                            {
                                public boolean evaluate(Object object)
                                {
                                    return ((ModelElementFacade)object).findTaggedValue(
                                            EJB3Profile.TAGGEDVALUE_EJB_VIEWTYPE) != null;
                                }
                            });
                }
                if (StringUtils.isBlank(viewType))
                {
                    viewType = (StringUtils.isNotBlank(defaultViewType) ?
                            defaultViewType : EJB3Globals.VIEW_TYPE_REMOTE);
                }
            }
        }
        return viewType.toLowerCase();
    }

    /**
     * Gets all the inherited instance attributes, excluding the instance attributes directory from this
     * <code>classifier</code>.
     *
     * @param classifier the ClassifierFacade from which to retrieve the inherited attributes.
     * @return a list of ordered attributes.
     */
    static List<AttributeFacade> getInheritedInstanceAttributes(ClassifierFacade classifier)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        ClassifierFacade current = (ClassifierFacade)classifier.getGeneralization();
        if (current == null)
        {
            return new ArrayList<AttributeFacade>();
        }
        List<AttributeFacade> retval = getInheritedInstanceAttributes(current);

        if (current.getInstanceAttributes() != null)
        {
            retval.addAll(current.getInstanceAttributes());
        }
        return retval;
    }

    /**
     * Gets all instance attributes including those instance attributes belonging to the
     * <code>classifier</code> and any inherited ones.
     *
     * @param classifier the ClassifierFacade from which to retrieve the instance attributes.
     * @return the list of all instance attributes.
     */
    static List<AttributeFacade> getAllInstanceAttributes(ClassifierFacade classifier)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        List<AttributeFacade> retval = getInheritedInstanceAttributes(classifier);
        retval.addAll(classifier.getInstanceAttributes());
        return retval;
    }

    /**
     * Gets all environment entries for the specified <code>classifier</code>. If <code>follow</code> is true, then a
     * search up the inheritance hierarchy will be performed and all super type environment entries will also be
     * retrieved.
     *
     * @param classifier the classifier from which to retrieve the env-entries
     * @param follow     true/false on whether or not to 'follow' the inheritance hierarchy when retrieving the
     *                   env-entries.
     * @return the collection of environment entries
     */
    static Collection<AttributeFacade> getEnvironmentEntries(
            ClassifierFacade classifier,
            boolean follow)
    {
        ExceptionUtils.checkNull("classifer", classifier);

        Collection<AttributeFacade> attributes = classifier.getStaticAttributes();

        if (follow)
        {
            for (classifier = (ClassifierFacade)classifier.getGeneralization();
                 classifier != null; classifier = (ClassifierFacade)classifier.getGeneralization())
            {
                attributes.addAll(classifier.getStaticAttributes());
            }
        }

        CollectionUtils.filter(attributes, new Predicate()
        {
            public boolean evaluate(Object object)
            {
                return ((AttributeFacade)object).hasStereotype(EJB3Profile.STEREOTYPE_ENV_ENTRY);
            }
        });

        return attributes;
    }

    /**
     * Returns the transaction type for the specified <code>classifier</code>.
     *
     * @param classifier the classifier from which to retrieve the transaction type.
     * @param defaultTransactionType the default transaction type if no tagged value is specified.
     * @return the transaction type as a String.
     */
    static String getTransactionType(ClassifierFacade classifier, String defaultTransactionType)
    {
        ExceptionUtils.checkNull("classifer", classifier);

        String transactionType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_EJB_TRANSACTION_TYPE);
        if (StringUtils.isNotBlank(transactionType))
        {
            transactionType = convertTransactionType(transactionType);
        }
        else
        {
            transactionType = defaultTransactionType;
        }
        return transactionType;
    }

    /**
     * Convert the transaction type from lower casing to upper casing.
     * This maintains reusable tagged value enumeration from EJB
     * implementation.
     *
     * @param transType
     * @return transactionType
     */
    static String convertTransactionType(String transType)
    {
        ExceptionUtils.checkNull("transType", transType);

        String type = null;
        if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_MANDATORY))
        {
            type = "MANDATORY";
        }
        else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_NEVER))
        {
            type = "NEVER";
        }
        else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_NOT_SUPPORTED))
        {
            type = "NOT_SUPPORTED";
        }
        else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_REQUIRED))
        {
            type = "REQUIRED";
        }
        else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_REQUIRES_NEW))
        {
            type = "REQUIRES_NEW";
        }
        else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_SUPPORTS))
        {
            type = "SUPPORTS";
        }
        return type;
    }

    /**
     * Gets all constants for the specified <code>classifier</code>.
     * If <code>follow</code> is true, then a search up
     * the inheritance hierarchy will be performed and all super
     * type constants will also be retrieved.
     *
     * @param classifier the classifier from which to retrieve the constants
     * @param follow     true/false on whether or not to 'follow' the inheritance hierarchy when retrieving the
     *                   constants.
     * @return the collection of environment entries
     */
    static Collection<AttributeFacade> getConstants(
            ClassifierFacade classifier,
            boolean follow)
    {
        ExceptionUtils.checkNull("classifer", classifier);

        Collection<AttributeFacade> attributes = classifier.getStaticAttributes();

        if (follow)
        {
            for (classifier = (ClassifierFacade)classifier.getGeneralization();
                 classifier != null; classifier = (ClassifierFacade)classifier.getGeneralization())
            {
                attributes.addAll(classifier.getStaticAttributes());
            }
        }

        CollectionUtils.filter(attributes, new Predicate()
        {
            public boolean evaluate(Object object)
            {
                return !((AttributeFacade)object).hasStereotype(EJB3Profile.STEREOTYPE_ENV_ENTRY);
            }
        });

        return attributes;
    }

    /**
     * Returns true/false based on whether or not synthetic or auto generated create methods should be allowed.
     *
     * @param classifier The entity or session bean.
     * @return true/false
     */
    static boolean allowSyntheticCreateMethod(ClassifierFacade classifier)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        return !classifier.isAbstract() && classifier.findTaggedValue(
                EJB3Profile.TAGGEDVALUE_EJB_NO_SYNTHETIC_CREATE_METHOD) == null;
    }

    /**
     * Creates a fully qualified name from the given <code>packageName</code>,
     * <code>name</code>, and <code>suffix</code>.
     *
     * @param packageName the name of the model element package.
     * @param name the name of the model element.
     * @param suffix the suffix to append.
     * @return the new fully qualified name.
     */
    static String getFullyQualifiedName(
        String packageName,
        String name,
        String suffix)
    {
        StringBuilder fullyQualifiedName = new StringBuilder(StringUtils.trimToEmpty(packageName));
        if (StringUtils.isNotBlank(packageName))
        {
            fullyQualifiedName.append('.');
        }
        fullyQualifiedName.append(StringUtils.trimToEmpty(name));
        fullyQualifiedName.append(StringUtils.trimToEmpty(suffix));
        return fullyQualifiedName.toString();
    }

    /**
     * Returns true if the Seam stereotype is modeled on the class.
     *
     * @param classifier The classifier to lookup if the stereotype is modeled
     * @return True is stereotype exists, false otherwise
     */
    static boolean isSeamComponent(ClassifierFacade classifier)
    {
        boolean isSeamComponent = false;
        if (classifier.hasStereotype(EJB3Profile.STEREOTYPE_SEAM_COMPONENT))
        {
            isSeamComponent = true;
        }
        return isSeamComponent;
    }

    /**
     * Gets the Seam component scope type for Entity and Session beans.
     * If no scope has been specified:
     *     If the Classifier is a stateless session bean, then returns STATELESS
     *     If the Classifier is an entity or stateful session bean, then returns CONVERSATION
     *
     * @param classifier The classifier to lookup the scope type tagged value
     * @param stateless Whether the classifier is a stateless session bean
     * @return The scope type as a String
     */
    static String getSeamComponentScopeType(ClassifierFacade classifier, boolean stateless)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        String scopeType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_SEAM_SCOPE_TYPE);
        if (StringUtils.isBlank(scopeType))
        {
            if (stateless)
            {
                scopeType = EJB3Globals.SEAM_COMPONENT_SCOPE_STATELESS;
            }
            else
            {
                scopeType = EJB3Globals.SEAM_COMPONENT_SCOPE_CONVERSATION;
            }
        }
        return scopeType;
    }

    /**
     * Returns the Seam component name.  Can override using tagged value, otherwise just the
     * class name.
     *
     * @param classifier The classifier to get the tagged value or the name from.
     * @return The Seam component name
     */
    static String getSeamComponentName(ClassifierFacade classifier)
    {
        ExceptionUtils.checkNull("classifer", classifier);
        String componentName = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_SEAM_COMPONENT_NAME);
        if (StringUtils.isBlank(componentName))
        {
            componentName = StringUtils.uncapitalize(classifier.getName());
        }
        return componentName;
    }

    /**
     * Builds an annotation parameter line
     * @param parameters The parameters
     * @return The parameter line
     */
    static String buildAnnotationParameters(Collection<String> parameters)
    {
        StringBuilder buf = new StringBuilder();
        if(!parameters.isEmpty())
        {
            buf.append("(");
            Iterator it = parameters.iterator();
            while(it.hasNext())
            {
                String option = (String) it.next();
                buf.append(option);
                if(it.hasNext())
                {
                    buf.append(", ");
                }
            }
            buf.append(")");
            return buf.toString();
        }
        else
        {
            return null;
        }
    }

    /**
     * Builds a multi valued parameter string
     * @param name The name of the parameter
     * @param values The values for the parameters
     * @return The parameter string
     */
    static String buildAnnotationMultivalueParameter(String name, Collection<String> values)
    {
        return buildAnnotationMultivalueParameter(name, values, true);
    }

    /**
     * Builds a multi valued parameter string
     * @param name The name of the parameter
     * @param values The values for the parameters
     * @param areStrings If the values are strings then surround with " sign
     * @return The parameter string
     */
    static String buildAnnotationMultivalueParameter(String name, Collection<String> values, boolean areStrings)
    {
        return buildAnnotationMultivalueParameter(name, values, areStrings, null);
    }

    /**
     * Builds a multi option string
     * Builds a multi valued parameter string
     * @param name The name of the parameter
     * @param values The values for the parameters
     * @param areStrings If the values are strings then surround with " sign
     * @param suffix Any suffix to add to the values
     * @return The parameter string
     */
    static String buildAnnotationMultivalueParameter(String name, Collection<String> values, boolean areStrings, String suffix)
    {
        if(values.isEmpty())
        {
            return null;
        }
        else
        {
            StringBuilder buf = new StringBuilder();
            buf.append(name + " = {");

            Iterator it = values.iterator();
            while(it.hasNext())
            {
                String parameter = (String) it.next();
                if(areStrings)
                {
                    buf.append("\"");
                }
                buf.append(parameter);
                if((suffix != null) && !parameter.endsWith(suffix))
                {
                    buf.append(suffix);
                }
                if(areStrings)
                {
                    buf.append("\"");
                }
                if(it.hasNext())
                {
                    buf.append(", ");
                }
            }
            buf.append("}");
            return buf.toString();
        }
    }

    /**
     * Gets the SQL name. (i.e. column name, table name, etc.). If it can't find
     * the corresponding tagged value with the specified <code>name</code>,
     * then it uses the element name by default and just returns that.
     *
     * @param prefix the optional prefix to add to the sql name (i.e. table name
     *        prefix, etc.).
     * @param element from which to retrieve the SQL name.
     * @param name the name of the tagged value.
     * @param nameMaxLength if this is not null, then the name returned will be
     *        trimmed to this length (if it happens to be longer).
     * @param suffix the optional suffix to add to the sql name (i.e. foreign
     *        key suffix, etc.)
     * @param separator character used to separate words
     * @param shortenSqlNameMethod The method used to shorten the name, i.e. removeVowels
     * @return the SQL name as a String.
     */
    public static String getSqlNameFromTaggedValue(
        String prefix,
        final EJB3AssociationFacade element,
        String name,
        final Short nameMaxLength,
        String suffix,
        final Object separator,
        final Object shortenSqlNameMethod)
    {
        if (element != null)
        {
            Object value = element.findTaggedValue(name);
            StringBuilder buffer = new StringBuilder(StringUtils.trimToEmpty((String)value));
            if (StringUtils.isBlank(buffer.toString()))
            {
                // if we can't find the tagValue then use the
                // element name for the name
                buffer = new StringBuilder(
                        EntityMetafacadeUtils.toSqlName(
                            element.getName(),
                            separator));

                suffix = StringUtils.trimToEmpty(suffix);
                prefix = StringUtils.trimToEmpty(prefix);
                if (nameMaxLength != null)
                {
                    final short maxLength = (short)(nameMaxLength.shortValue() - suffix.length() - prefix.length());
                    buffer =
                        new StringBuilder(
                            EntityMetafacadeUtils.ensureMaximumNameLength(
                                buffer.toString(),
                                new Short(maxLength),
                                (String)shortenSqlNameMethod));
                }
                if (StringUtils.isNotBlank(prefix))
                {
                    buffer.insert(
                        0,
                        prefix);
                }
                if (StringUtils.isNotBlank(suffix))
                {
                    buffer.append(suffix);
                }
            }
            name = buffer.toString();
        }
        return name;
    }
}