Leaked source code of windows server 2003
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.

1735 lines
57 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: PARSER.CXX
  7. //
  8. // Contents: Implementation of the CQueryParser class
  9. //
  10. // History: 30-Apr-92 AmyA Created.
  11. // 23-Jun-92 MikeHew Added weight parsing.
  12. // 11-May-94 t-jeffc Rewrote to support new queries;
  13. // added exception handling
  14. // 02-Mar-95 t-colinb Added CPropertyValueParser and
  15. // augmented the parser to generate
  16. // a CPropertyRestriction with a
  17. // value
  18. // 25-Sep-95 sundarA Modified relative date calculation;
  19. // Replaced 'c' runtime dependant time
  20. // functions. Modified
  21. // CPropertyValueParser::CheckForRelativeDate
  22. //
  23. // Notes: See bnf.txt for a complete listing of the grammar.
  24. //
  25. //----------------------------------------------------------------------------
  26. #include <pch.cxx>
  27. #pragma hdrstop
  28. #include <cichkstk.hxx>
  29. #include <parser.hxx>
  30. DECLARE_INFOLEVEL(qutil);
  31. static const GUID guidSystem = PSGUID_STORAGE;
  32. static CDbColId psContents( guidSystem, PID_STG_CONTENTS );
  33. //+---------------------------------------------------------------------------
  34. //
  35. // Member: CQueryParser::ParseQueryPhrase, public
  36. //
  37. // Synopsis: Parses a query and returns it in the form of a tree.
  38. //
  39. // Arguments: - none -
  40. //
  41. // History: 03 Feb 1998 AlanW Added error checks
  42. //
  43. //----------------------------------------------------------------------------
  44. CDbRestriction * CQueryParser::ParseQueryPhrase()
  45. {
  46. CTrackRecursionDepth<CQueryParser> track( *this );
  47. XDbRestriction prstQuery( Query( 0 ) );
  48. // extraneous input at the end of the query?
  49. if( !_scan.IsEmpty() )
  50. {
  51. SCODE sc = QPARSE_E_EXPECTING_EOS;
  52. if ( _scan.LookAhead() == NOT_TOKEN )
  53. sc = QPARSE_E_UNEXPECTED_NOT;
  54. THROW( CParserException( sc ) );
  55. }
  56. return prstQuery.Acquire();
  57. } //ParseQueryPhrase
  58. //+---------------------------------------------------------------------------
  59. //
  60. // Member: CQueryParser::Query, private
  61. //
  62. // Synopsis: Recursive function that calls QExpr and creates a vector node
  63. // if necessary.
  64. //
  65. // Arguments: [prstVector] -- if non-null, a Vector node that can be added to.
  66. //
  67. // Production: Query : QExpr
  68. // | QExpr COMMA_TOKEN Query
  69. //
  70. // History: 01-May-92 AmyA Created
  71. // 23-Jun-92 MikeHew Added weight parsing.
  72. // 10-Feb-93 KyleP Convert to restrictions
  73. // 11-May-94 t-jeffc Rewrote to support vectors
  74. //
  75. //----------------------------------------------------------------------------
  76. CDbRestriction* CQueryParser::Query( CDbNodeRestriction * prstVector )
  77. {
  78. CTrackRecursionDepth<CQueryParser> track( *this );
  79. XDbRestriction prstExpr( QExpr( 0 ) );
  80. unsigned pos;
  81. if( _scan.LookAhead() == COMMA_TOKEN )
  82. {
  83. _scan.Accept();
  84. if ( prstVector == 0 )
  85. {
  86. //
  87. // Special case: If the first value is a rank method specifier.
  88. //
  89. if ( prstExpr->GetCommandType() == DBOP_content )
  90. {
  91. CDbContentRestriction * pContent = (CDbContentRestriction *)prstExpr.GetPointer();
  92. WCHAR const * pPhrase = pContent->GetPhrase();
  93. if ( pPhrase[0] == L'-')
  94. {
  95. if ( 0 == _wcsicmp( L"--Jaccard--", pPhrase) )
  96. {
  97. _rankMethod = VECTOR_RANK_JACCARD;
  98. delete prstExpr.Acquire();
  99. }
  100. else if ( 0 == _wcsicmp( L"--Dice--", pPhrase) )
  101. {
  102. _rankMethod = VECTOR_RANK_DICE;
  103. delete prstExpr.Acquire();
  104. }
  105. else if ( 0 == _wcsicmp( L"--Inner--", pPhrase) )
  106. {
  107. _rankMethod = VECTOR_RANK_INNER;
  108. delete prstExpr.Acquire();
  109. }
  110. else if ( 0 == _wcsicmp( L"--Max--", pPhrase) )
  111. {
  112. _rankMethod = VECTOR_RANK_MAX;
  113. delete prstExpr.Acquire();
  114. }
  115. else if ( 0 == _wcsicmp( L"--Min--", pPhrase) )
  116. {
  117. _rankMethod = VECTOR_RANK_MIN;
  118. delete prstExpr.Acquire();
  119. }
  120. }
  121. }
  122. qutilDebugOut(( DEB_TRACE,
  123. "setting rank method to: %d\n",
  124. _rankMethod ));
  125. // create smart Vector node
  126. XDbVectorRestriction prstNew( new CDbVectorRestriction( _rankMethod ) );
  127. if ( prstNew.IsNull() )
  128. THROW( CException( E_OUTOFMEMORY ) );
  129. // add left expression & release its smart pointer
  130. if ( !prstExpr.IsNull() )
  131. {
  132. prstNew->AppendChild( prstExpr.GetPointer() );
  133. prstExpr.Acquire();
  134. }
  135. if ( !prstNew->IsValid() )
  136. THROW( CException( E_OUTOFMEMORY ) );
  137. // parse right expression
  138. CDbRestriction * prst = Query( prstNew.GetPointer() );
  139. // release pointer to the vector node
  140. prstNew.Acquire();
  141. return prst;
  142. }
  143. else // there already is a vector node
  144. {
  145. // add expression & release its smart pointer
  146. prstVector->AppendChild( prstExpr.GetPointer() );
  147. prstExpr.Acquire();
  148. // parse right expression
  149. return Query( prstVector );
  150. }
  151. }
  152. else // no more COMMA_TOKENs
  153. {
  154. if( prstVector != 0 ) // add last child
  155. {
  156. // add expression & release its smart pointer
  157. prstVector->AppendChild( prstExpr.GetPointer() );
  158. prstExpr.Acquire();
  159. return prstVector;
  160. }
  161. else // no vector nodes
  162. {
  163. // release & return expression
  164. return prstExpr.Acquire();
  165. }
  166. }
  167. } //Query
  168. //+---------------------------------------------------------------------------
  169. //
  170. // Member: CQueryParser::QExpr, private
  171. //
  172. // Synopsis: Recursive function that calls QTerm and creates an Or
  173. // node if necessary.
  174. //
  175. // Arguments: [prstOr] -- if non-null, an Or node that can be added to.
  176. //
  177. // Production: QExpr : QTerm
  178. // | QTerm OR_TOKEN QExpr
  179. //
  180. // History: 05-May-94 t-jeffc Created
  181. //
  182. //----------------------------------------------------------------------------
  183. CDbRestriction* CQueryParser::QExpr( CDbBooleanNodeRestriction * prstOr )
  184. {
  185. CTrackRecursionDepth<CQueryParser> track( *this );
  186. XDbRestriction prstTerm( QTerm( 0 ) );
  187. if ( _scan.LookAhead() == OR_TOKEN )
  188. {
  189. _scan.Accept();
  190. if ( 0 == prstOr )
  191. {
  192. // create smart Or node
  193. XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_or ) );
  194. if ( prstNew.IsNull() )
  195. THROW( CException( E_OUTOFMEMORY ) );
  196. // add left term & release its smart pointer
  197. prstNew->AppendChild( prstTerm.GetPointer() );
  198. // release smart Or node pointer
  199. prstTerm.Acquire();
  200. // parse right expression
  201. CDbRestriction * prst = QExpr( prstNew.GetPointer() );
  202. // release smart Or node pointer
  203. prstNew.Acquire();
  204. return prst;
  205. }
  206. else // there already is an Or node to add to
  207. {
  208. // add left term & release its smart pointer
  209. prstOr->AppendChild( prstTerm.GetPointer() );
  210. prstTerm.Acquire();
  211. // add more children
  212. return QExpr( prstOr );
  213. }
  214. }
  215. else // no more OR_TOKENs
  216. {
  217. if( prstOr != 0 ) // add last child
  218. {
  219. // add term & release its smart pointer
  220. prstOr->AppendChild( prstTerm.GetPointer() );
  221. prstTerm.Acquire();
  222. return prstOr;
  223. }
  224. else // no OR_TOKENs at all
  225. // release & return term
  226. return prstTerm.Acquire();
  227. }
  228. } //QExpr
  229. //+---------------------------------------------------------------------------
  230. //
  231. // Member: CQueryParser::QTerm, private
  232. //
  233. // Synopsis: Recursive function that calls QFactor and creates an And
  234. // node if necessary.
  235. //
  236. // Arguments: [prstAnd] -- if non-null, an And node that can be added to.
  237. //
  238. // Production: QTerm : (NOT_TOKEN) QProp (W_OPEN_TOKEN Weight W_CLOSE_TOKEN)
  239. // | (NOT_TOKEN) QProp (W_OPEN_TOKEN Weight W_CLOSE_TOKEN) AND_TOKEN QTerm
  240. //
  241. // History: 01-May-92 AmyA Created
  242. // 11-May-94 t-jeffc Added NOTs; moved weights to this level
  243. //
  244. //----------------------------------------------------------------------------
  245. CDbRestriction* CQueryParser::QTerm( CDbBooleanNodeRestriction * prstAnd )
  246. {
  247. CTrackRecursionDepth<CQueryParser> track( *this );
  248. XDbRestriction prstTerm;
  249. if ( _scan.LookAhead() == NOT_TOKEN )
  250. {
  251. _scan.Accept();
  252. // create smart Not node
  253. XDbNotRestriction prstNot( new CDbNotRestriction );
  254. if ( prstNot.IsNull() )
  255. THROW( CException( E_OUTOFMEMORY ) );
  256. // parse factor
  257. XDbRestriction prst( QProp() );
  258. // set child of Not node & release smart factor pointer
  259. prstNot->SetChild( prst.GetPointer() );
  260. prst.Acquire();
  261. // transfer ownership from prstNot to prstTerm
  262. prstTerm.Set( prstNot.Acquire() );
  263. }
  264. else
  265. {
  266. // wrap just the factor in the smart pointer
  267. prstTerm.Set( QProp() );
  268. }
  269. LONG lWeight;
  270. if( _scan.LookAhead() == W_OPEN_TOKEN )
  271. {
  272. _scan.Accept();
  273. BOOL fAtEnd;
  274. BOOL isNumber = _scan.GetNumber( lWeight, fAtEnd );
  275. if( !isNumber )
  276. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  277. _scan.Accept();
  278. if( _scan.LookAhead() != W_CLOSE_TOKEN )
  279. THROW( CParserException( QPARSE_E_EXPECTING_BRACE ) );
  280. _scan.Accept();
  281. if ( lWeight > MAX_QUERY_RANK )
  282. THROW( CParserException( QPARSE_E_WEIGHT_OUT_OF_RANGE ) );
  283. }
  284. else
  285. {
  286. lWeight = MAX_QUERY_RANK;
  287. }
  288. // We should be able to set weights on all the nodes.
  289. prstTerm->SetWeight( lWeight );
  290. if ( _scan.LookAhead() == AND_TOKEN )
  291. {
  292. _scan.Accept();
  293. if( 0 == prstAnd )
  294. {
  295. // create smart And node
  296. XDbBooleanNodeRestriction prstNew( new CDbBooleanNodeRestriction( DBOP_and ) );
  297. if ( prstNew.IsNull() )
  298. THROW( CException( E_OUTOFMEMORY ) );
  299. // add left factor & release its smart pointer
  300. prstNew->AppendChild( prstTerm.GetPointer() );
  301. prstTerm.Acquire();
  302. // parse right expression
  303. CDbRestriction * prst = QTerm( prstNew.GetPointer() );
  304. // release smart And node pointer
  305. prstNew.Acquire();
  306. return prst;
  307. }
  308. else // there already is an And node to add to
  309. {
  310. // add left factor & release its smart pointer
  311. prstAnd->AppendChild( prstTerm.GetPointer() );
  312. prstTerm.Acquire();
  313. // add more children
  314. return QTerm( prstAnd );
  315. }
  316. }
  317. else // no more AND_TOKENs
  318. {
  319. if ( 0 != prstAnd ) // add last child
  320. {
  321. // add factor & release its smart pointer
  322. prstAnd->AppendChild( prstTerm.GetPointer() );
  323. prstTerm.Acquire();
  324. return prstAnd;
  325. }
  326. // no AND_TOKENs at all
  327. // release & return factor
  328. return prstTerm.Acquire();
  329. }
  330. } //QTerm
  331. //
  332. // array for converting token to relop - this relies on the order
  333. // of the token enumeration in scanner.hxx
  334. //
  335. enum DBOPModifier
  336. {
  337. DBOPModifyNone = 0,
  338. DBOPModifyAll = 1,
  339. DBOPModifyAny = 2
  340. };
  341. static const DBCOMMANDOP rgRelopToken[] =
  342. {
  343. DBOP_equal,
  344. DBOP_not_equal,
  345. DBOP_greater,
  346. DBOP_greater_equal,
  347. DBOP_less,
  348. DBOP_less_equal,
  349. DBOP_allbits,
  350. DBOP_anybits,
  351. DBOP_equal_all,
  352. DBOP_not_equal_all,
  353. DBOP_greater_all,
  354. DBOP_greater_equal_all,
  355. DBOP_less_all,
  356. DBOP_less_equal_all,
  357. DBOP_allbits_all,
  358. DBOP_anybits_all,
  359. DBOP_equal_any,
  360. DBOP_not_equal_any,
  361. DBOP_greater_any,
  362. DBOP_greater_equal_any,
  363. DBOP_less_any,
  364. DBOP_less_equal_any,
  365. DBOP_allbits_any,
  366. DBOP_anybits_any,
  367. };
  368. const unsigned cRelopToken = sizeof rgRelopToken / sizeof rgRelopToken[0];
  369. inline DBCOMMANDOP FormDBOP( ULONG op, ULONG opModifier )
  370. {
  371. Win4Assert( cRelopToken == (SOMEOF_TOKEN+1) * (DBOPModifyAny+1) );
  372. Win4Assert( op <= SOMEOF_TOKEN );
  373. Win4Assert( opModifier <= DBOPModifyAny );
  374. return rgRelopToken[ opModifier*(SOMEOF_TOKEN+1) + op ];
  375. }
  376. //+---------------------------------------------------------------------------
  377. //
  378. // Member: CQueryParser::QProp, private
  379. //
  380. // Synopsis: Allows a prop specification or uses the current property
  381. // for the following factor.
  382. //
  383. // Production: QProp : QFactor
  384. // | PROP_TOKEN property QFactor
  385. // | PROP_REGEX_TOKEN property (EQUAL_TOKEN) REGEX
  386. // | PROP_NATLANG_TOKEN property QPhrase
  387. //
  388. // History: 05-May-92 AmyA Created
  389. // 11-May-94 t-jeffc Added regex support and expanded
  390. // property restrictions
  391. //
  392. //----------------------------------------------------------------------------
  393. CDbRestriction* CQueryParser::QProp()
  394. {
  395. CTrackRecursionDepth<CQueryParser> track( *this );
  396. XDbRestriction prstFactor;
  397. XPtrST<WCHAR> wcsProperty;
  398. switch( _scan.LookAhead() )
  399. {
  400. case PROP_TOKEN:
  401. {
  402. _scan.Accept();
  403. // parse property name & cache in smart pointer
  404. wcsProperty.Set( _scan.AcqColumn() );
  405. if( wcsProperty.GetPointer() == 0 )
  406. THROW( CParserException( QPARSE_E_EXPECTING_PROPERTY ) );
  407. _scan.AcceptColumn();
  408. // make this property the current one
  409. SetCurrentProperty( wcsProperty.GetPointer(), CONTENTS );
  410. prstFactor.Set( QFactor() );
  411. break;
  412. }
  413. case PROP_REGEX_TOKEN: // process 'PROP_REGEX_TOKEN property regex' rule
  414. {
  415. _scan.Accept();
  416. // get property name & cache in smart pointer
  417. wcsProperty.Set( _scan.AcqColumn() );
  418. if( wcsProperty.GetPointer() == 0 )
  419. THROW( CParserException( QPARSE_E_EXPECTING_PROPERTY ) );
  420. _scan.AcceptColumn();
  421. SetCurrentProperty( wcsProperty.GetPointer(), REGEX );
  422. // allow optional equal token in regex queries
  423. if ( EQUAL_TOKEN == _scan.LookAhead() )
  424. _scan.Accept();
  425. prstFactor.Set( QPhrase() );
  426. break;
  427. }
  428. case PROP_NATLANG_TOKEN: // process 'PROP_NATLANG_TOKEN property QGroup' rule
  429. _scan.Accept();
  430. // get property name & cache in smart pointer
  431. wcsProperty.Set( _scan.AcqColumn() );
  432. if( wcsProperty.GetPointer() == 0 )
  433. THROW( CParserException( QPARSE_E_EXPECTING_PROPERTY ) );
  434. _scan.AcceptColumn();
  435. SetCurrentProperty( wcsProperty.GetPointer(), NATLANGUAGE );
  436. prstFactor.Set( QPhrase() );
  437. break;
  438. default: // No property name
  439. prstFactor.Set( QFactor() );
  440. break;
  441. } // switch( _scan.LookAhead() )
  442. // release & return smart factor
  443. return prstFactor.Acquire();
  444. } //QProp
  445. //+---------------------------------------------------------------------------
  446. //
  447. // Member: CQueryParser::QFactor, private
  448. //
  449. // Synopsis: Calls Query if parentheses are detected. Processes property
  450. // query if a PROP_TOKEN, PROP_REGEX_TOKEN or OP_TOKEN are found.
  451. // Otherwise calls QGroup.
  452. //
  453. // Production: QFactor : QGroup
  454. // | OPEN_TOKEN Query CLOSE_TOKEN
  455. // | OP_TOKEN phrase
  456. //
  457. // History: 05-May-92 AmyA Created
  458. // 11-May-94 t-jeffc Added regex support and expanded
  459. // property restrictions
  460. //
  461. //----------------------------------------------------------------------------
  462. CDbRestriction* CQueryParser::QFactor()
  463. {
  464. CTrackRecursionDepth<CQueryParser> track( *this );
  465. XDbRestriction prstFactor;
  466. XPtrST<WCHAR> wcsProperty;
  467. switch( _scan.LookAhead() )
  468. {
  469. case OPEN_TOKEN: // process 'OPEN_TOKEN Query CLOSE_TOKEN' rule
  470. {
  471. _scan.Accept();
  472. // save-away the current property so it can be restored after
  473. // the expression in parenthesis is parsed.
  474. unsigned cwc = wcslen( GetCurrentProperty() );
  475. XGrowable<WCHAR, 20> xSaveProp( cwc + 1 );
  476. wcscpy( xSaveProp.Get(), GetCurrentProperty() );
  477. PropertyType ptSave = _propType;
  478. // parse expression
  479. prstFactor.Set( Query( 0 ) );
  480. if( _scan.LookAhead() != CLOSE_TOKEN )
  481. {
  482. SCODE sc = QPARSE_E_EXPECTING_PAREN;
  483. if ( _scan.LookAhead() == NOT_TOKEN )
  484. sc = QPARSE_E_UNEXPECTED_NOT;
  485. THROW( CParserException( sc ) );
  486. }
  487. _scan.Accept();
  488. SetCurrentProperty( xSaveProp.Get(), ptSave );
  489. break;
  490. }
  491. case EQUAL_TOKEN: // process 'OP_TOKEN phrase' rule
  492. case NOT_EQUAL_TOKEN: // (only if in non-regex mode)
  493. case GREATER_TOKEN:
  494. case GREATER_EQUAL_TOKEN:
  495. case LESS_TOKEN:
  496. case LESS_EQUAL_TOKEN:
  497. case ALLOF_TOKEN:
  498. case SOMEOF_TOKEN:
  499. if( !IsRegEx() )
  500. {
  501. prstFactor.Set( ParsePropertyRst() );
  502. break;
  503. }
  504. // FALL THROUGH
  505. default: // No parentheses or op token
  506. prstFactor.Set( QGroup( 0 ) );
  507. break;
  508. } // switch( _scan.LookAhead() )
  509. // release & return smart factor
  510. return prstFactor.Acquire();
  511. } //QFactor
  512. //+---------------------------------------------------------------------------
  513. //
  514. // Member: CQueryParser::ParsePropertyRst, private
  515. //
  516. // Synopsis: Parses a relational property restriction and returns
  517. // a CPropertyRestriction
  518. //
  519. // History: 26-May-94 t-jeffc Created
  520. // 02-Mar-95 t-colinb Added the parsing of vector properties
  521. //
  522. //----------------------------------------------------------------------------
  523. CDbRestriction * CQueryParser::ParsePropertyRst()
  524. {
  525. CTrackRecursionDepth<CQueryParser> track( *this );
  526. // create smart Property node
  527. XDbPropertyRestriction prstProp( new CDbPropertyRestriction );
  528. if ( prstProp.IsNull() )
  529. THROW( CException( E_OUTOFMEMORY ) );
  530. DBID *pdbid = 0;
  531. DBTYPE ptype;
  532. if( FAILED(_xList->GetPropInfoFromName(
  533. GetCurrentProperty(),
  534. &pdbid,
  535. &ptype,
  536. 0 )) )
  537. THROW( CParserException( QPARSE_E_NO_SUCH_PROPERTY ) );
  538. CDbColId * pps = (CDbColId *)pdbid;
  539. Win4Assert( 0 != pps && pps->IsValid() );
  540. if (! prstProp->SetProperty( *pps ) )
  541. THROW( CException( E_OUTOFMEMORY ) );
  542. // don't allow @contents <relop> X -- it's too expensive and we'll
  543. // never find any hits anyway (until we implement this feature)
  544. if ( *pps == psContents )
  545. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  546. ULONG op = _scan.LookAhead();
  547. _scan.Accept();
  548. ULONG opModifier = DBOPModifyNone;
  549. //
  550. // look for a relop modifier like allof or anyof
  551. //
  552. switch( _scan.LookAhead() )
  553. {
  554. case ALLOF_TOKEN :
  555. opModifier = DBOPModifyAll;
  556. _scan.Accept();
  557. break;
  558. case SOMEOF_TOKEN :
  559. opModifier = DBOPModifyAny;
  560. _scan.Accept();
  561. break;
  562. }
  563. prstProp->SetRelation( FormDBOP( op, opModifier ) );
  564. switch( _scan.LookAhead() )
  565. {
  566. case PROP_TOKEN:
  567. case PROP_REGEX_TOKEN: // process 'PROP_TOKEN property OP_TOKEN PROP_TOKEN property' rule
  568. #if 0
  569. {
  570. _scan.Accept();
  571. THROW( CParserException( QPARSE_E_NOT_YET_IMPLEMENTED ) );
  572. }
  573. #endif // 0
  574. default: // process 'PROP_TOKEN property OP_TOKEN string' rule
  575. {
  576. CPropertyValueParser PropValueParser( _scan, ptype, _locale );
  577. XPtr<CStorageVariant> pStorageVar( PropValueParser.AcquireStgVariant() );
  578. if ( 0 != pStorageVar.GetPointer() )
  579. {
  580. // This should always be the case - else PropValueParser would have thrown
  581. if ( ! ( ( prstProp->SetValue( pStorageVar.GetReference() ) ) &&
  582. ( prstProp->IsValid() ) ) )
  583. THROW( CException( E_OUTOFMEMORY ) );
  584. }
  585. }
  586. } // switch
  587. // release & return property restriction
  588. return prstProp.Acquire();
  589. } //ParsePropertyRst
  590. //+---------------------------------------------------------------------------
  591. //
  592. // Member: CQueryParser::QGroup, private
  593. //
  594. // Synopsis: Recursive function that calls QPhrase and creates a Proximity
  595. // node if necessary.
  596. //
  597. // Arguments: [prstProx] -- if non-null, a Proximity node that can be added to.
  598. //
  599. // Production: QGroup : QPhrase
  600. // | QPhrase PROX_TOKEN QGroup
  601. //
  602. // History: 04-May-92 AmyA Created
  603. //
  604. //----------------------------------------------------------------------------
  605. CDbRestriction* CQueryParser::QGroup( CDbProximityNodeRestriction * prstProx )
  606. {
  607. CTrackRecursionDepth<CQueryParser> track( *this );
  608. XDbRestriction prst;
  609. if ( 0 == prstProx )
  610. prst.Set( QPhrase() );
  611. else
  612. prst.Set( QProp() );
  613. if( _scan.LookAhead() == PROX_TOKEN )
  614. {
  615. _scan.Accept();
  616. if ( 0 == prstProx )
  617. {
  618. // create smart Prox node
  619. XDbProximityNodeRestriction prstNew(new CDbProximityNodeRestriction());
  620. if( prstNew.IsNull() || !prstNew->IsValid() )
  621. THROW( CException( E_OUTOFMEMORY ) );
  622. // add left phrase & release its smart pointer
  623. prstNew->AppendChild( prst.GetPointer() );
  624. prst.Acquire();
  625. // parse right expression
  626. CDbRestriction * prst = QGroup( prstNew.GetPointer() );
  627. // release smart Prox node pointer
  628. prstNew.Acquire();
  629. return prst;
  630. }
  631. else // there already is a Prox node to add to
  632. {
  633. // add left phrase & release its smart pointer
  634. prstProx->AppendChild( prst.GetPointer() );
  635. prst.Acquire();
  636. // add more children
  637. return QGroup( prstProx );
  638. }
  639. }
  640. else // no more PROX_TOKENs
  641. {
  642. if( prstProx != 0 ) // add last child
  643. {
  644. // add phrase & release its smart pointer
  645. prstProx->AppendChild( prst.GetPointer() );
  646. prst.Acquire();
  647. return prstProx;
  648. }
  649. else // no PROX_TOKENs at all
  650. // release & return phrase
  651. return prst.Acquire();
  652. }
  653. } //QGroup
  654. //+---------------------------------------------------------------------------
  655. //
  656. // Member: CQueryParser::QPhrase, private
  657. //
  658. // Synopsis: If expecting a content query, acquires the phrase, determines
  659. // the fuzzy level and creates a ContentRestriction. If expecting
  660. // a natural language query, acquires the phrase and creates a
  661. // NatLanguageRestriction. If expecting a regular expression,
  662. // acquires that from the scanner and creates a new PropertyRestriction.
  663. //
  664. // Production: QPhrase : phrase(FUZZY_TOKEN | FUZ2_TOKEN)
  665. // | REGEX
  666. // | QUOTES_TOKEN extended_phrase(FUZZY_TOKEN | FUZ2_TOKEN)
  667. //
  668. // History: 01-May-92 AmyA Created
  669. // 25-May-93 BartoszM Changed fuzzy syntax
  670. // 10-May-94 t-jeffc Recognizes regex phrases
  671. //
  672. //----------------------------------------------------------------------------
  673. CDbRestriction* CQueryParser::QPhrase()
  674. {
  675. CTrackRecursionDepth<CQueryParser> track( *this );
  676. CDbColId * pps = 0;
  677. DBID *pdbid = 0;
  678. DBTYPE dbType;
  679. if( FAILED(_xList->GetPropInfoFromName( GetCurrentProperty(),
  680. &pdbid,
  681. &dbType,
  682. 0 )) )
  683. THROW( CParserException( QPARSE_E_NO_SUCH_PROPERTY ) );
  684. pps = (CDbColId *)pdbid;
  685. if( IsRegEx() ) // used PROP_REGEX_CHAR to specify property
  686. {
  687. if ( ( ( DBTYPE_WSTR|DBTYPE_BYREF ) != dbType ) &&
  688. ( ( DBTYPE_STR|DBTYPE_BYREF ) != dbType ) &&
  689. ( VT_BSTR != dbType ) &&
  690. ( VT_LPWSTR != dbType ) &&
  691. ( VT_LPSTR != dbType ) )
  692. THROW( CParserException( QPARSE_E_EXPECTING_REGEX_PROPERTY ) );
  693. XPtrST<WCHAR> phraseRegEx( _scan.AcqRegEx() );
  694. if( phraseRegEx.GetPointer() == 0 )
  695. THROW( CParserException( QPARSE_E_EXPECTING_REGEX ) );
  696. _scan.Accept();
  697. // create smart Property node
  698. XDbPropertyRestriction prstProp( new CDbPropertyRestriction );
  699. if ( prstProp.IsNull() )
  700. THROW( CException( E_OUTOFMEMORY ) );
  701. prstProp->SetRelation(DBOP_like); // LIKE relation
  702. if ( ( ! prstProp->SetProperty( *pps ) ) ||
  703. ( ! prstProp->SetValue( phraseRegEx.GetPointer() ) ) ||
  704. ( ! prstProp->IsValid() ) )
  705. THROW( CException( E_OUTOFMEMORY ) );
  706. // release & return smart Property node
  707. return prstProp.Acquire();
  708. }
  709. else
  710. {
  711. XPtrST<WCHAR> phrase;
  712. if ( _scan.LookAhead() == QUOTES_TOKEN )
  713. {
  714. _scan.AcceptQuote();
  715. phrase.Set( _scan.AcqPhraseInQuotes() );
  716. }
  717. else
  718. phrase.Set( _scan.AcqPhrase() );
  719. if( phrase.GetPointer() == 0 )
  720. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  721. _scan.Accept();
  722. int fuzzy = 0;
  723. Token tok = _scan.LookAhead();
  724. if ( tok == FUZZY_TOKEN )
  725. {
  726. _scan.Accept();
  727. fuzzy = 1;
  728. }
  729. else if ( tok == FUZ2_TOKEN )
  730. {
  731. _scan.Accept();
  732. fuzzy = 2;
  733. }
  734. if ( _propType == CONTENTS ) // used PROP_TOKEN to specify property
  735. {
  736. // create smart Content node
  737. XDbContentRestriction prstContent( new CDbContentRestriction( phrase.GetPointer(),
  738. *pps,
  739. fuzzy,
  740. _locale ));
  741. if ( prstContent.IsNull() || !prstContent->IsValid() )
  742. THROW( CException( E_OUTOFMEMORY ) );
  743. // release & return smart Content node
  744. return prstContent.Acquire();
  745. }
  746. else // used PROP_NATLANG_TOKEN to specify property
  747. {
  748. // create smart Natural Language node
  749. XDbNatLangRestriction pNatLangRst( new CDbNatLangRestriction( phrase.GetPointer(),
  750. *pps,
  751. _locale ));
  752. if ( pNatLangRst.IsNull() || !pNatLangRst->IsValid() )
  753. THROW( CException( E_OUTOFMEMORY ) );
  754. // release & return smart Natural Language node
  755. return pNatLangRst.Acquire();
  756. }
  757. }
  758. } //QPhrase
  759. //+---------------------------------------------------------------------------
  760. //
  761. // Member: CQueryParser::SetCurrentProperty, private
  762. //
  763. // Synopsis: Changes the property used in content and property restrictions
  764. // from this point on in the input line.
  765. //
  766. // Arguments: wcsProperty -- friendly name of property
  767. // (can be 0)
  768. // propType -- specifies the property type
  769. //
  770. // Notes: Makes its own copy of the property name
  771. // (unlike GetCurrentProperty, which just returns a pointer)
  772. //
  773. // History: 18-May-94 t-jeffc Created
  774. //
  775. //----------------------------------------------------------------------------
  776. void CQueryParser::SetCurrentProperty( WCHAR const * wcsProperty,
  777. PropertyType propType )
  778. {
  779. delete [] _wcsProperty;
  780. _wcsProperty = 0;
  781. if ( 0 != wcsProperty )
  782. {
  783. int cwc = wcslen( wcsProperty ) + 1;
  784. _wcsProperty = new WCHAR[ cwc ];
  785. RtlCopyMemory( _wcsProperty, wcsProperty, cwc * sizeof WCHAR );
  786. }
  787. _propType = propType;
  788. } //SetCurrentProperty
  789. //+---------------------------------------------------------------------------
  790. //
  791. // Member: CPropertyValueParser::CPropertyValueParser, public
  792. //
  793. // Synopsis: This constructor reads token from scanner and
  794. // generates the corresponding CStorageVariant
  795. //
  796. // History: 02-Mar-95 t-colinb Created.
  797. // 02-Sep-98 KLam Added locale
  798. //
  799. //----------------------------------------------------------------------------
  800. CPropertyValueParser::CPropertyValueParser(
  801. CQueryScanner &scanner,
  802. DBTYPE PropType,
  803. LCID locale ) :
  804. _pStgVariant( 0 ),
  805. _locale ( locale )
  806. {
  807. unsigned cElements=0;
  808. BOOL fParsingVector = (C_OPEN_TOKEN == scanner.LookAhead());
  809. if ( fParsingVector )
  810. {
  811. // this is a vector
  812. if ( DBTYPE_VECTOR != ( PropType & DBTYPE_VECTOR ) )
  813. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  814. scanner.Accept();
  815. VARENUM ve = (VARENUM ) PropType;
  816. if ( PropType == ( DBTYPE_VECTOR | DBTYPE_WSTR ) )
  817. ve = (VARENUM) (VT_VECTOR | VT_LPWSTR);
  818. else if ( PropType == ( DBTYPE_VECTOR | DBTYPE_STR ) )
  819. ve = (VARENUM) (VT_VECTOR | VT_LPSTR);
  820. _pStgVariant.Set( new CStorageVariant( ve, cElements ) );
  821. }
  822. else
  823. {
  824. // ok to look for singletons with a vector (sometimes)
  825. _pStgVariant.Set( new CStorageVariant() );
  826. }
  827. if ( 0 == _pStgVariant.GetPointer() )
  828. THROW( CException( E_OUTOFMEMORY ) );
  829. // first check for an empty vector -- these are legal
  830. if ( ( fParsingVector ) &&
  831. ( C_CLOSE_TOKEN == scanner.LookAhead() ) )
  832. {
  833. scanner.Accept();
  834. return;
  835. }
  836. BOOL fFinished = FALSE;
  837. do
  838. {
  839. XPtrST<WCHAR> wcsPhrase;
  840. if ( QUOTES_TOKEN == scanner.LookAhead() )
  841. {
  842. // this is a phrase in quotes
  843. scanner.AcceptQuote();
  844. wcsPhrase.Set( scanner.AcqPhraseInQuotes() );
  845. if ( wcsPhrase.GetPointer() == 0 )
  846. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  847. }
  848. else
  849. {
  850. wcsPhrase.Set( scanner.AcqPhrase() );
  851. if ( wcsPhrase.GetPointer() == 0 )
  852. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  853. }
  854. scanner.Accept();
  855. switch ( PropType & ~DBTYPE_VECTOR )
  856. {
  857. case DBTYPE_WSTR :
  858. case DBTYPE_WSTR | DBTYPE_BYREF :
  859. {
  860. if ( PropType & DBTYPE_VECTOR )
  861. _pStgVariant->SetLPWSTR( wcsPhrase.GetPointer(), cElements );
  862. else
  863. _pStgVariant->SetLPWSTR( wcsPhrase.GetPointer() );
  864. break;
  865. }
  866. case DBTYPE_BSTR :
  867. {
  868. BSTR bstr = SysAllocString( wcsPhrase.GetPointer() );
  869. if ( 0 == bstr )
  870. THROW( CException( E_OUTOFMEMORY ) );
  871. if ( PropType & DBTYPE_VECTOR )
  872. _pStgVariant->SetBSTR( bstr, cElements );
  873. else
  874. _pStgVariant->SetBSTR( bstr );
  875. SysFreeString( bstr );
  876. break;
  877. }
  878. case DBTYPE_STR :
  879. case DBTYPE_STR | DBTYPE_BYREF :
  880. {
  881. // make sure there's enough room to translate
  882. unsigned cbBuffer = 1 + 3 * wcslen( wcsPhrase.GetPointer() );
  883. XArray<char> xBuf( cbBuffer );
  884. int cc = WideCharToMultiByte( CP_ACP,
  885. 0,
  886. wcsPhrase.GetPointer(),
  887. -1,
  888. xBuf.Get(),
  889. cbBuffer,
  890. NULL,
  891. NULL );
  892. if ( 0 == cc )
  893. {
  894. #if CIDBG
  895. ULONG ul = GetLastError();
  896. #endif
  897. THROW( CParserException( QPARSE_E_EXPECTING_PHRASE ) );
  898. }
  899. if ( PropType & DBTYPE_VECTOR )
  900. _pStgVariant->SetLPSTR( xBuf.Get(), cElements );
  901. else
  902. _pStgVariant->SetLPSTR( xBuf.Get() );
  903. break;
  904. }
  905. case DBTYPE_I1 :
  906. {
  907. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  908. LONG l = 0;
  909. BOOL fAtEndOfString;
  910. if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
  911. fAtEndOfString ) )
  912. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  913. if ( ( l > SCHAR_MAX ) ||
  914. ( l < SCHAR_MIN ) )
  915. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  916. if ( PropType & DBTYPE_VECTOR )
  917. _pStgVariant->SetI1( (CHAR) l, cElements );
  918. else
  919. _pStgVariant->SetI1( (CHAR) l );
  920. break;
  921. }
  922. case DBTYPE_UI1 :
  923. {
  924. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  925. ULONG ul = 0;
  926. BOOL fAtEndOfString;
  927. if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
  928. fAtEndOfString ) )
  929. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  930. if ( ul > UCHAR_MAX )
  931. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  932. if ( PropType & DBTYPE_VECTOR )
  933. _pStgVariant->SetUI1( (BYTE) ul, cElements );
  934. else
  935. _pStgVariant->SetUI1( (BYTE) ul );
  936. break;
  937. }
  938. case DBTYPE_I2 :
  939. {
  940. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  941. LONG l = 0;
  942. BOOL fAtEndOfString;
  943. if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
  944. fAtEndOfString ) )
  945. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  946. if ( ( l > SHRT_MAX ) ||
  947. ( l < SHRT_MIN ) )
  948. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  949. if ( PropType & DBTYPE_VECTOR )
  950. _pStgVariant->SetI2( (short) l, cElements );
  951. else
  952. _pStgVariant->SetI2( (short) l );
  953. break;
  954. }
  955. case DBTYPE_UI2 :
  956. {
  957. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  958. ULONG ul = 0;
  959. BOOL fAtEndOfString;
  960. if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
  961. fAtEndOfString ) )
  962. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  963. if ( ul > USHRT_MAX )
  964. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  965. if ( PropType & DBTYPE_VECTOR )
  966. _pStgVariant->SetUI2( (USHORT) ul, cElements );
  967. else
  968. _pStgVariant->SetUI2( (USHORT) ul );
  969. break;
  970. }
  971. case DBTYPE_I4 :
  972. {
  973. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  974. LONG l = 0;
  975. BOOL fAtEndOfString;
  976. if ( ! ( scan.GetNumber( l, fAtEndOfString ) &&
  977. fAtEndOfString ) )
  978. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  979. if ( PropType & DBTYPE_VECTOR )
  980. _pStgVariant->SetI4( l, cElements );
  981. else
  982. _pStgVariant->SetI4( l );
  983. break;
  984. }
  985. case DBTYPE_UI4 :
  986. {
  987. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  988. ULONG ul = 0;
  989. BOOL fAtEndOfString;
  990. if ( ! ( scan.GetNumber( ul, fAtEndOfString ) &&
  991. fAtEndOfString ) )
  992. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  993. if ( PropType & DBTYPE_VECTOR )
  994. _pStgVariant->SetUI4( ul, cElements );
  995. else
  996. _pStgVariant->SetUI4( ul );
  997. break;
  998. }
  999. case DBTYPE_ERROR :
  1000. {
  1001. // SCODE/HRESULT are typedefed as long (signed)
  1002. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  1003. SCODE sc = 0;
  1004. BOOL fAtEndOfString;
  1005. if ( ! ( scan.GetNumber( sc, fAtEndOfString ) &&
  1006. fAtEndOfString ) )
  1007. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  1008. if ( PropType & DBTYPE_VECTOR )
  1009. _pStgVariant->SetERROR( sc, cElements );
  1010. else
  1011. _pStgVariant->SetERROR( sc );
  1012. break;
  1013. }
  1014. case DBTYPE_I8 :
  1015. {
  1016. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  1017. _int64 ll = 0;
  1018. BOOL fAtEndOfString;
  1019. if ( ! ( scan.GetNumber( ll, fAtEndOfString ) &&
  1020. fAtEndOfString ) )
  1021. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  1022. LARGE_INTEGER LargeInt;
  1023. LargeInt.QuadPart = ll;
  1024. if ( PropType & DBTYPE_VECTOR )
  1025. _pStgVariant->SetI8( LargeInt, cElements );
  1026. else
  1027. _pStgVariant->SetI8( LargeInt );
  1028. break;
  1029. }
  1030. case DBTYPE_UI8 :
  1031. {
  1032. CQueryScanner scan( wcsPhrase.GetPointer(), FALSE, locale );
  1033. unsigned _int64 ull = 0;
  1034. BOOL fAtEndOfString;
  1035. if ( ! ( scan.GetNumber( ull, fAtEndOfString ) &&
  1036. fAtEndOfString ) )
  1037. THROW( CParserException( QPARSE_E_EXPECTING_INTEGER ) );
  1038. ULARGE_INTEGER LargeInt;
  1039. LargeInt.QuadPart = ull;
  1040. if ( PropType & DBTYPE_VECTOR )
  1041. _pStgVariant->SetUI8( LargeInt, cElements );
  1042. else
  1043. _pStgVariant->SetUI8( LargeInt );
  1044. break;
  1045. }
  1046. case DBTYPE_BOOL :
  1047. {
  1048. if( wcsPhrase.GetPointer()[0] == 'T' ||
  1049. wcsPhrase.GetPointer()[0] == 't' )
  1050. if ( PropType & DBTYPE_VECTOR )
  1051. _pStgVariant->SetBOOL( VARIANT_TRUE, cElements );
  1052. else
  1053. _pStgVariant->SetBOOL( VARIANT_TRUE );
  1054. else
  1055. if ( PropType & DBTYPE_VECTOR )
  1056. _pStgVariant->SetBOOL( VARIANT_FALSE, cElements );
  1057. else
  1058. _pStgVariant->SetBOOL( VARIANT_FALSE );
  1059. break;
  1060. }
  1061. case DBTYPE_R4 :
  1062. {
  1063. WCHAR *pwcEnd = 0;
  1064. float Float = (float)( wcstod( wcsPhrase.GetPointer(), &pwcEnd ) );
  1065. if( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
  1066. THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );
  1067. if ( PropType & DBTYPE_VECTOR )
  1068. _pStgVariant->SetR4( Float, cElements );
  1069. else
  1070. _pStgVariant->SetR4( Float );
  1071. break;
  1072. }
  1073. case DBTYPE_R8 :
  1074. {
  1075. WCHAR *pwcEnd = 0;
  1076. double Double = ( double )( wcstod( wcsPhrase.GetPointer(), &pwcEnd ) );
  1077. if( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
  1078. THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );
  1079. if ( PropType & DBTYPE_VECTOR )
  1080. _pStgVariant->SetR8( Double, cElements );
  1081. else
  1082. _pStgVariant->SetR8( Double );
  1083. break;
  1084. }
  1085. case DBTYPE_DECIMAL :
  1086. {
  1087. WCHAR *pwcEnd = 0;
  1088. double Double = ( double )( wcstod( wcsPhrase.GetPointer(), &pwcEnd ) );
  1089. if( *pwcEnd != 0 && !iswspace( *pwcEnd ) )
  1090. THROW( CParserException( QPARSE_E_EXPECTING_REAL ) );
  1091. // Vectors are not supported by OLE for VT_DECIMAL (yet)
  1092. Win4Assert( 0 == ( PropType & DBTYPE_VECTOR ) );
  1093. PROPVARIANT * pPropVar = (PROPVARIANT *) _pStgVariant.GetPointer();
  1094. VarDecFromR8( Double, &(pPropVar->decVal) );
  1095. pPropVar->vt = VT_DECIMAL;
  1096. break;
  1097. }
  1098. case DBTYPE_DATE :
  1099. {
  1100. FILETIME ftValue;
  1101. ParseDateTime( wcsPhrase.GetPointer(), ftValue );
  1102. SYSTEMTIME stValue;
  1103. BOOL fOK = FileTimeToSystemTime( &ftValue, &stValue );
  1104. if ( !fOK )
  1105. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1106. DATE dosDate;
  1107. fOK = SystemTimeToVariantTime( &stValue, &dosDate );
  1108. if ( !fOK )
  1109. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1110. if ( PropType & DBTYPE_VECTOR )
  1111. _pStgVariant->SetDATE( dosDate, cElements );
  1112. else
  1113. _pStgVariant->SetDATE( dosDate );
  1114. break;
  1115. }
  1116. case VT_FILETIME :
  1117. {
  1118. FILETIME ftValue;
  1119. ParseDateTime( wcsPhrase.GetPointer(), ftValue );
  1120. if ( PropType & DBTYPE_VECTOR )
  1121. _pStgVariant->SetFILETIME( ftValue, cElements );
  1122. else
  1123. _pStgVariant->SetFILETIME( ftValue );
  1124. break;
  1125. }
  1126. case DBTYPE_CY :
  1127. {
  1128. double dbl;
  1129. if( swscanf( wcsPhrase.GetPointer(),
  1130. L"%lf",
  1131. &dbl ) < 1 )
  1132. THROW( CParserException( QPARSE_E_EXPECTING_CURRENCY ) );
  1133. CY cyCurrency;
  1134. VarCyFromR8( dbl, &cyCurrency );
  1135. if ( PropType & DBTYPE_VECTOR )
  1136. _pStgVariant->SetCY( cyCurrency, cElements );
  1137. else
  1138. _pStgVariant->SetCY( cyCurrency );
  1139. break;
  1140. }
  1141. case DBTYPE_GUID :
  1142. case DBTYPE_GUID | DBTYPE_BYREF:
  1143. {
  1144. CLSID clsid;
  1145. if( swscanf( wcsPhrase.GetPointer(),
  1146. L"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  1147. &clsid.Data1,
  1148. &clsid.Data2,
  1149. &clsid.Data3,
  1150. &clsid.Data4[0], &clsid.Data4[1],
  1151. &clsid.Data4[2], &clsid.Data4[3],
  1152. &clsid.Data4[4], &clsid.Data4[5],
  1153. &clsid.Data4[6], &clsid.Data4[7] ) < 11 )
  1154. THROW( CParserException( QPARSE_E_EXPECTING_GUID ) );
  1155. if ( PropType & DBTYPE_VECTOR )
  1156. _pStgVariant->SetCLSID( clsid, cElements );
  1157. else
  1158. _pStgVariant->SetCLSID( &clsid );
  1159. break;
  1160. }
  1161. default:
  1162. {
  1163. THROW( CParserException( QPARSE_E_UNSUPPORTED_PROPERTY_TYPE ) );
  1164. }
  1165. } // switch
  1166. // make sure memory allocations succeeded
  1167. if ( !_pStgVariant->IsValid() )
  1168. THROW( CException( E_OUTOFMEMORY ) );
  1169. if ( fParsingVector )
  1170. {
  1171. cElements++;
  1172. switch( scanner.LookAhead() )
  1173. {
  1174. case COMMA_TOKEN :
  1175. scanner.Accept();
  1176. break;
  1177. case C_CLOSE_TOKEN :
  1178. scanner.Accept();
  1179. fFinished = TRUE;
  1180. break;
  1181. default:
  1182. THROW( CParserException( QPARSE_E_EXPECTING_COMMA ) );
  1183. }
  1184. }
  1185. else
  1186. {
  1187. fFinished = TRUE;
  1188. }
  1189. } while ( !fFinished );
  1190. } //CPropertyValueParser
  1191. //+---------------------------------------------------------------------------
  1192. //
  1193. // Member: CPropertyValueParser::ParseDateTime, private
  1194. //
  1195. // Synopsis: Attempts to parse a date expression.
  1196. //
  1197. // Arguments: phrase -- pointer to the phrase to parse
  1198. // ft -- reference to the FILETIME structure to fill in
  1199. // with the result
  1200. //
  1201. // History: 31-May-96 dlee Created
  1202. // 23-Jan-97 KyleP Better Year 2000 support
  1203. // 02-Sep-98 KLam Use user settings for Y2K support
  1204. //
  1205. //----------------------------------------------------------------------------
  1206. void CPropertyValueParser::ParseDateTime(
  1207. WCHAR const * phrase,
  1208. FILETIME & ft )
  1209. {
  1210. if( !CheckForRelativeDate( phrase, ft ) )
  1211. {
  1212. SYSTEMTIME stValue = { 0, 0, 0, 0, 0, 0, 0, 0 };
  1213. int cItems = swscanf( phrase,
  1214. L"%4hd/%2hd/%2hd %2hd:%2hd:%2hd:%3hd",
  1215. &stValue.wYear,
  1216. &stValue.wMonth,
  1217. &stValue.wDay,
  1218. &stValue.wHour,
  1219. &stValue.wMinute,
  1220. &stValue.wSecond,
  1221. &stValue.wMilliseconds );
  1222. if ( 1 == cItems )
  1223. cItems = swscanf( phrase,
  1224. L"%4hd-%2hd-%2hd %2hd:%2hd:%2hd:%3hd",
  1225. &stValue.wYear,
  1226. &stValue.wMonth,
  1227. &stValue.wDay,
  1228. &stValue.wHour,
  1229. &stValue.wMinute,
  1230. &stValue.wSecond,
  1231. &stValue.wMilliseconds );
  1232. if( cItems != 3 && cItems != 6 && cItems != 7)
  1233. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1234. //
  1235. // Make a sensible split for Year 2000 using the user's system settings
  1236. //
  1237. if ( stValue.wYear < 100 )
  1238. {
  1239. DWORD dwYearHigh = 0;
  1240. if ( 0 == GetCalendarInfo ( _locale,
  1241. CAL_GREGORIAN,
  1242. CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
  1243. 0,
  1244. 0,
  1245. &dwYearHigh ) )
  1246. {
  1247. THROW ( CException ( ) );
  1248. }
  1249. if ( ( dwYearHigh < 99 ) || ( dwYearHigh > 9999 ) )
  1250. dwYearHigh = 2029;
  1251. WORD wMaxDecade = (WORD) dwYearHigh % 100;
  1252. WORD wMaxCentury = (WORD) dwYearHigh - wMaxDecade;
  1253. if ( stValue.wYear <= wMaxDecade )
  1254. stValue.wYear += wMaxCentury;
  1255. else
  1256. stValue.wYear += ( wMaxCentury - 100 );
  1257. }
  1258. if( !SystemTimeToFileTime( &stValue, &ft ) )
  1259. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1260. }
  1261. } //ParseDateTime
  1262. //+---------------------------------------------------------------------------
  1263. //
  1264. // Member: CPropertyValueParser::CheckForRelativeDate, static
  1265. //
  1266. // Synopsis: Attempts to parse a relative date expression. If successful,
  1267. // it fills in the FILETIME structure with the calculated
  1268. // absolute date.
  1269. //
  1270. // Notes: Returns TRUE if the phrase is recognized as a relative
  1271. // date (i.e., it begins with a '-'). Otherwise, returns FALSE.
  1272. // The format of a relative date is
  1273. // "-"{INTEGER("h"|"n"|"s"|"y"|"q"|"m"|"d"|"w")}*
  1274. // Case is not significant.
  1275. //
  1276. // Arguments: phrase -- pointer to the phrase to parse
  1277. // ft -- reference to the FILETIME structure to fill in
  1278. // with the result
  1279. //
  1280. // History: 26-May-94 t-jeffc Created
  1281. // 02-Mar-95 t-colinb Moved from CQueryParser to
  1282. // be more accessible
  1283. // 22-Jan-97 KyleP Fix local/UTC discrepancy
  1284. // 25-Sep-98 sundarA Removed dependency on 'c' runtime
  1285. // functions for time.
  1286. //----------------------------------------------------------------------------
  1287. BOOL CPropertyValueParser::CheckForRelativeDate(
  1288. WCHAR const * phrase,
  1289. FILETIME & ft )
  1290. {
  1291. if( *phrase++ == L'-' )
  1292. {
  1293. SYSTEMTIME st;
  1294. LARGE_INTEGER liLocal;
  1295. LONGLONG llTicksInADay = ((LONGLONG)10000000) * ((LONGLONG)3600)
  1296. * ((LONGLONG) 24);
  1297. LONGLONG llTicksInAHour = ((LONGLONG) 10000000) * ((LONGLONG)3600);
  1298. int iMonthDays[12] = {1,-1,1,0,1,0,1,1,0,1,0,1};
  1299. int iLoopValue, iPrevMonth, iPrevQuarter, iQuarterOffset;
  1300. WORD wYear, wDayOfMonth, wStartDate;
  1301. //
  1302. //Obtain system time and convert it to file time
  1303. //Copy the filetime to largeint data struct
  1304. //
  1305. GetSystemTime(&st);
  1306. if(!SystemTimeToFileTime(&st, &ft))
  1307. THROW( CParserException( QPARSE_E_INVALID_LITERAL ));
  1308. liLocal.LowPart = ft.dwLowDateTime;
  1309. liLocal.HighPart = ft.dwHighDateTime;
  1310. LONGLONG llRelDate = (LONGLONG)0;
  1311. for( ;; )
  1312. {
  1313. // eat white space
  1314. while( iswspace( *phrase ) )
  1315. phrase++;
  1316. if( *phrase == 0 ) break;
  1317. // parse the number
  1318. WCHAR * pwcEnd;
  1319. LONG lValue = wcstol( phrase, &pwcEnd, 10 );
  1320. if( lValue < 0 )
  1321. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1322. // eat white space
  1323. phrase = pwcEnd;
  1324. while( iswspace( *phrase ) )
  1325. phrase++;
  1326. // grab the unit char & subtract the appropriate amount
  1327. WCHAR wcUnit = *phrase++;
  1328. switch( wcUnit )
  1329. {
  1330. case L'y':
  1331. case L'Y':
  1332. lValue *= 4;
  1333. // Fall through and handle year like 4 quarters
  1334. case L'q':
  1335. case L'Q':
  1336. lValue *= 3;
  1337. // Fall through and handle quarters like 3 months
  1338. case L'm':
  1339. case L'M':
  1340. // Getting the System time to determine the day and month.
  1341. if(!FileTimeToSystemTime(&ft, &st))
  1342. {
  1343. THROW(CParserException(QPARSE_E_INVALID_LITERAL));
  1344. }
  1345. wStartDate = st.wDay;
  1346. wDayOfMonth = st.wDay;
  1347. iLoopValue = lValue;
  1348. while(iLoopValue)
  1349. {
  1350. // Subtracting to the end of previous month
  1351. llRelDate = llTicksInADay * ((LONGLONG)(wDayOfMonth));
  1352. liLocal.QuadPart -= llRelDate;
  1353. ft.dwLowDateTime = liLocal.LowPart;
  1354. ft.dwHighDateTime = liLocal.HighPart;
  1355. SYSTEMTIME stTemp;
  1356. if(!FileTimeToSystemTime(&ft, &stTemp))
  1357. {
  1358. THROW(CParserException(QPARSE_E_INVALID_LITERAL));
  1359. }
  1360. //
  1361. // if the end of previous month is greated then start date then we subtract to back up to the
  1362. // start date. This will take care of 28/29 Feb(backing from 30/31 by 1 month).
  1363. //
  1364. if(stTemp.wDay > wStartDate)
  1365. {
  1366. llRelDate = llTicksInADay * ((LONGLONG)(stTemp.wDay - wStartDate));
  1367. liLocal.QuadPart -= llRelDate;
  1368. ft.dwLowDateTime = liLocal.LowPart;
  1369. ft.dwHighDateTime = liLocal.HighPart;
  1370. // Getting the date into stTemp for further iteration
  1371. if(!FileTimeToSystemTime(&ft, &stTemp))
  1372. {
  1373. THROW( CParserException( QPARSE_E_INVALID_LITERAL ));
  1374. }
  1375. }
  1376. wDayOfMonth = stTemp.wDay;
  1377. iLoopValue--;
  1378. } //End While
  1379. break;
  1380. case L'w':
  1381. case L'W':
  1382. lValue *= 7;
  1383. case L'd':
  1384. case L'D':
  1385. llRelDate = llTicksInADay * ((LONGLONG)lValue) ;
  1386. liLocal.QuadPart -= llRelDate;
  1387. ft.dwLowDateTime = liLocal.LowPart;
  1388. ft.dwHighDateTime = liLocal.HighPart;
  1389. break;
  1390. case L'h':
  1391. case L'H':
  1392. llRelDate = llTicksInAHour * ((LONGLONG)lValue) ;
  1393. liLocal.QuadPart -= llRelDate;
  1394. ft.dwLowDateTime = liLocal.LowPart;
  1395. ft.dwHighDateTime = liLocal.HighPart;
  1396. break;
  1397. case L'n':
  1398. case L'N':
  1399. llRelDate = ((LONGLONG)10000000) * ((LONGLONG)60)
  1400. * ((LONGLONG)lValue) ;
  1401. liLocal.QuadPart -= llRelDate;
  1402. ft.dwLowDateTime = liLocal.LowPart;
  1403. ft.dwHighDateTime = liLocal.HighPart;
  1404. break;
  1405. case L's':
  1406. case L'S':
  1407. llRelDate = ((LONGLONG)10000000) * ((LONGLONG)lValue);
  1408. liLocal.QuadPart -= llRelDate;
  1409. ft.dwLowDateTime = liLocal.LowPart;
  1410. ft.dwHighDateTime = liLocal.HighPart;
  1411. break;
  1412. default:
  1413. THROW( CParserException( QPARSE_E_EXPECTING_DATE ) );
  1414. }
  1415. } // for( ;; )
  1416. FileTimeToSystemTime(&ft, &st);
  1417. liLocal.LowPart = ft.dwLowDateTime;
  1418. liLocal.HighPart = ft.dwHighDateTime;
  1419. qutilDebugOut(( DEB_ERROR,
  1420. "Low part : %d ; High part ; %d \n",
  1421. liLocal.LowPart, liLocal.HighPart ));
  1422. qutilDebugOut(( DEB_ERROR,
  1423. "64 bit number %I64d \n",
  1424. liLocal.QuadPart ));
  1425. qutilDebugOut(( DEB_ERROR,
  1426. "st.Year %d ; month %d ; day %d ; hour %d ; min %d ; sec %d ; msec %d \n",
  1427. st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
  1428. return TRUE;
  1429. }
  1430. else
  1431. {
  1432. return FALSE;
  1433. }
  1434. } //CheckForRelativeDate