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