//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997 - 2000. // // File: strgroup.cxx // // Contents: Builds a nesting node object from a GroupBy string // // History: 10 Apr 1997 AlanW Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include static GUID guidBmk = DBBMKGUID; static CDbColId psBookmark( guidBmk, PROPID_DBBMK_BOOKMARK ); //+--------------------------------------------------------------------------- // // Function: GetStringDbGroupNode - public // // Synopsis: Builds a CDbNestingNode from the string passed // // Arguments: [pwszGroup] - the string containing the grouping specification // [pList] - the property list describing the column names // // Notes: CPListException is used instead of CParserException so the // error class will be IDQError, not RESError in idq.dll. // // History: 10 Apr 1997 AlanW Created. // //---------------------------------------------------------------------------- CDbNestingNode * GetStringDbGroupNode( const WCHAR * pwszGroup, IColumnMapper * pList ) { Win4Assert( 0 != pwszGroup ); CQueryScanner scanner( pwszGroup, FALSE, GetUserDefaultLCID(), TRUE ); CParseGrouping ParseObj( scanner, pList, FALSE ); ParseObj.Parse(); return ParseObj.AcquireNode(); } //+--------------------------------------------------------------------------- // // Method: CParseGrouping::Parse, public // // Synopsis: Parse a GroupBy string, construct the nesting node // // Arguments: -NONE- uses previously set member variables // // Notes: CPListException is used instead of CParserException so the // error class will be IDQError, not RESError in idq.dll. // // History: 10 Apr 1997 AlanW Created. // //---------------------------------------------------------------------------- void CParseGrouping::Parse( ) { while (_Scanner.LookAhead() != EOS_TOKEN) { XPtr xNode( ParseGrouping() ); if (_Scanner.LookAhead() == COMMA_TOKEN) { _Scanner.Accept(); } else if (_Scanner.LookAhead() != EOS_TOKEN) { THROW( CPListException(QPARSE_E_EXPECTING_COMMA, 0) ); } if (0 == _cNestings) { Win4Assert( _xNode.GetPointer() == 0 ); _xNode.Set( xNode.Acquire() ); } else { CDbNestingNode * pNode = _xNode.GetPointer(); for (unsigned i=1; i < _cNestings; i++) { Win4Assert( pNode->GetCommandType() == DBOP_nesting ); pNode = (CDbNestingNode *)pNode->GetFirstChild(); } Win4Assert( pNode != 0 && pNode->GetFirstChild()->GetCommandType() == DBOP_project_list_anchor ); pNode->AddTable( xNode.Acquire() ); } _cNestings++; } if (_fNeedSortNode) AddSortList( _xSortNode ); } //+--------------------------------------------------------------------------- // // Method: CParseGrouping::ParseGrouping, private // // Synopsis: Parse an individual grouping specification // // Arguments: -NONE- uses previously set member variables // // Notes: The scanner is advanced past the parsed grouping spec. // // History: 10 Apr 1997 AlanW Created. // //---------------------------------------------------------------------------- CDbNestingNode * CParseGrouping::ParseGrouping( ) { CDbNestingNode * pDbNestingNode = new CDbNestingNode(); if ( 0 == pDbNestingNode ) { THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } XPtr xDbNestingNode(pDbNestingNode); // // Parse any leading grouping type specification. // ULONG ulCatType = eUnique; // Assume unique value categories if ( _Scanner.LookAhead() == W_OPEN_TOKEN ) ulCatType = ParseGroupingType(); // NOTE: only the unique categorization is implemented. if (ulCatType != eUnique) THROW( CPListException(QPARSE_E_INVALID_GROUPING, 0) ); if ( 0 == _xSortNode.GetPointer() ) { CDbSortNode * pDbSortNode = new CDbSortNode(); if ( 0 == pDbSortNode ) { THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } _xSortNode.Set(pDbSortNode); } BOOL fFirstGroupingColumn = TRUE; for ( XPtrST pwszColumnName( _Scanner.AcqColumn() ); pwszColumnName.GetPointer() != 0; pwszColumnName.Set( _Scanner.AcqColumn() ) ) { _Scanner.AcceptColumn(); // Remove the column name // // Parse for the [a] or [d] parameters. // unsigned order = QUERY_SORTASCEND; // Assume ascending sort order if ( _Scanner.LookAhead() == W_OPEN_TOKEN ) { _Scanner.Accept(); // Remove the '[' WCHAR wchOrder = _Scanner.GetCommandChar(); if ( wchOrder == L'a' ) { order = QUERY_SORTASCEND; } else if ( wchOrder == L'd' ) { order = QUERY_SORTDESCEND; _fNeedSortNode = TRUE; } else { // Report an error THROW( CPListException(QPARSE_E_INVALID_SORT_ORDER, 0) ); } WCHAR wchNext = _Scanner.GetCommandChar(); if (wchNext != 0 && !iswspace(wchNext)) // some alphabetic character followed the '[a' or '[d'. THROW( CPListException(QPARSE_E_INVALID_SORT_ORDER, 0) ); _Scanner.AcceptCommand(); // Remove the command character if ( _Scanner.LookAhead() != W_CLOSE_TOKEN ) { // Report an error THROW( CPListException(QPARSE_E_EXPECTING_BRACE, 0) ); } _Scanner.Accept(); // Remove the ']' character } // // Add the new grouping column to the nesting node // CDbColId *pDbColId = 0; DBID *pdbid = 0; if ( FAILED(_xPropList->GetPropInfoFromName( pwszColumnName.GetPointer(), &pdbid, 0, 0 )) ) { // // Column name not found. // THROW( CPListException(QPARSE_E_NO_SUCH_SORT_PROPERTY, 0) ); } pDbColId = (CDbColId *)pdbid; if ( fFirstGroupingColumn ) { fFirstGroupingColumn = FALSE; if ( !pDbNestingNode->AddParentColumn(psBookmark) ) { // Report a failure. THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } } if ( !pDbNestingNode->AddParentColumn(*pDbColId) ) { // Report a failure. THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } BOOL fFail = _fKeepFriendlyNames ? !pDbNestingNode->AddGroupingColumn(*pDbColId, pwszColumnName.GetPointer()) : !pDbNestingNode->AddGroupingColumn(*pDbColId); if (fFail) { // Report a failure. THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } // // Add the column to the sort node in case we will need it. // CDbSortKey sortKey( *pDbColId, order == QUERY_SORTDESCEND ); if ( !_xSortNode->AddSortColumn(sortKey) ) { // Report a failure. THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); } pwszColumnName.Free(); // // Skip over +'s seperating grouping columns // if ( _Scanner.LookAhead() == PLUS_TOKEN ) { // We don't support this syntax (or document it) //_Scanner.Accept(); // Remove the '+' THROW( CPListException(QPARSE_E_INVALID_GROUPING, 0) ); } // // Find a new grouping specification if a comma is found if ( _Scanner.LookAhead() == COMMA_TOKEN ) { break; } } return xDbNestingNode.Acquire(); } //+--------------------------------------------------------------------------- // // Method: CParseGrouping::ParseGroupingType, private // // Synopsis: Parses and returns the grouping type. // // Arguments: -NONE- // // Returns: ECategoryType - grouping type found if valid // // Notes: Upon return, the scanner is advanced past the grouping type. // Throws CPListException in case of syntax errors // // History: 10 Apr 1997 AlanW Created. // //---------------------------------------------------------------------------- CParseGrouping::ECategoryType CParseGrouping::ParseGroupingType( ) { Win4Assert( _Scanner.LookAhead() == W_OPEN_TOKEN ); _Scanner.Accept(); // Remove the '[' XPtrST pwszCategoryName( _Scanner.AcqWord() ); ECategoryType CatType = eInvalidCategory; if ( pwszCategoryName.IsNull() ) THROW( CPListException(QPARSE_E_INVALID_GROUPING, 0) ); if ( _wcsicmp( pwszCategoryName.GetPointer(), L"unique" ) == 0 ) { CatType = eUnique; } else if ( _wcsicmp( pwszCategoryName.GetPointer(), L"quantile" ) == 0 ) { CatType = eBuckets; } else if ( _wcsicmp( pwszCategoryName.GetPointer(), L"cluster" ) == 0 ) { CatType = eCluster; } else if ( _wcsicmp( pwszCategoryName.GetPointer(), L"range" ) == 0 ) { CatType = eRange; } else if ( _wcsicmp( pwszCategoryName.GetPointer(), L"time" ) == 0 ) { CatType = eTime; } else if ( _wcsicmp( pwszCategoryName.GetPointer(), L"alpha" ) == 0 ) { CatType = eAlpha; } else { // Report an error THROW( CPListException(QPARSE_E_INVALID_GROUPING, 0) ); } _Scanner.AcceptWord(); // Remove the grouping type name if ( _Scanner.LookAhead() != W_CLOSE_TOKEN ) { // Report an error THROW( CPListException(QPARSE_E_EXPECTING_BRACE, 0) ); } _Scanner.Accept(); // Remove the ']' character return CatType; } //+--------------------------------------------------------------------------- // // Method: CParseGrouping::AddSortList, private // // Synopsis: Add sort columns. // // Arguments: [SortNode] - a smart pointer to the sort node to be added // // Notes: A sort list is added below the lowermost nesting node. // If there already is a sort node there, the sort columns are // appended to the list. // // History: 21 Apr 1997 AlanW Created. // //---------------------------------------------------------------------------- void CParseGrouping::AddSortList( XPtr & SortNode ) { Win4Assert( _cNestings > 0 ); CDbNestingNode * pNode = _xNode.GetPointer(); for (unsigned i=1; i < _cNestings; i++) { Win4Assert( pNode->GetCommandType() == DBOP_nesting ); pNode = (CDbNestingNode *)pNode->GetFirstChild(); } Win4Assert( pNode != 0 && (pNode->GetFirstChild()->GetCommandType() == DBOP_project_list_anchor || pNode->GetFirstChild()->GetCommandType() == DBOP_sort )); if (pNode->GetFirstChild()->GetCommandType() == DBOP_project_list_anchor) { // No sort node yet. Add a node. if (_xSortNode.GetPointer()) { pNode->AddTable( _xSortNode.Acquire() ); if ( 0 == SortNode.GetPointer() ) { // // If called with _xSortNode, the Acquire above cleared the // smart pointer. We're done. // return; } } else { pNode->AddTable( SortNode.Acquire() ); return; } } Win4Assert(pNode->GetFirstChild()->GetCommandType() == DBOP_sort); Win4Assert(pNode->GetFirstChild()->GetFirstChild()->GetCommandType() == DBOP_sort_list_anchor); Win4Assert(0 == _xSortNode.GetPointer()); // The sort list elements from SortNode will be carved off and appended // to the list in pSLA. CDbSortListAnchor * pSLA = (CDbSortListAnchor *)pNode->GetFirstChild()->GetFirstChild(); Win4Assert(SortNode.GetPointer() != 0); Win4Assert(SortNode->GetCommandType() == DBOP_sort); Win4Assert(SortNode->GetFirstChild()->GetCommandType() == DBOP_sort_list_anchor); XPtr xSLE ( (CDbSortListElement *) SortNode->GetFirstChild()->AcquireChildren() ); if (xSLE.GetPointer()) { pSLA->AppendList( xSLE.GetPointer() ); xSLE.Acquire(); } }