Xml.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /*
  2. * Copyright (C)2005-2012 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. import php.Lib;
  23. /*
  24. * Copyright (c) 2005, The haXe Project Contributors
  25. * All rights reserved.
  26. * Redistribution and use in source and binary forms, with or without
  27. * modification, are permitted provided that the following conditions are met:
  28. *
  29. * - Redistributions of source code must retain the above copyright
  30. * notice, this list of conditions and the following disclaimer.
  31. * - Redistributions in binary form must reproduce the above copyright
  32. * notice, this list of conditions and the following disclaimer in the
  33. * documentation and/or other materials provided with the distribution.
  34. *
  35. * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
  36. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  37. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
  39. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  40. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  41. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  42. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  43. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  44. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  45. * DAMAGE.
  46. */
  47. enum XmlType {
  48. }
  49. @:coreApi class Xml {
  50. public static var Element(default,null) : XmlType;
  51. public static var PCData(default,null) : XmlType;
  52. public static var CData(default,null) : XmlType;
  53. public static var Comment(default,null) : XmlType;
  54. public static var DocType(default,null) : XmlType;
  55. public static var Prolog(default,null) : XmlType;
  56. public static var Document(default,null) : XmlType;
  57. public var nodeType(default,null) : XmlType;
  58. public var nodeName(get,set) : String;
  59. public var nodeValue(get,set) : String;
  60. public var parent(get,null) : Xml;
  61. var _nodeName : String;
  62. var _nodeValue : String;
  63. var _attributes : Hash<String>;
  64. var _children : Array<Xml>;
  65. var _parent : Xml;
  66. private static var build : Xml;
  67. private static function __start_element_handler(parser : Dynamic, name : String, attribs : ArrayAccess<String>) : Void {
  68. var node = createElement(name);
  69. untyped __php__("foreach($attribs as $k => $v) $node->set($k, $v)");
  70. build.addChild(node);
  71. build = node;
  72. }
  73. private static function __end_element_handler(parser : Dynamic, name : String) : Void {
  74. build = build.parent;
  75. }
  76. private static function __decodeattr(value : String) : String
  77. {
  78. return untyped __call__("str_replace", "'", '&apos;', __call__("htmlspecialchars", value, __php__('ENT_COMPAT'), 'UTF-8'));
  79. }
  80. private static function __decodeent(value : String) : String
  81. {
  82. return untyped __call__("str_replace", "'", '&apos;', __call__("htmlentities", value, __php__('ENT_COMPAT'), 'UTF-8'));
  83. }
  84. private static function __character_data_handler(parser : Dynamic, data : String) : Void {
  85. var d = __decodeent(data);
  86. if ((untyped __call__("strlen", data) == 1 && d != data) || d == data) {
  87. var last = build._children[build._children.length - 1];
  88. if (null != last && last.nodeType == Xml.PCData)
  89. {
  90. last.nodeValue += d;
  91. } else
  92. build.addChild(createPCData(d));
  93. } else {
  94. build.addChild(createCData(data));
  95. }
  96. }
  97. private static function __default_handler(parser : Dynamic, data : String) : Void {
  98. //On some PHP setups (seems to happen when libexpat is used) we may get called for such "entities" although character_data will correctly be called afterward.
  99. if(data == "<![CDATA[")
  100. return;
  101. if(data == "]]>")
  102. return;
  103. if ("<!--" == data.substr(0, 4))
  104. build.addChild(createComment(data.substr(4, data.length-7)));
  105. else
  106. build.addChild(createPCData(data));
  107. }
  108. static var reHeader = ~/\s*(?:<\?(.+?)\?>)?(?:<!DOCTYPE ([^>]+)>)?/mi;
  109. public static function parse( str : String ) : Xml {
  110. build = createDocument();
  111. var xml_parser = untyped __call__("xml_parser_create");
  112. untyped __call__("xml_set_element_handler", xml_parser, __start_element_handler, __end_element_handler);
  113. untyped __call__("xml_set_character_data_handler", xml_parser, __character_data_handler);
  114. untyped __call__("xml_set_default_handler", xml_parser, __default_handler);
  115. untyped __call__("xml_parser_set_option", xml_parser, __php__("XML_OPTION_CASE_FOLDING"), 0);
  116. untyped __call__("xml_parser_set_option", xml_parser, __php__("XML_OPTION_SKIP_WHITE"), 0);
  117. reHeader.match(str);
  118. str = "<doc>"+reHeader.matchedRight()+"</doc>";
  119. if(1 != untyped __call__("xml_parse", xml_parser, str, true)) {
  120. throw "Xml parse error ("+untyped __call__("xml_error_string", __call__("xml_get_error_code", xml_parser)) + ") line #" + __call__("xml_get_current_line_number", xml_parser);
  121. }
  122. untyped __call__("xml_parser_free", xml_parser);
  123. build = build._children[0];
  124. build._parent = null;
  125. build._nodeName = null;
  126. build.nodeType = Document;
  127. var doctype = reHeader.matched(2);
  128. if (null != doctype)
  129. build.insertChild(createDocType(doctype), 0);
  130. var prolog = reHeader.matched(1);
  131. if (null != prolog)
  132. build.insertChild(createProlog(prolog), 0);
  133. return build;
  134. }
  135. private function new() : Void {}
  136. public static function createElement( name : String ) : Xml {
  137. var r = new Xml();
  138. r.nodeType = Xml.Element;
  139. r._children = new Array();
  140. r._attributes = new Hash();
  141. r.set_nodeName( name );
  142. return r;
  143. }
  144. public static function createPCData( data : String ) : Xml {
  145. var r = new Xml();
  146. r.nodeType = Xml.PCData;
  147. r.set_nodeValue( data );
  148. return r;
  149. }
  150. public static function createCData( data : String ) : Xml {
  151. var r = new Xml();
  152. r.nodeType = Xml.CData;
  153. r.set_nodeValue( data );
  154. return r;
  155. }
  156. public static function createComment( data : String ) : Xml {
  157. var r = new Xml();
  158. r.nodeType = Xml.Comment;
  159. r.set_nodeValue( data );
  160. return r;
  161. }
  162. public static function createDocType( data : String ) : Xml {
  163. var r = new Xml();
  164. r.nodeType = Xml.DocType;
  165. r.set_nodeValue( data );
  166. return r;
  167. }
  168. public static function createProlog( data : String ) : Xml {
  169. var r = new Xml();
  170. r.nodeType = Xml.Prolog;
  171. r.set_nodeValue( data );
  172. return r;
  173. }
  174. public static function createDocument() : Xml {
  175. var r = new Xml();
  176. r.nodeType = Xml.Document;
  177. r._children = new Array();
  178. return r;
  179. }
  180. private function get_nodeName() : String {
  181. if( nodeType != Xml.Element )
  182. throw "bad nodeType";
  183. return _nodeName;
  184. }
  185. private function set_nodeName( n : String ) : String {
  186. if( nodeType != Xml.Element )
  187. throw "bad nodeType";
  188. return _nodeName = n;
  189. }
  190. private function get_nodeValue() : String {
  191. if( nodeType == Xml.Element || nodeType == Xml.Document )
  192. throw "bad nodeType";
  193. return _nodeValue;
  194. }
  195. private function set_nodeValue( v : String ) : String {
  196. if( nodeType == Xml.Element || nodeType == Xml.Document )
  197. throw "bad nodeType";
  198. return _nodeValue = v;
  199. }
  200. private inline function get_parent() : Xml {
  201. return _parent;
  202. }
  203. public function get( att : String ) : String {
  204. if( nodeType != Xml.Element )
  205. throw "bad nodeType";
  206. return _attributes.get( att );
  207. }
  208. // TODO: check correct transform function
  209. @:ifFeature("Xml.parse")
  210. public function set( att : String, value : String ) : Void {
  211. if( nodeType != Xml.Element )
  212. throw "bad nodeType";
  213. _attributes.set( att, __decodeattr(value) );
  214. }
  215. public function remove( att : String ) : Void{
  216. if( nodeType != Xml.Element )
  217. throw "bad nodeType";
  218. _attributes.remove( att );
  219. }
  220. public function exists( att : String ) : Bool {
  221. if( nodeType != Xml.Element )
  222. throw "bad nodeType";
  223. return _attributes.exists( att );
  224. }
  225. public function attributes() : Iterator<String> {
  226. if( nodeType != Xml.Element )
  227. throw "bad nodeType";
  228. return _attributes.keys();
  229. }
  230. public function iterator() : Iterator<Xml> {
  231. if( _children == null ) throw "bad nodetype";
  232. return _children.iterator();
  233. }
  234. public function elements() : Iterator<Xml> {
  235. if( _children == null ) throw "bad nodetype";
  236. return Lambda.filter(_children, function(child) return child.nodeType == Xml.Element).iterator();
  237. }
  238. public function elementsNamed( name : String ) : Iterator<Xml> {
  239. if( _children == null ) throw "bad nodetype";
  240. return Lambda.filter(_children, function(child) return child.nodeType == Xml.Element && child.nodeName == name).iterator();
  241. }
  242. public function firstChild() : Xml {
  243. if( _children == null ) throw "bad nodetype";
  244. if( _children.length == 0 ) return null;
  245. return _children[0];
  246. }
  247. public function firstElement() : Xml {
  248. if( _children == null ) throw "bad nodetype";
  249. for (child in _children)
  250. if (child.nodeType == Xml.Element)
  251. return child;
  252. return null;
  253. }
  254. public function addChild( x : Xml ) : Void {
  255. if( _children == null ) throw "bad nodetype";
  256. if( x._parent != null ) x._parent._children.remove(x);
  257. x._parent = this;
  258. _children.push( x );
  259. }
  260. public function removeChild( x : Xml ) : Bool {
  261. if( _children == null ) throw "bad nodetype";
  262. var b = _children.remove( x );
  263. if( b )
  264. x._parent = null;
  265. return b;
  266. }
  267. public function insertChild( x : Xml, pos : Int ) : Void {
  268. if( _children == null ) throw "bad nodetype";
  269. if( x._parent != null ) x._parent._children.remove(x);
  270. x._parent = this;
  271. _children.insert( pos, x );
  272. }
  273. public function toString() : String {
  274. if( nodeType == Xml.PCData )
  275. return _nodeValue;
  276. var s = "";
  277. if( nodeType == Xml.Element ) {
  278. s += "<";
  279. s += _nodeName;
  280. for( k in _attributes.keys() ){
  281. s += " ";
  282. s += k;
  283. s += "=\""; // \"
  284. s += _attributes.get(k);
  285. s += "\""; // \"
  286. }
  287. if( _children.length == 0 ) {
  288. s += "/>";
  289. return s;
  290. }
  291. s += ">";
  292. } else if( nodeType == Xml.CData )
  293. return "<![CDATA["+_nodeValue+"]]>";
  294. else if( nodeType == Xml.Comment )
  295. return "<!--"+_nodeValue+"-->";
  296. else if( nodeType == Xml.DocType )
  297. return "<!DOCTYPE "+_nodeValue+">";
  298. else if ( nodeType == Xml.Prolog )
  299. return "<?"+_nodeValue+"?>";
  300. for( x in iterator() )
  301. s += x.toString();
  302. if( nodeType == Xml.Element ) {
  303. s += "</";
  304. s += _nodeName;
  305. s += ">";
  306. }
  307. return s;
  308. }
  309. static function __init__() : Void untyped {
  310. Xml.Element = "element";
  311. Xml.PCData = "pcdata";
  312. Xml.CData = "cdata";
  313. Xml.Comment = "comment";
  314. Xml.DocType = "doctype";
  315. Xml.Prolog = "prolog";
  316. Xml.Document = "document";
  317. }
  318. }