xml.bmx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. ' Copyright 2019-2020 Bruce A Henderson
  2. '
  3. ' Licensed under the Apache License, Version 2.0 (the "License");
  4. ' you may not use this file except in compliance with the License.
  5. ' You may obtain a copy of the License at
  6. '
  7. ' http://www.apache.org/licenses/LICENSE-2.0
  8. '
  9. ' Unless required by applicable law or agreed to in writing, software
  10. ' distributed under the License is distributed on an "AS IS" BASIS,
  11. ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. ' See the License for the specific language governing permissions and
  13. ' limitations under the License.
  14. '
  15. SuperStrict
  16. Rem
  17. bbdoc: XML
  18. End Rem
  19. Module Text.XML
  20. ModuleInfo "Version: 1.00"
  21. ModuleInfo "License: Apache 2.0"
  22. ModuleInfo "Copyright: 2019-2020 Bruce A Henderson"
  23. ModuleInfo "History: 1.00"
  24. ModuleInfo "History: Initial Release."
  25. Import "common.bmx"
  26. ' disable wrapping
  27. bmx_mxmlSetWrapMargin(0)
  28. Rem
  29. bbdoc: Sets the callback for handing errors, errors will print if not set.
  30. End Rem
  31. Function XMLSetErrorCallback(callback(message:Byte Ptr))
  32. bmx_mxmlSetErrorCallback(callback)
  33. EndFunction
  34. Rem
  35. bbdoc:
  36. End Rem
  37. Type TxmlBase Abstract
  38. Field nodePtr:Byte Ptr
  39. Rem
  40. bbdoc: Returns the element name.
  41. End Rem
  42. Method getName:String()
  43. Return bmx_mxmlGetElement(nodePtr)
  44. End Method
  45. Rem
  46. bbdoc: Returns a string representation of the element.
  47. End Rem
  48. Method ToString:String() Override
  49. Return bmx_mxmlSaveString(nodePtr, False)
  50. End Method
  51. Rem
  52. bbdoc: Returns a string representation of the element, optionally formatting the output.
  53. End Rem
  54. Method ToStringFormat:String(format:Int = False)
  55. Return bmx_mxmlSaveString(nodePtr, format)
  56. End Method
  57. End Type
  58. Rem
  59. bbdoc: An XML Node.
  60. End Rem
  61. Type TxmlNode Extends TxmlBase
  62. Function _create:TxmlNode(nodePtr:Byte Ptr)
  63. If nodePtr Then
  64. Local this:TxmlNode = New TxmlNode
  65. this.nodePtr = nodePtr
  66. Return this
  67. End If
  68. End Function
  69. Rem
  70. bbdoc: Creates a new node element.
  71. End Rem
  72. Function newNode:TxmlNode(name:String)
  73. Return _create(bmx_mxmlNewElement(Null, name))
  74. End Function
  75. Rem
  76. bbdoc: Gets the parent.
  77. returns: The parent to this object.
  78. End Rem
  79. Method GetParent:TxmlNode()
  80. Return TxmlNode._create(bmx_mxmlGetParent(nodePtr))
  81. End Method
  82. Rem
  83. bbdoc: Creates a new child element.
  84. about: Added at the end of child nodes list.
  85. End Rem
  86. Method addChild:TxmlNode(name:String, content:String = Null)
  87. Local n:TxmlNode = _create(bmx_mxmlNewElement(nodePtr, name))
  88. If content And n Then
  89. n.setContent(content)
  90. End If
  91. Return n
  92. End Method
  93. Rem
  94. Rem
  95. bbdoc: Adds a new node @node as the next sibling.
  96. End Rem
  97. Method addNextSibling(node:TxmlNode)
  98. Local parent:TxmlBase = GetParent()
  99. If parent Then
  100. bmx_mxmlAdd(parent.nodePtr, MXML_ADD_AFTER, nodePtr, node.nodePtr)
  101. End If
  102. End Method
  103. Rem
  104. bbdoc: Adds a new node @node as the previous sibling.
  105. End Rem
  106. Method addPreviousSibling(node:TxmlNode)
  107. Local parent:TxmlBase = GetParent()
  108. If parent Then
  109. bmx_mxmlAdd(parent.nodePtr, MXML_ADD_BEFORE, nodePtr, node.nodePtr)
  110. End If
  111. End Method
  112. Rem
  113. bbdoc: Appends the extra substring to the node content.
  114. End Rem
  115. Method addContent(content:String)
  116. bmx_mxmlAddContent(nodePtr, content)
  117. End Method
  118. Rem
  119. bbdoc: Replaces the content of a node.
  120. End Rem
  121. Method setContent(content:String)
  122. bmx_mxmlSetContent(nodePtr, content)
  123. End Method
  124. Rem
  125. bbdoc: Sets (or resets) the name of the node.
  126. End Rem
  127. Method setName(name:String)
  128. bmx_mxmlSetElement(nodePtr, name)
  129. End Method
  130. Rem
  131. bbdoc: Creates a new attribute.
  132. End Rem
  133. Method addAttribute(name:String, value:String = "")
  134. setAttribute(name, value)
  135. End Method
  136. Rem
  137. bbdoc: Sets (or resets) an attribute carried by the node.
  138. End Rem
  139. Method setAttribute(name:String, value:String = "")
  140. bmx_mxmlElementSetAttr(nodePtr, name, value)
  141. End Method
  142. Rem
  143. bbdoc: Provides the value of the attribute with the specified qualified name.
  144. End Rem
  145. Method getAttribute:String(name:String)
  146. Return bmx_mxmlElementGetAttr(nodePtr, name)
  147. End Method
  148. Rem
  149. bbdoc: Returns the list of node attributes.
  150. returns: The list of attributes.
  151. End Rem
  152. Method getAttributeList:TList()
  153. Local list:TList = New TList
  154. Local count:Int = bmx_mxmlElementGetAttrCount(nodePtr)
  155. If count Then
  156. For Local i:Int = 0 Until count
  157. Local name:String
  158. Local value:String = bmx_mxmlElementGetAttrByIndex(nodePtr, i, name)
  159. list.AddLast(New TxmlAttribute(name, value))
  160. Next
  161. End If
  162. Return list
  163. End Method
  164. Rem
  165. bbdoc: Remove an attribute carried by the node.
  166. End Rem
  167. Method unsetAttribute(name:String)
  168. bmx_mxmlElementDeleteAttr(nodePtr, name)
  169. End Method
  170. Rem
  171. bbdoc: Search an attribute associated to the node
  172. returns: #True if the attribute exists, or #False otherwise.
  173. End Rem
  174. Method hasAttribute:Int(name:String)
  175. Return bmx_mxmlElementHasAttr(nodePtr, name)
  176. End Method
  177. Rem
  178. bbdoc: Returns a list of child nodes.
  179. End Rem
  180. Method getChildren:TList()
  181. Local list:TList = New TList
  182. Local n:Byte Ptr = bmx_mxmlWalkNext(nodePtr, nodePtr, MXML_DESCEND)
  183. While n
  184. If bmx_mxmlGetType(n) = MXML_ELEMENT Then
  185. list.AddLast(TxmlNode._create(n))
  186. End If
  187. n = bmx_mxmlWalkNext(n, nodePtr, MXML_NO_DESCEND)
  188. Wend
  189. Return list
  190. End Method
  191. Rem
  192. bbdoc: Gets the first child.
  193. returns: The first child or Null if none.
  194. End Rem
  195. Method getFirstChild:TxmlBase()
  196. Return TxmlNode._create(bmx_mxmlGetFirstChild(nodePtr))
  197. End Method
  198. Rem
  199. bbdoc: Gets the last child.
  200. returns: The last child or Null if none.
  201. End Rem
  202. Method getLastChild:TxmlBase()
  203. Return TxmlNode._create(bmx_mxmlGetLastChild(nodePtr))
  204. End Method
  205. Rem
  206. bbdoc: Get the next sibling node
  207. returns: The next node or Null if there are none.
  208. End Rem
  209. Method nextSibling:TxmlNode()
  210. Return TxmlNode._create(bmx_mxmlGetNextSibling(nodePtr))
  211. End Method
  212. Rem
  213. bbdoc: Get the previous sibling node
  214. returns: The previous node or Null if there are none.
  215. End Rem
  216. Method previousSibling:TxmlNode()
  217. Return TxmlNode._create(bmx_mxmlGetPrevSibling(nodePtr))
  218. End Method
  219. Rem
  220. bbdoc: Reads the value of a node.
  221. returns: The node content.
  222. End Rem
  223. Method getContent:String()
  224. Local sb:TStringBuilder = New TStringBuilder()
  225. Local n:Byte Ptr = bmx_mxmlWalkNext(nodePtr, nodePtr, MXML_DESCEND)
  226. While n
  227. Select bmx_mxmlGetType(n)
  228. Case MXML_ELEMENT
  229. sb.Append(bmx_mxmlGetCDATA(n))
  230. Case MXML_OPAQUE
  231. sb.Append(bmx_mxmlGetContent(n))
  232. EndSelect
  233. n = bmx_mxmlWalkNext(n, nodePtr, MXML_DESCEND)
  234. Wend
  235. Return sb.ToString()
  236. End Method
  237. Rem
  238. bbdoc: Finds an element of the given @element name, attribute or attribute/value.
  239. returns: A node or Null if no match was found.
  240. End Rem
  241. Method findElement:TxmlNode(element:String = "", attr:String = "", value:String = "", descend:Int=MXML_DESCEND)
  242. Return TxmlNode._create(bmx_mxmlFindElement(nodePtr, element, attr, value, descend))
  243. End Method
  244. Rem
  245. bbdoc: Frees a node and all of its children.
  246. End Rem
  247. Method Free()
  248. If nodePtr Then
  249. bmx_mxmlDelete(nodePtr)
  250. nodePtr = Null
  251. End If
  252. End Method
  253. End Type
  254. Rem
  255. bbdoc: An XML Document.
  256. End Rem
  257. Type TxmlDoc Extends TxmlBase
  258. Function _create:TxmlDoc(nodePtr:Byte Ptr)
  259. If nodePtr Then
  260. Local this:TxmlDoc = New TxmlDoc
  261. this.nodePtr = nodePtr
  262. Return this
  263. End If
  264. End Function
  265. Rem
  266. bbdoc: Creates a new XML document.
  267. End Rem
  268. Function newDoc:TxmlDoc(version:String)
  269. Local this:TxmlDoc = New TxmlDoc
  270. this.nodePtr = bmx_mxmlNewXML(version)
  271. If this.nodePtr Then
  272. Return this
  273. End If
  274. End Function
  275. Rem
  276. bbdoc: Parses an XML document from a String or TStream and builds a tree.
  277. returns: The resulting document tree.
  278. End Rem
  279. Function readDoc:TxmlDoc(doc:Object)
  280. If String(doc) Then
  281. Local txt:String = String(doc)
  282. ' strip utf8 BOM
  283. If txt[..3] = BOM_UTF8 Then
  284. txt = txt[3..]
  285. End If
  286. Return TxmlDoc._create(bmx_mxmlLoadString(txt))
  287. Else If TStream(doc) Then
  288. Return parseFile(doc)
  289. End If
  290. End Function
  291. Rem
  292. bbdoc: Sets the root element of the document.
  293. returns: The old root element if any was found.
  294. End Rem
  295. Method setRootElement:TxmlNode(root:TxmlNode)
  296. Return TxmlNode._create(bmx_mxmlSetRootElement(nodePtr, root.nodePtr))
  297. End Method
  298. Rem
  299. bbdoc: Returns the root element of the document.
  300. End Rem
  301. Method getRootElement:TxmlNode()
  302. Return TxmlNode._create(bmx_mxmlGetRootElement(nodePtr))
  303. End Method
  304. Rem
  305. bbdoc: Dumps an XML document to a file.
  306. returns: True on success, or Fales otherwise.
  307. End Rem
  308. Method saveFile:Int(file:Object, autoClose:Int = True, format:Int = False)
  309. Local filename:String = String(file)
  310. Local created:Int
  311. If filename Then
  312. If filename = "-" Then
  313. Return bmx_mxmlSaveStdout(nodePtr, format)
  314. Else
  315. file = WriteStream(filename)
  316. created = True
  317. End If
  318. End If
  319. If TStream(file) Then
  320. Try
  321. Return bmx_mxmlSaveStream(nodePtr, TStream(file), format) = 0
  322. Finally
  323. If created Or autoClose Then
  324. TStream(file).Close()
  325. End If
  326. End Try
  327. End If
  328. Return False
  329. End Method
  330. Rem
  331. bbdoc: Parses an XML file and build a tree.
  332. returns: The resulting document tree or Null if error.
  333. End Rem
  334. Function parseFile:TxmlDoc(file:Object)
  335. Local filename:String = String(file)
  336. Local opened:Int
  337. If filename Then
  338. file = ReadStream(filename)
  339. opened = True
  340. End If
  341. If TStream(file) Then
  342. Local doc:TxmlDoc
  343. Try
  344. doc = _create(bmx_mxmlLoadStream(TStream(file)))
  345. Finally
  346. If opened Then
  347. TStream(file).close()
  348. End If
  349. End Try
  350. Return doc
  351. End If
  352. Return Null
  353. End Function
  354. Rem
  355. bbdoc: Frees the document.
  356. End Rem
  357. Method Free()
  358. If nodePtr Then
  359. bmx_mxmlDelete(nodePtr)
  360. nodePtr = Null
  361. End If
  362. End Method
  363. End Type
  364. Private
  365. Function _xmlstream_read:Int(stream:TStream, buf:Byte Ptr, count:UInt) { nomangle }
  366. Return stream.Read(buf, count)
  367. End Function
  368. Function _xmlstream_write:Int(stream:TStream, buf:Byte Ptr, count:UInt) { nomangle }
  369. Return stream.Write(buf, count)
  370. End Function
  371. Public
  372. Rem
  373. bbdoc: An xml element attribute name/value pair. (read only)
  374. End Rem
  375. Type TxmlAttribute
  376. Private
  377. Field name:String
  378. Field value:String
  379. Public
  380. Method New(name:String, value:String)
  381. Self.name = name
  382. Self.value = value
  383. End Method
  384. Method getName:String()
  385. Return name
  386. End Method
  387. Method getValue:String()
  388. Return value
  389. End Method
  390. End Type