001    /*
002    // $Id: //open/mondrian/src/main/mondrian/calc/impl/MemberValueCalc.java#7 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2006-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.calc.impl;
011    
012    import mondrian.calc.Calc;
013    import mondrian.calc.MemberCalc;
014    import mondrian.olap.Dimension;
015    import mondrian.olap.Evaluator;
016    import mondrian.olap.Exp;
017    import mondrian.olap.Member;
018    import mondrian.olap.type.ScalarType;
019    import mondrian.olap.type.Type;
020    
021    /**
022     * Expression which evaluates a few member expressions,
023     * sets the dimensional context to the result of those expressions,
024     * then yields the value of the current measure in the current
025     * dimensional context.
026     *
027     * <p>The evaluator's context is preserved.
028     *
029     * <p>Note that a MemberValueCalc with 0 member expressions is equivalent to a
030     * {@link mondrian.calc.impl.ValueCalc}; see also {@link mondrian.calc.impl.TupleValueCalc}.
031     *
032     * @author jhyde
033     * @version $Id: //open/mondrian/src/main/mondrian/calc/impl/MemberValueCalc.java#7 $
034     * @since Sep 27, 2005
035     */
036    public class MemberValueCalc extends GenericCalc {
037        private final MemberCalc[] memberCalcs;
038        private final Member[] savedMembers;
039    
040        public MemberValueCalc(Exp exp, MemberCalc[] memberCalcs) {
041            super(exp);
042            final Type type = exp.getType();
043            assert type instanceof ScalarType : exp;
044            this.memberCalcs = memberCalcs;
045            this.savedMembers = new Member[memberCalcs.length];
046        }
047    
048        public Object evaluate(Evaluator evaluator) {
049            Member[] members = new Member[memberCalcs.length];
050            for (int i = 0; i < memberCalcs.length; i++) {
051                MemberCalc memberCalc = memberCalcs[i];
052                final Member member = memberCalc.evaluateMember(evaluator);
053                if (member == null ||
054                        member.isNull()) {
055                    // This method needs to leave the evaluator in the same state
056                    // it found it.
057                    for (int j = 0; j < i; j++) {
058                        evaluator.setContext(savedMembers[j]);
059                    }
060                    return null;
061                }
062                savedMembers[i] = evaluator.setContext(member);
063                members[i] = member;
064            }
065            final boolean needToReturnNull =
066                evaluator.needToReturnNullForUnrelatedDimension(members);
067            if (needToReturnNull) {
068                evaluator.setContext(savedMembers);
069                return null;
070            }
071    
072            final Object result = evaluator.evaluateCurrent();
073            evaluator.setContext(savedMembers);
074            return result;
075        }
076    
077        public Calc[] getCalcs() {
078            return memberCalcs;
079        }
080    
081        public boolean dependsOn(Dimension dimension) {
082            if (super.dependsOn(dimension)) {
083                return true;
084            }
085            for (MemberCalc memberCalc : memberCalcs) {
086                // If the expression definitely includes the dimension (in this
087                // case, that means it is a member of that dimension) then we
088                // do not depend on the dimension. For example, the scalar value of
089                //   [Store].[USA]
090                // does not depend on [Store].
091                //
092                // If the dimensionality of the expression is unknown, then the
093                // expression MIGHT include the dimension, so to be safe we have to
094                // say that it depends on the given dimension. For example,
095                //   Dimensions(3).CurrentMember.Parent
096                // may depend on [Store].
097                if (memberCalc.getType().usesDimension(dimension, true)) {
098                    return false;
099                }
100            }
101            return true;
102        }
103    }
104    
105    // End MemberValueCalc.java