RecordElement.java

package com.mackenziehigh.autumn.lang.compiler.utils;

import com.google.common.collect.Sets;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import java.util.Collections;
import java.util.Set;

/**
 * An instance of this class represents an element in a record-type.
 *
 * @author Mackenzie High
 */
public final class RecordElement
{
    /**
     * This is a description of the record that contains the element.
     */
    public final RecordAnalyzer record;

    /**
     * This is the name of the element.
     */
    public final String name;

    /**
     * This is a cache for type() in order to improve performance.
     */
    private IVariableType memoized_type;

    /**
     * This is a cache for setter() in order to improve performance.
     */
    private SetterMethod memoized_setter;

    /**
     * This is a cache for getter() in order to improve performance.
     */
    private GetterMethod memoized_getter;

    /**
     * Sole Constructor.
     *
     * @param record is a description of the record-type.
     * @param name is the name of the element.
     */
    public RecordElement(final RecordAnalyzer record,
                         final String name)
    {
        this.record = record;
        this.name = name;
    }

    /**
     * This method determines the most specific type of the element.
     *
     * <p>
     * This will become the type of the field that actually stores the element's value.
     * </p>
     *
     * @return the type of the field.
     */
    public IVariableType type()
    {
        /**
         * If this method was already invoked, then return the memoized result.
         */
        if (memoized_type != null)
        {
            return memoized_type;
        }

        /**
         * Search for the return-type of the most specific getter.
         */
        for (GetterMethod getter : record.getters)
        {
            /**
             * If the getter is for a different element, skip it.
             */
            if (getter.name.equals(name) == false)
            {
                continue;
            }

            /**
             * If we have no idea what the type is supposed to be, use the first possible type.
             */
            memoized_type = memoized_type == null ? getter.returns : memoized_type;

            /**
             * If this getter returns a more specific type, then use its return-type.
             */
            memoized_type = getter.returns.isSubtypeOf(memoized_type) ? getter.returns : memoized_type;
        }

        /**
         * Return the return-type of the most specific getter method.
         */
        return memoized_type;
    }

    /**
     * This method determines which setter method is the most specific.
     *
     * <p>
     * This setter method is the one that bridge methods will invoke.
     * </p>
     *
     * @return a description of the primary setter method.
     */
    public SetterMethod setter()
    {
        /**
         * If this method was already invoked, then return the memoized result.
         */
        if (memoized_setter != null)
        {
            return memoized_setter;
        }

        /**
         * Search for the primary setter.
         */
        for (SetterMethod setter : record.setters)
        {
            /**
             * The primary setter must share its name with this element.
             */
            if (setter.name.equals(name) == false)
            {
                continue;
            }

            /**
             * The primary setter must be a direct member of the record.
             */
            if (setter.owner.equals(record.type) == false)
            {
                continue;
            }

            /**
             * The return-type of the primary setter must be the type of the record.
             */
            if (setter.returns.equals(record.type) == false)
            {
                continue;
            }

            /**
             * The primary setter must take the most specific type of this element as its argument.
             */
            if (type().equals(setter.parameter) == false)
            {
                continue;
            }

            /**
             * The primary setter has been found.
             */
            memoized_setter = setter;
            break;
        }

        assert memoized_setter != null;

        return memoized_setter;
    }

    /**
     * This method determines which getter method is the most specific.
     *
     * <p>
     * This setter method is the one that bridge methods will invoke.
     * </p>
     *
     * @return a description of the primary getter method.
     */
    public GetterMethod getter()
    {
        /**
         * If this method was already invoked, then return the memoized result.
         */
        if (memoized_getter != null)
        {
            return memoized_getter;
        }

        /**
         * Search for the primary setter.
         */
        for (GetterMethod getter : record.getters)
        {
            /**
             * The primary getter must share its name with this element.
             */
            if (getter.name.equals(name) == false)
            {
                continue;
            }

            /**
             * The primary getter must be a direct member of the record.
             */
            if (getter.owner.equals(record.type) == false)
            {
                continue;
            }

            /**
             * The return-type of the primary getter must be most specific type of the element.
             */
            if (type().equals(getter.returns) == false)
            {
                continue;
            }

            /**
             * The primary getter has been found.
             */
            memoized_getter = getter;
            break;
        }

        assert memoized_getter != null;

        return memoized_getter;
    }

    /**
     * This method finds the setter methods that simply invoke the primary setter method.
     *
     * @return an immutable set containing the bridge setters.
     */
    public Set<SetterMethod> bridgeSetters()
    {
        final Set<SetterMethod> bridges = Sets.newHashSet();

        for (SetterMethod setter : record.setters)
        {
            if (setter.owner.equals(record.type) && setter.name.equals(name) && !setter.returns.equals(record.type))
            {
                bridges.add(setter);
            }
        }

        return Collections.unmodifiableSet(bridges);
    }

    /**
     * This method finds the getter methods that simply invoke the primary getter method.
     *
     * @return an immutable set containing the bridge getters.
     */
    public Set<GetterMethod> bridgeGetters()
    {
        final Set<GetterMethod> bridges = Sets.newHashSet();

        for (GetterMethod getter : record.getters)
        {
            if (getter.owner.equals(record.type) && getter.name.equals(name) && !getter.returns.equals(getter().returns))
            {
                bridges.add(getter);
            }
        }

        return Collections.unmodifiableSet(bridges);
    }
}