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.

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