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.

479 lines
13 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: search.cxx
  7. //
  8. // Contents: The implementation specific methods of CSearch
  9. //
  10. // History: 05-Dec-94 Created BartoszM
  11. //
  12. //--------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. #include <curstk.hxx>
  16. #include <convert.hxx>
  17. #include <coldesc.hxx>
  18. #include <pidremap.hxx>
  19. #include <parstree.hxx>
  20. #include <drep.hxx>
  21. #include <mapper.hxx>
  22. #include <qsplit.hxx>
  23. #include <pidmap.hxx>
  24. #include <tsource.hxx>
  25. #include <pidmap.hxx>
  26. #include <propspec.hxx>
  27. #include <ciregkey.hxx>
  28. #include <regacc.hxx>
  29. #include <isearch.hxx>
  30. #include <recogniz.hxx>
  31. #include "skrep.hxx"
  32. #include "addprop.hxx"
  33. #include "appcur.hxx"
  34. //+-------------------------------------------------------------------------
  35. //
  36. // Member: CHitSink::AddPosition, public
  37. //
  38. // Synopsis: Add position to the current query hit
  39. //
  40. // History: 29-Sep-94 BartoszM Created.
  41. //
  42. //--------------------------------------------------------------------------
  43. void CHitSink::AddPosition ( const FILTERREGION& region )
  44. {
  45. // insert into sorted list
  46. unsigned count = _nFound;
  47. for ( unsigned i = 0; i < count; i++ )
  48. {
  49. if (Compare(region, _aRegion.Get(i)) > 0)
  50. break;
  51. }
  52. _aRegion.Insert ( region, i );
  53. _nFound++;
  54. }
  55. extern long gulcInstances;
  56. //+-------------------------------------------------------------------------
  57. //
  58. // Member: CSearch::CSearch, public
  59. //
  60. // Synopsis: Initialize search
  61. //
  62. // Arguments: [pRst] -- a DBCOMMANDTREE giving the expression to be
  63. // searched for.
  64. //
  65. // History: 29-Sep-94 BartoszM Created.
  66. //
  67. //--------------------------------------------------------------------------
  68. CSearch::CSearch ( DBCOMMANDTREE * pRst,
  69. ICiCLangRes * pICiCLangRes,
  70. ICiCOpenedDoc * pIOpenedDoc )
  71. : _cRefs(1),
  72. _pCursor(0),
  73. _pKeyRep (0),
  74. _iCurHit (0),
  75. _aHit(),
  76. _pidcvt( &_propMapper ),
  77. _pidmap( &_pidcvt ),
  78. _langList(pICiCLangRes)
  79. {
  80. CDbRestriction * pDbRst = (CDbRestriction *) (void *) pRst;
  81. InterlockedIncrement( &gulcInstances );
  82. CRestriction * pCRest = 0;
  83. if ( 0 != pDbRst )
  84. {
  85. CParseCommandTree parseTree;
  86. pCRest = parseTree.ParseExpression( pDbRst );
  87. }
  88. ParseAndSplitQuery( pCRest, _pidmap, _xRst, _langList );
  89. CRestriction* pSplitRst = _xRst.GetPointer();
  90. delete pCRest;
  91. if ( pIOpenedDoc )
  92. {
  93. pIOpenedDoc->AddRef();
  94. _xOpenedDoc.Set( pIOpenedDoc );
  95. }
  96. }
  97. //+-------------------------------------------------------------------------
  98. //
  99. // Member: CSearch::~CSearch, public
  100. //
  101. // Synopsis: Free resources
  102. //
  103. // History: 29-Sep-94 BartoszM Created.
  104. //
  105. //--------------------------------------------------------------------------
  106. CSearch::~CSearch ()
  107. {
  108. _aHit.Clear();
  109. Reset();
  110. InterlockedDecrement( &gulcInstances );
  111. }
  112. //+-------------------------------------------------------------------------
  113. //
  114. // Member: CSearch::Reset, public
  115. //
  116. // Synopsis: Free resources
  117. //
  118. // History: 29-Sep-94 BartoszM Created.
  119. //
  120. //--------------------------------------------------------------------------
  121. void CSearch::Reset ()
  122. {
  123. _iCurHit = 0;
  124. _aHit.Clear();
  125. delete _pCursor;
  126. _pCursor = 0;
  127. }
  128. //+-------------------------------------------------------------------------
  129. //
  130. // Member: CSearch::Init, public
  131. //
  132. // Synopsis: Do the search using the filter
  133. //
  134. // History: 29-Sep-94 BartoszM Created.
  135. //
  136. //--------------------------------------------------------------------------
  137. SCODE STDMETHODCALLTYPE CSearch::Init (
  138. IFilter * pFilter,
  139. ULONG ulFlags )
  140. {
  141. //
  142. // pFilter == 0 --> Release last filter. However, this implementation
  143. // doesn't hold onto pFilter after Init.
  144. //
  145. if ( 0 == pFilter )
  146. return S_OK;
  147. //
  148. // A null [content] restriction --> No possible hits.
  149. //
  150. if ( _xRst.IsNull() )
  151. return S_OK;
  152. Reset ();
  153. SCODE sc = S_OK;
  154. TRY
  155. {
  156. // Initialize the key recognizer
  157. // by scanning the restriction and
  158. // creating detectors for all the keys
  159. CRecognizer recog;
  160. recog.MakeDetectors(_xRst.GetPointer());
  161. // Create the filtering pipeline
  162. // that breaks text into keys and
  163. // deposits them in the key repository
  164. CSearchKeyRepository keyRep ( recog );
  165. CDataRepository dataRep ( keyRep, 0, FALSE, 0, _pidmap, _langList );
  166. // Using filter as the source of text, run it through
  167. // the filtering pipeline. The pipeline produces keys that
  168. // are put in the key repository which contains the recognizer.
  169. // The recognizer stores the filter positions of ALL the matched keys
  170. STAT_CHUNK statChunk;
  171. sc = pFilter->GetChunk( &statChunk );
  172. while ( SUCCEEDED(sc)
  173. || FILTER_E_LINK_UNAVAILABLE == sc
  174. || FILTER_E_EMBEDDING_UNAVAILABLE == sc
  175. || FILTER_E_NO_TEXT == sc )
  176. {
  177. if ( SUCCEEDED( sc ) )
  178. {
  179. //
  180. // Skip over unknown chunks.
  181. //
  182. if ( 0 == (statChunk.flags & (CHUNK_TEXT | CHUNK_VALUE)) )
  183. {
  184. ciDebugOut(( DEB_WARN,
  185. "Filtering of document for ISearch produced bogus chunk (not text or value)\n" ));
  186. sc = pFilter->GetChunk( &statChunk );
  187. continue;
  188. }
  189. if ( (statChunk.flags & CHUNK_VALUE) )
  190. {
  191. PROPVARIANT * pvar = 0;
  192. sc = pFilter->GetValue( &pvar );
  193. if ( SUCCEEDED(sc) )
  194. {
  195. XPtr<CStorageVariant> xvar( (CStorageVariant *)(ULONG_PTR)pvar );
  196. CSourceMapper mapper;
  197. mapper.NewChunk( statChunk.idChunk, 0 );
  198. keyRep.NextChunk ( mapper );
  199. if ( dataRep.PutLanguage( statChunk.locale ) &&
  200. dataRep.PutPropName( *((CFullPropSpec *)&statChunk.attribute) ) )
  201. {
  202. dataRep.PutValue( xvar.GetReference() );
  203. }
  204. }
  205. //
  206. // Only fetch next if we're done with this chunk.
  207. //
  208. if ( 0 == (statChunk.flags & CHUNK_TEXT) || !SUCCEEDED(sc) )
  209. {
  210. sc = pFilter->GetChunk( &statChunk );
  211. continue;
  212. }
  213. }
  214. if ( (statChunk.flags & CHUNK_TEXT) && SUCCEEDED(sc) )
  215. {
  216. if ( dataRep.PutLanguage( statChunk.locale ) &&
  217. dataRep.PutPropName( *((CFullPropSpec *)&statChunk.attribute )) )
  218. {
  219. // Maps position in tsource into FILTERREGION
  220. CSourceMapper mapper;
  221. // Text Source will reload statChunk when
  222. // data repository pulls it dry
  223. CTextSource tsource( pFilter, statChunk, &mapper );
  224. // prepare key repository for a new chunk
  225. keyRep.NextChunk ( mapper );
  226. // Do It!
  227. dataRep.PutStream( &tsource );
  228. sc = tsource.GetStatus();
  229. //
  230. // The text source may go a chunk too far if it
  231. // encounters a value chunk.
  232. //
  233. if ( sc == FILTER_E_NO_TEXT &&
  234. (statChunk.flags & CHUNK_VALUE) )
  235. sc = S_OK;
  236. }
  237. else
  238. {
  239. // skip chunk: reload statChunk explicitly
  240. sc = pFilter->GetChunk( &statChunk );
  241. }
  242. }
  243. }
  244. else
  245. {
  246. // Filter for embedding could not be found - try next chunk
  247. sc = pFilter->GetChunk( &statChunk );
  248. }
  249. }
  250. if ( FAILED( sc ) &&
  251. ( ( FILTER_E_END_OF_CHUNKS > sc ) ||
  252. ( FILTER_E_UNKNOWNFORMAT < sc ) ) )
  253. THROW( CException( sc ) );
  254. //
  255. // Add the properties.
  256. //
  257. if ( !_xOpenedDoc.IsNull() )
  258. {
  259. CSourceMapper mapper;
  260. mapper.NewChunk( statChunk.idChunk+1, 0 );
  261. keyRep.NextChunk ( mapper );
  262. BOOL fFilterOleProperties = (( ulFlags & IFILTER_FLAGS_OLE_PROPERTIES ) != 0);
  263. CSearchAddProp addProp( dataRep,
  264. _xOpenedDoc.GetReference(),
  265. fFilterOleProperties );
  266. addProp.DoIt();
  267. }
  268. sc = S_OK;
  269. // Create a cursor tree corresponding to the restriction
  270. // The leaves of the tree have access to the recognizer
  271. // which is now filled with key positions
  272. CAppQueriable queriable (recog, _hitSink);
  273. CRegAccess reg( RTL_REGISTRY_CONTROL, wcsRegAdmin );
  274. ULONG maxNodes = reg.Read( wcsMaxRestrictionNodes,
  275. CI_MAX_RESTRICTION_NODES_DEFAULT,
  276. CI_MAX_RESTRICTION_NODES_MIN,
  277. CI_MAX_RESTRICTION_NODES_MAX );
  278. CConverter convert( &queriable, maxNodes );
  279. _pCursor = convert.QueryCursor( _xRst.GetPointer() );
  280. if (_pCursor == 0)
  281. sc = E_UNEXPECTED;
  282. if ( convert.TooManyNodes() )
  283. sc = E_UNEXPECTED;
  284. //
  285. // Exhaust cursor of hits, ordering the hits
  286. // primarily by rank and secondarily by position.
  287. //
  288. if ( sc == S_OK && widInvalid != _pCursor->WorkId() )
  289. {
  290. // Deposit positions in the hit sink
  291. ULONG rank = _pCursor->Hit();
  292. if ( rank != rankInvalid )
  293. {
  294. do
  295. {
  296. // retrieve positions from the hit sink
  297. // and store them as a hit in a sorted
  298. // array of hits
  299. ConvertPositionsToHit( rank );
  300. // prepare for next hit
  301. _hitSink.Reset();
  302. // Deposit positions in the hit sink
  303. rank = _pCursor->NextHit();
  304. }
  305. while ( rank != rankInvalid );
  306. }
  307. }
  308. }
  309. CATCH (CException, e)
  310. {
  311. sc = GetOleError( e );
  312. }
  313. END_CATCH
  314. // empty the sink
  315. _hitSink.Reset();
  316. delete _pCursor;
  317. _pCursor = 0;
  318. return sc;
  319. }
  320. //+-------------------------------------------------------------------------
  321. //
  322. // Member: CSearch::NextHitOffset, public
  323. //
  324. // Synopsis: Return the region of the next hit
  325. //
  326. // History: 29-Sep-94 BartoszM Created.
  327. //
  328. //--------------------------------------------------------------------------
  329. SCODE STDMETHODCALLTYPE CSearch::NextHitOffset (
  330. ULONG* pcRegion,
  331. FILTERREGION ** paRegion )
  332. {
  333. if ( _xRst.IsNull() )
  334. return( S_FALSE );
  335. if (_iCurHit == _aHit.Count())
  336. {
  337. *paRegion = 0;
  338. *pcRegion = 0;
  339. return S_FALSE;
  340. }
  341. CSearchHit* pHit = _aHit.Get(_iCurHit);
  342. *pcRegion = pHit->Count();
  343. // this array is allocated using CoTaskMemAlloc
  344. *paRegion = pHit->AcqHit ();
  345. _iCurHit++;
  346. return S_OK;
  347. }
  348. //+-------------------------------------------------------------------------
  349. //
  350. // Member: CSearch::ConvertPositionToHit, public
  351. //
  352. // History: 29-Sep-94 BartoszM Created.
  353. //
  354. //--------------------------------------------------------------------------
  355. void CSearch::ConvertPositionsToHit( LONG rank )
  356. {
  357. //
  358. // Take the positions in the current list and convert
  359. // them into a single hit and put them in the ordered hit array
  360. //
  361. unsigned count = _hitSink.Count ();
  362. // These arrays will be returned across the interface
  363. FILTERREGION* aRegion = (FILTERREGION*) CoTaskMemAlloc ( count * sizeof(FILTERREGION));
  364. if (aRegion == 0)
  365. {
  366. THROW (CException(E_OUTOFMEMORY));
  367. }
  368. for (unsigned k = 0; k < count; k++)
  369. {
  370. aRegion [k] = _hitSink.GetRegion (k);
  371. }
  372. CSearchHit * pNewHit = new CSearchHit( rank, count, aRegion );
  373. //
  374. // Order Hits primarily by rank and secondarily by
  375. // position
  376. //
  377. for ( unsigned i = 0; i < _aHit.Count(); i++ )
  378. {
  379. CSearchHit* pHit = _aHit.Get(i);
  380. if ( pHit->Rank() < rank
  381. || pHit->Rank() == rank && Compare (pHit->FirstRegion(), aRegion[0]) > 0 )
  382. {
  383. break;
  384. }
  385. }
  386. _aHit.Insert(pNewHit, i);
  387. }
  388. SCODE STDMETHODCALLTYPE CSearch::NextHitMoniker(
  389. ULONG * pcMnk,
  390. IMoniker *** papMnk )
  391. {
  392. return E_NOTIMPL;
  393. }