001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.calc.impl;
011
012import mondrian.calc.Calc;
013import mondrian.calc.MemberCalc;
014import mondrian.olap.*;
015import mondrian.olap.type.ScalarType;
016import mondrian.olap.type.Type;
017
018/**
019 * Expression which evaluates a few member expressions,
020 * sets the dimensional context to the result of those expressions,
021 * then yields the value of the current measure in the current
022 * dimensional context.
023 *
024 * <p>The evaluator's context is preserved.
025 *
026 * <p>Note that a MemberValueCalc with 0 member expressions is equivalent to a
027 * {@link mondrian.calc.impl.ValueCalc}; see also {@link mondrian.calc.impl.TupleValueCalc}.
028 *
029 * @author jhyde
030 * @since Sep 27, 2005
031 */
032public class MemberValueCalc extends GenericCalc {
033    private final MemberCalc memberCalc;
034    private final boolean nullCheck;
035
036    /**
037     * Creates a MemberArrayValueCalc.
038     *
039     * <p>Clients outside this package should use the
040     * {@link MemberValueCalc#create(mondrian.olap.Exp,
041     * mondrian.calc.MemberCalc[], boolean)}
042     * factory method.
043     *
044     * @param exp Expression
045     * @param memberCalc Compiled expression
046     * @param nullCheck Whether to check for null values due to non-joining
047     *     dimensions in a virtual cube
048     */
049    public MemberValueCalc(Exp exp, MemberCalc memberCalc, boolean nullCheck) {
050        super(exp);
051        this.nullCheck = nullCheck;
052        final Type type = exp.getType();
053        assert type instanceof ScalarType : exp;
054        this.memberCalc = memberCalc;
055    }
056
057    /**
058     * Creates a {@link ValueCalc}, {@link MemberValueCalc} or
059     * {@link MemberArrayValueCalc}.
060     *
061     * @param exp Expression
062     * @param memberCalcs Array of members to evaluate
063     * @param nullCheck Whether to check for null values due to non-joining
064     *     dimensions in a virtual cube
065     * @return Compiled expression to evaluate each member expression, set
066     *   evaluator context to each resulting member, then evaluate the current
067     *   context
068     */
069    public static GenericCalc create(
070        Exp exp,
071        MemberCalc[] memberCalcs,
072        boolean nullCheck)
073    {
074        switch (memberCalcs.length) {
075        case 0:
076            return new ValueCalc(exp);
077        case 1:
078            return new MemberValueCalc(exp, memberCalcs[0], nullCheck);
079        default:
080            return new MemberArrayValueCalc(exp, memberCalcs, nullCheck);
081        }
082    }
083
084    public Object evaluate(Evaluator evaluator) {
085        final int savepoint = evaluator.savepoint();
086        try {
087            final Member member = memberCalc.evaluateMember(evaluator);
088            if (member == null
089                || member.isNull())
090            {
091                return null;
092            }
093            evaluator.setContext(member);
094            if (nullCheck
095                && evaluator.needToReturnNullForUnrelatedDimension(
096                    new Member[] {member}))
097            {
098                return null;
099            }
100            final Object result = evaluator.evaluateCurrent();
101            return result;
102        } finally {
103            evaluator.restore(savepoint);
104        }
105    }
106
107    public Calc[] getCalcs() {
108        return new MemberCalc[] {memberCalc};
109    }
110
111    public boolean dependsOn(Hierarchy hierarchy) {
112        if (super.dependsOn(hierarchy)) {
113            return true;
114        }
115        // If the expression definitely includes the dimension (in this
116        // case, that means it is a member of that dimension) then we
117        // do not depend on the dimension. For example, the scalar value of
118        //   [Store].[USA]
119        // does not depend on [Store].
120        //
121        // If the dimensionality of the expression is unknown, then the
122        // expression MIGHT include the dimension, so to be safe we have to
123        // say that it depends on the given dimension. For example,
124        //   Dimensions(3).CurrentMember.Parent
125        // may depend on [Store].
126        return !memberCalc.getType().usesHierarchy(hierarchy, true);
127    }
128}
129
130// End MemberValueCalc.java