AttributeFacadeLogicImpl.java

package org.andromda.metafacades.emf.uml22;

import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.EntityAttribute;
import org.andromda.metafacades.uml.EnumerationFacade;
import org.andromda.metafacades.uml.NameMasker;
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.utils.StringUtilsHelper;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Type;

/**
 * MetafacadeLogic implementation for
 * org.andromda.metafacades.uml.AttributeFacade.
 *
 * @see org.andromda.metafacades.uml.AttributeFacade
 * @author Bob Fields
 */
public class AttributeFacadeLogicImpl
    extends AttributeFacadeLogic
{
    private static final long serialVersionUID = 34L;
    /**
     * @param metaObjectIn
     * @param context
     */
    public AttributeFacadeLogicImpl(
        final Attribute metaObjectIn,
        final String context)
    {
        super(metaObjectIn, context);
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getGetterName()
     */
    @Override
    protected String handleGetGetterName()
    {
        return UMLMetafacadeUtils.getGetterPrefix(this.getType(), this.getLower()) + StringUtils.capitalize(this.handleGetName());
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getSetterName()
     */
    @Override
    protected String handleGetSetterName()
    {
        return "set" + StringUtils.capitalize(this.handleGetName());
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isReadOnly()
     */
    @Override
    protected boolean handleIsReadOnly()
    {
        return this.metaObject.isReadOnly();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getDefaultValue()
     */
    @Override
    protected String handleGetDefaultValue()
    {
        String defaultValue = this.metaObject.getDefault();
        // Put single or double quotes around default in case modeler forgot to do it. Most templates
        // declare Type attribute = $attribute.defaultValue, requiring quotes around the value
        if (StringUtils.isNotBlank(defaultValue) && !this.isMany())
        {
            final String typeName = this.metaObject.getType().getName();
            final String fullyQualifiedName = this.handleGetFullyQualifiedName();
            if ("String".equals(typeName) && defaultValue.indexOf('"')<0)
            {
                defaultValue = '"' + defaultValue + '"';
            }
            else if (("char".equals(typeName) || "Character".equals(typeName))
                && defaultValue.indexOf('\'')<0)
            {
                defaultValue = "'" + defaultValue.charAt(0) + '\'';
            }
            else if (fullyQualifiedName.startsWith("java.lang") && defaultValue.indexOf(".valueOf(")<0)
            {
                defaultValue = fullyQualifiedName + ".valueOf(" + defaultValue + ')';
            }
        }
        if (defaultValue==null) {defaultValue="";}
        return defaultValue;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isStatic()
     */
    @Override
    protected boolean handleIsStatic()
    {
        return this.metaObject.isStatic();
    }

    /**
     * @return this.metaObject.isLeaf()
     * @see org.andromda.metafacades.uml.AttributeFacade#isLeaf()
     */
    @Override
    protected boolean handleIsLeaf()
    {
        return this.metaObject.isLeaf();
    }

    /**
     * @see org.andromda.metafacades.uml.AssociationEndFacade#isMany()
     */
    @Override
    protected boolean handleIsMany()
    {
        // Because of MD11.5 (their multiplicity are String), we cannot use isMultiValued()
        // Name or type may be null during the model validation process.
        return this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED
               || (null!=this.getType() && (this.getType().isArrayType() || this.getType().isCollectionType()));
    }

    /**
     * @see org.andromda.metafacades.uml.AssociationEndFacade#isRequired()
     */
    @Override
    protected boolean handleIsRequired()
    {
        return this.getLower() > 0;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isChangeable()
     */
    @Override
    protected boolean handleIsChangeable()
    {
        return !this.metaObject.isReadOnly();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isAddOnly()
     */
    @Override
    protected boolean handleIsAddOnly()
    {
        // TODO: really nothing to do here ?
        return false;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteral()
     */
    @Override
    protected boolean handleIsEnumerationLiteral()
    {
        final ClassifierFacade owner = this.getOwner();
        return (owner != null) && owner.isEnumeration();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationValue()
     */
    @Override
    protected String handleGetEnumerationValue()
    {
        String value = null;
        if (this.isEnumerationLiteral())
        {
            value = this.getDefaultValue();
            value = (StringUtils.isBlank(value)) ? this.handleGetName() : String.valueOf(value);
        }
        if (this.getType().isStringType() && value!=null && value.indexOf('"')<0)
        {
            value = '\"' + value + '\"';
        }
        return value;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationMember()
     */
    @Override
    protected boolean handleIsEnumerationMember()
    {
        boolean isMemberVariable = false;
        final String isMemberVariableAsString = (String)this.findTaggedValue(
                UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_MEMBER_VARIABLE);
        if (StringUtils.isNotBlank(isMemberVariableAsString) && BooleanUtils.toBoolean(isMemberVariableAsString))
        {
            isMemberVariable = true;
        }
        return isMemberVariable;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationLiteralParameters()
     */
    @Override
    protected String handleGetEnumerationLiteralParameters()
    {
        return (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_LITERAL_PARAMETERS);
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteralParametersExist()
     */
    @Override
    protected boolean handleIsEnumerationLiteralParametersExist()
    {
        boolean parametersExist = false;
        if (StringUtils.isNotBlank(this.getEnumerationLiteralParameters()))
        {
            parametersExist = true;
        }
        return parametersExist;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getGetterSetterTypeName()
     */
    @Override
    protected String handleGetGetterSetterTypeName()
    {
        String name = null;
        if (this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED)
        {
            final TypeMappings mappings = this.getLanguageMappings();
            //TODO: Create Implementation types for declared types, with mappings from declaration -> implementation
            // TODO: Fix Metafacade models to properly reflect Unique/Ordered in associations, and update Impl classes
            /*if (this.handleIsUnique())
            {
                name =
                    this.isOrdered() ? mappings.getTo(UMLProfile.ORDERED_SET_TYPE_NAME)
                                     : mappings.getTo(UMLProfile.SET_TYPE_NAME);
            }
            else
            {*/
                name =
                    this.isOrdered() ? mappings.getTo(UMLProfile.LIST_TYPE_NAME)
                                     : mappings.getTo(UMLProfile.COLLECTION_TYPE_NAME);
            /*}*/

            // set this attribute's type as a template parameter if required
            if (BooleanUtils.toBoolean(
                    ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))))
            {
                String type = this.getType().getFullyQualifiedName();
                if (this.getType().isPrimitive() || this.getLower() > 0)
                {
                    // Can't template primitive values, Objects only. Convert to wrapped.
                    type = this.getType().getWrapperName();
                    if (type == null)
                    {
                        // No wrapper name configured
                        type = this.getType().getFullyQualifiedName();
                    }
                }
                // Allow List<Type[]> implementations.
                /*// Don't apply templating to modeled array types
                if (this.getType().isArrayType())
                {
                    type = type.substring(0, type.length()-2);
                }*/
                /*Collection<GeneralizableElementFacade> specializations = this.getType().getAllSpecializations();
                if ((specializations != null && !specializations.isEmpty()))
                {
                    name += "<? extends " + type + '>';
                }
                else
                {*/
                    name += '<' + type + '>';
                /*}*/
            }
        }
        if (name == null && this.handleGetType() != null)
        {
            name = this.getType().getFullyQualifiedName();
            // Special case: lower bound overrides primitive/wrapped type declaration
            // TODO Apply to all primitive types, not just booleans. This is a special case because of is/get Getters.
            if (this.getType().isBooleanType())
            {
                // Datatypes will be inconsistent with multiplicity but identifier attributes shouldn't be changed automatically
                if (this.getType().isPrimitive() && this.getLower() < 1 &&
                    (!(this instanceof EntityAttribute) || !((EntityAttribute) this).isIdentifier()))
                {
                    // Type is optional, should not be primitive
                    name = this.getType().getWrapperName();
                    if (name == null)
                    {
                        // No wrapper name configured
                        name = this.getType().getFullyQualifiedName();
                    }
                }
                /*else //if (this.getType().isPrimitive())
                {
                    // Type is required, should not be wrapped
                }*/
            }
        }
        return name;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isOrdered()
     */
    @Override
    protected boolean handleIsOrdered()
    {
        return this.metaObject.isOrdered();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isUnique()
     */
    @Override
    protected boolean handleIsUnique()
    {
        return this.metaObject.isUnique() || this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE);
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getOwner()
     */
    @Override
    protected Class handleGetOwner()
    {
        // This is sure for attribute
        return this.metaObject.getClass_();
    }

    /**
     * @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner()
     */
    @Override
    public Object getValidationOwner()
    {
        return this.getOwner();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getType()
     */
    @Override
    protected Type handleGetType()
    {
        return this.metaObject.getType();
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#getEnumeration()
     */
    @Override
    protected EnumerationFacade handleGetEnumeration()
    {
        return (EnumerationFacade)(this.isEnumerationLiteral() ? this.getOwner() : null);
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.AttributeFacadeLogic#handleIsDefaultValuePresent()
     */
    @Override
    protected boolean handleIsDefaultValuePresent()
    {
        return StringUtils.isNotBlank(this.getDefaultValue());
    }

    /**
     * Overridden to provide different handling of the name if this attribute represents a literal.
     *
     * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
     */
    @Override
    protected String handleGetName()
    {
        final String mask = String.valueOf(this.getConfiguredProperty(
            // http://andromda.sourceforge.net/jira/browse/UMLMETA-88
            this.getOwner() instanceof EnumerationFacade && !this.isEnumerationMember()
                ? UMLMetafacadeProperties.ENUMERATION_LITERAL_NAME_MASK
                : UMLMetafacadeProperties.CLASSIFIER_PROPERTY_NAME_MASK ));

        String name = NameMasker.mask(super.handleGetName(), mask);
        if (this.isMany() && this.isPluralizeAttributeNames() && !this.isEnumerationMember())
        {
            name = StringUtilsHelper.pluralize(name);
        }

        return name;
    }

    /**
     * Indicates whether or not we should pluralize association end names.
     *
     * @return true/false
     */
    private boolean isPluralizeAttributeNames()
    {
        final Object value = this.getConfiguredProperty(UMLMetafacadeProperties.PLURALIZE_ATTRIBUTE_NAMES);
        return value != null && Boolean.valueOf(String.valueOf(value));
    }

    /*  protected boolean handleIsBindingDependenciesPresent()
      {
          // TODO: Implement this with Templates.
          // This method has been overridden. Why ?
          return false;
      }

      protected boolean handleIsTemplateParametersPresent()
      {
          // TODO: This method has been overridden. Why ?
          return false;
      }

      protected void handleCopyTaggedValues(final ModelElementFacade element)
      {
          // TODO: This method has been overridden. Why ?
      }

      protected Object handleGetTemplateParameter(final String parameterName)
      {
          // TODO: This method has been overridden. Why ?
          return null;
      }

      protected Collection handleGetTemplateParameters()
      {
          // TODO: This method has been overridden. Why ?
          return null;
      } */

    /**
     * Get the UML upper multiplicity Not implemented for UML1.4
     * @return int upperMultiplicity, based UML multiplicity, default 1
     */
    @Override
    protected int handleGetUpper()
    {
        // MD11.5 Exports multiplicity as String
        return UmlUtilities.parseMultiplicity(this.metaObject.getUpperValue(), 1);
    }

    /**
     * Get the UML lower multiplicity Not implemented for UML1.4
     * @return int lowerMultiplicity, based on primitive/wrapped type and UML multiplicity, default 1
     */
    @Override
    protected int handleGetLower()
    {
        // MD11.5 Exports multiplicity as String
        return UmlUtilities.parseLowerMultiplicity(this.metaObject.getLowerValue(),
            this.getType(),
            ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)));
    }

    /**
     * Override to change private to public, since we provide accessors in generated code
     * Allows for protected, package level visibility of accessors/mutators in the model
     * @return String visibility
     */
    @Override
    protected String handleGetVisibility()
    {
        String visibility = super.handleGetVisibility();
        if (visibility==null || visibility.equals("private"))
        {
            visibility = "public";
        }
        return visibility;
    }

    /**
     * @see org.andromda.metafacades.emf.uml22.AttributeFacadeLogic#handleFindTaggedValue(String, boolean)
     */
    @Override
    protected Object handleFindTaggedValue(
        String name,
        final boolean follow)
    {
        name = StringUtils.trimToEmpty(name);
        Object value = this.findTaggedValue(name);
        if (follow)
        {
            ClassifierFacade type = this.getType();
            while (value == null && type != null)
            {
                value = type.findTaggedValue(name);
                type = (ClassifierFacade)type.getGeneralization();
            }
        }
        return value;
    }

    /**
     * @see org.andromda.metafacades.uml.AttributeFacade#isDerived()
     */
    @Override
    protected boolean handleIsDerived()
    {
        return this.metaObject.isDerived();
    }
}