/*
* This software is subject to the terms of the Eclipse Public License v1.0
* Agreement, available at the following URL:
* http://www.eclipse.org/legal/epl-v10.html.
* You must accept the terms of that agreement to use this software.
*
* Copyright (c) 2002-2013 Pentaho Corporation..  All rights reserved.
*/

package mondrian.calc.impl;

import mondrian.calc.Calc;
import mondrian.calc.MemberCalc;
import mondrian.olap.*;
import mondrian.olap.type.ScalarType;
import mondrian.olap.type.Type;

/**
 * Expression which evaluates a few member expressions,
 * sets the dimensional context to the result of those expressions,
 * then yields the value of the current measure in the current
 * dimensional context.
 *
 * <p>The evaluator's context is preserved.
 *
 * <p>Note that a MemberValueCalc with 0 member expressions is equivalent to a
 * {@link mondrian.calc.impl.ValueCalc}; see also {@link mondrian.calc.impl.TupleValueCalc}.
 *
 * @author jhyde
 * @since Sep 27, 2005
 */
public class MemberValueCalc extends GenericCalc {
    private final MemberCalc memberCalc;
    private final boolean nullCheck;

    /**
     * Creates a MemberArrayValueCalc.
     *
     * <p>Clients outside this package should use the
     * {@link MemberValueCalc#create(mondrian.olap.Exp,
     * mondrian.calc.MemberCalc[], boolean)}
     * factory method.
     *
     * @param exp Expression
     * @param memberCalc Compiled expression
     * @param nullCheck Whether to check for null values due to non-joining
     *     dimensions in a virtual cube
     */
    public MemberValueCalc(Exp exp, MemberCalc memberCalc, boolean nullCheck) {
        super(exp);
        this.nullCheck = nullCheck;
        final Type type = exp.getType();
        assert type instanceof ScalarType : exp;
        this.memberCalc = memberCalc;
    }

    /**
     * Creates a {@link ValueCalc}, {@link MemberValueCalc} or
     * {@link MemberArrayValueCalc}.
     *
     * @param exp Expression
     * @param memberCalcs Array of members to evaluate
     * @param nullCheck Whether to check for null values due to non-joining
     *     dimensions in a virtual cube
     * @return Compiled expression to evaluate each member expression, set
     *   evaluator context to each resulting member, then evaluate the current
     *   context
     */
    public static GenericCalc create(
        Exp exp,
        MemberCalc[] memberCalcs,
        boolean nullCheck)
    {
        switch (memberCalcs.length) {
        case 0:
            return new ValueCalc(exp);
        case 1:
            return new MemberValueCalc(exp, memberCalcs[0], nullCheck);
        default:
            return new MemberArrayValueCalc(exp, memberCalcs, nullCheck);
        }
    }

    public Object evaluate(Evaluator evaluator) {
        final int savepoint = evaluator.savepoint();
        try {
            final Member member = memberCalc.evaluateMember(evaluator);
            if (member == null
                || member.isNull())
            {
                return null;
            }
            evaluator.setContext(member);
            if (nullCheck
                && evaluator.needToReturnNullForUnrelatedDimension(
                    new Member[] {member}))
            {
                return null;
            }
            final Object result = evaluator.evaluateCurrent();
            return result;
        } finally {
            evaluator.restore(savepoint);
        }
    }

    public Calc[] getCalcs() {
        return new MemberCalc[] {memberCalc};
    }

    public boolean dependsOn(Hierarchy hierarchy) {
        if (super.dependsOn(hierarchy)) {
            return true;
        }
        // If the expression definitely includes the dimension (in this
        // case, that means it is a member of that dimension) then we
        // do not depend on the dimension. For example, the scalar value of
        //   [Store].[USA]
        // does not depend on [Store].
        //
        // If the dimensionality of the expression is unknown, then the
        // expression MIGHT include the dimension, so to be safe we have to
        // say that it depends on the given dimension. For example,
        //   Dimensions(3).CurrentMember.Parent
        // may depend on [Store].
        return !memberCalc.getType().usesHierarchy(hierarchy, true);
    }
}

// End MemberValueCalc.java
