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