//////////////////////////////////////////////////////////////////////////////// // // 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.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.TimerEvent; import flash.utils.Dictionary; import flash.utils.Proxy; import flash.utils.Timer; import flash.utils.getTimer; import mx.collections.ArrayCollection; import mx.collections.ICollectionView; import mx.collections.IList; import mx.collections.ISort; import mx.core.mx_internal; import mx.events.CubeEvent; import mx.resources.ResourceManager; import mx.rpc.AsyncToken; import mx.rpc.Fault; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; use namespace mx_internal; //-------------------------------------- // Events //-------------------------------------- /** * Dispatched when a cube has been created * and is ready to be queried. * * @eventType mx.events.CubeEvent.CUBE_COMPLETE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="complete", type="mx.events.CubeEvent")] /** * Dispatched continuously as a cube is being created * by a call to the refresh() method. * * @eventType mx.events.CubeEvent.CUBE_PROGRESS * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="progress", type="mx.events.CubeEvent")] /** * Dispatched continuously as a query result is being generated * by a call to the execute() method. * * @eventType mx.events.CubeEvent.QUERY_PROGRESS * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="queryProgress", type="mx.events.CubeEvent")] //-------------------------------------- // metadata //-------------------------------------- [DefaultProperty("elements")] [ResourceBundle("olap")] /** * The OLAPCube class represents an OLAP cube. * * @mxml *

* The <mx:OLAPCube> tag inherits all of the tag attributes * of its superclass, and adds the following tag attributes: *

*
 *  <mx:OLAPCube
 *    Properties
 *    dataProvider=""
 *    dimensions=""
 *    elements=""
 *    measures=""
 *  />
 *.
 *  @see mx.olap.IOLAPCube
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class OLAPCube extends Proxy implements IOLAPCube, IEventDispatcher
{
    include "../core/Version.as";
    
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Constructor.
     *
     *  @param name The name of the OLAP cube.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function OLAPCube(name:String=null)
    {
        OLAPTrace.traceMsg("Creating cube: " + name, OLAPTrace.TRACE_LEVEL_3);
        super();
        
        this.name = name;
    
        ev = new EventDispatcher(this);
    }
    
    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  A map of dimensions based on their name. 
     */
    private var _dimensionMap:Dictionary = new Dictionary(true);
    
    //flag indicating whether we have prepared the dataProvider for processing
    private var prepare:Boolean = false;
    
    //saved sort property of the source dataProvider.
    private var oldSort:ISort;

    // index of the dimension being processed.
    private var dimIndex:int = 0;

    //progress in cube building    
    private var progress:int;
    
    //total number of dimensions/data rows to process 
    private var total:int;
    
    //the embedded event dispatcher object to support IEventDispatcher interface.    
    private var ev:EventDispatcher;
    
    /**
     *  @private
     *  Timer used to build the cube. 
     */
    private var cubeBuildingTimer:Timer;
    
    /**
     *  @private
     *  Timer used to compute query results.
     */
    private var queryTimer:Timer;
    
    /**
     *  @private
     *  Array holding all queries to be executed 
     */
    private var _queriesPending:Array = [];
    
    /**
     *  @private
     *  Map of query objects to their AsyncToken 
     */
    private var _queryToken:Dictionary = new Dictionary(true);
    
    private var _cubeImpl:IOLAPCubeImpl = new DefaultCubeImpl;
    
    mx_internal var defaultMeasure:OLAPMeasure;
    
    mx_internal var attributeToIndex:Dictionary;
    
    /**
     *  Sets the name of the dimension for the measures of the OLAP cube. 
     *
     *  @default "Measures"
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var measureDimensionName:String = "Measures";
    
    /**
     *  The class used by an OLAPCube instance to return the result. 
     *  You can replace the default class, OLAPResult, with your own implementation 
     *  of the IOLAPResult interface to customize the result.
     *
     *  @default OLAPResult
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var resultClass:Class = OLAPResult;
    
    /**
     *  The time interval, in milliseconds, used by the timer of the refresh() method 
     *  to iteratively build the cube. 
     *  You can set it to a higher value if you can wait longer before the cube is built. 
     *  You can set it to a lower value, but it might negatively impact responsiveness of your application.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var cubeBuildingTimeInterval:int = 5;
    
    /**
     *  The time interval, in milliseconds, used by the timer of the execute() method 
     *  to iteratively process queries. 
     *  You can set it to a higher value if you can wait for longer 
     *  before the cube generates the query result. 
     *  You can set it to a lower value to obtain query results faster, 
     *  but it might negatively impact the responsiveness of your application.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var queryBuildingTimeInterval:int = 1;
    
    /**
     *  The time, in milliseconds, used by the refresh() method 
     *  to iteratively build the cube. 
     *  A higher value would mean more rows would get processed at each timer event.
     *  You can set it to a higher value if you want the cube to be built faster, 
     *  but it might negatively impact responsiveness of your application. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected var workDuration:int = 50;
    
    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    // attributeLevels
    //----------------------------------
    
    /**
     *  @private
     *  Returns an array of all levels in the cube.
     *  Attribute levels duplicated in user defined hierarchy are skipped.
     */
    private var _attributeLevels:Array;
    
    mx_internal function get attributeLevels():Array
    {
        if (_attributeLevels)
            return _attributeLevels;
            
        _attributeLevels = [];
        
        var a:OLAPAttribute; 
        for each (var dim:IOLAPDimension in dimensions)
        {
            //skip measure dimension
            if (dim.isMeasure)
                continue;
            
            //including attributes which are not part of any user defined hierarchy.
            for each (a in dim.attributes)
            {
                // if the attribute is used in a user defined hierarchy we want to
                // pick the attributes in the user specified order.
                // so we skip it here.
                if (!a.userHierarchyLevel)
                    _attributeLevels = _attributeLevels.concat(a.allLevels.toArray());
            }

            //attributes in user defined hierarchy
            for each (var h:OLAPHierarchy in dim.hierarchies)
            {
                var hLevels:IList = h.levels;
                var length:int = hLevels.length;
                for (var i:int = 0; i < length; ++i)
                {
                    a = OLAPLevel(hLevels.getItemAt(i)).attribute;
                    _attributeLevels = _attributeLevels.concat(a.allLevels.toArray());
                }
            }
        }
        
        return _attributeLevels;
    }
    
    //----------------------------------
    // dataProvider
    //----------------------------------
    
    private var _dataProvider:ICollectionView;
    
    /**
     *  The flat data used to populate the OLAP cube. 
     *  You must call the refresh() method 
     *  to initialize the cube after setting this property.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get dataProvider():ICollectionView
    {
        return _dataProvider;
    }
    
    /**
     *  @private
     */
    public function set dataProvider(value:ICollectionView):void
    {
        var collection:ICollectionView = value as ICollectionView;
        
        _dataProvider = collection;
    }
    
    //----------------------------------
    // defaultTupleMembers
    //----------------------------------
    
    mx_internal var _defaultTupleMembers:Array;
    
    mx_internal function get defaultTupleMembers():Array
    {
        if (!_defaultTupleMembers)
        {
            _defaultTupleMembers = new Array(attributeLevels.length/2);
            attributeToIndex = new Dictionary;
            var aIndex:int = 0;
            var n:int = attributeLevels.length;
            for (var i:int = 0; i < n; i += 2)
            {
                var attr:OLAPAttribute = attributeLevels[i].attribute;
                attributeToIndex[attr] = aIndex;
                _defaultTupleMembers[aIndex++] = attr.defaultMember;
            }
        }
        
        return _defaultTupleMembers.slice();
    }
    
    //----------------------------------
    // dimensions
    //----------------------------------
    
    /**
     * @private  
     */    
    private var _dimensions:IList = new ArrayCollection;
    
    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get dimensions():IList
    {
        return _dimensions;
    }

    /**
     *  @private
     */
    public function set dimensions(value:IList):void
    {
        //reset the cache of levels
        _attributeLevels = null;
        _dimensions = value;
        var n:int = value.length;
        for (var i:int = 0; i < n; ++i)
        {
            var dim:OLAPDimension = value.getItemAt(i) as OLAPDimension;
            dim.cube = this;
            _dimensionMap[dim.name] = dim;
        }
    }
    
    //----------------------------------
    // elements
    //----------------------------------
    
    /**
    *  Processes the input Array and initializes the dimensions
    *  and measures properties based on the elements of the Array.
    *  Dimensions are represented in the Array by instances of the OLAPDimension class, 
    *  and measures are represented by instances of the OLAPMeasure class.
    *
    *  

Use this property to define the dimensions and measures of a cube in a single Array.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function set elements(value:Array):void { var dims:ArrayCollection = new ArrayCollection(); var userMeasures:ArrayCollection = new ArrayCollection(); for each (var element:Object in value) { if (element is OLAPDimension) dims.addItem(element); else if (element is OLAPMeasure) userMeasures.addItem(element); else OLAPTrace.traceMsg("Invalid element specified for cube elements"); } dimensions = dims; measures = userMeasures; } //---------------------------------- // measures //---------------------------------- /** * @private */ private var _measures:IList; //of OLAPMeasures /** * Sets the measures of the OLAP cube, as a list of OLAPMeasure instances. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function set measures(value:IList):void { _measures = value; } //---------------------------------- // name //---------------------------------- /** * @private */ private var _name:String; /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get name():String { return _name; } /** * @private */ public function set name(value:String):void { _name = value; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function cancelQuery(query:IOLAPQuery):void { var queryIndex:int = _queriesPending.indexOf(query); if (queryIndex != -1) { // if the query was the current query ask cubeImpl to // abort the query. if (queryIndex == 0) _cubeImpl.cancelQuery(query); _queriesPending.splice(queryIndex, 1); //TODO make a fault call? delete _queryToken[query]; if (_queriesPending.length == 0) { queryTimer.stop(); queryTimer = null; } } } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function cancelRefresh():void { prepare = false; progress = 0; _cubeImpl.cancelRefresh(); if (cubeBuildingTimer) { cubeBuildingTimer.stop(); cubeBuildingTimer.removeEventListener(TimerEvent.TIMER, updateDimensions); cubeBuildingTimer.removeEventListener(TimerEvent.TIMER, buildCube); cubeBuildingTimer = null; } if (oldSort) { dataProvider.sort = oldSort; dataProvider.refresh(); } } /** * @private * Creates a new dimension of the cube with the specified name. * * @param name The name of the new dimension. * * @return An OLAPDimension instance representing the new dimension. */ public function createDimension(name:String):OLAPDimension { var dim:OLAPDimension = new OLAPDimension(name); dim.cube = this; _dimensions.addItem(dim); _dimensionMap[name] = dim; return dim; } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function findDimension(name:String):IOLAPDimension { return _dimensionMap[name]; } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function refresh():void { // check if the refresh is already in progress and return in that case. // we can get into a infinite loop if refresh is being called in // a collection change handler because a call to refresh sorts the dataProvider // resulting in a collection changed event if(cubeBuildingTimer) return; _attributeLevels = null; //TODO if user sets new dimensions after refreshing we need to again // position the measures dimension to the end of the list // is this the right place to do this creation of Measures Level? if (!findDimension(measureDimensionName)) { var measuresDim:OLAPDimension = createDimension(measureDimensionName); var measuresHierarchy:OLAPHierarchy = measuresDim.createHierarchy(measureDimensionName); measuresHierarchy.hasAll = false; measuresHierarchy.dimension = measuresDim; var measuresLevel:OLAPLevel = measuresHierarchy.createLevel(measureDimensionName); measuresDim.setAsMeasure(true); measuresDim.refresh(); if (!_measures || _measures.length == 0) { var message:String = ResourceManager.getInstance().getString( "olap", "noMeasures"); throw Error(message); } // loop: create measure members var n:int = _measures.length; for (var i:int = 0; i < n; ++i) { var m:OLAPMeasure = _measures.getItemAt(i) as OLAPMeasure; m.dimension = measuresDim; measuresLevel.addMember(m); } } defaultMeasure = _measures.getItemAt(0) as OLAPMeasure; //TODO a warning message to the user if (!dataProvider) return; cubeBuildingTimer = new Timer(cubeBuildingTimeInterval); cubeBuildingTimer.addEventListener(TimerEvent.TIMER, updateDimensions); cubeBuildingTimer.start(); } /** * @private */ private function dispatchCubeProgress(progress:int, total:int, message:String=null):void { var ev:CubeEvent = new CubeEvent(CubeEvent.CUBE_PROGRESS); ev.progress = progress; ev.total = total; ev.message = message; dispatchEvent(ev); } /** * @private */ private function updateDimensions(event:TimerEvent):void { if (!prepare) { //TODO may have to do it for each dimension if they have individual // dataProviders (to support star schema?) oldSort = dataProvider.sort; prepare = true; dimIndex = 0; dispatchCubeProgress(dimIndex, dimensions.length, ResourceManager.getInstance().getString("olap", "dimensionProcessingMessage", [dimensions.getItemAt(dimIndex).name])); return; } if (dimIndex < dimensions.length) { dispatchCubeProgress(dimIndex, dimensions.length, ResourceManager.getInstance().getString("olap", "dimensionProcessingMessage", [dimensions.getItemAt(dimIndex).name])); dimensions.getItemAt(dimIndex).refresh(); ++dimIndex; return; } dispatchCubeProgress(dimIndex, dimensions.length); prepare = false; cubeBuildingTimer.stop(); cubeBuildingTimer.removeEventListener(TimerEvent.TIMER, updateDimensions); dataProvider.sort = oldSort; oldSort = null; dataProvider.refresh(); if (cubeImpl) { cubeImpl.cube = this; cubeImpl.refresh(); cubeBuildingTimer.addEventListener(TimerEvent.TIMER, buildCube); cubeBuildingTimer.start(); progress = 0; total = dataProvider.length; } } /** * @private */ private function buildCube(event:TimerEvent):void { //once we have processed all rows cube would be doing a final pass //which takes a longer time. So we skip a loop to provide // a chance for the progress to be shown if (progress == (total + 1)) { //TODO use resource bundle dispatchCubeProgress(progress - 1, total, ResourceManager.getInstance().getString("olap", "finalizingMessage")); ++progress; return; } var startTime:int = getTimer(); var timeTaken:int = 0; var cubeBuilt:Boolean; // if total time taken for a action is less than 10 ms // call the action function again while (timeTaken < workDuration) { cubeBuilt = cubeImpl.buildCubeIteratively(); if (cubeBuilt) break; if (progress > total) dispatchCubeProgress(total, total, ResourceManager.getInstance().getString("olap", "finalizingMessage")); else dispatchCubeProgress(progress++, total, ResourceManager.getInstance().getString("olap", "progressMessage", [progress, total])); timeTaken = getTimer() - startTime; } if (cubeBuilt) { dispatchEvent(new CubeEvent(CubeEvent.CUBE_COMPLETE)); cubeBuildingTimer.stop(); cubeBuildingTimer = null; } } /** * Registers an event listener object with an EventDispatcher object so that the listener * receives notification of an event. * * @param type The type of event. * * @param listener The listener function that processes the event. * * @param useCapture Determines whether the listener works in the capture phase * or the target and bubbling phases. * * @param priority The priority level of the event listener. * * @param useWeakReference Determines whether the reference to the listener is strong or weak. * A strong reference (the default) prevents your listener from being garbage-collected. * A weak reference does not. * * @see flash.events.EventDispatcher#addEventListener() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { ev.addEventListener(type, listener, useCapture, priority, useWeakReference); } /** * Removes a listener. * If there no matching listener is registered, * a call to this method has no effect. * * @param type The type of event. * * @param listener The listener object to remove. * * @param useCapture Specifies whether the listener was registered for * the capture phase or the target and bubbling phases. * * @see flash.events.EventDispatcher#removeEventListener() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void { ev.removeEventListener(type, listener, useCapture); } /** * Checks whether an event listener is registered with this object or any of its ancestors for the specified event type. * This method returns true if an event listener is triggered during any phase of the event flow * when an event of the specified type is dispatched to this object or to any of its descendants. * * @param type The type of event. * * @return A value of true if a listener of the * specified type is triggered; false otherwise. * * @see flash.events.EventDispatcher#willTrigger() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function willTrigger(type:String):Boolean { return ev.willTrigger(type); } /** * Dispatches an event into the event flow. * The event target is the object upon which the dispatchEvent() method is called. * * @param event The Event object that is dispatched into the event flow. * * @return A value of true if the event was successfully dispatched. * A value of false indicates failure or that the * preventDefault() method was called on the event. * * @see flash.events.EventDispatcher#dispatchEvent() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function dispatchEvent(event:Event):Boolean { return ev.dispatchEvent(event); } /** * Checks whether the object has any listeners registered for a specific type of event. * This lets you determine where an object has altered handling of * an event type in the event flow hierarchy. * * @param type The type of event. * * @return A value of true if a listener of the specified type * is registered; false otherwise. * * @see flash.events.EventDispatcher#hasEventListener() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function hasEventListener(type:String):Boolean { return ev.hasEventListener(type); } /** * @private */ mx_internal function get cubeImpl():IOLAPCubeImpl { return _cubeImpl; } /** * @private */ mx_internal function set cubeImpl(value:IOLAPCubeImpl):void { _cubeImpl = value; _cubeImpl.cube = this; } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function execute(query:IOLAPQuery):AsyncToken { // attempt is being made by the user to execute // a query which has been already submitted for execution. if (_queriesPending.indexOf(query) != -1) return null; var token:AsyncToken = new AsyncToken(null); _queriesPending.push(query); _queryToken[query] = { tok:token }; if (!queryTimer) { queryTimer = new Timer(queryBuildingTimeInterval); queryTimer.addEventListener(TimerEvent.TIMER, executeQuery); queryTimer.start(); } return token; } /** * @private * * Query validation functions should be moved to a interface/class whcih can be implemented/extended. */ private function getAxisHierarchies(axis:IOLAPQueryAxis):Array { var axisHierarchies:Array = []; var sets:Array = axis.sets; var n:int = sets.length; for (var i:int = 0; i < n; ++i) { var tempSet:OLAPSet = sets[i]; var tuples:Array = tempSet.tuples; var m:int = tuples.length; for (var j:int = 0; j < m; ++j) { var tempTuple:OLAPTuple = tuples[j]; var list:IList = tempTuple.explicitMembers; var l:int = list.length; for (var k:int = 0; k < l; ++k) { var tempHeirarchy:OLAPHierarchy = list.getItemAt(k).hierarchy as OLAPHierarchy; if (tempHeirarchy) axisHierarchies.push(tempHeirarchy); } } } return axisHierarchies; } /** * @private * */ private function findDuplicateHierarchies(axisHierarchies1:Array, axisHierarchies2:Array):OLAPHierarchy { //TODO is it possible that same hierarchy is represented by different object? // then we need to change this find logic for each (var h:OLAPHierarchy in axisHierarchies1) { var foundIndex:int = axisHierarchies2.indexOf(h); if (foundIndex > -1) return h; } return null; } /** * @private * */ private function validateQuery(q:IOLAPQuery):void { //check usage of same hierarchy on different axis. var axes:Array = OLAPQuery(q).axes; var n:int = axes.length; for (var i:int = 0; i < n; ++i) { var queryAxis:OLAPQueryAxis = axes[i]; if (queryAxis.axisOrdinal != OLAPQuery.SLICER_AXIS) checkZeroLengthAxis(queryAxis, queryAxis.axisOrdinal); var axisHierarchies1:Array = getAxisHierarchies(queryAxis); var m:int = axes.length; for (var j:int = i + 1; j < m; ++j) { var axisHierarchies2:Array = getAxisHierarchies(axes[j]); var dup:OLAPHierarchy = findDuplicateHierarchies(axisHierarchies1, axisHierarchies2); if (dup) { var message:String = ResourceManager.getInstance().getString( "olap", "duplicateHierarchyOnAxes", [dup.name, i ,j]); throw new QueryError(message); } } } } /** * @private * Checks if the axis has no tuples */ private function checkZeroLengthAxis(queryAxis:IOLAPQueryAxis, index:int):void { if (queryAxis.tuples.length == 0) { var message:String = ResourceManager.getInstance().getString( "olap", "zeroElementsOnAxis", [index]); throw new QueryError(message); } } /** * @private * The query execution callback function. */ private function executeQuery(event:TimerEvent):void { var holder:Object = _queryToken[_queriesPending[0]]; var token:AsyncToken = holder.tok; if (token.hasResponder()) { var responder:IResponder; var result:OLAPResult; var q:IOLAPQuery = _queriesPending[0]; var deleteEntry:Boolean = true; var fault:Fault; var faultCode:String; var faultEvent:FaultEvent; try { if (!holder.hasOwnProperty("result")) { validateQuery(q); holder["result"] = result = new resultClass; } else { result = holder.result; } if (!_cubeImpl.execute(q, result)) { deleteEntry = false; return; } } catch(qe:QueryError) { faultCode = ResourceManager.getInstance().getString("olap", "queryError"); fault = new Fault(faultCode, qe.message); faultEvent = FaultEvent.createEvent(fault, token); for each (responder in token.responders) { responder.fault(faultEvent); } result = null; } catch (e:Error) { faultCode = ResourceManager.getInstance().getString("olap", "error"); fault = new Fault(faultCode, e.message); faultEvent = FaultEvent.createEvent(fault, token); for each (responder in token.responders) { responder.fault(faultEvent); } result = null; } finally { if (deleteEntry) { _queriesPending.splice(0, 1); delete _queryToken[q]; if (_queriesPending.length == 0) { queryTimer.stop(); queryTimer = null; } } } if (result) { for each (responder in token.responders) { responder.result(result); } } } } /** * @private * Returns true if the tuple addresses a valid cell in the cube. * If the members of the tuple form a invalid combination returns false. * * @param tuple the tuple whose validity is to be checked. */ mx_internal function isTupleValid(tuple:OLAPTuple):Boolean { return DefaultCubeImpl(cubeImpl).isTupleValid(tuple); } /** * Returns the name of the cube * * @return The name of the cube. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function toString():String { return name; } } }