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