int currentGroupLevel, boolean isDetailedRow, Context cx )
int currentGroupLevel, int calculationLevel, boolean isDetailedRow, Context cx )
	private AggrExprInfo newAggrExprInfo( AggregateExpression expr,			int currentGroupLevel, boolean isDetailedRow, Context cx )			throws DataException	{		AggrExprInfo aggr = new AggrExprInfo( );		assert expr != null;		assert currentGroupLevel >= 0;		aggr.aggregation = expr.getAggregation( );		List exprArgs = expr.getArguments( );		// Find out how many fixed arguments this aggregate function takes		// Optional filter and group arguments follow the fixed arguments		int nFixedArgs = aggr.aggregation.getParameterDefn( ).length;		// Verify that the expression has the right # of arguments		int nArgs = exprArgs.size( );		if ( nArgs < nFixedArgs || nArgs > nFixedArgs + 2 )		{			DataException e = new DataException( ResourceConstants.INVALID_AGGR_PARAMETER,					aggr.aggregation.getName( ) );			throw e;		}				// Determine grouping level for this aggregate		// Look at the group level argument. If it is not present, or is null, the group level		// is the same group in which the aggregate expression is defined.		aggr.groupLevel = currentGroupLevel;		if ( nArgs == nFixedArgs + 2 )		{			CompiledExpression groupExpr = (CompiledExpression) exprArgs.get( nArgs - 1 );			if ( !( groupExpr instanceof ConstantExpression ) )			{				DataException e = new DataException( ResourceConstants.INVALID_AGGR_GROUP_EXPRESSION,						aggr.aggregation.getName( ) );				throw e;			}			// Note that we use the data engine's shared scope for this			// evaluation. Group expression			// is not expected to depend on any query execution. In fact it			// should just be a constant			// expression most of the case			Object groupLevelObj = groupExpr.evaluate( cx,					runStates == BASE_QUERY ? cx.initStandardObjects( )							: scope );			if ( groupLevelObj == null )			{				// null argument; use default level			}			else if ( groupLevelObj instanceof String )			{				int innerMostGroup = 0;				if ( runStates == PREPARED_QUERY )				{					innerMostGroup = groupCount;				}				else				{					innerMostGroup = baseQuery.getGrouping( ) != null							? baseQuery.getGrouping( ).length : 0;				}					int groupLevel = AggregationConstantsUtil.getGroupLevel( (String)groupLevelObj, currentGroupLevel,innerMostGroup, isDetailedRow);				//When the groupLevelObj can be recognized, it will return a non-negative value.Else return -1.				if ( groupLevel != -1 )				{					aggr.groupLevel = groupLevel;				}				else				{					aggr.groupLevel = ( runStates == BASE_QUERY )							? getGroupIndex( groupLevelObj.toString( ) )							: getGroupIndexFromPreparedQuery( (String) groupLevelObj );				}			}			else if ( groupLevelObj instanceof Number )			{				int offset = ( (Number) groupLevelObj ).intValue( );				if ( offset < 0 )					aggr.groupLevel = currentGroupLevel + offset;				else					aggr.groupLevel = offset;			}			if ( aggr.groupLevel < 0					|| aggr.groupLevel > ( runStates == BASE_QUERY							? ( baseQuery.getGrouping( ) == null ? 0									: baseQuery.getGrouping( ).length )							: groupCount ) )			{				DataException e = new DataException( ResourceConstants.INVALID_GROUP_LEVEL,						aggr.aggregation.getName( ) );				throw e;			}		}		// Extract filter parameter		if ( nArgs > nFixedArgs )		{			aggr.filter = (CompiledExpression) exprArgs.get( nFixedArgs );			// If filter expression is a constant "null", ignore it			if ( aggr.filter instanceof ConstantExpression					&& ( (ConstantExpression) aggr.filter ).getValue( ) == null )			{				aggr.filter = null;			}		}		aggr.args = new CompiledExpression[nFixedArgs];		if ( nFixedArgs > 0 )		{			exprArgs.subList( 0, nFixedArgs ).toArray( aggr.args );		}		return aggr;	}