StringTools.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. * Copyright (C)2005-2015 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. /**
  23. This class provides advanced methods on Strings. It is ideally used with
  24. `using StringTools` and then acts as an extension to the String class.
  25. If the first argument to any of the methods is null, the result is
  26. unspecified.
  27. **/
  28. #if cpp
  29. using cpp.NativeString;
  30. #end
  31. class StringTools {
  32. /**
  33. Encode an URL by using the standard format.
  34. **/
  35. #if (!java && !cpp) inline #end public static function urlEncode( s : String ) : String {
  36. #if flash
  37. return untyped __global__["encodeURIComponent"](s);
  38. #elseif neko
  39. return untyped new String(_urlEncode(s.__s));
  40. #elseif js
  41. return untyped encodeURIComponent(s);
  42. #elseif cpp
  43. return untyped s.__URLEncode();
  44. #elseif java
  45. try
  46. return untyped __java__("java.net.URLEncoder.encode(s, \"UTF-8\")")
  47. catch (e:Dynamic) throw e;
  48. #elseif cs
  49. return untyped cs.system.Uri.EscapeDataString(s);
  50. #elseif python
  51. return python.lib.urllib.Parse.quote(s, "");
  52. #else
  53. return null;
  54. #end
  55. }
  56. /**
  57. Decode an URL using the standard format.
  58. **/
  59. #if (!java && !cpp) inline #end public static function urlDecode( s : String ) : String {
  60. #if flash
  61. return untyped __global__["decodeURIComponent"](s.split("+").join(" "));
  62. #elseif neko
  63. return untyped new String(_urlDecode(s.__s));
  64. #elseif js
  65. return untyped decodeURIComponent(s.split("+").join(" "));
  66. #elseif cpp
  67. return untyped s.__URLDecode();
  68. #elseif java
  69. try
  70. return untyped __java__("java.net.URLDecoder.decode(s, \"UTF-8\")")
  71. catch (e:Dynamic) throw e;
  72. #elseif cs
  73. return untyped cs.system.Uri.UnescapeDataString(s);
  74. #elseif python
  75. return python.lib.urllib.Parse.unquote(s);
  76. #else
  77. return null;
  78. #end
  79. }
  80. /**
  81. Escapes HTML special characters of the string `s`.
  82. The following replacements are made:
  83. - `&` becomes `&amp`;
  84. - `<` becomes `&lt`;
  85. - `>` becomes `&gt`;
  86. If `quotes` is true, the following characters are also replaced:
  87. - `"` becomes `&quot`;
  88. - `'` becomes `&#039`;
  89. **/
  90. public static function htmlEscape( s : String, ?quotes : Bool ) : String {
  91. s = s.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
  92. return quotes ? s.split('"').join("&quot;").split("'").join("&#039;") : s;
  93. }
  94. /**
  95. Unescapes HTML special characters of the string `s`.
  96. This is the inverse operation to htmlEscape, i.e. the following always
  97. holds: `htmlUnescape(htmlEscape(s)) == s`
  98. The replacements follow:
  99. - `&amp;` becomes `&`
  100. - `&lt;` becomes `<`
  101. - `&gt;` becomes `>`
  102. - `&quot;` becomes `"`
  103. - `&#039;` becomes `'`
  104. **/
  105. public static function htmlUnescape( s : String ) : String {
  106. return s.split("&gt;").join(">").split("&lt;").join("<").split("&quot;").join('"').split("&#039;").join("'").split("&amp;").join("&");
  107. }
  108. /**
  109. Tells if the string `s` starts with the string `start`.
  110. If `start` is `null`, the result is unspecified.
  111. If `start` is the empty String `""`, the result is true.
  112. **/
  113. public static #if (cs || java) inline #end function startsWith( s : String, start : String ) : Bool {
  114. #if java
  115. return untyped s.startsWith(start);
  116. #elseif cs
  117. return untyped s.StartsWith(start);
  118. #elseif cpp
  119. if (s.length<start.length)
  120. return false;
  121. var p0 = s.c_str();
  122. var p1 = start.c_str();
  123. for(i in 0...start.length)
  124. if ( p0.at(i) != p1.at(i) )
  125. return false;
  126. return true;
  127. #else
  128. return( s.length >= start.length && s.substr(0, start.length) == start );
  129. #end
  130. }
  131. /**
  132. Tells if the string `s` ends with the string `end`.
  133. If `end` is `null`, the result is unspecified.
  134. If `end` is the empty String `""`, the result is true.
  135. **/
  136. public static #if (cs || java) inline #end function endsWith( s : String, end : String ) : Bool {
  137. #if java
  138. return untyped s.endsWith(end);
  139. #elseif cs
  140. return untyped s.EndsWith(end);
  141. #elseif cpp
  142. if (s.length<end.length)
  143. return false;
  144. var p0 = s.c_str().add( s.length-end.length );
  145. var p1 = end.c_str();
  146. for(i in 0...end.length)
  147. if ( p0.at(i) != p1.at(i) )
  148. return false;
  149. return true;
  150. #else
  151. var elen = end.length;
  152. var slen = s.length;
  153. return( slen >= elen && s.substr(slen - elen, elen) == end );
  154. #end
  155. }
  156. /**
  157. Tells if the character in the string `s` at position `pos` is a space.
  158. A character is considered to be a space character if its character code
  159. is 9,10,11,12,13 or 32.
  160. If `s` is the empty String `""`, or if pos is not a valid position within
  161. `s`, the result is false.
  162. **/
  163. public static function isSpace( s : String, pos : Int ) : Bool {
  164. #if python
  165. if (s.length == 0 || pos < 0 || pos >= s.length) return false;
  166. #end
  167. var c = s.charCodeAt( pos );
  168. return (c > 8 && c < 14) || c == 32;
  169. }
  170. /**
  171. Removes leading space characters of `s`.
  172. This function internally calls `isSpace()` to decide which characters to
  173. remove.
  174. If `s` is the empty String `""` or consists only of space characters, the
  175. result is the empty String `""`.
  176. **/
  177. public #if cs inline #end static function ltrim( s : String ) : String {
  178. #if cs
  179. return untyped s.TrimStart();
  180. #else
  181. var l = s.length;
  182. var r = 0;
  183. while( r < l && isSpace(s,r) ){
  184. r++;
  185. }
  186. if( r > 0 )
  187. return s.substr(r, l-r);
  188. else
  189. return s;
  190. #end
  191. }
  192. /**
  193. Removes trailing space characters of `s`.
  194. This function internally calls `isSpace()` to decide which characters to
  195. remove.
  196. If `s` is the empty String `""` or consists only of space characters, the
  197. result is the empty String `""`.
  198. **/
  199. public #if cs inline #end static function rtrim( s : String ) : String {
  200. #if cs
  201. return untyped s.TrimEnd();
  202. #else
  203. var l = s.length;
  204. var r = 0;
  205. while( r < l && isSpace(s,l-r-1) ){
  206. r++;
  207. }
  208. if( r > 0 ){
  209. return s.substr(0, l-r);
  210. }else{
  211. return s;
  212. }
  213. #end
  214. }
  215. /**
  216. Removes leading and trailing space characters of `s`.
  217. This is a convenience function for `ltrim(rtrim(s))`.
  218. **/
  219. public #if (cs || java) inline #end static function trim( s : String ) : String {
  220. #if cs
  221. return untyped s.Trim();
  222. #elseif java
  223. return untyped s.trim();
  224. #else
  225. return ltrim(rtrim(s));
  226. #end
  227. }
  228. /**
  229. Concatenates `c` to `s` until `s.length` is at least `l`.
  230. If `c` is the empty String `""` or if `l` does not exceed `s.length`,
  231. `s` is returned unchanged.
  232. If `c.length` is 1, the resulting String length is exactly `l`.
  233. Otherwise the length may exceed `l`.
  234. If `c` is null, the result is unspecified.
  235. **/
  236. public static function lpad( s : String, c : String, l : Int ) : String {
  237. if (c.length <= 0)
  238. return s;
  239. while (s.length < l) {
  240. s = c + s;
  241. }
  242. return s;
  243. }
  244. /**
  245. Appends `c` to `s` until `s.length` is at least `l`.
  246. If `c` is the empty String `""` or if `l` does not exceed `s.length`,
  247. `s` is returned unchanged.
  248. If `c.length` is 1, the resulting String length is exactly `l`.
  249. Otherwise the length may exceed `l`.
  250. If `c` is null, the result is unspecified.
  251. **/
  252. public static function rpad( s : String, c : String, l : Int ) : String {
  253. if (c.length <= 0)
  254. return s;
  255. while (s.length < l) {
  256. s = s + c;
  257. }
  258. return s;
  259. }
  260. /**
  261. Replace all occurences of the String `sub` in the String `s` by the
  262. String `by`.
  263. If `sub` is the empty String `""`, `by` is inserted after each character
  264. of `s`. If `by` is also the empty String `""`, `s` remains unchanged.
  265. This is a convenience function for `s.split(sub).join(by)`.
  266. If `sub` or `by` are null, the result is unspecified.
  267. **/
  268. public static function replace( s : String, sub : String, by : String ) : String {
  269. #if java
  270. if (sub.length == 0)
  271. return s.split(sub).join(by);
  272. else
  273. return untyped s.replace(sub, by);
  274. #elseif cs
  275. if (sub.length == 0)
  276. return s.split(sub).join(by);
  277. else
  278. return untyped s.Replace(sub, by);
  279. #else
  280. return s.split(sub).join(by);
  281. #end
  282. }
  283. /**
  284. Encodes `n` into a hexadecimal representation.
  285. If `digits` is specified, the resulting String is padded with "0" until
  286. its `length` equals `digits`.
  287. **/
  288. public static function hex( n : Int, ?digits : Int ) {
  289. #if flash
  290. var n : UInt = n;
  291. var s : String = untyped n.toString(16);
  292. s = s.toUpperCase();
  293. #else
  294. var s = "";
  295. var hexChars = "0123456789ABCDEF";
  296. do {
  297. s = hexChars.charAt(n&15) + s;
  298. n >>>= 4;
  299. } while( n > 0 );
  300. #end
  301. #if python
  302. if (digits != null && s.length < digits) {
  303. var diff = digits - s.length;
  304. for (_ in 0...diff) {
  305. s = "0" + s;
  306. }
  307. }
  308. #else
  309. if( digits != null )
  310. while( s.length < digits )
  311. s = "0"+s;
  312. #end
  313. return s;
  314. }
  315. /**
  316. Returns the character code at position `index` of String `s`, or an
  317. end-of-file indicator at if `position` equals `s.length`.
  318. This method is faster than `String.charCodeAt()` on some platforms, but
  319. the result is unspecified if `index` is negative or greater than
  320. `s.length`.
  321. End of file status can be checked by calling `StringTools.isEof()` with
  322. the returned value as argument.
  323. This operation is not guaranteed to work if `s` contains the `\0`
  324. character.
  325. **/
  326. public static inline function fastCodeAt( s : String, index : Int ) : Int {
  327. #if neko
  328. return untyped __dollar__sget(s.__s, index);
  329. #elseif cpp
  330. return untyped s.cca(index);
  331. #elseif flash
  332. return untyped s.cca(index);
  333. #elseif java
  334. return ( index < s.length ) ? cast(_charAt(s, index), Int) : -1;
  335. #elseif cs
  336. return ( cast(index, UInt) < s.length ) ? cast(s[index], Int) : -1;
  337. #elseif js
  338. return (untyped s).charCodeAt(index);
  339. #elseif python
  340. return if (index >= s.length) -1 else python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
  341. #else
  342. return untyped s.cca(index);
  343. #end
  344. }
  345. /*
  346. Tells if `c` represents the end-of-file (EOF) character.
  347. */
  348. @:noUsing public static inline function isEof( c : Int ) : Bool {
  349. #if (flash || cpp)
  350. return c == 0;
  351. #elseif js
  352. return c != c; // fast NaN
  353. #elseif neko
  354. return c == null;
  355. #elseif cs
  356. return c == -1;
  357. #elseif java
  358. return c == -1;
  359. #elseif python
  360. return c == -1;
  361. #else
  362. return false;
  363. #end
  364. }
  365. #if java
  366. private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16 return untyped str._charAt(idx);
  367. #end
  368. #if neko
  369. private static var _urlEncode = neko.Lib.load("std","url_encode",1);
  370. private static var _urlDecode = neko.Lib.load("std","url_decode",1);
  371. #end
  372. }