1
2
3
4
5
6
7
8
9 package com.eyeq.pivot4j.ui.table;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Map;
17
18 import org.apache.commons.lang.ObjectUtils;
19 import org.olap4j.Axis;
20 import org.olap4j.OlapException;
21 import org.olap4j.Position;
22 import org.olap4j.metadata.Dimension.Type;
23 import org.olap4j.metadata.Hierarchy;
24 import org.olap4j.metadata.Level;
25 import org.olap4j.metadata.Measure;
26 import org.olap4j.metadata.Member;
27 import org.olap4j.metadata.Property;
28
29 import com.eyeq.pivot4j.PivotException;
30 import com.eyeq.pivot4j.ui.aggregator.Aggregator;
31 import com.eyeq.pivot4j.util.OlapUtils;
32 import com.eyeq.pivot4j.util.TreeNode;
33 import com.eyeq.pivot4j.util.TreeNodeCallback;
34
35 class TableHeaderNode extends TreeNode<TableAxisContext> {
36
37 private Position position;
38
39 private Member member;
40
41 private Property property;
42
43 private Hierarchy hierarchy;
44
45 private Integer colSpan;
46
47 private Integer rowSpan;
48
49 private Integer colIndex;
50
51 private Integer rowIndex;
52
53 private Integer maxRowIndex;
54
55 private Integer hierarchyDescendants;
56
57 private Integer memberChildren;
58
59 private boolean aggregation = false;
60
61 private Aggregator aggregator;
62
63
64
65
66 TableHeaderNode(TableAxisContext context) {
67 super(context);
68 }
69
70
71
72
73 public Position getPosition() {
74 return position;
75 }
76
77
78
79
80
81 public void setPosition(Position position) {
82 this.position = position;
83 }
84
85
86
87
88 public Member getMember() {
89 return member;
90 }
91
92
93
94
95
96 public void setMember(Member member) {
97 this.member = member;
98 }
99
100
101
102
103 public Level getMemberLevel() {
104 return member == null ? null : member.getLevel();
105 }
106
107
108
109
110 public Hierarchy getHierarchy() {
111 return hierarchy;
112 }
113
114
115
116
117
118 public void setHierarchy(Hierarchy hierarchy) {
119 this.hierarchy = hierarchy;
120 }
121
122
123
124
125 public Property getProperty() {
126 return property;
127 }
128
129
130
131
132
133 public void setProperty(Property property) {
134 this.property = property;
135 }
136
137 public void clearCache() {
138 this.colIndex = null;
139 this.rowIndex = null;
140 this.colSpan = null;
141 this.rowSpan = null;
142 this.maxRowIndex = null;
143 this.hierarchyDescendants = null;
144 this.memberChildren = null;
145 }
146
147 public int getHierarchyIndex() {
148 if (hierarchy == null) {
149 return -1;
150 }
151 return getReference().getHierarchies().indexOf(hierarchy);
152 }
153
154 public Level getRootLevel() {
155 int index = getHierarchyIndex();
156 if (index < 0) {
157 return null;
158 }
159
160 return getReference().getLevels(getHierarchy()).get(0);
161 }
162
163 public int getMaxRowIndex() {
164 if (maxRowIndex == null) {
165 if (getChildCount() == 0) {
166 this.maxRowIndex = getRowIndex();
167 } else {
168 this.maxRowIndex = 0;
169
170 for (TreeNode<TableAxisContext> child : getChildren()) {
171 TableHeaderNode nodeChild = (TableHeaderNode) child;
172 maxRowIndex = Math.max(maxRowIndex,
173 nodeChild.getMaxRowIndex());
174 }
175 }
176 }
177
178 return maxRowIndex;
179 }
180
181 void addHierarhcyHeaders() {
182 List<TreeNode<TableAxisContext>> children = new ArrayList<TreeNode<TableAxisContext>>(
183 getChildren());
184
185 for (TreeNode<TableAxisContext> child : children) {
186 TableHeaderNode nodeChild = (TableHeaderNode) child;
187
188 Hierarchy childHierarchy = nodeChild.getHierarchy();
189
190 if (childHierarchy != null
191 && !OlapUtils.equals(hierarchy, childHierarchy)) {
192 int index = getChildren().indexOf(child);
193
194 removeChild(child);
195
196 TableHeaderNode hierarchyNode = new TableHeaderNode(
197 getReference());
198 hierarchyNode.setHierarchy(childHierarchy);
199
200 addChild(index, hierarchyNode);
201 hierarchyNode.addChild(child);
202 }
203
204 nodeChild.addHierarhcyHeaders();
205 }
206 }
207
208 void addParentMemberHeaders() {
209 List<TreeNode<TableAxisContext>> children = new ArrayList<TreeNode<TableAxisContext>>(
210 getChildren());
211
212 for (TreeNode<TableAxisContext> child : children) {
213 TableHeaderNode nodeChild = (TableHeaderNode) child;
214
215 Member mem = nodeChild.getMember();
216 if (mem != null) {
217 int index = getChildren().indexOf(child);
218
219 removeChild(child);
220
221 TreeNode<TableAxisContext> childNode = child;
222
223 Member parent = mem;
224
225 while (parent != null) {
226 parent = getReference().getParentMember(parent);
227
228 if (parent == null) {
229 break;
230 }
231 TableHeaderNode parentNode = new TableHeaderNode(
232 getReference());
233 parentNode.setPosition(nodeChild.getPosition());
234 parentNode.setHierarchy(parent.getHierarchy());
235 parentNode.setMember(parent);
236 parentNode.addChild(childNode);
237
238 childNode = parentNode;
239 }
240
241 addChild(index, childNode);
242 }
243
244 nodeChild.addParentMemberHeaders();
245 }
246 }
247
248
249
250
251 void addMemberProperties() {
252 if (getReference().getAxis() != Axis.ROWS) {
253 return;
254 }
255
256 List<TreeNode<TableAxisContext>> children = null;
257
258 if (getMember() != null) {
259 List<Level> levels = getReference().getLevels(getHierarchy());
260
261 int index = levels.indexOf(getMember().getLevel());
262 int endIndex = getReference().getRenderer().getShowParentMembers() ? index + 1
263 : levels.size();
264
265 List<Level> lowerLevels = levels.subList(index, endIndex);
266
267 for (Level level : lowerLevels) {
268 List<Property> properties = getReference().getProperties(level);
269
270 if (!properties.isEmpty()) {
271 children = new ArrayList<TreeNode<TableAxisContext>>(
272 getChildren());
273 clear();
274
275 TableHeaderNode parentNode = this;
276
277 for (Property prop : properties) {
278 TableHeaderNode propertyNode = new TableHeaderNode(
279 getReference());
280 propertyNode.setPosition(position);
281 propertyNode.setHierarchy(getHierarchy());
282 propertyNode.setMember(getMember());
283 propertyNode.setProperty(prop);
284
285 parentNode.addChild(propertyNode);
286
287 parentNode = propertyNode;
288 }
289
290 for (TreeNode<TableAxisContext> child : children) {
291 parentNode.addChild(child);
292 }
293 }
294 }
295 }
296
297 if (children == null) {
298 children = getChildren();
299 }
300
301 for (TreeNode<TableAxisContext> child : children) {
302 TableHeaderNode nodeChild = (TableHeaderNode) child;
303 nodeChild.addMemberProperties();
304 }
305 }
306
307 void mergeChildren() {
308 List<TreeNode<TableAxisContext>> children = new ArrayList<TreeNode<TableAxisContext>>(
309 getChildren());
310
311 TableHeaderNode lastChild = null;
312
313 for (TreeNode<TableAxisContext> child : children) {
314 TableHeaderNode headerNode = (TableHeaderNode) child;
315
316 if (lastChild == null) {
317 lastChild = headerNode;
318 continue;
319 }
320
321 if (lastChild.canMergeWith(headerNode)) {
322 for (TreeNode<TableAxisContext> c : child.getChildren()) {
323 lastChild.addChild(c);
324 }
325
326 removeChild(child);
327 } else {
328 lastChild = headerNode;
329 }
330 }
331
332 for (TreeNode<TableAxisContext> child : getChildren()) {
333 TableHeaderNode headerNode = (TableHeaderNode) child;
334 headerNode.mergeChildren();
335 }
336 }
337
338
339
340
341
342 protected boolean canMergeWith(TableHeaderNode sibling) {
343 if (!OlapUtils.equals(hierarchy, sibling.getHierarchy())) {
344 return false;
345 }
346
347 if (!OlapUtils.equals(member, sibling.getMember())) {
348 return false;
349 }
350
351 if (!OlapUtils.equals(property, sibling.getProperty())) {
352 return false;
353 }
354
355 if (!OlapUtils.equals(property, sibling.getProperty())) {
356 return false;
357 }
358
359 if (aggregator == null) {
360 if (sibling.getAggregator() != null) {
361 return false;
362 }
363 } else {
364 Aggregator other = sibling.getAggregator();
365
366 if (other == null) {
367 return false;
368 }
369
370 if (!ObjectUtils.equals(aggregator.getName(), other.getName())) {
371 return false;
372 }
373
374 if (!ObjectUtils.equals(aggregator.getLevel(), other.getLevel())) {
375 return false;
376 }
377 }
378
379 return getRowSpan() == sibling.getRowSpan();
380 }
381
382 public int getColIndex() {
383 if (colIndex == null) {
384 if (getParent() == null) {
385 this.colIndex = 0;
386 return colIndex;
387 }
388
389 int index = ((TableHeaderNode) getParent()).getColIndex();
390 int childIndex = getParent().getChildren().indexOf(this);
391
392 for (int i = 0; i < childIndex; i++) {
393 index += getParent().getChildren().get(i).getWidth();
394 }
395
396 this.colIndex = index;
397 }
398
399 return colIndex;
400 }
401
402 public int getRowIndex() {
403 if (rowIndex == null) {
404 if (getParent() == null) {
405 this.rowIndex = 0;
406 return rowIndex;
407 } else {
408 TableHeaderNode headerParent = (TableHeaderNode) getParent();
409 this.rowIndex = headerParent.getRowIndex()
410 + headerParent.getRowSpan();
411 }
412 }
413 return rowIndex;
414 }
415
416 public int getColSpan() {
417 if (colSpan == null) {
418 this.colSpan = getWidth();
419 }
420
421 return colSpan;
422 }
423
424 public int getRowSpan() {
425 if (rowSpan == null) {
426 if ((member == null || property != null) && aggregator == null) {
427 this.rowSpan = 1;
428 return rowSpan;
429 }
430
431 final Map<Hierarchy, Integer> maxSpans = new HashMap<Hierarchy, Integer>(
432 getReference().getHierarchies().size());
433
434 if (aggregator != null) {
435 getRoot().walkTree(new TreeNodeCallback<TableAxisContext>() {
436
437 @Override
438 public int handleTreeNode(TreeNode<TableAxisContext> node) {
439 TableHeaderNode nodeChild = (TableHeaderNode) node;
440
441 if (nodeChild.getMember() == null) {
442 return TreeNodeCallback.CONTINUE;
443 } else {
444 Integer maxSpan = maxSpans.get(nodeChild
445 .getHierarchy());
446 if (maxSpan == null) {
447 maxSpan = 0;
448 }
449
450 int current = nodeChild.getHierarchyDescendents();
451
452 TableHeaderNode parent = nodeChild;
453 while (parent != null) {
454 parent = (TableHeaderNode) parent.getParent();
455
456 if (OlapUtils.equals(nodeChild.getHierarchy(),
457 parent.getHierarchy())
458 && parent.getMember() == null) {
459 current++;
460 } else {
461 break;
462 }
463 }
464
465 if (current > maxSpan) {
466 maxSpans.put(nodeChild.getHierarchy(), current);
467 }
468 }
469
470 return TreeNodeCallback.CONTINUE;
471 }
472 });
473 }
474
475 if (member == null) {
476 int totalSpans = 0;
477
478 for (Integer span : maxSpans.values()) {
479 totalSpans += span;
480 }
481
482 this.rowSpan = totalSpans;
483
484 if (hierarchy != null) {
485 for (Hierarchy hier : getReference().getHierarchies()) {
486 if (OlapUtils.equals(hier, hierarchy)) {
487 break;
488 }
489
490 this.rowSpan -= maxSpans.get(hier);
491 }
492
493 TableHeaderNode parent = this;
494 while (true) {
495 parent = (TableHeaderNode) parent.getParent();
496
497 if (parent == null
498 || !OlapUtils.equals(hierarchy,
499 parent.getHierarchy())) {
500 break;
501 } else {
502 this.rowSpan -= parent.getRowSpan();
503 }
504 }
505 }
506
507 TableHeaderNode child = this;
508 while (child != null) {
509 if (child.getChildCount() > 0) {
510 child = (TableHeaderNode) child.getChildren().get(0);
511 this.rowSpan -= child.getRowSpan();
512 } else {
513 break;
514 }
515 }
516 } else {
517 final int[] childSpan = new int[] { 0 };
518 final int[] maxSpan = new int[] { 0 };
519
520 walkChildrenAtColIndex(
521 new TreeNodeCallback<TableAxisContext>() {
522
523 @Override
524 public int handleTreeNode(
525 TreeNode<TableAxisContext> node) {
526 TableHeaderNode nodeChild = (TableHeaderNode) node;
527
528 if (nodeChild == TableHeaderNode.this) {
529 return TreeNodeCallback.CONTINUE;
530 } else if (OlapUtils.equals(hierarchy,
531 nodeChild.getHierarchy())) {
532 childSpan[0] += nodeChild.getRowSpan();
533 return TreeNodeCallback.CONTINUE;
534 } else {
535 return TreeNodeCallback.BREAK;
536 }
537 }
538 }, getColIndex());
539
540 getRoot().walkTree(new TreeNodeCallback<TableAxisContext>() {
541
542 @Override
543 public int handleTreeNode(TreeNode<TableAxisContext> node) {
544 TableHeaderNode nodeChild = (TableHeaderNode) node;
545
546 Level level = null;
547 Member nodeMember = nodeChild.getMember();
548
549 if (nodeChild == TableHeaderNode.this) {
550 return TreeNodeCallback.CONTINUE;
551 } else if (nodeMember != null) {
552 level = nodeMember.getLevel();
553 } else if (nodeChild.getAggregator() != null) {
554 level = nodeChild.getAggregator().getLevel();
555 }
556
557 if (OlapUtils.equals(member.getLevel(), level)) {
558 if (nodeMember != null
559 && (getReference().getAncestorMembers(
560 nodeMember).contains(member) || getReference()
561 .getAncestorMembers(member)
562 .contains(nodeMember))) {
563 return TreeNodeCallback.CONTINUE;
564 }
565
566 int span = nodeChild.getHierarchyDescendents();
567
568
569 if (aggregator == null
570 && nodeChild.getAggregator() != null
571 && member instanceof Measure
572 && getReference().getHierarchies().size() == 1) {
573 span++;
574 }
575
576 maxSpan[0] = Math.max(maxSpan[0], span);
577 }
578
579 return TreeNodeCallback.CONTINUE;
580 }
581 });
582
583 this.rowSpan = Math.max(1, maxSpan[0] - childSpan[0]);
584
585 if (aggregator != null) {
586 boolean child = false;
587
588 for (Hierarchy hier : getReference().getHierarchies()) {
589 if (OlapUtils.equals(hier, hierarchy)) {
590 child = true;
591 continue;
592 }
593
594 Type type;
595
596 try {
597 type = hier.getDimension().getDimensionType();
598 } catch (OlapException e) {
599 throw new PivotException(e);
600 }
601
602 if (child && type != Type.MEASURE) {
603 this.rowSpan += maxSpans.get(hier);
604 }
605 }
606 }
607 }
608 }
609
610 return rowSpan;
611 }
612
613
614
615
616 public boolean isAggregation() {
617 return aggregation;
618 }
619
620
621
622
623
624 public void setAggregation(boolean aggregation) {
625 this.aggregation = aggregation;
626 }
627
628
629
630
631 public Aggregator getAggregator() {
632 return aggregator;
633 }
634
635
636
637
638
639 public void setAggregator(Aggregator aggregator) {
640 this.aggregator = aggregator;
641 }
642
643 public TableHeaderNode getHierarchyRoot() {
644 TableHeaderNode parent = this;
645 while (true) {
646 TableHeaderNode node = (TableHeaderNode) parent.getParent();
647
648 if (node != null
649 && OlapUtils.equals(hierarchy, node.getHierarchy())) {
650 parent = node;
651 } else {
652 break;
653 }
654 }
655 return parent;
656 }
657
658 public int getHierarchyDescendents() {
659 if (member == null || getChildCount() == 0) {
660 return 1;
661 }
662
663 if (hierarchyDescendants == null) {
664 int height = 1;
665 for (TreeNode<TableAxisContext> child : getChildren()) {
666 TableHeaderNode nodeChild = (TableHeaderNode) child;
667 if (OlapUtils.equals(hierarchy, nodeChild.getHierarchy())) {
668 height = Math.max(height,
669 1 + nodeChild.getHierarchyDescendents());
670 }
671 }
672 this.hierarchyDescendants = height;
673 }
674
675 return hierarchyDescendants;
676 }
677
678 protected List<Member> getMemberPath() {
679 List<Member> path = new LinkedList<Member>();
680
681 TableHeaderNode node = (TableHeaderNode) getParent();
682
683 while (node != null) {
684 path.add(0, node.getMember());
685 node = (TableHeaderNode) node.getParent();
686 }
687
688 return path;
689 }
690
691
692
693
694
695
696 private static boolean isSubPath(List<Member> parentPath,
697 List<Member> childPath) {
698 Iterator<Member> it = childPath.iterator();
699 for (Member member : parentPath) {
700 if (!OlapUtils.equals(member, it.next())) {
701 return false;
702 }
703 }
704
705 return true;
706 }
707
708 public int getMemberChildren() {
709 if (member == null) {
710 return 0;
711 }
712
713 if (memberChildren == null) {
714 final List<Member> path = getMemberPath();
715
716 final int[] childCount = new int[] { 0 };
717
718 final int depth = member.getDepth();
719
720 getRoot().walkChildren(new TreeNodeCallback<TableAxisContext>() {
721
722 @Override
723 public int handleTreeNode(TreeNode<TableAxisContext> node) {
724 TableHeaderNode nodeChild = (TableHeaderNode) node;
725
726 if (node == TableHeaderNode.this) {
727 return TreeNodeCallback.CONTINUE;
728 }
729
730 if (OlapUtils.equals(hierarchy, nodeChild.getHierarchy())) {
731 List<Member> childPath = nodeChild.getMemberPath();
732
733 if (path.size() > childPath.size()
734 || !isSubPath(path, childPath)) {
735 return TreeNodeCallback.CONTINUE;
736 }
737
738 Member childMember = nodeChild.getMember();
739
740 if (childMember != null) {
741 int childDepth = childMember.getDepth();
742
743 if (getReference().getAncestorMembers(childMember)
744 .contains(member)) {
745 childCount[0]++;
746
747 return TreeNodeCallback.CONTINUE_SIBLING;
748 } else if (depth == childDepth) {
749 if (!OlapUtils.equals(childMember, member)) {
750 return TreeNodeCallback.CONTINUE_SIBLING;
751 }
752 } else if (depth < childDepth
753 || !getReference().getAncestorMembers(
754 member).contains(childMember)) {
755 return TreeNodeCallback.CONTINUE_SIBLING;
756 }
757 }
758 } else if (nodeChild.getMember() != null) {
759 TableHeaderNode parent = TableHeaderNode.this;
760
761 while (true) {
762 parent = (TableHeaderNode) parent.getParent();
763
764 if (parent == null) {
765 return TreeNodeCallback.CONTINUE_PARENT;
766 }
767
768 Member parentMember = parent.getMember();
769
770 if (OlapUtils.equals(parent.getHierarchy(),
771 nodeChild.getHierarchy())
772 && parentMember != null) {
773 if (OlapUtils.equals(parentMember,
774 nodeChild.getMember())
775 || getReference().getAncestorMembers(
776 parentMember).contains(
777 nodeChild.getMember())) {
778 return TreeNodeCallback.CONTINUE;
779 } else {
780 return TreeNodeCallback.CONTINUE_SIBLING;
781 }
782 }
783 }
784 }
785
786 return TreeNodeCallback.CONTINUE;
787 }
788 });
789
790 this.memberChildren = childCount[0];
791 }
792
793 return memberChildren;
794 }
795
796
797
798
799
800
801 public int walkChildrenAtRowIndex(
802 TreeNodeCallback<TableAxisContext> callbackHandler, int rowIndex) {
803 int code = 0;
804 for (TreeNode<TableAxisContext> child : getChildren()) {
805 TableHeaderNode nodeChild = (TableHeaderNode) child;
806 int childIndex = nodeChild.getRowIndex();
807
808 if (rowIndex == childIndex) {
809 code = callbackHandler.handleTreeNode(child);
810 if (code >= TreeNodeCallback.CONTINUE_PARENT) {
811 return code;
812 }
813 } else if (rowIndex > child.getLevel()) {
814 nodeChild.walkChildrenAtRowIndex(callbackHandler, rowIndex);
815 }
816 }
817 return code;
818 }
819
820
821
822
823
824 public TableHeaderNode getLeafNodeAtColIndex(int colIndex) {
825 if (getChildCount() == 0 && getColIndex() == colIndex) {
826 return this;
827 }
828
829 for (TreeNode<TableAxisContext> child : getChildren()) {
830 TableHeaderNode nodeChild = (TableHeaderNode) child;
831
832 int startIndex = nodeChild.getColIndex();
833 int endIndex = startIndex + nodeChild.getColSpan();
834
835 if (colIndex >= startIndex && colIndex < endIndex) {
836 return nodeChild.getLeafNodeAtColIndex(colIndex);
837 } else if (endIndex > colIndex) {
838 break;
839 }
840 }
841 return null;
842 }
843
844
845
846
847
848 public int walkChildrenAtColIndex(
849 TreeNodeCallback<TableAxisContext> callbackHandler, int colIndex) {
850 int code = 0;
851
852 if (getColIndex() == colIndex) {
853 code = callbackHandler.handleTreeNode(this);
854 if (code >= TreeNodeCallback.CONTINUE_PARENT) {
855 return code;
856 }
857 }
858
859 for (TreeNode<TableAxisContext> child : getChildren()) {
860 TableHeaderNode nodeChild = (TableHeaderNode) child;
861 int startIndex = nodeChild.getColIndex();
862 int endIndex = startIndex + nodeChild.getColSpan();
863
864 if (colIndex < startIndex) {
865 code = TreeNodeCallback.CONTINUE_SIBLING;
866 } else if (colIndex >= endIndex) {
867 code = TreeNodeCallback.CONTINUE_PARENT;
868 } else {
869 code = nodeChild.walkChildrenAtColIndex(callbackHandler,
870 colIndex);
871 break;
872 }
873 }
874
875 return code;
876 }
877
878
879
880
881 @Override
882 public String toString() {
883 if (member != null) {
884 return member.getCaption();
885 } else if (hierarchy != null) {
886 return hierarchy.getCaption();
887 } else {
888 return getReference().getAxis().name();
889 }
890 }
891 }