Xml.hx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /*
  2. * Copyright (c) 2005, The haXe Project Contributors
  3. * All rights reserved.
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * - Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * - Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
  17. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  20. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  23. * DAMAGE.
  24. */
  25. import flash.xml.XML;
  26. import flash.xml.XMLList;
  27. enum XmlType {
  28. }
  29. @:core_api class Xml {
  30. public static var Element(default,null) : XmlType;
  31. public static var PCData(default,null) : XmlType;
  32. public static var CData(default,null) : XmlType;
  33. public static var Comment(default,null) : XmlType;
  34. public static var DocType(default,null) : XmlType;
  35. public static var Prolog(default,null) : XmlType;
  36. public static var Document(default,null) : XmlType;
  37. public var nodeType(default,null) : XmlType;
  38. public var nodeName(getNodeName,setNodeName) : String;
  39. public var nodeValue(getNodeValue,setNodeValue) : String;
  40. public var parent(getParent,null) : Xml;
  41. var _node : flash.xml.XML;
  42. public static function parse( str : String ) : Xml {
  43. XML.ignoreWhitespace = false;
  44. XML.ignoreProcessingInstructions = false;
  45. XML.ignoreComments = false;
  46. var prefix = "<__document";
  47. var root = null;
  48. while( root == null ) {
  49. try {
  50. root = new flash.xml.XML(prefix+">" + str + "</__document>");
  51. } catch( e : flash.errors.TypeError ) {
  52. // if we miss a namespace, let's add it !
  53. var r = ~/"([^"]+)"/; //"
  54. if( e.errorID == 1083 && r.match(e.message) ) {
  55. var ns = r.matched(1);
  56. prefix += " xmlns:" + ns + '="@' + ns + '"';
  57. } else
  58. throw e;
  59. }
  60. }
  61. return wrap( root, Xml.Document );
  62. }
  63. @:keep #if as3 @:hack public #end static function compare( a : Xml, b : Xml ) : Bool {
  64. return a == null ? b == null : (b == null ? false : a._node == b._node);
  65. }
  66. private function new() : Void {}
  67. public static function createElement( name : String ) : Xml {
  68. return wrap( new flash.xml.XML("<"+name+"/>"), Xml.Element );
  69. }
  70. public static function createPCData( data : String ) : Xml {
  71. XML.ignoreWhitespace = false;
  72. return wrap( new flash.xml.XML(data), Xml.PCData );
  73. }
  74. public static function createCData( data : String ) : Xml {
  75. return wrap( new flash.xml.XML("<![CDATA["+data+"]]>"), Xml.CData );
  76. }
  77. public static function createComment( data : String ) : Xml {
  78. XML.ignoreComments = false;
  79. return wrap( new flash.xml.XML("<!--"+data+"-->"), Xml.Comment );
  80. }
  81. public static function createDocType( data : String ) : Xml {
  82. return wrap( new flash.xml.XML("<!DOCTYPE "+data+">"), Xml.DocType );
  83. }
  84. public static function createProlog( data : String ) : Xml {
  85. XML.ignoreProcessingInstructions = false;
  86. return wrap( new flash.xml.XML("<?"+data+"?>"), Xml.Prolog );
  87. }
  88. public static function createDocument() : Xml {
  89. return wrap( new flash.xml.XML("<__document/>"), Xml.Document );
  90. }
  91. private static function getNodeType( node : flash.xml.XML ) : XmlType {
  92. switch( node.nodeKind() ) {
  93. case "element":
  94. return Xml.Element;
  95. case "text":
  96. return Xml.PCData;
  97. case "processing-instruction":
  98. return Xml.Prolog;
  99. case "comment":
  100. return Xml.Comment;
  101. default :
  102. throw "unimplemented node type: " + node.nodeType;
  103. }
  104. return null;
  105. }
  106. private function getNodeName() : String {
  107. if( nodeType != Xml.Element )
  108. throw "bad nodeType";
  109. var ns = _node.namespace();
  110. return (ns.prefix == "") ? _node.localName() : ns.prefix+":"+_node.localName();
  111. }
  112. private function setNodeName( n : String ) : String {
  113. if( nodeType != Xml.Element )
  114. throw "bad nodeType";
  115. var ns = n.split(":");
  116. if( ns.length == 1 )
  117. _node.setLocalName(n);
  118. else {
  119. _node.setLocalName(ns[1]);
  120. _node.setNamespace(_node.namespace(ns[0]));
  121. }
  122. return n;
  123. }
  124. private function getNodeValue() : String {
  125. var nodeType = nodeType;
  126. if( nodeType == Xml.Element || nodeType == Xml.Document )
  127. throw "bad nodeType";
  128. if( nodeType == Xml.Comment )
  129. return _node.toString().substr(4,-7);
  130. return _node.toString();
  131. }
  132. private function setNodeValue( v : String ) : String {
  133. var nodeType = nodeType;
  134. var x = null;
  135. if( nodeType == Xml.Element || nodeType == Xml.Document )
  136. throw "bad nodeType";
  137. else if( nodeType == Xml.PCData )
  138. x = createPCData(v);
  139. else if( nodeType == Xml.CData )
  140. x = createCData(v);
  141. else if( nodeType == Xml.Comment )
  142. x = createComment(v);
  143. else if( nodeType == Xml.DocType )
  144. x = createDocType(v);
  145. else
  146. x = createProlog(v);
  147. var p = _node.parent();
  148. if( p != null ) {
  149. p.insertChildAfter(_node, x._node);
  150. var i = _node.childIndex();
  151. var children = p.children();
  152. untyped __delete__(children, Reflect.fields(children)[i]);
  153. }
  154. _node = x._node;
  155. return v;
  156. }
  157. private function getParent() :Xml {
  158. var p = _node.parent();
  159. return p == null ? null : wrap( p );
  160. }
  161. private static function wrap( node : XML, ?type : XmlType ) : Xml {
  162. var x = new Xml();
  163. x._node = node;
  164. x.nodeType = (type != null) ? type : getNodeType( node );
  165. return x;
  166. }
  167. private function wraps( xList : XMLList ) : Array<Xml> {
  168. var out = new Array<Xml>();
  169. for( i in 0...xList.length() )
  170. out.push( wrap(xList[i]) );
  171. return out;
  172. }
  173. function getAttribNS( cur : XML, ns : Array<String> ) : XMLList {
  174. var n = cur.namespace(ns[0]);
  175. if( n == null ) {
  176. var parent = cur.parent();
  177. if( parent == null ) {
  178. n = new flash.utils.Namespace(ns[0], "@"+ns[0]);
  179. cur.addNamespace(n);
  180. } else
  181. return getAttribNS(parent, ns);
  182. }
  183. return _node.attribute(new flash.utils.QName(n,ns[1]));
  184. }
  185. public function get( att : String ) : String {
  186. if( nodeType != Xml.Element )
  187. throw "bad nodeType";
  188. var ns = att.split(":");
  189. if( ns[0] == "xmlns" ) {
  190. var n = _node.namespace((ns[1] == null) ? "" : ns[1]);
  191. return (n == null) ? null : n.uri;
  192. }
  193. if( ns.length == 1 ) {
  194. if( !Reflect.hasField(_node,"@"+att) )
  195. return null;
  196. return Reflect.field(_node, "@"+att);
  197. }
  198. var a = getAttribNS(_node,ns);
  199. return (a.length() == 0) ? null : a.toString();
  200. }
  201. public function set( att : String, value : String ) : Void {
  202. if( nodeType != Xml.Element )
  203. throw "bad nodeType";
  204. var ns = att.split(":");
  205. if( ns[0] == "xmlns" ) {
  206. var n = _node.namespace((ns[1] == null) ? "" : ns[1]);
  207. if( n != null )
  208. throw "Can't modify namespace";
  209. if( ns[1] == null )
  210. throw "Can't set default namespace";
  211. _node.addNamespace(new flash.utils.Namespace(ns[1], value));
  212. return;
  213. }
  214. if( ns.length == 1 )
  215. Reflect.setField(_node, "@"+att, value);
  216. else {
  217. var a = getAttribNS(_node,ns);
  218. untyped a[0] = value;
  219. }
  220. }
  221. public function remove( att : String ) : Void{
  222. if( nodeType != Xml.Element )
  223. throw "bad nodeType";
  224. var ns = att.split(":");
  225. if( ns.length == 1 )
  226. Reflect.deleteField(_node, "@"+att);
  227. else
  228. untyped __delete__(getAttribNS(_node,ns),0);
  229. }
  230. public function exists( att : String ) : Bool {
  231. if( nodeType != Xml.Element )
  232. throw "bad nodeType";
  233. var ns = att.split(":");
  234. if( ns[0] == "xmlns" )
  235. return _node.namespace((ns[1] == null) ? "" : ns[1]) != null;
  236. if( ns.length == 1 )
  237. return Reflect.hasField(_node, "@"+att);
  238. return getAttribNS(_node,ns).length() > 0;
  239. }
  240. public function attributes() : Iterator<String> {
  241. if( nodeType != Xml.Element )
  242. throw "bad nodeType";
  243. var attributes :XMLList = _node.attributes();
  244. var names = Reflect.fields(attributes);
  245. var cur = 0;
  246. return {
  247. hasNext : function(){
  248. return cur < names.length;
  249. },
  250. next : function(){
  251. return attributes[Std.parseInt(names[cur++])].name();
  252. }
  253. }
  254. }
  255. public function iterator() : Iterator<Xml> {
  256. if( nodeType != Xml.Element && nodeType != Xml.Document )
  257. throw "bad nodeType";
  258. var children:XMLList = _node.children();
  259. var wrappers :Array<Xml> = wraps(children);
  260. var cur = 0;
  261. return {
  262. hasNext : function(){
  263. return cur < wrappers.length;
  264. },
  265. next : function(){
  266. return wrappers[cur++];
  267. }
  268. };
  269. }
  270. public function elements() : Iterator<Xml> {
  271. if( nodeType != Xml.Element && nodeType != Xml.Document )
  272. throw "bad nodeType";
  273. var elements:XMLList = _node.elements();
  274. var wrappers :Array<Xml> = wraps(elements);
  275. var cur = 0;
  276. return {
  277. hasNext : function(){
  278. return cur < wrappers.length;
  279. },
  280. next : function(){
  281. return wrappers[cur++];
  282. }
  283. };
  284. }
  285. public function elementsNamed( name : String ) : Iterator<Xml> {
  286. if( nodeType != Xml.Element && nodeType != Xml.Document )
  287. throw "bad nodeType";
  288. var ns = name.split(":");
  289. var elements:XMLList;
  290. if( ns.length == 1 )
  291. elements = _node.elements(name);
  292. else
  293. elements = _node.elements();
  294. var wrappers :Array<Xml> = wraps(elements);
  295. if( ns.length != 1 )
  296. for( w in wrappers.copy() )
  297. if( w._node.localName() != ns[1] || w._node.namespace().prefix != ns[0] )
  298. wrappers.remove(w);
  299. var cur = 0;
  300. return {
  301. hasNext : function(){
  302. return cur < wrappers.length;
  303. },
  304. next : function(){
  305. return wrappers[cur++];
  306. }
  307. };
  308. }
  309. public function firstChild() : Xml {
  310. if( nodeType != Xml.Element && nodeType != Xml.Document )
  311. throw "bad nodeType";
  312. var children:XMLList = _node.children();
  313. if( children.length() == 0 )
  314. return null;
  315. return wrap( children[0] );
  316. }
  317. public function firstElement() : Xml {
  318. if( nodeType != Xml.Element && nodeType != Xml.Document )
  319. throw "bad nodeType";
  320. var elements:XMLList = _node.elements();
  321. if( elements.length() == 0 )
  322. return null;
  323. return wrap( elements[0] );
  324. }
  325. public function addChild( x : Xml ) : Void {
  326. if( nodeType != Xml.Element && nodeType != Xml.Document )
  327. throw "bad nodeType";
  328. var children:XMLList = _node.children();
  329. _node.appendChild(x._node);
  330. }
  331. public function removeChild( x : Xml ) : Bool {
  332. if( nodeType != Xml.Element && nodeType != Xml.Document )
  333. throw "bad nodeType";
  334. var children:XMLList = _node.children();
  335. if( _node != x._node.parent() )
  336. return false;
  337. var i = x._node.childIndex();
  338. untyped __delete__(children, Reflect.fields(children)[i]);
  339. return true;
  340. }
  341. public function insertChild( x : Xml, pos : Int ) : Void {
  342. if( nodeType != Xml.Element && nodeType != Xml.Document )
  343. throw "bad nodeType";
  344. var children:XMLList = _node.children();
  345. if( pos < children.length() )
  346. _node.insertChildBefore(children[pos], x._node);
  347. else
  348. _node.appendChild(x._node);
  349. }
  350. public function toString() : String {
  351. XML.prettyPrinting = false;
  352. if( nodeType == Xml.Document ) {
  353. var str = _node.toXMLString();
  354. // remove <__document xmlns....>STR</__document> wrapper
  355. str = str.substr(str.indexOf(">") + 1);
  356. str = str.substr(0, str.length - 13);
  357. return str;
  358. }
  359. return _node.toXMLString();
  360. }
  361. static function __init__() : Void untyped {
  362. Element = "element";
  363. PCData = "pcdata";
  364. CData = "cdata";
  365. Comment = "comment";
  366. DocType = "doctype";
  367. Prolog = "prolog";
  368. Document = "document";
  369. }
  370. }