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.

1593 lines
43 KiB

  1. /********************************************************************
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. pfxml.cpp
  5. Abstract:
  6. A simple XML parser & object model (for read only access to an XML
  7. file. This is heavily (nearly stolen) from WSmith's SimpleXML
  8. stuff that he wrote for the Neptune comments button.
  9. Note that this parser / object model is NOT thread safe.
  10. Revision History:
  11. DerekM created 03/15/00
  12. ********************************************************************/
  13. #include "stdafx.h"
  14. #include "pfxml.h"
  15. #include "xmlparser.h"
  16. /////////////////////////////////////////////////////////////////////////////
  17. // globals
  18. WCHAR g_wszEncoding[] = L"<?xml version=\"1.0\" ?>";
  19. /////////////////////////////////////////////////////////////////////////////
  20. // tracing
  21. #ifdef THIS_FILE
  22. #undef THIS_FILE
  23. #endif
  24. static char __szTraceSourceFile[] = __FILE__;
  25. #define THIS_FILE __szTraceSourceFile
  26. /////////////////////////////////////////////////////////////////////////////
  27. // struct used to hold attribute info & associated class stuff
  28. struct SPFXMLNodeAttr
  29. {
  30. LPWSTR pwszName;
  31. LPWSTR pwszValue;
  32. };
  33. // ***************************************************************************
  34. void CPFArrayAttr::DeleteItem(LPVOID pv)
  35. {
  36. USE_TRACING("CPFArrayAttr::DeleteItem");
  37. SPFXMLNodeAttr *p = (SPFXMLNodeAttr *)pv;
  38. if (p != NULL)
  39. MyFree(p, g_hPFPrivateHeap);
  40. }
  41. // ***************************************************************************
  42. LPVOID CPFArrayAttr::AllocItemCopy(LPVOID pv)
  43. {
  44. USE_TRACING("CPFArrayAttr::AllocItem");
  45. SPFXMLNodeAttr *p = (SPFXMLNodeAttr *)pv;
  46. SPFXMLNodeAttr *pNew;
  47. HRESULT hr = NOERROR;
  48. SIZE_T cbName = 0, cbValue = 0;
  49. VALIDATEPARM(hr, (pv == NULL));
  50. if (FAILED(hr))
  51. return NULL;
  52. // assume that the only way that a node was allocated was with the
  53. // add_Attribute fn below... Thus, the pointers are always non-NULL
  54. // and valid & the all the data is in one allocated blob...
  55. cbName = (PBYTE)p->pwszName - ((PBYTE)p + sizeof(SPFXMLNodeAttr));
  56. cbValue = (PBYTE)p->pwszValue - ((PBYTE)p + sizeof(SPFXMLNodeAttr) + cbName);
  57. pNew = (SPFXMLNodeAttr *)MyAlloc(sizeof(SPFXMLNodeAttr) + cbName + cbValue, g_hPFPrivateHeap);
  58. VALIDATEEXPR(hr, (pNew == NULL), E_OUTOFMEMORY);
  59. if (FAILED(hr))
  60. return NULL;
  61. CopyMemory(pNew, p, sizeof(SPFXMLNodeAttr) + cbName + cbValue);
  62. pNew->pwszName = (LPWSTR)((PBYTE)pNew + sizeof(SPFXMLNodeAttr));
  63. pNew->pwszValue = (LPWSTR)((PBYTE)pNew->pwszName + cbName);
  64. return pNew;
  65. }
  66. /////////////////////////////////////////////////////////////////////////////
  67. // CPFXMLNodeFactory def (defined here cuz it's only used in this file)
  68. // ***************************************************************************
  69. class CPFXMLNodeFactory :
  70. public IXMLNodeFactory,
  71. public CPFPrivHeapGenericClassBase
  72. {
  73. private:
  74. CPFXMLNode *m_pcfxmlRoot;
  75. CPFXMLNode *m_pcfxmlCurrent;
  76. DWORD m_cRef;
  77. public:
  78. CPFXMLNodeFactory(void);
  79. ~CPFXMLNodeFactory(void);
  80. HRESULT Init(CPFXMLNode **pppfxmlRoot);
  81. public:
  82. static CPFXMLNodeFactory *CreateInstance(void) { return new CPFXMLNodeFactory; }
  83. // IUnknown Interface
  84. STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppv)
  85. {
  86. if (ppv == NULL)
  87. return E_INVALIDARG;
  88. *ppv = NULL;
  89. if (riid == IID_IUnknown)
  90. *ppv = (IUnknown *)this;
  91. else if (riid == IID_IXMLNodeFactory)
  92. *ppv = (IStream *)this;
  93. else if (riid == IID_ISequentialStream)
  94. *ppv = (ISequentialStream *)this;
  95. else
  96. return E_NOINTERFACE;
  97. this->AddRef();
  98. return NOERROR;
  99. }
  100. STDMETHOD_(ULONG, AddRef)()
  101. {
  102. return InterlockedIncrement((LONG *)&m_cRef);
  103. }
  104. STDMETHOD_(ULONG, Release)()
  105. {
  106. if (InterlockedDecrement((LONG *)&m_cRef) == 0)
  107. {
  108. delete this;
  109. return 0;
  110. }
  111. return m_cRef;
  112. }
  113. // IXMLNodeFactory
  114. STDMETHOD(NotifyEvent)(IXMLNodeSource* pSource, XML_NODEFACTORY_EVENT iEvt);
  115. STDMETHOD(BeginChildren)(IXMLNodeSource* pSource, XML_NODE_INFO* pNodeInfo);
  116. STDMETHOD(EndChildren)(IXMLNodeSource* pSource, BOOL fEmpty,
  117. XML_NODE_INFO* pNodeInfo);
  118. STDMETHOD(Error)(IXMLNodeSource* pSource, HRESULT hrErrorCode,
  119. USHORT cNumRecs, XML_NODE_INFO** aNodeInfo);
  120. STDMETHOD(CreateNode)(IXMLNodeSource* pSource, PVOID pNodeParent,
  121. USHORT cNumRecs, XML_NODE_INFO** aNodeInfo);
  122. };
  123. /////////////////////////////////////////////////////////////////////////////
  124. // CPFXMLNodeFactory construction
  125. // ***************************************************************************
  126. CPFXMLNodeFactory::CPFXMLNodeFactory()
  127. {
  128. m_pcfxmlRoot = NULL;
  129. m_pcfxmlCurrent = NULL;
  130. m_cRef = 0;
  131. }
  132. // ***************************************************************************
  133. CPFXMLNodeFactory::~CPFXMLNodeFactory()
  134. {
  135. if (m_pcfxmlRoot != NULL)
  136. m_pcfxmlRoot->Release();
  137. if (m_pcfxmlCurrent != NULL)
  138. m_pcfxmlCurrent->Release();
  139. }
  140. /////////////////////////////////////////////////////////////////////////////
  141. // CPFXMLNodeFactory exposed methods
  142. // ***************************************************************************
  143. HRESULT CPFXMLNodeFactory::Init(CPFXMLNode **pppfxmlRoot)
  144. {
  145. USE_TRACING("CPFXMLNodeFactory::Init");
  146. HRESULT hr = NOERROR;
  147. if (pppfxmlRoot != NULL)
  148. *pppfxmlRoot = NULL;
  149. m_pcfxmlRoot = CPFXMLNode::CreateInstance();
  150. VALIDATEEXPR(hr, (m_pcfxmlRoot == NULL), E_OUTOFMEMORY);
  151. if (FAILED(hr))
  152. goto done;
  153. m_pcfxmlRoot->put_NodeType(xmlntUnknown);
  154. if (pppfxmlRoot != NULL)
  155. {
  156. m_pcfxmlRoot->AddRef();
  157. *pppfxmlRoot = m_pcfxmlRoot;
  158. }
  159. done:
  160. return hr;
  161. }
  162. /////////////////////////////////////////////////////////////////////////////
  163. // CPFXMLNodeFactory IXMLNodeFactory
  164. // ***************************************************************************
  165. STDMETHODIMP CPFXMLNodeFactory::NotifyEvent(IXMLNodeSource* pSource,
  166. XML_NODEFACTORY_EVENT iEvt)
  167. {
  168. USE_TRACING("CPFXMLNodeFactory::NotifyEvent");
  169. IXMLParser *pxp = NULL;
  170. HRESULT hr = NOERROR;
  171. switch (iEvt)
  172. {
  173. // at the start of the document, we need to set our node to be
  174. case XMLNF_STARTDOCUMENT:
  175. VALIDATEEXPR(hr, (m_pcfxmlRoot == NULL), E_FAIL);
  176. if (FAILED(hr))
  177. goto done;
  178. hr = pSource->QueryInterface(IID_IXMLParser, (LPVOID *)&pxp);
  179. _ASSERT(SUCCEEDED(hr));
  180. TESTHR(hr, pxp->SetRoot(m_pcfxmlRoot));
  181. pxp->Release();
  182. if (FAILED(hr))
  183. goto done;
  184. break;
  185. default:
  186. break;
  187. }
  188. done:
  189. return hr;
  190. }
  191. // ***************************************************************************
  192. STDMETHODIMP CPFXMLNodeFactory::BeginChildren(IXMLNodeSource* pSource,
  193. XML_NODE_INFO* pNodeInfo)
  194. {
  195. USE_TRACING("CPFXMLNodeFactory::BeginChildren");
  196. if (m_pcfxmlCurrent != NULL)
  197. {
  198. m_pcfxmlCurrent->Release();
  199. m_pcfxmlCurrent = NULL;
  200. }
  201. return NOERROR;
  202. }
  203. // ***************************************************************************
  204. STDMETHODIMP CPFXMLNodeFactory::EndChildren(IXMLNodeSource* pSource,
  205. BOOL fEmpty,
  206. XML_NODE_INFO* pNodeInfo)
  207. {
  208. USE_TRACING("CPFXMLNodeFactory::EndChildren");
  209. if (m_pcfxmlCurrent != NULL)
  210. {
  211. m_pcfxmlCurrent->Release();
  212. m_pcfxmlCurrent = NULL;
  213. }
  214. return NOERROR;
  215. }
  216. // ***************************************************************************
  217. STDMETHODIMP CPFXMLNodeFactory::Error(IXMLNodeSource* pSource,
  218. HRESULT hrErrorCode,
  219. USHORT cNumRecs,
  220. XML_NODE_INFO** aNodeInfo)
  221. {
  222. USE_TRACING("CPFXMLNodeFactory::Error");
  223. ErrorTrace(0, "Error occurred while parsing XML: 0x%08x", hrErrorCode);
  224. if (m_pcfxmlCurrent != NULL)
  225. {
  226. m_pcfxmlCurrent->Release();
  227. m_pcfxmlCurrent = NULL;
  228. }
  229. return hrErrorCode;
  230. }
  231. // ***************************************************************************
  232. STDMETHODIMP CPFXMLNodeFactory::CreateNode(IXMLNodeSource* pSource,
  233. PVOID pNodeParent, USHORT cNumRecs,
  234. XML_NODE_INFO** aNodeInfo)
  235. {
  236. USE_TRACING("CPFXMLNodeFactory::CreateNode");
  237. XML_NODE_INFO *pni = aNodeInfo[0];
  238. CPFXMLNode *ppfxmlParent = (CPFXMLNode *)pNodeParent;
  239. CPFXMLNode *ppfxmlNode = NULL;
  240. HRESULT hr = NOERROR;
  241. WCHAR *pwszStart, *pwszEnd;
  242. DWORD cbData;
  243. BOOL fGetAttribs = FALSE;
  244. switch (pni->dwType)
  245. {
  246. case XML_ELEMENT:
  247. if (m_pcfxmlCurrent != NULL)
  248. {
  249. m_pcfxmlCurrent->Release();
  250. m_pcfxmlCurrent = NULL;
  251. }
  252. // Make a new node and add it to the parent node
  253. ppfxmlNode = CPFXMLNode::CreateInstance();
  254. VALIDATEEXPR(hr, (ppfxmlNode == NULL), E_OUTOFMEMORY);
  255. if (FAILED(hr))
  256. goto done;
  257. ppfxmlNode->put_NodeType(xmlntElement);
  258. TESTHR(hr, ppfxmlNode->put_Data(pni->pwcText, pni->ulLen));
  259. if (FAILED(hr))
  260. goto done;
  261. TESTHR(hr, ppfxmlParent->append_Child(ppfxmlNode));
  262. if (FAILED(hr))
  263. goto done;
  264. // set the current node so that future calls will have the correct
  265. // parent node
  266. pni->pNode = (LPVOID)ppfxmlNode;
  267. // collect attributes, if any
  268. if (cNumRecs > 1)
  269. {
  270. CComBSTR bstrVal;
  271. DWORD iName = 0;
  272. for (int i = 1; i < cNumRecs; i++)
  273. {
  274. XML_NODE_INFO* pniAtt = aNodeInfo[i];
  275. switch (pniAtt->dwType)
  276. {
  277. // the name of the attribute
  278. case XML_ATTRIBUTE:
  279. if (iName > 0)
  280. {
  281. TESTHR(hr,
  282. ppfxmlNode->add_Attribute(aNodeInfo[iName]->pwcText,
  283. bstrVal.m_str,
  284. aNodeInfo[iName]->ulLen,
  285. bstrVal.Length()));
  286. if (FAILED(hr))
  287. goto done;
  288. iName = 0;
  289. bstrVal.Empty();
  290. }
  291. iName = i;
  292. break;
  293. // the value of the attribute- threre may be more
  294. // than one of these if there is any encoding done
  295. // within the value- IXMLParser behavior
  296. case XML_PCDATA:
  297. case XML_CDATA:
  298. TESTHR(hr, bstrVal.Append(pniAtt->pwcText,
  299. pniAtt->ulLen));
  300. if (FAILED(hr))
  301. goto done;
  302. break;
  303. default:
  304. break;
  305. }
  306. }
  307. // do we have an attribute that hasn't been added yet?
  308. if (iName > 0)
  309. {
  310. TESTHR(hr, ppfxmlNode->add_Attribute(aNodeInfo[iName]->pwcText,
  311. bstrVal.m_str,
  312. aNodeInfo[iName]->ulLen,
  313. bstrVal.Length()));
  314. if (FAILED(hr))
  315. goto done;
  316. }
  317. }
  318. break;
  319. case XML_PCDATA:
  320. case XML_CDATA:
  321. // do we need to start up a new node?
  322. if (m_pcfxmlCurrent == NULL)
  323. {
  324. ppfxmlNode = CPFXMLNode::CreateInstance();
  325. VALIDATEEXPR(hr, (ppfxmlNode == NULL), E_OUTOFMEMORY);
  326. if (FAILED(hr))
  327. goto done;
  328. ppfxmlNode->put_NodeType(xmlntText);
  329. TESTHR(hr, ppfxmlParent->append_Child(ppfxmlNode));
  330. if (FAILED(hr))
  331. goto done;
  332. pni->pNode = (LPVOID)ppfxmlNode;
  333. m_pcfxmlCurrent = ppfxmlNode;
  334. ppfxmlNode = NULL;
  335. }
  336. _ASSERT(m_pcfxmlCurrent != NULL);
  337. // skip all leading whitespace
  338. pwszStart = (LPWSTR)pni->pwcText;
  339. cbData = pni->ulLen;
  340. while(iswspace(*pwszStart) && cbData > 0)
  341. {
  342. pwszStart++;
  343. cbData--;
  344. }
  345. // skip all trailing whitespace
  346. pwszEnd = ((LPWSTR)pni->pwcText) + pni->ulLen - 1;
  347. while(iswspace(*pwszEnd) && cbData > 0)
  348. {
  349. pwszEnd--;
  350. cbData--;
  351. }
  352. if (cbData > 0)
  353. {
  354. TESTHR(hr, m_pcfxmlCurrent->append_Data(pwszStart, cbData));
  355. if (FAILED(hr))
  356. goto done;
  357. }
  358. break;
  359. default:
  360. if (m_pcfxmlCurrent != NULL)
  361. {
  362. m_pcfxmlCurrent->Release();
  363. m_pcfxmlCurrent = NULL;
  364. }
  365. break;
  366. }
  367. done:
  368. if (ppfxmlNode != NULL)
  369. ppfxmlNode->Release();
  370. return hr;
  371. }
  372. /////////////////////////////////////////////////////////////////////////////
  373. //*************************************************************************//
  374. /////////////////////////////////////////////////////////////////////////////
  375. /////////////////////////////////////////////////////////////////////////////
  376. // CPFXMLDocument construction
  377. // ***************************************************************************
  378. CPFXMLDocument::CPFXMLDocument(void)
  379. {
  380. m_ppfxmlRoot = NULL;
  381. }
  382. // ***************************************************************************
  383. CPFXMLDocument::~CPFXMLDocument(void)
  384. {
  385. if (m_ppfxmlRoot != NULL)
  386. m_ppfxmlRoot->Release();
  387. }
  388. /////////////////////////////////////////////////////////////////////////////
  389. // CPFXMLDocument methods
  390. // ***************************************************************************
  391. HRESULT CPFXMLDocument::get_RootNode(CPFXMLNode **pppfxmlRoot)
  392. {
  393. USE_TRACING("CPFXMLDocument::get_RootNode");
  394. HRESULT hr = NOERROR;
  395. VALIDATEPARM(hr, (pppfxmlRoot == NULL));
  396. if (FAILED(hr))
  397. goto done;
  398. *pppfxmlRoot = NULL;
  399. VALIDATEEXPR(hr, (m_ppfxmlRoot == NULL), E_FAIL);
  400. if (FAILED(hr))
  401. goto done;
  402. m_ppfxmlRoot->AddRef();
  403. *pppfxmlRoot = m_ppfxmlRoot;
  404. done:
  405. return hr;
  406. }
  407. // ***************************************************************************
  408. HRESULT CPFXMLDocument::put_RootNode(CPFXMLNode *ppfxmlRoot)
  409. {
  410. USE_TRACING("CPFXMLDocument::set_RootNode");
  411. HRESULT hr = NOERROR;
  412. VALIDATEPARM(hr, (ppfxmlRoot == NULL));
  413. if (FAILED(hr))
  414. goto done;
  415. if (m_ppfxmlRoot != NULL)
  416. m_ppfxmlRoot->Release();
  417. ppfxmlRoot->AddRef();
  418. m_ppfxmlRoot = ppfxmlRoot;
  419. done:
  420. return hr;
  421. }
  422. // ***************************************************************************
  423. HRESULT CPFXMLDocument::ParseStream(IStream *pStm, DWORD cbStm)
  424. {
  425. USE_TRACING("CPFXMLDocument::ParseFile");
  426. CPFXMLNodeFactory *ppfnf = NULL;
  427. IXMLNodeFactory *pnf = NULL;
  428. IXMLParser *pxp = NULL;
  429. CPFXMLNode *ppfxmlRoot = NULL, *ppfxmlNode = NULL;
  430. HRESULT hr = NOERROR;
  431. WCHAR wch = 0xfeff;
  432. DWORD cbRead, cbToRead;
  433. BYTE pbBuffer[4096];
  434. VALIDATEPARM(hr, (pStm == NULL || cbStm == 0));
  435. if (FAILED(hr))
  436. goto done;
  437. ppfnf = CPFXMLNodeFactory::CreateInstance();
  438. VALIDATEEXPR(hr, (ppfnf == NULL), E_OUTOFMEMORY);
  439. if (FAILED(hr))
  440. goto done;
  441. ppfnf->AddRef();
  442. TESTHR(hr, ppfnf->Init(&ppfxmlNode));
  443. if (FAILED(hr))
  444. goto done;
  445. TESTHR(hr, ppfnf->QueryInterface(IID_IXMLNodeFactory, (LPVOID *)&pnf));
  446. if (FAILED(hr))
  447. goto done;
  448. TESTHR(hr, CoCreateInstance(CLSID_XMLParser, NULL, CLSCTX_INPROC_SERVER,
  449. IID_IXMLParser, (void**)&pxp));
  450. if (FAILED(hr))
  451. goto done;
  452. TESTHR(hr, pxp->SetFlags(XMLFLAG_CASEINSENSITIVE | XMLFLAG_NOWHITESPACE));
  453. if (FAILED(hr))
  454. goto done;
  455. TESTHR(hr, pxp->SetFactory(pnf));
  456. if (FAILED(hr))
  457. goto done;
  458. for(;;)
  459. {
  460. cbToRead = MyMin(cbStm, sizeof(pbBuffer));
  461. TESTHR(hr, pStm->Read(pbBuffer, cbToRead, &cbRead));
  462. if (FAILED(hr))
  463. goto done;
  464. // now push the actual XML in...
  465. TESTHR(hr, pxp->PushData((const char *)pbBuffer, cbRead,
  466. (cbToRead != sizeof(pbBuffer))));
  467. if (FAILED(hr))
  468. goto done;
  469. hr = pxp->Run(-1);
  470. if (hr != E_PENDING)
  471. {
  472. if (FAILED(hr))
  473. ErrorTrace(1, "pxp->Run(-1) failed. Err 0x%08x", hr);
  474. break;
  475. }
  476. }
  477. if (FAILED(hr))
  478. goto done;
  479. // woohoo! we're done, so lets save everything off (remembering to free
  480. // up anything we previously had, of course). Since the root node we
  481. // passed in earlier isn't actually a real node in the tree. It's just
  482. // a placeholder used to hold onto the real root. So we need to fetch the
  483. // real root out here...
  484. if (m_ppfxmlRoot != NULL)
  485. m_ppfxmlRoot->Release();
  486. // there should only be one child node of this if the XML was well formed.
  487. TESTHR(hr, ppfxmlNode->get_Child(0, &ppfxmlRoot));
  488. if (FAILED(hr))
  489. goto done;
  490. // don't need to addref cuz get_Child does that before handing it to us...
  491. m_ppfxmlRoot = ppfxmlRoot;
  492. ppfxmlRoot = NULL;
  493. done:
  494. if (ppfxmlRoot != NULL)
  495. ppfxmlRoot->Release();
  496. if (ppfxmlNode != NULL)
  497. ppfxmlNode->Release();
  498. if (pxp != NULL)
  499. pxp->Release();
  500. if (pnf != NULL)
  501. pnf->Release();
  502. if (ppfnf != NULL)
  503. ppfnf->Release();
  504. return hr;
  505. }
  506. // ***************************************************************************
  507. HRESULT CPFXMLDocument::ParseFile(LPWSTR wszFile)
  508. {
  509. USE_TRACING("CPFXMLDocument::ParseFile");
  510. HRESULT hr = NOERROR;
  511. LPVOID pvFile = NULL;
  512. DWORD cbFile;
  513. VALIDATEPARM(hr, (wszFile == NULL));
  514. if (FAILED(hr))
  515. goto done;
  516. TESTHR(hr, OpenFileMapped(wszFile, &pvFile, &cbFile));
  517. if (FAILED(hr))
  518. goto done;
  519. TESTHR(hr, this->ParseBlob((PBYTE)pvFile, cbFile));
  520. if (FAILED(hr))
  521. goto done;
  522. done:
  523. if (pvFile != NULL)
  524. UnmapViewOfFile(pvFile);
  525. return hr;
  526. }
  527. // ***************************************************************************
  528. HRESULT CPFXMLDocument::ParseBlob(BYTE *pbBlob, DWORD cbBlob)
  529. {
  530. USE_TRACING("CPFXMLDocument::ParseFile");
  531. CPFXMLNodeFactory *ppfnf = NULL;
  532. IXMLNodeFactory *pnf = NULL;
  533. IXMLParser *pxp = NULL;
  534. CPFXMLNode *ppfxmlRoot = NULL, *ppfxmlNode = NULL;
  535. HRESULT hr = NOERROR;
  536. WCHAR wch = 0xfeff;
  537. VALIDATEPARM(hr, (pbBlob == NULL || cbBlob == 0));
  538. if (FAILED(hr))
  539. goto done;
  540. ppfnf = CPFXMLNodeFactory::CreateInstance();
  541. VALIDATEEXPR(hr, (ppfnf == NULL), E_OUTOFMEMORY);
  542. if (FAILED(hr))
  543. goto done;
  544. ppfnf->AddRef();
  545. TESTHR(hr, ppfnf->Init(&ppfxmlNode));
  546. if (FAILED(hr))
  547. goto done;
  548. TESTHR(hr, ppfnf->QueryInterface(IID_IXMLNodeFactory, (LPVOID *)&pnf));
  549. if (FAILED(hr))
  550. goto done;
  551. TESTHR(hr, CoCreateInstance(CLSID_XMLParser, NULL, CLSCTX_INPROC_SERVER,
  552. IID_IXMLParser, (void**)&pxp));
  553. if (FAILED(hr))
  554. goto done;
  555. TESTHR(hr, pxp->SetFlags(XMLFLAG_CASEINSENSITIVE | XMLFLAG_NOWHITESPACE));
  556. if (FAILED(hr))
  557. goto done;
  558. TESTHR(hr, pxp->SetFactory(pnf));
  559. if (FAILED(hr))
  560. goto done;
  561. // now push the actual XML in...
  562. TESTHR(hr, pxp->PushData((const char *)pbBlob, cbBlob, TRUE));
  563. if (FAILED(hr))
  564. goto done;
  565. TESTHR(hr, pxp->Run(-1));
  566. if (FAILED(hr))
  567. goto done;
  568. // woohoo! we're done, so lets save everything off (remembering to free
  569. // up anything we previously had, of course). Since the root node we
  570. // passed in earlier isn't actually a real node in the tree (It's just
  571. // a placeholder used to hold onto the real root), we need to fetch the
  572. // real root out here...
  573. if (m_ppfxmlRoot != NULL)
  574. m_ppfxmlRoot->Release();
  575. // there should only be one child node of this if the XML was well formed.
  576. // And the parser would've errored if it was not well formed...
  577. TESTHR(hr, ppfxmlNode->get_Child(0, &ppfxmlRoot));
  578. if (FAILED(hr))
  579. goto done;
  580. // don't need to addref cuz get_Child does that before handing it to us...
  581. m_ppfxmlRoot = ppfxmlRoot;
  582. ppfxmlRoot = NULL;
  583. done:
  584. if (ppfxmlRoot != NULL)
  585. ppfxmlRoot->Release();
  586. if (ppfxmlNode != NULL)
  587. ppfxmlNode->Release();
  588. if (pxp != NULL)
  589. pxp->Release();
  590. if (pnf != NULL)
  591. pnf->Release();
  592. if (ppfnf != NULL)
  593. ppfnf->Release();
  594. return hr;
  595. }
  596. // ***************************************************************************
  597. HRESULT CPFXMLDocument::WriteFile(LPWSTR wszFile)
  598. {
  599. USE_TRACING("CPFXMLDocument::WriteFile");
  600. HRESULT hr = NOERROR;
  601. HANDLE hFile = INVALID_HANDLE_VALUE;
  602. DWORD cbWritten;
  603. WCHAR wch = 0xfeff;
  604. VALIDATEPARM(hr, (wszFile == NULL));
  605. if (FAILED(hr))
  606. goto done;
  607. VALIDATEEXPR(hr, (m_ppfxmlRoot == NULL), E_FAIL);
  608. if (FAILED(hr))
  609. goto done;
  610. // create a file mapping that we can hand to the parser
  611. hFile = CreateFileW(wszFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  612. TESTBOOL(hr, (hFile != INVALID_HANDLE_VALUE));
  613. if (FAILED(hr))
  614. goto done;
  615. // write out the stuff that marks it as a unicode file
  616. TESTBOOL(hr, ::WriteFile(hFile, &wch, sizeof(wch), &cbWritten, NULL));
  617. if (FAILED(hr))
  618. goto done;
  619. // write out the XML header
  620. TESTBOOL(hr, ::WriteFile(hFile, g_wszEncoding,
  621. sizeof(g_wszEncoding) - sizeof(WCHAR), &cbWritten,
  622. NULL));
  623. if (FAILED(hr))
  624. goto done;
  625. TESTHR(hr, m_ppfxmlRoot->Write(hFile));
  626. if (FAILED(hr))
  627. goto done;
  628. done:
  629. if (hFile != INVALID_HANDLE_VALUE)
  630. CloseHandle(hFile);
  631. return hr;
  632. }
  633. /////////////////////////////////////////////////////////////////////////////
  634. //*************************************************************************//
  635. /////////////////////////////////////////////////////////////////////////////
  636. // ***************************************************************************
  637. HRESULT WriteEncoded(HANDLE hFile, LPWSTR wszData)
  638. {
  639. USE_TRACING("WriteEncoded");
  640. HRESULT hr = NOERROR;
  641. // if the order / size of this array is changed, be sure to change the
  642. // lengths & indexes below in the switch statement
  643. WCHAR *rgwsz[5] = { L"&amp;", L"&lt;", L"&gt;", L"&apos;", L"&quot;" };
  644. WCHAR *pwszStart, *pwsz;
  645. DWORD cchToWrite, cbWritten;
  646. VALIDATEPARM(hr, (hFile == NULL || hFile == INVALID_HANDLE_VALUE));
  647. if (FAILED(hr))
  648. goto done;
  649. if (wszData == NULL)
  650. {
  651. hr = NOERROR;
  652. goto done;
  653. }
  654. pwszStart = pwsz = wszData;
  655. cchToWrite = 0;
  656. for(;;)
  657. {
  658. // loop thru until we find a 'bad' character
  659. while(*pwsz != L'&' && *pwsz != L'<' && *pwsz != L'>' &&
  660. *pwsz != L'\'' && *pwsz != L'\"' && *pwsz != L'\0')
  661. {
  662. cchToWrite++;
  663. pwsz++;
  664. }
  665. // write out the stuff we've accumulated so far
  666. if (cchToWrite > 0)
  667. {
  668. TESTBOOL(hr, WriteFile(hFile, pwszStart,
  669. cchToWrite * sizeof(WCHAR), &cbWritten,
  670. NULL));
  671. if (FAILED(hr))
  672. goto done;
  673. }
  674. // determine the escape sequence we want to write out- if we hit the
  675. // NULL terminator, we're done.
  676. // Note: the string lengths assigned here are hand calculated from
  677. // rgwsz (defined above).
  678. switch(*pwsz)
  679. {
  680. case L'&': pwszStart = rgwsz[0]; cchToWrite = 5; break;
  681. case L'<': pwszStart = rgwsz[1]; cchToWrite = 4; break;
  682. case L'>': pwszStart = rgwsz[2]; cchToWrite = 4; break;
  683. case L'\'': pwszStart = rgwsz[3]; cchToWrite = 6; break;
  684. case L'\"': pwszStart = rgwsz[4]; cchToWrite = 6; break;
  685. default:
  686. case L'\0':
  687. goto done;
  688. }
  689. // write the escape sequence
  690. TESTBOOL(hr, WriteFile(hFile, pwszStart,
  691. cchToWrite * sizeof(WCHAR), &cbWritten,
  692. NULL));
  693. if (FAILED(hr))
  694. goto done;
  695. // set the pointer to one after the 'bad' character
  696. pwsz++;
  697. pwszStart = pwsz;
  698. cchToWrite = 0;
  699. }
  700. done:
  701. return hr;
  702. }
  703. /////////////////////////////////////////////////////////////////////////////
  704. // CPFXMLNode construction
  705. // ***************************************************************************
  706. CPFXMLNode::CPFXMLNode(DWORD cRef)
  707. {
  708. m_cRef = cRef;
  709. m_xmlnt = xmlntUnknown;
  710. }
  711. // ***************************************************************************
  712. CPFXMLNode::~CPFXMLNode(void)
  713. {
  714. this->Cleanup();
  715. }
  716. /////////////////////////////////////////////////////////////////////////////
  717. // CPFXMLNode internal methods
  718. // ***************************************************************************
  719. void CPFXMLNode::Cleanup(void)
  720. {
  721. USE_TRACING("CPFXMLNode::Cleanup");
  722. m_xmlnt = xmlntUnknown;
  723. m_rgAttr.RemoveAll();
  724. m_rgChildren.RemoveAll();
  725. m_bstrTagData.Empty();
  726. }
  727. // ***************************************************************************
  728. HRESULT CPFXMLNode::Write(HANDLE hFile)
  729. {
  730. USE_TRACING("CPFXMLNode::Write");
  731. HRESULT hr = NOERROR;
  732. DWORD cbWritten, cbToWrite;
  733. BOOL fWriteCR = TRUE;
  734. VALIDATEPARM(hr, (hFile == INVALID_HANDLE_VALUE || hFile == NULL));
  735. if (FAILED(hr))
  736. goto done;
  737. VALIDATEEXPR(hr, (m_bstrTagData.m_str == NULL), E_FAIL);
  738. if (FAILED(hr))
  739. goto done;
  740. // write out an element node
  741. if (m_xmlnt == xmlntElement)
  742. {
  743. SPFXMLNodeAttr *ppfxmlna;
  744. CPFXMLNode *ppfxmln;
  745. WCHAR wsz[1024];
  746. DWORD i;
  747. // write out the tag name
  748. wcscpy(wsz, L"\r\n<");
  749. wcscat(wsz, m_bstrTagData.m_str);
  750. TESTBOOL(hr, WriteFile(hFile, wsz, wcslen(wsz) * sizeof(WCHAR),
  751. &cbWritten, NULL));
  752. if (FAILED(hr))
  753. goto done;
  754. // write out attributes
  755. for (i = 0; i < m_rgAttr.get_Highest() + 1; i++)
  756. {
  757. // we do NOT want to free the class we get back from this cuz it's
  758. // still held by the array.
  759. TESTHR(hr, m_rgAttr.get_Item(i, (LPVOID *)&ppfxmlna));
  760. if (FAILED(hr))
  761. continue;
  762. if (ppfxmlna == NULL || ppfxmlna->pwszName == NULL ||
  763. ppfxmlna->pwszValue == NULL)
  764. continue;
  765. // Ok, assume everything is going to fit into 1024 characters. If
  766. // it doesn't, skip the attribute
  767. cbToWrite = (wcslen(ppfxmlna->pwszName) + 3) * sizeof(WCHAR);
  768. if (cbToWrite >= 1024)
  769. continue;
  770. // yeah, this isn't the most efficient way of doing this, but it's
  771. // supposedly better than sprintf according to the 'faster' alias
  772. wcscpy(wsz, L" ");
  773. wcscat(wsz, ppfxmlna->pwszName);
  774. wcscat(wsz, L"=\"");
  775. TESTBOOL(hr, WriteFile(hFile, wsz, cbToWrite, &cbWritten, NULL));
  776. if (FAILED(hr))
  777. goto done;
  778. TESTHR(hr, WriteEncoded(hFile, ppfxmlna->pwszValue));
  779. if (FAILED(hr))
  780. goto done;
  781. wsz[0] = L'\"';
  782. TESTBOOL(hr, WriteFile(hFile, wsz, sizeof(WCHAR), &cbWritten, NULL));
  783. if (FAILED(hr))
  784. goto done;
  785. }
  786. // close off the tag (and end if there are no children)
  787. if (m_rgChildren.get_Highest() == (DWORD)-1)
  788. wcscpy(wsz, L" />");
  789. else
  790. wcscpy(wsz, L">");
  791. TESTBOOL(hr, WriteFile(hFile, wsz, wcslen(wsz) * sizeof(WCHAR),
  792. &cbWritten, NULL));
  793. if (FAILED(hr))
  794. goto done;
  795. // write out the kids
  796. for (i = 0; i < m_rgChildren.get_Highest() + 1; i++)
  797. {
  798. // we do NOT want to free the class we get back from this cuz
  799. // it's still held by the array.
  800. TESTHR(hr, m_rgChildren.get_Item(i, (LPVOID *)&ppfxmln));
  801. if (FAILED(hr))
  802. goto done;
  803. if (ppfxmln == NULL)
  804. continue;
  805. if (ppfxmln->m_xmlnt == xmlntText &&
  806. m_rgChildren.get_Highest() == 0)
  807. fWriteCR = FALSE;
  808. TESTHR(hr, ppfxmln->Write(hFile));
  809. if (FAILED(hr))
  810. goto done;
  811. }
  812. // if we had kids, then we need to write out the closing tag...
  813. if (m_rgChildren.get_Highest() != (DWORD)-1)
  814. {
  815. if (fWriteCR)
  816. wcscpy(wsz, L"\r\n</");
  817. else
  818. wcscpy(wsz, L"</");
  819. wcscat(wsz, m_bstrTagData.m_str);
  820. wcscat(wsz, L">");
  821. TESTBOOL(hr, WriteFile(hFile, wsz, wcslen(wsz) * sizeof(WCHAR),
  822. &cbWritten, NULL));
  823. if (FAILED(hr))
  824. goto done;
  825. }
  826. }
  827. // write out an text node
  828. else if (m_xmlnt == xmlntText)
  829. {
  830. TESTHR(hr, WriteEncoded(hFile, m_bstrTagData.m_str));
  831. if (FAILED(hr))
  832. goto done;
  833. }
  834. // um, it's neither. Don't fail, but toss out something to the trace log
  835. else
  836. {
  837. _ASSERT(FALSE);
  838. DebugTrace(0, "Encountered a non element & non text node");
  839. hr = NOERROR;
  840. }
  841. done:
  842. return hr;
  843. }
  844. /////////////////////////////////////////////////////////////////////////////
  845. // CPFXMLNode methods
  846. // ***************************************************************************
  847. DWORD CPFXMLNode::AddRef(void)
  848. {
  849. USE_TRACING("CPFXMLNode::AddRef");
  850. m_cRef++;
  851. return m_cRef;
  852. }
  853. // ***************************************************************************
  854. DWORD CPFXMLNode::Release(void)
  855. {
  856. USE_TRACING("CPFXMLNode::Release");
  857. m_cRef--;
  858. if (m_cRef == 0)
  859. {
  860. delete this;
  861. return 0;
  862. }
  863. return m_cRef;
  864. }
  865. // ***************************************************************************
  866. CPFXMLNode *CPFXMLNode::CreateInstance(void)
  867. {
  868. USE_TRACING("CPFXMLNode::CreateInstance");
  869. CPFXMLNode *ppfxml = NULL;
  870. HRESULT hr = NOERROR;
  871. ppfxml = new CPFXMLNode(1);
  872. if (ppfxml == NULL)
  873. goto done;
  874. TESTHR(hr, ppfxml->m_rgAttr.Init(8));
  875. if (FAILED(hr))
  876. goto done;
  877. TESTHR(hr, ppfxml->m_rgChildren.Init(16));
  878. if (FAILED(hr))
  879. goto done;
  880. done:
  881. if (FAILED(hr))
  882. {
  883. delete ppfxml;
  884. ppfxml = NULL;
  885. }
  886. return ppfxml;
  887. }
  888. // ***************************************************************************
  889. HRESULT CPFXMLNode::get_Data(BSTR *pbstrData)
  890. {
  891. USE_TRACING("CPFXMLNode::get_Data");
  892. HRESULT hr = NOERROR;
  893. VALIDATEPARM(hr, (pbstrData == NULL));
  894. if (FAILED(hr))
  895. goto done;
  896. *pbstrData = m_bstrTagData.Copy();
  897. VALIDATEEXPR(hr, (*pbstrData == NULL && m_bstrTagData.m_str != NULL),
  898. E_OUTOFMEMORY);
  899. if (FAILED(hr))
  900. goto done;
  901. done:
  902. return hr;
  903. }
  904. // ***************************************************************************
  905. HRESULT CPFXMLNode::put_Data(LPCWSTR wszData, DWORD cch)
  906. {
  907. USE_TRACING("CPFXMLNode::put_Data");
  908. HRESULT hr = NOERROR;
  909. // does he want us to delete the current contents?
  910. if (wszData == NULL || cch == 0)
  911. {
  912. m_bstrTagData.Empty();
  913. }
  914. // do we have a null terminated string passed to us?
  915. else if (cch == (DWORD)-1)
  916. {
  917. m_bstrTagData.Empty();
  918. TESTHR(hr, m_bstrTagData.Append(wszData));
  919. if (FAILED(hr))
  920. goto done;
  921. }
  922. // we just got a blob of WCHARs + length
  923. else
  924. {
  925. m_bstrTagData.Empty();
  926. TESTHR(hr, m_bstrTagData.Append(wszData, cch));
  927. if (FAILED(hr))
  928. goto done;
  929. }
  930. done:
  931. return hr;
  932. }
  933. // ***************************************************************************
  934. HRESULT CPFXMLNode::append_Data(LPCWSTR wszData, DWORD cch)
  935. {
  936. USE_TRACING("CPFXMLNode::append_Data");
  937. HRESULT hr = NOERROR;
  938. VALIDATEPARM(hr, (wszData == NULL || cch == 0));
  939. if (FAILED(hr))
  940. goto done;
  941. // do we have a null terminated string passed to us?
  942. if (cch == (DWORD)-1)
  943. {
  944. TESTHR(hr, m_bstrTagData.Append(wszData));
  945. if (FAILED(hr))
  946. goto done;
  947. }
  948. // we just got a blob of WCHARs + length
  949. else
  950. {
  951. TESTHR(hr, m_bstrTagData.Append(wszData, cch));
  952. if (FAILED(hr))
  953. goto done;
  954. }
  955. done:
  956. return hr;
  957. }
  958. // ***************************************************************************
  959. HRESULT CPFXMLNode::add_Attribute(LPCWSTR wszName, LPCWSTR wszVal,
  960. DWORD cchName, DWORD cchVal)
  961. {
  962. USE_TRACING("CPFXMLNode::add_Attribute");
  963. SPFXMLNodeAttr *ppfxmlna = NULL;
  964. HRESULT hr = NOERROR;
  965. DWORD cbNode;
  966. WCHAR *pwszName;
  967. VALIDATEPARM(hr, (wszName == NULL || cchName == 0));
  968. if (FAILED(hr))
  969. goto done;
  970. if (cchName == (DWORD)-1)
  971. cchName = wcslen(wszName);
  972. if (wszVal == NULL)
  973. cchVal = 0;
  974. else if (cchVal == (DWORD)-1)
  975. cchVal = wcslen(wszVal);
  976. // be perf concious & allocate the whole shebang in one blob
  977. cbNode = sizeof(SPFXMLNodeAttr) + (cchVal + cchName + 3) * sizeof(WCHAR);
  978. // alloc a new class to hold the attr
  979. ppfxmlna = (SPFXMLNodeAttr *)MyAlloc(cbNode, g_hPFPrivateHeap);
  980. VALIDATEEXPR(hr, (ppfxmlna == NULL), E_OUTOFMEMORY);
  981. if (FAILED(hr))
  982. goto done;
  983. // set up the node pointers
  984. ppfxmlna->pwszName = (LPWSTR)((PBYTE)ppfxmlna + sizeof(SPFXMLNodeAttr));
  985. ppfxmlna->pwszValue = (LPWSTR)((PBYTE)ppfxmlna->pwszName + (cchName + 1) * sizeof(WCHAR));
  986. wcsncpy(ppfxmlna->pwszName, wszName, cchName);
  987. ppfxmlna->pwszName[cchName] = L'\0';
  988. if (wszVal != NULL)
  989. wcsncpy(ppfxmlna->pwszValue, wszVal, cchVal);
  990. ppfxmlna->pwszValue[cchVal] = L'\0';
  991. // add it
  992. TESTHR(hr, m_rgAttr.Append(ppfxmlna));
  993. if (FAILED(hr))
  994. goto done;
  995. // since we save the class in Append, we definitely don't want to free
  996. // it later on...
  997. ppfxmlna = NULL;
  998. done:
  999. if (ppfxmlna != NULL)
  1000. MyFree(ppfxmlna, g_hPFPrivateHeap);
  1001. return hr;
  1002. }
  1003. // ***************************************************************************
  1004. HRESULT CPFXMLNode::get_Attribute(LPCWSTR wszName, BSTR *pbstrVal)
  1005. {
  1006. USE_TRACING("CPFXMLNode::get_Attribute");
  1007. SPFXMLNodeAttr *ppfxmlna;
  1008. HRESULT hr = NOERROR;
  1009. DWORD i;
  1010. VALIDATEPARM(hr, (wszName == NULL || pbstrVal == 0));
  1011. if (FAILED(hr))
  1012. goto done;
  1013. *pbstrVal = NULL;
  1014. // assume we won't find it...
  1015. hr = S_FALSE;
  1016. // but go ahead and try to find it anyway... :-)
  1017. for (i = 0; i < m_rgAttr.get_Highest() + 1; i++)
  1018. {
  1019. // we do NOT want to free the class we get back from this cuz it's
  1020. // still held by the array.
  1021. TESTHR(hr, m_rgAttr.get_Item(i, (LPVOID *)&ppfxmlna));
  1022. if (FAILED(hr))
  1023. goto done;
  1024. if (ppfxmlna != NULL &&
  1025. _wcsicmp(ppfxmlna->pwszName, wszName) == 0)
  1026. {
  1027. *pbstrVal = SysAllocString(ppfxmlna->pwszValue);
  1028. // this will set hr to NOERROR if it succeeds.
  1029. VALIDATEEXPR(hr, (*pbstrVal == NULL), E_OUTOFMEMORY);
  1030. if (FAILED(hr))
  1031. goto done;
  1032. break;
  1033. }
  1034. }
  1035. done:
  1036. return hr;
  1037. }
  1038. // ***************************************************************************
  1039. HRESULT CPFXMLNode::get_Attribute(DWORD iAttr, BSTR *pbstrVal)
  1040. {
  1041. USE_TRACING("CPFXMLNode::get_Attribute");
  1042. SPFXMLNodeAttr *ppfxmlna;
  1043. HRESULT hr = NOERROR;
  1044. DWORD i;
  1045. VALIDATEPARM(hr, (pbstrVal == 0));
  1046. if (FAILED(hr))
  1047. goto done;
  1048. VALIDATEEXPR(hr, (iAttr >= m_rgAttr.get_Highest() + 1),
  1049. Err2HR(RPC_S_INVALID_BOUND));
  1050. if (FAILED(hr))
  1051. goto done;
  1052. *pbstrVal = NULL;
  1053. // we do NOT want to free the class we get back from this cuz it's
  1054. // still held by the array.
  1055. TESTHR(hr, m_rgAttr.get_Item(iAttr, (LPVOID *)&ppfxmlna));
  1056. if (FAILED(hr))
  1057. goto done;
  1058. *pbstrVal = SysAllocString(ppfxmlna->pwszValue);
  1059. VALIDATEEXPR(hr, (*pbstrVal == NULL), E_OUTOFMEMORY);
  1060. if (FAILED(hr))
  1061. goto done;
  1062. done:
  1063. return hr;
  1064. }
  1065. // ***************************************************************************
  1066. HRESULT CPFXMLNode::append_Child(CPFXMLNode *ppfxml)
  1067. {
  1068. USE_TRACING("CPFXMLNode::append_Child");
  1069. HRESULT hr = NOERROR;
  1070. VALIDATEPARM(hr, (ppfxml == NULL));
  1071. if (FAILED(hr))
  1072. goto done;
  1073. // gotta addref here cuz the dyn array is dumb and just expects us to pass
  1074. // it a LPVOID
  1075. ppfxml->AddRef();
  1076. TESTHR(hr, m_rgChildren.Append(ppfxml));
  1077. if (FAILED(hr))
  1078. {
  1079. // if it failed, the map isn't holding it, so release the addref we
  1080. // just did
  1081. ppfxml->Release();
  1082. goto done;
  1083. }
  1084. done:
  1085. return hr;
  1086. }
  1087. // ***************************************************************************
  1088. HRESULT CPFXMLNode::append_Children(CPFArrayUnknown &rgNodes)
  1089. {
  1090. USE_TRACING("CPFXMLNode::append_Child");
  1091. CPFXMLNode *ppfxml = NULL;
  1092. HRESULT hr = NOERROR;
  1093. DWORD i;
  1094. for (i = 0; i < rgNodes.get_Highest() + 1; i++)
  1095. {
  1096. TESTHR(hr, rgNodes.get_Item(i, (LPVOID *)&ppfxml));
  1097. if (FAILED(hr))
  1098. goto done;
  1099. if (ppfxml == NULL)
  1100. continue;
  1101. // gotta addref here cuz the dyn array is dumb and just expects us to
  1102. // pass it a LPVOID
  1103. ppfxml->AddRef();
  1104. TESTHR(hr, m_rgChildren.Append(ppfxml));
  1105. if (FAILED(hr))
  1106. {
  1107. // if it failed, the map isn't holding it, so release the addref we
  1108. // just did
  1109. ppfxml->Release();
  1110. goto done;
  1111. }
  1112. }
  1113. done:
  1114. return hr;
  1115. }
  1116. // ***************************************************************************
  1117. HRESULT CPFXMLNode::get_Child(DWORD iChild, CPFXMLNode **pppfxml)
  1118. {
  1119. USE_TRACING("CPFXMLNode::get_Child");
  1120. CPFXMLNode *ppfxml = NULL;
  1121. HRESULT hr = NOERROR;
  1122. VALIDATEPARM(hr, (pppfxml == NULL));
  1123. if (FAILED(hr))
  1124. goto done;
  1125. *pppfxml = NULL;
  1126. VALIDATEEXPR(hr, (iChild >= m_rgChildren.get_Highest() + 1),
  1127. Err2HR(RPC_S_INVALID_BOUND));
  1128. if (FAILED(hr))
  1129. goto done;
  1130. TESTHR(hr, m_rgChildren.get_Item(iChild, (LPVOID *)&ppfxml));
  1131. if (FAILED(hr))
  1132. goto done;
  1133. if (ppfxml != NULL)
  1134. {
  1135. ppfxml->AddRef();
  1136. *pppfxml = ppfxml;
  1137. ppfxml = NULL;
  1138. }
  1139. else
  1140. {
  1141. hr = S_FALSE;
  1142. }
  1143. done:
  1144. // don't want to call release on ppfxml cuz the array is still holding it
  1145. return hr;
  1146. }
  1147. // ***************************************************************************
  1148. HRESULT CPFXMLNode::get_MatchingChildElements(LPCWSTR wszTag,
  1149. CPFArrayUnknown &rgNodes)
  1150. {
  1151. USE_TRACING("CPFXMLNode::get_ChildList");
  1152. CPFXMLNode *ppfxml = NULL;
  1153. HRESULT hr = NOERROR;
  1154. DWORD i;
  1155. VALIDATEPARM(hr, (wszTag == NULL));
  1156. if (FAILED(hr))
  1157. goto done;
  1158. // reset the dyn array
  1159. rgNodes.RemoveAll();
  1160. for(i = 0; i < m_rgChildren.get_Highest() + 1; i++)
  1161. {
  1162. TESTHR(hr, m_rgChildren.get_Item(i, (LPVOID *)&ppfxml));
  1163. if (FAILED(hr))
  1164. goto done;
  1165. // look for elements with tags equal to what was passed in. Since
  1166. // we're also inside a CPFXMLNode, we can peek directly into the
  1167. // class (cheating is cool)...
  1168. if (ppfxml != NULL && ppfxml->m_xmlnt == xmlntElement &&
  1169. _wcsicmp(wszTag, ppfxml->m_bstrTagData.m_str) == 0)
  1170. {
  1171. // gotta addref here cuz the dyn array is dumb and just expects us
  1172. // to pass it a LPVOID
  1173. ppfxml->AddRef();
  1174. TESTHR(hr, rgNodes.Append(ppfxml));
  1175. if (FAILED(hr))
  1176. {
  1177. // if it failed, the map isn't holding it, so release the
  1178. // addref we just did
  1179. ppfxml->Release();
  1180. goto done;
  1181. }
  1182. }
  1183. }
  1184. done:
  1185. return hr;
  1186. }
  1187. // ***************************************************************************
  1188. HRESULT CPFXMLNode::get_ChildText(BSTR *pbstrText)
  1189. {
  1190. USE_TRACING("CPFXMLNode::get_ChildText");
  1191. CPFXMLNode *ppfxml;
  1192. CComBSTR bstr;
  1193. HRESULT hr = NOERROR;
  1194. VALIDATEPARM(hr, (pbstrText == NULL));
  1195. if (FAILED(hr))
  1196. goto done;
  1197. *pbstrText = NULL;
  1198. if (m_rgChildren.get_Highest() + 1 == 0)
  1199. goto done;
  1200. // do NOT release this object as the array still has a hold of it
  1201. TESTHR(hr, m_rgChildren.get_Item(0, (LPVOID *)&ppfxml));
  1202. if (FAILED(hr))
  1203. goto done;
  1204. if (ppfxml->m_bstrTagData.m_str == NULL)
  1205. goto done;
  1206. TESTHR(hr, bstr.Append(ppfxml->m_bstrTagData));
  1207. if (FAILED(hr))
  1208. goto done;
  1209. *pbstrText = bstr.Detach();
  1210. done:
  1211. return hr;
  1212. }
  1213. // ***************************************************************************
  1214. HRESULT CPFXMLNode::DeleteAllChildren(void)
  1215. {
  1216. USE_TRACING("CPFXMLNode::DeleteAllChildren");
  1217. return m_rgChildren.RemoveAll();
  1218. }
  1219. // ***************************************************************************
  1220. HRESULT CPFXMLNode::CloneNode(CPFXMLNode **pppfxml, BOOL fWantChildren)
  1221. {
  1222. USE_TRACING("CPFXMLNode::CloneNode");
  1223. CPFXMLNode *ppfxml = NULL;
  1224. HRESULT hr = NOERROR;
  1225. VALIDATEPARM(hr, (pppfxml == NULL));
  1226. if (FAILED(hr))
  1227. goto done;
  1228. *pppfxml = NULL;
  1229. ppfxml = CPFXMLNode::CreateInstance();
  1230. VALIDATEEXPR(hr, (ppfxml == NULL), E_OUTOFMEMORY);
  1231. if (FAILED(hr))
  1232. goto done;
  1233. if (m_bstrTagData.m_str != NULL)
  1234. {
  1235. TESTHR(hr, ppfxml->m_bstrTagData.Append(m_bstrTagData.m_str));
  1236. if (FAILED(hr))
  1237. goto done;
  1238. }
  1239. ppfxml->m_xmlnt = m_xmlnt;
  1240. TESTHR(hr, ppfxml->m_rgAttr.CopyFrom(&m_rgAttr));
  1241. if (FAILED(hr))
  1242. goto done;
  1243. if (fWantChildren)
  1244. {
  1245. TESTHR(hr, ppfxml->m_rgChildren.CopyFrom(&m_rgChildren));
  1246. if (FAILED(hr))
  1247. goto done;
  1248. }
  1249. *pppfxml = ppfxml;
  1250. ppfxml = NULL;
  1251. done:
  1252. if (ppfxml != NULL)
  1253. ppfxml->Release();
  1254. return hr;
  1255. }