KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > jpivot > olap > query > Quax


1 /*
2  * ====================================================================
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) 2003-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.jpivot.olap.query;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Arrays JavaDoc;
17 import java.util.Collection JavaDoc;
18 import java.util.Collections JavaDoc;
19 import java.util.Comparator JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.log4j.Logger;
26
27 import com.tonbeller.jpivot.olap.model.Dimension;
28 import com.tonbeller.jpivot.olap.model.Hierarchy;
29 import com.tonbeller.jpivot.olap.model.Level;
30 import com.tonbeller.jpivot.olap.model.Member;
31 import com.tonbeller.jpivot.olap.model.Position;
32 import com.tonbeller.jpivot.olap.navi.CalcSet;
33 import com.tonbeller.jpivot.util.TreeNode;
34 import com.tonbeller.jpivot.util.TreeNodeCallback;
35
36 public class Quax {
37
38   static Logger logger = Logger.getLogger(Quax.class);
39
40   protected int nDimension;
41
42   private Hierarchy[] hiers;
43
44   // currently, we can handle the following Funcalls
45
// member.children, member.descendants, level.members
46
// other funcalls are "unknown functions"
47
private boolean[] containsUF;
48   private List JavaDoc[] ufMemberLists; // if there are unknonwn functions
49
// private UnknownFunction[] unknownFunctions;
50
protected TreeNode posTreeRoot = null; // Position tree used in normal mode
51

52   private int ordinal; // ordinal of query axis, never changed by swap
53
private boolean qubonMode = false;
54   private boolean hierarchizeNeeded = false;
55
56   // if there are multiple hiers on this quax,
57
// "nHierExclude" hierarchies (from right to left)
58
// will *not* be included to the Hierarchize Function.
59
// So MDX like
60
// Crossjoin(Hierarchize(Dim1.A + Dim1.A.Children), {Measures.A.
61
// Measures.B})
62
// will be generated, so that the Measures are excluded from Hierarchize.
63
private int nHierExclude = 0;
64   private int generateMode = 0;
65   private int generateIndex = -1; // we handle generate for only 1 dimension
66
private Object JavaDoc expGenerate = null;
67   private Collection JavaDoc changeListeners = new ArrayList JavaDoc();
68   private QuaxUti uti;
69   private Map JavaDoc canExpandMemberMap = new HashMap JavaDoc();
70   private Map JavaDoc canExpandPosMap = new HashMap JavaDoc();
71   private Map JavaDoc canCollapseMemberMap = new HashMap JavaDoc();
72   private Map JavaDoc canCollapsePosMap = new HashMap JavaDoc();
73
74   /**
75    * c'tor
76    *
77    * @param ordinal
78    */

79   public Quax(int ordinal) {
80     this.ordinal = ordinal;
81     qubonMode = false;
82   }
83
84   /**
85    * register change listener
86    *
87    * @param listener
88    */

89   public void addChangeListener(QuaxChangeListener listener) {
90     changeListeners.add(listener);
91   }
92
93   /**
94    * unregister change listener
95    *
96    * @param listener
97    */

98   public void removeChangeListener(QuaxChangeListener listener) {
99     changeListeners.remove(listener);
100   }
101
102   /**
103    * handle change
104    *
105    * @param source
106    * Originator of the quax change
107    * @param changedMemberSet
108    * true if the memberset was changed by the navigator
109    */

110   public void changed(Object JavaDoc source, boolean changedMemberSet) {
111     for (Iterator JavaDoc iter = changeListeners.iterator(); iter.hasNext();) {
112       QuaxChangeListener listener = (QuaxChangeListener) iter.next();
113       listener.quaxChanged(this, source, changedMemberSet);
114     }
115     canExpandMemberMap.clear();
116     canExpandPosMap.clear();
117     canCollapseMemberMap.clear();
118     canCollapsePosMap.clear();
119
120   }
121
122   /**
123    * Initialize quax from result positions
124    *
125    * @param positions
126    */

127   public void init(List JavaDoc positions) {
128     Member[][] aPosMem;
129     int nDimension = 0;
130     hierarchizeNeeded = false;
131     nHierExclude = 0;
132     qubonMode = true;
133
134     if (positions.size() == 0) {
135       // the axis does not have any positions
136
aPosMem = new Member[0][0];
137       setHiers(new Hierarchy[0]);
138       setHiers(hiers);
139       return;
140     } else {
141       nDimension = ((Position) positions.get(0)).getMembers().length;
142       aPosMem = new Member[positions.size()][nDimension];
143
144       int j = 0;
145       PositionLoop: for (Iterator JavaDoc iter = positions.iterator(); iter.hasNext();) {
146         Position pos = (Position) iter.next();
147         aPosMem[j++] = pos.getMembers();
148       }
149     }
150     Hierarchy[] hiers = new Hierarchy[nDimension];
151     for (int j = 0; j < hiers.length; j++) {
152       Member m = aPosMem[0][j];
153       hiers[j] = m.getLevel().getHierarchy();
154     }
155     setHiers(hiers);
156     initPositions(aPosMem);
157
158     // initialize the dimension flags
159
// if there is only one set node per dimension,
160
// we are in qubon mode
161
posTreeRoot.walkTree(new TreeNodeCallback() {
162
163       /**
164        * callback check qubon mode
165        */

166       public int handleTreeNode(TreeNode node) {
167         int iDim = node.getLevel();
168
169         if (iDim == Quax.this.nDimension)
170           return TreeNodeCallback.BREAK; // bottom reached
171

172         if (node.getChildren().size() == 1) {
173           return TreeNodeCallback.CONTINUE; // continue next level
174
} else {
175           // more than one child - break out
176
Quax.this.qubonMode = false;
177           return TreeNodeCallback.BREAK;
178         }
179       }
180     });
181
182     if (qubonMode)
183       nHierExclude = nDimension - 1; // nothing hierarchized
184

185   }
186
187   /**
188    * Initialize position member arrays after first result gotten
189    *
190    * @param aPosMemStart
191    */

192   private void initPositions(Member[][] aPosMemStart) {
193     // no positions - no tree
194
if (aPosMemStart.length == 0) {
195       posTreeRoot = null;
196       return;
197     }
198
199     // before the position tree is created,
200
// we want to hierarchize
201
/*
202      * if (nDimension > 1) hierarchizePositions(aPosMemStart);
203      */

204
205     // init position tree
206
posTreeRoot = new TreeNode(null); // root
207
int iEnd = addToPosTree(aPosMemStart, 0, aPosMemStart.length, 0, posTreeRoot);
208     while (iEnd < aPosMemStart.length) {
209       iEnd = addToPosTree(aPosMemStart, iEnd, aPosMemStart.length, 0, posTreeRoot);
210     }
211
212     // try to factor out the members of the last dimension
213
posTreeRoot.walkTree(new TreeNodeCallback() {
214
215       /**
216        * callback create member set for last dimension
217        */

218       public int handleTreeNode(TreeNode node) {
219         int iDim1 = node.getLevel();
220
221         if (iDim1 == Quax.this.nDimension - 1) {
222           if (node.getChildren().size() <= 1)
223             return TreeNodeCallback.CONTINUE_SIBLING; // continue
224
// next
225
// sibling
226
// more than one child in last dimension
227
// create a single set function node
228
Object JavaDoc[] memArray = new Object JavaDoc[node.getChildren().size()];
229           int i = 0;
230           for (Iterator JavaDoc iter = node.getChildren().iterator(); iter.hasNext();) {
231             TreeNode child = (TreeNode) iter.next();
232             memArray[i++] = child.getReference();
233           }
234           node.getChildren().clear();
235           Object JavaDoc oFun = uti.createFunCall("{}", memArray, QuaxUti.FUNTYPE_BRACES);
236           TreeNode newChild = new TreeNode(oFun);
237           node.addChildNode(newChild);
238           return TreeNodeCallback.CONTINUE_SIBLING; // continue next
239
// sibling
240
}
241         return TreeNodeCallback.CONTINUE;
242       }
243     });
244
245     containsUF = new boolean[nDimension]; // init false
246
ufMemberLists = new List JavaDoc[nDimension];
247
248     if (logger.isDebugEnabled())
249       logger.debug("after initPositions " + this.toString());
250   }
251
252   /**
253    * add members of dimension to tree recursively
254    *
255    * @param aPosMem
256    * positon member array
257    * @param iStartPos
258    * start position for this dimension
259    * @param iEndPos
260    * start position for this dimension
261    * @param iDim
262    * index of this dimension
263    * @param parentNode
264    * parent node (previous dimension)
265    * @return index of position where the member of this dimension changes
266    */

267   protected int addToPosTree(Member[][] aPosMem, int iStartPos, int iEndPos, int iDim,
268       TreeNode parentNode) {
269     Member currentOfDim = aPosMem[iStartPos][iDim];
270     Object JavaDoc o = uti.objForMember(currentOfDim);
271     TreeNode newNode = new TreeNode(o);
272     parentNode.addChildNode(newNode);
273
274     // check range where member of this dimension is constant
275
int iEndRange = iStartPos + 1;
276     for (; iEndRange < iEndPos; iEndRange++) {
277       if (aPosMem[iEndRange][iDim] != aPosMem[iStartPos][iDim])
278         break;
279     }
280     int nextDim = iDim + 1;
281     if (nextDim < nDimension) {
282       int iEndChild = addToPosTree(aPosMem, iStartPos, iEndRange, nextDim, newNode);
283       while (iEndChild < iEndRange) {
284         iEndChild = addToPosTree(aPosMem, iEndChild, iEndRange, nextDim, newNode);
285       }
286     }
287     return iEndRange;
288   }
289
290   /**
291    * find out, whether axis contains dimension
292    *
293    * @param dim
294    * @return index of dimension, -1 if not there
295    */

296   public int dimIdx(Dimension dim) {
297     if (hiers == null || hiers.length == 0)
298       return -1; // quax was not initialized yet
299
for (int i = 0; i < hiers.length; i++) {
300       if (hiers[i].getDimension().equals(dim))
301         return i;
302     }
303     return -1;
304   }
305
306   /**
307    * regenerate the position tree as crossjoin between sets
308    *
309    * @param hiersChanged
310    * indicates that the hierarchies were changed
311    */

312   public void regeneratePosTree(Object JavaDoc[] sets, boolean hiersChanged) {
313     if (hiersChanged) {
314       nDimension = sets.length;
315       hiers = new Hierarchy[nDimension];
316       for (int i = 0; i < nDimension; i++) {
317         try {
318           hiers[i] = uti.hierForExp(sets[i]);
319         } catch (CannotHandleException e) {
320           logger.fatal("could not determine Hierarchy for set");
321           logger.fatal(e);
322           throw new IllegalArgumentException JavaDoc(e.getMessage());
323         }
324       }
325
326       containsUF = new boolean[nDimension]; // init false
327
ufMemberLists = new List JavaDoc[nDimension];
328       generateIndex = 0;
329       generateMode = 0;
330     }
331     if (posTreeRoot == null)
332       return;
333     posTreeRoot.getChildren().clear();
334     TreeNode current = posTreeRoot;
335     // it would be fine, if we could get rid of an existing Hierarchize
336
// - but this is not easy to decide.
337
// we will not do it, if there is a "children" function call
338
// not on the highest Level. This indicates that we have drilled
339
// down any member.
340
nHierExclude = 0;
341     int nChildrenFound = 0;
342     boolean childrenFound = false;
343     for (int i = 0; i < nDimension; i++) {
344       TreeNode newNode;
345       if (sets[i] instanceof SetExp) {
346         SetExp setx = (SetExp) sets[i];
347         newNode = new TreeNode(setx.getOExp());
348         int mode = setx.getMode();
349         if (mode > 0) {
350           generateMode = mode;
351           generateIndex = i;
352           expGenerate = setx.getOExp();
353         }
354       } else {
355         // can we remove an existing "hierarchize needed"?
356
boolean bChildrenFound = findChildrenCall(sets[i], 0);
357         if (bChildrenFound) {
358           childrenFound = true;
359           nChildrenFound = i + 1;
360         }
361
362         newNode = new TreeNode(sets[i]);
363         if (generateIndex == i && generateMode == CalcSet.STICKY) {
364           // there was a sticky generate on this hier
365
// reset, if set expression is different now
366
if (!sets[i].equals(expGenerate))
367             resetGenerate();
368         }
369       }
370       current.addChildNode(newNode);
371       current = newNode;
372       if (!uti.canHandle(newNode.getReference())) {
373         // indicate that dimension i contains an unknown function,
374
// which cannot be handled in some cases.
375
// this will cause the member list of this dimension to be stored
376
containsUF[i] = true;
377       }
378     }
379     qubonMode = true;
380     nHierExclude = nDimension - nChildrenFound;
381
382     if (!childrenFound)
383       hierarchizeNeeded = false;
384   }
385
386   /**
387    * recursively find "children" Funcall
388    */

389   private boolean findChildrenCall(Object JavaDoc oExp, int level) {
390     if (!uti.isFunCall(oExp))
391       return false; // member or level or ...
392
if (level > 0 && uti.isFunCallTo(oExp, "children"))
393       return true;
394     int nArgs = uti.funCallArgCount(oExp);
395     for (int i = 0; i < nArgs; i++) {
396       if (findChildrenCall(uti.funCallArg(oExp, i), level + 1))
397         return true;
398     }
399     return false;
400   }
401
402   // ==========
403
// Expand
404
// ==========
405

406   /**
407    * check, whether a member in a specific position path can be expanded
408    *
409    * @param pathMembers
410    * position path to be expanded
411    */

412   public boolean canExpand(Member[] pathMembers) {
413     int iDim = pathMembers.length - 1;
414
415     // we only allow expand / collapse for a dimension
416
// left of a "sticky topcount"
417
if (!allowNavigate(iDim, false))
418       return false;
419
420     // first check the cache
421
List JavaDoc li = Arrays.asList(pathMembers);
422     if (canExpandPosMap.containsKey(li)) {
423       Boolean JavaDoc bCanExpand = (Boolean JavaDoc) canExpandPosMap.get(li);
424       return bCanExpand.booleanValue();
425     }
426
427     // loop over Position Tree
428
// reject expansion, if the axis already contains child-positions
429
boolean childFound = checkChildPosition(pathMembers);
430
431     // cache the result
432
Boolean JavaDoc bool = new Boolean JavaDoc(!childFound);
433     canExpandPosMap.put(li, bool);
434
435     return !childFound;
436   }
437
438   /**
439    * expand position path
440    *
441    * @param mPath
442    */

443   public void expand(Member[] mPath) {
444
445     if (qubonMode) {
446       resolveUnions();
447       if (logger.isDebugEnabled()) {
448         logger.debug("expand after resolveUnions " + this.toString());
449       }
450     }
451
452     int iDim = mPath.length - 1;
453
454     // update the position member tree
455
// assume mPath = (Product.Drink,Time.2003,Customers.USA)
456
// 1. find the node N1 for (Product.Drink,Time.2003)
457
// 2. add the child node Customers.USA.Children to the node N1
458
//
459
// if the node N1 for (Product.Drink,Time.2003) was not found:
460
// we look for a matching node and find for instance
461
// node N2 = (Product.AllProducts.Children,Time.2003)
462
// here, we cannot append Customers.USA.Children as a child node.
463
// we add a new branch
464
// (Product.Drink,Time.2003,Customers.USA.Children) to the tree.
465

466     TreeNode bestNode = findBestNode(mPath);
467     int bestNodeIndex = bestNode.getLevel() - 1;
468
469     // add branch at startNode
470
// example
471
// dimensions: Product,MaritalStatus,Gender,Customer
472
// mPath to Drill Down = (Product.AllProducts, MaritalStatus.M,
473
// Gender.AllGender)
474
// MaritalStatus.AllMaritalStatus was drilled down so best match is
475
// (Product.AllProducts)
476
// add the branch from MaritalStatus to this node giving
477
// (Product.AllProducts,MaritalStatus.M,Gender.AllGender.children)
478
// for the Customer Dimension, add all nodes matching
479
// (Product.AllProducts, MaritalStatus.M, Gender.AllGender, * )
480

481     List JavaDoc tailNodeList;
482     if (mPath.length < nDimension) {
483       tailNodeList = collectTailNodes(posTreeRoot, mPath);
484     } else {
485       tailNodeList = Collections.EMPTY_LIST;
486     }
487
488     TreeNode newNode;
489     Object JavaDoc oMember = uti.objForMember(mPath[iDim]);
490     Object JavaDoc fChildren = uti.createFunCall("Children", new Object JavaDoc[] { oMember},
491         QuaxUti.FUNTYPE_PROPERTY);
492     TreeNode parent = bestNode;
493
494     // if bestNode is matching mPath[iDim]
495
// we will add the children Funcall to its parent
496
// otherwise create path from bestNode to mPath[iDim-1] and
497
// add the children FunCall there
498
if (bestNodeIndex == iDim) {
499       parent = bestNode.getParent();
500     } else {
501       for (int i = bestNodeIndex + 1; i < mPath.length - 1; i++) {
502         oMember = uti.objForMember(mPath[i]);
503         newNode = new TreeNode(oMember);
504         parent.addChildNode(newNode);
505         parent = newNode;
506       }
507     }
508
509     // any dimension left and including iDim will *not* be excluded from
510
// hierarchize
511
int n = nDimension - iDim - 1;
512     if (n < nHierExclude)
513       nHierExclude = n;
514
515     newNode = new TreeNode(fChildren);
516     parent.addChildNode(newNode);
517     if (mPath.length < nDimension) {
518       for (Iterator JavaDoc iter = tailNodeList.iterator(); iter.hasNext();) {
519         TreeNode tailNode = (TreeNode) iter.next();
520         newNode.addChildNode(tailNode.deepCopy());
521       }
522     }
523
524     if (logger.isDebugEnabled()) {
525       logger.debug("after expand " + this.toString());
526     }
527
528     qubonMode = false;
529     hierarchizeNeeded = true;
530     changed(this, false);
531   }
532
533   /**
534    * check, whether a member can be expanded
535    *
536    * @param member
537    * member to be expanded
538    */

539   public boolean canExpand(Member member) {
540
541     // we only allow expand / collapse for a dimension
542
// left of a "sticky topcount"
543
if (!allowNavigate(member, false))
544       return false;
545
546     // first check the cache
547
if (canExpandMemberMap.containsKey(member)) {
548       Boolean JavaDoc bCanExpand = (Boolean JavaDoc) canExpandMemberMap.get(member);
549       return bCanExpand.booleanValue();
550     }
551
552     // loop over Position Tree
553
// reject expansion, if the axis already contains children of member
554
boolean b = !findMemberChild(member);
555
556     // cache the result
557
Boolean JavaDoc bool = new Boolean JavaDoc(b);
558     canExpandMemberMap.put(member, bool);
559
560     return b;
561   }
562
563   /**
564    * expand member all over position tree
565    *
566    * @param member
567    */

568   public void expand(final Member member) {
569
570     if (qubonMode) {
571       resolveUnions();
572       if (logger.isDebugEnabled()) {
573         logger.debug("expand after resolveUnions " + this.toString());
574       }
575     }
576
577     // old stuff, always hierarchize everything
578
nHierExclude = 0;
579
580     final int iDim = this.dimIdx(uti.dimForMember(member));
581     final List JavaDoc nodesForMember = new ArrayList JavaDoc();
582
583     // update the position member tree
584
// wherever we find monMember, expand it
585
// collect all nodes for monMember in workList
586
posTreeRoot.walkChildren(new TreeNodeCallback() {
587
588       /**
589        * callback find node matching member Path exactly
590        */

591       public int handleTreeNode(TreeNode node) {
592         int iDimNode = node.getLevel() - 1;
593         if (iDimNode < iDim)
594           return TreeNodeCallback.CONTINUE; // we are below iDim,
595
// don't care
596

597         // iDimNode == iDim
598
// node Exp must contain children of member[iDim]
599
Object JavaDoc oExp = node.getReference();
600         if (uti.isMember(oExp)) {
601           if (uti.equalMember(oExp, member))
602             nodesForMember.add(node);
603         } else {
604           // must be FunCall
605
if (isMemberInFunCall(oExp, member, iDim))
606             nodesForMember.add(node);
607         }
608         return TreeNodeCallback.CONTINUE_SIBLING; // continue next
609
// sibling
610
}
611     });
612
613     // add children of member to each node in list
614
Object JavaDoc oMember = uti.objForMember(member);
615     Object JavaDoc fChildren = uti.createFunCall("Children", new Object JavaDoc[] { oMember},
616         QuaxUti.FUNTYPE_PROPERTY);
617     for (Iterator JavaDoc iter = nodesForMember.iterator(); iter.hasNext();) {
618       TreeNode node = (TreeNode) iter.next();
619       TreeNode newNode = new TreeNode(fChildren);
620       for (Iterator JavaDoc iterator = node.getChildren().iterator(); iterator.hasNext();) {
621         TreeNode child = (TreeNode) iterator.next();
622         newNode.addChildNode(child.deepCopy());
623       }
624       TreeNode parent = node.getParent();
625       parent.addChildNode(newNode);
626     }
627
628     if (logger.isDebugEnabled()) {
629       logger.debug("after expand member " + this.toString());
630     }
631
632     hierarchizeNeeded = true;
633     changed(this, false);
634   }
635
636   // ==========
637
// Collapse
638
// ==========
639

640   /**
641    * check, whether a member path can be collapsed this is true if there is a child position path
642    *
643    * @param pathMembers
644    * position path to be collapsed
645    */

646   public boolean canCollapse(Member[] pathMembers) {
647
648     int iDim = pathMembers.length - 1;
649
650     // we only allow expand / collapse for a dimension
651
// left of a "sticky topcount"
652
if (!allowNavigate(iDim, false))
653       return false;
654
655     // first check the cache
656
List JavaDoc li = Arrays.asList(pathMembers);
657     if (canCollapsePosMap.containsKey(li)) {
658       Boolean JavaDoc bCanCollapse = (Boolean JavaDoc) canCollapsePosMap.get(li);
659       return bCanCollapse.booleanValue();
660     }
661
662     // loop over Position Tree
663
// collapse is possible, if the axis already contains child-positions
664
boolean childFound = checkChildPosition(pathMembers);
665
666     // cache the result
667
Boolean JavaDoc bool = new Boolean JavaDoc(childFound);
668     canCollapsePosMap.put(li, bool);
669
670     return childFound;
671   }
672
673   /**
674    * remove child positions of mPath from position tree
675    *
676    * @param mPath
677    * member path to be collapsed
678    */

679   public void collapse(final Member[] mPath) {
680
681     if (qubonMode) {
682       resolveUnions();
683       if (logger.isDebugEnabled()) {
684         logger.debug("collapse after resolveUnions " + this.toString());
685       }
686     }
687
688     final int iDim = mPath.length - 1;
689
690     // determine FunCall nodes to be split
691
final List JavaDoc[] splitLists = new List JavaDoc[mPath.length];
692     for (int i = 0; i < splitLists.length; i++) {
693       splitLists[i] = new ArrayList JavaDoc();
694     }
695
696     posTreeRoot.walkChildren(new TreeNodeCallback() {
697
698       /**
699        * callback Find child paths of member path. Collect FunCall nodes above in List. We have a
700        * list for any dimension, so that we can avoid dependency conflicts when we split the
701        * FunCalls.
702        */

703       public int handleTreeNode(TreeNode node) {
704         // check, whether this node matches mPath
705
Object JavaDoc oExp = node.getReference();
706         int idi = node.getLevel() - 1;
707         if (idi < iDim) {
708           if (uti.isMember(oExp)) {
709             if (uti.equalMember(oExp, mPath[idi]))
710               return TreeNodeCallback.CONTINUE;
711             else
712               return TreeNodeCallback.CONTINUE_SIBLING;
713           } else {
714             // Funcall
715
if (isMemberInFunCall(oExp, mPath[idi], idi))
716               return TreeNodeCallback.CONTINUE;
717             else
718               return TreeNodeCallback.CONTINUE_SIBLING;
719           }
720         }
721         //idi == iDim
722
// oExp *must* be descendant of mPath[iDim] to get deleted
723
boolean found = false;
724         if (uti.isMember(oExp)) {
725           // Member
726
if (uti.checkDescendantO(mPath[iDim], oExp)) {
727             found = true;
728           }
729         } else {
730           // FunCall
731
if (isChildOfMemberInFunCall(oExp, mPath[iDim], iDim))
732             found = true;
733         }
734
735         if (found) {
736           // add this node and all parent nodes, if they are funcalls,
737
// to split list
738
int level = node.getLevel();
739           TreeNode currentNode = node;
740           while (level > 0) {
741             Object JavaDoc o = currentNode.getReference();
742             if (!uti.isMember(o)) {
743               // Funcall
744
if (!splitLists[level - 1].contains(currentNode))
745                 splitLists[level - 1].add(currentNode);
746             }
747             currentNode = currentNode.getParent();
748             level = currentNode.getLevel();
749           }
750         }
751         return TreeNodeCallback.CONTINUE_SIBLING;
752       } // handleTreeNode
753
});
754
755     // split all FunCall nodes collected in worklist
756
// start with higher levels to avoid dependency conflicts
757
for (int i = splitLists.length - 1; i >= 0; i--) {
758       for (Iterator JavaDoc iter = splitLists[i].iterator(); iter.hasNext();) {
759         TreeNode n = (TreeNode) iter.next();
760         splitFunCall(n, mPath[i], i);
761       }
762     }
763
764     // remove child Paths of mPath from position tree
765
// collect nodes to be deleted
766
final List JavaDoc removeList = new ArrayList JavaDoc();
767     posTreeRoot.walkChildren(new TreeNodeCallback() {
768       /**
769        * callback remove child nodes of member path, first collect nodes in workList
770        */

771       public int handleTreeNode(TreeNode node) {
772         // check, whether this node matches mPath
773
Object JavaDoc oExp = node.getReference();
774         int idi = node.getLevel() - 1;
775         if (idi < iDim) {
776           if (uti.isMember(oExp)) {
777             if (uti.equalMember(oExp, mPath[idi]))
778               return TreeNodeCallback.CONTINUE;
779             else
780               return TreeNodeCallback.CONTINUE_SIBLING;
781           } else {
782             // FunCall
783
// cannot match as we just did the split of FunCalls
784
return TreeNodeCallback.CONTINUE_SIBLING;
785           }
786         } else if (idi == iDim) {
787           // *must* be descendant of mPath[iDim] to get deleted
788
if (!uti.isMember(oExp)) {
789             // FunCall
790
if (uti.isFunCallTo(oExp, "Children")) {
791               Object JavaDoc oMember = uti.funCallArg(oExp, 0);
792               if (uti.objForMember(mPath[iDim]).equals(oMember)
793                   || uti.checkDescendantO(mPath[iDim], oMember))
794                 removeList.add(node); // add to delete list
795
} else if (uti.isFunCallTo(oExp, "{}")) {
796               // set of members may be there as result of split,
797
// we will remove any descendant member from the set.
798
// if the set is empty thereafter, we will add the node
799
// to the remove list.
800
int nArgs = uti.funCallArgCount(oExp);
801               List JavaDoc removeMembers = new ArrayList JavaDoc();
802               for (int i = 0; i < nArgs; i++) {
803                 Object JavaDoc oSetMember = uti.funCallArg(oExp, i);
804                 if (uti.checkDescendantO(mPath[iDim], oSetMember)) {
805                   removeMembers.add(oSetMember);
806                 }
807               }
808               int nRemove = removeMembers.size();
809               if (nRemove == nArgs) {
810                 // all memers in set are descendants, remove the node
811
removeList.add(node); // add to delete list
812
} else if (nRemove > 0) {
813                 // remove descendant nodes from set
814
Object JavaDoc[] remaining = new Object JavaDoc[nArgs - nRemove];
815                 int j = 0;
816                 for (int i = 0; i < nArgs; i++) {
817                   Object JavaDoc oSetMember = uti.funCallArg(oExp, i);
818                   if (!removeMembers.contains(oSetMember))
819                     remaining[j++] = oSetMember;
820                 }
821                 if (remaining.length == 1) {
822                   node.setReference(remaining[0]); // single
823
// member
824
} else {
825                   Object JavaDoc newSet = uti.createFunCall("{}", remaining, QuaxUti.FUNTYPE_BRACES);
826                   node.setReference(newSet);
827                 }
828               }
829
830             } else if (uti.isFunCallTo(oExp, "Union")) {
831               // HHTASK Cleanup, always use removeDescendantsFromFunCall
832
Object JavaDoc oRemain = removeDescendantsFromFunCall(oExp, mPath[iDim], iDim);
833               if (oRemain == null)
834                 removeList.add(node);
835               else
836                 node.setReference(oRemain);
837             }
838             return TreeNodeCallback.CONTINUE_SIBLING;
839
840           } else if (uti.isMember(oExp)) {
841             if (uti.checkDescendantO(mPath[iDim], oExp))
842               removeList.add(node);
843           }
844           return TreeNodeCallback.CONTINUE_SIBLING;
845           // always break on level iDim, next sibling
846
} else {
847           // should never get here
848
logger.error("unexpected tree node level " + idi + " " + uti.memberString(mPath));
849         }
850         return TreeNodeCallback.BREAK;
851       } // handleTreeNode
852
});
853
854     // remove nodes collected in work list
855
for (Iterator JavaDoc iter = removeList.iterator(); iter.hasNext();) {
856       TreeNode nodeToRemove = (TreeNode) iter.next();
857       removePathToNode(nodeToRemove);
858     }
859
860     // any dimension left and including iDim will *not* be excluded from
861
// hierarchize
862
int n = nDimension - iDim - 1;
863     if (n < nHierExclude)
864       nHierExclude = n;
865
866     if (logger.isDebugEnabled()) {
867       logger.debug("after collapse " + this.toString());
868     }
869
870     changed(this, false);
871   } // collapse
872

873   /**
874    * check, whether a member path can be collapsed this is true if there is a child position path
875    *
876    * @param member
877    * position path to be collapsed
878    */

879   public boolean canCollapse(Member member) {
880
881     // we only allow expand / collapse for a dimension
882
// left of a "sticky topcount"
883
if (!allowNavigate(member, false))
884       return false;
885
886     // first check the cache
887
if (canCollapseMemberMap.containsKey(member)) {
888       Boolean JavaDoc bCanCollapse = (Boolean JavaDoc) canCollapseMemberMap.get(member);
889       return bCanCollapse.booleanValue();
890     }
891
892     // loop over Position Tree
893
// can collapse, if we find a descendant of member
894
boolean b = findMemberChild(member);
895
896     // cache the result
897
Boolean JavaDoc bool = new Boolean JavaDoc(b);
898     canCollapseMemberMap.put(member, bool);
899
900     return b;
901   }
902
903   /**
904    * remove child nodes of monMember
905    *
906    * @param member
907    * member to be collapsed
908    */

909   public void collapse(final Member member) {
910
911     if (qubonMode) {
912       resolveUnions();
913       if (logger.isDebugEnabled()) {
914         logger.debug("collapse member after resolveUnions " + this.toString());
915       }
916     }
917
918     final int iDim = this.dimIdx(uti.dimForMember(member));
919
920     final List JavaDoc nodesForMember = new ArrayList JavaDoc();
921
922     // update the position member tree
923
// wherever we find a descendant node of monMember, split and remove it
924
// collect all descendant nodes for monMember in workList
925
posTreeRoot.walkChildren(new TreeNodeCallback() {
926
927       /**
928        * callback find node matching member Path exactly
929        */

930       public int handleTreeNode(TreeNode node) {
931         int iDimNode = node.getLevel() - 1;
932         if (iDimNode < iDim)
933           return TreeNodeCallback.CONTINUE; // we are below iDim,
934
// don't care
935

936         // iDimNode == iDim
937
// node Exp must contain children of member[iDim]
938
Object JavaDoc oExp = node.getReference();
939         if (uti.isMember(oExp)) {
940           if (uti.checkDescendantO(member, oExp))
941             nodesForMember.add(node);
942         } else {
943           // must be FunCall
944

945           if (isDescendantOfMemberInFunCall(oExp, member, iDimNode))
946             nodesForMember.add(node);
947         }
948         return TreeNodeCallback.CONTINUE_SIBLING; // continue next
949
// sibling
950
}
951     });
952
953     for (Iterator JavaDoc iter = nodesForMember.iterator(); iter.hasNext();) {
954       TreeNode node = (TreeNode) iter.next();
955       Object JavaDoc oExp = node.getReference();
956       if (uti.isMember(oExp)) {
957         removePathToNode(node);
958       } else {
959         // FunCall
960
Object JavaDoc oComplement = removeDescendantsFromFunCall(oExp, member, iDim);
961         if (oComplement == null)
962           removePathToNode(node);
963         else
964           node.setReference(oComplement); // replace node object by complement
965
}
966     }
967     if (logger.isDebugEnabled()) {
968       logger.debug("after collapse " + this.toString());
969     }
970
971     changed(this, false);
972   } // collapse
973

974   // ==========
975
// Drill Down
976
// ==========
977

978   /**
979    * drill down is possible if there is no sticky generate
980    */

981   public boolean canDrillDown(Member member) {
982     return allowNavigate(member, true);
983   }
984
985   /**
986    * drill down
987    *
988    * @param member
989    * drill down member
990    */

991   public void drillDown(Member member) {
992     final int iDim = this.dimIdx(uti.dimForMember(member));
993
994     // collect the Exp's of all dimensions except iDim
995
Object JavaDoc[] sets = new Object JavaDoc[nDimension];
996     Object JavaDoc oMember = uti.objForMember(member);
997     Object JavaDoc fChildren = uti.createFunCall("Children", new Object JavaDoc[] { oMember},
998         QuaxUti.FUNTYPE_PROPERTY);
999     DimensionLoop: for (int i = 0; i < nDimension; i++) {
1000      if (i == iDim) {
1001        // replace drilldown dimension by member.children
1002
sets[i] = fChildren;
1003      } else {
1004        // generate exp for all nodes of this dimension
1005
sets[i] = genExpForDim(i);
1006      }
1007    } // DimensionLoop
1008

1009    // regenerate the position tree as crossjoin of sets
1010
regeneratePosTree(sets, false);
1011
1012    changed(this, false);
1013  }
1014
1015  // ==========
1016
// Drill Up
1017
// ==========
1018

1019  /**
1020   * drill up is possible if at least one member in the tree is not at the top level of this
1021   * hierarchy.
1022   */

1023  public boolean canDrillUp(Hierarchy hier) {
1024    final int iDim = this.dimIdx(hier.getDimension());
1025
1026    if (!allowNavigate(iDim, true))
1027      return false;
1028
1029    int ret = posTreeRoot.walkChildren(new TreeNodeCallback() {
1030
1031      /**
1032       * callback check for member of hierarchy not on top level
1033       */

1034      public int handleTreeNode(TreeNode node) {
1035        int iDimNode = node.getLevel() - 1;
1036        if (iDimNode < iDim)
1037          return TreeNodeCallback.CONTINUE;
1038        // iDimNode == workInt
1039
Object JavaDoc oExp = node.getReference();
1040        if (!uti.isMember(oExp)) {
1041          // FunCall
1042
if (isFunCallNotTopLevel(oExp, iDimNode))
1043            return TreeNodeCallback.BREAK; // got it
1044
else
1045            return TreeNodeCallback.CONTINUE_SIBLING;
1046        } else {
1047          // member
1048

1049          if (uti.levelDepthForMember(oExp) > 0)
1050            return TreeNodeCallback.BREAK; // got it
1051
else
1052            return TreeNodeCallback.CONTINUE_SIBLING;
1053        } // member
1054

1055      } // handlePositionTreeNode
1056
});
1057
1058    return (ret == TreeNodeCallback.BREAK);
1059  }
1060
1061  /**
1062   * drill down
1063   *
1064   * @param hier
1065   * drill down member
1066   */

1067  public void drillUp(Hierarchy hier) {
1068
1069    int iDim = dimIdx(hier.getDimension());
1070
1071    // collect the Exp's of all dimensions
1072
Object JavaDoc[] sets = new Object JavaDoc[nDimension];
1073
1074    DimensionLoop: for (int i = 0; i < nDimension; i++) {
1075      if (i == iDim) {
1076        // replace drillup dimension by drillup set
1077
sets[i] = drillupExp(iDim, hier);
1078      } else {
1079        sets[i] = genExpForDim(i);
1080      }
1081    } // DimensionLoop
1082

1083    // regenerate the position tree as crossjoin of sets
1084
regeneratePosTree(sets, false);
1085
1086    changed(this, false);
1087  }
1088
1089  // ==========
1090
// Query Axis Set
1091
// ==========
1092

1093  /**
1094   * MDX Generation
1095   * generate Exp from tree
1096   *
1097   * @return Exp for axis set
1098   */

1099  public Object JavaDoc genExp(boolean genHierarchize) {
1100
1101    if (generateMode > 0 && generateIndex > 0)
1102      return genGenerateExp(genHierarchize);
1103    else
1104      return genNormalExp(genHierarchize);
1105  }
1106
1107  /**
1108   * Normal MDX Generation - no Generate
1109   *
1110   * @return Exp for axis set
1111   */

1112  private Object JavaDoc genNormalExp(boolean genHierarchize) {
1113
1114    ExpGenerator expGenerator = new ExpGenerator(uti);
1115
1116    if (!genHierarchize) {
1117      // no Hierarchize
1118
expGenerator.init(posTreeRoot, hiers);
1119      Object JavaDoc exp = expGenerator.genExp();
1120      return exp;
1121    }
1122
1123    // do we need a special hierarchize ?
1124
// this will be true, if nHierExclude > 0
1125

1126    if (nHierExclude == 0) {
1127      // no special hierarchize needed
1128
expGenerator.init(posTreeRoot, hiers);
1129      Object JavaDoc exp = expGenerator.genExp();
1130      // Hierarchize around "everything"
1131
Object JavaDoc eHier = uti
1132          .createFunCall("Hierarchize", new Object JavaDoc[] { exp}, QuaxUti.FUNTYPE_FUNCTION);
1133      return eHier;
1134    }
1135
1136    // special hierarchize to be generated
1137
// the Qubon Mode Hierarchies are factored out,
1138
// as they consist only of a single set of members.
1139
// the left expression will be generated and then hierarchized,
1140
// *before* beeing crossjoined to the right Expression.
1141

1142    return genLeftRight(expGenerator, nDimension - nHierExclude, nHierExclude);
1143  }
1144
1145  /**
1146   * generate an expression
1147   * with hierarchize for the hierarchies < nHierExclude
1148   * without hierarchize for the hierarchies >= nHierExclude
1149   */

1150  private Object JavaDoc genLeftRight(ExpGenerator expGenerator, int nLeft, int nRight) {
1151    // generate left expression to be hierarchized
1152
Object JavaDoc leftExp = null;
1153    if (nLeft > 0) {
1154      TreeNode leftRoot = posTreeRoot.deepCopyPrune(nLeft);
1155      leftRoot.setReference(null);
1156      Hierarchy[] leftHiers = new Hierarchy[nLeft];
1157      for (int i = 0; i < leftHiers.length; i++) {
1158        leftHiers[i] = hiers[i];
1159      }
1160      expGenerator.init(leftRoot, leftHiers);
1161      leftExp = expGenerator.genExp();
1162      leftExp = uti.createFunCall("Hierarchize", new Object JavaDoc[] { leftExp}, QuaxUti.FUNTYPE_FUNCTION);
1163    }
1164
1165    // generate the right expression, not to be hierarchized
1166
Object JavaDoc rightExp = null;
1167    Hierarchy[] rightHiers = new Hierarchy[nRight];
1168    for (int i = 0; i < nRight; i++) {
1169      rightHiers[i] = hiers[nLeft + i];
1170    }
1171
1172    // go down to the first hier to be excluded from hierarchize
1173
// note: the subtree tree under any node of the hierarchy above
1174
// is always the same, so we can replicate any subtree under
1175
// a node of hierarchy nLeft-1
1176
TreeNode rightRoot = new TreeNode(null);
1177    TreeNode current = posTreeRoot;
1178    for (int i = 0; i < nLeft; i++) {
1179      List JavaDoc list = current.getChildren();
1180      current = (TreeNode) list.get(0);
1181    }
1182    List JavaDoc list = current.getChildren();
1183    for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();) {
1184      TreeNode node = (TreeNode) iter.next();
1185      TreeNode cnode = node.deepCopy();
1186      rightRoot.addChildNode(cnode);
1187    }
1188
1189    expGenerator.init(rightRoot, rightHiers);
1190    rightExp = expGenerator.genExp();
1191
1192    if (leftExp == null)
1193      return rightExp;
1194
1195    Object JavaDoc exp = uti.createFunCall("CrossJoin", new Object JavaDoc[] { leftExp, rightExp},
1196        QuaxUti.FUNTYPE_FUNCTION);
1197
1198    return exp;
1199  }
1200
1201  /**
1202   * MDX Generation for Generate
1203   *
1204   * @return Exp for axis set
1205   */

1206  private Object JavaDoc genGenerateExp(boolean genHierarchize) {
1207
1208    ExpGenerator expGenerator = new ExpGenerator(uti);
1209
1210    // Generate(GSet, FSet) to be generated
1211
// hierarchies >= generateIndex will not be "hierarchized"
1212
// we expect the hierarchies >= generateIndex to be excluded
1213
// from hierarchize.
1214
if (nDimension - generateIndex > nHierExclude)
1215      logger.warn("unexpected values: nHierExclude=" + nHierExclude + " generateIndex="
1216          + generateIndex);
1217
1218    // assume following situation:
1219
// 3 hierarchies
1220
// time - customers - product
1221
// we want top 5 customers, generated for each time member
1222
// 1. step
1223
// generate expression until customers (only time here), result = set1
1224
// if neccessary, put hierarchize around
1225
// 2. step
1226
// Generate(set1, Topcount(Crossjoin ({Time.Currentmember}, Set for Customers),
1227
// 5, condition))
1228
// result = set2
1229
// 3.step
1230
// append the tail nodes , here Product
1231
// Crossjoin(set2 , Product dimension nodes)
1232
//
1233
// 1. step left expression, potentially hierarchized
1234

1235    Object JavaDoc leftExp = null;
1236    // if nHierExclude > nDimension - generateIndex
1237
// and nHierExclude < nDimension
1238
// the the left expression (inside Generate) will be partly
1239
// hierarchized
1240
if (genHierarchize && nHierExclude > nDimension - generateIndex && nHierExclude < nDimension) {
1241      int nLeft = nDimension - nHierExclude;
1242      int nRight = generateIndex - nLeft;
1243      leftExp = genLeftRight(expGenerator, nLeft, nRight);
1244    } else {
1245      TreeNode leftRoot = posTreeRoot.deepCopyPrune(generateIndex);
1246      leftRoot.setReference(null);
1247      Hierarchy[] leftHiers = new Hierarchy[generateIndex];
1248      for (int i = 0; i < leftHiers.length; i++) {
1249        leftHiers[i] = hiers[i];
1250      }
1251      expGenerator.init(leftRoot, leftHiers);
1252      leftExp = expGenerator.genExp();
1253      if (genHierarchize)
1254        leftExp = uti.createFunCall("Hierarchize", new Object JavaDoc[] { leftExp},
1255            QuaxUti.FUNTYPE_FUNCTION);
1256    }
1257
1258    // 2. step Generate(set1, Topcount())
1259
TreeNode topCountNode = posTreeRoot;
1260    // top count node can be anything like topcount, bottomcount, filter
1261
for (int i = 0; i <= generateIndex; i++) {
1262      // the path to the topcount node at generateIndex does not matter
1263
List JavaDoc children = topCountNode.getChildren();
1264      topCountNode = (TreeNode) children.get(0);
1265    }
1266    Object JavaDoc topcount = topCountNode.getReference();
1267    // we have to replace the "set" of the topcount function
1268
Object JavaDoc origTopcountSet = uti.funCallArg(topcount, 0);
1269    // generate the Tuple of dimension.currentmember until generateIndex
1270
Object JavaDoc currentMembersTuple = genCurrentTuple();
1271    Object JavaDoc ocj = uti.createFunCall("Crossjoin",
1272        new Object JavaDoc[] { currentMembersTuple, origTopcountSet}, QuaxUti.FUNTYPE_FUNCTION);
1273    // replace the topcout original set
1274
String JavaDoc fun = uti.funCallName(topcount);
1275    int n = uti.funCallArgCount(topcount);
1276    Object JavaDoc[] args = new Object JavaDoc[n];
1277    for (int i = 1; i < n; i++) {
1278      args[i] = uti.funCallArg(topcount, i);
1279    }
1280    args[0] = ocj;
1281    Object JavaDoc newTopcount = uti.createFunCall(fun, args, QuaxUti.FUNTYPE_FUNCTION);
1282    Object JavaDoc oGenerate = uti.createFunCall("Generate", new Object JavaDoc[] { leftExp, newTopcount},
1283        QuaxUti.FUNTYPE_FUNCTION);
1284
1285    if (generateIndex + 1 == nDimension)
1286      return oGenerate;
1287
1288    // 3. step append the tail nodes
1289
// generate CrossJoin
1290
int nRight = nDimension - generateIndex - 1;
1291    Hierarchy[] rightHiers = new Hierarchy[nRight];
1292    for (int i = 1; i <= nRight; i++) {
1293      rightHiers[nRight - i] = hiers[nDimension - i];
1294    }
1295    TreeNode root = new TreeNode(null);
1296    List JavaDoc list = topCountNode.getChildren();
1297    for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();) {
1298      TreeNode node = (TreeNode) iter.next();
1299      root.addChildNode(node.deepCopy());
1300    }
1301    expGenerator.init(root, rightHiers);
1302    Object JavaDoc rightExp = expGenerator.genExp();
1303
1304    Object JavaDoc exp = uti.createFunCall("CrossJoin", new Object JavaDoc[] { oGenerate, rightExp},
1305        QuaxUti.FUNTYPE_FUNCTION);
1306    return exp;
1307  }
1308
1309  // ==========
1310
// private
1311
// ==========
1312

1313  /**
1314   * generate {(dim1.Currentmember, dim2.Currentmember, ... )}
1315   *
1316   * @return
1317   */

1318  private Object JavaDoc genCurrentTuple() {
1319    Object JavaDoc[] currentsOfDim = new Object JavaDoc[generateIndex];
1320    for (int i = 0; i < currentsOfDim.length; i++) {
1321      Dimension dim = hiers[i].getDimension();
1322      currentsOfDim[i] = uti.createFunCall("CurrentMember", new Object JavaDoc[] { uti.objForDim(dim)},
1323          QuaxUti.FUNTYPE_PROPERTY);
1324    }
1325    Object JavaDoc oTuple;
1326    if (generateIndex > 1)
1327      oTuple = uti.createFunCall("()", currentsOfDim, QuaxUti.FUNTYPE_TUPLE);
1328    else
1329      oTuple = currentsOfDim[0]; // just dimension.currentmember
1330
// generate set braces around tuple
1331
Object JavaDoc oSet = uti.createFunCall("{}", new Object JavaDoc[] { oTuple}, QuaxUti.FUNTYPE_BRACES);
1332    return oSet;
1333  }
1334
1335  /**
1336   * @return true if child position can be found
1337   */

1338  private boolean checkChildPosition(final Member[] mPath) {
1339
1340    int ret = posTreeRoot.walkChildren(new TreeNodeCallback() {
1341
1342      /**
1343       * callback find node matching member Path exactly
1344       */

1345      public int handleTreeNode(TreeNode node) {
1346        int iDim = mPath.length - 1;
1347        int iDimNode = node.getLevel() - 1;
1348        Object JavaDoc oExp = node.getReference();
1349        if (iDimNode < iDim) {
1350          // node Exp must match member[iDim]
1351
if (uti.isMember(oExp)) {
1352            if (uti.equalMember(oExp, mPath[iDimNode]))
1353              return TreeNodeCallback.CONTINUE;
1354            else
1355              return TreeNodeCallback.CONTINUE_SIBLING; // continue
1356
// next
1357
// sibling
1358
} else {
1359            // must be FunCall
1360
if (isMemberInFunCall(oExp, mPath[iDimNode], iDimNode))
1361              return TreeNodeCallback.CONTINUE;
1362            else
1363              return TreeNodeCallback.CONTINUE_SIBLING; // continue
1364
// next
1365
// sibling
1366
}
1367        }
1368
1369        // iDimNode == iDim
1370
// node Exp must contain children of member[iDim]
1371
if (uti.isMember(oExp)) {
1372          if (uti.checkParent(mPath[iDimNode], oExp))
1373            return TreeNodeCallback.BREAK; // found
1374
else
1375            return TreeNodeCallback.CONTINUE_SIBLING; // continue
1376
// next
1377
// sibling
1378
} else {
1379          // must be FunCall
1380
if (isChildOfMemberInFunCall(oExp, mPath[iDimNode], iDimNode))
1381            return TreeNodeCallback.BREAK; // found
1382
else
1383            return TreeNodeCallback.CONTINUE_SIBLING; // continue
1384
// next
1385
// sibling
1386
}
1387      }
1388    });
1389
1390    if (ret == TreeNodeCallback.BREAK)
1391      return true; // child path fund
1392
else
1393      return false;
1394  } // checkChildPosition
1395

1396  /**
1397   * resolve the qubon mode unions and crossjoins only used in "old" expand mode
1398   */

1399  private void resolveUnions() {
1400    final List JavaDoc[] setLists = new List JavaDoc[nDimension];
1401    for (int i = 0; i < setLists.length; i++) {
1402      setLists[i] = new ArrayList JavaDoc();
1403    }
1404    posTreeRoot.walkChildren(new TreeNodeCallback() {
1405
1406      /**
1407       * callback resolve sets of any dimension
1408       */

1409      public int handleTreeNode(TreeNode node) {
1410        int iDimNode = node.getLevel() - 1;
1411        Object JavaDoc oExp = node.getReference();
1412        if (!uti.isMember(oExp)) {
1413          // FunCall
1414
funToList(oExp, setLists[iDimNode]);
1415        } else {
1416          // member
1417
setLists[iDimNode].add(oExp);
1418        }
1419        return TreeNodeCallback.CONTINUE;
1420      } // handleTreeNode
1421
});
1422
1423    // unions and sets are resolved, now resolve crossjoins
1424
posTreeRoot = new TreeNode(null);
1425    crossJoinTree(setLists, posTreeRoot, 0);
1426
1427    qubonMode = false;
1428  }
1429
1430  /**
1431   * find best tree node for member path (longest match)
1432   */

1433  private TreeNode findBestNode(final Member[] mPath) {
1434    final TreeNode[] bestNode = new TreeNode[1];
1435    bestNode[0] = posTreeRoot;
1436    posTreeRoot.walkChildren(new TreeNodeCallback() {
1437
1438      /**
1439       * callback find node matching member Path exactly
1440       */

1441      public int handleTreeNode(TreeNode node) {
1442        int iDim = mPath.length - 1;
1443        int iDimNode = node.getLevel() - 1;
1444        Object JavaDoc oExp = node.getReference();
1445        if (!uti.isMember(oExp))
1446          return TreeNodeCallback.CONTINUE_SIBLING; // continue next
1447
// sibling
1448
if (uti.equalMember(oExp, mPath[iDimNode])) {
1449          // match
1450
if (iDimNode == iDim) {
1451            // found exactly matching node
1452
bestNode[0] = node;
1453            return TreeNodeCallback.BREAK;
1454          } else {
1455            // best match up to now
1456
bestNode[0] = node;
1457            return TreeNodeCallback.CONTINUE;
1458          }
1459        } else {
1460          // no match
1461
return TreeNodeCallback.CONTINUE_SIBLING; // continue next
1462
// sibling
1463
}
1464      }
1465    });
1466
1467    return bestNode[0];
1468  }
1469
1470  /**
1471   * collect tail nodes for all nodes matching member path
1472   */

1473  private List JavaDoc collectTailNodes(TreeNode startNode, final Member[] mPath) {
1474
1475    final List JavaDoc tailNodes = new ArrayList JavaDoc();
1476    startNode.walkChildren(new TreeNodeCallback() {
1477
1478      /**
1479       * callback find node matching mPath collect tail nodes
1480       */

1481      public int handleTreeNode(TreeNode node) {
1482        int iDim = mPath.length - 1;
1483        int iDimNode = node.getLevel() - 1;
1484        Object JavaDoc oExp = node.getReference();
1485        boolean match = false;
1486        if (uti.isMember(oExp)) {
1487          // exp is member
1488
if (uti.equalMember(oExp, mPath[iDimNode]))
1489            match = true;
1490        } else {
1491          // must be FunCall
1492
if (isMemberInFunCall(oExp, mPath[iDimNode], iDimNode))
1493            match = true;
1494        }
1495
1496        if (match) {
1497          if (iDimNode == iDim) {
1498            // add the children to the tail list
1499
tailNodes.addAll(node.getChildren());
1500            return TreeNodeCallback.CONTINUE_SIBLING;
1501          } else {
1502            // iDimNode < iDim
1503
return TreeNodeCallback.CONTINUE;
1504          }
1505        } else
1506          return TreeNodeCallback.CONTINUE_SIBLING; // no match,
1507
// continue next
1508
// sibling
1509

1510      } // handlePositionTreeNode
1511
});
1512
1513    return tailNodes;
1514  }
1515
1516  private boolean findMemberChild(final Member member) {
1517
1518    final int iDim = this.dimIdx(uti.dimForMember(member));
1519
1520    int ret = posTreeRoot.walkChildren(new TreeNodeCallback() {
1521
1522      /**
1523       * callback find child node of member
1524       */

1525      public int handleTreeNode(TreeNode node) {
1526        int iDimNode = node.getLevel() - 1;
1527        if (iDimNode < iDim)
1528          return TreeNodeCallback.CONTINUE; // we are below iDim,
1529
// don't care
1530

1531        // iDimNode == iDim
1532
// node Exp must contain children of member[iDim]
1533
Object JavaDoc oExp = node.getReference();
1534        if (uti.isMember(oExp)) {
1535          if (uti.checkParent(member, oExp))
1536            return TreeNodeCallback.BREAK; // found
1537
} else {
1538          // must be FunCall
1539
if (isChildOfMemberInFunCall(oExp, member, iDimNode))
1540            return TreeNodeCallback.BREAK; // found
1541
}
1542        return TreeNodeCallback.CONTINUE_SIBLING; // continue next
1543
// sibling
1544
}
1545    });
1546
1547    return (ret == TreeNodeCallback.BREAK);
1548  }
1549
1550  /**
1551   * String representation (debugging)
1552   */

1553  public String JavaDoc toString() {
1554    final StringBuffer JavaDoc sbPosTree = new StringBuffer JavaDoc();
1555    sbPosTree.append("number of hierarchies excluded from HIEARARCHIZE=" + nHierExclude);
1556    sbPosTree.append('\n');
1557    if (posTreeRoot == null) {
1558      sbPosTree.append("Root=null");
1559      return sbPosTree.toString();
1560    }
1561    posTreeRoot.walkChildren(new TreeNodeCallback() {
1562
1563      /**
1564       * callback quax to String
1565       */

1566      public int handleTreeNode(TreeNode node) {
1567        int iDimNode = node.getLevel() - 1;
1568        sbPosTree.append("\n");
1569        for (int i = 0; i < iDimNode - 1; i++) {
1570          sbPosTree.append(" ");
1571        }
1572        if (iDimNode > 0) {
1573          sbPosTree.append("+--");
1574        }
1575
1576        Object JavaDoc oExp = node.getReference();
1577        if (!uti.isMember(oExp)) {
1578          // FunCall
1579
sbPosTree.append(uti.funString(oExp));
1580        } else {
1581          // member
1582
sbPosTree.append(uti.getMemberUniqueName(oExp));
1583        }
1584        return TreeNodeCallback.CONTINUE;
1585      } // handleTreeNode
1586
});
1587    return sbPosTree.toString();
1588  }
1589
1590  /**
1591   * build tree resolving crossjoin
1592   *
1593   * @param currentNode
1594   * @param iDim
1595   */

1596  private void crossJoinTree(List JavaDoc[] setLists, TreeNode currentNode, int iDim) {
1597    for (Iterator JavaDoc iter = setLists[iDim].iterator(); iter.hasNext();) {
1598      Object JavaDoc oExp = iter.next();
1599      TreeNode newNode = new TreeNode(oExp);
1600      if (iDim < nDimension - 1)
1601        crossJoinTree(setLists, newNode, iDim + 1);
1602      currentNode.addChildNode(newNode);
1603    }
1604  }
1605
1606  /**
1607   * split Funcall to node and complement
1608   */

1609  private void splitFunCall(TreeNode nFunCall, Member member, int iHier) {
1610
1611    Object JavaDoc oExp = nFunCall.getReference();
1612
1613    // it is possible (if the split member is of dimension to be collapsed),
1614
// that this funcall does not contain member.
1615
// Then - there is nothing to split.
1616
if (!isMemberInFunCall(oExp, member, nFunCall.getLevel() - 1))
1617      return; // nothing to split
1618
Object JavaDoc oComplement = createComplement(oExp, member, iHier); // can be null
1619
if (oComplement == null) {
1620      // this means, that the set resolves to a single member,
1621
// mPath[iDimNode]
1622
nFunCall.setReference(uti.objForMember(member));
1623      // nothing to split
1624
return;
1625    }
1626
1627    // split the Funcall
1628
TreeNode newNodeComplement = new TreeNode(oComplement);
1629    TreeNode newNodeMember = new TreeNode(uti.objForMember(member));
1630    // add the children
1631
for (Iterator JavaDoc iter = nFunCall.getChildren().iterator(); iter.hasNext();) {
1632      TreeNode nChild = (TreeNode) iter.next();
1633      newNodeComplement.addChildNode(nChild.deepCopy());
1634      newNodeMember.addChildNode(nChild.deepCopy());
1635    }
1636
1637    TreeNode nInsert = nFunCall.getParent();
1638    nFunCall.remove();
1639    nInsert.addChildNode(newNodeComplement);
1640    nInsert.addChildNode(newNodeMember);
1641  } // splitFuncall
1642

1643  /**
1644   * remove Children node
1645   *
1646   * @param nodeToRemove
1647   */

1648  private void removePathToNode(TreeNode nodeToRemove) {
1649    if (nodeToRemove.getParent().getChildren().size() > 1) {
1650      // this node has siblings, just remove it
1651
nodeToRemove.remove();
1652    } else {
1653      // no siblings, remove the first parent node having siblings
1654
TreeNode parent = nodeToRemove.getParent();
1655      while (parent.getParent().getChildren().size() == 1) {
1656        parent = parent.getParent();
1657      }
1658      if (parent.getLevel() > 0) // should always be true
1659
parent.remove();
1660    }
1661  }
1662
1663  /**
1664   * generate Exp for all nodes of dimension iDimension
1665   *
1666   * @param iDimension
1667   * @return Exp for all nodes
1668   */

1669  public Object JavaDoc genExpForDim(int iDimension) {
1670    // if we got a generate function on this hier, preserve it
1671
if (generateIndex >= 0 && generateIndex == iDimension && generateMode > CalcSet.SIMPLE) {
1672      TreeNode topCountNode = (TreeNode) posTreeRoot.getChildren().get(0);
1673      for (int i = 0; i < generateIndex; i++) {
1674        // the path to the topcount node at generateIndex does not
1675
// matter
1676
List JavaDoc children = topCountNode.getChildren();
1677        topCountNode = (TreeNode) children.get(0);
1678      }
1679      Object JavaDoc topcount = topCountNode.getReference();
1680      SetExp setexp = new SetExp(generateMode, topcount, hiers[iDimension]);
1681      return setexp;
1682    }
1683    List JavaDoc funCallList = collectFunCalls(iDimension);
1684    List JavaDoc memberList = collectMembers(iDimension);
1685    cleanupMemberList(funCallList, memberList, iDimension);
1686
1687    if (funCallList.size() == 0 && memberList.size() == 1)
1688      return memberList.get(0); // single member only
1689

1690    Object JavaDoc mSet = null;
1691    if (memberList.size() > 0) {
1692      Object JavaDoc[] aExp = memberList.toArray(new Object JavaDoc[0]);
1693      mSet = uti.createFunCall("{}", aExp, QuaxUti.FUNTYPE_BRACES);
1694    }
1695    if (funCallList.size() == 0)
1696      return mSet;
1697
1698    if (funCallList.size() == 1 && mSet == null)
1699      return funCallList.get(0);
1700
1701    Object JavaDoc set;
1702    int start;
1703    if (mSet != null) {
1704      set = mSet;
1705      start = 0;
1706    } else {
1707      set = funCallList.get(0);
1708      start = 1;
1709    }
1710    for (int j = start; j < funCallList.size(); j++) {
1711      set = uti.createFunCall("Union", new Object JavaDoc[] { set, funCallList.get(j)},
1712          QuaxUti.FUNTYPE_FUNCTION);
1713    }
1714    return set;
1715  }
1716
1717  /**
1718   * create drillup expression for dimension
1719   *
1720   * @param iDim
1721   * dimension to be drilled up
1722   * @return
1723   */

1724  private Object JavaDoc drillupExp(int iDim, Hierarchy hier) {
1725
1726    // the drillup logic is:
1727
// for all members of this dimension find the deepest level.
1728
// find the members of this deepest level
1729
// find the grandfathers of those deepest members
1730
// drill up goes to the children of those grandfathers.
1731
// special cases:
1732
// the deepest level has all members (level.members)
1733
// the drillup goes to parent_level.members
1734

1735    final int[] maxLevel = new int[1];
1736    maxLevel[0] = 0;
1737
1738    List JavaDoc drillupList = collectDrillup(iDim, maxLevel);
1739    Object JavaDoc expForHier = null;
1740    if (maxLevel[0] == 0) {
1741      // drillup goes to top level members
1742
// we generate an explicit member set rather than level.members
1743
// usually, this is a single member "All xy"
1744
expForHier = uti.topLevelMembers(hier, false);
1745    } else {
1746      if (drillupList.size() == 1) {
1747        expForHier = drillupList.get(0);
1748      } else {
1749        // more than 1 set expression , need union
1750
for (Iterator JavaDoc iter = drillupList.iterator(); iter.hasNext();) {
1751          Object JavaDoc oExp = iter.next();
1752          if (expForHier == null)
1753            expForHier = oExp;
1754          else {
1755            expForHier = uti.createFunCall("Union", new Object JavaDoc[] { expForHier, oExp},
1756                QuaxUti.FUNTYPE_FUNCTION);
1757          }
1758        }
1759      }
1760    }
1761    return expForHier;
1762  }
1763
1764  /**
1765   * collect drillup Exps of dimension i
1766   *
1767   * @param iDim
1768   */

1769  private List JavaDoc collectDrillup(final int iDim, final int[] maxLevel) {
1770    final List JavaDoc drillupList = new ArrayList JavaDoc();
1771    posTreeRoot.walkChildren(new TreeNodeCallback() {
1772
1773      /**
1774       * callback collect GrandFathers of deepest for dimension workInt
1775       */

1776      public int handleTreeNode(TreeNode node) {
1777        int iDimNode = node.getLevel() - 1;
1778        if (iDimNode < iDim)
1779          return TreeNodeCallback.CONTINUE;
1780        // iDimNode == workInt
1781
Object JavaDoc oExp = node.getReference();
1782        if (!uti.isMember(oExp)) {
1783          // FunCall
1784
addFunCallToDrillup(drillupList, oExp, maxLevel);
1785        } else {
1786          // member
1787
Member m = uti.memberForObj(oExp);
1788          uti.addMemberUncles(drillupList, m, maxLevel);
1789        } // member
1790
return TreeNodeCallback.CONTINUE_SIBLING;
1791      } // handlePositionTreeNode
1792
});
1793    return drillupList;
1794  }
1795
1796  /**
1797   * collect Funcalls of dimension iDim
1798   *
1799   * @param iDim
1800   */

1801  private List JavaDoc collectFunCalls(final int iDim) {
1802    if (posTreeRoot == null)
1803      return Collections.EMPTY_LIST;
1804    final List JavaDoc funCallList = new ArrayList JavaDoc();
1805    posTreeRoot.walkChildren(new TreeNodeCallback() {
1806
1807      /**
1808       * callback collect Funcalls of dimension workInt
1809       */

1810      public int handleTreeNode(TreeNode node) {
1811        int iDimNode = node.getLevel() - 1;
1812        if (iDimNode < iDim)
1813          return TreeNodeCallback.CONTINUE;
1814        // iDimNode == workInt
1815
Object JavaDoc oExp = node.getReference();
1816        if (!uti.isMember(oExp)) {
1817          // FunCall
1818
// need unique representation in order to avoid doubles
1819
String JavaDoc unique = uti.funString(oExp).toString();
1820          if (!funCallList.contains(unique)) {
1821            funCallList.add(oExp);
1822            funCallList.add(unique);
1823          }
1824        }
1825        return TreeNodeCallback.CONTINUE_SIBLING;
1826      } // handlePositionTreeNode
1827
});
1828
1829    // remove the unique strings, which were just added to avoid doubles
1830
for (Iterator JavaDoc iter = funCallList.iterator(); iter.hasNext();) {
1831      Object JavaDoc element = iter.next();
1832      if (element instanceof String JavaDoc)
1833        iter.remove();
1834    }
1835
1836    return funCallList;
1837  }
1838
1839  /**
1840   * remove members from member list being in FunCall list
1841   *
1842   * @param funCallList
1843   * @param memberList
1844   */

1845  private void cleanupMemberList(List JavaDoc funCallList, List JavaDoc memberList, int iDim) {
1846    if (funCallList.size() > 0 && memberList.size() > 0) {
1847      MemberLoop: for (Iterator JavaDoc itMem = memberList.iterator(); itMem.hasNext();) {
1848        Object JavaDoc oMember = itMem.next();
1849        Member m = uti.memberForObj(oMember);
1850        for (Iterator JavaDoc itFun = funCallList.iterator(); itFun.hasNext();) {
1851          Object JavaDoc oFun = itFun.next();
1852          if (isMemberInFunCall(oFun, m, iDim)) {
1853            itMem.remove();
1854            continue MemberLoop;
1855          }
1856        }
1857      } // MemberLoop
1858
}
1859  }
1860
1861  /**
1862   * collect Members of dimension iDim
1863   *
1864   * @param iDim
1865   */

1866  List JavaDoc collectMembers(final int iDim) {
1867    if (posTreeRoot == null)
1868      return Collections.EMPTY_LIST;
1869    final List JavaDoc memberList = new ArrayList JavaDoc();
1870    posTreeRoot.walkChildren(new TreeNodeCallback() {
1871
1872      /**
1873       * callback collect Funcalls of dimension workInt
1874       */

1875      public int handleTreeNode(TreeNode node) {
1876        int iDimNode = node.getLevel() - 1;
1877        if (iDimNode < iDim)
1878          return TreeNodeCallback.CONTINUE;
1879        // iDimNode == workInt
1880
Object JavaDoc oExp = node.getReference();
1881        if (uti.isMember(oExp) && !memberList.contains(oExp))
1882          memberList.add(oExp);
1883        return TreeNodeCallback.CONTINUE_SIBLING;
1884      } // handlePositionTreeNode
1885
});
1886    return memberList;
1887  }
1888
1889  /**
1890   * add a Funcall to Drillup list
1891   */

1892  private void addFunCallToDrillup(List JavaDoc list, Object JavaDoc oFun, int[] maxLevel) {
1893    if (uti.isFunCallTo(oFun, "Union")) {
1894      for (int i = 0; i < 2; i++) {
1895        Object JavaDoc fExp = uti.funCallArg(oFun, i);
1896        addFunCallToDrillup(list, fExp, maxLevel);
1897      }
1898    } else if (uti.isFunCallTo(oFun, "{}")) {
1899      // set of members
1900
for (int i = 0; i < uti.funCallArgCount(oFun); i++) {
1901        Object JavaDoc oMember = uti.funCallArg(oFun, i);
1902        Member m = uti.memberForObj(oMember);
1903        uti.addMemberUncles(list, m, maxLevel);
1904      }
1905    } else if (uti.isFunCallTo(oFun, "Children")) {
1906      Object JavaDoc oMember = uti.funCallArg(oFun, 0);
1907      Member m = uti.memberForObj(oMember);
1908      uti.addMemberSiblings(list, m, maxLevel);
1909    } else if (uti.isFunCallTo(oFun, "Descendants")) {
1910      Object JavaDoc oMember = uti.funCallArg(oFun, 0);
1911      Member m = uti.memberForObj(oMember);
1912      Object JavaDoc oLevel = uti.funCallArg(oFun, 1);
1913      Level lev = uti.LevelForObj(oLevel);
1914      int level = uti.levelDepthForMember(m);
1915      int levlev = ((MDXLevel) lev).getDepth();
1916      if (levlev == level + 1)
1917        uti.addMemberSiblings(list, m, maxLevel); // same as children
1918
else if (levlev == level + 2)
1919        uti.addMemberChildren(list, m, maxLevel); // m *is* grandfather
1920
else {
1921        // add descendants of parent level
1922
Level parentLevel = uti.getParentLevel(lev);
1923        uti.addMemberDescendants(list, m, parentLevel, maxLevel);
1924      }
1925    } else if (uti.isFunCallTo(oFun, "Members")) {
1926      // add parent level members
1927
Object JavaDoc oLevel = uti.funCallArg(oFun, 0);
1928      Level lev = uti.LevelForObj(oLevel);
1929      int levlev = ((MDXLevel) lev).getDepth();
1930      if (levlev == 0)
1931        return; // cannot drill up
1932
Level parentLevel = uti.getParentLevel(lev);
1933      uti.addLevelMembers(list, parentLevel, maxLevel);
1934    } else {
1935      // must be Top/Bottom Function with arg[0] being base set
1936
Object JavaDoc oFun2 = uti.funCallArg(oFun, 0);
1937      addFunCallToDrillup(list, oFun2, maxLevel); // do not have a better
1938
// solution
1939
}
1940  }
1941
1942  /**
1943   * add FunCall to list
1944   *
1945   * @param oFun
1946   * @param list
1947   */

1948  private void funToList(Object JavaDoc oFun, List JavaDoc list) {
1949    if (uti.isFunCallTo(oFun, "Union")) {
1950      Object JavaDoc oArg0 = uti.funCallArg(oFun, 0);
1951      Object JavaDoc oArg1 = uti.funCallArg(oFun, 1);
1952      funToList(oArg0, list);
1953      funToList(oArg1, list);
1954    } else if (uti.isFunCallTo(oFun, "{}")) {
1955      for (int i = 0; i < uti.funCallArgCount(oFun); i++) {
1956        // member sets are resolved to single members
1957
Object JavaDoc oMember = uti.funCallArg(oFun, i);
1958        list.add(oMember);
1959      }
1960    } else {
1961      list.add(oFun);
1962    }
1963  }
1964
1965  // ==========
1966
// Utility
1967
// ==========
1968

1969  /**
1970   * hierarchize the query axis position array
1971   */

1972  // this code is not working
1973
public void hierarchizePositions(final Member[][] aPosMem) {
1974
1975    final int nDimension = aPosMem[0].length;
1976    final Map JavaDoc[] firstOccurrences = new HashMap JavaDoc[nDimension];
1977    for (int i = 0; i < nDimension; i++) {
1978      firstOccurrences[i] = new HashMap JavaDoc();
1979    }
1980    for (int i = 0; i < aPosMem.length; i++) {
1981      for (int j = 0; j < nDimension; j++) {
1982        // String uName = aPosMem[i][j].getUniqueName();
1983
if (!firstOccurrences[j].containsKey(aPosMem[i][j])) {
1984          firstOccurrences[j].put(aPosMem[i][j], new Integer JavaDoc(i));
1985        }
1986      } // j
1987
} //i
1988

1989    Arrays.sort(aPosMem, new Comparator JavaDoc() {
1990      public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1991        // compare two position member arrays
1992
Member[] a1 = (Member[]) o1;
1993        Member[] a2 = (Member[]) o2;
1994
1995        DimensionLoop: for (int i = 0; i < a1.length; i++) {
1996          if (a1[i].equals(a2[i]))
1997            continue DimensionLoop;
1998          // first difference at dimension index i
1999
// if it is on different level, the descendant is higher
2000
// otherwise - decide by first occurrence
2001
int level1 = ((MDXLevel) a1[i].getLevel()).getDepth();
2002          int level2 = ((MDXLevel) a1[i].getLevel()).getDepth();
2003          if (level1 == level2) {
2004            int first1 = ((Integer JavaDoc) firstOccurrences[i].get(a1[i])).intValue();
2005            int first2 = ((Integer JavaDoc) firstOccurrences[i].get(a2[i])).intValue();
2006            return first1 - first2;
2007          } else {
2008            return level1 - level2;
2009          }
2010          // everything equal up to here
2011
} // DimensionLoop
2012

2013        return 0; // equal positions, should not occur
2014
}
2015    });
2016
2017  }
2018
2019  // /**
2020
// * check, whether a parent.children Funcall is on the axis
2021
// */
2022
// public boolean isChildrenOnAxis(final Member parent) {
2023
//
2024
// final int iDim = this.dimIdx(uti.dimForMember(parent));
2025
//
2026
// int ret = posTreeRoot.walkChildren(new TreeNodeCallback() {
2027
//
2028
// /**
2029
// * callback
2030
// * find child node of monMember
2031
// */
2032
//
2033
// public int handleTreeNode(TreeNode node) {
2034
// int iDimNode = node.getLevel() - 1;
2035
// if (iDimNode < iDim)
2036
// return TreeNodeCallback.CONTINUE; // we are below iDim, don't care
2037
//
2038
// // iDimNode == iDim
2039
// // node Exp must be funcall evaluating to children of parent
2040
// Object oExp = node.getReference();
2041
// if (!uti.isMember(oExp)) {
2042
// // must be FunCall
2043
// if (uti.isMemberChildrenInFunCall(oExp, parent))
2044
// return TreeNodeCallback.BREAK; // found
2045
// }
2046
// return TreeNodeCallback.CONTINUE_SIBLING; // continue next sibling
2047
// }
2048
// });
2049
//
2050
// return (ret == TreeNodeCallback.BREAK);
2051
// }
2052

2053  // ==========
2054
// Getter / Setter
2055
// ==========
2056

2057  /**
2058   * @return
2059   */

2060  public QuaxUti getUti() {
2061    return uti;
2062  }
2063
2064  /**
2065   * @param uti
2066   */

2067  public void setUti(QuaxUti uti) {
2068    this.uti = uti;
2069  }
2070
2071  /**
2072   * @return
2073   */

2074  public int getNDimension() {
2075    return nDimension;
2076  }
2077
2078  /**
2079   * @return posTreeRoot
2080   */

2081  public TreeNode getPosTreeRoot() {
2082    return posTreeRoot;
2083  }
2084
2085  /**
2086   * @return
2087   */

2088  public boolean isHierarchizeNeeded() {
2089    return hierarchizeNeeded;
2090  }
2091
2092  /**
2093   * @param b
2094   */

2095  public void setHierarchizeNeeded(boolean b) {
2096    hierarchizeNeeded = b;
2097  }
2098
2099  /**
2100   * @param posTreeRoot
2101   */

2102  public void setPosTreeRoot(TreeNode posTreeRoot, boolean hiersChanged) {
2103    this.posTreeRoot = posTreeRoot;
2104    if (hiersChanged) {
2105      // count dimensions, set hierarchies
2106
TreeNode firstNode = posTreeRoot;
2107      List JavaDoc hiersList = new ArrayList JavaDoc();
2108      List JavaDoc children = firstNode.getChildren();
2109      while (children.size() > 0) {
2110        firstNode = (TreeNode) children.get(0);
2111        Object JavaDoc oExp = firstNode.getReference();
2112        Hierarchy hier;
2113        try {
2114          hier = uti.hierForExp(oExp);
2115        } catch (CannotHandleException e) {
2116          logger.fatal("could not determine Hierarchy for set");
2117          logger.fatal(e);
2118          throw new IllegalArgumentException JavaDoc(e.getMessage());
2119        }
2120        hiersList.add(hier);
2121        ++nDimension;
2122        children = firstNode.getChildren();
2123      }
2124      hiers = (Hierarchy[]) hiersList.toArray(new Hierarchy[0]);
2125      nDimension = hiers.length;
2126      containsUF = new boolean[nDimension]; // init false
2127
ufMemberLists = new List JavaDoc[nDimension];
2128
2129      // go through nodes and check for Unknown functions
2130
// only one unknown function is possible in one hierarchy
2131
posTreeRoot.walkChildren(new TreeNodeCallback() {
2132        /**
2133         * callback find unknown functions
2134         */

2135        public int handleTreeNode(TreeNode node) {
2136          int iDimNode = node.getLevel() - 1;
2137          Object JavaDoc oExp = node.getReference();
2138          if (!uti.canHandle(oExp)) {
2139            // indicate that dimension i contains an unknown function,
2140
// which cannot be handled in some cases.
2141
// this will cause the member list of this dimension to be stored
2142
containsUF[iDimNode] = true;
2143          }
2144
2145          return TreeNodeCallback.CONTINUE;
2146        } // handlePositionTreeNode
2147
});
2148    }
2149  }
2150
2151  /**
2152   * get Ordinal for axis, this is the immutable id of the quax
2153   *
2154   * @return ordinal
2155   */

2156  public int getOrdinal() {
2157    return ordinal;
2158  }
2159
2160  /**
2161   * @param hierarchies
2162   */

2163  public void setHiers(Hierarchy[] hierarchies) {
2164    hiers = hierarchies;
2165    nDimension = hierarchies.length;
2166  }
2167
2168  /**
2169   * @return hierarchies
2170   */

2171  public Hierarchy[] getHiers() {
2172    return hiers;
2173  }
2174
2175  /**
2176   * @return
2177   */

2178  public boolean isQubonMode() {
2179    return qubonMode;
2180  }
2181
2182  /**
2183   * @param qubonMode
2184   */

2185  public void setQubonMode(boolean qubonMode) {
2186    this.qubonMode = qubonMode;
2187  }
2188
2189  /**
2190   * check, whether member is in set defined by funcall
2191   *
2192   * @param oExp -
2193   * set funcall
2194   * @param member
2195   * @return
2196   */

2197  private boolean isMemberInFunCall(Object JavaDoc oExp, Member member, int hierIndex) {
2198    boolean b = false;
2199    try {
2200      b = uti.isMemberInFunCall(oExp, member);
2201    } catch (CannotHandleException e) {
2202      // it is an Unkown FunCall
2203
// assume "true" if the member is in the List for this dimension
2204
if (ufMemberLists[hierIndex] == null)
2205        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension="
2206            + hierIndex + " function=" + e.getMessage());
2207
2208      b = ufMemberLists[hierIndex].contains(member);
2209    }
2210    return b;
2211  }
2212
2213  /**
2214   * check whether a Funcall does NOT resolve to top level of hierarchy
2215   */

2216  private boolean isFunCallNotTopLevel(Object JavaDoc oExp, int hierIndex) {
2217    boolean b = false;
2218
2219    try {
2220      b = uti.isFunCallNotTopLevel(oExp);
2221    } catch (CannotHandleException e) {
2222      // it is an Unkown FunCall
2223
// assume "true" if the member is in the List for this dimension
2224
if (ufMemberLists[hierIndex] == null)
2225        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension="
2226            + hierIndex + " function=" + e.getMessage());
2227
2228      for (Iterator JavaDoc iter = ufMemberLists[hierIndex].iterator(); iter.hasNext();) {
2229        Member m = (Member) iter.next();
2230        if (!uti.isMemberOnToplevel(m)) {
2231          b = true;
2232          break;
2233        }
2234      }
2235    }
2236    return b;
2237  }
2238
2239  /**
2240   * check whether a Funcall contains child of member
2241   */

2242  private boolean isChildOfMemberInFunCall(Object JavaDoc oExp, Member member, int hierIndex) {
2243    boolean b = false;
2244
2245    try {
2246      b = uti.isChildOfMemberInFunCall(oExp, member);
2247    } catch (CannotHandleException e) {
2248      // it is an Unkown FunCall
2249
// assume "true" if the member List for this dimension contains child of member
2250
if (ufMemberLists[hierIndex] == null)
2251        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension="
2252            + hierIndex + " function=" + e.getMessage());
2253
2254      for (Iterator JavaDoc iter = ufMemberLists[hierIndex].iterator(); iter.hasNext();) {
2255        Member m = (Member) iter.next();
2256        if (uti.checkParent(member, uti.objForMember(m))) {
2257          b = true;
2258          break;
2259        }
2260      }
2261    }
2262    return b;
2263  }
2264
2265  /**
2266   * check whether a Funcall contains descendant of member
2267   */

2268  private boolean isDescendantOfMemberInFunCall(Object JavaDoc oExp, Member member, int hierIndex) {
2269    boolean b = false;
2270
2271    try {
2272      b = uti.isDescendantOfMemberInFunCall(oExp, member);
2273    } catch (CannotHandleException e) {
2274      // it is an Unkown FunCall
2275
// assume "true" if the member List for this dimension contains descendant of member
2276
if (ufMemberLists[hierIndex] == null)
2277        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension="
2278            + hierIndex + " function=" + e.getMessage());
2279
2280      for (Iterator JavaDoc iter = ufMemberLists[hierIndex].iterator(); iter.hasNext();) {
2281        Member m = (Member) iter.next();
2282        if (uti.checkDescendantM(member, m)) {
2283          b = true;
2284          break;
2285        }
2286      }
2287    }
2288    return b;
2289  }
2290
2291  /**
2292   * remove descendants of member from Funcall set
2293   * @return the remainder after descendants were removed
2294   */

2295  private Object JavaDoc removeDescendantsFromFunCall(Object JavaDoc oFun, Member member, int iHier) {
2296    try {
2297      return removeDescendantsFromFunCall(oFun, member);
2298    } catch (CannotHandleException e) {
2299      // the FunCall was not handled,
2300
// assume that it is an "Unkown FunCall" which was resolved by the latest result
2301
// the "Unknown Functions" are probably not properly resolved
2302
logger.error("Unkown FunCall " + uti.funCallName(oFun));
2303
2304      if (ufMemberLists[iHier] == null)
2305        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension=" + iHier
2306            + " function=" + e.getMessage());
2307
2308      List JavaDoc newList = new ArrayList JavaDoc();
2309      for (Iterator JavaDoc iter = ufMemberLists[iHier].iterator(); iter.hasNext();) {
2310        Member m = (Member) iter.next();
2311        if (!uti.checkDescendantM(member, m)) {
2312          newList.add(uti.objForMember(m));
2313        }
2314      }
2315      return uti.createFunCall("{}", newList.toArray(), QuaxUti.FUNTYPE_BRACES);
2316    }
2317  }
2318
2319  /**
2320   * remove descendants of member from Funcall set
2321   * @return the remainder after descendants were removed
2322   */

2323  private Object JavaDoc removeDescendantsFromFunCall(Object JavaDoc oFun, Member member)
2324      throws CannotHandleException {
2325    if (uti.isFunCallTo(oFun, "Children")) {
2326      // as we know, that there is a descendent of m in x.children,
2327
// we know that *all* x.children are descendants of m
2328
return null;
2329    } else if (uti.isFunCallTo(oFun, "Descendants")) {
2330      // as we know, that there is a descendent of m in x.descendants
2331
// we know that *all* x.descendants are descendants of m
2332
return null;
2333    } else if (uti.isFunCallTo(oFun, "Members")) {
2334      Level level = member.getLevel();
2335      Object JavaDoc[] members = uti.getLevelMembers(level);
2336      List JavaDoc remainder = new ArrayList JavaDoc();
2337      for (int i = 0; i < members.length; i++) {
2338        if (!uti.checkDescendantO(member, members[i]))
2339          remainder.add(members[i]);
2340      }
2341      return uti.createMemberSet(remainder);
2342    } else if (uti.isFunCallTo(oFun, "{}")) {
2343      List JavaDoc remainder = new ArrayList JavaDoc();
2344      for (int i = 0; i < uti.funCallArgCount(oFun); i++) {
2345        Object JavaDoc om = uti.funCallArg(oFun, i);
2346        if (!uti.checkDescendantO(member, om))
2347          remainder.add(om);
2348      }
2349      return uti.createMemberSet(remainder);
2350    } else if (uti.isFunCallTo(oFun, "Union")) {
2351      Object JavaDoc[] uargs = new Object JavaDoc[2];
2352      uargs[0] = removeDescendantsFromFunCall(uti.funCallArg(oFun, 0), member);
2353      uargs[1] = removeDescendantsFromFunCall(uti.funCallArg(oFun, 0), member);
2354      if (uargs[0] == null && uargs[1] == null)
2355        return null;
2356      if (uargs[1] == null)
2357        return uargs[0];
2358      if (uargs[0] == null)
2359        return uargs[1];
2360      if (uti.isMember(uargs[0])) {
2361        uargs[0] = uti.createFunCall("{}", new Object JavaDoc[] { uargs[0]}, QuaxUti.FUNTYPE_BRACES);
2362      }
2363      if (uti.isMember(uargs[1])) {
2364        uargs[1] = uti.createFunCall("{}", new Object JavaDoc[] { uargs[1]}, QuaxUti.FUNTYPE_BRACES);
2365      }
2366      if (uti.isFunCallTo(uargs[0], "{}") && uti.isFunCallTo(uargs[1], "{}"))
2367        return unionOfSets(uargs[0], uargs[1]);
2368
2369      return uti.createFunCall("Union", uargs, QuaxUti.FUNTYPE_FUNCTION);
2370    }
2371    throw new CannotHandleException(uti.funCallName(oFun));
2372
2373  }
2374
2375  /**
2376   * determine complement set (set minus member)
2377   */

2378  private Object JavaDoc createComplement(Object JavaDoc oFun, Member member, int iHier) {
2379    try {
2380      return createComplement(oFun, member);
2381    } catch (CannotHandleException e) {
2382      // the FunCall was not handled,
2383
// assume that it is an "Unkown FunCall" which was resolved by the latest result
2384
// the "Unknown Functions" are probably not properly resolved
2385
logger.error("Unkown FunCall " + uti.funCallName(oFun));
2386      if (ufMemberLists[iHier] == null)
2387        throw new IllegalArgumentException JavaDoc("Unknow Function - no member list, dimension=" + iHier
2388            + " function=" + e.getMessage());
2389
2390      List JavaDoc newList = new ArrayList JavaDoc();
2391      for (Iterator JavaDoc iter = ufMemberLists[iHier].iterator(); iter.hasNext();) {
2392        Member m = (Member) iter.next();
2393        if (!member.equals(m)) {
2394          newList.add(uti.objForMember(m));
2395        }
2396      }
2397      return uti.createFunCall("{}", newList.toArray(), QuaxUti.FUNTYPE_BRACES);
2398    }
2399  }
2400
2401  /**
2402   * determine complement set (set minus member)
2403   * @throws CannotHandleException
2404   */

2405  private Object JavaDoc createComplement(Object JavaDoc oFun, Member member) throws CannotHandleException {
2406    if (uti.isFunCallTo(oFun, "Children")) {
2407      Object JavaDoc oParent = uti.funCallArg(oFun, 0);
2408      // if member is NOT a child of Funcall arg, then the complement is the original set
2409
Object JavaDoc oMember = uti.objForMember(member);
2410      if (!uti.checkChild(member, oParent))
2411        return oFun;
2412      Object JavaDoc[] oChildren = uti.getChildren(oParent);
2413      if (oChildren.length < 2)
2414        return null;
2415      Object JavaDoc[] mComplement = new Object JavaDoc[oChildren.length - 1];
2416      int ii = 0;
2417      for (int i = 0; i < oChildren.length; i++) {
2418        if (!(oChildren[i].equals(oMember)))
2419          mComplement[ii++] = oChildren[i];
2420      }
2421      if (mComplement.length == 1)
2422        return mComplement[0]; // single member
2423
Object JavaDoc oComplement = uti.createFunCall("{}", mComplement, QuaxUti.FUNTYPE_BRACES);
2424      return oComplement;
2425
2426    } else if (uti.isFunCallTo(oFun, "{}")) {
2427      int nComp = 0;
2428      int nArg = uti.funCallArgCount(oFun);
2429      Object JavaDoc oMember = uti.objForMember(member);
2430      for (int i = 0; i < nArg; i++) {
2431        Object JavaDoc o = uti.funCallArg(oFun, i);
2432        if (!(o.equals(oMember)))
2433          ++nComp;
2434      }
2435      if (nComp == 0)
2436        return null;
2437      if (nComp == nArg) {
2438        // complement = same
2439
return oFun;
2440      }
2441
2442      Object JavaDoc[] mComplement = new Object JavaDoc[nComp];
2443      int ii = 0;
2444      for (int i = 0; i < nArg; i++) {
2445        Object JavaDoc o = uti.funCallArg(oFun, i);
2446        if (!(o.equals(oMember)))
2447          mComplement[ii++] = o;
2448      }
2449      if (mComplement.length == 1)
2450        return mComplement[0]; // single member
2451
Object JavaDoc oComplement = uti.createFunCall("{}", mComplement, QuaxUti.FUNTYPE_BRACES);
2452      return oComplement;
2453    } else if (uti.isFunCallTo(oFun, "Union")) {
2454      // Union of FunCalls, recursive
2455
// Complement(Union(a,b)) = Union(Complement(a), Complement(b))
2456
Object JavaDoc[] complements = new Object JavaDoc[2];
2457      for (int i = 0; i < 2; i++) {
2458        Object JavaDoc o = uti.funCallArg(oFun, i);
2459        complements[i] = createComplement(o, member);
2460      }
2461      if (complements[0] == null && complements[1] == null)
2462        return null;
2463      else if (complements[0] != null && complements[1] == null)
2464        return complements[0]; // No Union needed
2465
else if (complements[0] == null && complements[1] != null)
2466        return complements[1]; // No Union needed
2467
else {
2468        // complement can be single member
2469
if (!uti.isFunCall(complements[0])) {
2470          complements[0] = uti.createFunCall("{}", new Object JavaDoc[] { complements[0]},
2471              QuaxUti.FUNTYPE_BRACES);
2472        }
2473        if (!uti.isFunCall(complements[1])) {
2474          complements[1] = uti.createFunCall("{}", new Object JavaDoc[] { complements[1]},
2475              QuaxUti.FUNTYPE_BRACES);
2476        }
2477
2478        if (uti.isFunCallTo(complements[0], "{}") && uti.isFunCallTo(complements[1], "{}")) {
2479          // create single set as union ow two sets
2480
return unionOfSets(complements[0], complements[1]);
2481        }
2482        Object JavaDoc newUnion = uti.createFunCall("Union", complements, QuaxUti.FUNTYPE_FUNCTION);
2483        return newUnion;
2484      }
2485    }
2486    // the fun call is not supported
2487
throw new CannotHandleException(uti.funCallName(oFun));
2488
2489  }
2490
2491  /**
2492   */

2493  public int getGenerateIndex() {
2494    return generateIndex;
2495  }
2496
2497  /**
2498   */

2499  public void setGenerateIndex(int i) {
2500    generateIndex = i;
2501  }
2502
2503  /**
2504   */

2505  public int getGenerateMode() {
2506    return generateMode;
2507  }
2508
2509  /**
2510   */

2511  public void setGenerateMode(int i) {
2512    generateMode = i;
2513  }
2514
2515  /**
2516   * reset generate "topcount"
2517   */

2518  public void resetGenerate() {
2519    generateMode = 0;
2520    generateIndex = -1;
2521    expGenerate = null;
2522  }
2523
2524  /**
2525   * @return Returns the nHierExclude.
2526   */

2527  public int getNHierExclude() {
2528    return nHierExclude;
2529  }
2530
2531  /**
2532   * @param hierExclude
2533   * The nHierExclude to set.
2534   */

2535  public void setNHierExclude(int hierExclude) {
2536    nHierExclude = hierExclude;
2537  }
2538
2539  /**
2540   * only allow expand/collapse left of a "sticky topcount"
2541   */

2542  private boolean allowNavigate(Member member, boolean qubon) {
2543    int iDim = dimIdx(uti.dimForMember(member));
2544    return allowNavigate(iDim, qubon);
2545  }
2546
2547  /**
2548   * only allow expand/collapse left of a "sticky topcount"
2549   */

2550  private boolean allowNavigate(int iDim, boolean qubon) {
2551    if (qubon && generateIndex >= 0 && generateMode == CalcSet.STICKY && iDim == generateIndex)
2552      return false;
2553    else if (!qubon && generateIndex >= 0 && generateMode == CalcSet.STICKY
2554        && iDim >= generateIndex)
2555      return false;
2556    else
2557      return true;
2558  }
2559
2560  /**
2561   * create new set as union of 2 sets
2562   */

2563  private Object JavaDoc unionOfSets(Object JavaDoc set1, Object JavaDoc set2) {
2564    // create single set as union ow two sets
2565
int n1 = uti.funCallArgCount(set1);
2566    int n2 = uti.funCallArgCount(set2);
2567    Object JavaDoc[] newSet = new Object JavaDoc[n1 + n2];
2568    int i = 0;
2569    for (int j = 0; j < n1; j++) {
2570      newSet[i++] = uti.funCallArg(set1, j);
2571    }
2572    for (int j = 0; j < n2; j++) {
2573      newSet[i++] = uti.funCallArg(set2, j);
2574    }
2575    return uti.createFunCall("{}", newSet, QuaxUti.FUNTYPE_BRACES);
2576  }
2577
2578  /**
2579   * @param iHier index of Hierarchy
2580   * @param list Member List
2581   */

2582  public void setHierMemberList(int iHier, List JavaDoc list) {
2583    ufMemberLists[iHier] = list;
2584  }
2585  
2586  /**
2587   *
2588   * @param iHier index of Hierarchy
2589   * @return true, if the Hierarchy has an unknown function
2590   */

2591  public boolean isUnknownFunction( int iHier) {
2592    return containsUF[iHier];
2593  }
2594  
2595   /**
2596   * indicate, that an "unknown" Funcall was not handled
2597   */

2598  public static class CannotHandleException extends Exception JavaDoc {
2599
2600    /**
2601     * Constructor for CannotHandleException.
2602     *
2603     * @param arg0
2604     */

2605    public CannotHandleException(String JavaDoc arg0) {
2606      super(arg0);
2607    }
2608  } // CannotHandleException
2609

2610} //Quax
2611
Popular Tags