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) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho and others
009// All Rights Reserved.
010*/
011package mondrian.olap.fun;
012
013import mondrian.calc.*;
014import mondrian.calc.impl.*;
015import mondrian.mdx.ResolvedFunCall;
016import mondrian.olap.*;
017import mondrian.olap.type.NullType;
018import mondrian.resource.MondrianResource;
019import mondrian.rolap.RolapMember;
020
021/**
022 * Definition of the MDX <code>&lt;Member&gt : &lt;Member&gt;</code> operator,
023 * which returns the set of members between a given pair of members.
024 *
025 * @author jhyde
026 * @since 3 March, 2002
027 */
028class RangeFunDef extends FunDefBase {
029    static final RangeFunDef instance = new RangeFunDef();
030
031    private RangeFunDef() {
032        super(
033            ":",
034            "<Member> : <Member>",
035            "Infix colon operator returns the set of members between a given pair of members.",
036            "ixmm");
037    }
038
039
040    /**
041     * Returns two membercalc objects, substituting nulls with the hierarchy
042     * null member of the other expression.
043     *
044     * @param exp0 first expression
045     * @param exp1 second expression
046     *
047     * @return two member calcs
048     */
049    private MemberCalc[] compileMembers(
050        Exp exp0, Exp exp1, ExpCompiler compiler)
051    {
052        MemberCalc[] members = new MemberCalc[2];
053
054        if (exp0.getType() instanceof NullType) {
055            members[0] = null;
056        } else {
057            members[0] = compiler.compileMember(exp0);
058        }
059
060        if (exp1.getType() instanceof NullType) {
061            members[1] = null;
062        } else {
063            members[1] = compiler.compileMember(exp1);
064        }
065
066        // replace any null types with hierachy null member
067        // if both objects are null, throw exception
068
069        if (members[0] == null && members[1] == null) {
070            throw MondrianResource.instance().TwoNullsNotSupported.ex();
071        } else if (members[0] == null) {
072            Member nullMember =
073                ((RolapMember) members[1].evaluate(null)).getHierarchy()
074                .getNullMember();
075            members[0] = (MemberCalc)ConstantCalc.constantMember(nullMember);
076        } else if (members[1] == null) {
077            Member nullMember =
078                ((RolapMember) members[0].evaluate(null)).getHierarchy()
079                .getNullMember();
080            members[1] = (MemberCalc)ConstantCalc.constantMember(nullMember);
081        }
082
083        return members;
084    }
085
086    public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
087        final MemberCalc[] memberCalcs =
088            compileMembers(call.getArg(0), call.getArg(1), compiler);
089        return new AbstractListCalc(
090            call, new Calc[] {memberCalcs[0], memberCalcs[1]})
091        {
092            public TupleList evaluateList(Evaluator evaluator) {
093                final Member member0 = memberCalcs[0].evaluateMember(evaluator);
094                final Member member1 = memberCalcs[1].evaluateMember(evaluator);
095                if (member0.isNull() || member1.isNull()) {
096                    return TupleCollections.emptyList(1);
097                }
098                if (member0.getLevel() != member1.getLevel()) {
099                    throw evaluator.newEvalException(
100                        call.getFunDef(),
101                        "Members must belong to the same level");
102                }
103                return new UnaryTupleList(
104                    FunUtil.memberRange(evaluator, member0, member1));
105            }
106        };
107    }
108}
109
110// End RangeFunDef.java