Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1539 lines
42 KiB

  1. //////////////////////////////////////////////////////////////////////
  2. // DbQuery.cpp: implementation of the CDbQuery class.
  3. //
  4. // Created by JOEM 03-2000
  5. // Copyright (C) 2000 Microsoft Corporation
  6. // All Rights Reserved
  7. //
  8. /////////////////////////////////////////////////////// JOEM 3-2000 //
  9. #include "stdafx.h"
  10. #include "DbQuery.h"
  11. #include "PromptDb.h"
  12. #include <LIMITS>
  13. //////////////////////////////////////////////////////////////////////
  14. // CEquivCost
  15. //
  16. // Construction/Destruction
  17. //////////////////////////////////////////////////////////////////////
  18. CEquivCost::CEquivCost()
  19. {
  20. text = NULL;
  21. entry = NULL;
  22. fTagMatch = true;
  23. }
  24. CEquivCost::CEquivCost(const CEquivCost& old)
  25. {
  26. if ( text )
  27. {
  28. text = wcsdup(old.text);
  29. }
  30. else
  31. {
  32. text = NULL;
  33. }
  34. if ( entry )
  35. {
  36. entry = new CPromptEntry(*old.entry);
  37. }
  38. else
  39. {
  40. entry = NULL;
  41. }
  42. cost = old.cost;
  43. whereFrom = old.whereFrom;
  44. fTagMatch = old.fTagMatch;
  45. }
  46. CEquivCost::~CEquivCost()
  47. {
  48. if ( text )
  49. {
  50. free(text);
  51. text = NULL;
  52. }
  53. if ( entry )
  54. {
  55. entry->Release();
  56. entry = NULL;
  57. }
  58. }
  59. //////////////////////////////////////////////////////////////////////
  60. // CCandidate
  61. //
  62. // Construction/Destruction
  63. //////////////////////////////////////////////////////////////////////
  64. CCandidate::CCandidate()
  65. {
  66. equivList = NULL;
  67. parent = NULL;
  68. firstPos = 0;
  69. lastPos = 0;
  70. candMax = 0;
  71. candNum = 0;
  72. iQueryNum = 0;
  73. }
  74. CCandidate::CCandidate(const CCandidate& old)
  75. {
  76. CEquivCost* equiv = NULL;
  77. firstPos = old.firstPos;
  78. lastPos = old.lastPos;
  79. candMax = old.candMax;
  80. candNum = old.candNum;
  81. iQueryNum = old.iQueryNum;
  82. parent = old.parent;
  83. if ( old.equivList && old.equivList->GetSize() )
  84. {
  85. equivList = new CSPArray<CEquivCost*,CEquivCost*>;
  86. for (USHORT i=0; i<old.equivList->GetSize(); i++)
  87. {
  88. equiv = new CEquivCost( *(*old.equivList)[i] );
  89. equivList->Add(equiv);
  90. }
  91. }
  92. }
  93. CCandidate::~CCandidate()
  94. {
  95. USHORT i = 0;
  96. if ( equivList )
  97. {
  98. for ( i=0; i<equivList->GetSize(); i++ )
  99. {
  100. if ( (*equivList)[i] )
  101. {
  102. delete (*equivList)[i];
  103. (*equivList)[i] = NULL;
  104. }
  105. }
  106. equivList->RemoveAll();
  107. delete equivList;
  108. }
  109. parent = NULL;
  110. }
  111. //////////////////////////////////////////////////////////////////////
  112. // CDbQuery
  113. //
  114. // Construction/Destruction
  115. //////////////////////////////////////////////////////////////////////
  116. CDbQuery::CDbQuery()
  117. {
  118. m_pIDb = NULL;
  119. m_pPhoneContext = NULL;
  120. m_papQueries = NULL;
  121. m_pOutputSite = NULL;
  122. m_iCurrentQuery = 0;
  123. m_fAbort = false;
  124. }
  125. CDbQuery::~CDbQuery()
  126. {
  127. USHORT i = 0;
  128. if ( m_pIDb )
  129. {
  130. m_pIDb->Release();
  131. m_pIDb = NULL;
  132. }
  133. // These are just pointers set in CDbQuery::Init and CDbQuery::Query (deallocated elsewhere)
  134. m_pPhoneContext = NULL;
  135. m_papQueries = NULL;
  136. // To be safe, I'll keep this list deletion here.
  137. // However, it is generally deleted when CDbQuery::Query calls CDbQuery::Reset
  138. m_apCandEnd.RemoveAll();
  139. for ( i=0; i<m_apCandidates.GetSize(); i++ )
  140. {
  141. if ( m_apCandidates[i] )
  142. {
  143. delete m_apCandidates[i];
  144. m_apCandidates[i] = NULL;
  145. }
  146. }
  147. m_apCandidates.RemoveAll();
  148. }
  149. //////////////////////////////////////////////////////////////////////
  150. // CDbQuery::Init
  151. //
  152. //
  153. /////////////////////////////////////////////////////// JOEM 3-2000 //
  154. HRESULT CDbQuery::Init(IPromptDb *pIDb, CPhoneContext *pPhoneContext)
  155. {
  156. SPDBG_FUNC( "CDbQuery::Init" );
  157. HRESULT hr = S_OK;
  158. SPDBG_ASSERT(pIDb);
  159. SPDBG_ASSERT(pPhoneContext);
  160. m_pIDb = pIDb;
  161. m_pIDb->AddRef();
  162. m_pPhoneContext = pPhoneContext;
  163. SPDBG_REPORT_ON_FAIL( hr );
  164. return hr;
  165. }
  166. //////////////////////////////////////////////////////////////////////
  167. // CDbQuery::Reset
  168. //
  169. //
  170. /////////////////////////////////////////////////////// JOEM 3-2000 //
  171. HRESULT CDbQuery::Reset()
  172. {
  173. SPDBG_FUNC( "CDbQuery::Reset" );
  174. USHORT i = 0;
  175. m_iCurrentQuery = 0;
  176. m_papQueries = NULL;
  177. m_apCandEnd.RemoveAll();
  178. for ( i=0; i<m_apCandidates.GetSize(); i++ )
  179. {
  180. if ( m_apCandidates[i] )
  181. {
  182. delete m_apCandidates[i];
  183. m_apCandidates[i] = NULL;
  184. }
  185. }
  186. m_apCandidates.RemoveAll();
  187. return S_OK;
  188. }
  189. //////////////////////////////////////////////////////////////////////
  190. // CDbQuery::Query
  191. //
  192. // Takes a query list, created in CPromptEng::BuildQueryList, and
  193. // figures out which can be handled with prompts, and which must
  194. // go to TTS. Computes transition costs between candidate items,
  195. // and Backtracks the list selecting the minimum cost items.
  196. //
  197. /////////////////////////////////////////////////////// JOEM 3-2000 //
  198. HRESULT CDbQuery::Query(CSPArray<CQuery*,CQuery*>* papQueries, double* pdCost,
  199. ISpTTSEngineSite* pOutputSite, bool *fAbort)
  200. {
  201. SPDBG_FUNC( "CDbQuery::Query" );
  202. HRESULT hr = S_OK;
  203. USHORT i = 0;
  204. USHORT j = 0;
  205. USHORT unIdSize = 0;
  206. const WCHAR* pszId = NULL;
  207. WCHAR* pszText = NULL;
  208. CQuery* pQuery = NULL;
  209. SPDBG_ASSERT(papQueries);
  210. SPDBG_ASSERT(pOutputSite);
  211. m_pOutputSite = pOutputSite;
  212. m_papQueries = papQueries;
  213. m_fAbort = false;
  214. for ( i=0; SUCCEEDED(hr) && i < m_papQueries->GetSize(); i++ )
  215. {
  216. if ( m_pOutputSite->GetActions() & SPVES_ABORT )
  217. {
  218. m_fAbort = true;
  219. break;
  220. }
  221. if ( SUCCEEDED(hr) )
  222. {
  223. pQuery = (*m_papQueries)[i];
  224. m_iCurrentQuery = i;
  225. if ( !pQuery->m_fSpeak )
  226. {
  227. continue;
  228. }
  229. }
  230. if ( SUCCEEDED(hr) )
  231. {
  232. if ( pQuery->m_fXML )
  233. {
  234. // If the database needs to be changed, do it now.
  235. if ( pQuery->m_unDbAction )
  236. {
  237. switch ( pQuery->m_unDbAction )
  238. {
  239. case DB_ADD:
  240. if ( !pQuery->m_pszDbPath )
  241. {
  242. hr = E_INVALIDARG;
  243. }
  244. if ( SUCCEEDED(hr) )
  245. {
  246. hr = m_pIDb->AddDb(pQuery->m_pszDbName, pQuery->m_pszDbPath, pQuery->m_pszDbIdSet, DB_LOAD);
  247. }
  248. if ( SUCCEEDED(hr) )
  249. {
  250. free( pQuery->m_pszDbName );
  251. pQuery->m_pszDbName = NULL;
  252. }
  253. break;
  254. case DB_ACTIVATE:
  255. if ( !pQuery->m_pszDbName )
  256. {
  257. hr = E_INVALIDARG;
  258. }
  259. if ( SUCCEEDED(hr) )
  260. {
  261. hr = m_pIDb->ActivateDbName(pQuery->m_pszDbName);
  262. }
  263. break;
  264. case DB_UNLOAD:
  265. hr = m_pIDb->UnloadDb(pQuery->m_pszDbName);
  266. break;
  267. default:
  268. break;
  269. }
  270. continue;
  271. } // if ( pQuery->m_unDbAction )
  272. // ID search
  273. else if ( pQuery->m_pszId )
  274. {
  275. hr = SearchId( pQuery->m_pszId );
  276. continue;
  277. }
  278. else
  279. {
  280. // Any XML besides DATABASE or ID can be ignored here.
  281. if ( pQuery->m_fXML == UNKNOWN_XML )
  282. {
  283. pQuery->m_fTTS = true;
  284. }
  285. continue;
  286. }
  287. } // if ( pQuery->m_fXML )
  288. } // if ( SUCCEEDED(hr) )
  289. // For Text, search in the Db first, then do SearchBackup (for TTS costs)
  290. if ( SUCCEEDED (hr) )
  291. {
  292. if ( !pQuery->m_fTTS )
  293. {
  294. hr = SearchText( pQuery->m_pszExpandedText, pQuery->m_paTagList );
  295. if ( FAILED(hr) )
  296. {
  297. pQuery->m_afEntryMatch.Add(false);
  298. if ( pQuery->m_fFragType )
  299. {
  300. hr = RemoveLocalQueries(i);
  301. }
  302. }
  303. }
  304. // SearchBackup for TTS items, or if SearchText failed.
  305. if ( pQuery->m_fSpeak && ( pQuery->m_fTTS || FAILED(hr) ) )
  306. {
  307. pszText = new WCHAR[pQuery->m_pTextFrag->ulTextLen + 1];
  308. if ( pszText )
  309. {
  310. hr = S_OK;
  311. }
  312. else
  313. {
  314. hr = E_OUTOFMEMORY;
  315. }
  316. if ( SUCCEEDED(hr) )
  317. {
  318. wcsncpy(pszText, pQuery->m_pTextFrag->pTextStart, pQuery->m_pTextFrag->ulTextLen);
  319. pszText[pQuery->m_pTextFrag->ulTextLen] = L'\0';
  320. hr = SearchBackup( pszText );
  321. delete pszText;
  322. pszText = NULL;
  323. }
  324. if ( SUCCEEDED(hr) )
  325. {
  326. pQuery->m_fTTS = true;
  327. pQuery->m_afEntryMatch.Add(true);
  328. }
  329. }
  330. }
  331. } // for ( i=0; i < m_papQueries->GetSize(); i++ )
  332. // Backtrack, selecting the search items with min. cost
  333. if ( SUCCEEDED(hr) && !m_fAbort )
  334. {
  335. hr = Backtrack( pdCost );
  336. }
  337. Reset();
  338. *fAbort = m_fAbort;
  339. SPDBG_REPORT_ON_FAIL( hr );
  340. return hr;
  341. }
  342. //////////////////////////////////////////////////////////////////////
  343. // CDbQuery::SearchId
  344. //
  345. // Checks the Db for specific Ids, adds them to the candidate list
  346. // for backtracking later (in CDbQuery::Query).
  347. //
  348. /////////////////////////////////////////////////////// JOEM 3-2000 //
  349. HRESULT CDbQuery::SearchId(const WCHAR* pszId)
  350. {
  351. SPDBG_FUNC( "CDbQuery::SearchId" );
  352. HRESULT hr = S_OK;
  353. CCandidate* newCand = NULL;
  354. IPromptEntry* pIPE = NULL;
  355. USHORT i = 0;
  356. SPDBG_ASSERT( pszId );
  357. hr = m_pIDb->FindEntry(pszId, &pIPE);
  358. if ( SUCCEEDED(hr) )
  359. {
  360. newCand = new CCandidate;
  361. if ( !newCand )
  362. {
  363. hr = E_OUTOFMEMORY;
  364. }
  365. if ( SUCCEEDED(hr) )
  366. {
  367. newCand->firstPos = 0;
  368. newCand->lastPos = 0;
  369. newCand->candMax = 1;
  370. newCand->candNum = 0;
  371. newCand->parent = NULL;
  372. newCand->iQueryNum = m_iCurrentQuery;
  373. hr = ComputeCostsId (newCand, pIPE);
  374. }
  375. if ( SUCCEEDED(hr) )
  376. {
  377. m_apCandidates.Add(newCand);
  378. m_apCandEnd.RemoveAll();
  379. m_apCandEnd.Add(newCand);
  380. }
  381. if ( FAILED(hr) && newCand )
  382. {
  383. delete newCand;
  384. newCand = NULL;
  385. }
  386. pIPE->Release();
  387. }
  388. SPDBG_REPORT_ON_FAIL( hr );
  389. return hr;
  390. }
  391. //////////////////////////////////////////////////////////////////////
  392. // CDbQuery::SearchText
  393. //
  394. // Searches the Db for text. If found, adds the items (with computed
  395. // transition costs) to the candidate list for backtracking later
  396. // (in CDbQuery::Query).
  397. //
  398. /////////////////////////////////////////////////////// JOEM 3-2000 //
  399. HRESULT CDbQuery::SearchText(WCHAR *pszText, const CSPArray<CDynStr,CDynStr>* tags)
  400. {
  401. SPDBG_FUNC( "CDbQuery::SearchText" );
  402. HRESULT hr = S_OK;
  403. WCHAR** wordList = NULL;
  404. USHORT wordCount = 0;
  405. int i = 0; // THIS MUST BE SIGNED
  406. int j = 0;
  407. int k = 0;
  408. bool* pfSearch = NULL;
  409. CSPArray<CCandidate*,CCandidate*>* activeCand;
  410. CSPArray<CCandidate*,CCandidate*>* tmpCandEnd;
  411. CCandidate* newCand = NULL;
  412. hr = SplitWords (pszText, &wordList, &wordCount);
  413. if ( SUCCEEDED(hr) )
  414. {
  415. hr = RemovePunctuation(wordList, &wordCount);
  416. }
  417. if ( wordCount <= 0 )
  418. {
  419. return hr;
  420. }
  421. // These flags turn on the searches that start with word position i
  422. // A search is later turned on if a previous search ended at i-1.
  423. pfSearch = new bool[wordCount];
  424. if ( !pfSearch )
  425. {
  426. hr = E_OUTOFMEMORY;
  427. }
  428. else
  429. {
  430. pfSearch[0] = true; // always do the first search
  431. for ( i=1; i<wordCount; i++ )
  432. {
  433. pfSearch[i] = false;
  434. }
  435. }
  436. // Initialize stack decoder
  437. if ( SUCCEEDED(hr) )
  438. {
  439. activeCand = new CSPArray<CCandidate*,CCandidate*>;
  440. if ( !activeCand )
  441. {
  442. hr = E_OUTOFMEMORY;
  443. }
  444. }
  445. // Get the active candidate list from previous search, if any.
  446. if ( SUCCEEDED(hr) )
  447. {
  448. // Any previous candidates?
  449. if ( m_apCandEnd.GetSize() == 0 )
  450. {
  451. // No, then start a new cand list from scratch.
  452. newCand = new CCandidate;
  453. if ( !newCand )
  454. {
  455. hr = E_OUTOFMEMORY;
  456. }
  457. if ( SUCCEEDED(hr) )
  458. {
  459. newCand->equivList = NULL; // the list of IDs that exist for this text
  460. newCand->firstPos = 0; // the word pos where this cand starts
  461. newCand->lastPos = -1; // the last word pos for this candidate (-1 initially)
  462. newCand->candMax = wordCount; // the max number of cands for this string
  463. newCand->candNum = 0; // the number of cands for this path
  464. newCand->parent = NULL;
  465. newCand->iQueryNum = m_iCurrentQuery; // the query number where this cand goes when backtracking
  466. m_apCandidates.Add(newCand);
  467. activeCand->Add(newCand);
  468. }
  469. }
  470. else
  471. {
  472. // Yes, then get the current candEnds and put them on the activeCand list
  473. CCandidate* tmp = NULL;
  474. for ( i = m_apCandEnd.GetUpperBound(); i >= 0; i--)
  475. {
  476. tmp = m_apCandEnd[i];
  477. if ( !tmp )
  478. {
  479. hr = E_UNEXPECTED;
  480. }
  481. if ( SUCCEEDED(hr) )
  482. {
  483. tmp->firstPos = 0;
  484. tmp->lastPos = -1;
  485. tmp->candMax = wordCount;
  486. tmp->candNum = 0;
  487. activeCand->Add(tmp);
  488. }
  489. }
  490. } // else
  491. } // if ( SUCCEEDED(hr)
  492. // initialize a temporary candEnd list.
  493. if ( SUCCEEDED(hr) )
  494. {
  495. tmpCandEnd = new CSPArray<CCandidate*,CCandidate*>;
  496. if ( !tmpCandEnd )
  497. {
  498. hr = E_OUTOFMEMORY;
  499. }
  500. }
  501. // BEGIN THE SEARCH
  502. // For each word position (i), search to the end, which is (wordcount - i) searches.
  503. // e.g., for "A B C":
  504. // when i==0, search "A", "A B", "A B C"
  505. // when i==1, search "B", "B C"
  506. // when i==2, search "C"
  507. // For each successful search, compute the cost from the previous candEnds, and
  508. // for each ID returned by the search.
  509. // For each ID, keep only the cheapest path. Discard the rest.
  510. for ( i=0; SUCCEEDED(hr) && i<wordCount; i++ )
  511. {
  512. WCHAR* tmpStr = NULL;
  513. // Only search from this position if a prev. candidate ended at i-1
  514. if ( !pfSearch[i] )
  515. {
  516. continue;
  517. }
  518. // each search starts at position i, and has (wordCount - i) searches total.
  519. for ( j=i; SUCCEEDED(hr) && j<wordCount; j++ )
  520. {
  521. // keep going?
  522. if ( m_pOutputSite->GetActions() & SPVES_ABORT )
  523. {
  524. m_fAbort = true;
  525. break;
  526. }
  527. if ( SUCCEEDED(hr) )
  528. {
  529. hr = AssembleText( i, j, wordList, &tmpStr );
  530. }
  531. // Then, search for it
  532. if ( SUCCEEDED(hr) )
  533. {
  534. HRESULT hrSearch = S_OK;
  535. USHORT idCount = 0;
  536. //OutputDebugStringW ( L"Searching for: " );
  537. //OutputDebugStringW (tmpStr);
  538. //OutputDebugStringW ( L"\n" );
  539. hrSearch = m_pIDb->SearchDb(tmpStr, &idCount); // get a count of the id's to retrieve for this text
  540. // If the search succeeds, retrieve each ID for this text.
  541. if ( SUCCEEDED(hrSearch) )
  542. {
  543. const WCHAR* pszId = NULL;
  544. CSPArray<CDynStr,CDynStr> idList;
  545. for ( k=0; k<idCount; k++ )
  546. {
  547. hr = m_pIDb->RetrieveSearchItem( (USHORT) k, &pszId);
  548. if ( SUCCEEDED(hr) && pszId )
  549. {
  550. idList.Add(pszId);
  551. pszId = NULL;
  552. }
  553. }
  554. if ( k == idCount )
  555. {
  556. if ( j < wordCount-1 ) // if this is not the end,
  557. {
  558. pfSearch[j+1] = true; // activate search for remaining items
  559. }
  560. //OutputDebugStringW ( L"FOUND!\n" );
  561. }
  562. double dMin = DBL_MAX;
  563. double dCand = 0.0;
  564. CCandidate* bestCandidate = NULL;
  565. CCandidate* current = NULL;
  566. // compute costs from each activeCand that ends where this starts
  567. for ( k=0; SUCCEEDED(hr) && k<activeCand->GetSize(); k++ )
  568. {
  569. // keep going?
  570. if ( m_pOutputSite->GetActions() & SPVES_ABORT )
  571. {
  572. m_fAbort = true;
  573. break;
  574. }
  575. if ( SUCCEEDED(hr) )
  576. {
  577. current = (*activeCand)[k];
  578. if ( current->lastPos != i-1 ) // can the new cand attach to this one?
  579. {
  580. continue;
  581. }
  582. }
  583. // make a new temporary candidate
  584. if ( SUCCEEDED(hr) )
  585. {
  586. newCand = new CCandidate;
  587. if ( !newCand )
  588. {
  589. hr = E_OUTOFMEMORY;
  590. break;
  591. }
  592. }
  593. if ( SUCCEEDED(hr) )
  594. {
  595. newCand->equivList = NULL;
  596. newCand->firstPos = i; // starting search position
  597. newCand->lastPos = j; //+1; // last word number
  598. newCand->candMax = wordCount - j;
  599. newCand->candNum = 0;
  600. newCand->parent = current;
  601. newCand->iQueryNum = m_iCurrentQuery;
  602. }
  603. hr = ComputeCosts(current, newCand, tags, &idList, AS_ENTRY, &dCand);
  604. if ( SUCCEEDED(hr) )
  605. {
  606. // keep the best, delete the rest.
  607. if ( dCand < dMin )
  608. {
  609. delete bestCandidate;
  610. bestCandidate = newCand;
  611. dMin = dCand;
  612. }
  613. else
  614. {
  615. delete newCand;
  616. newCand = NULL;
  617. }
  618. }
  619. }
  620. if ( SUCCEEDED (hr) && !m_fAbort )
  621. {
  622. if ( j == wordCount-1 ) // complete candidate
  623. {
  624. tmpCandEnd->Add(bestCandidate);
  625. }
  626. else
  627. {
  628. activeCand->Add(bestCandidate); // save it so we can search for the rest.
  629. }
  630. m_apCandidates.Add(bestCandidate);
  631. }
  632. bestCandidate = NULL;
  633. for ( k=0; k<idList.GetSize(); k++ )
  634. {
  635. idList[k].dstr.Clear();
  636. }
  637. idList.RemoveAll();
  638. } // if ( SUCCEEDED(hrSearch) )
  639. // reset the search string
  640. if ( tmpStr )
  641. {
  642. delete [] tmpStr;
  643. tmpStr = NULL;
  644. }
  645. } // if ( SUCCEEDED(hr) )
  646. } // for ( j=i; SUCCEEDED(hr) && j<wordCount; j++ )
  647. if ( SUCCEEDED(hr) && !m_fAbort )
  648. {
  649. // remove all activeCands with lastPos < i (we're done with searches that start with i)
  650. for ( j=activeCand->GetUpperBound(); j>=0; j-- )
  651. {
  652. CCandidate* temp = (*activeCand)[j];
  653. if ( temp->lastPos < i )
  654. {
  655. activeCand->RemoveAt( j ); // remove it.
  656. }
  657. temp = NULL;
  658. }
  659. }
  660. } // for ( i=0; SUCCEEDED(hr) && i<wordCount; i++ )
  661. if ( SUCCEEDED(hr) && !m_fAbort )
  662. {
  663. if ( !tmpCandEnd->GetSize() )
  664. {
  665. hr = E_FAIL;
  666. }
  667. }
  668. if ( SUCCEEDED(hr) && !m_fAbort )
  669. {
  670. CCandidate* saveCand = NULL;
  671. m_apCandEnd.RemoveAll();
  672. while ( tmpCandEnd->GetSize() )
  673. {
  674. saveCand = (*tmpCandEnd)[tmpCandEnd->GetUpperBound()];
  675. tmpCandEnd->RemoveAt( tmpCandEnd->GetUpperBound() );
  676. m_apCandEnd.Add(saveCand);
  677. }
  678. }
  679. if ( SUCCEEDED(hr) && !m_fAbort )
  680. {
  681. // now these should be empty.
  682. SPDBG_ASSERT( !tmpCandEnd->GetSize() );
  683. SPDBG_ASSERT( !activeCand->GetSize() );
  684. }
  685. if ( tmpCandEnd )
  686. {
  687. delete tmpCandEnd;
  688. }
  689. if ( activeCand )
  690. {
  691. delete activeCand;
  692. }
  693. if ( wordList )
  694. {
  695. free (wordList);
  696. }
  697. if ( pfSearch )
  698. {
  699. delete [] pfSearch;
  700. pfSearch = NULL;
  701. }
  702. return hr;
  703. }
  704. //////////////////////////////////////////////////////////////////////
  705. // CDbQuery::SearchBackup
  706. //
  707. // Computes costs for a TTS item, adds it to candidate list for
  708. // backtracking (in CDbQuery::Query).
  709. //
  710. /////////////////////////////////////////////////////// JOEM 3-2000 //
  711. HRESULT CDbQuery::SearchBackup(const WCHAR *pszText)
  712. {
  713. SPDBG_FUNC( "CDbQuery::SearchBackup" );
  714. HRESULT hr = S_OK;
  715. CCandidate* newCand = NULL;
  716. USHORT i = 0;
  717. newCand = new CCandidate;
  718. if ( !newCand )
  719. {
  720. hr = E_OUTOFMEMORY;
  721. }
  722. if ( SUCCEEDED(hr) )
  723. {
  724. newCand->firstPos = 0;
  725. newCand->lastPos = 0;
  726. newCand->candMax = 1;
  727. newCand->candNum = 0;
  728. newCand->parent = NULL;
  729. newCand->iQueryNum = m_iCurrentQuery;
  730. hr = ComputeCostsText (newCand, pszText);
  731. }
  732. if ( SUCCEEDED(hr) )
  733. {
  734. m_apCandidates.Add(newCand);
  735. m_apCandEnd.RemoveAll();
  736. m_apCandEnd.Add(newCand);
  737. }
  738. SPDBG_REPORT_ON_FAIL( hr );
  739. return hr;
  740. }
  741. //////////////////////////////////////////////////////////////////////
  742. // CDbQuery::Backtrack
  743. //
  744. // Backtracks the candidate list, keeping items that minimize costs.
  745. //
  746. /////////////////////////////////////////////////////// JOEM 3-2000 //
  747. HRESULT CDbQuery::Backtrack(double* pdCost)
  748. {
  749. SPDBG_FUNC( "CDbQuery::Backtrack" );
  750. CCandidate* cur = NULL;
  751. CEquivCost* equiv = NULL;
  752. USHORT nCand = 0;
  753. USHORT nEquiv = 0;
  754. int minIdx = 0;
  755. int minIdx2 = 0;
  756. USHORT i = 0;
  757. USHORT j = 0;
  758. double minCost = 0;
  759. nCand = (USHORT) m_apCandEnd.GetSize();
  760. if ( nCand )
  761. {
  762. minCost = DBL_MAX;
  763. }
  764. // Look at the last item in each path, and get the Idx of the path with lowest cost
  765. for (i=0; i<nCand; i++)
  766. {
  767. cur = m_apCandEnd[i];
  768. if ( cur->equivList )
  769. {
  770. nEquiv = (USHORT) cur->equivList->GetSize();
  771. }
  772. for (j=0; j<nEquiv; j++)
  773. {
  774. equiv = (*cur->equivList)[j];
  775. if (equiv->cost < minCost)
  776. {
  777. minCost = equiv->cost;
  778. minIdx = i;
  779. minIdx2 = j;
  780. }
  781. }
  782. }
  783. // Go back through the path and get the items
  784. if (nCand)
  785. {
  786. cur = m_apCandEnd[minIdx];
  787. while ( cur && cur->equivList && cur->equivList->GetSize() )
  788. {
  789. equiv = (*cur->equivList)[minIdx2];
  790. if ( equiv->entry )
  791. {
  792. (*m_papQueries)[cur->iQueryNum]->m_apEntry.Add(equiv->entry);
  793. equiv->entry->AddRef();
  794. (*m_papQueries)[cur->iQueryNum]->m_afEntryMatch.Add(equiv->fTagMatch);
  795. (*m_papQueries)[cur->iQueryNum]->m_fTTS = false;
  796. }
  797. else
  798. {
  799. (*m_papQueries)[cur->iQueryNum]->m_fTTS = true;
  800. }
  801. minIdx2 = equiv->whereFrom;
  802. cur = cur->parent;
  803. equiv = NULL;
  804. }
  805. }
  806. *pdCost = minCost;
  807. return S_OK;
  808. }
  809. //////////////////////////////////////////////////////////////////////
  810. // CDbQuery::ComputeCosts
  811. //
  812. // Cost computation
  813. //
  814. /////////////////////////////////////////////////////// JOEM 3-2000 //
  815. HRESULT CDbQuery::ComputeCosts(CCandidate *parent, CCandidate *child, const CSPArray<CDynStr,CDynStr>* tags,
  816. CSPArray<CDynStr,CDynStr>* idList, const bool asEntry, double *dCandCost)
  817. {
  818. SPDBG_FUNC( "CDbQuery::ComputeCosts" );
  819. HRESULT hr = S_OK;
  820. USHORT nEquiv = 0;
  821. USHORT nParentEquiv = 0;
  822. USHORT i = 0;
  823. USHORT j = 0;
  824. int minIdx = 0;
  825. const WCHAR* id = NULL;
  826. double minCost = 0.0;
  827. double cost = 0.0;
  828. CEquivCost* curCand = NULL;
  829. CEquivCost* prevCand = NULL;
  830. IPromptEntry* pIPE = NULL;
  831. CSPArray<CEquivCost*,CEquivCost*>* equivList = NULL;
  832. SPDBG_ASSERT (parent);
  833. SPDBG_ASSERT (child);
  834. SPDBG_ASSERT (idList);
  835. nEquiv = (USHORT) idList->GetSize();
  836. if ( parent->equivList )
  837. {
  838. nParentEquiv = (USHORT) parent->equivList->GetSize();
  839. }
  840. equivList = new CSPArray<CEquivCost*,CEquivCost*>;
  841. if ( !equivList )
  842. {
  843. hr = E_OUTOFMEMORY;
  844. }
  845. for (i=0; i<nEquiv && SUCCEEDED(hr); i++)
  846. {
  847. curCand = new CEquivCost;
  848. if ( !curCand )
  849. {
  850. hr = E_OUTOFMEMORY;
  851. }
  852. id = (*idList)[i].dstr;
  853. if ( SUCCEEDED(hr) )
  854. {
  855. if ( asEntry )
  856. {
  857. hr = m_pIDb->FindEntry(id, &pIPE);
  858. if ( SUCCEEDED(hr) )
  859. {
  860. hr = CopyEntry(pIPE, &curCand->entry);
  861. pIPE->Release();
  862. }
  863. }
  864. }
  865. if ( SUCCEEDED(hr) )
  866. {
  867. curCand->cost = 0.0;
  868. curCand->whereFrom = -1;
  869. minCost = DBL_MAX;
  870. minIdx = -1;
  871. }
  872. // This is the minimizing loop
  873. if ( SUCCEEDED(hr) )
  874. {
  875. if (nParentEquiv)
  876. {
  877. for (j=0; j<nParentEquiv && SUCCEEDED(hr); j++)
  878. {
  879. prevCand = (*parent->equivList)[j];
  880. SPDBG_ASSERT (prevCand);
  881. hr = CostFunction (prevCand, curCand, tags, &cost);
  882. if ( SUCCEEDED(hr) )
  883. {
  884. if (cost < minCost )
  885. {
  886. minCost = cost;
  887. minIdx = j;
  888. }
  889. }
  890. }
  891. if ( SUCCEEDED(hr) && minIdx == -1 )
  892. {
  893. hr = E_FAIL;
  894. }
  895. }
  896. else
  897. {
  898. hr = CostFunction (prevCand, curCand, tags, &minCost);
  899. }
  900. }
  901. if ( SUCCEEDED(hr) )
  902. {
  903. curCand->cost = minCost;
  904. if ( dCandCost )
  905. {
  906. *dCandCost = minCost;
  907. }
  908. curCand->whereFrom = minIdx;
  909. equivList->Add(curCand);
  910. }
  911. } // for
  912. if ( SUCCEEDED(hr) )
  913. {
  914. child->equivList = equivList;
  915. }
  916. else
  917. {
  918. if ( curCand )
  919. {
  920. delete curCand;
  921. curCand = NULL;
  922. }
  923. }
  924. SPDBG_REPORT_ON_FAIL( hr );
  925. return hr;
  926. }
  927. //////////////////////////////////////////////////////////////////////
  928. // CDbQuery::ComputeCostsId
  929. //
  930. // Cost computation
  931. //
  932. /////////////////////////////////////////////////////// JOEM 3-2000 //
  933. HRESULT CDbQuery::ComputeCostsId(CCandidate *child, IPromptEntry* pIPE)
  934. {
  935. SPDBG_FUNC( "CDbQuery::ComputeCostsId" );
  936. HRESULT hr = S_OK;
  937. CCandidate* parent = NULL;
  938. CCandidate* minParent = NULL;
  939. CEquivCost* curCand = NULL;
  940. CEquivCost* prevCand = NULL;
  941. USHORT nParent = 0;
  942. USHORT nParentEquiv= 0;
  943. int minIdx = 0;
  944. double minCost = 0.0;
  945. double cost = 0.0;
  946. USHORT i = 0;
  947. USHORT j = 0;
  948. CSPArray<CEquivCost*,CEquivCost*>* equivList;
  949. curCand = new CEquivCost;
  950. if ( !curCand )
  951. {
  952. hr = E_OUTOFMEMORY;
  953. }
  954. hr = CopyEntry( pIPE, &curCand->entry );
  955. if ( SUCCEEDED(hr) )
  956. {
  957. curCand->cost = 0.0;
  958. curCand->whereFrom = -1;
  959. minParent = NULL;
  960. minCost = DBL_MAX;
  961. minIdx = -1;
  962. nParent = (USHORT) m_apCandEnd.GetSize();
  963. }
  964. // This is the minimizing loop
  965. for (i=0; i<nParent && SUCCEEDED(hr); i++)
  966. {
  967. parent = m_apCandEnd[i];
  968. nParentEquiv = (USHORT) parent->equivList->GetSize();
  969. if (nParentEquiv)
  970. {
  971. for ( j=0; j<nParentEquiv; j++ )
  972. {
  973. prevCand = (*parent->equivList)[j];
  974. hr = CostFunction (prevCand, curCand, NULL, &cost);
  975. if ( SUCCEEDED(hr) )
  976. {
  977. if (cost < minCost )
  978. {
  979. minCost = cost;
  980. minIdx = j;
  981. minParent = parent;
  982. }
  983. }
  984. }
  985. if ( SUCCEEDED(hr) && minIdx == -1 )
  986. {
  987. hr = E_FAIL;
  988. }
  989. }
  990. else
  991. {
  992. minCost = 0.0;
  993. }
  994. if ( SUCCEEDED(hr) )
  995. {
  996. curCand->cost = minCost;
  997. curCand->whereFrom = minIdx;
  998. }
  999. }
  1000. if ( SUCCEEDED(hr) )
  1001. {
  1002. equivList = new CSPArray<CEquivCost*,CEquivCost*>;
  1003. if (!equivList)
  1004. {
  1005. hr = E_OUTOFMEMORY;
  1006. }
  1007. }
  1008. if ( SUCCEEDED(hr) )
  1009. {
  1010. equivList->Add(curCand);
  1011. child->equivList = equivList;
  1012. child->parent = minParent;
  1013. }
  1014. if ( FAILED(hr) )
  1015. {
  1016. delete curCand;
  1017. }
  1018. SPDBG_REPORT_ON_FAIL( hr );
  1019. return hr;
  1020. }
  1021. //////////////////////////////////////////////////////////////////////
  1022. // CDbQuery::ComputeCostsText
  1023. //
  1024. // Cost computation
  1025. //
  1026. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1027. HRESULT CDbQuery::ComputeCostsText(CCandidate *child, const WCHAR *pszText)
  1028. {
  1029. SPDBG_FUNC( "CDbQuery::ComputeCostsText" );
  1030. HRESULT hr = S_OK;
  1031. USHORT nParent = 0;
  1032. USHORT nParentEquiv = 0;
  1033. int minIdx = 0;
  1034. double minCost = 0.0;
  1035. double cost = 0.0;
  1036. USHORT i = 0;
  1037. USHORT j = 0;
  1038. CEquivCost* curCand = NULL;
  1039. CEquivCost* prevCand = NULL;
  1040. CCandidate* parent = NULL;
  1041. CCandidate* minParent= NULL;
  1042. CSPArray<CEquivCost*,CEquivCost*>* equivList;
  1043. SPDBG_ASSERT (child);
  1044. SPDBG_ASSERT (pszText);
  1045. equivList = new CSPArray<CEquivCost*,CEquivCost*>;
  1046. if ( !equivList )
  1047. {
  1048. hr = E_OUTOFMEMORY;
  1049. }
  1050. if ( SUCCEEDED(hr) )
  1051. {
  1052. curCand = new CEquivCost;
  1053. if ( !curCand )
  1054. {
  1055. hr = E_OUTOFMEMORY;
  1056. }
  1057. }
  1058. if ( SUCCEEDED(hr) )
  1059. {
  1060. curCand->cost = 0.0;
  1061. curCand->whereFrom = -1;
  1062. curCand->text = wcsdup(pszText);
  1063. if ( !curCand->text )
  1064. {
  1065. hr = E_OUTOFMEMORY;
  1066. }
  1067. }
  1068. if ( SUCCEEDED(hr) )
  1069. {
  1070. minParent = NULL;
  1071. minCost = DBL_MAX;
  1072. minIdx = -1;
  1073. nParent = (USHORT) m_apCandEnd.GetSize();
  1074. }
  1075. if ( !nParent )
  1076. {
  1077. hr = CostFunction (NULL, curCand, NULL, &cost);
  1078. if ( SUCCEEDED(hr) )
  1079. {
  1080. curCand->cost = cost;
  1081. curCand->whereFrom = minIdx;
  1082. }
  1083. }
  1084. else
  1085. {
  1086. // This is the minimizing loop
  1087. for (i=0; i<nParent && SUCCEEDED(hr); i++)
  1088. {
  1089. parent = m_apCandEnd[i];
  1090. nParentEquiv = (USHORT) parent->equivList->GetSize();
  1091. if (nParentEquiv)
  1092. {
  1093. for (j=0; j<nParentEquiv; j++)
  1094. {
  1095. prevCand = (*parent->equivList)[j];
  1096. hr = CostFunction (prevCand, curCand, NULL, &cost);
  1097. if ( SUCCEEDED(hr) )
  1098. {
  1099. if (cost < minCost )
  1100. {
  1101. minCost = cost;
  1102. minIdx = j;
  1103. minParent = parent;
  1104. }
  1105. }
  1106. }
  1107. if ( SUCCEEDED(hr) && minIdx == -1 )
  1108. {
  1109. hr = E_FAIL;
  1110. }
  1111. }
  1112. else
  1113. {
  1114. minCost = 0.0;
  1115. }
  1116. if ( SUCCEEDED(hr) )
  1117. {
  1118. curCand->cost = minCost;
  1119. curCand->whereFrom = minIdx;
  1120. }
  1121. }
  1122. }
  1123. if ( SUCCEEDED(hr) )
  1124. {
  1125. equivList->Add(curCand);
  1126. child->equivList = equivList;
  1127. child->parent = minParent;
  1128. }
  1129. else
  1130. {
  1131. if ( curCand )
  1132. {
  1133. delete curCand;
  1134. }
  1135. }
  1136. SPDBG_REPORT_ON_FAIL( hr );
  1137. return hr;
  1138. }
  1139. //////////////////////////////////////////////////////////////////////
  1140. // CDbQuery::CostFunction
  1141. //
  1142. //
  1143. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1144. HRESULT CDbQuery::CostFunction(CEquivCost *prev, CEquivCost *cur, const CSPArray<CDynStr,CDynStr>* tags, double *cost)
  1145. {
  1146. SPDBG_FUNC( "CDbQuery::CostFunction" );
  1147. HRESULT hr = S_OK;
  1148. SPDBG_ASSERT (cur);
  1149. SPDBG_ASSERT (cost);
  1150. if ( prev )
  1151. {
  1152. *cost = prev->cost;
  1153. }
  1154. else
  1155. {
  1156. *cost = 0.0;
  1157. }
  1158. // Cost of using TTS
  1159. if ( cur->text )
  1160. {
  1161. if ( prev )
  1162. {
  1163. // if we're transitioning from prompts, add the transition cost + TTS insert cost.
  1164. if ( prev->entry )
  1165. {
  1166. *cost += FIXED_TRANSITION_COST; // Fixed cost for a transition
  1167. *cost += TTS_INSERT_COST; // * CountWords (cur->text);
  1168. }
  1169. }
  1170. else // otherwise, just add the TTS cost
  1171. {
  1172. *cost += TTS_INSERT_COST; // * CountWords (cur->text);
  1173. }
  1174. }
  1175. else // Cost of using an entry;
  1176. {
  1177. double tagCost = MATCHING_TAG_COST;
  1178. USHORT nEntryTags = 0;
  1179. if ( prev )
  1180. {
  1181. *cost += FIXED_TRANSITION_COST; // Fixed cost for a transition
  1182. }
  1183. cur->entry->CountTags(&nEntryTags);
  1184. if ( !tags )
  1185. {
  1186. if ( nEntryTags )
  1187. {
  1188. *cost += NON_MATCHING_TAG_COST;
  1189. cur->fTagMatch = false;
  1190. }
  1191. }
  1192. else
  1193. {
  1194. USHORT i = 0;
  1195. CSpDynamicString curTag;
  1196. USHORT nTags = (USHORT) tags->GetSize();
  1197. for (i=0; i<nTags && SUCCEEDED(hr); i++)
  1198. {
  1199. curTag = (*tags)[i].dstr;
  1200. const WCHAR* emptyTag = L"";
  1201. if ( nEntryTags )
  1202. {
  1203. const WCHAR* entryTag = NULL;
  1204. USHORT j = 0;
  1205. for (j=0; j< nEntryTags; j++)
  1206. {
  1207. hr = cur->entry->GetTag(&entryTag, j);
  1208. if ( SUCCEEDED(hr) )
  1209. {
  1210. if ( wcscmp(curTag, entryTag) == 0 )
  1211. {
  1212. entryTag = NULL;
  1213. break;
  1214. }
  1215. entryTag = NULL;
  1216. }
  1217. }
  1218. if (j == nEntryTags)
  1219. {
  1220. tagCost = NON_MATCHING_TAG_COST;
  1221. cur->fTagMatch = false;
  1222. }
  1223. }
  1224. //--- An empty curTag should still match with an entry that has no tags
  1225. else if ( wcscmp(curTag,emptyTag) != 0 )
  1226. {
  1227. tagCost = NON_MATCHING_TAG_COST;
  1228. cur->fTagMatch = false;
  1229. }
  1230. *cost += tagCost;
  1231. curTag.Clear();
  1232. } // for each tag
  1233. }
  1234. } // else
  1235. // If there is information about phonetic
  1236. // context, apply it
  1237. if ( SUCCEEDED(hr) )
  1238. {
  1239. if ( m_pPhoneContext && prev && prev->entry && cur->entry )
  1240. {
  1241. double cntxtCost;
  1242. hr = m_pPhoneContext->Apply( prev->entry, cur->entry, &cntxtCost );
  1243. if ( SUCCEEDED (hr) )
  1244. {
  1245. *cost += cntxtCost;
  1246. }
  1247. }
  1248. }
  1249. SPDBG_REPORT_ON_FAIL( hr );
  1250. return hr;
  1251. }
  1252. //////////////////////////////////////////////////////////////////////
  1253. // CDbQuery::CopyEntry
  1254. //
  1255. // Creates and returns a copy of the entry pointed to by pIPE.
  1256. //
  1257. /////////////////////////////////////////////////////// JOEM 5-2000 //
  1258. HRESULT CDbQuery::CopyEntry(IPromptEntry *pIPE, CPromptEntry** inEntry)
  1259. {
  1260. SPDBG_FUNC( "CDbQuery::CopyEntry" );
  1261. HRESULT hr = S_OK;
  1262. double entryTime = 0.0;
  1263. const WCHAR* entryText = NULL;
  1264. USHORT i = 0;
  1265. USHORT tagCount = 0;
  1266. CPromptEntry* newEntry = new CPromptEntry ( *(CPromptEntry*)pIPE );
  1267. if ( !newEntry )
  1268. {
  1269. hr = E_OUTOFMEMORY;
  1270. }
  1271. *inEntry = newEntry;
  1272. SPDBG_REPORT_ON_FAIL( hr );
  1273. return hr;
  1274. }
  1275. //////////////////////////////////////////////////////////////////////
  1276. // CDbQuery::RemoveLocalQueries
  1277. //
  1278. // When a local query (from an expansion rule) fails, remove all
  1279. // associated local queries, and reinstate the main query.
  1280. //
  1281. /////////////////////////////////////////////////////// JOEM 5-2000 //
  1282. HRESULT CDbQuery::RemoveLocalQueries(const USHORT unQueryNum)
  1283. {
  1284. SPDBG_FUNC( "CDbQuery::RemoveLocalQueries" );
  1285. SHORT i = 0;
  1286. CQuery* pQuery = NULL;
  1287. // Take care of current query first
  1288. pQuery = (*m_papQueries)[unQueryNum];
  1289. // If it's a local query, cancel it and the surrounding local queries,
  1290. // and restoring the original for TTS.
  1291. if ( pQuery->m_fFragType == LOCAL_FRAG )
  1292. {
  1293. pQuery->m_fSpeak = FALSE;
  1294. // step backward, flagging previous local queries as "don't speak"
  1295. for ( i = unQueryNum-1; i>=0; i-- )
  1296. {
  1297. pQuery = (*m_papQueries)[i];
  1298. if ( pQuery->m_fFragType == LOCAL_FRAG )
  1299. {
  1300. pQuery->m_fSpeak = false;
  1301. }
  1302. else
  1303. {
  1304. break;
  1305. }
  1306. }
  1307. // step forward, flagging upcoming local queries as "don't speak"
  1308. for ( i = unQueryNum+1; i<m_papQueries->GetSize(); i++ )
  1309. {
  1310. pQuery = (*m_papQueries)[i];
  1311. if ( pQuery->m_fFragType == LOCAL_FRAG )
  1312. {
  1313. pQuery->m_fSpeak = false;
  1314. }
  1315. else
  1316. {
  1317. pQuery->m_fSpeak = true;
  1318. pQuery->m_fTTS = true;
  1319. // this wasn't supposed to be spoken, but now it must, so match is false.
  1320. pQuery->m_afEntryMatch.Add(false);
  1321. break;
  1322. }
  1323. }
  1324. }
  1325. else // It's a combined frag - restore upcoming combined frags too.
  1326. {
  1327. pQuery->m_fTTS = true;
  1328. for ( i = unQueryNum+1; i<m_papQueries->GetSize(); i++ )
  1329. {
  1330. pQuery = (*m_papQueries)[i];
  1331. if ( pQuery->m_fFragType == COMBINED_FRAG )
  1332. {
  1333. pQuery->m_fFragType = SAPI_FRAG;
  1334. pQuery->m_fSpeak = true;
  1335. pQuery->m_fTTS = true;
  1336. }
  1337. else
  1338. {
  1339. break;
  1340. }
  1341. }
  1342. }
  1343. return S_OK;
  1344. }