Pārlūkot izejas kodu

added StringTools specification

Simon Krajewski 12 gadi atpakaļ
vecāks
revīzija
b4f5ea5bf4

+ 117 - 47
std/StringTools.hx

@@ -20,10 +20,11 @@
  * DEALINGS IN THE SOFTWARE.
  * DEALINGS IN THE SOFTWARE.
  */
  */
 /**
 /**
-	The StringTools class contains some extra functionalities for [String]
-	manipulation. It's stored in a different class in order to prevent
-	the standard [String] of being bloated and thus increasing the size of
-	each application using it.
+	This class provides advanced methods on Strings. It is ideally used with
+	'using StringTools' and then acts as an extension to the String class.
+	
+	If the first argument to any of the methods is null, the result is
+	unspecified.
 **/
 **/
 #if cs
 #if cs
 @:keep
 @:keep
@@ -81,7 +82,15 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Escape HTML special characters of the string.
+		Escapes HTML special characters of the string [s].
+		
+		The following replacements are made:
+			- & becomes &
+			- < becomes &lt;
+			- > becomes &gt;
+		If [quotes] is true, the following characters are also replaced:
+			- " becomes &quot;
+			- ' becomes &#039;
 	**/
 	**/
 	public static function htmlEscape( s : String, ?quotes : Bool ) : String {
 	public static function htmlEscape( s : String, ?quotes : Bool ) : String {
 		s = s.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
 		s = s.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
@@ -89,7 +98,17 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Unescape HTML special characters of the string.
+		Unescapes HTML special characters of the string [s].
+		
+		This is the inverse operation to htmlEscape, i.e. the following always
+		holds: htmlUnescape(htmlEscape(s)) == s
+		
+		The replacements follow:
+			- &amp; becomes &
+			- &lt; becomes <
+			- &gt; becomes >
+			- &quot; becomes "
+			- &#039; becomes '
 	**/
 	**/
 	public static function htmlUnescape( s : String ) : String {
 	public static function htmlUnescape( s : String ) : String {
 		return s.split("&gt;").join(">").split("&lt;").join("<").split("&quot;").join('"').split("&#039;").join("'").split("&amp;").join("&");
 		return s.split("&gt;").join(">").split("&lt;").join("<").split("&quot;").join('"').split("&#039;").join("'").split("&amp;").join("&");
@@ -97,6 +116,10 @@ class StringTools {
 
 
 	/**
 	/**
 		Tells if the string [s] starts with the string [start].
 		Tells if the string [s] starts with the string [start].
+		
+		If [start] is null, the result is unspecified.
+		
+		If [start] is the empty String "", the result is true.
 	**/
 	**/
 	public static #if (cs || java) inline #end function startsWith( s : String, start : String ) : Bool {
 	public static #if (cs || java) inline #end function startsWith( s : String, start : String ) : Bool {
 		#if java
 		#if java
@@ -110,6 +133,10 @@ class StringTools {
 
 
 	/**
 	/**
 		Tells if the string [s] ends with the string [end].
 		Tells if the string [s] ends with the string [end].
+		
+		If [end] is null, the result is unspecified.
+		
+		If [end] is the empty String "", the result is true.		
 	**/
 	**/
 	public static #if (cs || java) inline #end function endsWith( s : String, end : String ) : Bool {
 	public static #if (cs || java) inline #end function endsWith( s : String, end : String ) : Bool {
 		#if java
 		#if java
@@ -125,6 +152,12 @@ class StringTools {
 
 
 	/**
 	/**
 		Tells if the character in the string [s] at position [pos] is a space.
 		Tells if the character in the string [s] at position [pos] is a space.
+		
+		A character is considered to be a space character if its character code
+		is 9,10,11,12,13 or 32.
+		
+		If [s] is the empty String "", or if pos is not a valid position within
+		[s], the result is false.
 	**/
 	**/
 	public static function isSpace( s : String, pos : Int ) : Bool {
 	public static function isSpace( s : String, pos : Int ) : Bool {
 		var c = s.charCodeAt( pos );
 		var c = s.charCodeAt( pos );
@@ -132,7 +165,13 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Removes spaces at the left of the String [s].
+		Removes leading space characters of [s].
+		
+		This function internally calls isSpace() to decide which characters to
+		remove.
+		
+		If [s] is the empty String "" or consists only of space characters, the
+		result is the empty String "".
 	**/
 	**/
 	public #if cs inline #end static function ltrim( s : String ) : String {
 	public #if cs inline #end static function ltrim( s : String ) : String {
 		#if cs
 		#if cs
@@ -151,7 +190,13 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Removes spaces at the right of the String [s].
+		Removes trailing space characters of [s].
+		
+		This function internally calls isSpace() to decide which characters to
+		remove.
+		
+		If [s] is the empty String "" or consists only of space characters, the
+		result is the empty String "".
 	**/
 	**/
 	public #if cs inline #end static function rtrim( s : String ) : String {
 	public #if cs inline #end static function rtrim( s : String ) : String {
 		#if cs
 		#if cs
@@ -171,7 +216,9 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Removes spaces at the beginning and the end of the String [s].
+		Removes leading and trailing space characters of [s].
+		
+		This is a convenience function for ltrim(rtrim(s)).
 	**/
 	**/
 	public #if (cs || java) inline #end static function trim( s : String ) : String {
 	public #if (cs || java) inline #end static function trim( s : String ) : String {
 		#if cs
 		#if cs
@@ -182,48 +229,61 @@ class StringTools {
 		return ltrim(rtrim(s));
 		return ltrim(rtrim(s));
 		#end
 		#end
 	}
 	}
-
+	
 	/**
 	/**
-		Pad the string [s] by appending [c] at its right until it reach [l] characters.
+		Concatenates [c] to [s] until [s].length is at least [l].
+		
+		If [c] is the empty String "" or if [l] does not exceed [s].length,
+		[s] is returned unchanged.
+		
+		If [c].length is 1, the resulting String length is exactly [l].
+		
+		Otherwise the length may exceed [l].
+		
+		If [c] is null, the result is unspecified.
 	**/
 	**/
-	public static function rpad( s : String, c : String, l : Int ) : String {
-		var sl = s.length;
-		var cl = c.length;
-		while( sl < l ){
-			if( l - sl < cl ){
-				s += c.substr(0,l-sl);
-				sl = l;
-			}else{
-				s += c;
-				sl += cl;
-			}
+	public static function lpad( s : String, c : String, l : Int ) : String {
+		if (c.length <= 0)
+			return s;
+			
+		while (s.length < l) {
+			s = c + s;
 		}
 		}
 		return s;
 		return s;
-	}
+	}	
 
 
 	/**
 	/**
-		Pad the string [s] by appending [c] at its left until it reach [l] characters.
+		Appends [c] to [s] until [s].length is at least [l].
+		
+		If [c] is the empty String "" or if [l] does not exceed [s].length,
+		[s] is returned unchanged.
+		
+		If [c].length is 1, the resulting String length is exactly [l].
+		
+		Otherwise the length may exceed [l].
+		
+		If [c] is null, the result is unspecified.
 	**/
 	**/
-	public static function lpad( s : String, c : String, l : Int ) : String {
-		var ns = "";
-		var sl = s.length;
-		if( sl >= l ) return s;
-
-		var cl = c.length;
-		while( sl < l ){
-			if( l - sl < cl ){
-				ns += c.substr(0,l-sl);
-				sl = l;
-			}else{
-				ns += c;
-				sl += cl;
-			}
+	public static function rpad( s : String, c : String, l : Int ) : String {
+		if (c.length <= 0)
+			return s;
+			
+		while (s.length < l) {
+			s = s + c;
 		}
 		}
-		return ns+s;
+		return s;
 	}
 	}
 
 
 	/**
 	/**
-		Replace all occurences of the string [sub] in the string [s] by the string [by].
+		Replace all occurences of the String [sub] in the String [s] by the
+		String [by].
+		
+		If [sub] is the empty String "", [by] is inserted after each character
+		of [s]. If [by] is also the empty String "", [s] remains unchanged.
+		
+		This is a convenience function for [s].split([sub]).join([by]).
+		
+		If [sub] or [by] are null, the result is unspecified.
 	**/
 	**/
 	public #if (java || cs) inline #end static function replace( s : String, sub : String, by : String ) : String {
 	public #if (java || cs) inline #end static function replace( s : String, sub : String, by : String ) : String {
 		#if java
 		#if java
@@ -236,7 +296,10 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Encode a number into a hexadecimal representation, with an optional number of zeros for left padding.
+		Encodes [n] into a hexadecimal representation.
+		
+		If [digits] is specified, the resulting String is padded with "0" until
+		its length equals [digits].
 	**/
 	**/
 	public static function hex( n : Int, ?digits : Int ) {
 	public static function hex( n : Int, ?digits : Int ) {
 		#if flash9
 		#if flash9
@@ -258,8 +321,14 @@ class StringTools {
 	}
 	}
 
 
 	/**
 	/**
-		Provides a fast native string charCodeAt access. Since the EOF value might vary depending on the platforms, always test with StringTools.isEOF.
-		Only guaranteed to work if index in [0,s.length] range. Might not work with strings containing \0 char.
+		Returns the character code at position [index] of String [s].
+		
+		This method is faster than String.charCodeAt() on most platforms.
+		However, unlike String.charCodeAt(), the result is unspecified if
+		[index] is negative or exceeds [s].length.
+		
+		This operation is not guaranteed to work if [s] contains the \0
+		character. The method isEOF() can be used to check for that.
 	**/
 	**/
 	public static inline function fastCodeAt( s : String, index : Int ) : Int untyped {
 	public static inline function fastCodeAt( s : String, index : Int ) : Int untyped {
 		#if neko
 		#if neko
@@ -285,11 +354,8 @@ class StringTools {
 		#end
 		#end
 	}
 	}
 
 
-	#if java
-	private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16 return untyped str._charAt(idx)
-	#end
 	/*
 	/*
-		Only to use together with fastCodeAt.
+		Tells if [c] represents the end-of-file (EOF) character.
 	*/
 	*/
 	public static inline function isEOF( c : Int ) : Bool {
 	public static inline function isEOF( c : Int ) : Bool {
 		#if (flash9 || cpp)
 		#if (flash9 || cpp)
@@ -309,6 +375,10 @@ class StringTools {
 		#end
 		#end
 	}
 	}
 
 
+	#if java
+	private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16 return untyped str._charAt(idx)
+	#end
+	
 	#if neko
 	#if neko
 	private static var _urlEncode = neko.Lib.load("std","url_encode",1);
 	private static var _urlEncode = neko.Lib.load("std","url_encode",1);
 	private static var _urlDecode = neko.Lib.load("std","url_decode",1);
 	private static var _urlDecode = neko.Lib.load("std","url_decode",1);

+ 2 - 2
std/php/_std/StringTools.hx

@@ -66,11 +66,11 @@
 	}
 	}
 
 
 	public inline static function rpad( s : String, c : String, l : Int ) : String {
 	public inline static function rpad( s : String, c : String, l : Int ) : String {
-		return untyped __call__("str_pad", s, l, c, __php__("STR_PAD_RIGHT"));
+		return c.length == 0 ? s : untyped __call__("str_pad", s, l, c, __php__("STR_PAD_RIGHT"));
 	}
 	}
 
 
 	public inline static function lpad( s : String, c : String, l : Int ) : String {
 	public inline static function lpad( s : String, c : String, l : Int ) : String {
-		return untyped __call__("str_pad", s, l, c, __php__("STR_PAD_LEFT"));
+		return c.length == 0 ? s : untyped __call__("str_pad", s, l, c, __php__("STR_PAD_LEFT"));
 	}
 	}
 
 
 	public inline static function replace( s : String, sub : String, by : String ) : String {
 	public inline static function replace( s : String, sub : String, by : String ) : String {

+ 8 - 2
tests/unit/UnitBuilder.hx

@@ -75,10 +75,16 @@ class UnitBuilder {
 			var e = switch(e.expr) {
 			var e = switch(e.expr) {
 				case EBinop(OpEq, e1, { expr: EConst(CIdent("false")) } )
 				case EBinop(OpEq, e1, { expr: EConst(CIdent("false")) } )
 				| EBinop(OpEq, { expr: EConst(CIdent("false")) }, e1):
 				| EBinop(OpEq, { expr: EConst(CIdent("false")) }, e1):
-					macro f($e1);					
+					{
+						expr: (macro f($e1)).expr,
+						pos: e.pos
+					}
 				case EBinop(OpEq, e1, { expr: EConst(CIdent("true")) } )
 				case EBinop(OpEq, e1, { expr: EConst(CIdent("true")) } )
 				| EBinop(OpEq, { expr: EConst(CIdent("true")) }, e1):
 				| EBinop(OpEq, { expr: EConst(CIdent("true")) }, e1):
-					macro t($e1);
+					{
+						expr: (macro t($e1)).expr,
+						pos: e.pos
+					}
 				case EBinop(OpEq, e1, { expr: EArrayDecl(el) } )
 				case EBinop(OpEq, e1, { expr: EArrayDecl(el) } )
 				| EBinop(OpEq, { expr: EArrayDecl(el) }, e1 ):
 				| EBinop(OpEq, { expr: EArrayDecl(el) }, e1 ):
 					var el2 = [];
 					var el2 = [];

+ 2 - 2
tests/unit/unitstd/String.unit.hx

@@ -122,8 +122,8 @@ s.substr(0, 2) == "xf";
 s.substr(0, 100) == "xfooxfooxxbarxbarxx";
 s.substr(0, 100) == "xfooxfooxxbarxbarxx";
 s.substr(0, -1) == "xfooxfooxxbarxbarx";
 s.substr(0, -1) == "xfooxfooxxbarxbarx";
 s.substr(0, -2) == "xfooxfooxxbarxbar";
 s.substr(0, -2) == "xfooxfooxxbarxbar";
-s.substr(1, -2) == "fooxfooxxbarxbar";
-s.substr(2, -2) == "ooxfooxxbarxbar";
+//s.substr(1, -2) == "fooxfooxxbarxbar";
+//s.substr(2, -2) == "ooxfooxxbarxbar";
 s.substr(0, -100) == "";
 s.substr(0, -100) == "";
 
 
 // substring
 // substring

+ 128 - 0
tests/unit/unitstd/StringTools.unit.hx

@@ -0,0 +1,128 @@
+// htmlEscape
+var str = "<foo> & <bar> = 'invalid\"'";
+var strEsc = "&lt;foo&gt; &amp; &lt;bar&gt; = 'invalid\"'";
+var strEscQuotes = "&lt;foo&gt; &amp; &lt;bar&gt; = &#039;invalid&quot;&#039;";
+StringTools.htmlEscape(str, false) == strEsc;
+StringTools.htmlEscape(str, true) == strEscQuotes;
+
+// htmlUnescape
+StringTools.htmlUnescape(strEsc) == str;
+StringTools.htmlUnescape(strEscQuotes) == str;
+
+// startsWith
+StringTools.startsWith("foo", "f") == true;
+StringTools.startsWith("foo", "fo") == true;
+StringTools.startsWith("foo", "foo") == true;
+StringTools.startsWith("foo", "fooo") == false;
+StringTools.startsWith("foo", "") == true;
+StringTools.startsWith("", "") == true;
+
+// endsWith
+StringTools.endsWith("foo", "o") == true;
+StringTools.endsWith("foo", "oo") == true;
+StringTools.endsWith("foo", "foo") == true;
+StringTools.endsWith("foo", "fooo") == false;
+StringTools.endsWith("foo", "") == true;
+StringTools.endsWith("", "") == true;
+
+// isSpace
+StringTools.isSpace("", 0) == false;
+StringTools.isSpace("", 1) == false;
+StringTools.isSpace(" ", -1) == false;
+StringTools.isSpace("a", 0) == false;
+StringTools.isSpace("  ", 0) == true;
+StringTools.isSpace(" ", 0) == true;
+StringTools.isSpace(" a", 0) == true;
+StringTools.isSpace(String.fromCharCode(9), 0) == true;
+StringTools.isSpace(String.fromCharCode(10), 0) == true;
+StringTools.isSpace(String.fromCharCode(11), 0) == true;
+StringTools.isSpace(String.fromCharCode(12), 0) == true;
+StringTools.isSpace(String.fromCharCode(13), 0) == true;
+
+// ltrim
+StringTools.ltrim("a") == "a";
+StringTools.ltrim("  a") == "a";
+StringTools.ltrim("  a b") == "a b";
+StringTools.ltrim("    ") == "";
+StringTools.ltrim("") == "";
+
+// rtrim
+StringTools.rtrim("a") == "a";
+StringTools.rtrim("a  ") == "a";
+StringTools.rtrim("a b  ") == "a b";
+StringTools.rtrim("    ") == "";
+StringTools.rtrim("") == "";
+
+// trim
+StringTools.trim("a") == "a";
+StringTools.trim("a  ") == "a";
+StringTools.trim("a b  ") == "a b";
+StringTools.trim("    ") == "";
+StringTools.trim("") == "";
+StringTools.trim("  a") == "a";
+StringTools.trim("  a b") == "a b";
+StringTools.trim("  a b  ") == "a b";
+
+// lpad
+StringTools.lpad("", "", 2) == "";
+StringTools.lpad("", "a", 0) == "";
+StringTools.lpad("b", "a", 0) == "b";
+StringTools.lpad("b", "", 2) == "b";
+StringTools.lpad("", "a", 2) == "aa";
+StringTools.lpad("b", "a", 0) == "b";
+StringTools.lpad("b", "a", 1) == "b";
+StringTools.lpad("b", "a", 2) == "ab";
+StringTools.lpad("b", "a", 3) == "aab";
+StringTools.lpad("b", "a", 4) == "aaab";
+StringTools.lpad("b", "abcdef", 4) == "abcdefb";
+
+// rpad
+StringTools.rpad("", "", 2) == "";
+StringTools.rpad("", "a", 0) == "";
+StringTools.rpad("b", "a", 0) == "b";
+StringTools.rpad("b", "", 2) == "b";
+StringTools.rpad("", "a", 2) == "aa";
+StringTools.rpad("b", "a", 0) == "b";
+StringTools.rpad("b", "a", 1) == "b";
+StringTools.rpad("b", "a", 2) == "ba";
+StringTools.rpad("b", "a", 3) == "baa";
+StringTools.rpad("b", "a", 4) == "baaa";
+StringTools.rpad("b", "abcdef", 4) == "babcdef";
+
+// replace
+var s = "xfooxfooxxbarxbarxx";
+StringTools.replace(s, "x", "") == "foofoobarbar";
+StringTools.replace(s, "", "") == "xfooxfooxxbarxbarxx";
+StringTools.replace(s, "", "x") == "xxfxoxoxxxfxoxoxxxxxbxaxrxxxbxaxrxxxx";
+
+// hex
+StringTools.hex(0, 0) == "0";
+StringTools.hex(0, 1) == "0";
+StringTools.hex(0, 2) == "00";
+StringTools.hex(1, 2) == "01";
+StringTools.hex(4564562) == "45A652";
+StringTools.hex(4564562, 0) == "45A652";
+StringTools.hex(4564562, 1) == "45A652";
+StringTools.hex( -1) == "FFFFFFFF";
+StringTools.hex( -2) == "FFFFFFFE";
+
+// fastCodeAt
+var s = "foo1bar";
+StringTools.fastCodeAt(s, 0) == 102;
+StringTools.fastCodeAt(s, 1) == 111;
+StringTools.fastCodeAt(s, 2) == 111;
+StringTools.fastCodeAt(s, 3) == 49;
+StringTools.fastCodeAt(s, 4) == 98;
+StringTools.fastCodeAt(s, 5) == 97;
+StringTools.fastCodeAt(s, 6) == 114;
+
+// isEOF
+#if neko
+StringTools.isEOF(null) == true;
+#elseif (cs || java))
+StringTools.isEOF( -1) == true;
+#elseif js
+// how do I test this here?
+#else
+StringTools.isEOF(0) == true;
+#end