1
2
3
4
5
6
7
8
9 package com.eyeq.pivot4j.ui.table;
10
11 import static com.eyeq.pivot4j.ui.CellTypes.AGG_VALUE;
12 import static com.eyeq.pivot4j.ui.CellTypes.LABEL;
13 import static com.eyeq.pivot4j.ui.CellTypes.VALUE;
14 import static com.eyeq.pivot4j.ui.table.TableCellTypes.FILL;
15 import static com.eyeq.pivot4j.ui.table.TableCellTypes.TITLE;
16 import static com.eyeq.pivot4j.ui.table.TablePropertyCategories.CELL;
17 import static com.eyeq.pivot4j.ui.table.TablePropertyCategories.HEADER;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.HashMap;
24 import java.util.LinkedHashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.apache.commons.configuration.HierarchicalConfiguration;
31 import org.apache.commons.lang.NullArgumentException;
32 import org.apache.commons.lang.StringUtils;
33 import org.olap4j.Axis;
34 import org.olap4j.Cell;
35 import org.olap4j.CellSet;
36 import org.olap4j.CellSetAxis;
37 import org.olap4j.OlapException;
38 import org.olap4j.Position;
39 import org.olap4j.metadata.Hierarchy;
40 import org.olap4j.metadata.Level;
41 import org.olap4j.metadata.Measure;
42 import org.olap4j.metadata.Member;
43 import org.olap4j.metadata.Property;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import com.eyeq.pivot4j.PivotException;
48 import com.eyeq.pivot4j.PivotModel;
49 import com.eyeq.pivot4j.transform.ChangeSlicer;
50 import com.eyeq.pivot4j.ui.AbstractPivotRenderer;
51 import com.eyeq.pivot4j.ui.aggregator.Aggregator;
52 import com.eyeq.pivot4j.ui.aggregator.AggregatorFactory;
53 import com.eyeq.pivot4j.ui.aggregator.AggregatorPosition;
54 import com.eyeq.pivot4j.util.OlapUtils;
55 import com.eyeq.pivot4j.util.RaggedMemberWrapper;
56 import com.eyeq.pivot4j.util.TreeNode;
57 import com.eyeq.pivot4j.util.TreeNodeCallback;
58
59 public class TableRenderer extends
60 AbstractPivotRenderer<TableRenderContext, TableRenderCallback> {
61
62 private Logger logger = LoggerFactory.getLogger(getClass());
63
64 private boolean hideSpans = false;
65
66 private boolean showParentMembers = false;
67
68 private boolean showDimensionTitle = true;
69
70
71
72
73 @Override
74 protected List<String> getRenderPropertyCategories() {
75 List<String> categories = new LinkedList<String>(
76 super.getRenderPropertyCategories());
77
78 categories.add(CELL);
79 categories.add(HEADER);
80
81 return categories;
82 }
83
84
85
86
87 public boolean getHideSpans() {
88 return hideSpans;
89 }
90
91
92
93
94
95 public void setHideSpans(boolean hideSpans) {
96 this.hideSpans = hideSpans;
97 }
98
99
100
101
102 public boolean getShowParentMembers() {
103 return showParentMembers;
104 }
105
106
107
108
109
110 public void setShowParentMembers(boolean showParentMembers) {
111 this.showParentMembers = showParentMembers;
112 }
113
114
115
116
117 public boolean getShowDimensionTitle() {
118 return showDimensionTitle;
119 }
120
121
122
123
124
125 public void setShowDimensionTitle(boolean showDimensionTitle) {
126 this.showDimensionTitle = showDimensionTitle;
127 }
128
129 public void swapAxes() {
130 for (AggregatorPosition position : AggregatorPosition.values()) {
131 swapAggregators(position);
132 }
133 }
134
135
136
137
138 private void swapAggregators(AggregatorPosition position) {
139 List<String> aggregators = getAggregators(Axis.COLUMNS, position);
140
141 setAggregators(Axis.COLUMNS, position,
142 getAggregators(Axis.ROWS, position));
143 setAggregators(Axis.ROWS, position, aggregators);
144 }
145
146
147
148
149
150
151 @Override
152 protected String getLabel(TableRenderContext context) {
153 String label;
154
155 if (LABEL.equals(context.getCellType())) {
156 label = getHeaderLabel(context);
157 } else if (TITLE.equals(context.getCellType())) {
158 label = getTitleLabel(context);
159 } else if (VALUE.equals(context.getCellType())) {
160 label = getValueLabel(context);
161 } else if (AGG_VALUE.equals(context.getCellType())) {
162 label = getAggregationLabel(context);
163 } else {
164 label = null;
165 }
166
167 return label;
168 }
169
170
171
172
173
174 protected String getHeaderLabel(TableRenderContext context) {
175 String label;
176
177 if (context.getProperty() == null) {
178 if (context.getMember() == null) {
179 label = context.getHierarchy().getCaption();
180 } else {
181 label = context.getMember().getCaption();
182 }
183 } else {
184 try {
185 label = context.getMember().getPropertyFormattedValue(
186 context.getProperty());
187 } catch (OlapException e) {
188 throw new PivotException(e);
189 }
190 }
191
192 return label;
193 }
194
195
196
197
198
199 protected String getTitleLabel(TableRenderContext context) {
200 String label = null;
201
202 if (context.getAxis() == Axis.FILTER) {
203 label = context.getResourceBundle().getString("label.filter");
204 } else if (context.getProperty() != null) {
205 label = context.getProperty().getCaption();
206 } else if (context.getLevel() != null) {
207 label = context.getLevel().getCaption();
208 } else if (context.getHierarchy() != null) {
209 label = context.getHierarchy().getCaption();
210 }
211
212 return label;
213 }
214
215
216
217
218
219 protected String getValueLabel(TableRenderContext context) {
220 String label;
221
222 Cell cell = context.getCell();
223
224 if (cell == null) {
225 if (context.getAxis() == Axis.FILTER) {
226 if (context.getMember() != null) {
227 label = context.getMember().getCaption();
228 } else {
229 label = context.getHierarchy().getCaption();
230 }
231 } else {
232 Aggregator aggregator = context.getAggregator();
233
234 if (aggregator == null) {
235 label = null;
236 } else {
237 label = aggregator.getFormattedValue(context);
238 }
239 }
240 } else {
241 label = cell.getFormattedValue();
242 }
243
244 return label;
245 }
246
247
248
249
250
251 protected String getAggregationLabel(TableRenderContext context) {
252 String label;
253
254 Aggregator aggregator = context.getAggregator();
255
256 if (aggregator == null && context.getMember() != null) {
257 label = context.getMember().getCaption();
258 } else {
259 label = aggregator.getLabel(context);
260 }
261
262 return label;
263 }
264
265
266
267
268
269 @Override
270 protected String getRenderPropertyCategory(TableRenderContext context) {
271 String category;
272
273 if (VALUE.equals(context.getCellType())
274 || AGG_VALUE.equals(context.getCellType())) {
275 category = CELL;
276 } else {
277 category = HEADER;
278 }
279
280 return category;
281 }
282
283
284
285
286 @Override
287 public Serializable saveState() {
288 Serializable[] states = new Serializable[6];
289
290 int index = 0;
291
292 states[index++] = super.saveState();
293 states[index++] = showParentMembers;
294 states[index++] = showDimensionTitle;
295 states[index++] = hideSpans;
296
297 return states;
298 }
299
300
301
302
303 @Override
304 public void restoreState(Serializable state) {
305 if (state == null) {
306 throw new NullArgumentException("state");
307 }
308
309 Serializable[] states = (Serializable[]) state;
310
311 int index = 0;
312
313 super.restoreState(states[index++]);
314
315 this.showParentMembers = (Boolean) states[index++];
316 this.showDimensionTitle = (Boolean) states[index++];
317 this.hideSpans = (Boolean) states[index++];
318 }
319
320
321
322
323 @Override
324 public void saveSettings(HierarchicalConfiguration configuration) {
325 super.saveSettings(configuration);
326
327 configuration.addProperty("showDimensionTitle", showDimensionTitle);
328 configuration.addProperty("showParentMembers", showParentMembers);
329 configuration.addProperty("hideSpans", hideSpans);
330 }
331
332
333
334
335 @Override
336 public void restoreSettings(HierarchicalConfiguration configuration) {
337 super.restoreSettings(configuration);
338
339 this.showDimensionTitle = configuration.getBoolean(
340 "showDimensionTitle", true);
341 this.showParentMembers = configuration.getBoolean("showParentMembers",
342 false);
343 this.hideSpans = configuration.getBoolean("hideSpans", false);
344 }
345
346
347
348
349
350 @Override
351 public void render(PivotModel model, TableRenderCallback callback) {
352 if (model == null) {
353 throw new NullArgumentException("model");
354 }
355
356 if (callback == null) {
357 throw new NullArgumentException("callback");
358 }
359
360 CellSet cellSet = model.getCellSet();
361
362 if (cellSet == null) {
363 return;
364 }
365
366 List<CellSetAxis> axes = cellSet.getAxes();
367 if (axes.isEmpty()) {
368 return;
369 }
370
371 TableHeaderNode columnRoot = createAxisTree(model, Axis.COLUMNS);
372 if (columnRoot == null) {
373 return;
374 }
375
376 TableHeaderNode rowRoot = createAxisTree(model, Axis.ROWS);
377 if (rowRoot == null) {
378 return;
379 }
380
381 configureAxisTree(model, Axis.COLUMNS, columnRoot);
382 configureAxisTree(model, Axis.ROWS, rowRoot);
383
384 invalidateAxisTree(model, Axis.COLUMNS, columnRoot);
385 invalidateAxisTree(model, Axis.ROWS, rowRoot);
386
387 TableRenderContext context = createRenderContext(model, columnRoot,
388 rowRoot);
389
390 callback.startRender(context);
391 callback.startTable(context);
392
393 renderHeader(context, columnRoot, rowRoot, callback);
394 renderBody(context, columnRoot, rowRoot, callback);
395
396 callback.endTable(context);
397
398 if (getRenderSlicer()) {
399 renderFilter(context, callback);
400 }
401
402 callback.endRender(context);
403 }
404
405
406
407
408
409
410
411 protected TableRenderContext createRenderContext(PivotModel model,
412 TableHeaderNode columnRoot, TableHeaderNode rowRoot) {
413 int columnHeaderCount = columnRoot.getMaxRowIndex();
414 int rowHeaderCount = rowRoot.getMaxRowIndex();
415
416 int columnCount = columnRoot.getWidth();
417 int rowCount = rowRoot.getWidth();
418
419 Map<String, Member> cachedParents = new HashMap<String, Member>();
420
421 cachedParents.putAll(columnRoot.getReference().getParentMembersCache());
422 cachedParents.putAll(rowRoot.getReference().getParentMembersCache());
423
424 TableRenderContext context = new TableRenderContext(model, this,
425 columnCount, rowCount, columnHeaderCount, rowHeaderCount);
426
427 context.setAttribute(TableRenderContext.ATTRIBUTE_CACHED_MEMBERS,
428 cachedParents);
429
430 return context;
431 }
432
433
434
435
436
437
438
439 protected void renderHeader(final TableRenderContext context,
440 final TableHeaderNode columnRoot, final TableHeaderNode rowRoot,
441 final TableRenderCallback callback) {
442
443 callback.startHeader(context);
444 context.setRenderPropertyCategory(HEADER);
445
446 int count = context.getColumnHeaderCount();
447
448 for (int rowIndex = 0; rowIndex < count; rowIndex++) {
449 context.setAxis(Axis.COLUMNS);
450 context.setColIndex(0);
451 context.setRowIndex(rowIndex);
452
453 callback.startRow(context);
454
455 renderHeaderCorner(context, columnRoot, rowRoot, callback);
456
457
458 context.setAxis(Axis.COLUMNS);
459
460 columnRoot.walkChildrenAtRowIndex(
461 new TreeNodeCallback<TableAxisContext>() {
462
463 @Override
464 public int handleTreeNode(
465 TreeNode<TableAxisContext> node) {
466 TableHeaderNode headerNode = (TableHeaderNode) node;
467
468 context.setColIndex(headerNode.getColIndex()
469 + context.getRowHeaderCount());
470 context.setColSpan(headerNode.getColSpan());
471 context.setRowSpan(headerNode.getRowSpan());
472
473 context.setMember(headerNode.getMember());
474 context.setProperty(headerNode.getProperty());
475 context.setHierarchy(headerNode.getHierarchy());
476 context.setPosition(headerNode.getPosition());
477 context.setColumnPosition(headerNode.getPosition());
478 context.setAggregator(headerNode.getAggregator());
479 context.setCell(null);
480
481 if (headerNode.isAggregation()) {
482 context.setCellType(AGG_VALUE);
483 } else if (context.getMember() == null) {
484 if (context.getHierarchy() == null) {
485 context.setCellType(FILL);
486 } else {
487 context.setCellType(TITLE);
488 }
489 } else {
490 context.setCellType(LABEL);
491 }
492
493 callback.startCell(context);
494 callback.renderCommands(context,
495 getCommands(context));
496 callback.renderContent(context, getLabel(context));
497 callback.endCell(context);
498
499 return TreeNodeCallback.CONTINUE;
500 }
501 }, rowIndex + 1);
502
503 callback.endRow(context);
504 }
505
506 callback.endHeader(context);
507 }
508
509
510
511
512
513
514
515 protected void renderBody(final TableRenderContext context,
516 final TableHeaderNode columnRoot, final TableHeaderNode rowRoot,
517 final TableRenderCallback callback) {
518 callback.startBody(context);
519
520 int count = rowRoot.getColSpan();
521
522 for (int rowIndex = 0; rowIndex < count; rowIndex++) {
523 context.setAxis(Axis.ROWS);
524 context.setColIndex(0);
525 context.setRowIndex(rowIndex + context.getColumnHeaderCount());
526
527 callback.startRow(context);
528
529 rowRoot.walkChildrenAtColIndex(
530 new TreeNodeCallback<TableAxisContext>() {
531
532 @Override
533 public int handleTreeNode(
534 TreeNode<TableAxisContext> node) {
535 TableHeaderNode headerNode = (TableHeaderNode) node;
536
537 if (headerNode.getRowIndex() == 0) {
538 return TreeNodeCallback.CONTINUE;
539 }
540
541 context.setColIndex(headerNode.getRowIndex() - 1);
542 context.setColSpan(headerNode.getRowSpan());
543 context.setRowSpan(headerNode.getColSpan());
544 context.setMember(headerNode.getMember());
545 context.setLevel(headerNode.getMemberLevel());
546 context.setProperty(headerNode.getProperty());
547 context.setHierarchy(headerNode.getHierarchy());
548 context.setPosition(headerNode.getPosition());
549 context.setRowPosition(headerNode.getPosition());
550 context.setAggregator(headerNode.getAggregator());
551 context.setCell(null);
552 context.setRenderPropertyCategory(HEADER);
553
554 if (headerNode.isAggregation()) {
555 context.setCellType(AGG_VALUE);
556 } else if (context.getMember() == null) {
557 if (context.getHierarchy() == null) {
558 context.setCellType(FILL);
559 } else {
560 context.setCellType(TITLE);
561 }
562 } else {
563 context.setCellType(LABEL);
564 }
565
566 callback.startCell(context);
567 callback.renderCommands(context,
568 getCommands(context));
569 callback.renderContent(context, getLabel(context));
570 callback.endCell(context);
571
572 if (headerNode.getChildCount() == 0) {
573 renderDataRow(context, columnRoot, rowRoot,
574 (TableHeaderNode) node, callback);
575 }
576
577 return TreeNodeCallback.CONTINUE;
578 }
579 }, rowIndex);
580
581 callback.endRow(context);
582 }
583
584 callback.endBody(context);
585 }
586
587
588
589
590
591
592
593
594 protected void renderDataRow(TableRenderContext context,
595 TableHeaderNode columnRoot, TableHeaderNode rowRoot,
596 TableHeaderNode rowNode, TableRenderCallback callback) {
597 context.setCellType(VALUE);
598 context.setRenderPropertyCategory(CELL);
599
600 for (int i = 0; i < context.getColumnCount(); i++) {
601 Cell cell = null;
602
603 TableHeaderNode columnNode = columnRoot.getLeafNodeAtColIndex(i);
604
605 if (columnNode != null && columnNode.getPosition() != null
606 && columnNode.getPosition().getOrdinal() != -1
607 && rowNode.getPosition() != null
608 && rowNode.getPosition().getOrdinal() != -1) {
609 cell = context.getCellSet().getCell(columnNode.getPosition(),
610 rowNode.getPosition());
611 }
612
613 context.setColIndex(context.getRowHeaderCount() + i);
614 context.setColSpan(1);
615 context.setRowSpan(1);
616 context.setAggregator(null);
617
618 context.setAxis(null);
619 context.setHierarchy(null);
620 context.setLevel(null);
621 context.setMember(null);
622 context.setCell(cell);
623
624 context.setPosition(null);
625 context.setColumnPosition(columnNode.getPosition());
626 context.setRowPosition(rowNode.getPosition());
627
628 if (columnNode.getAggregator() == null) {
629 if (rowNode.getAggregator() != null) {
630 context.setAggregator(rowNode.getAggregator());
631 context.setAxis(Axis.ROWS);
632 }
633 } else if (rowNode.getAggregator() == null
634 || columnNode.getAggregator().getMeasure() != null) {
635 context.setAggregator(columnNode.getAggregator());
636 context.setAxis(Axis.COLUMNS);
637 } else if (rowNode.getAggregator().getMeasure() != null) {
638 context.setAggregator(rowNode.getAggregator());
639 context.setAxis(Axis.ROWS);
640 }
641
642 callback.startCell(context);
643 callback.renderCommands(context, getCommands(context));
644 callback.renderContent(context, getLabel(context));
645 callback.endCell(context);
646
647 context.setPosition(context.getRowPosition());
648
649 for (AggregatorPosition position : AggregatorPosition.values()) {
650 for (Aggregator aggregator : rowRoot.getReference()
651 .getAggregators(position)) {
652 aggregate(context, rowNode, aggregator, position);
653 }
654 }
655
656 context.setPosition(context.getColumnPosition());
657
658 for (AggregatorPosition position : AggregatorPosition.values()) {
659 for (Aggregator aggregator : columnRoot.getReference()
660 .getAggregators(position)) {
661 aggregate(context, columnNode, aggregator, position);
662 }
663 }
664 }
665
666 context.setAggregator(null);
667 }
668
669
670
671
672
673
674
675 protected void aggregate(TableRenderContext context, TableHeaderNode node,
676 Aggregator aggregator, AggregatorPosition position) {
677 Measure measure = aggregator.getMeasure();
678
679 List<Member> members = aggregator.getMembers();
680
681 if (context.getCell() == null
682 && (measure == null || context.getAggregator() == null)) {
683 return;
684 }
685
686 if (context.getAggregator() != null
687 && (context.getAggregator().getAxis() == aggregator.getAxis())) {
688 return;
689 }
690
691 List<Member> positionMembers = context.getPosition().getMembers();
692
693 int index = 0;
694 for (Member member : members) {
695 if (positionMembers.size() <= index) {
696 return;
697 }
698
699 Member positionMember = positionMembers.get(index);
700
701 if (positionMember.getDepth() > 1
702 && context.getParentMember(positionMember) == null) {
703 positionMember = new RaggedMemberWrapper(positionMember,
704 context.getModel().getCube());
705 }
706
707 if (!OlapUtils.equals(member, positionMember)
708 && (member.getDepth() >= positionMember.getDepth() || !context
709 .getAncestorMembers(positionMember)
710 .contains(member))) {
711 return;
712 }
713
714 index++;
715 }
716
717 if (measure != null && !positionMembers.isEmpty()) {
718 Member member = positionMembers.get(positionMembers.size() - 1);
719
720 if (!measure.equals(member)) {
721 return;
722 }
723 }
724
725 TableHeaderNode parent = node;
726
727 while (parent != null) {
728 if (parent.getHierarchyDescendents() == 1
729 && parent.getMemberChildren() > 0) {
730 switch (position) {
731 case Grand:
732 return;
733 case Hierarchy:
734 if (!members.contains(parent.getMember())) {
735 return;
736 }
737 break;
738 case Member:
739 if (node == parent
740 || members.lastIndexOf(parent.getMember()) == members
741 .size() - 1) {
742 return;
743 }
744 break;
745 default:
746 assert false;
747 }
748 }
749
750 parent = (TableHeaderNode) parent.getParent();
751 }
752
753 aggregator.aggregate(context);
754 }
755
756
757
758
759
760
761
762 protected void renderHeaderCorner(TableRenderContext context,
763 TableHeaderNode columnRoot, TableHeaderNode rowRoot,
764 TableRenderCallback callback) {
765 int offset = 0;
766
767 if (getShowDimensionTitle()) {
768 offset = showParentMembers ? 2 : 1;
769 }
770
771 context.setAxis(null);
772
773 context.setHierarchy(null);
774 context.setLevel(null);
775 context.setMember(null);
776 context.setProperty(null);
777
778 context.setCell(null);
779 context.setCellType(FILL);
780
781 context.setPosition(null);
782 context.setColumnPosition(null);
783 context.setRowPosition(null);
784
785 boolean renderDimensionTitle = showDimensionTitle
786 && (context.getRowIndex() == context.getColumnHeaderCount()
787 - offset);
788 boolean renderLevelTitle = showDimensionTitle
789 && showParentMembers
790 && (context.getRowIndex() == context.getColumnHeaderCount() - 1);
791
792 if (context.getRowIndex() == 0 && !renderDimensionTitle
793 && !renderLevelTitle) {
794 context.setColSpan(context.getRowHeaderCount());
795 context.setRowSpan(context.getColumnHeaderCount() - offset);
796
797 callback.startCell(context);
798 callback.renderCommands(context, getCommands(context));
799 callback.renderContent(context, getLabel(context));
800 callback.endCell(context);
801 } else if (renderDimensionTitle) {
802 final Map<Hierarchy, Integer> spans = new HashMap<Hierarchy, Integer>();
803 final Map<Hierarchy, List<Property>> propertyMap = new HashMap<Hierarchy, List<Property>>();
804
805 rowRoot.walkChildrenAtColIndex(
806 new TreeNodeCallback<TableAxisContext>() {
807
808 @Override
809 public int handleTreeNode(
810 TreeNode<TableAxisContext> node) {
811 TableHeaderNode headerNode = (TableHeaderNode) node;
812 if (headerNode.getHierarchy() == null) {
813 return TreeNodeCallback.CONTINUE;
814 }
815
816 Hierarchy hierarchy = headerNode.getHierarchy();
817
818 if (headerNode.getProperty() == null) {
819 Integer span = spans.get(hierarchy);
820 if (span == null) {
821 span = 0;
822 }
823
824 span += headerNode.getRowSpan();
825 spans.put(headerNode.getHierarchy(), span);
826 } else {
827 List<Property> properties = propertyMap
828 .get(hierarchy);
829 if (properties == null) {
830 properties = new ArrayList<Property>();
831 propertyMap.put(hierarchy, properties);
832 }
833
834 properties.add(headerNode.getProperty());
835 }
836
837 return TreeNodeCallback.CONTINUE;
838 }
839 }, 0);
840
841 context.setAxis(Axis.ROWS);
842 context.setRowSpan(1);
843 context.setCellType(TITLE);
844
845 for (Hierarchy hierarchy : rowRoot.getReference().getHierarchies()) {
846 Integer span = spans.get(hierarchy);
847 if (span == null) {
848 span = 1;
849 }
850
851 context.setColSpan(span);
852 context.setHierarchy(hierarchy);
853
854 callback.startCell(context);
855 callback.renderCommands(context, getCommands(context));
856 callback.renderContent(context, getLabel(context));
857 callback.endCell(context);
858
859 context.setColIndex(context.getColumnIndex() + span);
860
861 List<Property> properties = propertyMap.get(hierarchy);
862 if (properties != null) {
863 for (Property property : properties) {
864 context.setColSpan(1);
865 context.setColIndex(context.getColumnIndex() + 1);
866 context.setProperty(property);
867
868 callback.startCell(context);
869 callback.renderCommands(context, getCommands(context));
870 callback.renderContent(context, getLabel(context));
871 callback.endCell(context);
872 }
873 }
874 }
875 } else if (renderLevelTitle) {
876 final Map<Integer, Level> levels = new HashMap<Integer, Level>();
877 final Map<Integer, Property> properties = new HashMap<Integer, Property>();
878
879 rowRoot.walkChildren(new TreeNodeCallback<TableAxisContext>() {
880
881 @Override
882 public int handleTreeNode(TreeNode<TableAxisContext> node) {
883 TableHeaderNode headerNode = (TableHeaderNode) node;
884 int colIndex = headerNode.getRowIndex() - 1;
885
886 if (headerNode.getMember() != null
887 && !levels.containsKey(colIndex)) {
888 levels.put(colIndex, headerNode.getMember().getLevel());
889 }
890
891 if (headerNode.getProperty() != null
892 && !properties.containsKey(colIndex)) {
893 properties.put(colIndex, headerNode.getProperty());
894 }
895
896 return TreeNodeCallback.CONTINUE;
897 }
898 });
899
900 context.setAxis(Axis.ROWS);
901 context.setColSpan(1);
902 context.setRowSpan(1);
903 context.setCellType(TITLE);
904
905 for (int i = 0; i < context.getRowHeaderCount(); i++) {
906 context.setColIndex(i);
907
908 Level level = levels.get(i);
909
910 if (level == null) {
911 context.setHierarchy(null);
912 context.setLevel(null);
913 } else {
914 context.setHierarchy(level.getHierarchy());
915 context.setLevel(level);
916 }
917
918 context.setProperty(properties.get(i));
919
920 callback.startCell(context);
921 callback.renderCommands(context, getCommands(context));
922 callback.renderContent(context, getLabel(context));
923 callback.endCell(context);
924 }
925 }
926
927 context.setHierarchy(null);
928 }
929
930
931
932
933
934
935 protected TableHeaderNode createAxisTree(PivotModel model, Axis axis) {
936 List<CellSetAxis> axes = model.getCellSet().getAxes();
937
938 if (axes.size() < 2) {
939 return null;
940 }
941
942 CellSetAxis cellSetAxis = axes.get(axis.axisOrdinal());
943
944 List<Position> positions = cellSetAxis.getPositions();
945 if (positions == null || positions.isEmpty()) {
946 return null;
947 }
948
949 List<Hierarchy> hierarchies = new ArrayList<Hierarchy>();
950
951 List<Aggregator> aggregators = new ArrayList<Aggregator>();
952 List<Aggregator> hierarchyAggregators = new ArrayList<Aggregator>();
953 List<Aggregator> memberAggregators = new ArrayList<Aggregator>();
954
955 Map<AggregatorPosition, List<Aggregator>> aggregatorMap = new HashMap<AggregatorPosition, List<Aggregator>>();
956 aggregatorMap.put(AggregatorPosition.Grand, aggregators);
957 aggregatorMap.put(AggregatorPosition.Hierarchy, hierarchyAggregators);
958 aggregatorMap.put(AggregatorPosition.Member, memberAggregators);
959
960 Map<Hierarchy, List<Level>> levelsMap = new HashMap<Hierarchy, List<Level>>();
961
962 Comparator<Level> levelComparator = new Comparator<Level>() {
963
964 @Override
965 public int compare(Level l1, Level l2) {
966 Integer d1 = l1.getDepth();
967 Integer d2 = l2.getDepth();
968 return d1.compareTo(d2);
969 }
970 };
971
972 boolean containsMeasure = false;
973
974 List<Member> firstMembers = positions.get(0).getMembers();
975
976 int index = 0;
977 for (Member member : firstMembers) {
978 if (member instanceof Measure) {
979 containsMeasure = true;
980 break;
981 }
982
983 index++;
984 }
985
986 AggregatorFactory aggregatorFactory = getAggregatorFactory();
987
988 List<String> aggregatorNames = null;
989 List<String> hierarchyAggregatorNames = null;
990 List<String> memberAggregatorNames = null;
991
992 if (aggregatorFactory != null
993 && (!containsMeasure || index == firstMembers.size() - 1)) {
994 aggregatorNames = getAggregators(axis, AggregatorPosition.Grand);
995 hierarchyAggregatorNames = getAggregators(axis,
996 AggregatorPosition.Hierarchy);
997 memberAggregatorNames = getAggregators(axis,
998 AggregatorPosition.Member);
999 }
1000
1001 if (aggregatorNames == null) {
1002 aggregatorNames = Collections.emptyList();
1003 }
1004
1005 if (hierarchyAggregatorNames == null) {
1006 hierarchyAggregatorNames = Collections.emptyList();
1007 }
1008
1009 if (memberAggregatorNames == null) {
1010 memberAggregatorNames = Collections.emptyList();
1011 }
1012
1013 TableAxisContext nodeContext = new TableAxisContext(axis, hierarchies,
1014 levelsMap, aggregatorMap, this);
1015
1016 TableHeaderNode axisRoot = new TableHeaderNode(nodeContext);
1017
1018 Set<Measure> grandTotalMeasures = new LinkedHashSet<Measure>();
1019 Set<Measure> totalMeasures = new LinkedHashSet<Measure>();
1020
1021 Map<Hierarchy, List<AggregationTarget>> memberParentMap = new HashMap<Hierarchy, List<AggregationTarget>>();
1022
1023 Position lastPosition = null;
1024
1025 for (Position position : positions) {
1026 TableHeaderNode lastChild = null;
1027
1028 List<Member> members = position.getMembers();
1029
1030 int memberCount = members.size();
1031
1032 for (int i = memberCount - 1; i >= 0; i--) {
1033 Member member = members.get(i);
1034
1035 if (member instanceof Measure) {
1036 Measure measure = (Measure) member;
1037
1038 if (!totalMeasures.contains(measure)) {
1039 totalMeasures.add(measure);
1040 }
1041
1042 if (!grandTotalMeasures.contains(measure)) {
1043 grandTotalMeasures.add(measure);
1044 }
1045 } else if (member != null && member.getDepth() > 0
1046 && nodeContext.getParentMember(member) == null) {
1047 member = new RaggedMemberWrapper(member, model.getCube());
1048 }
1049
1050 if (hierarchies.size() < memberCount) {
1051 hierarchies.add(0, member.getHierarchy());
1052 }
1053
1054 List<Level> levels = levelsMap.get(member.getHierarchy());
1055
1056 if (levels == null) {
1057 levels = new ArrayList<Level>();
1058 levelsMap.put(member.getHierarchy(), levels);
1059 }
1060
1061 if (!levels.contains(member.getLevel())) {
1062 levels.add(0, member.getLevel());
1063 Collections.sort(levels, levelComparator);
1064 }
1065
1066 TableHeaderNode childNode = new TableHeaderNode(nodeContext);
1067
1068 childNode.setMember(member);
1069 childNode.setHierarchy(member.getHierarchy());
1070 childNode.setPosition(position);
1071
1072 if (lastChild != null) {
1073 childNode.addChild(lastChild);
1074 }
1075
1076 lastChild = childNode;
1077
1078 if (!hierarchyAggregatorNames.isEmpty() && lastPosition != null) {
1079 int start = memberCount - 1;
1080
1081 if (containsMeasure) {
1082 start--;
1083 }
1084
1085 if (i < start) {
1086 Member lastMember = lastPosition.getMembers().get(i);
1087
1088 if (OlapUtils.equals(lastMember.getHierarchy(),
1089 member.getHierarchy())
1090 && !OlapUtils.equals(lastMember, member)
1091 || !OlapUtils.equals(position, lastPosition, i)) {
1092 for (String aggregatorName : hierarchyAggregatorNames) {
1093 createAggregators(
1094 aggregatorName,
1095 nodeContext,
1096 hierarchyAggregators,
1097 axisRoot,
1098 null,
1099 lastPosition.getMembers().subList(0,
1100 i + 1), totalMeasures);
1101 }
1102 }
1103 }
1104 }
1105
1106 if (!memberAggregatorNames.isEmpty()) {
1107 List<AggregationTarget> memberParents = memberParentMap
1108 .get(member.getHierarchy());
1109
1110 if (memberParents == null) {
1111 memberParents = new ArrayList<AggregationTarget>();
1112 memberParentMap.put(member.getHierarchy(),
1113 memberParents);
1114 }
1115
1116 AggregationTarget lastSibling = null;
1117
1118 if (!memberParents.isEmpty()) {
1119 lastSibling = memberParents
1120 .get(memberParents.size() - 1);
1121 }
1122
1123 Member parentMember;
1124
1125 if (member instanceof RaggedMemberWrapper) {
1126 parentMember = ((RaggedMemberWrapper) member)
1127 .getTopMember();
1128 } else {
1129 parentMember = axisRoot.getReference().getParentMember(
1130 member);
1131 }
1132
1133 if (parentMember != null) {
1134 if (lastSibling == null
1135 || axisRoot.getReference()
1136 .getAncestorMembers(parentMember)
1137 .contains(lastSibling.getParent())) {
1138 memberParents.add(new AggregationTarget(
1139 parentMember, member.getLevel()));
1140 } else if (!OlapUtils.equals(parentMember,
1141 lastSibling.getParent())) {
1142 while (!memberParents.isEmpty()) {
1143 int lastIndex = memberParents.size() - 1;
1144
1145 AggregationTarget lastParent = memberParents
1146 .get(lastIndex);
1147
1148 if (OlapUtils.equals(lastParent.getParent(),
1149 parentMember)) {
1150 break;
1151 }
1152
1153 memberParents.remove(lastIndex);
1154
1155 List<Member> path = new ArrayList<Member>(
1156 lastPosition.getMembers().subList(0, i));
1157 path.add(lastParent.getParent());
1158
1159 Level parentLevel = lastParent.getParent()
1160 .getLevel();
1161 if (!levels.contains(parentLevel)) {
1162 levels.add(0, parentLevel);
1163 Collections.sort(levels, levelComparator);
1164 }
1165
1166 for (String aggregatorName : memberAggregatorNames) {
1167 createAggregators(aggregatorName,
1168 nodeContext, memberAggregators,
1169 axisRoot, lastParent.getLevel(),
1170 path, totalMeasures);
1171 }
1172 }
1173 }
1174 }
1175
1176 if (lastPosition != null
1177 && !OlapUtils.equals(position, lastPosition, i)) {
1178 Hierarchy hierarchy = nodeContext.getHierarchies().get(
1179 i);
1180
1181 Level rootLevel = nodeContext.getLevels(hierarchy).get(
1182 0);
1183
1184 for (AggregationTarget target : memberParents) {
1185 Member memberParent = target.getParent();
1186
1187 if (memberParent.getLevel().getDepth() < rootLevel
1188 .getDepth()) {
1189 continue;
1190 }
1191
1192 List<Member> path = new ArrayList<Member>(
1193 lastPosition.getMembers().subList(0, i));
1194 path.add(memberParent);
1195
1196 for (String aggregatorName : memberAggregatorNames) {
1197 createAggregators(aggregatorName, nodeContext,
1198 memberAggregators, axisRoot,
1199 target.getLevel(), path, totalMeasures);
1200 }
1201 }
1202
1203 memberParents.clear();
1204 }
1205 }
1206 }
1207
1208 if (lastChild != null) {
1209 axisRoot.addChild(lastChild);
1210 }
1211
1212 lastPosition = position;
1213 }
1214
1215 if (lastPosition != null) {
1216 int memberCount = lastPosition.getMembers().size();
1217
1218 int start = memberCount - 1;
1219
1220 if (containsMeasure) {
1221 start--;
1222 }
1223
1224 for (int i = start; i >= 0; i--) {
1225 if (!memberAggregatorNames.isEmpty()) {
1226 Hierarchy hierarchy = nodeContext.getHierarchies().get(i);
1227
1228 Level rootLevel = nodeContext.getLevels(hierarchy).get(0);
1229
1230 List<AggregationTarget> memberParents = memberParentMap
1231 .get(hierarchy);
1232
1233 for (AggregationTarget target : memberParents) {
1234 Member member = target.getParent();
1235
1236 if (member.getLevel().getDepth() < rootLevel.getDepth()) {
1237 continue;
1238 }
1239
1240 List<Member> path = new ArrayList<Member>(lastPosition
1241 .getMembers().subList(0, i));
1242 path.add(member);
1243
1244 for (String aggregatorName : memberAggregatorNames) {
1245 createAggregators(aggregatorName, nodeContext,
1246 memberAggregators, axisRoot,
1247 target.getLevel(), path, totalMeasures);
1248 }
1249 }
1250 }
1251
1252 if (!hierarchyAggregatorNames.isEmpty()) {
1253 for (String aggregatorName : hierarchyAggregatorNames) {
1254 createAggregators(aggregatorName, nodeContext,
1255 hierarchyAggregators, axisRoot, null,
1256 lastPosition.getMembers().subList(0, i),
1257 totalMeasures);
1258 }
1259 }
1260 }
1261
1262 if (!aggregatorNames.isEmpty()) {
1263 List<Member> members = Collections.emptyList();
1264
1265 for (String aggregatorName : aggregatorNames) {
1266 createAggregators(aggregatorName, nodeContext, aggregators,
1267 axisRoot, null, members, grandTotalMeasures);
1268 }
1269 }
1270 }
1271
1272 if (logger.isDebugEnabled()) {
1273 logger.debug("Original axis tree root for " + axis);
1274
1275 axisRoot.walkTree(new TreeNodeCallback<TableAxisContext>() {
1276
1277 @Override
1278 public int handleTreeNode(TreeNode<TableAxisContext> node) {
1279 String label = node.toString();
1280 logger.trace(StringUtils.leftPad(label, node.getLevel()
1281 + label.length(), ' '));
1282
1283 return CONTINUE;
1284 }
1285 });
1286 }
1287
1288 return axisRoot;
1289 }
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 private void createAggregators(String aggregatorName,
1301 TableAxisContext context, List<Aggregator> aggregators,
1302 TableHeaderNode axisRoot, Level level, List<Member> members,
1303 Set<Measure> measures) {
1304 AggregatorFactory factory = getAggregatorFactory();
1305
1306 if (measures.isEmpty()) {
1307 Aggregator aggregator = factory.createAggregator(aggregatorName,
1308 context.getAxis(), members, level, null);
1309 if (aggregator != null) {
1310 aggregators.add(aggregator);
1311
1312 TableHeaderNode aggNode = createAggregationNode(context,
1313 aggregator);
1314 axisRoot.addChild(aggNode);
1315 }
1316 } else {
1317 for (Measure measure : measures) {
1318 Aggregator aggregator = factory.createAggregator(
1319 aggregatorName, context.getAxis(), members, level,
1320 measure);
1321
1322 if (aggregator != null) {
1323 aggregators.add(aggregator);
1324
1325 TableHeaderNode aggNode = createAggregationNode(context,
1326 aggregator);
1327 axisRoot.addChild(aggNode);
1328 }
1329 }
1330 }
1331 }
1332
1333
1334
1335
1336
1337
1338 protected TableHeaderNode createAggregationNode(
1339 TableAxisContext nodeContext, Aggregator aggregator) {
1340 TableHeaderNode result = null;
1341
1342 TableHeaderNode parent = null;
1343
1344 List<Member> members = new ArrayList<Member>(aggregator.getMembers());
1345
1346 Position position = new AggregatePosition(members);
1347
1348 for (Member member : aggregator.getMembers()) {
1349 TableHeaderNode node = new TableHeaderNode(nodeContext);
1350
1351 node.setAggregation(true);
1352 node.setMember(member);
1353 node.setPosition(position);
1354 node.setHierarchy(member.getHierarchy());
1355
1356 if (result == null) {
1357 result = node;
1358 }
1359
1360 if (parent != null) {
1361 parent.addChild(node);
1362 }
1363
1364 parent = node;
1365 }
1366
1367 if (parent != null && aggregator.getLevel() != null
1368 && !getShowParentMembers()) {
1369 parent.setAggregator(aggregator);
1370 }
1371
1372 int index = Math.min(nodeContext.getHierarchies().size() - 1,
1373 members.size());
1374
1375 Hierarchy hierarchy;
1376
1377 if (aggregator.getLevel() == null) {
1378 hierarchy = nodeContext.getHierarchies().get(index);
1379 } else {
1380 hierarchy = aggregator.getLevel().getHierarchy();
1381 }
1382
1383 if (aggregator.getLevel() == null || getShowParentMembers()) {
1384 TableHeaderNode node = new TableHeaderNode(nodeContext);
1385 node.setAggregation(true);
1386 node.setAggregator(aggregator);
1387 node.setPosition(position);
1388 node.setHierarchy(hierarchy);
1389
1390 if (parent != null) {
1391 parent.addChild(node);
1392 }
1393
1394 if (result == null) {
1395 result = node;
1396 }
1397
1398 parent = node;
1399 }
1400
1401 Measure measure = aggregator.getMeasure();
1402
1403 if (measure != null) {
1404 TableHeaderNode measureNode = new TableHeaderNode(nodeContext);
1405
1406 measureNode.setAggregation(true);
1407 measureNode.setAggregator(aggregator);
1408 measureNode.setPosition(position);
1409 measureNode.setMember(measure);
1410 measureNode.setHierarchy(measure.getHierarchy());
1411
1412 parent.addChild(measureNode);
1413
1414 members.add(measure);
1415 }
1416
1417 return result;
1418 }
1419
1420
1421
1422
1423
1424
1425 protected void configureAxisTree(PivotModel model, Axis axis,
1426 TableHeaderNode node) {
1427 if (getShowDimensionTitle() && axis.equals(Axis.COLUMNS)) {
1428 node.addHierarhcyHeaders();
1429 }
1430
1431 if (getShowParentMembers()) {
1432 node.addParentMemberHeaders();
1433 }
1434
1435 if (!getHideSpans()) {
1436 node.mergeChildren();
1437 }
1438
1439 if (getPropertyCollector() != null) {
1440 node.addMemberProperties();
1441 }
1442
1443 if (logger.isDebugEnabled()) {
1444 logger.debug("Configured axis tree root for " + axis);
1445
1446 node.walkTree(new TreeNodeCallback<TableAxisContext>() {
1447
1448 @Override
1449 public int handleTreeNode(TreeNode<TableAxisContext> node) {
1450 String label = node.toString();
1451 logger.debug(StringUtils.leftPad(label, node.getLevel()
1452 + label.length(), ' '));
1453
1454 return CONTINUE;
1455 }
1456 });
1457 }
1458 }
1459
1460
1461
1462
1463
1464
1465 protected void invalidateAxisTree(PivotModel model, Axis axis,
1466 TableHeaderNode node) {
1467 node.walkChildren(new TreeNodeCallback<TableAxisContext>() {
1468
1469 @Override
1470 public int handleTreeNode(TreeNode<TableAxisContext> node) {
1471 TableHeaderNode headerNode = (TableHeaderNode) node;
1472 headerNode.clearCache();
1473 return TreeNodeCallback.CONTINUE;
1474 }
1475 });
1476 }
1477
1478 static class AggregationTarget {
1479
1480 private Member parent;
1481
1482 private Level level;
1483
1484
1485
1486
1487
1488 AggregationTarget(Member parent, Level level) {
1489 this.parent = parent;
1490 this.level = level;
1491 }
1492
1493
1494
1495
1496 public Member getParent() {
1497 return parent;
1498 }
1499
1500
1501
1502
1503 public Level getLevel() {
1504 return level;
1505 }
1506 }
1507
1508 static class AggregatePosition implements Position {
1509
1510 private List<Member> members;
1511
1512
1513
1514
1515 AggregatePosition(List<Member> members) {
1516 this.members = members;
1517 }
1518
1519
1520
1521
1522 @Override
1523 public List<Member> getMembers() {
1524 return members;
1525 }
1526
1527
1528
1529
1530 @Override
1531 public int getOrdinal() {
1532 return -1;
1533 }
1534
1535
1536
1537
1538 @Override
1539 public int hashCode() {
1540 return 31 + members.hashCode();
1541 }
1542
1543
1544
1545
1546 @Override
1547 public boolean equals(Object obj) {
1548 if (this == obj) {
1549 return true;
1550 } else if (obj == null) {
1551 return false;
1552 } else if (getClass() != obj.getClass()) {
1553 return false;
1554 }
1555
1556 AggregatePosition other = (AggregatePosition) obj;
1557 return members.equals(other.members);
1558 }
1559 }
1560
1561
1562
1563
1564
1565 protected void renderFilter(TableRenderContext context,
1566 TableRenderCallback callback) {
1567 ChangeSlicer transform = context.getModel().getTransform(
1568 ChangeSlicer.class);
1569
1570 List<Hierarchy> hierarchies = transform.getHierarchies();
1571 if (hierarchies.isEmpty()) {
1572 return;
1573 }
1574
1575 context.setAxis(Axis.FILTER);
1576 context.setHierarchy(null);
1577 context.setLevel(null);
1578 context.setMember(null);
1579 context.setCell(null);
1580 context.setAggregator(null);
1581 context.setPosition(null);
1582
1583 callback.startTable(context);
1584
1585 callback.startHeader(context);
1586 callback.endHeader(context);
1587
1588 int rowIndex = 0;
1589
1590 callback.startBody(context);
1591
1592 for (Hierarchy hierarchy : hierarchies) {
1593 List<Member> members = transform.getSlicer(hierarchy);
1594
1595 context.setHierarchy(hierarchy);
1596 context.setMember(null);
1597
1598 context.setRowIndex(rowIndex);
1599 context.setColIndex(0);
1600
1601 callback.startRow(context);
1602
1603 context.setCellType(LABEL);
1604
1605 context.setColSpan(1);
1606 context.setRowSpan(Math.max(1, members.size()));
1607
1608 callback.startCell(context);
1609 callback.renderCommands(context, getCommands(context));
1610 callback.renderContent(context, getLabel(context));
1611 callback.endCell(context);
1612
1613 boolean firstRow = true;
1614
1615 for (Member member : members) {
1616 if (firstRow) {
1617 firstRow = false;
1618 } else {
1619 callback.endRow(context);
1620
1621 context.setRowIndex(++rowIndex);
1622
1623 callback.startRow(context);
1624 }
1625
1626 context.setColIndex(1);
1627
1628 context.setMember(member);
1629 context.setLevel(member.getLevel());
1630
1631 context.setRowSpan(1);
1632
1633 context.setCellType(VALUE);
1634
1635 callback.startCell(context);
1636 callback.renderCommands(context, getCommands(context));
1637 callback.renderContent(context, getLabel(context));
1638 callback.endCell(context);
1639 }
1640
1641 callback.endRow(context);
1642
1643 rowIndex++;
1644 }
1645
1646 callback.endBody(context);
1647 callback.endTable(context);
1648 }
1649 }