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 and others 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.GenericCalc; 014import mondrian.mdx.ResolvedFunCall; 015import mondrian.olap.*; 016import mondrian.olap.type.TypeUtil; 017import mondrian.rolap.RolapCube; 018import mondrian.rolap.RolapMember; 019 020import java.util.*; 021 022/** 023 * Definition of the <code>ValidMeasure</code> MDX function. 024 * 025 * <p>Returns a valid measure in a virtual cube by forcing inapplicable 026 * dimensions to their top level. 027 * 028 * <p>Syntax: 029 * <blockquote><code> 030 * ValidMeasure(<Tuple>) 031 * </code></blockquote> 032 * 033 * @author kwalker, mpflug 034 */ 035public class ValidMeasureFunDef extends FunDefBase 036{ 037 static final ValidMeasureFunDef instance = new ValidMeasureFunDef(); 038 039 private ValidMeasureFunDef() { 040 super( 041 "ValidMeasure", 042 "Returns a valid measure in a virtual cube by forcing inapplicable dimensions to their top level.", 043 "fnt"); 044 } 045 046 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 047 final Calc calc; 048 final Exp arg = call.getArg(0); 049 if (TypeUtil.couldBeMember(arg.getType())) { 050 calc = compiler.compileMember(arg); 051 } else { 052 calc = compiler.compileTuple(arg); 053 } 054 return new CalcImpl(call, calc); 055 } 056 057 private static class CalcImpl 058 extends GenericCalc 059 { 060 private final Calc calc; 061 062 public CalcImpl(ResolvedFunCall call, Calc calc) { 063 super(call); 064 this.calc = calc; 065 } 066 067 public Object evaluate(Evaluator evaluator) { 068 final List<Member> memberList; 069 if (calc.isWrapperFor(MemberCalc.class)) { 070 memberList = new ArrayList<Member>(1); 071 memberList.add( 072 calc.unwrap(MemberCalc.class).evaluateMember(evaluator)); 073 } else { 074 final Member[] tupleMembers = 075 calc.unwrap((TupleCalc.class)).evaluateTuple(evaluator); 076 memberList = Arrays.asList(tupleMembers); 077 } 078 RolapCube baseCube = null; 079 RolapCube virtualCube = (RolapCube) evaluator.getCube(); 080 // find the measure in the tuple 081 int measurePosition = -1; 082 for (int i = 0; i < memberList.size(); i++) { 083 if (memberList.get(i).getDimension().isMeasures()) { 084 measurePosition = i; 085 break; 086 } 087 } 088 // problem: if measure is in two base cubes 089 baseCube = 090 getBaseCubeofMeasure( 091 evaluator, memberList.get(measurePosition), baseCube); 092 List<Dimension> vMinusBDimensions = 093 getDimensionsToForceToAllLevel( 094 virtualCube, baseCube, memberList); 095 // declare members array and fill in with all needed members 096 final List<Member> validMeasureMembers = 097 new ArrayList<Member>(memberList); 098 // start adding to validMeasureMembers at right place 099 for (Dimension vMinusBDimension : vMinusBDimensions) { 100 final Hierarchy hierarchy = vMinusBDimension.getHierarchy(); 101 if (hierarchy.hasAll()) { 102 validMeasureMembers.add(hierarchy.getAllMember()); 103 } else { 104 validMeasureMembers.add(hierarchy.getDefaultMember()); 105 } 106 } 107 // this needs to be done before validmeasuremembers are set on the 108 // context since calculated members defined on a non joining 109 // dimension might have been pulled to default member 110 List<Member> calculatedMembers = 111 getCalculatedMembersFromContext(evaluator); 112 113 evaluator.setContext(validMeasureMembers); 114 evaluator.setContext(calculatedMembers); 115 116 return evaluator.evaluateCurrent(); 117 } 118 119 private List<Member> getCalculatedMembersFromContext( 120 Evaluator evaluator) 121 { 122 Member[] currentMembers = evaluator.getMembers(); 123 List<Member> calculatedMembers = new ArrayList<Member>(); 124 for (Member currentMember : currentMembers) { 125 if (currentMember.isCalculated()) { 126 calculatedMembers.add(currentMember); 127 } 128 } 129 return calculatedMembers; 130 } 131 132 public Calc[] getCalcs() { 133 return new Calc[]{calc}; 134 } 135 136 private RolapCube getBaseCubeofMeasure( 137 Evaluator evaluator, Member member, RolapCube baseCube) 138 { 139 final Cube[] cubes = evaluator.getSchemaReader().getCubes(); 140 for (Cube cube1 : cubes) { 141 RolapCube cube = (RolapCube) cube1; 142 if (!cube.isVirtual()) { 143 for (RolapMember measure : cube.getMeasuresMembers()) { 144 if (measure.getName().equals(member.getName())) { 145 baseCube = cube; 146 } 147 } 148 } 149 if (baseCube != null) { 150 break; 151 } 152 } 153 return baseCube; 154 } 155 156 private List<Dimension> getDimensionsToForceToAllLevel( 157 RolapCube virtualCube, 158 RolapCube baseCube, 159 List<Member> memberList) 160 { 161 List<Dimension> vMinusBDimensions = new ArrayList<Dimension>(); 162 Set<Dimension> virtualCubeDims = new HashSet<Dimension>(); 163 virtualCubeDims.addAll(Arrays.asList(virtualCube.getDimensions())); 164 165 Set<Dimension> nonJoiningDims = 166 baseCube.nonJoiningDimensions(virtualCubeDims); 167 168 for (Dimension nonJoiningDim : nonJoiningDims) { 169 if (!isDimInMembersList(memberList, nonJoiningDim)) { 170 vMinusBDimensions.add(nonJoiningDim); 171 } 172 } 173 return vMinusBDimensions; 174 } 175 176 private boolean isDimInMembersList( 177 List<Member> members, 178 Dimension dimension) 179 { 180 for (Member member : members) { 181 if (member.getName().equalsIgnoreCase(dimension.getName())) { 182 return true; 183 } 184 } 185 return false; 186 } 187 188 public boolean dependsOn(Hierarchy hierarchy) { 189 // depends on all hierarchies 190 return butDepends(getCalcs(), hierarchy); 191 } 192 } 193} 194 195// End ValidMeasureFunDef.java