KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > RolapResult


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapResult.java#96 $
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) 2001-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2007 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 10 August, 2001
12 */

13
14 package mondrian.rolap;
15 import mondrian.calc.Calc;
16 import mondrian.calc.ParameterSlot;
17 import mondrian.olap.*;
18 import mondrian.olap.fun.MondrianEvaluationException;
19 import mondrian.resource.MondrianResource;
20 import mondrian.rolap.agg.AggregationManager;
21 import mondrian.util.Format;
22 import mondrian.util.Bug;
23 import mondrian.util.ObjectPool;
24
25 import org.apache.log4j.Logger;
26
27 import java.util.Collections JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.ListIterator JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Locale JavaDoc;
35
36 /**
37  * A <code>RolapResult</code> is the result of running a query.
38  *
39  * @author jhyde
40  * @since 10 August, 2001
41  * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapResult.java#96 $
42  */

43 public
44 class RolapResult extends ResultBase {
45
46     private static final Logger LOGGER = Logger.getLogger(ResultBase.class);
47
48     private RolapEvaluator evaluator;
49     private final CellKey point;
50
51     private CellInfoContainer cellInfos;
52     private FastBatchingCellReader batchingReader;
53     AggregatingCellReader aggregatingReader = new AggregatingCellReader();
54     private Modulos modulos = null;
55     private final int maxEvalDepth =
56             MondrianProperties.instance().MaxEvalDepth.get();
57
58     RolapResult(Query query, boolean execute) {
59         super(query, new Axis[query.axes.length]);
60
61         this.point = CellKey.Generator.newCellKey(query.axes.length);
62         final int expDeps = MondrianProperties.instance().TestExpDependencies.get();
63         if (expDeps > 0) {
64             this.evaluator = new RolapDependencyTestingEvaluator(this, expDeps);
65         } else {
66             final RolapEvaluator.RolapEvaluatorRoot root =
67                     new RolapResultEvaluatorRoot(this);
68             this.evaluator = new RolapEvaluator(root);
69         }
70         RolapCube rcube = (RolapCube) query.getCube();
71         this.batchingReader = new FastBatchingCellReader(rcube);
72
73         this.cellInfos = (query.axes.length > 4)
74             ? new CellInfoMap(point) : new CellInfoPool(query.axes.length);
75
76
77         if (!execute) {
78             return;
79         }
80
81         boolean normalExecution = true;
82         try {
83             // This call to clear the cube's cache only has an
84
// effect if caching has been disabled, otherwise
85
// nothing happens.
86
// Clear the local cache before a query has run
87
rcube.clearCachedAggregations();
88             // Check if there are modifications to the global aggregate cache
89
rcube.checkAggregateModifications();
90
91
92             /////////////////////////////////////////////////////////////////
93
//
94
// Evaluation Algorithm
95
//
96
// There are three basic steps to the evaluation algorithm:
97
// 1) Determine all Members for each axis but do not save
98
// information (do not build the RolapAxis),
99
// 2) Save all Members for each axis (build RolapAxis).
100
// 3) Evaluate and store each Cell determined by the Members
101
// of the axes.
102
// Step 1 converges on the stable set of Members pre axis.
103
// Steps 1 and 2 make sure that the data has been loaded.
104
//
105
// More detail follows.
106
//
107
// Explicit and Implicit Members:
108
// A Member is said to be 'explicit' if it appears on one of
109
// the Axes (one of the RolapAxis Position List of Members).
110
// A Member is 'implicit' if it is in the query but does not
111
// end up on any Axes (its usage, for example, is in a function).
112
// When for a Dimension none of its Members are explicit in the
113
// query, then the default Member is used which is like putting
114
// the Member in the Slicer.
115
//
116
// Special Dimensions:
117
// There are 2 special dimensions.
118
// The first is the Time dimension. If in a schema there is
119
// no ALL Member, then Whatever happens to be the default
120
// Member is used if Time Members are not explicitly set
121
// in the query.
122
// The second is the Measures dimension. This dimension
123
// NEVER has an ALL Member. A cube's default Measure is set
124
// by convention - its simply the first Measure defined in the
125
// cube.
126
//
127
// First a RolapEvaluator is created. During its creation,
128
// it gets a Member from each Hierarchy. Each Member is the
129
// default Member of the Hierarchy. For most Hierarchies this
130
// Member is the ALL Member, but there are cases where 1)
131
// a Hierarchy does not have an ALL Member or 2) the Hierarchy
132
// has an ALL Member but that Member is not the default Member.
133
// In these cases, the default Member is still used, but its
134
// use can cause evaluation issues (seemingly strange evaluation
135
// results).
136
//
137
// Next, load all root Members for Hierarchies that have no ALL
138
// Member and load ALL Members that are not the default Member.
139
//
140
// Determine the Members of the Slicer axis (Step 1 above). Any
141
// Members found are added to the AxisMember object. If one of these
142
// Members happens to be a Measure, then the Slicer is explicitly
143
// specifying the query's Measure and this should be put into the
144
// evaluator's context (replacing the default Measure which just
145
// happens to be the first Measure defined in the cube). Other
146
// Members found in the AxisMember object are also placed into the
147
// evaluator's context since these also are explicitly specified.
148
// Also, any other Members in the AxisMember object which have the
149
// same Hierarchy as Members in the list of root Members for
150
// Hierarchies that have no ALL Member, replace those Members - they
151
// Slicer has explicitly determined which ones to use. The
152
// AxisMember object is now cleared.
153
// The Slicer does not depend upon the other Axes, but the other
154
// Axes depend upon both the Slicer and each other.
155
//
156
// The AxisMember object also checks if the number of Members
157
// exceeds the ResultLimit property throwing a
158
// TotalMembersLimitExceeded Exception if it does.
159
//
160
// For all non-Slicer axes, the Members are determined (Step 1
161
// above). If a Measure is found in the AxisMember, then an
162
// Axis is explicitly specifying a Measure.
163
// If any Members in the AxisMember object have the same Hierarchy
164
// as a Member in the set of root Members for Hierarchies that have
165
// no ALL Member, then replace those root Members with the Member
166
// from the AxisMember object. In this case, again, a Member
167
// was explicitly specified in an Axis. If this replacement
168
// occurs, then one must redo this step with the new Members.
169
//
170
// Now Step 3 above is done. First to the Slicer Axis and then
171
// to the other Axes. Here the Axes are actually generated.
172
// If a Member of an Axis is an Calculated Member (and the
173
// Calculated Member is not a Member of the Measure Hierarchy),
174
// then find the Dimension associated with the Calculated
175
// Member and remove Members with the same Dimension in the set of
176
// root Members for Hierarchies that have no ALL Member.
177
// This is done because via the Calculated Member the Member
178
// was implicitly specified in the query. If this removal occurs,
179
// then the Axes must be re-evaluated repeating Step 3.
180
//
181
/////////////////////////////////////////////////////////////////
182

183
184             // The AxisMember object is used to hold Members that are found
185
// during Step 1 when the Axes are determined.
186
final AxisMember axisMembers = new AxisMember();
187
188
189             // list of ALL Members that are not default Members
190
final List JavaDoc<Member> nonDefaultAllMembers = new ArrayList JavaDoc<Member>();
191
192             // List of Members of Hierarchies that do not have an ALL Member
193
List JavaDoc<Member[]> nonAllMembers = new ArrayList JavaDoc<Member[]>();
194
195             // List of Measures
196
final List JavaDoc<Member> measureMembers = new ArrayList JavaDoc<Member>();
197
198             // load all root Members for Hierarchies that have no ALL
199
// Member and load ALL Members that are not the default Member.
200
// Also, all Measures are are gathered.
201
loadSpecialMembers(nonDefaultAllMembers,
202                                 nonAllMembers, measureMembers);
203
204             // clear evaluation cache
205
query.clearEvalCache();
206
207             // Save, may be needed by some Expression Calc's
208
query.putEvalCache("ALL_MEMBER_LIST", nonDefaultAllMembers);
209
210
211             final List JavaDoc<Member[]> emptyNonAllMembers = Collections.emptyList();
212
213             /////////////////////////////////////////////////////////////////
214
//
215
// Determine Slicer
216
//
217
axisMembers.setSlicer(true);
218             loadMembers(emptyNonAllMembers, evaluator,
219                         query.slicerAxis, query.slicerCalc, axisMembers);
220             axisMembers.setSlicer(false);
221
222             if (! axisMembers.isEmpty()) {
223                 for (Member m : axisMembers) {
224                     evaluator.setContext(m);
225                     if (m.isMeasure()) {
226                         // A Measure was explicitly declared in the
227
// Slicer, don't need to worry about Measures
228
// for this query.
229
measureMembers.clear();
230                     }
231                 }
232                 replaceNonAllMembers(nonAllMembers, axisMembers);
233                 axisMembers.clearMembers();
234             }
235             //
236
/////////////////////////////////////////////////////////////////
237

238
239             /////////////////////////////////////////////////////////////////
240
//
241
// Determine Axes
242
//
243
boolean changed = false;
244
245             // reset to total member count
246
axisMembers.clearTotalCellCount();
247
248             for (int i = 0; i < axes.length; i++) {
249                 final QueryAxis axis = query.axes[i];
250                 final Calc calc = query.axisCalcs[i];
251                 loadMembers(emptyNonAllMembers, evaluator,
252                             axis, calc, axisMembers);
253             }
254
255             if (! axisMembers.isEmpty()) {
256                 for (Member m : axisMembers) {
257                     if (m.isMeasure()) {
258                         // A Measure was explicitly declared on an
259
// axis, don't need to worry about Measures
260
// for this query.
261
measureMembers.clear();
262                     }
263                 }
264                 changed = replaceNonAllMembers(nonAllMembers, axisMembers);
265                 axisMembers.clearMembers();
266             }
267
268 /*
269             // This code allows replacing default Measure Member
270             // with one found in the query, an implicit Measure.
271             // Fixes some problems (RolapResultTest._testNullDefaultMeasure)
272             // but causes other ones (NamedSetTest.testNamedSetUsedInCrossJoin)
273             // so it can not be used.
274             Member previous = null;
275             if (! measureMembers.isEmpty()) {
276                  MeasureVisitor visitor = new MeasureVisitor();
277                 for (int i = 0; i < axes.length; i++) {
278                     query.axes[i].accept(visitor);
279                 }
280                 println("Measures on Axis:");
281                 for (Member m : visitor.measures) {
282                     println(" m=" +m.getUniqueName());
283                 }
284                 if (! visitor.measures.isEmpty()) {
285                     Member m = visitor.measures.get(0);
286                     previous = evaluator.setContext(m);
287                     changed |= ! m.equals(previous);
288                 }
289             }
290             // move this
291             // it should be set just before calling executeBody
292             // evaluator.setContext(previous);
293 */

294
295
296             if (changed) {
297                 // only count number of members, do not collect any
298
axisMembers.countOnly(true);
299                 // reset to total member count
300
axisMembers.clearTotalCellCount();
301
302                 for (int i = 0; i < axes.length; i++) {
303                     final QueryAxis axis = query.axes[i];
304                     final Calc calc = query.axisCalcs[i];
305                     loadMembers(nonAllMembers,
306                                 (RolapEvaluator) evaluator.push(),
307                                 axis, calc, axisMembers);
308                 }
309             }
310
311             // throws exception if number of members exceeds limit
312
axisMembers.checkLimit();
313
314             //
315
/////////////////////////////////////////////////////////////////
316

317             /////////////////////////////////////////////////////////////////
318
//
319
// Execute Slicer
320
//
321
this.slicerAxis = evalExecute(nonAllMembers,
322                                     nonAllMembers.size()-1,
323                                     (RolapEvaluator) evaluator.push(),
324                                     query.slicerAxis,
325                                     query.slicerCalc);
326             // Use the context created by the slicer for the other
327
// axes. For example, "select filter([Customers], [Store
328
// Sales] > 100) on columns from Sales where
329
// ([Time].[1998])" should show customers whose 1998 (not
330
// total) purchases exceeded 100.
331

332             // Getting the Position list's size and the Position
333
// at index == 0 will, in fact, cause an Iterable-base
334
// Axis Position List to become a List-base Axis
335
// Position List (and increae memory usage), but for
336
// the slicer axis, the number of Positions is very
337
// small, so who cares.
338
switch (this.slicerAxis.getPositions().size()) {
339             case 0:
340                 throw MondrianResource.instance().EmptySlicer.ex();
341             case 1:
342                 break;
343             default:
344                 throw MondrianResource.instance().CompoundSlicer.ex();
345             }
346             //
347
/////////////////////////////////////////////////////////////////
348

349             /////////////////////////////////////////////////////////////////
350
//
351
// Execute Axes
352
//
353
boolean redo = true;
354             while (redo) {
355                 RolapEvaluator e = (RolapEvaluator) evaluator.push();
356                 redo = false;
357
358                 for (int i = 0; i < axes.length; i++) {
359                     QueryAxis axis = query.axes[i];
360                     final Calc calc = query.axisCalcs[i];
361                     Axis axisResult = evalExecute(nonAllMembers,
362                                 nonAllMembers.size()-1, e, axis, calc);
363
364                     if (! nonAllMembers.isEmpty()) {
365                         List JavaDoc<Position> pl = axisResult.getPositions();
366                         for (Position p: pl) {
367                             for (Member m: p) {
368                                 if (m.isCalculated()) {
369                                     CalculatedMeasureVisitor visitor =
370                                         new CalculatedMeasureVisitor();
371                                     m.getExpression().accept(visitor);
372                                     Dimension dimension = visitor.dimension;
373                                     redo = removeDimension(dimension, nonAllMembers);
374                                 }
375                             }
376                             // Only need to process the first Position
377
break;
378                         }
379                     }
380                     this.axes[i] = axisResult;
381                 }
382             }
383             //
384
/////////////////////////////////////////////////////////////////
385

386             /////////////////////////////////////////////////////////////////
387
//
388
// Get value for each Cell
389
//
390
executeBody(query);
391             //
392
/////////////////////////////////////////////////////////////////
393

394             // If you are very close to running out of memory due to
395
// the number of CellInfo's in cellInfos, then calling this
396
// may cause the out of memory one is trying to aviod.
397
// On the other hand, calling this can reduce the size of
398
// the ObjectPool's internal storage by half (but, of course,
399
// it will not reduce the size of the stored objects themselves).
400
// Only call this if there are lots of CellInfo.
401
if (this.cellInfos.size() > 10000) {
402                 this.cellInfos.trimToSize();
403             }
404
405         } catch (ResultLimitExceededException ex) {
406             // If one gets a ResultLimitExceededException, then
407
// don't count on anything being worth caching.
408
normalExecution = false;
409
410             // De-reference data structures that might be holding
411
// partial results but surely are taking up memory.
412
evaluator = null;
413             cellInfos = null;
414             batchingReader = null;
415             for (int i = 0; i < axes.length; i++) {
416                 axes[i] = null;
417             }
418             slicerAxis = null;
419
420             query.clearEvalCache();
421
422             throw ex;
423
424         } finally {
425             if (normalExecution) {
426                 // Push all modifications to the aggregate cache to the global
427
// cache so each thread can start using it
428
rcube.pushAggregateModificationsToGlobalCache();
429
430                 evaluator.clearExpResultCache();
431             }
432             if (LOGGER.isDebugEnabled()) {
433                 LOGGER.debug("RolapResult<init>: " + Util.printMemory());
434             }
435         }
436     }
437     protected boolean removeDimension(Dimension dimension, List JavaDoc<Member[]> nonAllMembers) {
438         boolean changed = false;
439         for (ListIterator JavaDoc<Member[]> it = nonAllMembers.listIterator();
440                 it.hasNext();) {
441             Member[] ms = it.next();
442             Dimension d = ms[0].getHierarchy().getDimension();
443             if (d.equals(dimension)) {
444                 it.remove();
445             }
446         }
447         return changed;
448     }
449
450     private static class CalculatedMeasureVisitor extends mondrian.mdx.MdxVisitorImpl {
451         Dimension dimension;
452         CalculatedMeasureVisitor() {
453         }
454         public Object JavaDoc visit(mondrian.olap.Formula formula) {
455             return null;
456         }
457         public Object JavaDoc visit(mondrian.mdx.ResolvedFunCall call) {
458             return null;
459         }
460         public Object JavaDoc visit(mondrian.olap.Id id) {
461             return null;
462         }
463         public Object JavaDoc visit(mondrian.mdx.ParameterExpr parameterExpr) {
464             return null;
465         }
466         public Object JavaDoc visit(mondrian.mdx.DimensionExpr dimensionExpr) {
467             dimension = dimensionExpr.getDimension();
468             return null;
469         }
470         public Object JavaDoc visit(mondrian.mdx.HierarchyExpr hierarchyExpr) {
471             Hierarchy hierarchy = hierarchyExpr.getHierarchy();
472             dimension = hierarchy.getDimension();
473             return null;
474         }
475         public Object JavaDoc visit(mondrian.mdx.LevelExpr levelExpr) {
476             return null;
477         }
478         public Object JavaDoc visit(mondrian.mdx.MemberExpr memberExpr) {
479             Member member = memberExpr.getMember();
480             dimension = member.getHierarchy().getDimension();
481             return null;
482         }
483         public Object JavaDoc visit(mondrian.mdx.NamedSetExpr namedSetExpr) {
484             return null;
485         }
486         public Object JavaDoc visit(mondrian.olap.Literal literal) {
487             return null;
488         }
489     }
490     private static class MeasureVisitor extends mondrian.mdx.MdxVisitorImpl {
491         List JavaDoc<Member> measures;
492         MeasureVisitor() {
493             measures = new ArrayList JavaDoc<Member>();
494         }
495         public Object JavaDoc visit(mondrian.olap.Formula formula) {
496             return null;
497         }
498         public Object JavaDoc visit(mondrian.mdx.ResolvedFunCall call) {
499             return null;
500         }
501         public Object JavaDoc visit(mondrian.olap.Id id) {
502             return null;
503         }
504         public Object JavaDoc visit(mondrian.mdx.ParameterExpr parameterExpr) {
505             return null;
506         }
507         public Object JavaDoc visit(mondrian.mdx.DimensionExpr dimensionExpr) {
508             return null;
509         }
510         public Object JavaDoc visit(mondrian.mdx.HierarchyExpr hierarchyExpr) {
511             return null;
512         }
513         public Object JavaDoc visit(mondrian.mdx.LevelExpr levelExpr) {
514             return null;
515         }
516         public Object JavaDoc visit(mondrian.mdx.MemberExpr memberExpr) {
517             Member member = memberExpr.getMember();
518             if (member.isMeasure()) {
519                 if (! measures.contains(member)) {
520                     measures.add(member);
521                 }
522             }
523             return null;
524         }
525         public Object JavaDoc visit(mondrian.mdx.NamedSetExpr namedSetExpr) {
526             return null;
527         }
528         public Object JavaDoc visit(mondrian.olap.Literal literal) {
529             return null;
530         }
531     }
532
533     protected boolean replaceNonAllMembers(List JavaDoc<Member[]> nonAllMembers,
534                                             AxisMember axisMembers) {
535
536         boolean changed = false;
537         List JavaDoc<Member> mList = new ArrayList JavaDoc<Member>();
538         for (ListIterator JavaDoc<Member[]> it = nonAllMembers.listIterator();
539                 it.hasNext();) {
540             Member[] ms = it.next();
541             Hierarchy h = ms[0].getHierarchy();
542             mList.clear();
543             for (Member m : axisMembers) {
544                 if (m.getHierarchy().equals(h)) {
545                     mList.add(m);
546                 }
547             }
548             if (! mList.isEmpty()) {
549                 changed = true;
550                 Member[] ml = mList.toArray(new Member[mList.size()]);
551                 it.set(ml);
552             }
553         }
554         return changed;
555
556     }
557
558     protected void loadMembers(List JavaDoc<Member[]> nonAllMembers,
559                     RolapEvaluator evaluator, QueryAxis axis, Calc calc,
560                     AxisMember axisMembers) {
561         int attempt = 0;
562         evaluator.setCellReader(batchingReader);
563         while (true) {
564             axisMembers.clearAxisCount();
565             evalLoad(nonAllMembers, nonAllMembers.size()-1,
566                         evaluator, axis, calc, axisMembers);
567             evaluator.clearExpResultCache();
568
569             if (!batchingReader.loadAggregations(query)) {
570                 break;
571             }
572
573             if (attempt++ > maxEvalDepth) {
574                 throw Util.newInternal(
575                         "Failed to load all aggregations after " +
576                         maxEvalDepth +
577                         "passes; there's probably a cycle");
578             }
579         }
580     }
581
582     void evalLoad(List JavaDoc<Member[]> nonAllMembers, int cnt,
583             Evaluator evaluator, QueryAxis axis, Calc calc,
584             AxisMember axisMembers) {
585         if (cnt < 0) {
586             executeAxis(evaluator.push(), axis, calc, false, axisMembers);
587         } else {
588             for (Member m : nonAllMembers.get(cnt)) {
589                 evaluator.setContext(m);
590                 evalLoad(nonAllMembers, cnt-1, evaluator, axis, calc, axisMembers);
591             }
592         }
593     }
594     Axis evalExecute(List JavaDoc<Member[]> nonAllMembers, int cnt,
595             RolapEvaluator evaluator, QueryAxis axis, Calc calc) {
596         Axis axisResult = null;
597         if (cnt < 0) {
598             evaluator.setCellReader(aggregatingReader);
599             axisResult =
600                     executeAxis(evaluator.push(), axis, calc, true, null);
601             evaluator.clearExpResultCache();
602         } else {
603             for (Member m : nonAllMembers.get(cnt)) {
604                 evaluator.setContext(m);
605                 Axis a = evalExecute(nonAllMembers, cnt-1, evaluator, axis, calc);
606                 axisResult = mergeAxes(axisResult, a);
607             }
608         }
609         return axisResult;
610     }
611
612     /**
613      * Finds all root Members 1) whose Hierarchy does not have an ALL
614      * Member, 2) whose default Member is not the ALL Member and 3)
615      * all Measures.
616      *
617      * @param nonDefaultAllMembers List of all root Members for Hierarchies
618      * whose default Member is not the ALL Member.
619      * @param nonAllMembers List of root Members for Hierarchies that have no
620      * ALL Member.
621      * @param measureMembers List all Measures
622      */

623     protected void loadSpecialMembers(List JavaDoc<Member> nonDefaultAllMembers,
624                 List JavaDoc<Member[]> nonAllMembers,
625                 List JavaDoc<Member> measureMembers) {
626         SchemaReader schemaReader = evaluator.getSchemaReader();
627         Member[] evalMembers = evaluator.getMembers();
628         for (int i = 0, j = 0; i < evalMembers.length; i++) {
629             Member em = evalMembers[i];
630             if (em.isCalculated()) {
631                 continue;
632             }
633             Hierarchy h = em.getHierarchy();
634             Dimension d = h.getDimension();
635             if (d.getDimensionType() == DimensionType.TimeDimension) {
636                 continue;
637             }
638             if (! em.isAll()) {
639                 Member[] rootMembers = schemaReader.getHierarchyRootMembers(h);
640                 if (em.isMeasure()) {
641                     for (Member mm : rootMembers) {
642                         measureMembers.add(mm);
643                     }
644                 } else {
645                     if (h.hasAll()) {
646                         boolean found = false;
647                         for (Member m : rootMembers) {
648                             if (m.isAll()) {
649                                 nonDefaultAllMembers.add(m);
650                                 found = true;
651                                 break;
652                             }
653                         }
654                         if (! found) {
655                         }
656                     } else {
657                         nonAllMembers.add(rootMembers);
658                     }
659                 }
660             }
661         }
662     }
663
664     protected Logger getLogger() {
665         return LOGGER;
666     }
667     public Cube getCube() {
668         return evaluator.getCube();
669     }
670
671     // implement Result
672
public Axis[] getAxes() {
673         return axes;
674     }
675
676     /**
677      * Get the Cell for the given Cell position.
678      *
679      * @param pos Cell position.
680      * @return the Cell associated with the Cell position.
681      */

682     public Cell getCell(int[] pos) {
683         if (pos.length != point.size()) {
684             throw Util.newError(
685                     "coordinates should have dimension " + point.size());
686         }
687
688         CellInfo ci = cellInfos.lookup(pos);
689         if (ci.value == null) {
690             ci.value = Util.nullValue;
691         }
692
693         return new RolapCell(this, pos.clone(), ci);
694     }
695
696     private Axis executeAxis(
697         Evaluator evaluator,
698         QueryAxis axis,
699         Calc axisCalc,
700         boolean construct,
701         AxisMember axisMembers)
702     {
703         Axis axisResult = null;
704         if (axis == null) {
705             // Create an axis containing one position with no members (not
706
// the same as an empty axis).
707
if (construct) {
708                 axisResult = new RolapAxis.SingleEmptyPosition();
709             }
710
711         } else {
712             evaluator.setNonEmpty(axis.isNonEmpty());
713             evaluator.setEvalAxes(true);
714             Object JavaDoc value = axisCalc.evaluate(evaluator);
715             evaluator.setNonEmpty(false);
716             if (value != null) {
717                 // List or Iterable of Member or Member[]
718
if (value instanceof List JavaDoc) {
719                     List JavaDoc<Object JavaDoc> list = (List JavaDoc) value;
720                     if (construct) {
721                         if (list.size() == 0) {
722                             // should be???
723
axisResult = new RolapAxis.NoPosition();
724                         } else if (list.get(0) instanceof Member[]) {
725                             axisResult =
726                                 new RolapAxis.MemberArrayList((List JavaDoc<Member[]>)value);
727                         } else {
728                             axisResult =
729                                 new RolapAxis.MemberList((List JavaDoc<Member>)value);
730                         }
731                     } else if (axisMembers != null) {
732                         axisMembers.merge(value);
733                     }
734                 } else {
735                     // Iterable
736
Iterable JavaDoc<Object JavaDoc> iter = (Iterable JavaDoc) value;
737                     Iterator JavaDoc it = iter.iterator();
738                     if (construct) {
739                         if (! it.hasNext()) {
740                             axisResult = new RolapAxis.NoPosition();
741                         } else if (it.next() instanceof Member[]) {
742                             axisResult = new RolapAxis.MemberArrayIterable(
743                                             (Iterable JavaDoc<Member[]>)value);
744                         } else {
745                             axisResult = new RolapAxis.MemberIterable(
746                                             (Iterable JavaDoc<Member>)value);
747                         }
748                     } else if (axisMembers != null) {
749                         axisMembers.merge(it);
750                     }
751                 }
752             }
753             evaluator.setEvalAxes(false);
754         }
755         return axisResult;
756     }
757
758     private void executeBody(Query query) {
759         try {
760             // Compute the cells several times. The first time, use a dummy
761
// evaluator which collects requests.
762
int count = 0;
763             while (true) {
764
765                 evaluator.setCellReader(this.batchingReader);
766                 executeStripe(query.axes.length - 1,
767                                 (RolapEvaluator) evaluator.push());
768                 evaluator.clearExpResultCache();
769
770                 // Retrieve the aggregations collected.
771
//
772
if (!batchingReader.loadAggregations(query)) {
773                     // We got all of the cells we needed, so the result must be
774
// correct.
775
return;
776                 }
777                 if (count++ > maxEvalDepth) {
778                     if (evaluator instanceof RolapDependencyTestingEvaluator) {
779                         // The dependency testing evaluator can trigger new
780
// requests every cycle. So let is run as normal for
781
// the first N times, then run it disabled.
782
((RolapDependencyTestingEvaluator.DteRoot) evaluator.root).disabled = true;
783                         if (count > maxEvalDepth * 2) {
784                             throw Util.newInternal("Query required more than "
785                                 + count + " iterations");
786                         }
787                     } else {
788                         throw Util.newInternal("Query required more than "
789                             + count + " iterations");
790                     }
791                 }
792
793                 cellInfos.clear();
794             }
795         } finally {
796
797         }
798     }
799
800     boolean isDirty() {
801         return batchingReader.isDirty();
802     }
803
804     private Object JavaDoc evaluateExp(Calc calc, Evaluator evaluator) {
805         int attempt = 0;
806         boolean dirty = batchingReader.isDirty();
807         while (true) {
808             RolapEvaluator ev = (RolapEvaluator) evaluator.push();
809
810             ev.setCellReader(batchingReader);
811             Object JavaDoc preliminaryValue = calc.evaluate(ev);
812             Util.discard(preliminaryValue);
813             if (!batchingReader.loadAggregations(evaluator.getQuery())) {
814                 break;
815             }
816             if (attempt++ > maxEvalDepth) {
817                 throw Util.newInternal(
818                         "Failed to load all aggregations after " +
819                         maxEvalDepth + "passes; there's probably a cycle");
820             }
821         }
822
823         // If there were pending reads when we entered, some of the other
824
// expressions may have been evaluated incorrectly. Set the reaader's
825
// 'dirty' flag so that the caller knows that it must re-evaluate them.
826
if (dirty) {
827             batchingReader.setDirty(true);
828         }
829         RolapEvaluator ev = (RolapEvaluator) evaluator.push();
830         ev.setCellReader(aggregatingReader);
831         Object JavaDoc value = calc.evaluate(ev);
832         return value;
833     }
834
835     /**
836      * An <code>AggregatingCellReader</code> reads cell values from the
837      * {@link RolapAggregationManager}.
838      */

839     private static class AggregatingCellReader implements CellReader {
840         private final RolapAggregationManager aggMan =
841             AggregationManager.instance();
842
843         // implement CellReader
844
public Object JavaDoc get(Evaluator evaluator) {
845             final RolapEvaluator rolapEvaluator = (RolapEvaluator) evaluator;
846             return aggMan.getCellFromCache(rolapEvaluator.getMembers());
847         }
848
849         public int getMissCount() {
850             return aggMan.getMissCount();
851         }
852     }
853
854     private void executeStripe(int axisOrdinal, RolapEvaluator revaluator) {
855         if (axisOrdinal < 0) {
856             Axis axis = slicerAxis;
857             List JavaDoc<Position> positions = axis.getPositions();
858             for (Position position: positions) {
859                 getQuery().checkCancelOrTimeout();
860                 revaluator.setContext(position);
861                 Object JavaDoc o;
862                 try {
863                     o = revaluator.evaluateCurrent();
864                 } catch (MondrianEvaluationException e) {
865                     o = e;
866                 }
867
868                 CellInfo ci = null;
869
870                 // Get the Cell's format string and value formatting
871
// Object.
872
try {
873                     // This code is a combination of the code found in
874
// the old RolapResult
875
// <code>getCellNoDefaultFormatString</code> method and
876
// the old RolapCell <code>getFormattedValue</code> method.
877

878                     // Create a CellInfo object for the given position
879
// integer array.
880
ci = cellInfos.create(point.getOrdinals());
881
882                     String JavaDoc cachedFormatString = null;
883                     ValueFormatter valueFormatter;
884
885                     // Determine if there is a CellFormatter registered for
886
// the current Cube's Measure's Dimension. If so,
887
// then find or create a CellFormatterValueFormatter
888
// for it. If not, then find or create a Locale based
889
// FormatValueFormatter.
890
RolapCube cube = (RolapCube) getCube();
891                     Dimension measuresDim =
892                             cube.getMeasuresHierarchy().getDimension();
893                     RolapMeasure m =
894                             (RolapMeasure) revaluator.getContext(measuresDim);
895                     CellFormatter cf = m.getFormatter();
896                     if (cf != null) {
897                         valueFormatter = cellFormatters.get(cf);
898                         if (valueFormatter == null) {
899                             valueFormatter = new CellFormatterValueFormatter(cf);
900                             cellFormatters.put(cf, valueFormatter);
901                         }
902                     } else {
903                         cachedFormatString = revaluator.getFormatString();
904                         Locale JavaDoc locale = query.getConnection().getLocale();
905                         valueFormatter = formatValueFormatters.get(locale);
906                         if (valueFormatter == null) {
907                             valueFormatter = new FormatValueFormatter(locale);
908                             formatValueFormatters.put(locale, valueFormatter);
909                         }
910                     }
911
912                     ci.formatString = cachedFormatString;
913                     ci.valueFormatter = valueFormatter;
914
915                 } catch (ResultLimitExceededException e) {
916                     // Do NOT ignore a ResultLimitExceededException!!!
917
throw e;
918
919                 } catch (MondrianEvaluationException e) {
920                     // ignore
921

922                 } catch (Throwable JavaDoc e) {
923                     Util.discard(e);
924                 }
925
926                 if (o == RolapUtil.valueNotReadyException) {
927                     continue;
928                 }
929
930                 ci.value = o;
931             }
932         } else {
933             Axis axis = axes[axisOrdinal];
934             List JavaDoc<Position> positions = axis.getPositions();
935             int i = 0;
936             for (Position position: positions) {
937                 point.setAxis(axisOrdinal, i);
938                 revaluator.setContext(position);
939                 getQuery().checkCancelOrTimeout();
940                 executeStripe(axisOrdinal - 1, revaluator);
941                 i++;
942             }
943         }
944     }
945
946     /**
947      * Converts a cell ordinal to a set of cell coordinates. Converse of
948      * {@link #getCellOrdinal}. For example, if this result is 10 x 10 x 10,
949      * then cell ordinal 537 has coordinates (5, 3, 7).
950      * <p>
951      * This method is no longer used.
952      */

953     int[] getCellPos(int cellOrdinal) {
954         if (modulos == null) {
955             makeModulos();
956         }
957         return modulos.getCellPos(cellOrdinal);
958     }
959
960     /**
961      * Converts a set of cell coordinates to a cell ordinal. Converse of
962      * {@link #getCellPos}.
963      * <p>
964      * This method can be expensive, because the ordinal is computed from the
965      * length of the axes, and therefore the axes need to be instantiated.
966      */

967     int getCellOrdinal(int[] pos) {
968         if (modulos == null) {
969             makeModulos();
970         }
971         return modulos.getCellOrdinal(pos);
972     }
973
974     /*
975      * Instantiates the calculator to convert cell coordinates to a cell ordinal
976      * and vice versa.
977      *
978      * <p>To create the calculator, any axis that is based upon an Iterable is
979      * converted into a List - thus increasing memory usage.
980      */

981     protected void makeModulos() {
982         modulos = Modulos.Generator.create(axes);
983     }
984
985     /**
986      * Called only by RolapCell.
987      *
988      * @param pos Coordinates of cell
989      * @return Evaluator whose context is the given cell
990      */

991     RolapEvaluator getCellEvaluator(int[] pos) {
992         final RolapEvaluator cellEvaluator = (RolapEvaluator) evaluator.push();
993         for (int i = 0; i < pos.length; i++) {
994             Position position = axes[i].getPositions().get(pos[i]);
995             cellEvaluator.setContext(position);
996         }
997         return cellEvaluator;
998     }
999
1000    /**
1001     * Called only by RolapCell. Use this when creating an Evaluator
1002     * (using method {@link #getCellEvaluator}) is not required.
1003     *
1004     * @param pos Coordinates of cell
1005     * @return Members which form the context of the given cell
1006     */

1007    Member[] getCellMembers(int[] pos) {
1008        Member[] members = evaluator.getMembers().clone();
1009        final Cube cube = getCube();
1010        for (int i = 0; i < pos.length; i++) {
1011            Position position = axes[i].getPositions().get(pos[i]);
1012            for (Member member: position) {
1013                RolapMember m = (RolapMember) member;
1014                int ordinal = m.getDimension().getOrdinal(cube);
1015                members[ordinal] = m;
1016            }
1017
1018        }
1019        return members;
1020    }
1021
1022    Evaluator getRootEvaluator() {
1023        return evaluator;
1024    }
1025
1026    Evaluator getEvaluator(int[] pos) {
1027        // Set up evaluator's context, so that context-dependent format
1028
// strings work properly.
1029
Evaluator cellEvaluator = evaluator.push();
1030        for (int i = -1; i < axes.length; i++) {
1031            Axis axis;
1032            int index;
1033            if (i < 0) {
1034                axis = slicerAxis;
1035                index = 0;
1036            } else {
1037                axis = axes[i];
1038                index = pos[i];
1039            }
1040            Position position = axis.getPositions().get(index);
1041            cellEvaluator.setContext(position);
1042        }
1043        return cellEvaluator;
1044    }
1045
1046
1047    /**
1048     * Counts and collects Members found of the axes.
1049     * This class does two things. First it collects all Members
1050     * found during the Member-Determination phase.
1051     * Secondly, it counts how many Members are on each axis and
1052     * forms the product, the totalCellCount which is checked against
1053     * the ResultLimit property value.
1054     */

1055    private static class AxisMember implements Iterable JavaDoc<Member> {
1056        private final List JavaDoc<Member> members;
1057        private final int limit;
1058        private boolean isSlicer;
1059        private int totalCellCount;
1060        private int axisCount;
1061        private boolean countOnly;
1062
1063        AxisMember() {
1064            this.countOnly = false;
1065            this.members = new ArrayList JavaDoc<Member>();
1066            this.totalCellCount = 1;
1067            this.axisCount = 0;
1068            // Now that the axes are evaluated, make sure that the number of
1069
// cells does not exceed the result limit.
1070
this.limit = MondrianProperties.instance().ResultLimit.get();
1071        }
1072        public Iterator JavaDoc<Member> iterator() {
1073            return members.iterator();
1074        }
1075        void setSlicer(final boolean isSlicer) {
1076            this.isSlicer = isSlicer;
1077        }
1078        boolean isEmpty() {
1079            return this.members.isEmpty();
1080        }
1081        void countOnly(boolean countOnly) {
1082            this.countOnly = countOnly;
1083        }
1084        void checkLimit() {
1085            if (this.limit > 0) {
1086                this.totalCellCount *= this.axisCount;
1087                if (this.totalCellCount > this.limit) {
1088                    throw MondrianResource.instance().
1089                        TotalMembersLimitExceeded.ex(this.totalCellCount,
1090                                                        this.limit);
1091                }
1092                this.axisCount = 0;
1093            }
1094        }
1095        void clearAxisCount() {
1096            this.axisCount = 0;
1097        }
1098        void clearTotalCellCount() {
1099            this.totalCellCount = 1;
1100        }
1101        void clearMembers() {
1102            this.members.clear();
1103            this.axisCount = 0;
1104            this.totalCellCount = 1;
1105        }
1106        List JavaDoc<Member> members() {
1107            return this.members;
1108        }
1109        void merge(Object JavaDoc value) {
1110            List JavaDoc<Object JavaDoc> list = (List JavaDoc) value;
1111            if (list.size() != 0) {
1112                if (list.get(0) instanceof Member[]) {
1113                    for (Member[] o : (List JavaDoc<Member[]>) value) {
1114                        merge(o);
1115                    }
1116                } else {
1117                    for (Member o : (List JavaDoc<Member>) value) {
1118                        merge(o);
1119                    }
1120                }
1121            }
1122        }
1123        void merge(Iterator JavaDoc it) {
1124            if (it.hasNext()) {
1125                Object JavaDoc o = it.next();
1126                if (o instanceof Member[]) {
1127                    merge((Member[]) o);
1128                    while (it.hasNext()) {
1129                        o = it.next();
1130                        merge((Member[]) o);
1131                    }
1132                } else {
1133                    merge((Member) o);
1134                    while (it.hasNext()) {
1135                        o = it.next();
1136                        merge((Member) o);
1137                    }
1138                }
1139            }
1140        }
1141        private Member getTopParent(final Member m) {
1142            Member parent = m.getParentMember();
1143            return (parent == null) ? m : getTopParent(parent);
1144        }
1145
1146        private void merge(final List JavaDoc<Member> members) {
1147            for (Member member : members) {
1148                merge(member);
1149            }
1150        }
1151        private void merge(final Member[] members) {
1152            for (Member member : members) {
1153                merge(member);
1154            }
1155        }
1156
1157        private void merge(final Member member) {
1158            this.axisCount++;
1159            if (! countOnly) {
1160                if (isSlicer) {
1161                    if (! members.contains(member)) {
1162                        members.add(member);
1163                    }
1164                } else {
1165                    if (member.isNull()) {
1166                        return;
1167                    } else if (member.isMeasure()) {
1168                        return;
1169                    } else if (member.isCalculated()) {
1170                        return;
1171                    } else if (member.isAll()) {
1172                        return;
1173                    }
1174                    Member topParent = getTopParent(member);
1175                    if (! this.members.contains(topParent)) {
1176                        this.members.add(topParent);
1177                    }
1178                }
1179            }
1180        }
1181    }
1182
1183
1184    /**
1185     * Extension to {@link RolapEvaluator.RolapEvaluatorRoot} which is capable
1186     * of evaluating named sets.<p/>
1187     *
1188     * A given set is only evaluated once each time a query is executed; the
1189     * result is added to the {@link #namedSetValues} cache on first execution
1190     * and re-used.<p/>
1191     *
1192     * Named sets are always evaluated in the context of the slicer.<p/>
1193     */

1194    protected static class RolapResultEvaluatorRoot
1195            extends RolapEvaluator.RolapEvaluatorRoot {
1196        /**
1197         * Maps the names of sets to their values. Populated on demand.
1198         */

1199        private final Map JavaDoc<String JavaDoc, Object JavaDoc> namedSetValues =
1200            new HashMap JavaDoc<String JavaDoc, Object JavaDoc>();
1201
1202        /**
1203         * Evaluator containing context resulting from evaluating the slicer.
1204         */

1205        private RolapEvaluator slicerEvaluator;
1206        private final RolapResult result;
1207        private static final Object JavaDoc Sentinel = new Object JavaDoc();
1208
1209        public RolapResultEvaluatorRoot(RolapResult result) {
1210            super(result.query);
1211            this.result = result;
1212        }
1213
1214        protected void init(Evaluator evaluator) {
1215            slicerEvaluator = (RolapEvaluator) evaluator;
1216        }
1217
1218        protected Object JavaDoc evaluateNamedSet(String JavaDoc name, Exp exp) {
1219            Object JavaDoc value = namedSetValues.get(name);
1220            if (value == null) {
1221                final RolapEvaluator.RolapEvaluatorRoot root =
1222                    slicerEvaluator.root;
1223                final Calc calc = root.getCompiled(exp, false);
1224                Object JavaDoc o = result.evaluateExp(calc, slicerEvaluator.push());
1225                List JavaDoc list;
1226                if (o instanceof List JavaDoc) {
1227                    list = (List JavaDoc) o;
1228                } else {
1229                    // Iterable
1230

1231                    // TODO:
1232
// Here, we have to convert the Iterable into a List,
1233
// materialize it, because in the class
1234
// mondrian.mdx.NamedSetExpr the Calc returned by the
1235
// 'accept' method is an AbstractListCalc (hence we must
1236
// provide a list here). It would be better if the
1237
// NamedSetExpr class knew if it needed a ListCalc or
1238
// an IterCalc.
1239
Iterable JavaDoc iter = (Iterable JavaDoc) o;
1240                    list = new ArrayList JavaDoc();
1241                    for (Object JavaDoc e: iter) {
1242                        list.add(e);
1243                    }
1244                }
1245                // Make immutable, just in case expressions are modifying the
1246
// results we give them.
1247
value = Collections.unmodifiableList(list);
1248                namedSetValues.put(name, value);
1249            }
1250            return value;
1251        }
1252
1253        protected void clearNamedSets() {
1254            namedSetValues.clear();
1255        }
1256
1257        public Object JavaDoc getParameterValue(ParameterSlot slot) {
1258            Object JavaDoc value = slot.getParameterValue();
1259            if (value != null) {
1260                return value;
1261            }
1262
1263            // Look in other places for the value. Which places we look depends
1264
// on the scope of the parameter.
1265
Parameter.Scope scope = slot.getParameter().getScope();
1266            switch (scope) {
1267            case System:
1268                // TODO: implement system params
1269

1270                // fall through
1271
case Schema:
1272                // TODO: implement schema params
1273

1274                // fall through
1275
case Connection:
1276                // if it's set in the session, return that value
1277

1278                // fall through
1279
case Statement:
1280                break;
1281
1282            default:
1283                throw Util.badValue(scope);
1284            }
1285
1286            // Not set in any accessible scope. Evaluate the default value,
1287
// then cache it.
1288
value = slot.getCachedDefaultValue();
1289            if (value != null) {
1290                if (value == Sentinel) {
1291                    throw MondrianResource.instance().
1292                        CycleDuringParameterEvaluation.ex(
1293                        slot.getParameter().getName());
1294                }
1295                return value;
1296            }
1297            // Set value to a sentinel, so we can detect cyclic evaluation.
1298
slot.setCachedDefaultValue(Sentinel);
1299            value = result.evaluateExp(
1300                slot.getDefaultValueCalc(), slicerEvaluator.push());
1301            slot.setCachedDefaultValue(value);
1302            return value;
1303        }
1304    }
1305
1306    /**
1307     * Formatter to convert values into formatted strings.
1308     *
1309     * <p>Every Cell has a value, a format string (or CellFormatter) and a
1310     * formatted value string.
1311     * There are a wide range of possible values (pick a Double, any
1312     * Double - its a value). Because there are lots of possible values,
1313     * there are also lots of possible formatted value strings. On the
1314     * other hand, there are only a very small number of format strings
1315     * and CellFormatter's. These formatters are to be cached
1316     * in a synchronized HashMaps in order to limit how many copies
1317     * need to be kept around.
1318     *
1319     * <p>
1320     * There are two implementations of the ValueFormatter interface:<ul>
1321     * <li>{@link CellFormatterValueFormatter}, which formats using a
1322     * user-registered {@link CellFormatter}; and
1323     * <li> {@link FormatValueFormatter}, which takes the {@link Locale} object.
1324     * </ul>
1325     */

1326    interface ValueFormatter {
1327        String JavaDoc format(Object JavaDoc value, String JavaDoc formatString);
1328    }
1329
1330    /**
1331     * A CellFormatterValueFormatter uses a user-defined {@link CellFormatter}
1332     * to format values.
1333     */

1334    class CellFormatterValueFormatter implements ValueFormatter {
1335        final CellFormatter cf;
1336
1337        /**
1338         * Creates a CellFormatterValueFormatter
1339         *
1340         * @param cf Cell formatter
1341         */

1342        CellFormatterValueFormatter(CellFormatter cf) {
1343            this.cf = cf;
1344        }
1345        public String JavaDoc format(Object JavaDoc value, String JavaDoc formatString) {
1346            return cf.formatCell(value);
1347        }
1348    }
1349
1350    /**
1351     * A FormatValueFormatter takes a {@link Locale}
1352     * as a parameter and uses it to get the {@link mondrian.util.Format}
1353     * to be used in formatting an Object value with a
1354     * given format string.
1355     */

1356    class FormatValueFormatter implements ValueFormatter{
1357        final Locale JavaDoc locale;
1358
1359        /**
1360         * Creates a FormatValueFormatter.
1361         *
1362         * @param locale Locale
1363         */

1364        FormatValueFormatter(Locale JavaDoc locale) {
1365            this.locale = locale;
1366        }
1367        public String JavaDoc format(Object JavaDoc value, String JavaDoc formatString) {
1368            if (value == Util.nullValue) {
1369                Format format = getFormat(formatString);
1370                return format.format(null);
1371            } else if (value instanceof Throwable JavaDoc) {
1372                return "#ERR: " + value.toString();
1373            } else if (value instanceof String JavaDoc) {
1374                return (String JavaDoc) value;
1375            } else {
1376                Format format = getFormat(formatString);
1377                return format.format(value);
1378            }
1379        }
1380        private Format getFormat(String JavaDoc formatString) {
1381            return Format.get(formatString, locale);
1382        }
1383    }
1384
1385    /*
1386     * Generate a long ordinal based upon the values of the integers
1387     * stored in the cell position array. With this mechanism, the
1388     * Cell information can be stored using a long key (rather than
1389     * the array integer of positions) thus saving memory. The trick
1390     * is to use a 'large number' per axis in order to convert from
1391     * position array to long key where the 'large number' is greater
1392     * than the number of members in the axis.
1393     * The largest 'long' is java.lang.Long.MAX_VALUE which is
1394     * 9,223,372,036,854,776,000. The product of the maximum number
1395     * of members per axis must be less than this maximum 'long'
1396     * value (otherwise one gets hashing collisions).
1397     * <p>
1398     * For a single axis, the maximum number of members is equal to
1399     * the max 'long' number, 9,223,372,036,854,776,000.
1400     * <p>
1401     * For two axes, the maximum number of members is the square root
1402     * of the max 'long' number, 9,223,372,036,854,776,000, which is
1403     * slightly bigger than 2,147,483,647 (which is the maximum integer).
1404     * <p>
1405     * For three axes, the maximum number of members per axis is the
1406     * cube root of the max 'long' which is about 2,000,000
1407     * <p>
1408     * For four axes the forth root is about 50,000.
1409     * <p>
1410     * For five or more axes, the maximum number of members per axis
1411     * based upon the root of the maximum 'long' number,
1412     * start getting too small to guarantee that it will be
1413     * smaller than the number of members on a given axis and so
1414     * we must resort to the Map-base Cell container.
1415     */

1416
1417
1418
1419    /**
1420     * Synchronized Map from Locale to ValueFormatter. It is expected that
1421     * there will be only a small number of Locale's.
1422     * Should these be a WeakHashMap?
1423     */

1424    protected static final Map JavaDoc<Locale JavaDoc, ValueFormatter>
1425            formatValueFormatters =
1426            Collections.synchronizedMap(new HashMap JavaDoc<Locale JavaDoc, ValueFormatter>());
1427
1428    /**
1429     * Synchronized Map from CellFormatter to ValueFormatter.
1430     * CellFormatter's are defined in schema files. It is expected
1431     * the there will only be a small number of CellFormatter's.
1432     * Should these be a WeakHashMap?
1433     */

1434    protected static final Map JavaDoc<CellFormatter, ValueFormatter>
1435            cellFormatters =
1436            Collections.synchronizedMap(new HashMap JavaDoc<CellFormatter, ValueFormatter>());
1437
1438    /**
1439     * A CellInfo contains all of the information that a Cell requires.
1440     * It is placed in the cellInfos map during evaluation and
1441     * serves as a constructor parameter for {@link RolapCell}.
1442     *
1443     * <p>During the evaluation stage they are mutable but after evaluation has
1444     * finished they are not changed.
1445     */

1446    static class CellInfo {
1447        Object JavaDoc value;
1448        String JavaDoc formatString;
1449        ValueFormatter valueFormatter;
1450        long key;
1451
1452        /**
1453         * Creates a CellInfo representing the position of a cell.
1454         *
1455         * @param key Ordinal representing the position of a cell
1456         */

1457        CellInfo(long key) {
1458            this(key, null, null, null);
1459        }
1460
1461        /**
1462         * Creates a CellInfo with position, value, format string and formatter
1463         * of a cell.
1464         *
1465         * @param key Ordinal representing the position of a cell
1466         * @param value Value of cell, or null if not yet known
1467         * @param formatString Format string of cell, or null
1468         * @param valueFormatter Formatter for cell, or null
1469         */

1470        CellInfo(
1471            long key,
1472            Object JavaDoc value,
1473            String JavaDoc formatString,
1474            ValueFormatter valueFormatter)
1475        {
1476            this.key = key;
1477            this.value = value;
1478            this.formatString = formatString;
1479            this.valueFormatter = valueFormatter;
1480        }
1481
1482        public int hashCode() {
1483            return (int)(key ^ (key >>> 32));
1484        }
1485        public boolean equals(Object JavaDoc o) {
1486            if (o instanceof CellInfo) {
1487                CellInfo that = (CellInfo) o;
1488                return that.key == this.key;
1489            } else {
1490                return false;
1491            }
1492        }
1493
1494        /**
1495         * Returns the formatted value of the Cell
1496         * @return formatted value of the Cell
1497         */

1498        String JavaDoc getFormatValue() {
1499            return valueFormatter.format(value, formatString);
1500        }
1501    }
1502
1503    /**
1504     * API for the creation and
1505     * lookup of {@link CellInfo} objects. There are two implementations,
1506     * one that uses a Map for storage and the other uses an ObjectPool.
1507     */

1508    interface CellInfoContainer {
1509        /**
1510         * Returns the number of CellInfo objects in this container.
1511         * @return the number of CellInfo objects.
1512         */

1513        int size();
1514        /**
1515         * Reduces the size of the internal data structures needed to
1516         * support the current entries. This should be called after
1517         * all CellInfo objects have been added to container.
1518         */

1519        void trimToSize();
1520        /**
1521         * Removes all CellInfo objects from container. Does not
1522         * change the size of the internal data structures.
1523         */

1524        void clear();
1525        /**
1526         * Creates a new CellInfo object, adds it to the container
1527         * a location <code>pos</code> and returns it.
1528         *
1529         * @param pos where to store CellInfo object.
1530         * @return the newly create CellInfo object.
1531         */

1532        CellInfo create(int[] pos);
1533        /**
1534         * Gets the CellInfo object at the location <code>pos</code>.
1535         *
1536         * @param pos where to find the CellInfo object.
1537         * @return the CellInfo found or null.
1538         */

1539        CellInfo lookup(int[] pos);
1540    }
1541
1542    /**
1543     * Implementation of {@link CellInfoContainer} which uses a {@link Map} to
1544     * store CellInfo Objects.
1545     *
1546     * <p>Note that the CellKey point instance variable is the same
1547     * Object (NOT a copy) that is used and modified during
1548     * the recursive calls to executeStripe - the
1549     * <code>create</code> method relies on this fact.
1550     */

1551    static class CellInfoMap implements CellInfoContainer {
1552        private final Map JavaDoc<CellKey, CellInfo> cellInfoMap;
1553        private final CellKey point;
1554
1555        /**
1556         * Creates a CellInfoMap
1557         *
1558         * @param point Cell position
1559         */

1560        CellInfoMap(CellKey point) {
1561            this.point = point;
1562            this.cellInfoMap = new HashMap JavaDoc<CellKey, CellInfo>();
1563        }
1564        public int size() {
1565            return this.cellInfoMap.size();
1566        }
1567        public void trimToSize() {
1568            // empty
1569
}
1570        public void clear() {
1571            this.cellInfoMap.clear();
1572        }
1573        public CellInfo create(int[] pos) {
1574            CellKey key = this.point.copy();
1575            CellInfo ci = this.cellInfoMap.get(key);
1576            if (ci == null) {
1577                ci = new CellInfo(0);
1578                this.cellInfoMap.put(key, ci);
1579            }
1580            return ci;
1581        }
1582        public CellInfo lookup(int[] pos) {
1583            CellKey key = CellKey.Generator.newCellKey(pos);
1584            return this.cellInfoMap.get(key);
1585        }
1586    }
1587
1588    /**
1589     * Implementation of {@link CellInfoContainer} which uses an
1590     * {@link ObjectPool} to store {@link CellInfo} Objects.
1591     *
1592     * <p>There is an inner interface (<code>CellKeyMaker</code>) and
1593     * implementations for 0 through 4 axes that convert the Cell
1594     * position integer array into a long.
1595     *
1596     * <p>
1597     * It should be noted that there is an alternate approach.
1598     * As the <code>executeStripe</code>
1599     * method is recursively called, at each call it is known which
1600     * axis is being iterated across and it is known whether or
1601     * not the Position object for that axis is a List or just
1602     * an Iterable. It it is a List, then one knows the real
1603     * size of the axis. If it is an Iterable, then one has to
1604     * use one of the MAX_AXIS_SIZE values. Given that this information
1605     * is available when one recursives down to the next
1606     * <code>executeStripe</code> call, the Cell ordinal, the position
1607     * integer array could converted to an <code>long</code>, could
1608     * be generated on the call stack!! Just a thought for the future.
1609     */

1610    static class CellInfoPool implements CellInfoContainer {
1611        /**
1612         * The maximum number of Members, 2,147,483,647, that can be any given
1613         * Axis when the number of Axes is 2.
1614         */

1615        protected static final long MAX_AXIS_SIZE_2 = 2147483647;
1616        /**
1617         * The maximum number of Members, 2,000,000, that can be any given
1618         * Axis when the number of Axes is 3.
1619         */

1620        protected static final long MAX_AXIS_SIZE_3 = 2000000;
1621        /**
1622         * The maximum number of Members, 50,000, that can be any given
1623         * Axis when the number of Axes is 4.
1624         */

1625        protected static final long MAX_AXIS_SIZE_4 = 50000;
1626
1627        /**
1628         * Implementations of CellKeyMaker convert the Cell
1629         * position integer array to a <code>long</code>.
1630         */

1631        interface CellKeyMaker {
1632            long generate(int[] pos);
1633        }
1634        /**
1635         * For axis of size 0.
1636         */

1637        static class Zero implements CellKeyMaker {
1638            public long generate(int[] pos) {
1639                return 0;
1640            }
1641        }
1642        /**
1643         * For axis of size 1.
1644         */

1645        static class One implements CellKeyMaker {
1646            public long generate(int[] pos) {
1647                return pos[0];
1648            }
1649        }
1650        /**
1651         * For axis of size 2.
1652         */

1653        static class Two implements CellKeyMaker {
1654            public long generate(int[] pos) {
1655                long l = pos[0];
1656                l += (MAX_AXIS_SIZE_2 * (long) pos[1]);
1657                return l;
1658            }
1659        }
1660        /**
1661         * For axis of size 3.
1662         */

1663        static class Three implements CellKeyMaker {
1664            public long generate(int[] pos) {
1665                long l = pos[0];
1666                l += (MAX_AXIS_SIZE_3 * (long) pos[1]);
1667                l += (MAX_AXIS_SIZE_3 * MAX_AXIS_SIZE_3 * (long) pos[2]);
1668                return l;
1669            }
1670        }
1671        /**
1672         * For axis of size 4.
1673         */

1674        static class Four implements CellKeyMaker {
1675            public long generate(int[] pos) {
1676                long l = pos[0];
1677                l += (MAX_AXIS_SIZE_4 * (long) pos[1]);
1678                l += (MAX_AXIS_SIZE_4 * MAX_AXIS_SIZE_4 * (long) pos[2]);
1679                l += (MAX_AXIS_SIZE_4 * MAX_AXIS_SIZE_4 * MAX_AXIS_SIZE_4 * (long) pos[3]);
1680                return l;
1681            }
1682        }
1683
1684        private final ObjectPool<CellInfo> cellInfoPool;
1685        private final CellKeyMaker cellKeyMaker;
1686
1687        CellInfoPool(int axisLength) {
1688            this.cellInfoPool = new ObjectPool<CellInfo>();
1689            switch (axisLength) {
1690            case 0:
1691                this.cellKeyMaker = new Zero();
1692                break;
1693            case 1:
1694                this.cellKeyMaker = new One();
1695                break;
1696            case 2:
1697                this.cellKeyMaker = new Two();
1698                break;
1699            case 3:
1700                this.cellKeyMaker = new Three();
1701                break;
1702            case 4:
1703                this.cellKeyMaker = new Four();
1704                break;
1705            default:
1706                throw new RuntimeException JavaDoc(
1707                    "Creating CellInfoPool with axisLength=" +axisLength);
1708            }
1709        }
1710        public int size() {
1711            return this.cellInfoPool.size();
1712        }
1713        public void trimToSize() {
1714            this.cellInfoPool.trimToSize();
1715        }
1716        public void clear() {
1717            this.cellInfoPool.clear();
1718        }
1719        public CellInfo create(int[] pos) {
1720            long key = this.cellKeyMaker.generate(pos);
1721            return this.cellInfoPool.add(new CellInfo(key));
1722        }
1723        public CellInfo lookup(int[] pos) {
1724            long key = this.cellKeyMaker.generate(pos);
1725            return this.cellInfoPool.add(new CellInfo(key));
1726        }
1727    }
1728
1729    static Axis mergeAxes(Axis axis1, Axis axis2) {
1730        if (axis1 == null) {
1731            return axis2;
1732        }
1733        List JavaDoc<Position> pl1 = axis1.getPositions();
1734        List JavaDoc<Position> pl2 = axis2.getPositions();
1735        int arrayLen = -1;
1736        if (pl1 instanceof RolapAxis.PositionListBase) {
1737            if (pl1.size() == 0) {
1738                return axis2;
1739            }
1740            arrayLen = pl1.get(0).size();
1741        }
1742        if (axis1 instanceof RolapAxis.SingleEmptyPosition) {
1743            return axis2;
1744        }
1745        if (axis1 instanceof RolapAxis.NoPosition) {
1746            return axis2;
1747        }
1748        if (pl2 instanceof RolapAxis.PositionListBase) {
1749            if (pl2.size() == 0) {
1750                return axis1;
1751            }
1752            arrayLen = pl2.get(0).size();
1753        }
1754        if (axis2 instanceof RolapAxis.SingleEmptyPosition) {
1755            return axis1;
1756        }
1757        if (axis2 instanceof RolapAxis.NoPosition) {
1758            return axis1;
1759        }
1760        if (arrayLen == -1) {
1761            // Avoid materialization of axis
1762
arrayLen = 0;
1763            for (Position p1: pl1) {
1764                for (Member m1: p1) {
1765                    arrayLen++;
1766                }
1767                break;
1768            }
1769        }
1770        if (arrayLen == 1) {
1771            // single Member per position
1772
List JavaDoc<Member> list = new ArrayList JavaDoc<Member>();
1773            for (Position p1: pl1) {
1774                for (Member m1: p1) {
1775                    list.add(m1);
1776                }
1777            }
1778            for (Position p2: pl2) {
1779                for (Member m2: p2) {
1780                    if (! list.contains(m2)) {
1781                        list.add(m2);
1782                    }
1783                }
1784            }
1785            return new RolapAxis.MemberList(list);
1786        } else {
1787            // array of Members per position
1788
List JavaDoc<Member[]> list = new ArrayList JavaDoc<Member[]>();
1789            for (Position p1: pl1) {
1790                Member[] members = new Member[arrayLen];
1791                int i = 0;
1792                for (Member m1: p1) {
1793                    members[i++] = m1;
1794                }
1795                list.add(members);
1796            }
1797            List JavaDoc<Member[]> extras = new ArrayList JavaDoc<Member[]>();
1798            for (Position p2: pl2) {
1799                int i = 0;
1800                Member[] members = new Member[arrayLen];
1801                for (Member m2: p2) {
1802                    members[i++] = m2;
1803                }
1804                Iterator JavaDoc<Member[]> it1 = list.iterator();
1805                boolean found = false;
1806                while (it1.hasNext()) {
1807                    Member[] m1 = it1.next();
1808                    if (java.util.Arrays.equals(members, m1)) {
1809                        found = true;
1810                        break;
1811                    }
1812                }
1813                if (! found) {
1814                    extras.add(members);
1815                }
1816            }
1817            list.addAll(extras);
1818            return new RolapAxis.MemberArrayList(list);
1819        }
1820    }
1821}
1822
1823// End RolapResult.java
1824
Popular Tags