-
Notifications
You must be signed in to change notification settings - Fork 141
/
QueryDimension.java
585 lines (539 loc) · 18.5 KB
/
QueryDimension.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
/*
// $Id$
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2007-2010 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package org.olap4j.query;
import org.olap4j.OlapException;
import org.olap4j.impl.IdentifierParser;
import org.olap4j.mdx.IdentifierSegment;
import org.olap4j.metadata.*;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.AbstractList;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Usage of a dimension for an OLAP query.
*
* <p>It references an {@link org.olap4j.metadata.Dimension} and allows the
* query creator to manage the member selections for the dimension.
* The state of a QueryDimension does not affect the
* Dimension object in any way so a single Dimension object
* can be referenced by many QueryDimension objects.
*
* @author jdixon, jhyde, Luc Boudreau
* @version $Id$
* @since May 29, 2007
*/
public class QueryDimension extends QueryNodeImpl {
protected QueryAxis axis;
protected final List<Selection> inclusions = new SelectionList();
protected final List<Selection> exclusions = new SelectionList();
private final Query query;
protected Dimension dimension;
private SortOrder sortOrder = null;
private HierarchizeMode hierarchizeMode = null;
public QueryDimension(Query query, Dimension dimension) {
super();
this.query = query;
this.dimension = dimension;
}
public Query getQuery() {
return query;
}
public void setAxis(QueryAxis axis) {
this.axis = axis;
}
public QueryAxis getAxis() {
return axis;
}
public String getName() {
return dimension.getName();
}
/**
* Selects members and includes them in the query.
*
* <p>This method selects and includes a single member with the
* {@link Selection.Operator#MEMBER} operator.
*
* @param nameParts Name of the member to select and include.
* @throws OlapException If no member corresponding to the supplied
* name parts could be resolved in the cube.
*/
public Selection include(
List<IdentifierSegment> nameParts)
throws OlapException
{
return this.include(Selection.Operator.MEMBER, nameParts);
}
public Selection createSelection(
List<IdentifierSegment> nameParts)
throws OlapException
{
return this.createSelection(Selection.Operator.MEMBER, nameParts);
}
/**
* Selects members and includes them in the query.
*
* <p>This method selects and includes a member along with its
* relatives, depending on the supplied {@link Selection.Operator}
* operator.
*
* @param operator Selection operator that defines what relatives of the
* supplied member name to include along.
* @param nameParts Name of the root member to select and include.
* @throws OlapException If no member corresponding to the supplied
* name parts could be resolved in the cube.
*/
public Selection include(
Selection.Operator operator,
List<IdentifierSegment> nameParts) throws OlapException
{
Member member = this.getQuery().getCube().lookupMember(nameParts);
if (member == null) {
throw new OlapException(
"Unable to find a member with name " + nameParts);
}
return this.include(
operator,
member);
}
public Selection createSelection(
Selection.Operator operator,
List<IdentifierSegment> nameParts) throws OlapException
{
Member member = this.getQuery().getCube().lookupMember(nameParts);
if (member == null) {
throw new OlapException(
"Unable to find a member with name " + nameParts);
}
return this.createSelection(
operator,
member);
}
/**
* Selects members and includes them in the query.
* <p>This method selects and includes a single member with the
* {@link Selection.Operator#MEMBER} selection operator.
* @param member The member to select and include in the query.
*/
public Selection include(Member member) {
return include(Selection.Operator.MEMBER, member);
}
public Selection createSelection(Member member) {
return createSelection(Selection.Operator.MEMBER, member);
}
/**
* Selects a level and includes it in the query.
* <p>This method selects and includes a all members of the given
* query using the {@link Selection.Operator#MEMBERS} selection operator.
* @param member The member to select and include in the query.
*/
public Selection include(Level level) {
if (level.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createLevelSelection(level);
this.include(selection);
return selection;
}
return null;
}
/**
* Selects members and includes them in the query.
* <p>This method selects and includes a member along with it's
* relatives, depending on the supplied {@link Selection.Operator}
* operator.
* @param operator Selection operator that defines what relatives of the
* supplied member name to include along.
* @param member Root member to select and include.
*/
public Selection createSelection(
Selection.Operator operator,
Member member)
{
if (member.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createMemberSelection(
member, operator);
return selection;
}
return null;
}
/**
* Selects level and includes all members in the query.
* <p>This method selects and includes all members of a
* given Level, using the MEMBERS operator {@link Selection.Operator}
* @param member Root member to select and include.
*/
public Selection createSelection(Level level)
{
if (level.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createLevelSelection(level);
return selection;
}
return null;
}
/**
* Selects members and includes them in the query.
* <p>This method selects and includes a member along with it's
* relatives, depending on the supplied {@link Selection.Operator}
* operator.
* @param operator Selection operator that defines what relatives of the
* supplied member name to include along.
* @param member Root member to select and include.
*/
public Selection include(
Selection.Operator operator,
Member member)
{
if (member.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createMemberSelection(
member, operator);
this.include(selection);
return selection;
}
return null;
}
/**
* Includes a selection of members in the query.
* @param selection The selection of members to include.
*/
private void include(Selection selection) {
this.getInclusions().add(selection);
Integer index = Integer.valueOf(
this.getInclusions().indexOf(selection));
this.notifyAdd(selection, index);
}
/**
* Clears the current member inclusions from this query dimension.
*/
public void clearInclusions() {
Map<Integer, QueryNode> removed = new HashMap<Integer, QueryNode>();
for (Selection node : this.inclusions) {
removed.put(
Integer.valueOf(this.inclusions.indexOf(node)),
node);
((QueryNodeImpl) node).clearListeners();
}
this.inclusions.clear();
this.notifyRemove(removed);
}
/**
* Selects members and excludes them from the query.
*
* <p>This method selects and excludes a single member with the
* {@link Selection.Operator#MEMBER} operator.
*
* @param nameParts Name of the member to select and exclude.
* @throws OlapException If no member corresponding to the supplied
* name parts could be resolved in the cube.
*/
public void exclude(
List<IdentifierSegment> nameParts)
throws OlapException
{
this.exclude(Selection.Operator.MEMBER, nameParts);
}
/**
* Selects members and excludes them from the query.
*
* <p>This method selects and excludes a member along with its
* relatives, depending on the supplied {@link Selection.Operator}
* operator.
*
* @param operator Selection operator that defines what relatives of the
* supplied member name to exclude along.
* @param nameParts Name of the root member to select and exclude.
* @throws OlapException If no member corresponding to the supplied
* name parts could be resolved in the cube.
*/
public void exclude(
Selection.Operator operator,
List<IdentifierSegment> nameParts) throws OlapException
{
Member rootMember = this.getQuery().getCube().lookupMember(nameParts);
if (rootMember == null) {
throw new OlapException(
"Unable to find a member with name " + nameParts);
}
this.exclude(
operator,
rootMember);
}
/**
* Selects level members and excludes them from the query.
* <p>This method selects and excludes members of a level with the
* {@link Selection.Operator#MEMBERS} selection operator.
* @param level The level to select and exclude from the query.
*/
public void exclude(Level level) {
if (level.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createLevelSelection(level);
this.exclude(selection);
}
}
/**
* Selects members and excludes them from the query.
* <p>This method selects and excludes a single member with the
* {@link Selection.Operator#MEMBER} selection operator.
* @param member The member to select and exclude from the query.
*/
public void exclude(Member member) {
exclude(Selection.Operator.MEMBER, member);
}
/**
* Selects members and excludes them from the query.
* <p>This method selects and excludes a member along with it's
* relatives, depending on the supplied {@link Selection.Operator}
* operator.
* @param operator Selection operator that defines what relatives of the
* supplied member name to exclude along.
* @param member Root member to select and exclude.
*/
public void exclude(
Selection.Operator operator,
Member member)
{
if (member.getDimension().equals(this.dimension)) {
Selection selection =
query.getSelectionFactory().createMemberSelection(
member, operator);
this.exclude(selection);
}
}
/**
* Excludes a selection of members from the query.
* @param selection The selection of members to exclude.
*/
private void exclude(Selection selection) {
this.getExclusions().add(selection);
Integer index = Integer.valueOf(
this.getExclusions().indexOf(selection));
this.notifyAdd(selection, index);
}
/**
* Clears the current member inclusions from this query dimension.
*/
public void clearExclusions() {
Map<Integer, QueryNode> removed = new HashMap<Integer, QueryNode>();
for (Selection node : this.exclusions) {
removed.put(
Integer.valueOf(this.exclusions.indexOf(node)),
node);
((QueryNodeImpl) node).clearListeners();
}
this.exclusions.clear();
this.notifyRemove(removed);
}
/**
* Resolves a selection of members into an actual list
* of the root member and it's relatives selected by the Selection object.
* @param selection The selection of members to resolve.
* @return A list of the actual members selected by the selection object.
* @throws OlapException If resolving the selections triggers an exception
* while looking up members in the underlying cube.
*/
public List<Member> resolve(Selection selection) throws OlapException
{
assert selection != null;
final Member.TreeOp op;
Member.TreeOp secondOp = null;
switch (selection.getOperator()) {
case CHILDREN:
op = Member.TreeOp.CHILDREN;
break;
case SIBLINGS:
op = Member.TreeOp.SIBLINGS;
break;
case INCLUDE_CHILDREN:
op = Member.TreeOp.SELF;
secondOp = Member.TreeOp.CHILDREN;
break;
case MEMBER:
op = Member.TreeOp.SELF;
break;
default:
throw new OlapException(
"Operation not supported: " + selection.getOperator());
}
Set<Member.TreeOp> set = new TreeSet<Member.TreeOp>();
set.add(op);
if (secondOp != null) {
set.add(secondOp);
}
try {
return
query.getCube().lookupMembers(
set,
IdentifierParser.parseIdentifier(
selection.getUniqueName()));
} catch (Exception e) {
throw new OlapException(
"Error while resolving selection " + selection.toString(),
e);
}
}
/**
* Returns a list of the inclusions within this dimension.
*
* <p>Be aware that modifications to this list might
* have unpredictable consequences.</p>
*
* @return list of inclusions
*/
public List<Selection> getInclusions() {
return inclusions;
}
/**
* Returns a list of the exclusions within this dimension.
*
* <p>Be aware that modifications to this list might
* have unpredictable consequences.</p>
*
* @return list of exclusions
*/
public List<Selection> getExclusions() {
return exclusions;
}
/**
* Returns the underlying dimension object onto which
* this query dimension is based.
* <p>Returns a mutable object so operations on it have
* unpredictable consequences.
* @return The underlying dimension representation.
*/
public Dimension getDimension() {
return dimension;
}
/**
* Forces a change onto which dimension is the current
* base of this QueryDimension object.
* <p>Forcing a change in the duimension assignment has
* unpredictable consequences.
* @param dimension The new dimension to assign to this
* query dimension.
*/
public void setDimension(Dimension dimension) {
this.dimension = dimension;
}
/**
* Sorts the dimension members by name in the
* order supplied as a parameter.
* @param order The {@link SortOrder} to use.
*/
public void sort(SortOrder order) {
this.sortOrder = order;
}
/**
* Returns the current order in which the
* dimension members are sorted.
* @return A value of {@link SortOrder}
*/
public SortOrder getSortOrder() {
return this.sortOrder;
}
/**
* Clears the current sorting settings.
*/
public void clearSort() {
this.sortOrder = null;
}
/**
* Returns the current mode of hierarchization, or null
* if no hierarchization is currently performed.
*
* <p>This capability is only available when a single dimension is
* selected on an axis
*
* @return Either a hierarchization mode value or null
* if no hierarchization is currently performed.
*/
public HierarchizeMode getHierarchizeMode() {
return hierarchizeMode;
}
/**
* Triggers the hierarchization of the included members within this
* QueryDimension.
*
* <p>The dimension inclusions will be wrapped in an MDX Hierarchize
* function call.
*
* <p>This capability is only available when a single dimension is
* selected on an axis.
*
* @param hierarchizeMode If parents should be included before or after
* their children. (Equivalent to the POST/PRE MDX literal for the
* Hierarchize() function)
* inside the Hierarchize() MDX function call.
*/
public void setHierarchizeMode(HierarchizeMode hierarchizeMode) {
this.hierarchizeMode = hierarchizeMode;
}
/**
* Tells the QueryDimension not to hierarchize its included
* selections.
*
* <p>This capability is only available when a single dimension is
* selected on an axis.
*/
public void clearHierarchizeMode() {
this.hierarchizeMode = null;
}
private class SelectionList extends AbstractList<Selection> {
private final List<Selection> list = new ArrayList<Selection>();
public Selection get(int index) {
return list.get(index);
}
public int size() {
return list.size();
}
public Selection set(int index, Selection selection) {
return list.set(index, selection);
}
public void add(int index, Selection selection) {
if (this.contains(selection)) {
throw new IllegalStateException(
"dimension already contains selection");
}
list.add(index, selection);
}
public Selection remove(int index) {
return list.remove(index);
}
}
/**
* Defines in which way the hierarchize operation
* should be performed.
*/
public static enum HierarchizeMode {
/**
* Parents are placed before children.
*/
PRE,
/**
* Parents are placed after children
*/
POST
}
void tearDown() {
for (Selection node : this.inclusions) {
((QueryNodeImpl)node).clearListeners();
}
for (Selection node : this.exclusions) {
((QueryNodeImpl)node).clearListeners();
}
this.inclusions.clear();
this.exclusions.clear();
}
}
// End QueryDimension.java