📄 quax.java
字号:
/*
* ====================================================================
* This software is subject to the terms of the Common Public License
* Agreement, available at the following URL:
* http://www.opensource.org/licenses/cpl.html .
* Copyright (C) 2003-2004 TONBELLER AG.
* All Rights Reserved.
* You must accept the terms of that agreement to use this software.
* ====================================================================
*
*
*/
package com.tonbeller.jpivot.olap.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.tonbeller.jpivot.olap.model.Dimension;
import com.tonbeller.jpivot.olap.model.Hierarchy;
import com.tonbeller.jpivot.olap.model.Level;
import com.tonbeller.jpivot.olap.model.Member;
import com.tonbeller.jpivot.olap.model.Position;
import com.tonbeller.jpivot.olap.navi.CalcSet;
import com.tonbeller.jpivot.util.TreeNode;
import com.tonbeller.jpivot.util.TreeNodeCallback;
public class Quax {
static Logger logger = Logger.getLogger(Quax.class);
protected int nDimension;
private Hierarchy[] hiers;
// currently, we can handle the following Funcalls
// member.children, member.descendants, level.members
// other funcalls are "unknown functions"
private UnknownFunction[] unknownFunctions;
protected TreeNode posTreeRoot = null; // Position tree used in normal mode
private int ordinal; // ordinal of query axis, never changed by swap
private boolean qubonMode = false;
private boolean hierarchizeNeeded = false;
// if there are multiple hiers on this quax,
// "nHierExclude" hierarchies (from right to left)
// will *not* be included to the Hierarchize Function.
// So MDX like
// Crossjoin(Hierarchize(Dim1.A + Dim1.A.Children), {Measures.A.
// Measures.B})
// will be generated, so that the Measures are excluded from Hierarchize.
private int nHierExclude = 0;
private int generateMode = 0;
private int generateIndex = -1; // we handle generate for only 1 dimension
private Object expGenerate = null;
private Collection changeListeners = new ArrayList();
private QuaxUti uti;
private Map canExpandMemberMap = new HashMap();
private Map canExpandPosMap = new HashMap();
private Map canCollapseMemberMap = new HashMap();
private Map canCollapsePosMap = new HashMap();
/**
* c'tor
*
* @param ordinal
*/
public Quax(int ordinal) {
this.ordinal = ordinal;
qubonMode = false;
}
/**
* register change listener
*
* @param listener
*/
public void addChangeListener(QuaxChangeListener listener) {
changeListeners.add(listener);
}
/**
* unregister change listener
*
* @param listener
*/
public void removeChangeListener(QuaxChangeListener listener) {
changeListeners.remove(listener);
}
/**
* handle change
*
* @param source
* Originator of the quax change
* @param changedMemberSet
* true if the memberset was changed by the navigator
*/
public void changed(Object source, boolean changedMemberSet) {
for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
QuaxChangeListener listener = (QuaxChangeListener) iter.next();
listener.quaxChanged(this, source, changedMemberSet);
}
canExpandMemberMap.clear();
canExpandPosMap.clear();
canCollapseMemberMap.clear();
canCollapsePosMap.clear();
}
/**
* Initialize quax from result positions
*
* @param result
*/
public void init(List positions) {
Member[][] aPosMem;
int nDimension = 0;
hierarchizeNeeded = false;
nHierExclude = 0;
qubonMode = true;
if (positions.size() == 0) {
// the axis does not have any positions
aPosMem = new Member[0][0];
setHiers(new Hierarchy[0]);
setHiers(hiers);
return;
} else {
nDimension = ((Position) positions.get(0)).getMembers().length;
aPosMem = new Member[positions.size()][nDimension];
int j = 0;
PositionLoop: for (Iterator iter = positions.iterator(); iter.hasNext();) {
Position pos = (Position) iter.next();
aPosMem[j++] = pos.getMembers();
}
}
Hierarchy[] hiers = new Hierarchy[nDimension];
for (int j = 0; j < hiers.length; j++) {
Member m = aPosMem[0][j];
hiers[j] = m.getLevel().getHierarchy();
}
setHiers(hiers);
initPositions(aPosMem);
// initialize the dimension flags
// if there is only one set node per dimension,
// we are in qubon mode
posTreeRoot.walkTree(new TreeNodeCallback() {
/**
* callback check qubon mode
*/
public int handleTreeNode(TreeNode node) {
int iDim = node.getLevel();
if (iDim == Quax.this.nDimension)
return TreeNodeCallback.BREAK; // bottom reached
if (node.getChildren().size() == 1) {
return TreeNodeCallback.CONTINUE; // continue next level
} else {
// more than one child - break out
Quax.this.qubonMode = false;
return TreeNodeCallback.BREAK;
}
}
});
if (qubonMode)
nHierExclude = nDimension - 1; // nothing hierarchized
}
/**
* Initialize position member arrays after first result gotten
*
* @param aPosMemStart
*/
private void initPositions(Member[][] aPosMemStart) {
// no positions - no tree
if (aPosMemStart.length == 0) {
posTreeRoot = null;
return;
}
// before the position tree is created,
// we want to hierarchize
/*
* if (nDimension > 1) hierarchizePositions(aPosMemStart);
*/
// init position tree
posTreeRoot = new TreeNode(null); // root
int iEnd = addToPosTree(aPosMemStart, 0, aPosMemStart.length, 0, posTreeRoot);
while (iEnd < aPosMemStart.length) {
iEnd = addToPosTree(aPosMemStart, iEnd, aPosMemStart.length, 0, posTreeRoot);
}
// try to factor out the members of the last dimension
posTreeRoot.walkTree(new TreeNodeCallback() {
/**
* callback create member set for last dimension
*/
public int handleTreeNode(TreeNode node) {
int iDim1 = node.getLevel();
if (iDim1 == Quax.this.nDimension - 1) {
if (node.getChildren().size() <= 1)
return TreeNodeCallback.CONTINUE_SIBLING; // continue
// next
// sibling
// more than one child in last dimension
// create a single set function node
Object[] memArray = new Object[node.getChildren().size()];
int i = 0;
for (Iterator iter = node.getChildren().iterator(); iter.hasNext();) {
TreeNode child = (TreeNode) iter.next();
memArray[i++] = child.getReference();
}
node.getChildren().clear();
Object oFun = uti.createFunCall("{}", memArray, QuaxUti.FUNTYPE_BRACES);
TreeNode newChild = new TreeNode(oFun);
node.addChildNode(newChild);
return TreeNodeCallback.CONTINUE_SIBLING; // continue next
// sibling
}
return TreeNodeCallback.CONTINUE;
}
});
unknownFunctions = new UnknownFunction[nDimension];
for (int i = 0; i < nDimension; i++) {
unknownFunctions[i] = null;
}
if (logger.isDebugEnabled())
logger.debug("after initPositions " + this.toString());
}
/**
* add members of dimension to tree recursively
*
* @param aPosMem
* positon member array
* @param iStartPos
* start position for this dimension
* @param iEndPos
* start position for this dimension
* @param iDim
* index of this dimension
* @param parentNode
* parent node (previous dimension)
* @return index of position where the member of this dimension changes
*/
protected int addToPosTree(Member[][] aPosMem, int iStartPos, int iEndPos, int iDim,
TreeNode parentNode) {
Member currentOfDim = aPosMem[iStartPos][iDim];
Object o = uti.objForMember(currentOfDim);
TreeNode newNode = new TreeNode(o);
parentNode.addChildNode(newNode);
// check range where member of this dimension is constant
int iEndRange = iStartPos + 1;
for (; iEndRange < iEndPos; iEndRange++) {
if (aPosMem[iEndRange][iDim] != aPosMem[iStartPos][iDim])
break;
}
int nextDim = iDim + 1;
if (nextDim < nDimension) {
int iEndChild = addToPosTree(aPosMem, iStartPos, iEndRange, nextDim, newNode);
while (iEndChild < iEndRange) {
iEndChild = addToPosTree(aPosMem, iEndChild, iEndRange, nextDim, newNode);
}
}
return iEndRange;
}
/**
* find out, whether axis contains dimension
*
* @param monDim
* @return index of dimension, -1 if not there
*/
public int dimIdx(Dimension dim) {
if (hiers == null || hiers.length == 0)
return -1; // quax was not initialized yet
for (int i = 0; i < hiers.length; i++) {
if (hiers[i].getDimension().equals(dim))
return i;
}
return -1;
}
/**
* regenerate the position tree as crossjoin between sets
*
* @param hiersChanged
* indicates that the hierarchies were changed
*/
public void regeneratePosTree(Object[] sets, boolean hiersChanged) {
if (hiersChanged) {
nDimension = sets.length;
hiers = new Hierarchy[nDimension];
for (int i = 0; i < nDimension; i++) {
try {
hiers[i] = uti.hierForExp(sets[i]);
} catch (CannotHandleException e) {
logger.fatal("could not determine Hierarchy for set");
logger.fatal(e);
throw new IllegalArgumentException(e.getMessage());
}
}
unknownFunctions = new UnknownFunction[nDimension];
generateIndex = 0;
generateMode = 0;
}
if (posTreeRoot == null)
return;
posTreeRoot.getChildren().clear();
TreeNode current = posTreeRoot;
// it would be fine, if we could get rid of an existing Hierarchize
// - but this is not easy to decide.
// we will not do it, if there is a "children" function call
// not on the highest Level. This indicates that we have drilled
// down any member.
nHierExclude = 0;
int nChildrenFound = 0;
boolean childrenFound = false;
for (int i = 0; i < nDimension; i++) {
TreeNode newNode;
if (sets[i] instanceof SetExp) {
SetExp setx = (SetExp) sets[i];
newNode = new TreeNode(setx.getOExp());
int mode = setx.getMode();
if (mode > 0) {
generateMode = mode;
generateIndex = i;
expGenerate = setx.getOExp();
}
} else {
// can we remove an existing "hierarchize needed"?
boolean bChildrenFound = findChildrenCall(sets[i], 0);
if (bChildrenFound) {
childrenFound = true;
nChildrenFound = i + 1;
}
newNode = new TreeNode(sets[i]);
if (generateIndex == i && generateMode == CalcSet.STICKY) {
// there was a sticky generate on this hier
// reset, if set expression is different now
if (!sets[i].equals(expGenerate))
resetGenerate();
}
}
current.addChildNode(newNode);
current = newNode;
if (uti.canHandle(newNode.getReference())) {
unknownFunctions[i] = null;
} else {
unknownFunctions[i] = new UnknownFunction(i, newNode.getReference());
}
}
qubonMode = true;
nHierExclude = nDimension - nChildrenFound;
if (!childrenFound)
hierarchizeNeeded = false;
}
/**
* recursively find "children" Funcall
*/
private boolean findChildrenCall(Object oExp, int level) {
if (!uti.isFunCall(oExp))
return false; // member or level or ...
if (level > 0 && uti.isFunCallTo(oExp, "children"))
return true;
int nArgs = uti.funCallArgCount(oExp);
for (int i = 0; i < nArgs; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -