001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/AbstractAggregateFunDef.java#19 $ 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) 2005-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.calc.*; 013 import mondrian.olap.*; 014 import mondrian.resource.MondrianResource; 015 import mondrian.mdx.UnresolvedFunCall; 016 import mondrian.rolap.RolapMember; 017 import mondrian.rolap.RolapCube; 018 import mondrian.rolap.RolapStoredMeasure; 019 020 import 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 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/AbstractAggregateFunDef.java#19 $ 029 */ 030 public class AbstractAggregateFunDef extends FunDefBase { 031 public AbstractAggregateFunDef(FunDef dummyFunDef) { 032 super(dummyFunDef); 033 } 034 035 protected Exp validateArg( 036 Validator validator, Exp[] args, int i, int category) { 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 final Exp cacheCall = new UnresolvedFunCall( 043 CacheFunDef.NAME, 044 Syntax.Function, 045 new Exp[] {arg}); 046 return validator.validate(cacheCall, false); 047 } 048 } 049 return super.validateArg(validator, args, i, category); 050 } 051 052 /** 053 * Evaluates the list of members or tuples used in computing the aggregate. 054 * If the measure for aggregation has to ignore unrelated dimensions 055 * this method will push unrelated dimension members to top level member. 056 * This behaviour is driven by the ignoreUnrelatedDimensions property 057 * on a base cube usage specified in the virtual cube.Keeps track of the 058 * number of iterations that will be required to iterate over the members 059 * or tuples needed to compute the aggregate within the current context. 060 * In doing so, also determines if the cross product of all iterations 061 * across all parent evaluation contexts will exceed the limit set in the 062 * properties file. 063 * 064 * @param listCalc calculator used to evaluate the member list 065 * @param evaluator current evalutor 066 * @return list of evaluated members or tuples 067 */ 068 protected static List evaluateCurrentList( 069 ListCalc listCalc, 070 Evaluator evaluator) 071 { 072 List tuples = listCalc.evaluateList(evaluator); 073 074 int currLen = tuples.size(); 075 crossProd(evaluator, currLen); 076 077 return processUnrelatedDimensions(tuples, evaluator); 078 } 079 080 protected Iterable evaluateCurrentIterable( 081 IterCalc iterCalc, 082 Evaluator evaluator) 083 { 084 Iterable iter = iterCalc.evaluateIterable(evaluator); 085 086 int currLen = 0; 087 crossProd(evaluator, currLen); 088 089 return iter; 090 } 091 092 private static void crossProd(Evaluator evaluator, int currLen) { 093 long iterationLimit = 094 MondrianProperties.instance().IterationLimit.get(); 095 if (iterationLimit > 0) { 096 int productLen = currLen; 097 Evaluator parent = evaluator.getParent(); 098 while (parent != null) { 099 productLen *= parent.getIterationLength(); 100 parent = parent.getParent(); 101 } 102 if (productLen > iterationLimit) { 103 throw MondrianResource.instance(). 104 IterationLimitExceeded.ex(iterationLimit); 105 } 106 } 107 evaluator.setIterationLength(currLen); 108 } 109 110 /** 111 * Pushes unrelated dimensions to the top level member from the given list 112 * of tuples if the ignoreUnrelatedDimensions property is set on the base 113 * cube usage in the virtual cube. 114 * 115 * <p>If IgnoreMeasureForNonJoiningDimension is set to true and 116 * ignoreUnrelatedDimensions on CubeUsage is set to false then if a non 117 * joining dimension exists in the aggregation list then return an empty 118 * list else return the original list. 119 * 120 * @param tuplesForAggregation is a list of members or tuples used in 121 * computing the aggregate 122 * @param evaluator 123 * @return list of members or tuples 124 */ 125 private static List processUnrelatedDimensions( 126 List tuplesForAggregation, 127 Evaluator evaluator) 128 { 129 130 if (tuplesForAggregation.size() == 0) { 131 return tuplesForAggregation; 132 } 133 134 RolapMember measure = (RolapMember) evaluator.getMembers()[0]; 135 136 if (measure.isCalculated()) { 137 return tuplesForAggregation; 138 } 139 140 RolapCube virtualCube = (RolapCube) evaluator.getCube(); 141 RolapCube baseCube = (RolapCube) evaluator.getMeasureCube(); 142 if (virtualCube.isVirtual() && baseCube != null) { 143 if (virtualCube.shouldIgnoreUnrelatedDimensions(baseCube.getName())) { 144 return ignoreUnrelatedDimensions(tuplesForAggregation, baseCube); 145 } else if (MondrianProperties.instance() 146 .IgnoreMeasureForNonJoiningDimension.get()) { 147 return ignoreMeasureForNonJoiningDimension( 148 tuplesForAggregation, baseCube); 149 } 150 } 151 return tuplesForAggregation; 152 } 153 154 /** 155 * If a non joining dimension exists in the aggregation list then return 156 * an empty list else return the original list. 157 158 * @param tuplesForAggregation is a list of members or tuples used in 159 * computing the aggregate 160 * @param baseCube 161 * @return list of members or tuples 162 */ 163 private static List ignoreMeasureForNonJoiningDimension( 164 List tuplesForAggregation, 165 RolapCube baseCube) 166 { 167 Set<Dimension> nonJoiningDimensions = 168 nonJoiningDimensions(baseCube, tuplesForAggregation); 169 if (nonJoiningDimensions.size() > 0) { 170 return new ArrayList(); 171 } 172 return tuplesForAggregation; 173 } 174 175 /** 176 * Pushes unrelated dimensions to the top level member from the given list 177 * of tuples if the ignoreUnrelatedDimensions property is set on the base 178 * cube usage in the virtual cube. 179 * 180 * @param tuplesForAggregation is a list of members or tuples used in 181 * computing the aggregate 182 * @return list of members or tuples 183 */ 184 private static List ignoreUnrelatedDimensions( 185 List tuplesForAggregation, 186 RolapCube baseCube) 187 { 188 Set<Dimension> nonJoiningDimensions = 189 nonJoiningDimensions(baseCube, tuplesForAggregation); 190 Set processedTuples = new LinkedHashSet(tuplesForAggregation.size()); 191 for (int i = 0; i < tuplesForAggregation.size(); i++) { 192 Member[] tuples = copy(tupleAsArray(tuplesForAggregation.get(i))); 193 for (int j = 0; j < tuples.length; j++) { 194 if (nonJoiningDimensions.contains( 195 tuples[j].getDimension())) { 196 final Hierarchy hierarchy = 197 tuples[j].getDimension().getHierarchy(); 198 if (hierarchy.hasAll()) { 199 tuples[j] = hierarchy.getAllMember(); 200 } else { 201 tuples[j] = hierarchy.getDefaultMember(); 202 } 203 } 204 } 205 if (tuplesForAggregation.get(i) instanceof Member[]) { 206 processedTuples.add(new MemberArray(tuples)); 207 } else { 208 processedTuples.add(tuples[0]); 209 } 210 211 } 212 return tuplesAsList(processedTuples); 213 } 214 215 private static Set<Dimension> nonJoiningDimensions( 216 RolapCube baseCube, 217 List tuplesForAggregation) 218 { 219 Member[] tuple = tupleAsArray(tuplesForAggregation.get(0)); 220 return baseCube.nonJoiningDimensions(tuple); 221 } 222 223 private static List tuplesAsList(Set tuples) { 224 List results = new ArrayList(tuples.size()); 225 for (Object tuple : tuples) { 226 if (tuple instanceof MemberArray) { 227 results.add(((MemberArray) tuple).memberArray); 228 } else { 229 results.add(tuple); 230 } 231 } 232 return results; 233 } 234 235 private static Member[] copy(Member[] members) { 236 Member[] result = new Member[members.length]; 237 System.arraycopy(members, 0, result, 0, members.length); 238 return result; 239 } 240 241 private static Member[] tupleAsArray(Object tuple) { 242 Member[] result; 243 if (tuple instanceof Member[]) { 244 result = ((Member[]) tuple); 245 } else { 246 result = new Member[]{((Member) tuple)}; 247 } 248 return result; 249 } 250 251 private static class MemberArray { 252 private Object[] memberArray; 253 254 public MemberArray(Object[] memberArray) { 255 this.memberArray = memberArray; 256 } 257 258 public int hashCode() { 259 return Arrays.hashCode(memberArray); 260 } 261 262 public boolean equals(Object obj) { 263 return Arrays.deepEquals( 264 memberArray, 265 ((MemberArray) obj).memberArray); 266 } 267 } 268 } 269 270 // End AbstractAggregateFunDef.java