<commit_before>/*******************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: 
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.data.engine.olap.query.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.birt.core.data.DataType;
import org.eclipse.birt.core.data.DataTypeUtil;
import org.eclipse.birt.core.data.ExpressionUtil;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.script.ScriptContext;
import org.eclipse.birt.core.script.ScriptExpression;
import org.eclipse.birt.data.engine.api.IBaseExpression;
import org.eclipse.birt.data.engine.api.IBinding;
import org.eclipse.birt.data.engine.api.IConditionalExpression;
import org.eclipse.birt.data.engine.api.IExpressionCollection;
import org.eclipse.birt.data.engine.api.IFilterDefinition;
import org.eclipse.birt.data.engine.api.IScriptExpression;
import org.eclipse.birt.data.engine.api.ISortDefinition;
import org.eclipse.birt.data.engine.api.aggregation.AggregationManager;
import org.eclipse.birt.data.engine.api.aggregation.IAggrFunction;
import org.eclipse.birt.data.engine.api.querydefn.Binding;
import org.eclipse.birt.data.engine.api.querydefn.FilterDefinition;
import org.eclipse.birt.data.engine.core.DataException;
import org.eclipse.birt.data.engine.olap.api.query.IComputedMeasureDefinition;
import org.eclipse.birt.data.engine.olap.api.query.ICubeOperation;
import org.eclipse.birt.data.engine.olap.api.query.ICubeQueryDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IDimensionDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IEdgeDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IEdgeDrillFilter;
import org.eclipse.birt.data.engine.olap.api.query.IHierarchyDefinition;
import org.eclipse.birt.data.engine.olap.api.query.ILevelDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IMeasureDefinition;
import org.eclipse.birt.data.engine.olap.api.query.LevelDefiniton;
import org.eclipse.birt.data.engine.olap.api.query.ICubeFilterDefinition;
import org.eclipse.birt.data.engine.olap.data.api.DimLevel;
import org.eclipse.birt.data.engine.olap.data.api.IDimensionSortDefn;
import org.eclipse.birt.data.engine.olap.data.api.ILevel;
import org.eclipse.birt.data.engine.olap.data.api.ISelection;
import org.eclipse.birt.data.engine.olap.data.api.cube.ICube;
import org.eclipse.birt.data.engine.olap.data.api.cube.IDimension;
import org.eclipse.birt.data.engine.olap.data.api.cube.IHierarchy;
import org.eclipse.birt.data.engine.olap.data.impl.AggregationDefinition;
import org.eclipse.birt.data.engine.olap.data.impl.AggregationFunctionDefinition;
import org.eclipse.birt.data.engine.olap.data.impl.SelectionFactory;
import org.eclipse.birt.data.engine.olap.data.impl.aggregation.filter.LevelFilter;
import org.eclipse.birt.data.engine.olap.impl.query.MeasureDefinition;
import org.eclipse.birt.data.engine.olap.util.CubeAggrDefn;
import org.eclipse.birt.data.engine.olap.util.CubeAggrDefnOnMeasure;
import org.eclipse.birt.data.engine.olap.util.CubeRunningNestAggrDefn;
import org.eclipse.birt.data.engine.olap.util.OlapExpressionCompiler;
import org.eclipse.birt.data.engine.olap.util.OlapExpressionUtil;
import org.eclipse.birt.data.engine.olap.util.filter.IJSFacttableFilterEvalHelper;
import org.eclipse.birt.data.engine.olap.util.filter.JSFacttableFilterEvalHelper;
import org.eclipse.birt.data.engine.script.ScriptConstants;
import org.eclipse.birt.data.engine.script.ScriptEvalUtil;
import org.mozilla.javascript.Scriptable;
/**
 * Utility class
 *
 */
public class CubeQueryDefinitionUtil
{

	/**
	 * Populate all aggregation member in CubeQueryDefinition. For initial
	 * implementation: we only consider IMeasureDefintion we will take into
	 * consider to handle the aggregation definition in binding expression;
	 * 
	 * @param queryDefn
	 * @param measureMapping 
	 * @return
	 * @throws DataException 
	 */
	static CalculatedMember[] getCalculatedMembers(
			ICubeQueryDefinition queryDefn, Scriptable scope, Map measureMapping, ScriptContext cx ) throws DataException
	{
		CubeAggrDefn[] cubeAggrs = OlapExpressionUtil.getAggrDefns( queryDefn.getBindings( ) );

		populateMeasureFromBinding( queryDefn, cx );
		populateMeasureFromFilter( queryDefn, cx );
		populateMeasureFromSort( queryDefn, cx );

		CalculatedMember[] calculatedMembers1 = extractMeasure( queryDefn,
				measureMapping );

		int startRsId = calculatedMembers1.length == 0 ? 0 : 1;

		CalculatedMember[] calculatedMembers2 = createCalculatedMembersByAggrOnList( startRsId,
				cubeAggrs,
				scope,
				cx );

		return uniteCalculatedMember( calculatedMembers1, calculatedMembers2 );
		
	}

	private static CalculatedMember[] extractMeasure(
			ICubeQueryDefinition queryDefn, Map measureMapping )
			throws DataException
	{
		List measureList = queryDefn.getMeasures( );

		if ( measureList == null )
			return new CalculatedMember[0];
		List measureAggrOns = populateMeasureAggrOns( queryDefn );

		List unreferencedMeasures = getUnreferencedMeasures( queryDefn,
				measureList,
				measureMapping,
				measureAggrOns );
		CalculatedMember[] calculatedMembers1 = new CalculatedMember[unreferencedMeasures.size( )];
		int index = 0;

		Iterator measureIter = unreferencedMeasures.iterator( );
		while ( measureIter.hasNext( ) )
		{
			MeasureDefinition measureDefn = (MeasureDefinition) measureIter.next( );
			String innerName = OlapExpressionUtil.createMeasureCalculateMemeberName( measureDefn.getName( ) );
			measureMapping.put( measureDefn.getName( ), innerName );
			// all the measures will consume one result set, and the default
			// rsID is 0. If no unreferenced measures are found, the
			// bindings' start index of rsID will be 0
			calculatedMembers1[index] = new CalculatedMember( new CubeAggrDefnOnMeasure( innerName,
					measureDefn.getName( ),
					measureAggrOns,
					adaptAggrFunction( measureDefn ),
					null,
					null ),
					0 );
			index++;
		}
		return calculatedMembers1;
	}
	
	public static CalculatedMember[] createCalculatedMembersByAggrOnList(
			int startRsId, CubeAggrDefn[] cubeAggrs, Scriptable scope,
			ScriptContext cx ) throws DataException
	{
		if ( cubeAggrs == null )
		{
			return new CalculatedMember[0];
		}

		assert startRsId >= 0;

		int preparedRsId = startRsId;
		CalculatedMember[] result = new CalculatedMember[cubeAggrs.length];
		List<CalculatedMember> withDistinctRsIds = new ArrayList<CalculatedMember>( );
		int index = 0;
		for ( CubeAggrDefn cubeAggrDefn : cubeAggrs )
		{
			int id = getResultSetIndex( withDistinctRsIds,
					cubeAggrDefn.getAggrLevelsInAggregationResult( ) );
			if ( id == -1 )
			{
				result[index] = new CalculatedMember( cubeAggrDefn,
						preparedRsId );
				withDistinctRsIds.add( result[index] );
				preparedRsId++;
			}
			else
			{
				result[index] = new CalculatedMember( cubeAggrDefn, id );
			}

			if ( cubeAggrDefn.getFilter( ) != null )
			{
				IJSFacttableFilterEvalHelper filterEvalHelper = new JSFacttableFilterEvalHelper( scope, cx,
						new FilterDefinition( cubeAggrDefn.getFilter( ) ) , null, null );
				result[index].setFilterEvalHelper( filterEvalHelper );
			}
			index++;
		}
		return result;
	}

	public static boolean isRunnnigAggr( CubeAggrDefn cubeAggr ) throws DataException
	{
		return isRunnnigAggr( cubeAggr.getAggrName( ) );

	}
	
	public static boolean isRunnnigAggr( String aggrFunc ) throws DataException
	{
		IAggrFunction af = AggregationManager.getInstance( ).getAggregation( aggrFunc );
		return af != null && af.getType( ) == IAggrFunction.RUNNING_AGGR;
	}
	

	public static ICubeQueryDefinition[] getCubeQueryFromDrills(
			ICubeQueryDefinition query, ICube cube, int edgeType ) throws DataException
	{
		List queryDefinition = new ArrayList( );
		IEdgeDefinition edge = query.getEdge( edgeType );

		if ( edge != null && edge.getDrillFilter( ).size( ) > 0 )
		{
			IEdgeDrillFilter rowDrill = null, columnDrill = null;
			for ( int i = 0; i < edge.getDrillFilter( ).size( ); i++ )
			{
				if ( edgeType == ICubeQueryDefinition.COLUMN_EDGE )
					columnDrill = (IEdgeDrillFilter) edge.getDrillFilter( )
							.get( i );
				else
					rowDrill = (IEdgeDrillFilter) edge.getDrillFilter( )
							.get( i );
				queryDefinition.add( cloneCubeQueryDefinition( query,
						columnDrill,
						rowDrill,
						cube ) );
			}
		}
		ICubeQueryDefinition[] drillQuery = new ICubeQueryDefinition[queryDefinition.size( )];
		for ( int i = 0; i < drillQuery.length; i++ )
		{
			drillQuery[i] = (ICubeQueryDefinition) queryDefinition.get( i );
		}
		return drillQuery;
	}
	
	public static ICubeQueryDefinition[] getCrossCubeQueryFromDrills(
			ICubeQueryDefinition query, ICube cube ) throws DataException
	{
		IEdgeDefinition columnEdge = query.getEdge( ICubeQueryDefinition.COLUMN_EDGE );
		IEdgeDefinition rowEdge = query.getEdge( ICubeQueryDefinition.ROW_EDGE );
		List<ICubeQueryDefinition> queryDefinition = new ArrayList<ICubeQueryDefinition>( );

		if ( columnEdge != null
				&& columnEdge.getDrillFilter( ).size( ) > 0 && rowEdge != null
				&& rowEdge.getDrillFilter( ).size( ) > 0 )
		{
			IEdgeDrillFilter rowDrill = null, columnDrill = null;
			for ( int i = 0; i < columnEdge.getDrillFilter( ).size( ); i++ )
			{
				columnDrill = (IEdgeDrillFilter) columnEdge.getDrillFilter( )
						.get( i );
				for ( int j = 0; j < rowEdge.getDrillFilter( ).size( ); j++ )
				{
					rowDrill = (IEdgeDrillFilter) rowEdge.getDrillFilter( )
							.get( j );

					queryDefinition.add( cloneCubeQueryDefinition( query,
							columnDrill,
							rowDrill,
							cube ) );
				}
			}
		}
		ICubeQueryDefinition[] drillQuery = new ICubeQueryDefinition[queryDefinition.size( )];
		for ( int i = 0; i < drillQuery.length; i++ )
		{
			drillQuery[i] = (ICubeQueryDefinition) queryDefinition.get( i );
		}
		return drillQuery;
	}

	private static ICubeQueryDefinition cloneCubeQueryDefinition(
			ICubeQueryDefinition query, IEdgeDrillFilter columnDrill,
			IEdgeDrillFilter rowDrill, ICube cube ) throws DataException
	{
		DrillCubeQueryDefinition cloneQuery = null;
		if( query!= null )
		{
			cloneQuery = new DrillCubeQueryDefinition( query.getName( ) );
			if ( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ) != null )
			{
				if ( columnDrill != null )
				{
					( (DrillCubeQueryDefinition) cloneQuery ).setTupleOnColumn( columnDrill.getTuple( ) );					
				}

				cloneEdgeDefinition( cloneQuery,
						query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ),
						ICubeQueryDefinition.COLUMN_EDGE,
						columnDrill,
						cube );
			}
			if ( query.getEdge( ICubeQueryDefinition.ROW_EDGE ) != null )
			{
				if ( rowDrill != null )
				{
					( (DrillCubeQueryDefinition) cloneQuery ).setTupleOnRow( rowDrill.getTuple( ) );
				}

				cloneEdgeDefinition( cloneQuery,
						query.getEdge( ICubeQueryDefinition.ROW_EDGE ),
						ICubeQueryDefinition.ROW_EDGE,
						rowDrill,
						cube );
			}
			for ( int i = 0; i < query.getMeasures( ).size( ); i++ )
			{
				IMeasureDefinition measure = ( (IMeasureDefinition) query.getMeasures( )
						.get( i ) );
				IMeasureDefinition cloneMeasure = cloneQuery.createMeasure( measure.getName( ) );
				cloneMeasure.setAggrFunction( measure.getAggrFunction( ) );
			}
			for ( int i = 0; i < query.getComputedMeasures( ).size( ); i++ )
			{
				IComputedMeasureDefinition measure = ( (IComputedMeasureDefinition) query.getComputedMeasures( )
						.get( i ) );
				IMeasureDefinition cloneMeasure = cloneQuery.createComputedMeasure( measure.getName( ),
						measure.getType( ),
						measure.getExpression( ) );
				cloneMeasure.setAggrFunction( measure.getAggrFunction( ) );
			}

			List<String> levelDefnOnColumn = new ArrayList<String>();
			List<String> levelDefnOnRow = new ArrayList<String>();

			if ( columnDrill != null )
			{
				levelDefnOnColumn = findLevelExpressionFromQuery( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ),
						columnDrill, cube );
			}
			if ( rowDrill != null )
			{
				levelDefnOnRow = findLevelExpressionFromQuery( query.getEdge( ICubeQueryDefinition.ROW_EDGE ),
						rowDrill, cube );
			}

			for ( int i = 0; i < query.getBindings( ).size( ); i++ )
			{
				IBinding binding = (IBinding) query.getBindings( ).get( i );
				if ( binding.getAggrFunction( ) != null )
				{
					List dimLevelOnColumn = getReferenceDimLevelOnEdge( query, ICubeQueryDefinition.COLUMN_EDGE );
					List dimLevelOnRow = getReferenceDimLevelOnEdge( query, ICubeQueryDefinition.ROW_EDGE );
					
					IBinding newBinding = new Binding( binding.getBindingName( ),
							binding.getExpression( ) );
					newBinding.setDataType( binding.getDataType( ) );
					newBinding.setAggrFunction( binding.getAggrFunction( ) );
					newBinding.setDisplayName( binding.getDisplayName( ) );
					newBinding.setExportable( binding.exportable( ) );
					newBinding.setFilter( newBinding.getFilter( ) );
					for ( int k = 0; k < binding.getArguments( ).size( ); k++ )
					{
						newBinding.addArgument( (IBaseExpression) binding.getArguments( )
								.get( k ) );
					}

					List aggrOns = binding.getAggregatOns( );
					
					if ( aggrOns != null && !aggrOns.isEmpty( ) )
					{
						boolean columnExist = false, rowExist = false;
						
						boolean detailLevelOnColumn = isGrandTotalOnEdge( aggrOns,
								dimLevelOnColumn );
						boolean detailLevelOnRow = isGrandTotalOnEdge( aggrOns,
								dimLevelOnRow );
						if ( !detailLevelOnColumn && !detailLevelOnRow )
						{
							continue;
						}

						if ( ( detailLevelOnColumn && columnDrill != null )
								|| ( detailLevelOnRow && rowDrill != null ) )
						{
							for ( int k = 0; k < aggrOns.size( ); k++ )
							{
								String aggrExpr = aggrOns.get( k ).toString( );
								DimLevel target = OlapExpressionUtil.getTargetDimLevel( aggrExpr );

								String dimensionNameOnColumn = columnDrill != null
										? columnDrill.getTargetHierarchy( )
												.getDimension( )
												.getName( ) : null;
								String dimensionNameOnRow = rowDrill != null
										? rowDrill.getTargetHierarchy( )
												.getDimension( )
												.getName( ) : null;
								if ( !columnExist
										&& target.getDimensionName( )
												.equals( dimensionNameOnColumn ) )
								{
									if ( !detailLevelOnColumn )
									{
										newBinding.addAggregateOn( aggrExpr );
									}
									else
									{
										columnExist = true;
										for ( int t = 0; t < levelDefnOnColumn.size( ); t++ )
											newBinding.addAggregateOn( (String) levelDefnOnColumn.get( t ) );
									}
								}
								else if ( !rowExist
										&& target.getDimensionName( )
												.equals( dimensionNameOnRow ) )
								{
									if ( !detailLevelOnRow )
									{
										newBinding.addAggregateOn( aggrExpr );
									}
									else
									{
										rowExist = true;
										for ( int t = 0; t < levelDefnOnRow.size( ); t++ )
											newBinding.addAggregateOn( (String) levelDefnOnRow.get( t ) );
									}
								}
								else if ( !target.getDimensionName( )
										.equals( dimensionNameOnColumn )
										&& !target.getDimensionName( )
												.equals( dimensionNameOnRow ) )
									newBinding.addAggregateOn( aggrExpr );
							}
							cloneQuery.addBinding( newBinding );
						}
					}
				}
				else
					cloneQuery.addBinding( (IBinding) query.getBindings( )
							.get( i ) );
			}
			for ( int i = 0; i < query.getFilters( ).size( ); i++ )
			{
				if ( canAddFilterToDrill( query, rowDrill, columnDrill, i ) )
				{
					cloneQuery.addFilter( (IFilterDefinition) query.getFilters( )
							.get( i ) );
				}
			}
			for ( int i = 0; i < query.getSorts( ).size( ); i++ )
			{
				cloneQuery.addSort( (ISortDefinition) query.getSorts( ).get( i ) );
			}
		}
		return cloneQuery;
	}

	private static boolean canAddFilterToDrill( ICubeQueryDefinition query,
			IEdgeDrillFilter rowDrill, IEdgeDrillFilter columnDrill, int i )
	{
		List<IDimensionDefinition> columnEdgeDimensions = null;

		if ( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ) != null )
			columnEdgeDimensions = query.getEdge( ICubeQueryDefinition.COLUMN_EDGE )
					.getDimensions( );

		List<IDimensionDefinition> rowEdgeDimensions = null;

		if ( query.getEdge( ICubeQueryDefinition.ROW_EDGE ) != null )
			rowEdgeDimensions = query.getEdge( ICubeQueryDefinition.ROW_EDGE )
					.getDimensions( );

		if( ! ( query.getFilters( ).get( i ) instanceof ICubeFilterDefinition ) )
		{
			return true;
		}
		
		ICubeFilterDefinition filter = (ICubeFilterDefinition) query.getFilters( )
				.get( i );

		if ( filter.getTargetLevel( ) == null )
			return false;

		int filterIndex = -1;
		int rowDrillIndex = -1;
		int columnDrillIndex = -1;
		int edgeTag = -1;
		boolean canAddFilter = false;

		if ( rowEdgeDimensions != null && rowDrill != null )
		{
			for ( int p = 0; p < rowEdgeDimensions.size( ); p++ )
			{
				IDimensionDefinition rowEdgeDimension = rowEdgeDimensions.get( p );
				String hierarchyName = rowEdgeDimension.getHierarchy( )
						.get( 0 )
						.getName( );

				if ( hierarchyName.equals( filter.getTargetLevel( )
						.getHierarchy( )
						.getName( ) ) )
				{
					filterIndex = p;
					edgeTag = ICubeQueryDefinition.ROW_EDGE;
				}
				if ( rowDrill.getTargetHierarchy( )
						.getName( )
						.equals( hierarchyName ) )<commit_msg>Make a little refactor<commit_after>/*******************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: 
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.data.engine.olap.query.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.birt.core.data.DataType;
import org.eclipse.birt.core.data.DataTypeUtil;
import org.eclipse.birt.core.data.ExpressionUtil;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.script.ScriptContext;
import org.eclipse.birt.core.script.ScriptExpression;
import org.eclipse.birt.data.engine.api.IBaseExpression;
import org.eclipse.birt.data.engine.api.IBinding;
import org.eclipse.birt.data.engine.api.IConditionalExpression;
import org.eclipse.birt.data.engine.api.IExpressionCollection;
import org.eclipse.birt.data.engine.api.IFilterDefinition;
import org.eclipse.birt.data.engine.api.IScriptExpression;
import org.eclipse.birt.data.engine.api.ISortDefinition;
import org.eclipse.birt.data.engine.api.aggregation.AggregationManager;
import org.eclipse.birt.data.engine.api.aggregation.IAggrFunction;
import org.eclipse.birt.data.engine.api.querydefn.Binding;
import org.eclipse.birt.data.engine.api.querydefn.FilterDefinition;
import org.eclipse.birt.data.engine.core.DataException;
import org.eclipse.birt.data.engine.olap.api.query.IComputedMeasureDefinition;
import org.eclipse.birt.data.engine.olap.api.query.ICubeFilterDefinition;
import org.eclipse.birt.data.engine.olap.api.query.ICubeOperation;
import org.eclipse.birt.data.engine.olap.api.query.ICubeQueryDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IDimensionDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IEdgeDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IEdgeDrillFilter;
import org.eclipse.birt.data.engine.olap.api.query.IHierarchyDefinition;
import org.eclipse.birt.data.engine.olap.api.query.ILevelDefinition;
import org.eclipse.birt.data.engine.olap.api.query.IMeasureDefinition;
import org.eclipse.birt.data.engine.olap.api.query.LevelDefiniton;
import org.eclipse.birt.data.engine.olap.data.api.DimLevel;
import org.eclipse.birt.data.engine.olap.data.api.IDimensionSortDefn;
import org.eclipse.birt.data.engine.olap.data.api.ILevel;
import org.eclipse.birt.data.engine.olap.data.api.ISelection;
import org.eclipse.birt.data.engine.olap.data.api.cube.ICube;
import org.eclipse.birt.data.engine.olap.data.api.cube.IDimension;
import org.eclipse.birt.data.engine.olap.data.api.cube.IHierarchy;
import org.eclipse.birt.data.engine.olap.data.impl.AggregationDefinition;
import org.eclipse.birt.data.engine.olap.data.impl.AggregationFunctionDefinition;
import org.eclipse.birt.data.engine.olap.data.impl.SelectionFactory;
import org.eclipse.birt.data.engine.olap.data.impl.aggregation.filter.LevelFilter;
import org.eclipse.birt.data.engine.olap.impl.query.MeasureDefinition;
import org.eclipse.birt.data.engine.olap.util.CubeAggrDefn;
import org.eclipse.birt.data.engine.olap.util.CubeAggrDefnOnMeasure;
import org.eclipse.birt.data.engine.olap.util.CubeRunningNestAggrDefn;
import org.eclipse.birt.data.engine.olap.util.OlapExpressionCompiler;
import org.eclipse.birt.data.engine.olap.util.OlapExpressionUtil;
import org.eclipse.birt.data.engine.olap.util.filter.IJSFacttableFilterEvalHelper;
import org.eclipse.birt.data.engine.olap.util.filter.JSFacttableFilterEvalHelper;
import org.eclipse.birt.data.engine.script.ScriptConstants;
import org.eclipse.birt.data.engine.script.ScriptEvalUtil;
import org.mozilla.javascript.Scriptable;
/**
 * Utility class
 *
 */
public class CubeQueryDefinitionUtil
{

	/**
	 * Populate all aggregation member in CubeQueryDefinition. For initial
	 * implementation: we only consider IMeasureDefintion we will take into
	 * consider to handle the aggregation definition in binding expression;
	 * 
	 * @param queryDefn
	 * @param measureMapping 
	 * @return
	 * @throws DataException 
	 */
	static CalculatedMember[] getCalculatedMembers(
			ICubeQueryDefinition queryDefn, Scriptable scope, Map measureMapping, ScriptContext cx ) throws DataException
	{
		CubeAggrDefn[] cubeAggrs = OlapExpressionUtil.getAggrDefns( queryDefn.getBindings( ) );

		populateMeasureFromBinding( queryDefn, cx );
		populateMeasureFromFilter( queryDefn, cx );
		populateMeasureFromSort( queryDefn, cx );

		CalculatedMember[] calculatedMembers1 = extractMeasure( queryDefn,
				measureMapping );

		int startRsId = calculatedMembers1.length == 0 ? 0 : 1;

		CalculatedMember[] calculatedMembers2 = createCalculatedMembersByAggrOnList( startRsId,
				cubeAggrs,
				scope,
				cx );

		return uniteCalculatedMember( calculatedMembers1, calculatedMembers2 );
		
	}

	private static CalculatedMember[] extractMeasure(
			ICubeQueryDefinition queryDefn, Map measureMapping )
			throws DataException
	{
		List measureList = queryDefn.getMeasures( );

		if ( measureList == null )
			return new CalculatedMember[0];
		List measureAggrOns = populateMeasureAggrOns( queryDefn );

		List unreferencedMeasures = getUnreferencedMeasures( queryDefn,
				measureList,
				measureMapping,
				measureAggrOns );
		CalculatedMember[] calculatedMembers1 = new CalculatedMember[unreferencedMeasures.size( )];
		int index = 0;

		Iterator measureIter = unreferencedMeasures.iterator( );
		while ( measureIter.hasNext( ) )
		{
			MeasureDefinition measureDefn = (MeasureDefinition) measureIter.next( );
			String innerName = OlapExpressionUtil.createMeasureCalculateMemeberName( measureDefn.getName( ) );
			measureMapping.put( measureDefn.getName( ), innerName );
			// all the measures will consume one result set, and the default
			// rsID is 0. If no unreferenced measures are found, the
			// bindings' start index of rsID will be 0
			calculatedMembers1[index] = new CalculatedMember( new CubeAggrDefnOnMeasure( innerName,
					measureDefn.getName( ),
					measureAggrOns,
					adaptAggrFunction( measureDefn ),
					null,
					null ),
					0 );
			index++;
		}
		return calculatedMembers1;
	}
	
	public static CalculatedMember[] createCalculatedMembersByAggrOnList(
			int startRsId, CubeAggrDefn[] cubeAggrs, Scriptable scope,
			ScriptContext cx ) throws DataException
	{
		if ( cubeAggrs == null )
		{
			return new CalculatedMember[0];
		}

		assert startRsId >= 0;

		int preparedRsId = startRsId;
		CalculatedMember[] result = new CalculatedMember[cubeAggrs.length];
		List<CalculatedMember> withDistinctRsIds = new ArrayList<CalculatedMember>( );
		int index = 0;
		for ( CubeAggrDefn cubeAggrDefn : cubeAggrs )
		{
			int id = getResultSetIndex( withDistinctRsIds,
					cubeAggrDefn.getAggrLevelsInAggregationResult( ) );
			if ( id == -1 )
			{
				result[index] = new CalculatedMember( cubeAggrDefn,
						preparedRsId );
				withDistinctRsIds.add( result[index] );
				preparedRsId++;
			}
			else
			{
				result[index] = new CalculatedMember( cubeAggrDefn, id );
			}

			if ( cubeAggrDefn.getFilter( ) != null )
			{
				IJSFacttableFilterEvalHelper filterEvalHelper = new JSFacttableFilterEvalHelper( scope, cx,
						new FilterDefinition( cubeAggrDefn.getFilter( ) ) , null, null );
				result[index].setFilterEvalHelper( filterEvalHelper );
			}
			index++;
		}
		return result;
	}

	public static boolean isRunnnigAggr( CubeAggrDefn cubeAggr ) throws DataException
	{
		return isRunnnigAggr( cubeAggr.getAggrName( ) );

	}
	
	public static boolean isRunnnigAggr( String aggrFunc ) throws DataException
	{
		IAggrFunction af = AggregationManager.getInstance( ).getAggregation( aggrFunc );
		return af != null && af.getType( ) == IAggrFunction.RUNNING_AGGR;
	}
	

	public static ICubeQueryDefinition[] getCubeQueryFromDrills(
			ICubeQueryDefinition query, ICube cube, int edgeType ) throws DataException
	{
		List queryDefinition = new ArrayList( );
		IEdgeDefinition edge = query.getEdge( edgeType );

		if ( edge != null && edge.getDrillFilter( ).size( ) > 0 )
		{
			IEdgeDrillFilter rowDrill = null, columnDrill = null;
			for ( int i = 0; i < edge.getDrillFilter( ).size( ); i++ )
			{
				if ( edgeType == ICubeQueryDefinition.COLUMN_EDGE )
					columnDrill = (IEdgeDrillFilter) edge.getDrillFilter( )
							.get( i );
				else
					rowDrill = (IEdgeDrillFilter) edge.getDrillFilter( )
							.get( i );
				queryDefinition.add( cloneCubeQueryDefinition( query,
						columnDrill,
						rowDrill,
						cube ) );
			}
		}
		ICubeQueryDefinition[] drillQuery = new ICubeQueryDefinition[queryDefinition.size( )];
		for ( int i = 0; i < drillQuery.length; i++ )
		{
			drillQuery[i] = (ICubeQueryDefinition) queryDefinition.get( i );
		}
		return drillQuery;
	}
	
	public static ICubeQueryDefinition[] getCrossCubeQueryFromDrills(
			ICubeQueryDefinition query, ICube cube ) throws DataException
	{
		IEdgeDefinition columnEdge = query.getEdge( ICubeQueryDefinition.COLUMN_EDGE );
		IEdgeDefinition rowEdge = query.getEdge( ICubeQueryDefinition.ROW_EDGE );
		List<ICubeQueryDefinition> queryDefinition = new ArrayList<ICubeQueryDefinition>( );

		if ( columnEdge != null
				&& columnEdge.getDrillFilter( ).size( ) > 0 && rowEdge != null
				&& rowEdge.getDrillFilter( ).size( ) > 0 )
		{
			IEdgeDrillFilter rowDrill = null, columnDrill = null;
			for ( int i = 0; i < columnEdge.getDrillFilter( ).size( ); i++ )
			{
				columnDrill = (IEdgeDrillFilter) columnEdge.getDrillFilter( )
						.get( i );
				for ( int j = 0; j < rowEdge.getDrillFilter( ).size( ); j++ )
				{
					rowDrill = (IEdgeDrillFilter) rowEdge.getDrillFilter( )
							.get( j );

					queryDefinition.add( cloneCubeQueryDefinition( query,
							columnDrill,
							rowDrill,
							cube ) );
				}
			}
		}
		ICubeQueryDefinition[] drillQuery = new ICubeQueryDefinition[queryDefinition.size( )];
		for ( int i = 0; i < drillQuery.length; i++ )
		{
			drillQuery[i] = (ICubeQueryDefinition) queryDefinition.get( i );
		}
		return drillQuery;
	}

	private static ICubeQueryDefinition cloneCubeQueryDefinition(
			ICubeQueryDefinition query, IEdgeDrillFilter columnDrill,
			IEdgeDrillFilter rowDrill, ICube cube ) throws DataException
	{
		DrillCubeQueryDefinition cloneQuery = null;
		if( query!= null )
		{
			cloneQuery = new DrillCubeQueryDefinition( query.getName( ) );
			if ( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ) != null )
			{
				if ( columnDrill != null )
				{
					( (DrillCubeQueryDefinition) cloneQuery ).setTupleOnColumn( columnDrill.getTuple( ) );					
				}

				cloneEdgeDefinition( cloneQuery,
						query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ),
						ICubeQueryDefinition.COLUMN_EDGE,
						columnDrill,
						cube );
			}
			if ( query.getEdge( ICubeQueryDefinition.ROW_EDGE ) != null )
			{
				if ( rowDrill != null )
				{
					( (DrillCubeQueryDefinition) cloneQuery ).setTupleOnRow( rowDrill.getTuple( ) );
				}

				cloneEdgeDefinition( cloneQuery,
						query.getEdge( ICubeQueryDefinition.ROW_EDGE ),
						ICubeQueryDefinition.ROW_EDGE,
						rowDrill,
						cube );
			}
			for ( int i = 0; i < query.getMeasures( ).size( ); i++ )
			{
				IMeasureDefinition measure = ( (IMeasureDefinition) query.getMeasures( )
						.get( i ) );
				IMeasureDefinition cloneMeasure = cloneQuery.createMeasure( measure.getName( ) );
				cloneMeasure.setAggrFunction( measure.getAggrFunction( ) );
			}
			for ( int i = 0; i < query.getComputedMeasures( ).size( ); i++ )
			{
				IComputedMeasureDefinition measure = ( (IComputedMeasureDefinition) query.getComputedMeasures( )
						.get( i ) );
				IMeasureDefinition cloneMeasure = cloneQuery.createComputedMeasure( measure.getName( ),
						measure.getType( ),
						measure.getExpression( ) );
				cloneMeasure.setAggrFunction( measure.getAggrFunction( ) );
			}

			List<String> levelDefnOnColumn = new ArrayList<String>();
			List<String> levelDefnOnRow = new ArrayList<String>();

			if ( columnDrill != null )
			{
				levelDefnOnColumn = findLevelExpressionFromQuery( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ),
						columnDrill, cube );
			}
			if ( rowDrill != null )
			{
				levelDefnOnRow = findLevelExpressionFromQuery( query.getEdge( ICubeQueryDefinition.ROW_EDGE ),
						rowDrill, cube );
			}

			for ( int i = 0; i < query.getBindings( ).size( ); i++ )
			{
				IBinding binding = (IBinding) query.getBindings( ).get( i );
				if ( binding.getAggrFunction( ) != null )
				{
					List dimLevelOnColumn = getReferenceDimLevelOnEdge( query, ICubeQueryDefinition.COLUMN_EDGE );
					List dimLevelOnRow = getReferenceDimLevelOnEdge( query, ICubeQueryDefinition.ROW_EDGE );
					
					IBinding newBinding = new Binding( binding.getBindingName( ),
							binding.getExpression( ) );
					newBinding.setDataType( binding.getDataType( ) );
					newBinding.setAggrFunction( binding.getAggrFunction( ) );
					newBinding.setDisplayName( binding.getDisplayName( ) );
					newBinding.setExportable( binding.exportable( ) );
					newBinding.setFilter( newBinding.getFilter( ) );
					for ( int k = 0; k < binding.getArguments( ).size( ); k++ )
					{
						newBinding.addArgument( (IBaseExpression) binding.getArguments( )
								.get( k ) );
					}

					List aggrOns = binding.getAggregatOns( );
					
					if ( aggrOns != null && !aggrOns.isEmpty( ) )
					{
						boolean columnExist = false, rowExist = false;
						
						boolean detailLevelOnColumn = isGrandTotalOnEdge( aggrOns,
								dimLevelOnColumn );
						boolean detailLevelOnRow = isGrandTotalOnEdge( aggrOns,
								dimLevelOnRow );
						if ( !detailLevelOnColumn && !detailLevelOnRow )
						{
							continue;
						}

						if ( ( detailLevelOnColumn && columnDrill != null )
								|| ( detailLevelOnRow && rowDrill != null ) )
						{
							for ( int k = 0; k < aggrOns.size( ); k++ )
							{
								String aggrExpr = aggrOns.get( k ).toString( );
								DimLevel target = OlapExpressionUtil.getTargetDimLevel( aggrExpr );

								String dimensionNameOnColumn = columnDrill != null
										? columnDrill.getTargetHierarchy( )
												.getDimension( )
												.getName( ) : null;
								String dimensionNameOnRow = rowDrill != null
										? rowDrill.getTargetHierarchy( )
												.getDimension( )
												.getName( ) : null;
								if ( !columnExist
										&& target.getDimensionName( )
												.equals( dimensionNameOnColumn ) )
								{
									if ( !detailLevelOnColumn )
									{
										newBinding.addAggregateOn( aggrExpr );
									}
									else
									{
										columnExist = true;
										for ( int t = 0; t < levelDefnOnColumn.size( ); t++ )
											newBinding.addAggregateOn( (String) levelDefnOnColumn.get( t ) );
									}
								}
								else if ( !rowExist
										&& target.getDimensionName( )
												.equals( dimensionNameOnRow ) )
								{
									if ( !detailLevelOnRow )
									{
										newBinding.addAggregateOn( aggrExpr );
									}
									else
									{
										rowExist = true;
										for ( int t = 0; t < levelDefnOnRow.size( ); t++ )
											newBinding.addAggregateOn( (String) levelDefnOnRow.get( t ) );
									}
								}
								else if ( !target.getDimensionName( )
										.equals( dimensionNameOnColumn )
										&& !target.getDimensionName( )
												.equals( dimensionNameOnRow ) )
									newBinding.addAggregateOn( aggrExpr );
							}
							cloneQuery.addBinding( newBinding );
						}
					}
				}
				else
					cloneQuery.addBinding( (IBinding) query.getBindings( )
							.get( i ) );
			}
			for ( int i = 0; i < query.getFilters( ).size( ); i++ )
			{
				if ( canAddFilterToDrill( query, rowDrill, columnDrill, i ) )
				{
					cloneQuery.addFilter( (IFilterDefinition) query.getFilters( )
							.get( i ) );
				}
			}
			for ( int i = 0; i < query.getSorts( ).size( ); i++ )
			{
				cloneQuery.addSort( (ISortDefinition) query.getSorts( ).get( i ) );
			}
		}
		return cloneQuery;
	}

	private static boolean canAddFilterToDrill( ICubeQueryDefinition query,
			IEdgeDrillFilter rowDrill, IEdgeDrillFilter columnDrill, int i )
	{
		List<IDimensionDefinition> columnEdgeDimensions = null;

		if ( query.getEdge( ICubeQueryDefinition.COLUMN_EDGE ) != null )
			columnEdgeDimensions = query.getEdge( ICubeQueryDefinition.COLUMN_EDGE )
					.getDimensions( );

		List<IDimensionDefinition> rowEdgeDimensions = null;

		if ( query.getEdge( ICubeQueryDefinition.ROW_EDGE ) != null )
			rowEdgeDimensions = query.getEdge( ICubeQueryDefinition.ROW_EDGE )
					.getDimensions( );
		if( ! ( query.getFilters( ).get( i ) instanceof ICubeFilterDefinition ) )
		{
			return true;
		}
			
		ICubeFilterDefinition filter = (ICubeFilterDefinition) query.getFilters( )
				.get( i );

		if ( filter.getTargetLevel( ) == null )
			return false;

		int filterIndex = -1;
		int rowDrillIndex = -1;
		int columnDrillIndex = -1;
		int edgeTag = -1;
		boolean canAddFilter = false;

		if ( rowEdgeDimensions != null && rowDrill != null )
		{
			for ( int p = 0; p < rowEdgeDimensions.size( ); p++ )
			{
				IDimensionDefinition rowEdgeDimension = rowEdgeDimensions.get( p );
				String hierarchyName = rowEdgeDimension.getHierarchy( )
						.get( 0 )
						.getName( );

				if ( hierarchyName.equals( filter.getTargetLevel( )
						.getHierarchy( )
						.getName( ) ) )
				{
					filterIndex = p;
					edgeTag = ICubeQueryDefinition.ROW_EDGE;
				}
				if ( rowDrill.getTargetHierarchy( )
						.getName( )
						.equals( hierarchyName ) )
{'fraction_non_alphanumeric': 0.11615275340519762, 'fraction_numerical': 0.0030639815046934627, 'mean_word_length': 3.5342258145996466, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2898825', 'n_tokens_mistral': 14085, 'n_tokens_neox': 11371, 'n_words': 2155}