/* * This software is subject to the terms of the Eclipse Public License v1.0 * Agreement, available at the following URL: * http://www.eclipse.org/legal/epl-v10.html. * You must accept the terms of that agreement to use this software. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package mondrian.olap.fun; import mondrian.calc.*; import mondrian.calc.impl.AbstractMemberCalc; import mondrian.calc.impl.ConstantCalc; import mondrian.mdx.ResolvedFunCall; import mondrian.olap.*; import mondrian.olap.type.MemberType; import mondrian.olap.type.Type; import mondrian.resource.MondrianResource; import mondrian.rolap.RolapCube; import mondrian.rolap.RolapHierarchy; /** * Definition of the <code>ParallelPeriod</code> MDX function. * * @author jhyde * @since Mar 23, 2006 */ class ParallelPeriodFunDef extends FunDefBase { static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver( "ParallelPeriod", "ParallelPeriod([<Level>[, <Numeric Expression>[, <Member>]]])", "Returns a member from a prior period in the same relative position as a specified member.", new String[] {"fm", "fml", "fmln", "fmlnm"}, ParallelPeriodFunDef.class); public ParallelPeriodFunDef(FunDef dummyFunDef) { super(dummyFunDef); } public Type getResultType(Validator validator, Exp[] args) { if (args.length == 0) { // With no args, the default implementation cannot // guess the hierarchy, so we supply the Time // dimension. RolapHierarchy defaultTimeHierarchy = ((RolapCube) validator.getQuery().getCube()).getTimeHierarchy( getName()); return MemberType.forHierarchy(defaultTimeHierarchy); } return super.getResultType(validator, args); } public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { // Member defaults to [Time].currentmember Exp[] args = call.getArgs(); // Numeric Expression defaults to 1. final IntegerCalc lagValueCalc = (args.length >= 2) ? compiler.compileInteger(args[1]) : ConstantCalc.constantInteger(1); // If level is not specified, we compute it from // member at runtime. final LevelCalc ancestorLevelCalc = args.length >= 1 ? compiler.compileLevel(args[0]) : null; final MemberCalc memberCalc; switch (args.length) { case 3: memberCalc = compiler.compileMember(args[2]); break; case 1: final Hierarchy hierarchy = args[0].getType().getHierarchy(); if (hierarchy != null) { // For some functions, such as Levels(<string expression>), // the dimension cannot be determined at compile time. memberCalc = new HierarchyCurrentMemberFunDef.FixedCalcImpl( call, hierarchy); } else { memberCalc = null; } break; default: final RolapHierarchy timeHierarchy = ((RolapCube) compiler.getEvaluator().getCube()) .getTimeHierarchy(getName()); memberCalc = new HierarchyCurrentMemberFunDef.FixedCalcImpl( call, timeHierarchy); break; } return new AbstractMemberCalc( call, new Calc[] {memberCalc, lagValueCalc, ancestorLevelCalc}) { public Member evaluateMember(Evaluator evaluator) { Member member; int lagValue = lagValueCalc.evaluateInteger(evaluator); Level ancestorLevel; if (ancestorLevelCalc != null) { ancestorLevel = ancestorLevelCalc.evaluateLevel(evaluator); if (memberCalc == null) { member = evaluator.getContext(ancestorLevel.getHierarchy()); } else { member = memberCalc.evaluateMember(evaluator); } } else { member = memberCalc.evaluateMember(evaluator); Member parent = member.getParentMember(); if (parent == null) { // This is a root member, // so there is no parallelperiod. return member.getHierarchy().getNullMember(); } ancestorLevel = parent.getLevel(); } return parallelPeriod( member, ancestorLevel, evaluator, lagValue); } }; } Member parallelPeriod( Member member, Level ancestorLevel, Evaluator evaluator, int lagValue) { // Now do some error checking. // The ancestorLevel and the member must be from the // same hierarchy. if (member.getHierarchy() != ancestorLevel.getHierarchy()) { MondrianResource.instance().FunctionMbrAndLevelHierarchyMismatch.ex( "ParallelPeriod", ancestorLevel.getHierarchy().getUniqueName(), member.getHierarchy().getUniqueName()); } if (lagValue == Integer.MIN_VALUE) { // Bump up lagValue by one; otherwise -lagValue (used in // the getleadMember call below) is out of range because // Integer.MAX_VALUE == -(Integer.MIN_VALUE + 1) lagValue += 1; } int distance = member.getLevel().getDepth() - ancestorLevel.getDepth(); Member ancestor = FunUtil.ancestor( evaluator, member, distance, ancestorLevel); Member inLaw = evaluator.getSchemaReader() .getLeadMember(ancestor, -lagValue); return FunUtil.cousin( evaluator.getSchemaReader(), member, inLaw); } } // End ParallelPeriodFunDef.java