blob: 041147d4502e20149bfd24cefb1d64db22a4022e [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.olap
{
import flash.utils.getTimer;
import mx.collections.ArrayCollection;
import mx.collections.CursorBookmark;
import mx.collections.IList;
import mx.collections.IViewCursor;
import mx.collections.ISort;
import mx.collections.ISortField;
import mx.collections.Sort;
import mx.collections.SortField;
import mx.core.mx_internal;
import mx.events.CubeEvent;
use namespace mx_internal;
/**
* @private
*/
public class DefaultCubeImpl implements IOLAPCubeImpl
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Arrays holding indices of valid rows and columns.
* They are used to eliminate rows and columns which do not
* contain even a single valid value.
*/
private var validRows:Array;
private var validColumns:Array;
/**
* @private
* Index of row currently being processed.
*/
private var queryCubeRowIndex:int;
/**
* @private
* Index of column currently being processed.
*/
private var queryCubeColIndex:int;
/**
* @private
* Indicates whether the query results cube has been built or not.
*/
private var queryCubeBuilt:Boolean;
/**
* @private
* Indicates whether the tuples for the query has been built or not.
*/
private var queryTuplesBuilt:Boolean;
/**
* @private
* progress and total values to be processed in the query
*/
private var _queryProgress:int = 0;
private var _queryTotal:int = 0;
/**
* @private
* A single query tuple.
*/
private var queryTuple:OLAPTuple;
private var queryAxisPositions:Array;
// the column positions on the result's column axis
private var colPositions:Array; // array of OLAPAxisPositions
// the row positions on the result's row axis
private var rowPositions:Array; // array of OLAPAxisPositions
// the slicer positions on the result's slicer axis
private var slicerPositions:Array;
// the result object containing the query result
private var newResult:OLAPResult;
// the column axis of the result
private var colAxis:OLAPResultAxis;
// the row axis of the result
private var rowAxis:OLAPResultAxis;
// the slicer axis of the result
private var slicerAxis:OLAPResultAxis;
// container for all the tuples in the query
private var queryTuples:Array;
//same as rowPositions/columnPositions/slierPositions
private var colPos:IList;
private var rowPos:IList;
//the members on the slicer axis which need to be removed
//from the tuple to read aggregation result from the query cube
private var removableSlicerMembers:IList;
//if user has specified a measure on the slicer axis
//this one would point to it
private var measureInSlice:IOLAPMember;
//private var tupleCubeMembers:Array = [];
//array of all levels from all dimensions
private var levels:Array; //of IOLAPLevels;
//flag to indicate whether we are done with preparing
//for building the cube
private var prepared:Boolean = false;
//the bookmark to keep track of the data row we are processing
private var currentPosition:CursorBookmark = CursorBookmark.FIRST;
//the dataProvider cursor
private var iterator:IViewCursor = null;
//saved sort value
private var oldSort:ISort;
//sort object used to gather members of dimensions
private var newSort:ISort;
//Cube builder instance
private var nodeBuilder:CubeNodeBuilder;
//query cube builder instance
private var queryCubeBuilder:QueryCubeBuilder;
/**
* @private
* The function which should be invoked next, to build the query result.
* Each action function has the following signature.
* function action_fn_name(query:IOLAPQuery):Boolean
* Each action function performs its work and after completion switches
* the pointer to the next function that should be called.
* The last function to get called should set the pointer back to the
* 'prepareForNewQuery' function.
*/
private var actionFunction:Function = prepareForNewQuery;
//flag indicates whether the query has a row axis or not
private var validRowPositions:Boolean = true;
//query row count can be zero when user has specified only column axis
private var queryRowCount:int;
//result row count is always > 0
private var resultRowCount:int;
//number of elements in the first query axis
private var queryColCount:int;
// a container to keep track of latest indices of query axis
// positions while tuples are being generated
private var tupleIndexObject:Object = {};
//if true signals that we have finished generating
//all the tuples.
private var reachedEnd:Boolean = false;
/*private var tempStartTime:int;
private var tOnce:Boolean;
private var cOnce:Boolean;
private var rOnce:Boolean;
private var totalRunCount:int = 0;
private var totalTupleTime:Number = 0;
private var totalCubeTime:Number = 0;
private var totalResultTime:Number= 0;
*/
protected var queryProgressEventThreashold:int = 1000;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/**
* @private
* The cube on which the query is being run.
*/
private var _cube:OLAPCube;
public function set cube(c:IOLAPCube):void
{
_cube = c as OLAPCube;
}
/**
* Returns the number of query tuples which have been processed
* in the current iteration.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get queryProgress():int
{
return _queryProgress;
}
/**
* Returns the total number of query tuples being processed.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get queryTotal():int
{
return _queryTotal;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Populates the result object with axis and data values computed based
* on the query. The computation happens in steps/stages.
* Returns true when the result is completely computed. A return value of
* false indicates that the function needs to be called again to continue
* the operation.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function execute(query:IOLAPQuery, result:OLAPResult):Boolean
{
newResult = result;
var startTime:int = getTimer();
var actionResult:Boolean;
var timeTaken:int;
// if total time taken for a action is less than 10 ms
// call the action function again
do
{
actionResult = actionFunction(query);
timeTaken = getTimer() - startTime;
}
while (!actionResult && timeTaken < 10);
return actionResult;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function cancelQuery(q:IOLAPQuery):void
{
actionFunction = prepareForNewQuery;
//let us release all references
queryCubeBuilder = null;
validRows = validColumns = null;
queryTuples = null;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function cancelRefresh():void
{
prepared = false;
currentPosition = CursorBookmark.FIRST;
iterator = null;
if (newSort)
{
_cube.dataProvider.sort = oldSort;
_cube.dataProvider.refresh();
}
}
public function refresh():void
{
//trace("Call buildCubeIteratively");
//while (!buildCubeIteratively());
}
/**
* @private
* Updates the OLAP cube by processing one data row at a time.
* Returns true when cube is completely built. A return value of false
* indicates that this function needs to be called again to complete
* cube building.
*/
public function buildCubeIteratively():Boolean
{
if (!_cube.dataProvider)
return true;
var level:OLAPLevel;
var attr:OLAPAttribute;
if (!prepared)
{
levels = []; //of IOLAPLevels;
// create as many levels as number of dimensions
for each (var dim:IOLAPDimension in _cube.dimensions)
{
//TODO should we skip this or just use this?
if (dim.isMeasure)
continue;
for each (attr in dim.attributes)
{
//include attributes which are not present in the hierarchy
if (!attr.userHierarchyLevel)
levels = levels.concat(attr.levels.toArray());
}
for each (var h:OLAPHierarchy in dim.hierarchies)
{
levels = levels.concat(h.levels.toArray());
}
}
// sort the data as it makes decision about completion of handling
oldSort = _cube.dataProvider.sort;
//enable check to compare sort-cube performance
//if (!oldSort)
{
newSort = new Sort;
var fields:Array = [];
var field:ISortField ;
for each (level in levels)
{
if (level.attribute && level.attribute.userDataFunction)
{
attr = level.attribute;
field= new SortField(attr.dataField);
field.compareFunction = attr.dataCompareFunction;
fields.push(field);
}
else
{
field= new SortField(level.dataField);
fields.push(field);
}
}
newSort.fields = fields;
_cube.dataProvider.sort = newSort;
_cube.dataProvider.refresh();
}
nodeBuilder = null;
queryCubeBuilder = null;
initNodeBuilder();
prepared = true;
iterator = _cube.dataProvider.createCursor();
return false;
}
// go through each row of data
iterator.seek(currentPosition);
if (!iterator.afterLast && currentPosition != CursorBookmark.LAST)
{
var currentData:Object = iterator.current;
// Signals the builder to prepare itself for a new data row.
nodeBuilder.moveToNextRound();
var values:Array = [];
for each (level in levels)
{
// go through each dataField in the order of dimension (TODO or user specified order)
var value:Object = level.dataFunction(currentData, level.dataField);
values.push(value);
}
nodeBuilder.addValueToNodeBuilder(values, currentData);
iterator.moveNext();
currentPosition = iterator.bookmark;
return false;
}
else
{
if (!completeNodeBuilder())
return false;
prepared = false;
currentPosition = CursorBookmark.FIRST;
iterator = null;
if (newSort)
{
_cube.dataProvider.sort = oldSort;
_cube.dataProvider.refresh();
}
return true;
}
}
/**
* @private
* Method to be invoked repeatedly to generate tuples
* for each combination of row and column query axis position
* intersection. reachedEnd flag should be set to false to start
* a fresh round of iteration.
*/
private function getNextTuple():OLAPTuple
{
//if we have reached end returns null
if (reachedEnd)
return null;
var tempTuple:OLAPTuple = queryTuple;
//add members from all query axes.
var axisIndex:int = 0;
for each (var pos:Array in queryAxisPositions)
{
tempTuple.addMembers(pos[ tupleIndexObject[axisIndex] ].members);
++axisIndex;
}
//adjust for the last increment
--axisIndex;
//increment the axis index of the last query axis
//if the last element of the last query axis has been reached
//reset it to zero and increment the index of last-1 query axis
//do this till we reach the last element of the first query axis
var lastIndex:int = ++tupleIndexObject[axisIndex];
if (lastIndex >= pos.length)
{
while(lastIndex >= pos.length)
{
tupleIndexObject[axisIndex] = 0;
++tupleIndexObject[axisIndex-1];
lastIndex = tupleIndexObject[axisIndex-1];
--axisIndex;
//have we reached the last element of the first query axis?
if (axisIndex != -1)
pos = queryAxisPositions[axisIndex];
else
{
//done!
reachedEnd = true;
break;
}
}
}
return tempTuple;
}
/**
* @private
* Builds a array of tuples to build the query cube.
*/
private function buildQueryTuples(query:IOLAPQuery):Boolean
{
var member:IOLAPMember;
if (queryCubeColIndex < colPositions.length)
{
while (queryCubeRowIndex < queryRowCount || (!validRowPositions && queryCubeRowIndex == 0))
{
do
{
//queryTuple would contain the resultant tuple
getNextTuple();
if (isTupleValid(queryTuple))
{
validRows[queryCubeRowIndex] = 1;
validColumns[queryCubeColIndex] = 1;
if (!queryTuples[queryCubeRowIndex][queryCubeColIndex])
queryTuples[queryCubeRowIndex][queryCubeColIndex] = [ queryTuple ];
else
{
queryTuples[queryCubeRowIndex][queryCubeColIndex].push(queryTuple);
}
//allocate new tuple for next iteration
queryTuple = new OLAPTuple();
}
else
{
//we didn't use the tuple so we can reuse it
queryTuple.clear();
}
} while(!reachedEnd && queryCubeColIndex == tupleIndexObject[0] &&
queryCubeRowIndex == tupleIndexObject[1]);
++queryCubeRowIndex;
return false;
}
queryCubeRowIndex = 0;
++queryCubeColIndex;
return false;
}
//remove unused columns and rows.
var n:int = validRows.length;
for (var i:int = n - 1; i > -1; --i)
{
if (validRows[i] == undefined)
{
rowPositions.splice(i, 1);
queryTuples.splice(i, 1);
}
}
queryRowCount = rowPositions.length;
n = validColumns.length;
for (i = n - 1; i > -1; --i)
{
if (validColumns[i] == undefined)
{
colPositions.splice(i, 1);
for each (var a:Array in queryTuples)
{
a.splice(i, 1);
}
}
}
_queryProgress = 0;
queryCubeRowIndex = 0;
queryCubeColIndex = 0;
queryTuplesBuilt = true;
validRows = validColumns = null;
return true;
}
/**
* @private
* Builds the query cube step by step. Returns false
* if the process is not complete and this function
* needs to be called again.
*
*/
private function buildQueryCube(query:IOLAPQuery):Boolean
{
var member:IOLAPMember;
var cubeToRead:CubeNode = rootNode;
//build the sub cube with the query result values
var tuple:OLAPTuple;// = new OLAPTuple;
if (!queryCubeBuilder)
{
//prepare a new query result cube
initQueryNodeBuilder();
validRows = new Array(queryRowCount);
validColumns = new Array(colPositions.length);
queryCubeRowIndex = 0;
queryCubeColIndex = 0;
}
if (queryCubeColIndex < colPositions.length)
{
var tupleMember:IOLAPMember;
while (queryCubeRowIndex < queryRowCount || (!validRowPositions && queryCubeRowIndex == 0))
{
var tArray:Array = queryTuples[queryCubeRowIndex][queryCubeColIndex];
if (!tArray)
{
++queryCubeRowIndex;
continue;
}
for each (tuple in tArray)
{
var generatedTupleMembers:Array = tuple.membersArray;
var value:* = cubeToRead;
//skip the last measure level;
var memCount:int = generatedTupleMembers.length -1;
var tupleIndex:int;
var userMembers:Array = tuple.explicitMembers.toArray();
validRows[queryCubeRowIndex] = 1;
validColumns[queryCubeColIndex] = 1;
value = cubeToRead;
moveToNextRoundQuery();
var prevMemberName:String;
for (tupleIndex = 0; tupleIndex < memCount; ++tupleIndex)
{
tupleMember = generatedTupleMembers[tupleIndex];
//check for member existence
//Is this check redundant as the tuple has already been validated?
if (value.hasOwnProperty(tupleMember.name))
{
value = value[tupleMember.name];
prevMemberName = tupleMember.uniqueName;
addValueToQueryNodeBuilder(tupleMember.uniqueName, value);
}
else
{
// remove the position from the axis
value = undefined;
break;
}
}
if (value != undefined)
{
if (value is CubeNode)
value = value[allNodePropertyName];
var measure:OLAPMeasure = generatedTupleMembers[generatedTupleMembers.length-1] as OLAPMeasure;
addMeasureToQueryNodeBuilder(tupleMember.uniqueName, value, measure);
}
}
++queryCubeRowIndex;
return false;
}
queryCubeRowIndex = 0;
++queryCubeColIndex;
return false;
}
completeQueryNodeBuilding();
//remove unused columns and rows.
for (var temp:int = validRows.length-1; temp > -1; --temp)
{
if (validRows[temp] == undefined)
{
rowPositions.splice(temp, 1);
queryTuples.splice(temp, 1);
}
}
queryRowCount = rowPositions.length;
for (temp = validColumns.length-1; temp > -1; --temp)
{
if (validColumns[temp] == undefined)
{
colPositions.splice(temp, 1);
for each (var a:Array in queryTuples)
{
a.splice(temp, 1);
}
}
}
_queryProgress = 0;
queryCubeRowIndex = 0;
queryCubeColIndex = 0;
queryCubeBuilt = true;
return true;
}
/**
* @private
* Prepares the result object.
*
*/
private function prepareResult(query:IOLAPQuery):void
{
queryCubeBuilt = false;
queryTuplesBuilt = false;
colPositions = [];
rowPositions = [];
queryTuples = [];
queryTuple = new OLAPTuple();
queryAxisPositions = [];
var member:IOLAPMember;
var position:OLAPAxisPosition;
var queryIndex:int = 0;
var queryAxis:OLAPQueryAxis = query.getAxis(queryIndex) as OLAPQueryAxis;
while (queryAxis)
{
var queryPositions:Array = [];
for each (var t:OLAPTuple in queryAxis.tuples)
{
position = new OLAPAxisPosition;
for each (member in t.explicitMembers)
position.addMember(member);
queryPositions.push(position);
}
if (queryPositions.length)
{
queryAxisPositions.push(queryPositions);
//add a new result axis
newResult.setAxis(queryIndex, new OLAPResultAxis());
//initialize the index object map
tupleIndexObject[queryIndex] = 0;
}
++queryIndex;
queryAxis = query.getAxis(queryIndex) as OLAPQueryAxis;
}
colPositions = queryAxisPositions[0];
if (queryAxisPositions[1])
rowPositions = queryAxisPositions[1];
colAxis = newResult.getAxis(0) as OLAPResultAxis;
rowAxis = newResult.getAxis(1) as OLAPResultAxis;
if (!rowAxis)
{
rowAxis = new OLAPResultAxis();
newResult.setAxis(OLAPResult.ROW_AXIS, rowAxis);
}
slicerAxis = newResult.getAxis(2) as OLAPResultAxis;
slicerPositions = queryAxisPositions[2];
}
/**
* @private
* Dispatch the query progress event
*
*/
private function dispatchCubeProgress(progress:int, total:int, message:String=null):void
{
var ev:CubeEvent = new CubeEvent(CubeEvent.QUERY_PROGRESS);
ev.progress = progress;
ev.total = total;
if (!message)
ev.message = "Processing cell : " + progress + " of " + total;
ev.message = message;
_cube.dispatchEvent(ev);
}
/**
* @private
* Insert a value into the object result.
*
*/
private function insertResult():Boolean
{
var mainCubeTuple:OLAPTuple;//= new OLAPTuple;
while (queryCubeColIndex < colPos.length)
{
while (queryCubeRowIndex < rowPos.length || (rowPos.length == 0 && queryCubeRowIndex ==0))
{
var tArray:Array = queryTuples[queryCubeRowIndex][queryCubeColIndex];
if (!tArray)
{
++queryCubeRowIndex;
continue;
}
// we use the first tuple in the tuples array to read the resultant
// aggregation value
mainCubeTuple = tArray[0];
// we need to initialize it for each loop
var value:* = undefined;
//attempt a read from the query cube.
if (queryRootNode)
{
// to read from the query cube we need to remove
// the slicer members as we need to only read
// the aggregated value.
if (removableSlicerMembers && removableSlicerMembers.length)
{
mainCubeTuple.removeElementsAtEnd(removableSlicerMembers.length);
if (measureInSlice)
mainCubeTuple.addMember(measureInSlice);
}
value = readCubeCell(queryRootNode, false, mainCubeTuple.membersArray);
}
//attempt a read from the main cube as we failed to read from the
//query cube.
else if (value == undefined)
{
//OLAPTrace.traceMsg("Reading from the main cube.", OLAPTrace.TRACE_LEVEL_2);
value = readCubeCell(rootNode, true, mainCubeTuple.membersArray);
}
if (value != undefined)
{
validRows[queryCubeRowIndex] = 1;
validColumns[queryCubeColIndex] = 1;
newResult.setCell(queryCubeRowIndex, queryCubeColIndex, Number(value));
}
++queryCubeRowIndex;
return false;
}
queryCubeRowIndex = 0;
++queryCubeColIndex;
return false;
}
return true;
}
/**
* @private
* Prepare for building the query tuples, query cube and query result.
*
*/
private function prepareForNewQuery(query:IOLAPQuery):Boolean
{
/*
++totalRunCount;
tempStartTime = getTimer();
tOnce = cOnce = rOnce = false;
*/
queryCubeBuilder = null;
prepareResult(query);
validRows = validColumns = null;
_queryProgress = 0;
// Even if user has not specified a row axis
// we will have one row in the result
if (rowPositions.length)
{
validRowPositions = true;
queryRowCount = resultRowCount = rowPositions.length;
}
else
{
validRowPositions = false;
// we will have minimum of one row
resultRowCount = 1;
queryRowCount = 0;
}
_queryTotal = resultRowCount * colPositions.length;
dispatchCubeProgress(_queryProgress, _queryTotal);
newResult.query = query;
actionFunction = buildQueryTuplesAction;
validRows = new Array(resultRowCount);
validColumns = new Array(colPositions.length);
queryCubeRowIndex = 0;
queryCubeColIndex = 0;
queryTuples = new Array(resultRowCount);
for (var index:int = 0; index < resultRowCount; ++index)
queryTuples[index] = new Array(colPositions.length);
reachedEnd = false;
return false;
}
/**
* @private
* The action function which in turn calls the buildQueryTyples function.
*
*/
private function buildQueryTuplesAction(query:IOLAPQuery):Boolean
{
if (!buildQueryTuples(query))
{
if (_cube.hasEventListener(CubeEvent.QUERY_PROGRESS))
{
var newProgress:int = queryCubeColIndex * resultRowCount + queryCubeRowIndex;
if (newProgress - _queryProgress > queryProgressEventThreashold)
{
_queryProgress = newProgress;
dispatchCubeProgress(_queryProgress, _queryTotal, "First Pass - Processing cell : " + _queryProgress + " of " + _queryTotal);
}
}
return false;
}
/* code for performance measurement.
if (!tOnce)
{
totalTupleTime += (getTimer() - tempStartTime);
trace("Time taken for building Query tuples:" + (totalTupleTime/totalRunCount));
tempStartTime = getTimer();
tOnce = true;
}
*/
actionFunction = buildQueryCubeAction;
return false;
}
/**
* @private
* The action function which in turn calls the buildQueryCube function.
*
*/
private function buildQueryCubeAction(query:IOLAPQuery):Boolean
{
if (slicerAxis)
{
if (!buildQueryCube(query))
{
if (_cube.hasEventListener(CubeEvent.QUERY_PROGRESS))
{
var newProgress:int = queryCubeColIndex * resultRowCount + queryCubeRowIndex;
if (newProgress - _queryProgress > queryProgressEventThreashold)
{
_queryProgress = newProgress;
dispatchCubeProgress(_queryProgress, _queryTotal, "Second Pass - Processing cell : " + _queryProgress + " of " + _queryTotal);
}
}
return false;
}
}
/* code for performance measurement.
if (!cOnce)
{
totalCubeTime += getTimer() - tempStartTime;
trace("Time taken for building Query tuples + queryCube:" + (totalCubeTime/totalRunCount));
tempStartTime = getTimer();
cOnce = true;
}
*/
actionFunction = buildQueryResultAction;
prepareToGenerateResult();
return false;
}
/**
* @private
*
*/
private function prepareToGenerateResult():void
{
var member:IOLAPMember;
if (queryCubeRowIndex == 0 && queryCubeColIndex == 0)
{
dispatchCubeProgress(_queryTotal, _queryTotal);
colAxis.positions = new ArrayCollection(colPositions);
colPos = colAxis.positions;
validColumns = new Array(colPos.length);
rowAxis.positions = new ArrayCollection(rowPositions);
rowPos = rowAxis.positions;
validRows = new Array(rowPos.length);
_queryProgress = 0;
_queryTotal = resultRowCount * colPositions.length;
removableSlicerMembers = new ArrayCollection();
if (slicerAxis)
{
var slicerPos:IList = slicerAxis.positions = new ArrayCollection(slicerPositions);
//gather members which need to be removed from tuples
var mems:ArrayCollection = slicerPos[0].members;
for (var mIndex:int = 0; mIndex < mems.length; ++mIndex)
{
member = mems.getItemAt(mIndex) as IOLAPMember;
removableSlicerMembers.addItem(member);
if (member.isMeasure)
measureInSlice = member;
}
}
}
}
/**
* @private
* The action function which in turn calls the insertResult function.
*
*/
private function buildQueryResultAction(query:IOLAPQuery):Boolean
{
if (!insertResult())
{
if (_cube.hasEventListener(CubeEvent.QUERY_PROGRESS))
{
var newProgress:int = queryCubeColIndex * rowPos.length + queryCubeRowIndex;
if (newProgress - _queryProgress > queryProgressEventThreashold)
{
_queryProgress = newProgress;
dispatchCubeProgress(_queryProgress, _queryTotal, "Third Pass - Processing cell : " + _queryProgress + " of " + _queryTotal);
}
}
return false;
}
/* code for performance measurement.
if (!rOnce)
{
totalResultTime += (getTimer() - tempStartTime);
trace("Time taken for building Query tuples + queryCube + result :" + (totalResultTime/totalRunCount));
tempStartTime = getTimer();
rOnce = true;
}
*/
dispatchCubeProgress(_queryTotal, _queryTotal, "Third Pass - Processing cell : " + _queryTotal + " of " + _queryTotal);
//remove unused columns and rows.
for (var temp:int = validRows.length-1; temp > -1; --temp)
{
if (validRows[temp] == undefined)
{
rowPos.removeItemAt(temp);
newResult.removeRowData(temp);
}
}
for (temp = validColumns.length-1; temp > -1; --temp)
{
if (validColumns[temp] == undefined)
{
colPos.removeItemAt(temp);
newResult.removeColumnData(temp);
}
}
//we are done with this query
actionFunction = prepareForNewQuery;
//let us release all references
queryCubeBuilder = null;
validRows = validColumns = null;
queryTuples = null;
return true;
}
/**
* @private
* Returns a value reading it from the cube based on the members array
* describing the path to the cell that should be read.
* The mainCube flag indicates whether rootNode points to the main OLAP cube or
* a query cube.
*/
private function readCubeCell(rootNode:CubeNode, mainCube:Boolean,
tupleCubeMembers:Array):*
{
var value:* = rootNode;
for each (var index:IOLAPMember in tupleCubeMembers)
{
var memberName:String;
if (index.isMeasure)
{
memberName = index.name;
}
else if (index.isAll)
{
memberName = mainCube ? allNodePropertyName : allQueryNodePropertyName;
//if (!slicerAxis)
// memberName = index.uniqueName ;
}
else
{
memberName = mainCube ? index.name : index.uniqueName;
}
if (value.hasOwnProperty(memberName))
{
value = value[memberName];
}
else
{
value = undefined;
break;
}
}
if (value != undefined)
{
// user has stopped at this level
// pick the agg all value from the levels below.
if (value is CubeNode)
{
if (mainCube)
value = value[allNodePropertyName];
else
value = value[allQueryNodePropertyName];
}
if (index is OLAPMeasure)
{
//user has chosen measure on the axis
//we will use this
//value = value[index.name];
//if (!(value is Number))
// trace("Invalid assumption about having a value");
}
else
{
var measure:OLAPMeasure = _cube.findDimension("Measures").defaultMember as OLAPMeasure;
value = value[measure.name];
}
}
return value;
}
/*
* @private
*/
mx_internal function sortData():void
{
for each (var dim:IOLAPDimension in _cube.dimensions)
{
for each (var hierarchy:OLAPHierarchy in dim.hierarchies)
{
levels = levels.concat(hierarchy.levels.toArray());
}
}
// sort the data as it makes decision about completion of handling
var newSort:ISort = new Sort;
var fields:Array = [];
for each (var level:OLAPLevel in levels)
{
var field:ISortField = new SortField(level.dataField);
fields.push(field);
}
newSort.fields = fields;
_cube.dataProvider.sort = newSort;
_cube.dataProvider.refresh();
}
// cube builder API
/**
* @private
* Signals the start of main OLAP cube building.
*
*/
private function initNodeBuilder():void
{
if (!nodeBuilder)
{
nodeBuilder = new CubeNodeBuilder;
nodeBuilder.cube = _cube;
nodeBuilder.levelPreviousToMeasuresIndex = levels.length - 1;
}
nodeBuilder.initNodeBuilding();
}
/**
* @private
* Property Name used by the main cube in each node for a property,
* which points to the aggregation node of nodes below.
*
*/
private function get allNodePropertyName():String
{
return nodeBuilder.allNodePropertyName;
}
/**
* Returns the root CubeNode of the main OLAP cube.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal function get rootNode():CubeNode
{
return nodeBuilder.rootNode;
}
/**
* @private
*
*/
private function addValueToNodeBuilder(value:Object, data:Object):void
{
nodeBuilder.addValueToNodeBuilder(value, data);
}
/**
* @private
* Signals end of input data processing. The node builder
* finalizes the cube by computing aggregates.
*
*/
private function completeNodeBuilder():Boolean
{
return nodeBuilder.completeNodeBuilding();
}
/**
* @private
* QUERY cube builder API
*/
private function initQueryNodeBuilder():void
{
if (!queryCubeBuilder)
{
queryCubeBuilder = new QueryCubeBuilder;
queryCubeBuilder.cube = _cube;
}
queryCubeBuilder.initNodeBuilding();
}
/**
* @private
* Property Name used by the query cube in each node for a property,
* which points to the aggregation node of nodes below.
*
*/
private function get allQueryNodePropertyName():String
{
return queryCubeBuilder.allNodePropertyName;
}
/**
* @private
* Returns the root node for the query cube.
*
*/
private function get queryRootNode():CubeNode
{
if (queryCubeBuilder)
return queryCubeBuilder.rootNode;
return null;
}
/**
* @private
* Adds a value to the query cube.
*
*/
private function addValueToQueryNodeBuilder(value:Object, data:Object):void
{
queryCubeBuilder.addValueToNodeBuilder(value, data);
}
/**
* @private
* Adds a measure value to the query cube.
*
*/
private function addMeasureToQueryNodeBuilder(value:Object, data:Object, measure:OLAPMeasure):void
{
queryCubeBuilder.addMeasureValueToNode(value, data, measure);
}
/**
* @private
* Signals the queryCube builder to finalize the cube so that
* results can be read from it.
*
*/
private function completeQueryNodeBuilding():void
{
queryCubeBuilder.completeNodeBuilding();
}
/**
* @private
* Signals end of processing one tuple in the query.
*/
private function moveToNextRoundQuery():void
{
queryCubeBuilder.moveToNextRound();
}
/**
* A helper function which returns true if the Tuple addresses a valid
* cell in the cube.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal function isTupleValid(tuple:OLAPTuple):Boolean
{
var generatedTupleMembers:Array = tuple.membersArray;
var value:* = rootNode;
//skip the last measure level;
var memCount:int = generatedTupleMembers.length -1;
var tupleMember:IOLAPMember;
for (var tupleIndex:int = 0; tupleIndex < memCount; ++tupleIndex)
{
tupleMember = generatedTupleMembers[tupleIndex];
if (tupleMember.isAll)
{
value = value[allNodePropertyName];
}
else
{
//move to the next level
value = value[tupleMember.name];
//absense of any value indicates a invalid tuple.
if (value === undefined)
return false;
}
}
return true;
}
}
}