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.AbstractMemberCalc; 014import mondrian.calc.impl.ConstantCalc; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017import mondrian.olap.type.MemberType; 018import mondrian.olap.type.Type; 019import mondrian.resource.MondrianResource; 020import mondrian.rolap.RolapCube; 021import mondrian.rolap.RolapHierarchy; 022 023/** 024 * Definition of the <code>ParallelPeriod</code> MDX function. 025 * 026 * @author jhyde 027 * @since Mar 23, 2006 028 */ 029class ParallelPeriodFunDef extends FunDefBase { 030 static final ReflectiveMultiResolver Resolver = 031 new ReflectiveMultiResolver( 032 "ParallelPeriod", 033 "ParallelPeriod([<Level>[, <Numeric Expression>[, <Member>]]])", 034 "Returns a member from a prior period in the same relative position as a specified member.", 035 new String[] {"fm", "fml", "fmln", "fmlnm"}, 036 ParallelPeriodFunDef.class); 037 038 public ParallelPeriodFunDef(FunDef dummyFunDef) { 039 super(dummyFunDef); 040 } 041 042 public Type getResultType(Validator validator, Exp[] args) { 043 if (args.length == 0) { 044 // With no args, the default implementation cannot 045 // guess the hierarchy, so we supply the Time 046 // dimension. 047 RolapHierarchy defaultTimeHierarchy = 048 ((RolapCube) validator.getQuery().getCube()).getTimeHierarchy( 049 getName()); 050 return MemberType.forHierarchy(defaultTimeHierarchy); 051 } 052 return super.getResultType(validator, args); 053 } 054 055 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 056 // Member defaults to [Time].currentmember 057 Exp[] args = call.getArgs(); 058 059 // Numeric Expression defaults to 1. 060 final IntegerCalc lagValueCalc = 061 (args.length >= 2) 062 ? compiler.compileInteger(args[1]) 063 : ConstantCalc.constantInteger(1); 064 065 // If level is not specified, we compute it from 066 // member at runtime. 067 final LevelCalc ancestorLevelCalc = 068 args.length >= 1 069 ? compiler.compileLevel(args[0]) 070 : null; 071 072 final MemberCalc memberCalc; 073 switch (args.length) { 074 case 3: 075 memberCalc = compiler.compileMember(args[2]); 076 break; 077 case 1: 078 final Hierarchy hierarchy = args[0].getType().getHierarchy(); 079 if (hierarchy != null) { 080 // For some functions, such as Levels(<string expression>), 081 // the dimension cannot be determined at compile time. 082 memberCalc = 083 new HierarchyCurrentMemberFunDef.FixedCalcImpl( 084 call, hierarchy); 085 } else { 086 memberCalc = null; 087 } 088 break; 089 default: 090 final RolapHierarchy timeHierarchy = 091 ((RolapCube) compiler.getEvaluator().getCube()) 092 .getTimeHierarchy(getName()); 093 memberCalc = 094 new HierarchyCurrentMemberFunDef.FixedCalcImpl( 095 call, timeHierarchy); 096 break; 097 } 098 099 return new AbstractMemberCalc( 100 call, 101 new Calc[] {memberCalc, lagValueCalc, ancestorLevelCalc}) 102 { 103 public Member evaluateMember(Evaluator evaluator) { 104 Member member; 105 int lagValue = lagValueCalc.evaluateInteger(evaluator); 106 Level ancestorLevel; 107 if (ancestorLevelCalc != null) { 108 ancestorLevel = ancestorLevelCalc.evaluateLevel(evaluator); 109 if (memberCalc == null) { 110 member = 111 evaluator.getContext(ancestorLevel.getHierarchy()); 112 } else { 113 member = memberCalc.evaluateMember(evaluator); 114 } 115 } else { 116 member = memberCalc.evaluateMember(evaluator); 117 Member parent = member.getParentMember(); 118 if (parent == null) { 119 // This is a root member, 120 // so there is no parallelperiod. 121 return member.getHierarchy().getNullMember(); 122 } 123 ancestorLevel = parent.getLevel(); 124 } 125 return parallelPeriod( 126 member, ancestorLevel, evaluator, lagValue); 127 } 128 }; 129 } 130 131 Member parallelPeriod( 132 Member member, 133 Level ancestorLevel, 134 Evaluator evaluator, 135 int lagValue) 136 { 137 // Now do some error checking. 138 // The ancestorLevel and the member must be from the 139 // same hierarchy. 140 if (member.getHierarchy() != ancestorLevel.getHierarchy()) { 141 MondrianResource.instance().FunctionMbrAndLevelHierarchyMismatch.ex( 142 "ParallelPeriod", 143 ancestorLevel.getHierarchy().getUniqueName(), 144 member.getHierarchy().getUniqueName()); 145 } 146 147 if (lagValue == Integer.MIN_VALUE) { 148 // Bump up lagValue by one; otherwise -lagValue (used in 149 // the getleadMember call below) is out of range because 150 // Integer.MAX_VALUE == -(Integer.MIN_VALUE + 1) 151 lagValue += 1; 152 } 153 154 int distance = 155 member.getLevel().getDepth() 156 - ancestorLevel.getDepth(); 157 Member ancestor = FunUtil.ancestor( 158 evaluator, member, distance, ancestorLevel); 159 Member inLaw = evaluator.getSchemaReader() 160 .getLeadMember(ancestor, -lagValue); 161 return FunUtil.cousin( 162 evaluator.getSchemaReader(), member, inLaw); 163 } 164} 165 166// End ParallelPeriodFunDef.java