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.*;
013import mondrian.olap.*;
014import mondrian.olap.fun.TupleFunDef;
015import mondrian.olap.type.TupleType;
016import mondrian.olap.type.Type;
017
018/**
019 * Expression which evaluates a tuple expression,
020 * sets the dimensional context to the result of that expression,
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 * @see mondrian.calc.impl.ValueCalc
027 * @see mondrian.calc.impl.MemberValueCalc
028 *
029 * @author jhyde
030 * @since Sep 27, 2005
031 */
032public class TupleValueCalc extends GenericCalc {
033    private final TupleCalc tupleCalc;
034    private final boolean nullCheck;
035
036    /**
037     * Creates a TupleValueCalc.
038     *
039     * @param exp Expression
040     * @param tupleCalc Compiled expression to evaluate the tuple
041     * @param nullCheck Whether to check for null values due to non-joining
042     *     dimensions in a virtual cube
043     */
044    public TupleValueCalc(Exp exp, TupleCalc tupleCalc, boolean nullCheck) {
045        super(exp);
046        this.tupleCalc = tupleCalc;
047        this.nullCheck = nullCheck;
048    }
049
050    public Object evaluate(Evaluator evaluator) {
051        final Member[] members = tupleCalc.evaluateTuple(evaluator);
052        if (members == null) {
053            return null;
054        }
055
056        if (nullCheck
057            && evaluator.needToReturnNullForUnrelatedDimension(members))
058        {
059            return null;
060        }
061
062        final int savepoint = evaluator.savepoint();
063        try {
064            evaluator.setContext(members);
065            Object result = evaluator.evaluateCurrent();
066            return result;
067        } finally {
068            evaluator.restore(savepoint);
069        }
070    }
071
072    public Calc[] getCalcs() {
073        return new Calc[] {tupleCalc};
074    }
075
076    public boolean dependsOn(Hierarchy hierarchy) {
077        if (super.dependsOn(hierarchy)) {
078            return true;
079        }
080        for (Type type : ((TupleType) tupleCalc.getType()).elementTypes) {
081            // If the expression definitely includes the dimension (in this
082            // case, that means it is a member of that dimension) then we
083            // do not depend on the dimension. For example, the scalar value of
084            //   ([Store].[USA], [Gender].[F])
085            // does not depend on [Store].
086            //
087            // If the dimensionality of the expression is unknown, then the
088            // expression MIGHT include the dimension, so to be safe we have to
089            // say that it depends on the given dimension. For example,
090            //   (Dimensions(3).CurrentMember.Parent, [Gender].[F])
091            // may depend on [Store].
092            if (type.usesHierarchy(hierarchy, true)) {
093                return false;
094            }
095        }
096        return true;
097    }
098
099    /**
100     * Optimizes the scalar evaluation of a tuple. It evaluates the members
101     * of the tuple, sets the context to these members, and evaluates the
102     * scalar result in one step, without generating a tuple.<p/>
103     *
104     * This is useful when evaluating calculated members:<blockquote><code>
105     *
106     * <pre>WITH MEMBER [Measures].[Sales last quarter]
107     *   AS ' ([Measures].[Unit Sales], [Time].PreviousMember) '</pre>
108     *
109     * </code></blockquote>
110     *
111     * @return optimized expression
112     */
113    public Calc optimize() {
114        if (tupleCalc instanceof TupleFunDef.CalcImpl) {
115            TupleFunDef.CalcImpl calc = (TupleFunDef.CalcImpl) tupleCalc;
116            return MemberValueCalc.create(
117                new DummyExp(type),
118                calc.getMemberCalcs(),
119                nullCheck);
120        }
121        return this;
122    }
123}
124
125// End TupleValueCalc.java