KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > RolapDependencyTestingEvaluator


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapDependencyTestingEvaluator.java#6 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2006-2006 Julian Hyde
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10 package mondrian.rolap;
11
12 import mondrian.olap.*;
13 import mondrian.calc.*;
14 import mondrian.calc.impl.DelegatingExpCompiler;
15 import mondrian.calc.impl.GenericCalc;
16
17 import java.util.*;
18 import java.io.StringWriter JavaDoc;
19 import java.io.PrintWriter JavaDoc;
20
21 /**
22  * Evaluator which checks dependencies of expressions.
23  *
24  * <p>For each expression evaluation, this valuator evaluates each
25  * expression more times, and makes sure that the results of the expression
26  * are independent of dimensions which the expression claims to be
27  * independent of.
28  *
29  * <p>Since it evaluates each expression twice, it also exposes function
30  * implementations which change the context of the evaluator.
31  *
32  * @author jhyde
33  * @since September, 2005
34  * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapDependencyTestingEvaluator.java#6 $
35  */

36 public class RolapDependencyTestingEvaluator extends RolapEvaluator {
37
38     /**
39      * Creates an evaluator.
40      */

41     RolapDependencyTestingEvaluator(RolapResult result, int expDeps) {
42         super(new DteRoot(result, expDeps));
43     }
44
45     /**
46      * Creates a child evaluator.
47      */

48     private RolapDependencyTestingEvaluator(
49             RolapEvaluatorRoot root,
50             RolapDependencyTestingEvaluator evaluator,
51             CellReader cellReader,
52             Member[] cloneCurrentMembers) {
53         super(root, evaluator, cellReader, cloneCurrentMembers);
54     }
55
56     public Object JavaDoc evaluate(
57             Calc calc,
58             Dimension[] independentDimensions,
59             String JavaDoc mdxString) {
60         final DteRoot dteRoot =
61                 (DteRoot) root;
62         if (dteRoot.faking) {
63             ++dteRoot.fakeCallCount;
64         } else {
65             ++dteRoot.callCount;
66         }
67         // Evaluate the call for real.
68
final Object JavaDoc result = calc.evaluate(this);
69         if (dteRoot.result.isDirty()) {
70             return result;
71         }
72
73         // Change one of the allegedly independent dimensions and evaluate
74
// again.
75
//
76
// Don't do it if the faking is disabled,
77
// or if we're already faking another dimension,
78
// or if we're filtering out nonempty cells (which makes us
79
// dependent on everything),
80
// or if the ratio of fake evals to real evals is too high (which
81
// would make us too slow).
82
if (dteRoot.disabled ||
83                 dteRoot.faking ||
84                 isNonEmpty() ||
85                 (double) dteRoot.fakeCallCount >
86                 (double) dteRoot.callCount * dteRoot.random.nextDouble() *
87                 2 * dteRoot.expDeps) {
88             return result;
89         }
90         if (independentDimensions.length == 0) {
91             return result;
92         }
93         dteRoot.faking = true;
94         ++dteRoot.fakeCount;
95         ++dteRoot.fakeCallCount;
96         final int i = dteRoot.random.nextInt(independentDimensions.length);
97         final Member saveMember = getContext(independentDimensions[i]);
98         final Member otherMember =
99                 dteRoot.chooseOtherMember(
100                         saveMember, getQuery().getSchemaReader(false));
101         setContext(otherMember);
102         final Object JavaDoc otherResult = calc.evaluate(this);
103         if (false) {
104             System.out.println(
105                     "original=" + saveMember.getUniqueName() +
106                     ", member=" + otherMember.getUniqueName() +
107                     ", originalResult=" + result + "" +
108                     ", result=" + otherResult);
109         }
110         if (!equals(otherResult, result)) {
111             final Member[] members = getMembers();
112             final StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
113             for (int j = 0; j < members.length; j++) {
114                 if (j > 0) {
115                     buf.append(", ");
116                 }
117                 buf.append(members[j].getUniqueName());
118             }
119             throw Util.newInternal(
120                     "Expression '" + mdxString +
121                     "' claims to be independent of dimension " +
122                     saveMember.getDimension() + " but is not; context is {" +
123                     buf.toString() + "}; First result: " +
124                     toString(result) + ", Second result: " +
125                     toString(otherResult));
126         }
127         // Restore context.
128
setContext(saveMember);
129         dteRoot.faking = false;
130         return result;
131     }
132
133     public RolapEvaluator _push() {
134         Member[] cloneCurrentMembers = getMembers().clone();
135         return new RolapDependencyTestingEvaluator(
136                 root,
137                 this,
138                 cellReader,
139                 cloneCurrentMembers);
140     }
141
142
143     private boolean equals(Object JavaDoc o1, Object JavaDoc o2) {
144         if (o1 == null) {
145             return o2 == null;
146         }
147         if (o2 == null) {
148             return false;
149         }
150         if (o1 instanceof Object JavaDoc[]) {
151             if (o2 instanceof Object JavaDoc[]) {
152                 Object JavaDoc[] a1 = (Object JavaDoc[]) o1;
153                 Object JavaDoc[] a2 = (Object JavaDoc[]) o2;
154                 if (a1.length == a2.length) {
155                     for (int i = 0; i < a1.length; i++) {
156                         if (!equals(a1[i], a2[i])) {
157                             return false;
158                         }
159                     }
160                     return true;
161                 }
162             }
163             return false;
164         }
165         if (o1 instanceof List) {
166             if (o2 instanceof List) {
167                 return equals(
168                         ((List) o1).toArray(),
169                         ((List) o2).toArray());
170             }
171             return false;
172         }
173         return o1.equals(o2);
174     }
175
176     private String JavaDoc toString(Object JavaDoc o) {
177         StringWriter JavaDoc sw = new StringWriter JavaDoc();
178         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
179         toString(o, pw);
180         return sw.toString();
181     }
182
183     private void toString(Object JavaDoc o, PrintWriter JavaDoc pw) {
184         if (o instanceof Object JavaDoc[]) {
185             Object JavaDoc[] a = (Object JavaDoc[]) o;
186             pw.print("{");
187             for (int i = 0; i < a.length; i++) {
188                 Object JavaDoc o1 = a[i];
189                 if (i > 0) {
190                     pw.print(", ");
191                 }
192                 toString(o1, pw);
193             }
194             pw.print("}");
195         } else if (o instanceof List) {
196             List list = (List) o;
197             toString(list.toArray(), pw);
198         } else if (o instanceof Member) {
199             Member member = (Member) o;
200             pw.print(member.getUniqueName());
201         } else {
202             pw.print(o);
203         }
204     }
205
206     /**
207      * Holds context for a tree of {@link RolapDependencyTestingEvaluator}.
208      */

209     static class DteRoot extends RolapResult.RolapResultEvaluatorRoot {
210         final int expDeps;
211         final RolapResult result;
212         int callCount;
213         int fakeCallCount;
214         int fakeCount;
215         boolean faking;
216         boolean disabled;
217         final Random random = Util.createRandom(
218                 MondrianProperties.instance().TestSeed.get());
219
220         DteRoot(RolapResult result, int expDeps) {
221             super(result);
222             this.expDeps = expDeps;
223             this.result = result;
224         }
225
226         /**
227          * Chooses another member of the same hierarchy.
228          * The member will come from all levels with the same probability.
229          * Calculated members are not included.
230          *
231          * @param save
232          * @param schemaReader
233          */

234         private Member chooseOtherMember(
235                 final Member save, SchemaReader schemaReader) {
236             final Hierarchy hierarchy = save.getHierarchy();
237             int attempt = 0;
238             while (true) {
239                 // Choose a random level.
240
final Level[] levels = hierarchy.getLevels();
241                 final int levelDepth = random.nextInt(levels.length) + 1;
242                 Member member = null;
243                 for (int i = 0; i < levelDepth; i++) {
244                     Member[] members;
245                     if (i == 0) {
246                         members = schemaReader.getLevelMembers(levels[i], false);
247                     } else {
248                         members = schemaReader.getMemberChildren(member);
249                     }
250                     if (members.length == 0) {
251                         break;
252                     }
253                     member = members[random.nextInt(members.length)];
254                 }
255                 // If the member chosen happens to be the same as the original
256
// member, try again. Give up after 100 attempts (in case the
257
// hierarchy has only one member).
258
if (member != save || ++attempt > 100) {
259                     return member;
260                 }
261             }
262         }
263     }
264
265     /**
266      * Expression which checks dependencies and list immutability.
267      */

268     private static class DteCalcImpl extends GenericCalc {
269         private final Calc calc;
270         private final Dimension[] independentDimensions;
271         private final boolean mutableList;
272         private final String JavaDoc mdxString;
273
274         DteCalcImpl(
275                 Calc calc,
276                 Dimension[] independentDimensions,
277                 boolean mutableList,
278                 String JavaDoc mdxString) {
279             super(new DummyExp(calc.getType()));
280             this.calc = calc;
281             this.independentDimensions = independentDimensions;
282             this.mutableList = mutableList;
283             this.mdxString = mdxString;
284         }
285
286         public Calc[] getCalcs() {
287             return new Calc[] {calc};
288         }
289
290         public Object JavaDoc evaluate(Evaluator evaluator) {
291             RolapDependencyTestingEvaluator dtEval =
292                     (RolapDependencyTestingEvaluator) evaluator;
293             return dtEval.evaluate(calc, independentDimensions, mdxString);
294         }
295
296         public List evaluateList(Evaluator evaluator) {
297             List list = super.evaluateList(evaluator);
298             if (!mutableList) {
299                 list = Collections.unmodifiableList(list);
300             }
301             return list;
302         }
303
304         public ExpCompiler.ResultStyle getResultStyle() {
305             return calc.getResultStyle();
306         }
307     }
308
309     /**
310      * Expression compiler which introduces dependency testing.
311      *
312      * <p>It also checks that the caller does not modify lists unless it has
313      * explicitly asked for a mutable list.
314      */

315     static class DteCompiler extends DelegatingExpCompiler {
316         DteCompiler(ExpCompiler compiler) {
317             super(compiler);
318         }
319
320         protected Calc afterCompile(Exp exp, Calc calc, boolean mutable) {
321             Dimension[] dimensions = getIndependentDimensions(calc);
322             calc = super.afterCompile(exp, calc, mutable);
323             return new DteCalcImpl(
324                     calc,
325                     dimensions,
326                     mutable,
327                     Util.unparse(exp));
328         }
329
330         /**
331          * Returns the dimensions an expression depends on.
332          */

333         private Dimension[] getIndependentDimensions(Calc calc) {
334             List<Dimension> indDimList = new ArrayList<Dimension>();
335             final Dimension[] dims =
336                 getValidator().getQuery().getCube().getDimensions();
337             for (Dimension dim : dims) {
338                 if (!calc.dependsOn(dim)) {
339                     indDimList.add(dim);
340                 }
341             }
342             return indDimList.toArray(new Dimension[indDimList.size()]);
343         }
344     }
345 }
346
347 // End RolapDependencyTestingEvaluator.java
348
Popular Tags