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.

404 lines
11 KiB

  1. //------------------------------------------------------------------------------
  2. //
  3. // File: xml_supp.cpp
  4. // Copyright (C) 1995-2000 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // Purpose:
  8. // implements helper functions for parsing XML document
  9. //
  10. //------------------------------------------------------------------------------
  11. #include "stdafx.h"
  12. #include "xml_supp.h"
  13. // include "strings.h" to get tag names and attribute names for XML elements in MSC file
  14. #define INIT_MMC_BASE_STRINGS
  15. #include "strings.h"
  16. // note if you want to untie the project from MMC, copy the definitions for
  17. // the following strings from strings.h here:
  18. /*
  19. XML_TAG_MMC_CONSOLE_FILE;
  20. XML_TAG_MMC_STRING_TABLE;
  21. XML_TAG_STRING_TABLE_MAP;
  22. XML_TAG_STRING_TABLE;
  23. XML_TAG_VALUE_GUID;
  24. XML_TAG_STRING_TABLE_STRING;
  25. XML_ATTR_STRING_TABLE_STR_ID;
  26. */
  27. LPCSTR strXMLStringTablePath[] = { XML_TAG_MMC_CONSOLE_FILE,
  28. XML_TAG_MMC_STRING_TABLE,
  29. XML_TAG_STRING_TABLE_MAP };
  30. /***************************************************************************\
  31. *
  32. * METHOD: LocateNextElementNode
  33. *
  34. * PURPOSE: locates sibling node of type ELEMENT
  35. *
  36. * PARAMETERS:
  37. * IXMLDOMNode *pNode [in] - node which sibling to locate
  38. * IXMLDOMNode **ppNode [out] - sibling node
  39. *
  40. * RETURNS:
  41. * HRESULT - result code
  42. *
  43. \***************************************************************************/
  44. static HRESULT LocateNextElementNode(IXMLDOMNode *pNode, IXMLDOMNode **ppNode)
  45. {
  46. // parameter check
  47. if (ppNode == NULL)
  48. return E_INVALIDARG;
  49. // init out parameter
  50. *ppNode = NULL;
  51. // check [in] parameter
  52. if (pNode == NULL)
  53. return E_INVALIDARG;
  54. // loop thru siblings
  55. CComPtr<IXMLDOMNode> spCurrNode = pNode;
  56. CComPtr<IXMLDOMNode> spResultNode;
  57. while (1)
  58. {
  59. // get sibling node
  60. HRESULT hr = spCurrNode->get_nextSibling(&spResultNode);
  61. if (FAILED(hr))
  62. return hr;
  63. // check the pointer
  64. if (spResultNode == NULL)
  65. return E_FAIL; // not found
  66. // done if it's ELEMENT node
  67. DOMNodeType elType = NODE_INVALID;
  68. spResultNode->get_nodeType(&elType);
  69. if (elType == NODE_ELEMENT)
  70. {
  71. *ppNode = spResultNode.Detach();
  72. return S_OK;
  73. }
  74. // get to the next one
  75. spCurrNode = spResultNode;
  76. }
  77. return E_UNEXPECTED;
  78. }
  79. /***************************************************************************\
  80. *
  81. * METHOD: OpenXMLStringTable
  82. *
  83. * PURPOSE: Opens XML document and locates string table node in it
  84. *
  85. * PARAMETERS:
  86. * LPCWSTR lpstrFileName - [in] file to load document from
  87. * IXMLDOMNode **ppStringTableNode - [out] pointer to node containing string table
  88. *
  89. * RETURNS:
  90. * HRESULT - result code
  91. *
  92. \***************************************************************************/
  93. HRESULT OpenXMLStringTable(LPCWSTR lpstrFileName, IXMLDOMNode **ppStringTableNode)
  94. {
  95. // do parameter check
  96. if (lpstrFileName == NULL || ppStringTableNode == NULL)
  97. return E_INVALIDARG;
  98. // init return value
  99. *ppStringTableNode = NULL;
  100. // cocreate xml document
  101. CComQIPtr<IXMLDOMDocument> spDocument;
  102. HRESULT hr = spDocument.CoCreateInstance(CLSID_DOMDocument);
  103. if (FAILED(hr))
  104. return hr;
  105. // prevent re-formating
  106. spDocument->put_preserveWhiteSpace(VARIANT_TRUE);
  107. // load the file
  108. VARIANT_BOOL bOK = VARIANT_FALSE;
  109. hr = spDocument->load(CComVariant(lpstrFileName), &bOK);
  110. if (hr != S_OK || bOK != VARIANT_TRUE)
  111. return FAILED(hr) ? hr : E_FAIL;
  112. // the path represents element tags in similar to the file system manner
  113. // so 'c' from <a><b><c/></b></a> can be selected by "a/b/c"
  114. // construct the path
  115. std::string strPath;
  116. for (int i = 0; i< sizeof(strXMLStringTablePath)/sizeof(strXMLStringTablePath[0]); i++)
  117. strPath.append(i > 0 ? 1 : 0, '/' ).append(strXMLStringTablePath[i]);
  118. // locate required node
  119. hr = spDocument->selectSingleNode(CComBSTR(strPath.c_str()), ppStringTableNode);
  120. if (FAILED(hr))
  121. return hr;
  122. return S_OK;
  123. }
  124. /***************************************************************************\
  125. *
  126. * METHOD: SaveXMLContents
  127. *
  128. * PURPOSE: Saves XML document to file
  129. *
  130. * PARAMETERS:
  131. * LPCWSTR lpstrFileName [in] - file to save to
  132. * IXMLDOMNode *pStringTableNode [in] - pointer to <any> document's element
  133. *
  134. * RETURNS:
  135. * HRESULT - result code
  136. *
  137. \***************************************************************************/
  138. HRESULT SaveXMLContents(LPCWSTR lpstrFileName, IXMLDOMNode *pStringTableNode)
  139. {
  140. // do parameter check
  141. if (lpstrFileName == NULL || pStringTableNode == NULL)
  142. return E_INVALIDARG;
  143. // get the document
  144. CComQIPtr<IXMLDOMDocument> spDocument;
  145. HRESULT hr = pStringTableNode->get_ownerDocument(&spDocument);
  146. if (FAILED(hr))
  147. return hr;
  148. // save the file
  149. hr = spDocument->save(CComVariant(lpstrFileName));
  150. if (FAILED(hr))
  151. return hr;
  152. return S_OK;
  153. }
  154. /***************************************************************************\
  155. *
  156. * METHOD: GetXMLElementContents
  157. *
  158. * PURPOSE: retuns XML text elements' contents as BSTR
  159. *
  160. * PARAMETERS:
  161. * IXMLDOMNode *pNode [in] - node which contents is requested
  162. * CComBSTR& bstrResult [out] - resulting string
  163. *
  164. * RETURNS:
  165. * HRESULT - result code
  166. *
  167. \***************************************************************************/
  168. HRESULT GetXMLElementContents(IXMLDOMNode *pNode, CComBSTR& bstrResult)
  169. {
  170. // init result
  171. bstrResult.Empty();
  172. // parameter check
  173. if (pNode == NULL)
  174. return E_INVALIDARG;
  175. // locate required node
  176. CComQIPtr<IXMLDOMNode> spTextNode;
  177. HRESULT hr = pNode->selectSingleNode(CComBSTR(L"text()"), &spTextNode);
  178. if (FAILED(hr))
  179. return hr;
  180. // recheck the pointer
  181. if (spTextNode == NULL)
  182. return E_POINTER;
  183. // done
  184. return spTextNode->get_text(&bstrResult);
  185. }
  186. /***************************************************************************\
  187. *
  188. * METHOD: ReadXMLStringTables
  189. *
  190. * PURPOSE: Reads string tables to std::map - based structure
  191. *
  192. * PARAMETERS:
  193. * IXMLDOMNode *pNode [in] - string table node
  194. * CStringTableMap& mapResult [out] - map containing string tables
  195. *
  196. * RETURNS:
  197. * HRESULT - result code
  198. *
  199. \***************************************************************************/
  200. HRESULT ReadXMLStringTables(IXMLDOMNode *pNode, CStringTableMap& mapResult)
  201. {
  202. mapResult.clear();
  203. // parameter check
  204. if (pNode == NULL)
  205. return E_INVALIDARG;
  206. // get the node list
  207. CComQIPtr<IXMLDOMNodeList> spGUIDNodes;
  208. HRESULT hr = pNode->selectNodes(CComBSTR(XML_TAG_VALUE_GUID), &spGUIDNodes);
  209. if (FAILED(hr))
  210. return hr;
  211. // recheck the pointer
  212. if (spGUIDNodes == NULL)
  213. return E_POINTER;
  214. // get the item count
  215. long length = 0;
  216. hr = spGUIDNodes->get_length(&length);
  217. if (FAILED(hr))
  218. return hr;
  219. // read the items
  220. for (int n = 0; n < length; n++)
  221. {
  222. // get one node
  223. CComQIPtr<IXMLDOMNode> spGUIDNode;
  224. hr = spGUIDNodes->get_item(n, &spGUIDNode);
  225. if (FAILED(hr))
  226. return hr;
  227. // read the text
  228. CComBSTR bstrLastGUID;
  229. hr = GetXMLElementContents(spGUIDNode, bstrLastGUID);
  230. if (FAILED(hr))
  231. return hr;
  232. // Add the entry to the map;
  233. CStringMap& rMapStrings = mapResult[static_cast<LPOLESTR>(bstrLastGUID)];
  234. //get the strings node following the guid
  235. CComPtr<IXMLDOMNode> spStringsNode;
  236. hr = LocateNextElementNode(spGUIDNode, &spStringsNode);
  237. if (FAILED(hr))
  238. return hr;
  239. // recheck
  240. if (spStringsNode == NULL)
  241. return E_POINTER;
  242. // select strings for this guid
  243. CComQIPtr<IXMLDOMNodeList> spStringNodeList;
  244. HRESULT hr = spStringsNode->selectNodes(CComBSTR(XML_TAG_STRING_TABLE_STRING), &spStringNodeList);
  245. if (FAILED(hr))
  246. return hr;
  247. // recheck the pointer
  248. if (spStringNodeList == NULL)
  249. return E_POINTER;
  250. // count the strings
  251. long nStrCount = 0;
  252. hr = spStringNodeList->get_length(&nStrCount);
  253. if (FAILED(hr))
  254. return hr;
  255. // add all the strings to map
  256. CComQIPtr<IXMLDOMNode> spStringNode;
  257. for(int iStr = 0; iStr < nStrCount; iStr++)
  258. {
  259. // get n-th string
  260. spStringNode.Release();
  261. hr = spStringNodeList->get_item(iStr, &spStringNode);
  262. if (FAILED(hr))
  263. return hr;
  264. CComQIPtr<IXMLDOMElement> spElement = spStringNode;
  265. if (spElement == NULL)
  266. return E_UNEXPECTED;
  267. // get string id
  268. CComVariant val;
  269. hr = spElement->getAttribute(CComBSTR(XML_ATTR_STRING_TABLE_STR_ID), &val);
  270. if (FAILED(hr))
  271. continue;
  272. DWORD dwID = val.bstrVal ? wcstoul(val.bstrVal, NULL, 10) : 0;
  273. // get string text
  274. CComBSTR bstrText;
  275. hr = GetXMLElementContents(spStringNode, bstrText);
  276. if (FAILED(hr))
  277. return hr;
  278. // add to the map
  279. rMapStrings[dwID] = bstrText;
  280. }
  281. }
  282. return S_OK;
  283. }
  284. /***************************************************************************\
  285. *
  286. * METHOD: UpdateXMLString
  287. *
  288. * PURPOSE: Updates string in string table
  289. *
  290. * PARAMETERS:
  291. * IXMLDOMNode *pNode [in] - string tables
  292. * const std::wstring& strGUID [in] - GUID of string table
  293. * DWORD ID [in] - id of string
  294. * const std::wstring& strNewVal [in] - new value for string
  295. *
  296. * RETURNS:
  297. * HRESULT - result code
  298. *
  299. \***************************************************************************/
  300. HRESULT UpdateXMLString(IXMLDOMNode *pNode, const std::wstring& strGUID, DWORD ID, const std::wstring& strNewVal)
  301. {
  302. // parameter check
  303. if (pNode == NULL)
  304. return E_INVALIDARG;
  305. USES_CONVERSION;
  306. // locate the GUID node
  307. std::wstring strTagGUID(T2CW(XML_TAG_VALUE_GUID));
  308. std::wstring strGUIDPattern( strTagGUID + L"[text() = \"" + strGUID + L"\"]" );
  309. CComQIPtr<IXMLDOMNode> spGUIDNode;
  310. HRESULT hr = pNode->selectSingleNode(CComBSTR(strGUIDPattern.c_str()), &spGUIDNode);
  311. if (FAILED(hr))
  312. return hr;
  313. // recheck
  314. if (spGUIDNode == NULL)
  315. return E_POINTER;
  316. //get the strings node following the guid
  317. CComPtr<IXMLDOMNode> spStringsNode;
  318. hr = LocateNextElementNode(spGUIDNode, &spStringsNode);
  319. if (FAILED(hr))
  320. return hr;
  321. // recheck
  322. if (spStringsNode == NULL)
  323. return E_POINTER;
  324. // locate the string node by ID (actually its text node)
  325. CString strPattern;
  326. strPattern.Format("%s[@%s = %d]/text()", XML_TAG_STRING_TABLE_STRING,
  327. XML_ATTR_STRING_TABLE_STR_ID, ID);
  328. CComQIPtr<IXMLDOMNode> spTextNode;
  329. hr = spStringsNode->selectSingleNode(CComBSTR(strPattern), &spTextNode);
  330. if (FAILED(hr))
  331. return hr;
  332. // recheck
  333. if (spTextNode == NULL)
  334. return E_POINTER;
  335. // set the contents
  336. hr = spTextNode->put_text(CComBSTR(strNewVal.c_str()));
  337. if (FAILED(hr))
  338. return hr;
  339. return S_OK; // done
  340. }