NativeXml.hx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package flash;
  23. import flash.xml.XML;
  24. import flash.xml.XMLList;
  25. extern enum XmlType {}
  26. typedef NativeXml = Xml;
  27. class Xml {
  28. public static var Element(default, null):XmlType;
  29. public static var PCData(default, null):XmlType;
  30. public static var CData(default, null):XmlType;
  31. public static var Comment(default, null):XmlType;
  32. public static var DocType(default, null):XmlType;
  33. public static var ProcessingInstruction(default, null):XmlType;
  34. public static var Document(default, null):XmlType;
  35. public var nodeType(default, null):XmlType;
  36. public var nodeName(get, set):String;
  37. public var nodeValue(get, set):String;
  38. public var parent(get, null):Xml;
  39. var _node:flash.xml.XML;
  40. public static function parse(str:String):Xml {
  41. XML.ignoreWhitespace = false;
  42. XML.ignoreProcessingInstructions = false;
  43. XML.ignoreComments = false;
  44. var prefix = "<__document";
  45. var root = null;
  46. while (root == null) {
  47. try {
  48. root = new flash.xml.XML(prefix + ">" + str + "</__document>");
  49. } catch (e:flash.errors.TypeError) {
  50. // if we miss a namespace, let's add it !
  51. var r = ~/"([^"]+)"/; // "
  52. if (e.errorID == 1083 && r.match(e.message)) {
  53. var ns = r.matched(1);
  54. prefix += " xmlns:" + ns + '="@' + ns + '"';
  55. } else
  56. throw e;
  57. }
  58. }
  59. return wrap(root, Xml.Document);
  60. }
  61. @:keep static function compare(a:Xml, b:Xml):Bool {
  62. return a == null ? b == null : (b == null ? false : a._node == b._node);
  63. }
  64. private function new():Void {}
  65. public static function createElement(name:String):Xml {
  66. return wrap(new flash.xml.XML("<" + name + "/>"), Xml.Element);
  67. }
  68. public static function createPCData(data:String):Xml {
  69. XML.ignoreWhitespace = false;
  70. return wrap(new flash.xml.XML(data), Xml.PCData);
  71. }
  72. public static function createCData(data:String):Xml {
  73. return wrap(new flash.xml.XML("<![CDATA[" + data + "]]>"), Xml.CData);
  74. }
  75. public static function createComment(data:String):Xml {
  76. XML.ignoreComments = false;
  77. return wrap(new flash.xml.XML("<!--" + data + "-->"), Xml.Comment);
  78. }
  79. public static function createDocType(data:String):Xml {
  80. return wrap(new flash.xml.XML("<!DOCTYPE " + data + ">"), Xml.DocType);
  81. }
  82. public static function createProcessingInstruction(data:String):Xml {
  83. XML.ignoreProcessingInstructions = false;
  84. return wrap(new flash.xml.XML("<?" + data + "?>"), Xml.ProcessingInstruction);
  85. }
  86. public static function createDocument():Xml {
  87. return wrap(new flash.xml.XML("<__document/>"), Xml.Document);
  88. }
  89. private static function getNodeType(node:flash.xml.XML):XmlType {
  90. switch (node.nodeKind()) {
  91. case "element":
  92. return Xml.Element;
  93. case "text":
  94. return Xml.PCData;
  95. case "processing-instruction":
  96. return Xml.ProcessingInstruction;
  97. case "comment":
  98. return Xml.Comment;
  99. default:
  100. throw "unimplemented node type: " + node.nodeType;
  101. }
  102. }
  103. private function get_nodeName():String {
  104. if (nodeType != Xml.Element)
  105. throw "bad nodeType";
  106. var ns = _node.namespace();
  107. return (ns.prefix == "") ? _node.localName() : ns.prefix + ":" + _node.localName();
  108. }
  109. private function set_nodeName(n:String):String {
  110. if (nodeType != Xml.Element)
  111. throw "bad nodeType";
  112. var ns = n.split(":");
  113. if (ns.length == 1)
  114. _node.setLocalName(n);
  115. else {
  116. _node.setLocalName(ns[1]);
  117. _node.setNamespace(_node.namespace(ns[0]));
  118. }
  119. return n;
  120. }
  121. private function get_nodeValue():String {
  122. var nodeType = nodeType;
  123. if (nodeType == Xml.Element || nodeType == Xml.Document)
  124. throw "bad nodeType";
  125. if (nodeType == Xml.Comment)
  126. return _node.toString().substr(4, -7);
  127. return _node.toString();
  128. }
  129. private function set_nodeValue(v:String):String {
  130. var nodeType = nodeType;
  131. var x = null;
  132. if (nodeType == Xml.Element || nodeType == Xml.Document)
  133. throw "bad nodeType";
  134. else if (nodeType == Xml.PCData)
  135. x = createPCData(v);
  136. else if (nodeType == Xml.CData)
  137. x = createCData(v);
  138. else if (nodeType == Xml.Comment)
  139. x = createComment(v);
  140. else if (nodeType == Xml.DocType)
  141. x = createDocType(v);
  142. else
  143. x = createProcessingInstruction(v);
  144. var p = _node.parent();
  145. if (p != null) {
  146. p.insertChildAfter(_node, x._node);
  147. var i = _node.childIndex();
  148. var children = p.children();
  149. untyped __delete__(children, Reflect.fields(children)[i]);
  150. }
  151. _node = x._node;
  152. return v;
  153. }
  154. private function get_parent():Xml {
  155. var p = _node.parent();
  156. return p == null ? null : wrap(p);
  157. }
  158. private static function wrap(node:XML, ?type:XmlType):Xml {
  159. var x = new Xml();
  160. x._node = node;
  161. x.nodeType = (type != null) ? type : getNodeType(node);
  162. return x;
  163. }
  164. private function wraps(xList:XMLList):Array<Xml> {
  165. var out = new Array<Xml>();
  166. for (i in 0...xList.length())
  167. out.push(wrap(xList[i]));
  168. return out;
  169. }
  170. function getAttribNS(cur:XML, ns:Array<String>):XMLList {
  171. var n = cur.namespace(ns[0]);
  172. if (n == null) {
  173. var parent = cur.parent();
  174. if (parent == null) {
  175. n = new flash.utils.Namespace(ns[0], "@" + ns[0]);
  176. cur.addNamespace(n);
  177. } else
  178. return getAttribNS(parent, ns);
  179. }
  180. return _node.attribute(new flash.utils.QName(n, ns[1]));
  181. }
  182. public function get(att:String):String {
  183. if (nodeType != Xml.Element)
  184. throw "bad nodeType";
  185. var ns = att.split(":");
  186. if (ns[0] == "xmlns") {
  187. var n = _node.namespace((ns[1] == null) ? "" : ns[1]);
  188. return (n == null) ? null : n.uri;
  189. }
  190. if (ns.length == 1) {
  191. if (!Reflect.hasField(_node, "@" + att))
  192. return null;
  193. return Reflect.field(_node, "@" + att);
  194. }
  195. var a = getAttribNS(_node, ns);
  196. return (a.length() == 0) ? null : a.toString();
  197. }
  198. public function set(att:String, value:String):Void {
  199. if (nodeType != Xml.Element)
  200. throw "bad nodeType";
  201. var ns = att.split(":");
  202. if (ns[0] == "xmlns") {
  203. var n = _node.namespace((ns[1] == null) ? "" : ns[1]);
  204. if (n != null)
  205. throw "Can't modify namespace";
  206. if (ns[1] == null)
  207. throw "Can't set default namespace";
  208. _node.addNamespace(new flash.utils.Namespace(ns[1], value));
  209. return;
  210. }
  211. if (ns.length == 1)
  212. Reflect.setField(_node, "@" + att, value);
  213. else {
  214. var a = getAttribNS(_node, ns);
  215. untyped a[0] = value;
  216. }
  217. }
  218. public function remove(att:String):Void {
  219. if (nodeType != Xml.Element)
  220. throw "bad nodeType";
  221. var ns = att.split(":");
  222. if (ns.length == 1)
  223. Reflect.deleteField(_node, "@" + att);
  224. else
  225. untyped __delete__(getAttribNS(_node, ns), 0);
  226. }
  227. public function exists(att:String):Bool {
  228. if (nodeType != Xml.Element)
  229. throw "bad nodeType";
  230. var ns = att.split(":");
  231. if (ns[0] == "xmlns")
  232. return _node.namespace((ns[1] == null) ? "" : ns[1]) != null;
  233. if (ns.length == 1)
  234. return Reflect.hasField(_node, "@" + att);
  235. return getAttribNS(_node, ns).length() > 0;
  236. }
  237. public function attributes():Iterator<String> {
  238. if (nodeType != Xml.Element)
  239. throw "bad nodeType";
  240. var attributes:XMLList = _node.attributes();
  241. var names = Reflect.fields(attributes);
  242. var cur = 0;
  243. var nss = _node.namespaceDeclarations();
  244. return {
  245. hasNext: function() {
  246. return cur < names.length + nss.length;
  247. },
  248. next: function() {
  249. if (cur < names.length) {
  250. return attributes[Std.parseInt(names[cur++])].name();
  251. } else {
  252. var ns:flash.utils.Namespace = nss[cur++ - names.length];
  253. return "xmlns:" + ns.prefix;
  254. }
  255. }
  256. }
  257. }
  258. public function iterator():Iterator<Xml> {
  259. if (nodeType != Xml.Element && nodeType != Xml.Document)
  260. throw "bad nodeType";
  261. var children:XMLList = _node.children();
  262. var wrappers:Array<Xml> = wraps(children);
  263. var cur = 0;
  264. return {
  265. hasNext: function() {
  266. return cur < wrappers.length;
  267. },
  268. next: function() {
  269. return wrappers[cur++];
  270. }
  271. };
  272. }
  273. public function elements():Iterator<Xml> {
  274. if (nodeType != Xml.Element && nodeType != Xml.Document)
  275. throw "bad nodeType";
  276. var elements:XMLList = _node.elements();
  277. var wrappers:Array<Xml> = wraps(elements);
  278. var cur = 0;
  279. return {
  280. hasNext: function() {
  281. return cur < wrappers.length;
  282. },
  283. next: function() {
  284. return wrappers[cur++];
  285. }
  286. };
  287. }
  288. public function elementsNamed(name:String):Iterator<Xml> {
  289. if (nodeType != Xml.Element && nodeType != Xml.Document)
  290. throw "bad nodeType";
  291. var ns = name.split(":");
  292. var elements:XMLList;
  293. if (ns.length == 1)
  294. elements = _node.elements(name);
  295. else
  296. elements = _node.elements();
  297. var wrappers:Array<Xml> = wraps(elements);
  298. if (ns.length != 1)
  299. for (w in wrappers.copy())
  300. if (w._node.localName() != ns[1] || w._node.namespace().prefix != ns[0])
  301. wrappers.remove(w);
  302. var cur = 0;
  303. return {
  304. hasNext: function() {
  305. return cur < wrappers.length;
  306. },
  307. next: function() {
  308. return wrappers[cur++];
  309. }
  310. };
  311. }
  312. public function firstChild():Xml {
  313. if (nodeType != Xml.Element && nodeType != Xml.Document)
  314. throw "bad nodeType";
  315. var children:XMLList = _node.children();
  316. if (children.length() == 0)
  317. return null;
  318. return wrap(children[0]);
  319. }
  320. public function firstElement():Xml {
  321. if (nodeType != Xml.Element && nodeType != Xml.Document)
  322. throw "bad nodeType";
  323. var elements:XMLList = _node.elements();
  324. if (elements.length() == 0)
  325. return null;
  326. return wrap(elements[0]);
  327. }
  328. public function addChild(x:Xml):Void {
  329. if (nodeType != Xml.Element && nodeType != Xml.Document)
  330. throw "bad nodeType";
  331. if (x.parent != null)
  332. x.parent.removeChild(x);
  333. var children:XMLList = _node.children();
  334. _node.appendChild(x._node);
  335. }
  336. public function removeChild(x:Xml):Bool {
  337. if (nodeType != Xml.Element && nodeType != Xml.Document)
  338. throw "bad nodeType";
  339. var children:XMLList = _node.children();
  340. if (_node != x._node.parent())
  341. return false;
  342. var i = x._node.childIndex();
  343. untyped __delete__(children, Reflect.fields(children)[i]);
  344. return true;
  345. }
  346. public function insertChild(x:Xml, pos:Int):Void {
  347. if (nodeType != Xml.Element && nodeType != Xml.Document)
  348. throw "bad nodeType";
  349. if (x.parent != null)
  350. x.parent.removeChild(x);
  351. var children:XMLList = _node.children();
  352. if (pos < children.length())
  353. _node.insertChildBefore(children[pos], x._node);
  354. else
  355. _node.appendChild(x._node);
  356. }
  357. public function toString():String {
  358. XML.prettyPrinting = false;
  359. if (nodeType == Xml.Document) {
  360. var str = _node.toXMLString();
  361. // remove <__document xmlns....>STR</__document> wrapper
  362. str = str.substr(str.indexOf(">") + 1);
  363. str = str.substr(0, str.length - 13);
  364. return str;
  365. }
  366. return _node.toXMLString();
  367. }
  368. static function __init__():Void
  369. untyped {
  370. Element = "element";
  371. PCData = "pcdata";
  372. CData = "cdata";
  373. Comment = "comment";
  374. DocType = "doctype";
  375. ProcessingInstruction = "processingInstruction";
  376. Document = "document";
  377. }
  378. }