windows-nt/Source/XPSP1/NT/enduser/speech/tts/prompts/engine/dbquery.cpp
2020-09-26 16:20:57 +08:00

1540 lines
42 KiB
C++

//////////////////////////////////////////////////////////////////////
// DbQuery.cpp: implementation of the CDbQuery class.
//
// Created by JOEM 03-2000
// Copyright (C) 2000 Microsoft Corporation
// All Rights Reserved
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
#include "stdafx.h"
#include "DbQuery.h"
#include "PromptDb.h"
#include <LIMITS>
//////////////////////////////////////////////////////////////////////
// CEquivCost
//
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CEquivCost::CEquivCost()
{
text = NULL;
entry = NULL;
fTagMatch = true;
}
CEquivCost::CEquivCost(const CEquivCost& old)
{
if ( text )
{
text = wcsdup(old.text);
}
else
{
text = NULL;
}
if ( entry )
{
entry = new CPromptEntry(*old.entry);
}
else
{
entry = NULL;
}
cost = old.cost;
whereFrom = old.whereFrom;
fTagMatch = old.fTagMatch;
}
CEquivCost::~CEquivCost()
{
if ( text )
{
free(text);
text = NULL;
}
if ( entry )
{
entry->Release();
entry = NULL;
}
}
//////////////////////////////////////////////////////////////////////
// CCandidate
//
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCandidate::CCandidate()
{
equivList = NULL;
parent = NULL;
firstPos = 0;
lastPos = 0;
candMax = 0;
candNum = 0;
iQueryNum = 0;
}
CCandidate::CCandidate(const CCandidate& old)
{
CEquivCost* equiv = NULL;
firstPos = old.firstPos;
lastPos = old.lastPos;
candMax = old.candMax;
candNum = old.candNum;
iQueryNum = old.iQueryNum;
parent = old.parent;
if ( old.equivList && old.equivList->GetSize() )
{
equivList = new CSPArray<CEquivCost*,CEquivCost*>;
for (USHORT i=0; i<old.equivList->GetSize(); i++)
{
equiv = new CEquivCost( *(*old.equivList)[i] );
equivList->Add(equiv);
}
}
}
CCandidate::~CCandidate()
{
USHORT i = 0;
if ( equivList )
{
for ( i=0; i<equivList->GetSize(); i++ )
{
if ( (*equivList)[i] )
{
delete (*equivList)[i];
(*equivList)[i] = NULL;
}
}
equivList->RemoveAll();
delete equivList;
}
parent = NULL;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery
//
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDbQuery::CDbQuery()
{
m_pIDb = NULL;
m_pPhoneContext = NULL;
m_papQueries = NULL;
m_pOutputSite = NULL;
m_iCurrentQuery = 0;
m_fAbort = false;
}
CDbQuery::~CDbQuery()
{
USHORT i = 0;
if ( m_pIDb )
{
m_pIDb->Release();
m_pIDb = NULL;
}
// These are just pointers set in CDbQuery::Init and CDbQuery::Query (deallocated elsewhere)
m_pPhoneContext = NULL;
m_papQueries = NULL;
// To be safe, I'll keep this list deletion here.
// However, it is generally deleted when CDbQuery::Query calls CDbQuery::Reset
m_apCandEnd.RemoveAll();
for ( i=0; i<m_apCandidates.GetSize(); i++ )
{
if ( m_apCandidates[i] )
{
delete m_apCandidates[i];
m_apCandidates[i] = NULL;
}
}
m_apCandidates.RemoveAll();
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::Init
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::Init(IPromptDb *pIDb, CPhoneContext *pPhoneContext)
{
SPDBG_FUNC( "CDbQuery::Init" );
HRESULT hr = S_OK;
SPDBG_ASSERT(pIDb);
SPDBG_ASSERT(pPhoneContext);
m_pIDb = pIDb;
m_pIDb->AddRef();
m_pPhoneContext = pPhoneContext;
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::Reset
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::Reset()
{
SPDBG_FUNC( "CDbQuery::Reset" );
USHORT i = 0;
m_iCurrentQuery = 0;
m_papQueries = NULL;
m_apCandEnd.RemoveAll();
for ( i=0; i<m_apCandidates.GetSize(); i++ )
{
if ( m_apCandidates[i] )
{
delete m_apCandidates[i];
m_apCandidates[i] = NULL;
}
}
m_apCandidates.RemoveAll();
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::Query
//
// Takes a query list, created in CPromptEng::BuildQueryList, and
// figures out which can be handled with prompts, and which must
// go to TTS. Computes transition costs between candidate items,
// and Backtracks the list selecting the minimum cost items.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::Query(CSPArray<CQuery*,CQuery*>* papQueries, double* pdCost,
ISpTTSEngineSite* pOutputSite, bool *fAbort)
{
SPDBG_FUNC( "CDbQuery::Query" );
HRESULT hr = S_OK;
USHORT i = 0;
USHORT j = 0;
USHORT unIdSize = 0;
const WCHAR* pszId = NULL;
WCHAR* pszText = NULL;
CQuery* pQuery = NULL;
SPDBG_ASSERT(papQueries);
SPDBG_ASSERT(pOutputSite);
m_pOutputSite = pOutputSite;
m_papQueries = papQueries;
m_fAbort = false;
for ( i=0; SUCCEEDED(hr) && i < m_papQueries->GetSize(); i++ )
{
if ( m_pOutputSite->GetActions() & SPVES_ABORT )
{
m_fAbort = true;
break;
}
if ( SUCCEEDED(hr) )
{
pQuery = (*m_papQueries)[i];
m_iCurrentQuery = i;
if ( !pQuery->m_fSpeak )
{
continue;
}
}
if ( SUCCEEDED(hr) )
{
if ( pQuery->m_fXML )
{
// If the database needs to be changed, do it now.
if ( pQuery->m_unDbAction )
{
switch ( pQuery->m_unDbAction )
{
case DB_ADD:
if ( !pQuery->m_pszDbPath )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED(hr) )
{
hr = m_pIDb->AddDb(pQuery->m_pszDbName, pQuery->m_pszDbPath, pQuery->m_pszDbIdSet, DB_LOAD);
}
if ( SUCCEEDED(hr) )
{
free( pQuery->m_pszDbName );
pQuery->m_pszDbName = NULL;
}
break;
case DB_ACTIVATE:
if ( !pQuery->m_pszDbName )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED(hr) )
{
hr = m_pIDb->ActivateDbName(pQuery->m_pszDbName);
}
break;
case DB_UNLOAD:
hr = m_pIDb->UnloadDb(pQuery->m_pszDbName);
break;
default:
break;
}
continue;
} // if ( pQuery->m_unDbAction )
// ID search
else if ( pQuery->m_pszId )
{
hr = SearchId( pQuery->m_pszId );
continue;
}
else
{
// Any XML besides DATABASE or ID can be ignored here.
if ( pQuery->m_fXML == UNKNOWN_XML )
{
pQuery->m_fTTS = true;
}
continue;
}
} // if ( pQuery->m_fXML )
} // if ( SUCCEEDED(hr) )
// For Text, search in the Db first, then do SearchBackup (for TTS costs)
if ( SUCCEEDED (hr) )
{
if ( !pQuery->m_fTTS )
{
hr = SearchText( pQuery->m_pszExpandedText, pQuery->m_paTagList );
if ( FAILED(hr) )
{
pQuery->m_afEntryMatch.Add(false);
if ( pQuery->m_fFragType )
{
hr = RemoveLocalQueries(i);
}
}
}
// SearchBackup for TTS items, or if SearchText failed.
if ( pQuery->m_fSpeak && ( pQuery->m_fTTS || FAILED(hr) ) )
{
pszText = new WCHAR[pQuery->m_pTextFrag->ulTextLen + 1];
if ( pszText )
{
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
wcsncpy(pszText, pQuery->m_pTextFrag->pTextStart, pQuery->m_pTextFrag->ulTextLen);
pszText[pQuery->m_pTextFrag->ulTextLen] = L'\0';
hr = SearchBackup( pszText );
delete pszText;
pszText = NULL;
}
if ( SUCCEEDED(hr) )
{
pQuery->m_fTTS = true;
pQuery->m_afEntryMatch.Add(true);
}
}
}
} // for ( i=0; i < m_papQueries->GetSize(); i++ )
// Backtrack, selecting the search items with min. cost
if ( SUCCEEDED(hr) && !m_fAbort )
{
hr = Backtrack( pdCost );
}
Reset();
*fAbort = m_fAbort;
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::SearchId
//
// Checks the Db for specific Ids, adds them to the candidate list
// for backtracking later (in CDbQuery::Query).
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::SearchId(const WCHAR* pszId)
{
SPDBG_FUNC( "CDbQuery::SearchId" );
HRESULT hr = S_OK;
CCandidate* newCand = NULL;
IPromptEntry* pIPE = NULL;
USHORT i = 0;
SPDBG_ASSERT( pszId );
hr = m_pIDb->FindEntry(pszId, &pIPE);
if ( SUCCEEDED(hr) )
{
newCand = new CCandidate;
if ( !newCand )
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
newCand->firstPos = 0;
newCand->lastPos = 0;
newCand->candMax = 1;
newCand->candNum = 0;
newCand->parent = NULL;
newCand->iQueryNum = m_iCurrentQuery;
hr = ComputeCostsId (newCand, pIPE);
}
if ( SUCCEEDED(hr) )
{
m_apCandidates.Add(newCand);
m_apCandEnd.RemoveAll();
m_apCandEnd.Add(newCand);
}
if ( FAILED(hr) && newCand )
{
delete newCand;
newCand = NULL;
}
pIPE->Release();
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::SearchText
//
// Searches the Db for text. If found, adds the items (with computed
// transition costs) to the candidate list for backtracking later
// (in CDbQuery::Query).
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::SearchText(WCHAR *pszText, const CSPArray<CDynStr,CDynStr>* tags)
{
SPDBG_FUNC( "CDbQuery::SearchText" );
HRESULT hr = S_OK;
WCHAR** wordList = NULL;
USHORT wordCount = 0;
int i = 0; // THIS MUST BE SIGNED
int j = 0;
int k = 0;
bool* pfSearch = NULL;
CSPArray<CCandidate*,CCandidate*>* activeCand;
CSPArray<CCandidate*,CCandidate*>* tmpCandEnd;
CCandidate* newCand = NULL;
hr = SplitWords (pszText, &wordList, &wordCount);
if ( SUCCEEDED(hr) )
{
hr = RemovePunctuation(wordList, &wordCount);
}
if ( wordCount <= 0 )
{
return hr;
}
// These flags turn on the searches that start with word position i
// A search is later turned on if a previous search ended at i-1.
pfSearch = new bool[wordCount];
if ( !pfSearch )
{
hr = E_OUTOFMEMORY;
}
else
{
pfSearch[0] = true; // always do the first search
for ( i=1; i<wordCount; i++ )
{
pfSearch[i] = false;
}
}
// Initialize stack decoder
if ( SUCCEEDED(hr) )
{
activeCand = new CSPArray<CCandidate*,CCandidate*>;
if ( !activeCand )
{
hr = E_OUTOFMEMORY;
}
}
// Get the active candidate list from previous search, if any.
if ( SUCCEEDED(hr) )
{
// Any previous candidates?
if ( m_apCandEnd.GetSize() == 0 )
{
// No, then start a new cand list from scratch.
newCand = new CCandidate;
if ( !newCand )
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
newCand->equivList = NULL; // the list of IDs that exist for this text
newCand->firstPos = 0; // the word pos where this cand starts
newCand->lastPos = -1; // the last word pos for this candidate (-1 initially)
newCand->candMax = wordCount; // the max number of cands for this string
newCand->candNum = 0; // the number of cands for this path
newCand->parent = NULL;
newCand->iQueryNum = m_iCurrentQuery; // the query number where this cand goes when backtracking
m_apCandidates.Add(newCand);
activeCand->Add(newCand);
}
}
else
{
// Yes, then get the current candEnds and put them on the activeCand list
CCandidate* tmp = NULL;
for ( i = m_apCandEnd.GetUpperBound(); i >= 0; i--)
{
tmp = m_apCandEnd[i];
if ( !tmp )
{
hr = E_UNEXPECTED;
}
if ( SUCCEEDED(hr) )
{
tmp->firstPos = 0;
tmp->lastPos = -1;
tmp->candMax = wordCount;
tmp->candNum = 0;
activeCand->Add(tmp);
}
}
} // else
} // if ( SUCCEEDED(hr)
// initialize a temporary candEnd list.
if ( SUCCEEDED(hr) )
{
tmpCandEnd = new CSPArray<CCandidate*,CCandidate*>;
if ( !tmpCandEnd )
{
hr = E_OUTOFMEMORY;
}
}
// BEGIN THE SEARCH
// For each word position (i), search to the end, which is (wordcount - i) searches.
// e.g., for "A B C":
// when i==0, search "A", "A B", "A B C"
// when i==1, search "B", "B C"
// when i==2, search "C"
// For each successful search, compute the cost from the previous candEnds, and
// for each ID returned by the search.
// For each ID, keep only the cheapest path. Discard the rest.
for ( i=0; SUCCEEDED(hr) && i<wordCount; i++ )
{
WCHAR* tmpStr = NULL;
// Only search from this position if a prev. candidate ended at i-1
if ( !pfSearch[i] )
{
continue;
}
// each search starts at position i, and has (wordCount - i) searches total.
for ( j=i; SUCCEEDED(hr) && j<wordCount; j++ )
{
// keep going?
if ( m_pOutputSite->GetActions() & SPVES_ABORT )
{
m_fAbort = true;
break;
}
if ( SUCCEEDED(hr) )
{
hr = AssembleText( i, j, wordList, &tmpStr );
}
// Then, search for it
if ( SUCCEEDED(hr) )
{
HRESULT hrSearch = S_OK;
USHORT idCount = 0;
//OutputDebugStringW ( L"Searching for: " );
//OutputDebugStringW (tmpStr);
//OutputDebugStringW ( L"\n" );
hrSearch = m_pIDb->SearchDb(tmpStr, &idCount); // get a count of the id's to retrieve for this text
// If the search succeeds, retrieve each ID for this text.
if ( SUCCEEDED(hrSearch) )
{
const WCHAR* pszId = NULL;
CSPArray<CDynStr,CDynStr> idList;
for ( k=0; k<idCount; k++ )
{
hr = m_pIDb->RetrieveSearchItem( (USHORT) k, &pszId);
if ( SUCCEEDED(hr) && pszId )
{
idList.Add(pszId);
pszId = NULL;
}
}
if ( k == idCount )
{
if ( j < wordCount-1 ) // if this is not the end,
{
pfSearch[j+1] = true; // activate search for remaining items
}
//OutputDebugStringW ( L"FOUND!\n" );
}
double dMin = DBL_MAX;
double dCand = 0.0;
CCandidate* bestCandidate = NULL;
CCandidate* current = NULL;
// compute costs from each activeCand that ends where this starts
for ( k=0; SUCCEEDED(hr) && k<activeCand->GetSize(); k++ )
{
// keep going?
if ( m_pOutputSite->GetActions() & SPVES_ABORT )
{
m_fAbort = true;
break;
}
if ( SUCCEEDED(hr) )
{
current = (*activeCand)[k];
if ( current->lastPos != i-1 ) // can the new cand attach to this one?
{
continue;
}
}
// make a new temporary candidate
if ( SUCCEEDED(hr) )
{
newCand = new CCandidate;
if ( !newCand )
{
hr = E_OUTOFMEMORY;
break;
}
}
if ( SUCCEEDED(hr) )
{
newCand->equivList = NULL;
newCand->firstPos = i; // starting search position
newCand->lastPos = j; //+1; // last word number
newCand->candMax = wordCount - j;
newCand->candNum = 0;
newCand->parent = current;
newCand->iQueryNum = m_iCurrentQuery;
}
hr = ComputeCosts(current, newCand, tags, &idList, AS_ENTRY, &dCand);
if ( SUCCEEDED(hr) )
{
// keep the best, delete the rest.
if ( dCand < dMin )
{
delete bestCandidate;
bestCandidate = newCand;
dMin = dCand;
}
else
{
delete newCand;
newCand = NULL;
}
}
}
if ( SUCCEEDED (hr) && !m_fAbort )
{
if ( j == wordCount-1 ) // complete candidate
{
tmpCandEnd->Add(bestCandidate);
}
else
{
activeCand->Add(bestCandidate); // save it so we can search for the rest.
}
m_apCandidates.Add(bestCandidate);
}
bestCandidate = NULL;
for ( k=0; k<idList.GetSize(); k++ )
{
idList[k].dstr.Clear();
}
idList.RemoveAll();
} // if ( SUCCEEDED(hrSearch) )
// reset the search string
if ( tmpStr )
{
delete [] tmpStr;
tmpStr = NULL;
}
} // if ( SUCCEEDED(hr) )
} // for ( j=i; SUCCEEDED(hr) && j<wordCount; j++ )
if ( SUCCEEDED(hr) && !m_fAbort )
{
// remove all activeCands with lastPos < i (we're done with searches that start with i)
for ( j=activeCand->GetUpperBound(); j>=0; j-- )
{
CCandidate* temp = (*activeCand)[j];
if ( temp->lastPos < i )
{
activeCand->RemoveAt( j ); // remove it.
}
temp = NULL;
}
}
} // for ( i=0; SUCCEEDED(hr) && i<wordCount; i++ )
if ( SUCCEEDED(hr) && !m_fAbort )
{
if ( !tmpCandEnd->GetSize() )
{
hr = E_FAIL;
}
}
if ( SUCCEEDED(hr) && !m_fAbort )
{
CCandidate* saveCand = NULL;
m_apCandEnd.RemoveAll();
while ( tmpCandEnd->GetSize() )
{
saveCand = (*tmpCandEnd)[tmpCandEnd->GetUpperBound()];
tmpCandEnd->RemoveAt( tmpCandEnd->GetUpperBound() );
m_apCandEnd.Add(saveCand);
}
}
if ( SUCCEEDED(hr) && !m_fAbort )
{
// now these should be empty.
SPDBG_ASSERT( !tmpCandEnd->GetSize() );
SPDBG_ASSERT( !activeCand->GetSize() );
}
if ( tmpCandEnd )
{
delete tmpCandEnd;
}
if ( activeCand )
{
delete activeCand;
}
if ( wordList )
{
free (wordList);
}
if ( pfSearch )
{
delete [] pfSearch;
pfSearch = NULL;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::SearchBackup
//
// Computes costs for a TTS item, adds it to candidate list for
// backtracking (in CDbQuery::Query).
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::SearchBackup(const WCHAR *pszText)
{
SPDBG_FUNC( "CDbQuery::SearchBackup" );
HRESULT hr = S_OK;
CCandidate* newCand = NULL;
USHORT i = 0;
newCand = new CCandidate;
if ( !newCand )
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
newCand->firstPos = 0;
newCand->lastPos = 0;
newCand->candMax = 1;
newCand->candNum = 0;
newCand->parent = NULL;
newCand->iQueryNum = m_iCurrentQuery;
hr = ComputeCostsText (newCand, pszText);
}
if ( SUCCEEDED(hr) )
{
m_apCandidates.Add(newCand);
m_apCandEnd.RemoveAll();
m_apCandEnd.Add(newCand);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::Backtrack
//
// Backtracks the candidate list, keeping items that minimize costs.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::Backtrack(double* pdCost)
{
SPDBG_FUNC( "CDbQuery::Backtrack" );
CCandidate* cur = NULL;
CEquivCost* equiv = NULL;
USHORT nCand = 0;
USHORT nEquiv = 0;
int minIdx = 0;
int minIdx2 = 0;
USHORT i = 0;
USHORT j = 0;
double minCost = 0;
nCand = (USHORT) m_apCandEnd.GetSize();
if ( nCand )
{
minCost = DBL_MAX;
}
// Look at the last item in each path, and get the Idx of the path with lowest cost
for (i=0; i<nCand; i++)
{
cur = m_apCandEnd[i];
if ( cur->equivList )
{
nEquiv = (USHORT) cur->equivList->GetSize();
}
for (j=0; j<nEquiv; j++)
{
equiv = (*cur->equivList)[j];
if (equiv->cost < minCost)
{
minCost = equiv->cost;
minIdx = i;
minIdx2 = j;
}
}
}
// Go back through the path and get the items
if (nCand)
{
cur = m_apCandEnd[minIdx];
while ( cur && cur->equivList && cur->equivList->GetSize() )
{
equiv = (*cur->equivList)[minIdx2];
if ( equiv->entry )
{
(*m_papQueries)[cur->iQueryNum]->m_apEntry.Add(equiv->entry);
equiv->entry->AddRef();
(*m_papQueries)[cur->iQueryNum]->m_afEntryMatch.Add(equiv->fTagMatch);
(*m_papQueries)[cur->iQueryNum]->m_fTTS = false;
}
else
{
(*m_papQueries)[cur->iQueryNum]->m_fTTS = true;
}
minIdx2 = equiv->whereFrom;
cur = cur->parent;
equiv = NULL;
}
}
*pdCost = minCost;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::ComputeCosts
//
// Cost computation
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::ComputeCosts(CCandidate *parent, CCandidate *child, const CSPArray<CDynStr,CDynStr>* tags,
CSPArray<CDynStr,CDynStr>* idList, const bool asEntry, double *dCandCost)
{
SPDBG_FUNC( "CDbQuery::ComputeCosts" );
HRESULT hr = S_OK;
USHORT nEquiv = 0;
USHORT nParentEquiv = 0;
USHORT i = 0;
USHORT j = 0;
int minIdx = 0;
const WCHAR* id = NULL;
double minCost = 0.0;
double cost = 0.0;
CEquivCost* curCand = NULL;
CEquivCost* prevCand = NULL;
IPromptEntry* pIPE = NULL;
CSPArray<CEquivCost*,CEquivCost*>* equivList = NULL;
SPDBG_ASSERT (parent);
SPDBG_ASSERT (child);
SPDBG_ASSERT (idList);
nEquiv = (USHORT) idList->GetSize();
if ( parent->equivList )
{
nParentEquiv = (USHORT) parent->equivList->GetSize();
}
equivList = new CSPArray<CEquivCost*,CEquivCost*>;
if ( !equivList )
{
hr = E_OUTOFMEMORY;
}
for (i=0; i<nEquiv && SUCCEEDED(hr); i++)
{
curCand = new CEquivCost;
if ( !curCand )
{
hr = E_OUTOFMEMORY;
}
id = (*idList)[i].dstr;
if ( SUCCEEDED(hr) )
{
if ( asEntry )
{
hr = m_pIDb->FindEntry(id, &pIPE);
if ( SUCCEEDED(hr) )
{
hr = CopyEntry(pIPE, &curCand->entry);
pIPE->Release();
}
}
}
if ( SUCCEEDED(hr) )
{
curCand->cost = 0.0;
curCand->whereFrom = -1;
minCost = DBL_MAX;
minIdx = -1;
}
// This is the minimizing loop
if ( SUCCEEDED(hr) )
{
if (nParentEquiv)
{
for (j=0; j<nParentEquiv && SUCCEEDED(hr); j++)
{
prevCand = (*parent->equivList)[j];
SPDBG_ASSERT (prevCand);
hr = CostFunction (prevCand, curCand, tags, &cost);
if ( SUCCEEDED(hr) )
{
if (cost < minCost )
{
minCost = cost;
minIdx = j;
}
}
}
if ( SUCCEEDED(hr) && minIdx == -1 )
{
hr = E_FAIL;
}
}
else
{
hr = CostFunction (prevCand, curCand, tags, &minCost);
}
}
if ( SUCCEEDED(hr) )
{
curCand->cost = minCost;
if ( dCandCost )
{
*dCandCost = minCost;
}
curCand->whereFrom = minIdx;
equivList->Add(curCand);
}
} // for
if ( SUCCEEDED(hr) )
{
child->equivList = equivList;
}
else
{
if ( curCand )
{
delete curCand;
curCand = NULL;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::ComputeCostsId
//
// Cost computation
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::ComputeCostsId(CCandidate *child, IPromptEntry* pIPE)
{
SPDBG_FUNC( "CDbQuery::ComputeCostsId" );
HRESULT hr = S_OK;
CCandidate* parent = NULL;
CCandidate* minParent = NULL;
CEquivCost* curCand = NULL;
CEquivCost* prevCand = NULL;
USHORT nParent = 0;
USHORT nParentEquiv= 0;
int minIdx = 0;
double minCost = 0.0;
double cost = 0.0;
USHORT i = 0;
USHORT j = 0;
CSPArray<CEquivCost*,CEquivCost*>* equivList;
curCand = new CEquivCost;
if ( !curCand )
{
hr = E_OUTOFMEMORY;
}
hr = CopyEntry( pIPE, &curCand->entry );
if ( SUCCEEDED(hr) )
{
curCand->cost = 0.0;
curCand->whereFrom = -1;
minParent = NULL;
minCost = DBL_MAX;
minIdx = -1;
nParent = (USHORT) m_apCandEnd.GetSize();
}
// This is the minimizing loop
for (i=0; i<nParent && SUCCEEDED(hr); i++)
{
parent = m_apCandEnd[i];
nParentEquiv = (USHORT) parent->equivList->GetSize();
if (nParentEquiv)
{
for ( j=0; j<nParentEquiv; j++ )
{
prevCand = (*parent->equivList)[j];
hr = CostFunction (prevCand, curCand, NULL, &cost);
if ( SUCCEEDED(hr) )
{
if (cost < minCost )
{
minCost = cost;
minIdx = j;
minParent = parent;
}
}
}
if ( SUCCEEDED(hr) && minIdx == -1 )
{
hr = E_FAIL;
}
}
else
{
minCost = 0.0;
}
if ( SUCCEEDED(hr) )
{
curCand->cost = minCost;
curCand->whereFrom = minIdx;
}
}
if ( SUCCEEDED(hr) )
{
equivList = new CSPArray<CEquivCost*,CEquivCost*>;
if (!equivList)
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
equivList->Add(curCand);
child->equivList = equivList;
child->parent = minParent;
}
if ( FAILED(hr) )
{
delete curCand;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::ComputeCostsText
//
// Cost computation
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::ComputeCostsText(CCandidate *child, const WCHAR *pszText)
{
SPDBG_FUNC( "CDbQuery::ComputeCostsText" );
HRESULT hr = S_OK;
USHORT nParent = 0;
USHORT nParentEquiv = 0;
int minIdx = 0;
double minCost = 0.0;
double cost = 0.0;
USHORT i = 0;
USHORT j = 0;
CEquivCost* curCand = NULL;
CEquivCost* prevCand = NULL;
CCandidate* parent = NULL;
CCandidate* minParent= NULL;
CSPArray<CEquivCost*,CEquivCost*>* equivList;
SPDBG_ASSERT (child);
SPDBG_ASSERT (pszText);
equivList = new CSPArray<CEquivCost*,CEquivCost*>;
if ( !equivList )
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
curCand = new CEquivCost;
if ( !curCand )
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
curCand->cost = 0.0;
curCand->whereFrom = -1;
curCand->text = wcsdup(pszText);
if ( !curCand->text )
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
minParent = NULL;
minCost = DBL_MAX;
minIdx = -1;
nParent = (USHORT) m_apCandEnd.GetSize();
}
if ( !nParent )
{
hr = CostFunction (NULL, curCand, NULL, &cost);
if ( SUCCEEDED(hr) )
{
curCand->cost = cost;
curCand->whereFrom = minIdx;
}
}
else
{
// This is the minimizing loop
for (i=0; i<nParent && SUCCEEDED(hr); i++)
{
parent = m_apCandEnd[i];
nParentEquiv = (USHORT) parent->equivList->GetSize();
if (nParentEquiv)
{
for (j=0; j<nParentEquiv; j++)
{
prevCand = (*parent->equivList)[j];
hr = CostFunction (prevCand, curCand, NULL, &cost);
if ( SUCCEEDED(hr) )
{
if (cost < minCost )
{
minCost = cost;
minIdx = j;
minParent = parent;
}
}
}
if ( SUCCEEDED(hr) && minIdx == -1 )
{
hr = E_FAIL;
}
}
else
{
minCost = 0.0;
}
if ( SUCCEEDED(hr) )
{
curCand->cost = minCost;
curCand->whereFrom = minIdx;
}
}
}
if ( SUCCEEDED(hr) )
{
equivList->Add(curCand);
child->equivList = equivList;
child->parent = minParent;
}
else
{
if ( curCand )
{
delete curCand;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::CostFunction
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CDbQuery::CostFunction(CEquivCost *prev, CEquivCost *cur, const CSPArray<CDynStr,CDynStr>* tags, double *cost)
{
SPDBG_FUNC( "CDbQuery::CostFunction" );
HRESULT hr = S_OK;
SPDBG_ASSERT (cur);
SPDBG_ASSERT (cost);
if ( prev )
{
*cost = prev->cost;
}
else
{
*cost = 0.0;
}
// Cost of using TTS
if ( cur->text )
{
if ( prev )
{
// if we're transitioning from prompts, add the transition cost + TTS insert cost.
if ( prev->entry )
{
*cost += FIXED_TRANSITION_COST; // Fixed cost for a transition
*cost += TTS_INSERT_COST; // * CountWords (cur->text);
}
}
else // otherwise, just add the TTS cost
{
*cost += TTS_INSERT_COST; // * CountWords (cur->text);
}
}
else // Cost of using an entry;
{
double tagCost = MATCHING_TAG_COST;
USHORT nEntryTags = 0;
if ( prev )
{
*cost += FIXED_TRANSITION_COST; // Fixed cost for a transition
}
cur->entry->CountTags(&nEntryTags);
if ( !tags )
{
if ( nEntryTags )
{
*cost += NON_MATCHING_TAG_COST;
cur->fTagMatch = false;
}
}
else
{
USHORT i = 0;
CSpDynamicString curTag;
USHORT nTags = (USHORT) tags->GetSize();
for (i=0; i<nTags && SUCCEEDED(hr); i++)
{
curTag = (*tags)[i].dstr;
const WCHAR* emptyTag = L"";
if ( nEntryTags )
{
const WCHAR* entryTag = NULL;
USHORT j = 0;
for (j=0; j< nEntryTags; j++)
{
hr = cur->entry->GetTag(&entryTag, j);
if ( SUCCEEDED(hr) )
{
if ( wcscmp(curTag, entryTag) == 0 )
{
entryTag = NULL;
break;
}
entryTag = NULL;
}
}
if (j == nEntryTags)
{
tagCost = NON_MATCHING_TAG_COST;
cur->fTagMatch = false;
}
}
//--- An empty curTag should still match with an entry that has no tags
else if ( wcscmp(curTag,emptyTag) != 0 )
{
tagCost = NON_MATCHING_TAG_COST;
cur->fTagMatch = false;
}
*cost += tagCost;
curTag.Clear();
} // for each tag
}
} // else
// If there is information about phonetic
// context, apply it
if ( SUCCEEDED(hr) )
{
if ( m_pPhoneContext && prev && prev->entry && cur->entry )
{
double cntxtCost;
hr = m_pPhoneContext->Apply( prev->entry, cur->entry, &cntxtCost );
if ( SUCCEEDED (hr) )
{
*cost += cntxtCost;
}
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::CopyEntry
//
// Creates and returns a copy of the entry pointed to by pIPE.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
HRESULT CDbQuery::CopyEntry(IPromptEntry *pIPE, CPromptEntry** inEntry)
{
SPDBG_FUNC( "CDbQuery::CopyEntry" );
HRESULT hr = S_OK;
double entryTime = 0.0;
const WCHAR* entryText = NULL;
USHORT i = 0;
USHORT tagCount = 0;
CPromptEntry* newEntry = new CPromptEntry ( *(CPromptEntry*)pIPE );
if ( !newEntry )
{
hr = E_OUTOFMEMORY;
}
*inEntry = newEntry;
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CDbQuery::RemoveLocalQueries
//
// When a local query (from an expansion rule) fails, remove all
// associated local queries, and reinstate the main query.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
HRESULT CDbQuery::RemoveLocalQueries(const USHORT unQueryNum)
{
SPDBG_FUNC( "CDbQuery::RemoveLocalQueries" );
SHORT i = 0;
CQuery* pQuery = NULL;
// Take care of current query first
pQuery = (*m_papQueries)[unQueryNum];
// If it's a local query, cancel it and the surrounding local queries,
// and restoring the original for TTS.
if ( pQuery->m_fFragType == LOCAL_FRAG )
{
pQuery->m_fSpeak = FALSE;
// step backward, flagging previous local queries as "don't speak"
for ( i = unQueryNum-1; i>=0; i-- )
{
pQuery = (*m_papQueries)[i];
if ( pQuery->m_fFragType == LOCAL_FRAG )
{
pQuery->m_fSpeak = false;
}
else
{
break;
}
}
// step forward, flagging upcoming local queries as "don't speak"
for ( i = unQueryNum+1; i<m_papQueries->GetSize(); i++ )
{
pQuery = (*m_papQueries)[i];
if ( pQuery->m_fFragType == LOCAL_FRAG )
{
pQuery->m_fSpeak = false;
}
else
{
pQuery->m_fSpeak = true;
pQuery->m_fTTS = true;
// this wasn't supposed to be spoken, but now it must, so match is false.
pQuery->m_afEntryMatch.Add(false);
break;
}
}
}
else // It's a combined frag - restore upcoming combined frags too.
{
pQuery->m_fTTS = true;
for ( i = unQueryNum+1; i<m_papQueries->GetSize(); i++ )
{
pQuery = (*m_papQueries)[i];
if ( pQuery->m_fFragType == COMBINED_FRAG )
{
pQuery->m_fFragType = SAPI_FRAG;
pQuery->m_fSpeak = true;
pQuery->m_fTTS = true;
}
else
{
break;
}
}
}
return S_OK;
}