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) 2005-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.DelegatingTupleList; 014import mondrian.mdx.UnresolvedFunCall; 015import mondrian.olap.*; 016import mondrian.resource.MondrianResource; 017import mondrian.rolap.RolapCube; 018import mondrian.rolap.RolapMember; 019 020import java.util.*; 021 022/** 023 * Abstract base class for all aggregate functions (<code>Aggregate</code>, 024 * <code>Sum</code>, <code>Avg</code>, et cetera). 025 * 026 * @author jhyde 027 * @since 2005/8/14 028 */ 029public class AbstractAggregateFunDef extends FunDefBase { 030 public AbstractAggregateFunDef(FunDef dummyFunDef) { 031 super(dummyFunDef); 032 } 033 034 protected Exp validateArg( 035 Validator validator, Exp[] args, int i, int category) 036 { 037 // If expression cache is enabled, wrap first expression (the set) 038 // in a function which will use the expression cache. 039 if (i == 0) { 040 if (MondrianProperties.instance().EnableExpCache.get()) { 041 Exp arg = args[0]; 042 if (FunUtil.worthCaching(arg)) { 043 final Exp cacheCall = 044 new UnresolvedFunCall( 045 CacheFunDef.NAME, 046 Syntax.Function, 047 new Exp[] {arg}); 048 return validator.validate(cacheCall, false); 049 } 050 } 051 } 052 return super.validateArg(validator, args, i, category); 053 } 054 055 /** 056 * Evaluates the list of members or tuples used in computing the aggregate. 057 * If the measure for aggregation has to ignore unrelated dimensions 058 * this method will push unrelated dimension members to top level member. 059 * This behaviour is driven by the ignoreUnrelatedDimensions property 060 * on a base cube usage specified in the virtual cube.Keeps track of the 061 * number of iterations that will be required to iterate over the members 062 * or tuples needed to compute the aggregate within the current context. 063 * In doing so, also determines if the cross product of all iterations 064 * across all parent evaluation contexts will exceed the limit set in the 065 * properties file. 066 * 067 * @param listCalc calculator used to evaluate the member list 068 * @param evaluator current evaluation context 069 * @return list of evaluated members or tuples 070 */ 071 protected static TupleList evaluateCurrentList( 072 ListCalc listCalc, 073 Evaluator evaluator) 074 { 075 final int savepoint = evaluator.savepoint(); 076 TupleList tuples; 077 try { 078 evaluator.setNonEmpty(false); 079 tuples = listCalc.evaluateList(evaluator); 080 } finally { 081 evaluator.restore(savepoint); 082 } 083 int currLen = tuples.size(); 084 TupleList dims; 085 try { 086 dims = processUnrelatedDimensions(tuples, evaluator); 087 } finally { 088 evaluator.restore(savepoint); 089 } 090 crossProd(evaluator, currLen); 091 return dims; 092 } 093 094 protected TupleIterable evaluateCurrentIterable( 095 IterCalc iterCalc, 096 Evaluator evaluator) 097 { 098 final int savepoint = evaluator.savepoint(); 099 int currLen = 0; 100 TupleIterable iterable; 101 try { 102 evaluator.setNonEmpty(false); 103 iterable = iterCalc.evaluateIterable(evaluator); 104 } finally { 105 evaluator.restore(savepoint); 106 } 107 crossProd(evaluator, currLen); 108 return iterable; 109 } 110 111 private static void crossProd(Evaluator evaluator, int currLen) { 112 long iterationLimit = 113 MondrianProperties.instance().IterationLimit.get(); 114 final int productLen = currLen * evaluator.getIterationLength(); 115 if (iterationLimit > 0) { 116 if (productLen > iterationLimit) { 117 throw MondrianResource.instance() 118 .IterationLimitExceeded.ex(iterationLimit); 119 } 120 } 121 evaluator.setIterationLength(currLen); 122 } 123 124 /** 125 * Pushes unrelated dimensions to the top level member from the given list 126 * of tuples if the ignoreUnrelatedDimensions property is set on the base 127 * cube usage in the virtual cube. 128 * 129 * <p>If IgnoreMeasureForNonJoiningDimension is set to true and 130 * ignoreUnrelatedDimensions on CubeUsage is set to false then if a non 131 * joining dimension exists in the aggregation list then return an empty 132 * list else return the original list. 133 * 134 * @param tuplesForAggregation is a list of members or tuples used in 135 * computing the aggregate 136 * @param evaluator Evaluator 137 * @return list of members or tuples 138 */ 139 private static TupleList processUnrelatedDimensions( 140 TupleList tuplesForAggregation, 141 Evaluator evaluator) 142 { 143 if (tuplesForAggregation.size() == 0) { 144 return tuplesForAggregation; 145 } 146 147 RolapMember measure = (RolapMember) evaluator.getMembers()[0]; 148 149 if (measure.isCalculated()) { 150 return tuplesForAggregation; 151 } 152 153 RolapCube virtualCube = (RolapCube) evaluator.getCube(); 154 RolapCube baseCube = (RolapCube) evaluator.getMeasureCube(); 155 if (virtualCube.isVirtual() && baseCube != null) { 156 if (virtualCube.shouldIgnoreUnrelatedDimensions(baseCube.getName())) 157 { 158 return ignoreUnrelatedDimensions( 159 tuplesForAggregation, baseCube); 160 } else if (MondrianProperties.instance() 161 .IgnoreMeasureForNonJoiningDimension.get()) 162 { 163 return ignoreMeasureForNonJoiningDimension( 164 tuplesForAggregation, baseCube); 165 } 166 } 167 return tuplesForAggregation; 168 } 169 170 /** 171 * If a non joining dimension exists in the aggregation list then return 172 * an empty list else return the original list. 173 * 174 * @param tuplesForAggregation is a list of members or tuples used in 175 * computing the aggregate 176 * @param baseCube 177 * @return list of members or tuples 178 */ 179 private static TupleList ignoreMeasureForNonJoiningDimension( 180 TupleList tuplesForAggregation, 181 RolapCube baseCube) 182 { 183 Set<Dimension> nonJoiningDimensions = 184 nonJoiningDimensions(baseCube, tuplesForAggregation); 185 if (nonJoiningDimensions.size() > 0) { 186 return TupleCollections.emptyList(tuplesForAggregation.getArity()); 187 } 188 return tuplesForAggregation; 189 } 190 191 /** 192 * Pushes unrelated dimensions to the top level member from the given list 193 * of tuples if the ignoreUnrelatedDimensions property is set on the base 194 * cube usage in the virtual cube. 195 * 196 * @param tuplesForAggregation is a list of members or tuples used in 197 * computing the aggregate 198 * @return list of members or tuples 199 */ 200 private static TupleList ignoreUnrelatedDimensions( 201 TupleList tuplesForAggregation, 202 RolapCube baseCube) 203 { 204 Set<Dimension> nonJoiningDimensions = 205 nonJoiningDimensions(baseCube, tuplesForAggregation); 206 final Set<List<Member>> processedTuples = 207 new LinkedHashSet<List<Member>>(tuplesForAggregation.size()); 208 for (List<Member> tuple : tuplesForAggregation) { 209 List<Member> tupleCopy = tuple; 210 for (int j = 0; j < tuple.size(); j++) { 211 final Member member = tuple.get(j); 212 if (nonJoiningDimensions.contains(member.getDimension())) { 213 if (tupleCopy == tuple) { 214 // Avoid making a copy until we have to change a tuple. 215 tupleCopy = new ArrayList<Member>(tuple); 216 } 217 final Hierarchy hierarchy = 218 member.getDimension().getHierarchy(); 219 if (hierarchy.hasAll()) { 220 tupleCopy.set(j, hierarchy.getAllMember()); 221 } else { 222 tupleCopy.set(j, hierarchy.getDefaultMember()); 223 } 224 } 225 } 226 processedTuples.add(tupleCopy); 227 } 228 return new DelegatingTupleList( 229 tuplesForAggregation.getArity(), 230 new ArrayList<List<Member>>( 231 processedTuples)); 232 } 233 234 private static Set<Dimension> nonJoiningDimensions( 235 RolapCube baseCube, 236 TupleList tuplesForAggregation) 237 { 238 List<Member> tuple = tuplesForAggregation.get(0); 239 return baseCube.nonJoiningDimensions( 240 tuple.toArray(new Member[tuple.size()])); 241 } 242} 243 244// End AbstractAggregateFunDef.java