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.olap.fun;
011
012import mondrian.calc.*;
013import mondrian.calc.impl.AbstractListCalc;
014import mondrian.calc.impl.UnaryTupleList;
015import mondrian.mdx.ResolvedFunCall;
016import mondrian.olap.*;
017import mondrian.olap.type.*;
018import mondrian.rolap.RolapCube;
019import mondrian.rolap.RolapHierarchy;
020
021import java.util.*;
022
023/**
024 * Definition of the <code>LastPeriods</code> MDX function.
025 *
026 * @author jhyde
027 * @since Mar 23, 2006
028 */
029class LastPeriodsFunDef extends FunDefBase {
030    static final ReflectiveMultiResolver Resolver =
031        new ReflectiveMultiResolver(
032            "LastPeriods",
033            "LastPeriods(<Index> [, <Member>])",
034            "Returns a set of members prior to and including a specified member.",
035            new String[] {"fxn", "fxnm"},
036            LastPeriodsFunDef.class);
037
038    public LastPeriodsFunDef(FunDef dummyFunDef) {
039        super(dummyFunDef);
040    }
041
042    public Type getResultType(Validator validator, Exp[] args) {
043        if (args.length == 1) {
044            // If Member is not specified,
045            // it is Time.CurrentMember.
046            RolapHierarchy defaultTimeHierarchy =
047                ((RolapCube) validator.getQuery().getCube()).getTimeHierarchy(
048                    getName());
049            return new SetType(MemberType.forHierarchy(defaultTimeHierarchy));
050        } else {
051            Type type = args[1].getType();
052            Type memberType =
053            TypeUtil.toMemberOrTupleType(type);
054            return new SetType(memberType);
055        }
056    }
057
058    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
059        // Member defaults to [Time].currentmember
060        Exp[] args = call.getArgs();
061        final MemberCalc memberCalc;
062        if (args.length == 1) {
063            final RolapHierarchy timeHierarchy =
064                ((RolapCube) compiler.getEvaluator().getCube())
065                    .getTimeHierarchy(getName());
066            memberCalc =
067                new HierarchyCurrentMemberFunDef.FixedCalcImpl(
068                    call, timeHierarchy);
069        } else {
070            memberCalc = compiler.compileMember(args[1]);
071        }
072
073        // Numeric Expression.
074        final IntegerCalc indexValueCalc =
075                compiler.compileInteger(args[0]);
076
077        return new AbstractListCalc(
078            call, new Calc[] {memberCalc, indexValueCalc})
079        {
080            public TupleList evaluateList(Evaluator evaluator) {
081                Member member = memberCalc.evaluateMember(evaluator);
082                int indexValue = indexValueCalc.evaluateInteger(evaluator);
083
084                return new UnaryTupleList(
085                    lastPeriods(member, evaluator, indexValue));
086            }
087        };
088    }
089
090    /**
091     * If Index is positive, returns the set of Index
092     * members ending with Member and starting with the
093     * member lagging Index - 1 from Member.
094     *
095     * <p>If Index is negative, returns the set of (- Index)
096     * members starting with Member and ending with the
097     * member leading (- Index - 1) from Member.
098     *
099     * <p>If Index is zero, the empty set is returned.
100    */
101    List<Member> lastPeriods(
102        Member member,
103        Evaluator evaluator,
104        int indexValue)
105    {
106        // empty set
107        if ((indexValue == 0) || member.isNull()) {
108            return Collections.emptyList();
109        }
110        List<Member> list = new ArrayList<Member>();
111
112        // set with just member
113        if ((indexValue == 1) || (indexValue == -1)) {
114            list.add(member);
115            return list;
116        }
117
118        // When null is found, getting the first/last
119        // member at a given level is not particularly
120        // fast.
121        Member startMember;
122        Member endMember;
123        if (indexValue > 0) {
124            startMember = evaluator.getSchemaReader()
125                .getLeadMember(member, - (indexValue - 1));
126            endMember = member;
127            if (startMember.isNull()) {
128                List<Member> members = evaluator.getSchemaReader()
129                    .getLevelMembers(member.getLevel(), false);
130                startMember = members.get(0);
131            }
132        } else {
133            startMember = member;
134            endMember = evaluator.getSchemaReader()
135                .getLeadMember(member, -(indexValue + 1));
136            if (endMember.isNull()) {
137                List<Member> members = evaluator.getSchemaReader()
138                    .getLevelMembers(member.getLevel(), false);
139                endMember = members.get(members.size() - 1);
140            }
141        }
142
143        evaluator.getSchemaReader().getMemberRange(
144            member.getLevel(),
145            startMember,
146            endMember,
147            list);
148        return list;
149    }
150}
151
152// End LastPeriodsFunDef.java