All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.andromda.metafacades.emf.uml22.OperationFacadeLogicImpl Maven / Gradle / Ivy

Go to download

The Eclipse EMF UML2 v2.X metafacades. This is the set of EMF UML2 2.X metafacades implementations. These implement the common UML metafacades for .uml model files.

The newest version!
package org.andromda.metafacades.emf.uml22;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.ConstraintFacade;
import org.andromda.metafacades.uml.DependencyFacade;
import org.andromda.metafacades.uml.MetafacadeUtils;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.NameMasker;
import org.andromda.metafacades.uml.OperationFacade;
import org.andromda.metafacades.uml.ParameterFacade;
import org.andromda.metafacades.uml.TypeMappings;
import org.andromda.metafacades.uml.UMLMetafacadeProperties;
import org.andromda.metafacades.uml.UMLProfile;
import org.andromda.translation.ocl.ExpressionKinds;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.uml2.uml.CallConcurrencyKind;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.Type;

/**
 * MetafacadeLogic implementation for
 * org.andromda.metafacades.uml.OperationFacade.
 *
 * @see org.andromda.metafacades.uml.OperationFacade
 * @author Bob Fields
 */
public class OperationFacadeLogicImpl
    extends OperationFacadeLogic
{
    private static final long serialVersionUID = 34L;
    /**
     * The logger instance.
     */
    private static final Logger LOGGER = Logger.getLogger(OperationFacadeLogicImpl.class);

    /**
     * @param metaObject
     * @param context
     */
    public OperationFacadeLogicImpl(
        final Operation metaObject,
        final String context)
    {
        super(metaObject, context);
    }

    /**
     * Not yet implemented, always returns null. To implement: walk through the
     * related elements from the Sequence Diagram or OCL Body in the UML model to produce compilable code.
     * @return null
     * @see org.andromda.metafacades.uml.OperationFacade#getMethodBody()
     */
    @Override
    protected String handleGetMethodBody()
    {
        return null;
    }

    /**
     * Overridden to provide name masking.
     *
     * @see org.andromda.metafacades.uml.ModelElementFacade#getName()
     */
    @Override
    protected String handleGetName()
    {
        final String nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.OPERATION_NAME_MASK));
        return NameMasker.mask(
            super.handleGetName(),
            nameMask);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getSignature()
     */
    @Override
    protected String handleGetSignature()
    {
        return this.getSignature(true);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getCall()
     */
    @Override
    protected String handleGetCall()
    {
        return this.getCall(this.handleGetName());
    }

    /**
     * Constructs the operation call with the given name
     *
     * @param name
     *            the name form which to construct the operation call.
     * @return the operation call.
     */
    private String getCall(final String name)
    {
        final StringBuilder buffer = new StringBuilder(name);
        buffer.append('(');
        buffer.append(this.getArgumentNames());
        buffer.append(')');
        return buffer.toString();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList()
     */
    @Override
    protected String handleGetTypedArgumentList()
    {
        return this.getTypedArgumentList(true);
    }

    private String getTypedArgumentList(final boolean withArgumentNames)
    {
        // TODO: Possible covariant of the method 'getTypedArgumentList' defined in the class 'OperationFacadeLogic'
        return MetafacadeUtils.getTypedArgumentList(
            this.getArguments(),
            withArgumentNames,
            null);
    }

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

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isAbstract()
     */
    @Override
    protected boolean handleIsAbstract()
    {
        return this.metaObject.isAbstract();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getExceptionList()
     */
    @Override
    protected String handleGetExceptionList()
    {
        return this.getExceptionList(null);
    }

    /**
     * Finds both exceptions and exception dependency references
     * @see org.andromda.metafacades.uml.OperationFacade#getExceptions()
     */
    @Override
    protected Collection handleGetExceptions()
    {
        final Collection exceptions = new LinkedHashSet();

        // finds both exceptions and exception references
        final class ExceptionFilter
                implements Predicate
        {
            public boolean evaluate(final Object object)
            {
                boolean hasException = object instanceof DependencyFacade;
                if (hasException)
                {
                    final DependencyFacade dependency = (DependencyFacade)object;
                    // first check for exception references
                    hasException = dependency.hasStereotype(UMLProfile.STEREOTYPE_EXCEPTION_REF);

                    // if there wasn't any exception reference
                    // now check for actual exceptions
                    if (!hasException)
                    {
                        final ModelElementFacade targetElement = dependency.getTargetElement();
                        hasException = targetElement != null && targetElement.hasStereotype(
                                UMLProfile.STEREOTYPE_EXCEPTION);
                    }
                }
                return hasException;
            }
        }

        // first get any dependencies on this operation's
        // owner (because these will represent the default exception(s))
        final Collection ownerDependencies = new ArrayList(this.getOwner().getSourceDependencies());
        if (!ownerDependencies.isEmpty())
        {
            CollectionUtils.filter(ownerDependencies, new ExceptionFilter());
            exceptions.addAll(ownerDependencies);
        }

        final Collection operationDependencies = new ArrayList(this.getSourceDependencies());
        // now get any exceptions directly on the operation
        if (!operationDependencies.isEmpty())
        {
            CollectionUtils.filter(operationDependencies, new ExceptionFilter());
            exceptions.addAll(operationDependencies);
        }

        // now transform the dependency(s) to the actual exception(s)
        CollectionUtils.transform(exceptions, new Transformer()
        {
            public Object transform(final Object object)
            {
                if (object == null) return null;
                return ((DependencyFacade)object).getTargetElement();
            }
        });

        // finally add in any members of the UML2 RaisedException list
        // (the 'proper' UML2 way of doing exceptions .. or at least one way).
        final Collection raisedExceptions = this.metaObject.getRaisedExceptions();
        exceptions.addAll(this.shieldedElements(raisedExceptions));

        return exceptions;
    }


    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isReturnTypePresent()
     */
    @Override
    protected boolean handleIsReturnTypePresent()
    {
        boolean hasReturnType = false;
        if (this.getReturnType() != null)
        {
            hasReturnType = !("void".equalsIgnoreCase(StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName()))
                || StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName(true)).equals(UMLProfile.VOID_TYPE_NAME));
        }
        if (LOGGER.isDebugEnabled())
        {
            final String rtnFQN = this.getReturnType().getFullyQualifiedName(true);
            final boolean voidType = "void".equalsIgnoreCase(StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName()));
            final String voidRtn = this.getReturnType().getFullyQualifiedName();
            LOGGER.debug("OperationFacadeLogicImpl.handleIsReturnTypePresent rtnFQN=" + rtnFQN + " voidType=" + voidType + " voidRtn=" + voidRtn + " hasReturnType=" + hasReturnType);
        }
        return hasReturnType;
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isExceptionsPresent()
     */
    @Override
    protected boolean handleIsExceptionsPresent()
    {
        return !this.getExceptions().isEmpty();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getArgumentNames()
     */
    @Override
    protected String handleGetArgumentNames()
    {
        final StringBuilder buffer = new StringBuilder();

        final Iterator iterator = this.metaObject.getOwnedParameters().iterator();

        boolean commaNeeded = false;
        final String comma = ", ";
        while (iterator.hasNext())
        {
            final Parameter parameter = iterator.next();
            if (!parameter.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL))
            {
                if (commaNeeded)
                {
                    buffer.append(comma);
                }
                final ParameterFacade facade = (ParameterFacade)this.shieldedElement(parameter);
                buffer.append(facade.getName());
                commaNeeded = true;
            }
        }
        return buffer.toString();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getArgumentTypeNames()
     */
    @Override
    protected String handleGetArgumentTypeNames()
    {
        final StringBuilder buffer = new StringBuilder();

        final Iterator iterator = this.metaObject.getOwnedParameters().iterator();

        boolean commaNeeded = false;
        while (iterator.hasNext())
        {
            final Parameter parameter = iterator.next();

            if (!parameter.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL))
            {
                if (commaNeeded)
                {
                    buffer.append(", ");
                }
                final ParameterFacade facade = (ParameterFacade)this.shieldedElement(parameter);
                buffer.append(facade.getType().getFullyQualifiedName());
                commaNeeded = true;
            }
        }
        return buffer.toString();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isQuery()
     */
    @Override
    protected boolean handleIsQuery()
    {
        return this.metaObject.isQuery();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getConcurrency()
     */
    @Override
    protected String handleGetConcurrency()
    {
        String concurrency = null;

        final CallConcurrencyKind concurrencyKind = this.metaObject.getConcurrency();
        if (concurrencyKind == null || concurrencyKind.equals(CallConcurrencyKind.CONCURRENT_LITERAL))
        {
            concurrency = "concurrent";
        }
        else if (concurrencyKind.equals(CallConcurrencyKind.GUARDED_LITERAL))
        {
            concurrency = "guarded";
        }
        else// CallConcurrencyKindEnum.CCK_SEQUENTIAL
        {
            concurrency = "sequential";
        }

        final TypeMappings languageMappings = this.getLanguageMappings();
        if (languageMappings != null)
        {
            concurrency = languageMappings.getTo(concurrency);
        }

        return concurrency;
    }

    /**
     * Gets the pattern for constructing the precondition name.
     *
     * @return the precondition pattern.
     */
    private String getPreconditionPattern()
    {
        return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.PRECONDITION_NAME_PATTERN));
    }

    /**
     * Gets the pattern for constructing the postcondition name.
     *
     * @return the postcondition pattern.
     */
    private String getPostconditionPattern()
    {
        return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.POSTCONDITION_NAME_PATTERN));
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionName()
     */
    @Override
    protected String handleGetPreconditionName()
    {
        return this.getPreconditionPattern().replaceAll(
            "\\{0\\}",
            this.handleGetName());
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPostconditionName()
     */
    @Override
    protected String handleGetPostconditionName()
    {
        return this.getPostconditionPattern().replaceAll(
            "\\{0\\}",
            this.handleGetName());
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionSignature()
     */
    @Override
    protected String handleGetPreconditionSignature()
    {
        return MetafacadeUtils.getSignature(
            this.getPreconditionName(),
            this.getArguments(),
            true,
            null);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionCall()
     */
    @Override
    protected String handleGetPreconditionCall()
    {
        return this.getCall(this.getPreconditionName());
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isPreconditionsPresent()
     */
    @Override
    protected boolean handleIsPreconditionsPresent()
    {
        final Collection preconditions = this.getPreconditions();
        return preconditions != null && !preconditions.isEmpty();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#isPostconditionsPresent()
     */
    @Override
    protected boolean handleIsPostconditionsPresent()
    {
        final Collection postconditions = this.getPostconditions();
        return postconditions != null && !postconditions.isEmpty();
    }

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

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getExceptionList(String)
     */
    @Override
    protected String handleGetExceptionList(String initialExceptions)
    {
        initialExceptions = StringUtils.trimToEmpty(initialExceptions);
        final StringBuilder exceptionList = new StringBuilder(initialExceptions);
        // TODO getExceptions = Collection or ?
        final Collection exceptions = this.getExceptions();
        if (exceptions != null && !exceptions.isEmpty())
        {
            if (StringUtils.isNotBlank(initialExceptions))
            {
                exceptionList.append(", ");
            }
            final Iterator exceptionIt = exceptions.iterator();
            while (exceptionIt.hasNext())
            {
                final ModelElementFacade exception = (ModelElementFacade)exceptionIt.next();
                exceptionList.append(exception.getFullyQualifiedName());
                if (exceptionIt.hasNext())
                {
                    exceptionList.append(", ");
                }
            }
        }

        return exceptionList.toString();
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getSignature(boolean)
     */
    @Override
    protected String handleGetSignature(final boolean withArgumentNames)
    {
        return MetafacadeUtils.getSignature(
            this.handleGetName(),
            this.getArguments(),
            withArgumentNames,
            null);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList(String)
     */
    @Override
    protected String handleGetTypedArgumentList(final String modifier)
    {
        return MetafacadeUtils.getTypedArgumentList(
            this.getArguments(),
            true,
            modifier);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getSignature(String)
     */
    @Override
    protected String handleGetSignature(final String argumentModifier)
    {
        return MetafacadeUtils.getSignature(
            this.handleGetName(),
            this.getArguments(),
            true,
            argumentModifier);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getOwner()
     */
    @Override
    protected Object handleGetOwner()
    {
        Object obj = null;
        // Fix from UML2, no longer calls getOwner to get the owning Class
        if (this.metaObject.getInterface()!=null)
        {
            obj = this.metaObject.getInterface();
        }
        else if (this.metaObject.getDatatype()!=null)
        {
            obj = this.metaObject.getDatatype();
        }
        else
        {
            obj = this.metaObject.getClass_();
        }
        return obj;
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getParameters()
     */
    @Override
    protected Collection handleGetParameters()
    {
        final Collection params = new ArrayList(this.metaObject.getOwnedParameters());
        params.add(this.metaObject.getReturnResult());
        CollectionUtils.filter(
            params,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    return object != null && !((Parameter)object).isException();
                }
            });
        return params;
    }

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

    /**
     * @return this.getReturnType().getFullyQualifiedName()
     * @see org.andromda.metafacades.uml.OperationFacade#getGetterSetterReturnTypeName()
     */
    protected String handleGetGetterSetterReturnTypeName()
    {
        String name = null;
        final ClassifierFacade returnType = this.getReturnType();

        if (returnType!=null && (this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED))
        {
            final TypeMappings mappings = this.getLanguageMappings();
            name =
                this.handleIsOrdered() ? 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 = returnType.getFullyQualifiedName();
                if (returnType.isPrimitive())
                {
                    // Can't template primitive values, Objects only. Convert to wrapped.
                    type = this.getReturnType().getWrapperName();
                }
                // Allow List implementations.
                /*// Don't apply templating to modeled array types
                if (returnType.isArrayType())
                {
                    type = type.substring(0, type.length()-2);
                }*/
                name += '<' + type + '>';
            }
        }
        if (name == null && returnType != null)
        {
            name = returnType.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.getReturnType().isBooleanType())
            {
                if (this.getReturnType().isPrimitive() && (this.getLower() < 1))
                {
                    // Type is optional, should not be primitive
                    name = StringUtils.capitalize(name);
                }
                else if (!this.getReturnType().isPrimitive() && this.getLower() > 0)
                {
                    // Type is required, should not be wrapped
                    name = StringUtils.uncapitalize(name);
                }
            }
        }
        return name;
    }

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

    /**
     * @return this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED
     * @see org.andromda.metafacades.uml.OperationFacade#isMany()
     */
    protected boolean handleIsMany()
    {
        // Because of MD11.5 (their multiplicity are String), we cannot use
        // isMultiValued()
        // RJF3 True if either the operation is many or the return parameter is many
        final ParameterFacade returnParameter = this.getReturnParameter();
        // Parameter or parameter type may be null during model validation
        final boolean returnMany = returnParameter!=null && returnParameter.getType()!=null &&
           (returnParameter.getUpper() > 1 ||
            returnParameter.getUpper() == LiteralUnlimitedNatural.UNLIMITED
            || returnParameter.getType().isArrayType());
        return returnMany || this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED;
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getArguments()
     */
    @Override
    protected Collection handleGetArguments()
    {
        final Collection arguments = new ArrayList(this.metaObject.getOwnedParameters());
        CollectionUtils.filter(
            arguments,
            new Predicate()
            {
                public boolean evaluate(final Object object)
                {
                    final Parameter param = (Parameter)object;
                    return !param.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL) && !param.isException();
                }
            });
        return arguments;
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPreconditions()
     */
    @Override
    protected Collection handleGetPreconditions()
    {
        return this.handleGetConstraints(ExpressionKinds.PRE);
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getPostconditions()
     */
    @Override
    protected Collection handleGetPostconditions()
    {
        return this.handleGetConstraints(ExpressionKinds.POST);
    }

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

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#findParameter(String)
     */
    @Override
    protected ParameterFacade handleFindParameter(final String name)
    {
        return (ParameterFacade)this.shieldedElement(this.metaObject.getOwnedParameter(name, null));
    }

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

    /**
     * Get the UML lower multiplicity Not implemented for UML1.4
     * @return multiplicity lowerBound
     */
    @Override
    protected int handleGetLower()
    {
        // MD11.5 Exports multiplicity as String
        return this.metaObject.getLower();
        //return UmlUtilities.parseLowerMultiplicity(this.metaObject.getLower(),
        //    (ClassifierFacade) this.getReturnType(), "0");
            // ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)));
    }

    /**
     * Get the first UML2 ReturnResult parameter. Not implemented for UML1.4
     * @return ReturnResult parameter
     */
    @Override
    public ParameterFacade handleGetReturnParameter()
    {
        return (ParameterFacade)this.shieldedElement(this.metaObject.getReturnResult());
    }

    /**
     * Override to change private to public, since it makes no sense to have private operations in generated code
     * Allows for protected, package level visibility of operations 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.uml.OperationFacade#isOverriding()
     */
    @Override
    protected boolean handleIsOverriding()
    {
        return this.getOverriddenOperation() != null;
    }

    /**
     * @see org.andromda.metafacades.uml.OperationFacade#getOverriddenOperation()
     */
    @Override
    protected OperationFacade handleGetOverriddenOperation()
    {
        OperationFacade overriddenOperation = null;

        final String signature = this.getSignature(false);

        ClassifierFacade ancestor = this.getOwner().getSuperClass();
        while (overriddenOperation == null && ancestor != null)
        {
            for (final Iterator operationIterator = ancestor.getOperations().iterator();
                 overriddenOperation == null && operationIterator.hasNext();)
            {
                final OperationFacade ancestorOperation = operationIterator.next();
                if (signature.equals(ancestorOperation.getSignature(false)))
                {
                    overriddenOperation = ancestorOperation;
                }
            }

            ancestor = ancestor.getSuperClass();
        }

        return overriddenOperation;
    }

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

    /**
     * @return this.metaObject.isUnique()
     * @see org.andromda.metafacades.uml.OperationFacade#isUnique()
     */
    protected boolean handleIsUnique()
    {
        return this.metaObject.isUnique();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy