/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: SQL1FILT.CPP Abstract: History: --*/ #include "sql1filt.h" void* CSql1Filter::GetInterface(REFIID riid) { if(riid == IID_IHmmFilter || riid == IID_IHmmSql1Filter) return (IHmmSql1Filter*)&m_XFilter; else if(riid == IID_IConfigureHmmSql1Filter) return (IConfigureHmmSql1Filter*)&m_XConfigure; else if(riid == IID_IHmmParse) return (IHmmParse*)&m_XParse; else return NULL; } STDMETHODIMP CSql1Filter::XFilter:: IsSpecial() { if(m_pObject->m_apTokens.GetSize() == 0) { return HMM_S_ACCEPTS_EVERYTHING; } else { return HMM_S_FALSE; } } STDMETHODIMP CSql1Filter::XFilter:: CheckObject(IN IHmmPropertySource* pSource, OUT IHmmPropertyList** ppList, OUT IUnknown** ppHint) { if(ppHint) *ppHint = NULL; return m_pObject->Evaluate(FALSE, pSource, ppList); } STDMETHODIMP CSql1Filter::XFilter:: GetType(IID* piid) { if(piid == NULL) return HMM_E_INVALID_PARAMETER; *piid = IID_IHmmSql1Filter; return HMM_S_NO_ERROR; } STDMETHODIMP CSql1Filter::XFilter:: GetSelectedPropertyList(IN long lFlags, OUT IHmmPropertyList** ppList) { return m_pObject->m_TargetClass.GetSelected().QueryInterface( IID_IHmmPropertyList, (void**)ppList); } STDMETHODIMP CSql1Filter::XFilter:: GetTargetClass(OUT HMM_CLASS_INFO* pTargetClass) { if(pTargetClass == NULL) return HMM_E_INVALID_PARAMETER; m_pObject->m_XParse.EnsureTarget(); m_pObject->m_TargetClass.Save(*pTargetClass); return HMM_S_NO_ERROR; } STDMETHODIMP CSql1Filter::XFilter:: GetTokens(IN long lFirstIndex, IN long lNumTokens, OUT long* plTokensReturned, OUT HMM_SQL1_TOKEN* aTokens) { if(plTokensReturned == NULL || lNumTokens <= 0 || lFirstIndex < 0 || aTokens == NULL) { return HMM_E_INVALID_PARAMETER; } m_pObject->m_XParse.EnsureWhere(); long lHaveTokens = m_pObject->m_apTokens.GetSize(); if(lFirstIndex >= lHaveTokens) { *plTokensReturned = 0; return HMM_S_NO_MORE_DATA; } long lGaveTokens = 0; long lCurrentIndex = lFirstIndex; while(lGaveTokens < lNumTokens && lCurrentIndex < lHaveTokens) { CSql1Token* pToken = m_pObject->m_apTokens[lCurrentIndex]; if(pToken->m_lTokenType != TOKENTYPE_SPECIAL) { pToken->Save(aTokens[lGaveTokens++]); } lCurrentIndex++; } *plTokensReturned = lGaveTokens; return HMM_S_NO_ERROR; } //********************************* Configuration ****************************** STDMETHODIMP CSql1Filter::XConfigure:: SetTargetClass(IN HMM_CLASS_INFO* pTargetClass) { if(pTargetClass == NULL) return HMM_E_INVALID_PARAMETER; m_pObject->m_TargetClass.Load(*pTargetClass); m_pObject->InvalidateTree(); return HMM_S_NO_ERROR; } STDMETHODIMP CSql1Filter::XConfigure:: AddTokens(IN long lNumTokens, IN HMM_SQL1_TOKEN* aTokens) { if(lNumTokens <= 0 || aTokens == NULL) return HMM_E_INVALID_PARAMETER; for(long l = 0; l < lNumTokens; l++) { m_pObject->m_apTokens.Add(new CSql1Token(aTokens[l])); } m_pObject->InvalidateTree(); return HMM_S_NO_ERROR; } STDMETHODIMP CSql1Filter::XConfigure:: RemoveAllTokens() { m_pObject->m_apTokens.RemoveAll(); m_pObject->InvalidateTree(); return HMM_S_NO_ERROR; } STDMETHODIMP CSql1Filter::XConfigure:: RemoveAllProperties() { return m_pObject->m_TargetClass.GetSelected().RemoveAllProperties(); } STDMETHODIMP CSql1Filter::XConfigure:: AddProperties(IN long lNumProps, HMM_WSTR* awszProps) { return m_pObject->m_TargetClass.GetSelected(). AddProperties(lNumProps, awszProps); } //*********************************** Parsing ********************************* STDMETHODIMP CSql1Filter::XParse:: Parse(IN HMM_WSTR wszText, IN long lFlags) { delete m_pParser; m_pParser = NULL; if((lFlags & HMM_FLAG_LAZY) == 0) { m_wsText.Empty(); m_nStatus = not_parsing; m_pObject->InvalidateTree(); CAbstractSql1Parser Parser(new CTextLexSource(wszText)); int nRes = Parser.Parse(this, CAbstractSql1Parser::FULL_PARSE); m_InnerStack.Empty(); return ParserError(nRes); } else { m_wsText = wszText; m_pParser = new CAbstractSql1Parser(new CTextLexSource((LPWSTR)m_wsText)); m_nStatus = at_0; return HMM_S_NO_ERROR; } } void CSql1Filter::XParse::SetClassName(LPCWSTR wszClass) { m_pObject->m_TargetClass.AccessClassName() = (LPWSTR)wszClass; m_pObject->m_TargetClass.AccessIncludeChildren() = TRUE; } void CSql1Filter::XParse::AddToken(COPY const HMM_SQL1_TOKEN& Token) { m_pObject->m_apTokens.Add(new CSql1Token(Token)); if(Token.m_lTokenType == SQL1_OR || Token.m_lTokenType == SQL1_AND) { // Find the inner guy // ================== long lInnerIndex; if(!m_InnerStack.Pop(lInnerIndex)) return; V_I4(&m_pObject->m_apTokens[lInnerIndex]->m_vConstValue) = m_pObject->m_apTokens.GetSize(); } } void CSql1Filter::XParse::AddProperty(COPY LPCWSTR wszProperty) { m_pObject->m_TargetClass.GetSelected(). AddProperties(1, (LPWSTR*)&wszProperty); } void CSql1Filter::XParse::AddAllProperties() { m_pObject->m_TargetClass.GetSelected().AddAllProperties(); } void CSql1Filter::XParse::InOrder(long lOp) { /* CSql1Token* pToken = new CSql1Token; pToken->m_lTokenType = TOKENTYPE_SPECIAL; pToken->m_lOperator = lOp; V_I4(&pToken->m_vConstValue) = -1; m_InnerStack.Push(m_pObject->m_apTokens.GetSize()); m_pObject->m_apTokens.Add(pToken); */ } HRESULT CSql1Filter::XParse::EnsureTarget() { if(m_nStatus == at_0) { m_pObject->InvalidateTree(); int nRes = m_pParser->Parse(this, CAbstractSql1Parser::NO_WHERE); m_nStatus = at_where; return ParserError(nRes); } else return HMM_S_NO_ERROR; } HRESULT CSql1Filter::XParse::EnsureWhere() { if(m_nStatus == not_parsing) { return HMM_S_NO_ERROR; } else { m_pObject->InvalidateTree(); int nRes = m_pParser->Parse(this, (m_nStatus == at_0)?CAbstractSql1Parser::FULL_PARSE :CAbstractSql1Parser::JUST_WHERE); m_InnerStack.Empty(); delete m_pParser; m_pParser = NULL; m_nStatus = not_parsing; return ParserError(nRes); } } HRESULT CSql1Filter::XParse::ParserError(int nRes) { switch(nRes) { case CAbstractSql1Parser::SUCCESS: return HMM_S_NO_ERROR; case CAbstractSql1Parser::SYNTAX_ERROR: return HMM_E_INVALID_QUERY; case CAbstractSql1Parser::LEXICAL_ERROR: return HMM_E_INVALID_QUERY; case CAbstractSql1Parser::FAILED: return HMM_E_FAILED; default: return HMM_E_CRITICAL_ERROR; } } CSql1Filter::XParse::~XParse() { delete m_pParser; } //***************************************************************************** //*************************** Query Evaluator ********************************* //***************************************************************************** //***************************************************************************** // // See sql1eveal.h for documentation // //***************************************************************************** HRESULT CSql1Filter::Evaluate(BOOL bSkipTarget, IN READ_ONLY IHmmPropertySource* pPropSource, OUT IHmmPropertyList** ppList) { HRESULT hres; GetTree(); if(m_pTree) m_pTree->Release(); if(ppList) *ppList = NULL; hres = CHmmNode::EvaluateNode(m_pTree, pPropSource); if(hres != HMM_S_NO_ERROR) return hres; if(ppList) { m_TargetClass.GetSelected().QueryInterface(IID_IHmmPropertyList, (void**)ppList); } return HMM_S_NO_ERROR; if(!bSkipTarget) { // Check the class of the object // ============================= hres = m_XParse.EnsureTarget(); if(FAILED(hres)) return hres; CHmmClassInfo* pInfo = &m_TargetClass; hres = CHmmClassInfo::CheckObjectAgainstMany(1, &pInfo, pPropSource, ppList, NULL); if(hres != HMM_S_NO_ERROR) { // Either an error or a simple class mismatch // ========================================== return hres; } } // Now go for the expression // ========================= hres = m_XParse.EnsureWhere(); if(FAILED(hres)) return hres; int nNumTokens = m_apTokens.GetSize(); if(nNumTokens == 0) { // Empty query // =========== return HMM_S_NO_ERROR; } // Allocate boolean stack of appropriate length // ============================================ CBooleanStack Stack(nNumTokens); for(int nTokenIndex = 0; nTokenIndex < nNumTokens; nTokenIndex++) { CSql1Token* pToken = m_apTokens[nTokenIndex]; BOOL bVal1, bVal2; switch(pToken->m_lTokenType) { case SQL1_AND: // Pop the last two operands and AND them together // =============================================== if(!Stack.Pop(bVal1) || !Stack.Pop(bVal2)) { return HMM_E_INVALID_QUERY; } Stack.Push(bVal1 && bVal2); break; case SQL1_OR: // Pop the last two operands and OR them together // =============================================== if(!Stack.Pop(bVal1) || !Stack.Pop(bVal2)) { return HMM_E_INVALID_QUERY; } Stack.Push(bVal1 || bVal2); break; case SQL1_NOT: // Pop the last value and invert it // ================================ if(!Stack.Pop(bVal1)) { return HMM_E_INVALID_QUERY; } Stack.Push(!bVal1); break; case SQL1_OP_EXPRESSION: // Evaluate the expression and push its value // ========================================== hres = pToken->Evaluate(pPropSource); if(FAILED(hres)) return hres; bVal1 = (hres == HMM_S_NO_ERROR); Stack.Push(bVal1); break; case TOKENTYPE_SPECIAL: if(!Stack.Pop(bVal1)) { return HMM_E_INVALID_QUERY; } Stack.Push(bVal1); if((pToken->m_lOperator == SQL1_OR && bVal1) || (pToken->m_lOperator == SQL1_AND && !bVal1)) { nTokenIndex = V_I4(&pToken->m_vConstValue) - 1; } break; } } // All tokens have been processed. There better be one element on the stack // ======================================================================== BOOL bResult; if(!Stack.Pop(bResult)) { return HMM_E_INVALID_QUERY; } if(!Stack.IsEmpty()) { return HMM_E_INVALID_QUERY; } if(bResult) return HMM_S_NO_ERROR; else return HMM_S_FALSE; } CHmmNode* CSql1Filter::GetTree() { if(m_pTree != NULL) { m_pTree->AddRef(); return m_pTree; } // Construct the tree from the RPN // =============================== CUniquePointerStack aNodes(m_apTokens.GetSize()); CHmmNode* pNode1; CHmmNode* pNode2; CLogicalNode* pNewNode; CSql1Token* pNewToken; for(int i = 0; i < m_apTokens.GetSize(); i++) { CSql1Token* pToken = m_apTokens[i]; switch(pToken->m_lTokenType) { case SQL1_OR: case SQL1_AND: if(!aNodes.Pop(pNode2)) return NULL; if(!aNodes.Pop(pNode1)) return NULL; pNewNode = new CLogicalNode; pNewNode->m_lTokenType = pToken->m_lTokenType; pNewNode->Add(pNode1); pNewNode->Add(pNode2); aNodes.Push(pNewNode); break; case SQL1_NOT: if(!aNodes.Pop(pNode1)) return NULL; pNode1->Negate(); aNodes.Push(pNode1); break; case SQL1_OP_EXPRESSION: pNewToken = new CSql1Token(*pToken); aNodes.Push(pNewToken); break; default: return NULL; } } CHmmNode* pWhere; if(!aNodes.Pop(pWhere)) { if(i == 0) pWhere = NULL; else return NULL; } if(!aNodes.IsEmpty()) return NULL; // Add the class check // =================== CHmmNode* pTarget = m_TargetClass.GetTree(); CLogicalNode* pMain = new CLogicalNode; pMain->m_lTokenType = SQL1_AND; pMain->Add(pTarget); pTarget->Release(); // Matching GetTree() if(pWhere) { pMain->Add(pWhere); } m_pTree = pMain; m_pTree->AddRef(); // for storage m_pTree->AddRef(); // for return m_pTree->Print(stdout, 0); return m_pTree; }