KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > agg > Segment


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/agg/Segment.java#46 $
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) 2002-2002 Kana Software, Inc.
7 // Copyright (C) 2002-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, 21 March, 2002
12 */

13 package mondrian.rolap.agg;
14
15 import mondrian.olap.*;
16 import mondrian.rolap.*;
17
18 import java.sql.*;
19 import java.util.*;
20 import java.io.PrintWriter JavaDoc;
21
22 /**
23  * A <code>Segment</code> is a collection of cell values parameterized by
24  * a measure, and a set of (column, value) pairs. An example of a segment is</p>
25  *
26  * <blockquote>
27  * <p>(Unit sales, Gender = 'F', State in {'CA','OR'}, Marital Status = <i>
28  * anything</i>)</p>
29  * </blockquote>
30  *
31  * <p>All segments over the same set of columns belong to an Aggregation, in this
32  * case</p>
33  *
34  * <blockquote>
35  * <p>('Sales' Star, Gender, State, Marital Status)</p>
36  * </blockquote>
37  *
38  * <p>Note that different measures (in the same Star) occupy the same Aggregation.
39  * Aggregations belong to the AggregationManager, a singleton.</p>
40  * <p>Segments are pinned during the evaluation of a single MDX query. The query
41  * evaluates the expressions twice. The first pass, it finds which cell values it
42  * needs, pins the segments containing the ones which are already present (one
43  * pin-count for each cell value used), and builds a {@link CellRequest
44  * cell request} for those which are not present. It executes
45  * the cell request to bring the required cell values into the cache, again,
46  * pinned. Then it evalutes the query a second time, knowing that all cell values
47  * are available. Finally, it releases the pins.</p>
48  *
49  * <p>A Segment may have a list of excluded {@link Region} objects. These are
50  * caused by cache flushing. Usually a segment is a hypercube: it is defined by
51  * a set of values on each of its axes. But after a cache flush request, a
52  * segment may have a rectangular 'hole', and therefore not be a hypercube
53  * anymore.
54  *
55  * <p>For example, the segment defined by {CA, OR, WA} * {F, M} is a
56  * 2-dimensional hyper-rectangle with 6 cells. After flushing {CA, OR, TX} *
57  * {F}, the result is 4 cells:
58  *
59  * <pre>
60  * F M
61  * CA out in
62  * OR out in
63  * WA in in
64  * </pre>
65  *
66  * defined by the original segment minus the region ({CA, OR} * {F}).
67  *
68  * @author jhyde
69  * @since 21 March, 2002
70  * @version $Id: //open/mondrian/src/main/mondrian/rolap/agg/Segment.java#46 $
71  */

72 class Segment {
73     private static int nextId = 0; // generator for "id"
74

75     final int id; // for debug
76
private String JavaDoc desc;
77     final Aggregation aggregation;
78     final RolapStar.Measure measure;
79
80     final Aggregation.Axis[] axes;
81     private SegmentDataset data;
82     private final CellKey cellKey; // workspace
83

84     /** State of the segment (loading, ready, etc.). */
85     private State state;
86
87     /**
88      * List of regions to ignore when reading this segment. This list is
89      * populated when a region is flushed. The cells for these regions may be
90      * physically in the segment, because trimming segments can be expensive,
91      * but should still be ignored.
92      */

93     private final List<Region> excludedRegions;
94
95     /**
96      * Creates a <code>Segment</code>; it's not loaded yet.
97      *
98      * @param aggregation The aggregation this <code>Segment</code> belongs to
99      * @param measure Measure whose values this Segment contains
100      * @param axes List of axes; each is a constraint plus a list of values
101      * @param excludedRegions List of regions which are not in this segment.
102      */

103     Segment(
104         Aggregation aggregation,
105         RolapStar.Measure measure,
106         Aggregation.Axis[] axes,
107         List<Region> excludedRegions)
108     {
109         this.id = nextId++;
110         this.aggregation = aggregation;
111         this.measure = measure;
112         this.axes = axes;
113         this.cellKey = CellKey.Generator.newCellKey(axes.length);
114         this.state = State.Loading;
115         this.excludedRegions = excludedRegions;
116         for (Region region : excludedRegions) {
117             assert region.getPredicates().size() == axes.length;
118         }
119     }
120
121     /**
122      * Sets the data, and notifies any threads which are blocked in
123      * {@link #waitUntilLoaded}.
124      */

125     synchronized void setData(
126             SegmentDataset data,
127             RolapAggregationManager.PinSet pinnedSegments) {
128         Util.assertTrue(this.data == null);
129         Util.assertTrue(this.state == State.Loading);
130
131         this.data = data;
132         this.state = State.Ready;
133         notifyAll();
134     }
135
136     /**
137      * If this segment is still loading, signals that it failed to load, and
138      * notifies any threads which are blocked in {@link #waitUntilLoaded}.
139      */

140     synchronized void setFailed() {
141         switch (state) {
142         case Loading:
143             Util.assertTrue(this.data == null);
144             this.state = State.Failed;
145             notifyAll();
146             break;
147         case Ready:
148             // The segment loaded just fine.
149
break;
150         default:
151             throw Util.badValue(state);
152         }
153     }
154
155     public boolean isReady() {
156         return (state == State.Ready);
157     }
158
159     private void makeDescription(StringBuilder JavaDoc buf, boolean values) {
160         final String JavaDoc sep = Util.nl + " ";
161         buf.append("Segment #");
162         buf.append(id);
163         buf.append(" {");
164         buf.append(sep);
165         buf.append("measure=");
166         buf.append(measure.getAggregator().getExpression(
167                         measure.getExpression().getGenericExpression()));
168
169         RolapStar.Column[] columns = aggregation.getColumns();
170         for (int i = 0; i < columns.length; i++) {
171             buf.append(sep);
172             buf.append(columns[i].getExpression().getGenericExpression());
173             final Aggregation.Axis axis = axes[i];
174             axis.getPredicate().describe(buf);
175             if (values) {
176                 Object JavaDoc[] keys = axis.getKeys();
177                 buf.append(", values={");
178                 for (int j = 0; j < keys.length; j++) {
179                     if (j > 0) {
180                         buf.append(", ");
181                     }
182                     Object JavaDoc key = keys[j];
183                     buf.append(key);
184                 }
185                 buf.append("}");
186             }
187         }
188         if (!excludedRegions.isEmpty()) {
189             buf.append(sep);
190             buf.append("excluded={");
191             int k = 0;
192             for (Region excludedRegion : excludedRegions) {
193                 if (k++ > 0) {
194                     buf.append(", ");
195                 }
196                 excludedRegion.describe(buf);
197             }
198             buf.append('}');
199         }
200         buf.append('}');
201     }
202
203     public String JavaDoc toString() {
204         if (this.desc == null) {
205             StringBuilder JavaDoc buf = new StringBuilder JavaDoc(64);
206             makeDescription(buf, false);
207             this.desc = buf.toString();
208         }
209         return this.desc;
210     }
211
212     /**
213      * Retrieves the value at the location identified by
214      * <code>keys</code>.
215      *
216      * <p>Returns<ul>
217      *
218      * <li>{@link Util#nullValue} if the cell value
219      * is null (because no fact table rows met those criteria);</li>
220      *
221      * <li><code>null</code> if the value is not supposed to be in this segment
222      * (because one or more of the keys do not pass the axis criteria);</li>
223      *
224      * <li>the data value otherwise</li>
225      *
226      * </ul></p>
227      *
228      * <p>Note: Must be called from a synchronized context, because uses the
229      * <code>cellKey[]</code> as workspace.</p>
230      */

231     Object JavaDoc getCellValue(Object JavaDoc[] keys) {
232         assert keys.length == axes.length;
233         int missed = 0;
234         for (int i = 0; i < keys.length; i++) {
235             Object JavaDoc key = keys[i];
236             int offset = axes[i].getOffset(key);
237             if (offset < 0) {
238                 if (axes[i].getPredicate().evaluate(key)) {
239                     // see whether this segment should contain this value
240
missed++;
241                     continue;
242                 } else {
243                     // this value should not appear in this segment; we
244
// should be looking in a different segment
245
return null;
246                 }
247             }
248             cellKey.setAxis(i, offset);
249         }
250         if (isExcluded(keys)) {
251             // this value should not appear in this segment; we
252
// should be looking in a different segment
253
return null;
254         }
255         if (missed > 0) {
256             // the value should be in this segment, but isn't, because one
257
// or more of its keys does have any values
258
return Util.nullValue;
259         } else {
260             Object JavaDoc o = data.get(cellKey);
261             if (o == null) {
262                 o = Util.nullValue;
263             }
264             return o;
265         }
266     }
267
268     /**
269      * Returns whether the given set of key values will be in this segment
270      * when it finishes loading.
271      */

272     boolean wouldContain(Object JavaDoc[] keys) {
273         Util.assertTrue(keys.length == axes.length);
274         for (int i = 0; i < keys.length; i++) {
275             Object JavaDoc key = keys[i];
276             if (!axes[i].getPredicate().evaluate(key)) {
277                 return false;
278             }
279         }
280         return !isExcluded(keys);
281     }
282
283     /**
284      * Returns whether a cell value is excluded from this segment.
285      */

286     private boolean isExcluded(Object JavaDoc[] keys) {
287         for (Region excludedRegion : excludedRegions) {
288             if (excludedRegion.wouldContain(keys)) {
289                 return true;
290             }
291         }
292         return false;
293     }
294
295     /**
296      * Reads a segment of <code>measure</code>, where <code>columns</code> are
297      * constrained to <code>values</code>. Each entry in <code>values</code>
298      * can be null, meaning don't constrain, or can have several values. For
299      * example, <code>getSegment({Unit_sales}, {Region, State, Year}, {"West"},
300      * {"CA", "OR", "WA"}, null})</code> returns sales in states CA, OR and WA
301      * in the Western region, for all years.
302      *
303      * @pre segments[i].aggregation == aggregation
304      */

305     static void load(
306         final Segment[] segments,
307         final BitKey levelBitKey,
308         final BitKey measureBitKey,
309         final RolapAggregationManager.PinSet pinnedSegments,
310         final Aggregation.Axis[] axes)
311     {
312         String JavaDoc sql = AggregationManager.instance().generateSql(
313                 segments,
314                 levelBitKey,
315                 measureBitKey);
316         Segment segment0 = segments[0];
317         RolapStar star = segment0.aggregation.getStar();
318         RolapStar.Column[] columns = segment0.aggregation.getColumns();
319         int arity = columns.length;
320
321         // Workspace to build up lists of distinct values for each axis.
322
SortedSet<Comparable JavaDoc<?>>[] axisValueSets = new SortedSet[arity];
323         boolean[] axisContainsNull = new boolean[arity];
324         for (int i = 0; i < axisValueSets.length; i++) {
325             if (Util.PreJdk15) {
326                 // Work around the fact that Boolean is not Comparable until JDK
327
// 1.5.
328
assert !(Comparable JavaDoc.class.isAssignableFrom(Boolean JavaDoc.class));
329                 axisValueSets[i] =
330                     new TreeSet(
331                         new Comparator() {
332                             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
333                                 if (o1 instanceof Boolean JavaDoc) {
334                                     Boolean JavaDoc b1 = (Boolean JavaDoc) o1;
335                                     if (o2 instanceof Boolean JavaDoc) {
336                                         Boolean JavaDoc b2 = (Boolean JavaDoc) o2;
337                                         return (b1 == b2 ? 0 : (b1 ? 1 : -1));
338                                     } else {
339                                         return -1;
340                                     }
341                                 } else {
342                                     return ((Comparable JavaDoc) o1).compareTo(o2);
343                                 }
344                             }
345                         }
346                     );
347             } else {
348                 assert Comparable JavaDoc.class.isAssignableFrom(Boolean JavaDoc.class);
349                 axisValueSets[i] = new TreeSet<Comparable JavaDoc<?>>();
350             }
351         }
352
353         // execute
354
SqlStatement stmt =
355             RolapUtil.executeQuery(
356                 star.getDataSource(), sql, "Segment.load",
357                 "Error while loading segment");
358         final int measureCount = segments.length;
359         try {
360             List<Object JavaDoc[]> rows = new ArrayList<Object JavaDoc[]>();
361             ResultSet resultSet = stmt.getResultSet();
362             while (resultSet.next()) {
363                 ++stmt.rowCount;
364                 Object JavaDoc[] row = new Object JavaDoc[arity + measureCount];
365                 // get the columns
366
int k = 1;
367                 for (int i = 0; i < arity; i++) {
368                     Object JavaDoc o = resultSet.getObject(k++);
369                     if (o == null) {
370                         o = RolapUtil.sqlNullValue;
371                         axisContainsNull[i] = true;
372                     } else {
373                         axisValueSets[i].add(Aggregation.Axis.wrap(o));
374                     }
375                     row[i] = o;
376                 }
377                 // get the measure
378
for (int i = 0; i < measureCount; i++) {
379                     Object JavaDoc o = resultSet.getObject(k++);
380                     if (o == null) {
381                         o = Util.nullValue; // convert to placeholder
382
} else if (segments[i].measure.getDatatype().isNumeric()) {
383                         if (o instanceof Double JavaDoc) {
384                             // nothing to do
385
} else if (o instanceof Number JavaDoc) {
386                             o = ((Number JavaDoc) o).doubleValue();
387                         } else if (o instanceof byte[]) {
388                             // On MySQL 5.0 in German locale, values can come
389
// out as byte arrays. Don't know why. Bug 1594119.
390
o = Double.parseDouble(new String JavaDoc((byte []) o));
391                         } else {
392                             o = Double.parseDouble(o.toString());
393                         }
394                     }
395                     row[arity + i] = o;
396                 }
397                 rows.add(row);
398             }
399
400             // Figure out size of dense array, and allocate it, or use a sparse
401
// array if appropriate.
402
boolean sparse = false;
403             int n = 1;
404             for (int i = 0; i < arity; i++) {
405                 Aggregation.Axis axis = axes[i];
406                 SortedSet<Comparable JavaDoc<?>> valueSet = axisValueSets[i];
407                 int size = axis.loadKeys(valueSet, axisContainsNull[i]);
408
409                 int previous = n;
410                 n *= size;
411                 if ((n < previous) || (n < size)) {
412                     // Overflow has occurred.
413
n = Integer.MAX_VALUE;
414                     sparse = true;
415                 }
416             }
417             sparse = sparse || useSparse((double) n, (double) rows.size());
418
419             final SegmentDataset[] dataSets;
420             final SparseSegmentDataset[] sparseDatasets;
421             final DenseSegmentDataset[] denseDatasets;
422             if (sparse) {
423                 sparseDatasets = new SparseSegmentDataset[segments.length];
424                 dataSets = sparseDatasets;
425                 denseDatasets = null;
426             } else {
427                 denseDatasets = new DenseSegmentDataset[segments.length];
428                 dataSets = denseDatasets;
429                 sparseDatasets = null;
430             }
431
432             if (sparse) {
433                 for (int i = 0; i < segments.length; i++) {
434                     sparseDatasets[i] = new SparseSegmentDataset(segments[i]);
435                 }
436             } else {
437                 for (int i = 0; i < segments.length; i++) {
438                     denseDatasets[i] = new DenseSegmentDataset(
439                             segments[i], new Object JavaDoc[n]);
440                 }
441             }
442             // now convert the rows into a sparse array
443
int[] pos = new int[arity];
444             for (int i = 0, count = rows.size(); i < count; i++) {
445                 Object JavaDoc[] row = rows.get(i);
446                 int k = 0;
447                 for (int j = 0; j < arity; j++) {
448                     k *= axes[j].getKeys().length;
449                     Object JavaDoc o = row[j];
450                     Aggregation.Axis axis = axes[j];
451                     int offset = axis.getOffset(o);
452                     pos[j] = offset;
453                     k += offset;
454                 }
455                 CellKey key;
456                 if (sparse) {
457                     key = CellKey.Generator.newCellKey(pos);
458                     for (int j = 0; j < segments.length; j++) {
459                         final Object JavaDoc o = row[arity + j];
460                         sparseDatasets[j].put(key, o);
461                     }
462                 } else {
463                     for (int j = 0; j < segments.length; j++) {
464                         final Object JavaDoc o = row[arity + j];
465                         denseDatasets[j].set(k, o);
466                     }
467                 }
468             }
469
470             for (int i = 0; i < segments.length; i++) {
471                 segments[i].setData(dataSets[i], pinnedSegments);
472             }
473
474         } catch (SQLException e) {
475             throw stmt.handle(e);
476         } finally {
477             stmt.close();
478             // Any segments which are still loading have failed.
479
for (Segment segment : segments) {
480                 segment.setFailed();
481             }
482         }
483     }
484
485     /**
486      * Decides whether to use a sparse representation for this segment, using
487      * the formula described
488      * {@link MondrianProperties#SparseSegmentCountThreshold here}.
489      *
490      * @param possibleCount Number of values in the space.
491      * @param actualCount Actual number of values.
492      * @return Whether to use a sparse representation.
493      */

494     private static boolean useSparse(
495             final double possibleCount,
496             final double actualCount) {
497         final MondrianProperties properties = MondrianProperties.instance();
498         double densityThreshold = properties.SparseSegmentDensityThreshold.get();
499         if (densityThreshold < 0) {
500             densityThreshold = 0;
501         }
502         if (densityThreshold > 1) {
503             densityThreshold = 1;
504         }
505         int countThreshold = properties.SparseSegmentCountThreshold.get();
506         if (countThreshold < 0) {
507             countThreshold = 0;
508         }
509         boolean sparse =
510             (possibleCount - countThreshold) * densityThreshold > actualCount;
511         if (possibleCount < countThreshold) {
512             assert !sparse :
513                     "Should never use sparse if count is less " +
514                     "than threshold, possibleCount=" + possibleCount +
515                     ", actualCount=" + actualCount +
516                     ", countThreshold=" + countThreshold +
517                     ", densityThreshold=" + densityThreshold;
518         }
519         if (possibleCount == actualCount) {
520             assert !sparse :
521                     "Should never use sparse if result is 100% dense: " +
522                     "possibleCount=" + possibleCount +
523                     ", actualCount=" + actualCount +
524                     ", countThreshold=" + countThreshold +
525                     ", densityThreshold=" + densityThreshold;
526         }
527         return sparse;
528     }
529
530     /**
531      * Blocks until this segment has finished loading; if this segment has
532      * already loaded, returns immediately.
533      */

534     public synchronized void waitUntilLoaded() {
535         if (!isReady()) {
536             try {
537                 wait();
538             } catch (InterruptedException JavaDoc e) {
539             }
540             switch (state) {
541             case Ready:
542                 return; // excellent!
543
case Failed:
544                 throw Util.newError("Pending segment failed to load: "
545                     + toString());
546             default:
547                 throw Util.badValue(state);
548             }
549         }
550     }
551
552     /**
553      * Prints the state of this <code>Segment</code>, including constraints
554      * and values.
555      *
556      * @param pw Writer
557      */

558     public void print(PrintWriter JavaDoc pw) {
559         final StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
560         makeDescription(buf, true);
561         pw.print(buf.toString());
562         pw.println();
563     }
564
565     public List<Region> getExcludedRegions() {
566         return excludedRegions;
567     }
568
569     /**
570      * Returns the number of cells in this Segment, deducting cells in
571      * excluded regions.
572      *
573      * <p>This method may return a value which is slightly too low, or
574      * occasionally even negative. This occurs when a Segment has more than one
575      * excluded region, and those regions overlap. Cells which are in both
576      * regions will be counted twice.
577      *
578      * @return Number of cells in this Segment
579      */

580     public int getCellCount() {
581         int cellCount = 1;
582         for (Aggregation.Axis axis : axes) {
583             cellCount *= axis.getKeys().length;
584         }
585         for (Region excludedRegion : excludedRegions) {
586             cellCount -= excludedRegion.cellCount;
587         }
588         return cellCount;
589     }
590
591     /**
592      * Creates a Segment which has the same dimensionality as this Segment and a
593      * subset of the values.
594      *
595      * <p>If <code>bestColumn</code> is not -1, the <code>bestColumn</code>th
596      * column's predicate should be replaced by <code>bestPredicate</code>.
597      *
598      * @param axisKeepBitSets For each axis, a bitmap of the axis values to
599      * keep; each axis must have at least one bit set
600      * @param bestColumn
601      * @param bestPredicate
602      * @param excludedRegions List of regions to exclude from segment
603      * @return Segment containing a subset of the values
604      */

605     Segment createSubSegment(
606         BitSet[] axisKeepBitSets,
607         int bestColumn,
608         StarColumnPredicate bestPredicate,
609         List<Segment.Region> excludedRegions) {
610         assert axisKeepBitSets.length == axes.length;
611
612         // Create a new segment with a subset of the values. If only one
613
// of the axes is restricted, restrict just that axis. If more than
614
// one of the axis is restricted, add a negation to the segment.
615
final Aggregation.Axis[] newAxes = axes.clone();
616
617         // For each axis, map from old position to new position.
618
final Map<Integer JavaDoc,Integer JavaDoc>[] axisPosMaps = new Map[axes.length];
619
620         int valueCount = 1;
621         for (int j = 0; j < axes.length; j++) {
622             Aggregation.Axis axis = axes[j];
623             StarColumnPredicate newPredicate = axis.getPredicate();
624             if (j == bestColumn) {
625                 newPredicate = bestPredicate;
626             }
627             final Comparable JavaDoc<?>[] axisKeys = axis.getKeys();
628             BitSet keepBitSet = axisKeepBitSets[j];
629             int firstClearBit = keepBitSet.nextClearBit(0);
630             Comparable JavaDoc<?>[] newAxisKeys;
631             if (firstClearBit >= axisKeys.length) {
632                 // Keep everything
633
newAxisKeys = axisKeys;
634                 axisPosMaps[j] = null; // identity map
635
} else {
636                 List<Object JavaDoc> newAxisKeyList = new ArrayList<Object JavaDoc>();
637                 Map<Integer JavaDoc, Integer JavaDoc> map
638                     = axisPosMaps[j]
639                     = new HashMap<Integer JavaDoc, Integer JavaDoc>();
640                 for (int bit = keepBitSet.nextSetBit(0);
641                     bit >= 0;
642                     bit = keepBitSet.nextSetBit(bit + 1)) {
643                     map.put(bit, newAxisKeyList.size());
644                     newAxisKeyList.add(axisKeys[bit]);
645                 }
646                 newAxisKeys =
647                     newAxisKeyList.toArray(
648                         new Comparable JavaDoc<?>[newAxisKeyList.size()]);
649                 assert newAxisKeys.length > 0;
650             }
651             final Aggregation.Axis newAxis =
652                 new Aggregation.Axis(newPredicate, newAxisKeys);
653             newAxes[j] = newAxis;
654             valueCount *= newAxisKeys.length;
655         }
656
657         // Create a new segment.
658
final Segment newSegment =
659             new Segment(aggregation, measure, newAxes, excludedRegions);
660
661         // Create a dataset containing a subset of the current dataset.
662
// Keep the same representation as the current dataset.
663
// (We could be smarter - sometimes a subset of a sparse dataset will
664
// be dense and VERY occasionally a subset of a relatively dense dataset
665
// will be sparse.)
666
SegmentDataset newData;
667         if (data instanceof SparseSegmentDataset) {
668             newData =
669                 new SparseSegmentDataset(
670                     newSegment);
671         } else {
672             Object JavaDoc[] newValues = new Object JavaDoc[valueCount];
673             newData =
674                 new DenseSegmentDataset(
675                     newSegment,
676                     newValues);
677         }
678
679         // If the source is sparse, it is more efficient to iterate over the
680
// values we need. If it's dense, it doesn't matter too much.
681
int[] pos = new int[axes.length];
682         CellKey newKey = CellKey.Generator.newRefCellKey(pos);
683         data:
684         for (Map.Entry<CellKey, Object JavaDoc> entry : data) {
685             CellKey key = entry.getKey();
686
687             // Map each of the source coordinates to the target coordinate.
688
// If any of the coordinates maps to null, it means that the
689
// cell falls outside the subset.
690
for (int i = 0; i < pos.length; i++) {
691                 int ordinal = key.getAxis(i);
692
693                 Map<Integer JavaDoc, Integer JavaDoc> axisPosMap = axisPosMaps[i];
694                 if (axisPosMap == null) {
695                     pos[i] = ordinal;
696                 } else {
697                     Integer JavaDoc integer = axisPosMap.get(ordinal);
698                     if (integer == null) {
699                         continue data;
700                     }
701                     pos[i] = integer;
702                 }
703             }
704             newData.put(newKey, entry.getValue());
705         }
706         newSegment.setData(newData, null);
707
708         return newSegment;
709     }
710
711     /**
712      * Returns this Segment's dataset, or null if the data has not yet been
713      * loaded.
714      */

715     SegmentDataset getData() {
716         return data;
717     }
718
719     /**
720      * <code>State</code> enumerates the allowable values of a segment's
721      * state.
722      */

723     private static enum State {
724         Initial, Loading, Ready, Failed
725     }
726
727     /**
728      * Definition of a region of values which are not in a segment.
729      *
730      * <p>A region is defined by a set of constraints, one for each column
731      * in the segment. A constraint may be
732      * {@link mondrian.rolap.agg.LiteralStarPredicate}(true), meaning that
733      * the column is unconstrained.
734      *
735      * <p>For example,
736      * <pre>
737      * segment (State={CA, OR, WA}, Gender=*)
738      * actual values {1997, 1998} * {CA, OR, WA} * {M, F} = 12 cells
739      * excluded region (Year=*, State={CA, OR}, Gender={F})
740      * excluded values {1997, 1998} * {CA, OR} * {F} = 4 cells
741      *
742      * Values:
743      *
744      * F M
745      * CA out in
746      * OR out in
747      * WA in in
748      * </pre>
749      *
750      * <p>Note that the resulting segment is not a hypercube: it has a 'hole'.
751      * This is why regions are required.
752      */

753     static class Region {
754         private final StarColumnPredicate[] predicates;
755         private final StarPredicate[] multiColumnPredicates;
756         private final int cellCount;
757
758         Region(
759             List<StarColumnPredicate> predicateList,
760             List<StarPredicate> multiColumnPredicateList,
761             int cellCount)
762         {
763             this.predicates =
764                 predicateList.toArray(
765                     new StarColumnPredicate[predicateList.size()]);
766             this.multiColumnPredicates =
767                 multiColumnPredicateList.toArray(
768                     new StarPredicate[multiColumnPredicateList.size()]);
769             this.cellCount = cellCount;
770         }
771
772         public List<StarColumnPredicate> getPredicates() {
773             return Collections.unmodifiableList(Arrays.asList(predicates));
774         }
775
776         public List<StarPredicate> getMultiColumnPredicates() {
777             return Collections.unmodifiableList(
778                 Arrays.asList(multiColumnPredicates));
779         }
780
781         public int getCellCount() {
782             return cellCount;
783         }
784
785         public boolean wouldContain(Object JavaDoc[] keys) {
786             assert keys.length == predicates.length;
787             for (int i = 0; i < keys.length; i++) {
788                 final Object JavaDoc key = keys[i];
789                 final StarColumnPredicate predicate = predicates[i];
790                 if (!predicate.evaluate(key)) {
791                     return false;
792                 }
793             }
794             return true;
795         }
796
797         public boolean equals(Object JavaDoc obj) {
798             if (obj instanceof Region) {
799                 Region that = (Region) obj;
800                 return Arrays.equals(
801                     this.predicates, that.predicates) &&
802                     Arrays.equals(
803                         this.multiColumnPredicates,
804                         that.multiColumnPredicates);
805             } else {
806                 return false;
807             }
808         }
809
810         public int hashCode() {
811             return Arrays.hashCode(multiColumnPredicates) ^
812                 Arrays.hashCode(predicates);
813         }
814
815         /**
816          * Describes this Segment.
817          * @param buf Buffer to write to.
818          */

819         public void describe(StringBuilder JavaDoc buf) {
820             int k = 0;
821             for (StarColumnPredicate predicate : predicates) {
822                 if (predicate instanceof LiteralStarPredicate &&
823                     ((LiteralStarPredicate) predicate).getValue()) {
824                     continue;
825                 }
826                 if (k++ > 0) {
827                     buf.append(" AND ");
828                 }
829                 predicate.describe(buf);
830             }
831             for (StarPredicate predicate : multiColumnPredicates) {
832                 if (predicate instanceof LiteralStarPredicate &&
833                     ((LiteralStarPredicate) predicate).getValue()) {
834                     continue;
835                 }
836                 if (k++ > 0) {
837                     buf.append(" AND ");
838                 }
839                 predicate.describe(buf);
840             }
841         }
842     }
843 }
844
845 // End Segment.java
846
Popular Tags