//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   4 Dec 05  Brian Frank  Creation
//

**
** Str represents a sequence of Unicode characters.
**
const final class Str
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Private constructor.
  **
  private new privateMake()

  **
  ** Default value is "".
  **
  const static Str defVal

  **
  ** Construct a string from a list of unicode code points.
  ** Also see `chars`.
  **
  static Str fromChars(Int[] chars)

//////////////////////////////////////////////////////////////////////////
// Obj Overrides
//////////////////////////////////////////////////////////////////////////

  **
  ** Return true if a Str with exact same char sequence.
  **
  override Bool equals(Obj? obj)

  **
  ** Convenience for 'compareIgnoreCase(s) == 0'.
  ** Only ASCII character case is taken into account.
  ** See `localeCompare` for localized case insensitive
  ** comparisions.
  **
  Bool equalsIgnoreCase(Str s)

  **
  ** Compare based on Unicode character values.  Case is not
  ** taken into account - also see `compareIgnoreCase`
  ** and `localeCompare`.
  **
  ** Examples:
  **   "a".compare("b")    =>  -1
  **   "hi".compare("hi")  =>  0
  **   "hi".compare("HI")  =>  1
  **   "b".compare("a")    =>  1
  **
  override Int compare(Obj obj)

  **
  ** Compare two strings without regard to case and return -1, 0, or 1
  ** if this string is less than, equal to, or greater than the specified
  ** string.  Only ASCII character case is taken into account.
  ** See `localeCompare` for localized case insensitive comparisions.
  **
  ** Examples:
  **   "a".compareIgnoreCase("b")    =>  -1
  **   "hi".compareIgnoreCase("HI")  =>  0
  **   "b".compareIgnoreCase("a")    =>  1
  **
  Int compareIgnoreCase(Str s)

  **
  ** The hash for a Str is platform dependent.
  **
  override Int hash()

  **
  ** Return this.
  **
  override Str toStr()

  **
  ** Return this.  This method is used to enable 'toLocale' to
  ** be used with duck typing across most built-in types.
  **
  Str toLocale()

//////////////////////////////////////////////////////////////////////////
// Identity
//////////////////////////////////////////////////////////////////////////

  **
  ** Return if 'size() == 0'.
  **
  Bool isEmpty()

  **
  ** Return number of characters in this string.
  **
  Int size()

  **
  ** Internalize this Str such that two strings which are equal
  ** via the '==' operator will have the same reference such that
  ** '===' will be true.
  **
  Str intern()

  **
  ** Return if this Str starts with the specified Str.
  **
  Bool startsWith(Str s)

  **
  ** Return if this Str ends with the specified Str.
  **
  Bool endsWith(Str s)

  **
  ** Return the first occurrence of the specified substring searching
  ** forward, starting at the specified offset index.  A negative offset
  ** may be used to access from the end of string.  Return null if no
  ** occurrences are found.
  **
  ** Examples:
  **   "abcabc".index("b")     => 1
  **   "abcabc".index("b", 1)  => 1
  **   "abcabc".index("b", 3)  => 4
  **   "abcabc".index("b", -3) => 4
  **   "abcabc".index("x")     => null
  **
  Int? index(Str s, Int offset := 0)

  **
  ** Reverse index - return the first occurrence of the specified
  ** substring searching backward, starting at the specified offset
  ** index.  A negative offset may be used to access from the end
  ** of string.  Return null if no occurrences are found.
  **
  ** Examples:
  **   "abcabc".indexr("b")     => 4
  **   "abcabc".indexr("b", -3) => 1
  **   "abcabc".indexr("b", 0)  => null
  **
  Int? indexr(Str s, Int offset := -1)

  **
  ** Find the index just like `index`, but ignoring case for
  ** ASCII chars only.
  **
  Int? indexIgnoreCase(Str s, Int offset := 0)

  **
  ** Find the index just like `indexr`, but ignoring case for
  ** ASCII chars only.
  **
  Int? indexrIgnoreCase(Str s, Int offset := -1)

  **
  ** Return if this string contains the specified string.
  ** Convenience for index(s) != null
  **
  Bool contains(Str s)

  **
  ** Return if this string contains the specified character.
  **
  Bool containsChar(Int ch)

//////////////////////////////////////////////////////////////////////////
// Operators
//////////////////////////////////////////////////////////////////////////

  **
  ** Get the character at the zero based index as a Unicode code point.
  ** Negative indexes may be used to access from the end of the string.
  ** This method is accessed via the [] operator.  Throw IndexErr if the
  ** index is out of range.
  **
  @Operator Int get(Int index)

  **
  ** Get the character at the zero based index as a Unicode code point.
  ** Negative indexes may be used to access from the end of the string.
  ** Unlike `get`, this method does not throw IndexErr when the index
  ** is out or range, instead it returns 'def'.
  **
  Int getSafe(Int index, Int def := 0)

  **
  ** Return a substring based on the specified range.  Negative indexes
  ** may be used to access from the end of the string.  This method
  ** is accessed via the [] operator.  Throw IndexErr if range illegal.
  **
  ** Examples:
  **   "abcd"[0..2]   => "abc"
  **   "abcd"[3..3]   => "d"
  **   "abcd"[-2..-1] => "cd"
  **   "abcd"[0..<2]  => "ab"
  **   "abcd"[1..-2]  => "bc"
  **   "abcd"[4..-1]  => ""
  **
  @Operator Str getRange(Range range)

  **
  ** Concat the value of obj.toStr
  **
  @Operator Str plus(Obj? obj)

  **
  ** Duplicate this string the given number of times.  Any integer
  ** less than or equal to zero returns an empty string.
  **
  ** Examples:
  **   "x" * 4  =>  "xxxx"
  **
  @Operator Str mult(Int times)

//////////////////////////////////////////////////////////////////////////
// Iterators
//////////////////////////////////////////////////////////////////////////

  **
  ** Get the characters in this string as a list of integer code points.
  ** Also see `fromChars`.
  **
  Int[] chars()

  **
  ** Call the specified function for every char in the string
  ** with index 0 and incrementing up to size-1.
  **
  ** Example:
  **   "abc".each |Int c| { echo(c.toChar) }
  **
  Void each(|Int ch, Int index| c)

  **
  ** Reverse each - call the specified function for every char in
  ** the string starting with index size-1 and decrementing down
  ** to 0.
  **
  ** Example:
  **   "abc".eachr |Int c| { echo(c.toChar) }
  **
  Void eachr(|Int ch, Int index| c)

  **
  ** Iterate each character in the string until the function returns
  ** non-null.  If function returns non-null, then break the iteration
  ** and return the resulting object.  Return null if the function
  ** returns null for every item.
  **
  Obj? eachWhile(|Int ch, Int index->Obj?| c)

  **
  ** Return true if c returns true for any of the characters in
  ** this string.  If this string is empty, return false.
  **
  ** Example:
  **   "Foo".any |c| { c.isUpper } => true
  **   "foo".any |c| { c.isUpper } => false
  **
  Bool any(|Int ch, Int index->Bool| c)

  **
  ** Return true if c returns true for all of the characters in
  ** this string.  If this string is empty, return true.
  **
  ** Example:
  **   "Bar".all |c| { c.isUpper } => false
  **   "BAR".all |c| { c.isUpper } => true
  **
  Bool all(|Int ch, Int index->Bool| c)

//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////

  **
  ** Get a Str containing the specified number of spaces.  Also
  ** see `justl` and `justr` to justify an existing string.
  **
  ** Examples:
  **   Str.spaces(1)  =>  " "
  **   Str.spaces(2)  =>  "  "
  **
  static Str spaces(Int n)

  **
  ** Return this string with all uppercase characters replaced
  ** to lowercase.  The case conversion is for ASCII only.
  ** Also see `upper`, `localeLower`, `Int.lower`, `Int.localeLower`.
  **
  ** Example:
  **   "Apple".lower => "apple"
  **
  Str lower()

  **
  ** Return this string with all lowercase characters replaced
  ** to uppercase.  The case conversion is for ASCII only.
  ** Also see `lower`, `localeUpper`, `Int.upper`, `Int.localeUpper`.
  **
  ** Example:
  **   "Foo Bar".upper => "FOO BAR"
  **
  Str upper()

  **
  ** Return this string with the first character converted
  ** uppercase.  The case conversion is for ASCII only.
  ** Also see `decapitalize` and `localeCapitalize`.
  **
  ** Example:
  **   "foo".capitalize => "Foo"
  **
  Str capitalize()

  **
  ** Return this string with the first character converted
  ** lowercase.  The case conversion is for ASCII only.
  ** Also see `capitalize` and `localeDecapitalize`.
  **
  ** Example:
  **   "Foo".decapitalize => "foo"
  **
  Str decapitalize()

  **
  ** Translate a programmer name like "fooBar" to "Foo Bar".
  ** This method capitalizes the first letter, then walks
  ** the string looking for ASCII capital letters and inserting
  ** a space.  Any underbars are replaced with a space.  Also
  ** see `fromDisplayName`.
  **
  ** Examples:
  **   "foo".toDisplayName       ->  "Foo
  **   "fooBar".toDisplayName    ->  "Foo Bar"
  **   "fooBarBaz".toDisplayName ->  "Foo Bar Baz"
  **   "foo33".toDisplayName     ->  "Foo 33"
  **   "fooXML".toDisplayName    ->  "Foo XML"
  **   "Foo".toDisplayName       ->  "Foo"
  **   "foo_bar".toDisplayName   ->  "Foo Bar"
  **
  Str toDisplayName()

  **
  ** Translate a display name like "Foo Bar" to a programmatic
  ** name "fooBar".  This method decapitalizes the first letter,
  ** then walks the string removing spaces.  Also see `toDisplayName`.
  **
  ** Examples:
  **   "Foo".fromDisplayName         ->  "foo"
  **   "Foo Bar".fromDisplayName     ->  "fooBar"
  **   "Foo Bar Baz".fromDisplayName ->  "fooBarBaz"
  **   "Foo 33 Bar".fromDisplayName  ->  "foo33Bar"
  **   "Foo XML".fromDisplayName     ->  "fooXML"
  **   "foo bar".fromDisplayName     ->  "fooBar"
  **
  Str fromDisplayName()

  **
  ** If size is less than width, then add spaces to the right
  ** to create a left justified string.  Also see `padr`.
  **
  ** Examples:
  **   "xyz".justl(2) => "xyz"
  **   "xyz".justl(4) => "xyz "
  **
  Str justl(Int width)

  **
  ** If size is less than width, then add spaces to the left
  ** to create a right justified string.  Also see `padl`.
  **
  ** Examples:
  **   "xyz".justr(2) => "xyz"
  **   "xyz".justr(4) => " xyz"
  **
  Str justr(Int width)

  **
  ** If size is less than width, then add the given char to the
  ** left to achieve the specified width.  Also see `justr`.
  **
  ** Examples:
  **   "3".padl(3, '0') => "003"
  **   "123".padl(2, '0') => "123"
  **
  Str padl(Int width, Int char := ' ')

  **
  ** If size is less than width, then add the given char to
  ** the left to achieve the specified width.  Also see `justl`.
  **
  ** Examples:
  **   "xyz".padr(2, '.') => "xyz"
  **   "xyz".padr(5, '-') => "xyz--"
  **
  Str padr(Int width, Int char := ' ')

  **
  ** Reverse the contents of this string.
  **
  ** Example:
  **   "stressed".reverse => "desserts"
  **
  Str reverse()

  **
  ** Trim whitespace from the beginning and end of the string.  For the purposes
  ** of this method, whitespace is defined as any character equal to or less
  ** than the 0x20 space character (including ' ', '\r', '\n', and '\t').
  **
  ** Examples:
  **    "foo".trim      =>  "foo"
  **    "  foo".trim    =>  "foo"
  **    " foo ".trim    =>  "foo"
  **    "  foo\n".trim  =>  "foo"
  **    "   ".trim      =>  ""
  **
  Str trim()

  **
  ** Trim whitespace from the beginning and end of the string.
  ** Should the resultant string be empty, 'null' is returned.
  **
  ** For the purposes of this method, whitespace is defined as any character
  ** equal to or less than the 0x20 space character (including ' ', '\r', '\n',
  ** and '\t').
  **
  ** Examples:
  **    "foo".trimToNull      =>  "foo"
  **    "  foo  ".trimToNull  =>  "foo"
  **    "".trimToNull         =>  null
  **    "   ".trimToNull      =>  null
  **
  Str? trimToNull()

  **
  ** Trim whitespace only from the beginning of the string.
  ** See `trim` for definition of whitespace.
  **
  ** Examples:
  **    "foo".trim    =>  "foo"
  **    " foo ".trim  =>  "foo "
  **
  Str trimStart()

  **
  ** Trim whitespace only from the end of the string.
  ** See `trim` for definition of whitespace.
  **
  ** Examples:
  **    "foo".trim    =>  "foo"
  **    " foo ".trim  =>  " foo"
  **
  Str trimEnd()

  **
  ** Split a string into a list of substrings using the
  ** given separator character.  If there are contiguous separators,
  ** then they are split into empty strings.  If trim is true,
  ** then whitespace is trimmed from the beginning and end of
  ** the results.
  **
  ** If separator is null, then the string is split according
  ** to any sequence of whitespace characters (any character equal
  ** to or less than the 0x20 space character including ' ', '\r', '\n',
  ** and '\t').
  **
  ** If this is the empty string or there are no splits return a
  ** list of one item.
  **
  ** Examples:
  **   // split on whitespace
  **   "".split                   =>  [""]
  **   "x".split                  =>  ["x"]
  **   "x y".split                =>  ["x", "y"]
  **   " x y ".split              =>  ["x", "y"]
  **   " x \n y \n z ".split      =>  ["x", "y", "z"]
  **
  **   // split on sep with trim
  **   "".split('|')              =>  [""]
  **   "22".split(';')            =>  ["22"]
  **   "22;33".split(';')         =>  ["22","33"]
  **   "22, 33".split(',')        =>  ["22","33"]
  **   " 22 ; 33 ".split(';')     =>  ["22","33"]
  **
  **   // split on sep with no trim
  **   "22#33".split('#', false)  =>  ["22","33"]
  **   " x ; y".split(';', false) =>  [" x "," y"]
  **
  Str[] split(Int? separator := null, Bool trim := true)

  **
  ** Split this string into individual lines where lines are
  ** terminated by \n, \r\n, or \r.  The returned strings
  ** do not contain the newline character.
  **
  ** Examples:
  **   "x\ny".splitLines  => ["x", "y"]
  **   "".splitLines      => [""]
  **   "x".splitLines     => ["x"]
  **   "\r\n".splitLines  => ["", ""]
  **   "x\n".splitLines   => ["x", ""]
  **
  Str[] splitLines()

  **
  ** Replace all occurrences of 'from' with 'to'.
  **
  ** Examples:
  **   "hello".replace("hell", "t")  =>  "to"
  **   "aababa".replace("ab", "-")   =>  "a--a"
  **
  Str replace(Str from, Str to)

  **
  ** Count the number of newline combinations: "\n", "\r", or "\r\n".
  **
  Int numNewlines()

  **
  ** Return if every character in this Str is a US-ASCII character
  ** less than 128.
  **
  Bool isAscii()

  **
  ** Return if every character in this Str is whitespace: space \t \n \r \f
  **
  Bool isSpace()

  **
  ** Return if every character in this Str is ASCII uppercase: 'A'-'Z'.
  **
  Bool isUpper()

  **
  ** Return if every character in this Str is ASCII lowercase: 'a'-'z'.
  **
  Bool isLower()

  **
  ** Return if every char is an ASCII [letter]`Int.isAlpha`.
  **
  Bool isAlpha()

  **
  ** Return if every char is an ASCII [alpha-numeric]`Int.isAlphaNum`.
  **
  Bool isAlphaNum()

//////////////////////////////////////////////////////////////////////////
// Locale
//////////////////////////////////////////////////////////////////////////

  **
  ** Compare two strings without regard to case according to the
  ** current locale.  Return -1, 0, or 1 if this string is less
  ** than, equal to, or greater than the specified string.
  **
  ** Examples (assuming English locale):
  **   "a".localeCompare("b")   =>  -1
  **   "hi".localeCompare("HI") =>  0
  **   "b".localeCompare("A")   =>  1
  **
  Int localeCompare(Str s)

  **
  ** Return this string with all uppercase characters
  ** replaced to lowercase using the current locale.
  ** Also see `localeUpper`, `lower`, and `Int.localeLower`.
  **
  Str localeLower()

  **
  ** Return this string with all lowercase characters
  ** replaced to uppercase using the current locale.
  ** Also see `localeLower`, `upper`, and `Int.localeUpper`.
  **
  Str localeUpper()

  **
  ** Return this string with the first character
  ** converted to uppercase using the current locale.
  ** Also see `localeDecapitalize` and `capitalize`.
  **
  Str localeCapitalize()

  **
  ** Return this string with the first character
  ** converted to lowercase using the current locale.
  ** Also see `localeCapitalize` and `decapitalize`.
  **
  Str localeDecapitalize()

//////////////////////////////////////////////////////////////////////////
// Coersions
//////////////////////////////////////////////////////////////////////////

  **
  ** Convenience for `Bool.fromStr` using this string.
  **
  Bool? toBool(Bool checked := true)

  **
  ** Convenience for `Int.fromStr` using this string.
  **
  Int? toInt(Int radix := 10, Bool checked := true)

  **
  ** Convenience for `Float.fromStr` using this string.
  **
  Float? toFloat(Bool checked := true)

  **
  ** Convenience for `Decimal.fromStr` using this string.
  **
  Decimal? toDecimal(Bool checked := true)

  **
  ** Return this string as its Fantom source code and serialization
  ** representation surrounded by the specified quote character (which
  ** defaults to '"').  If quote is null then the return is unquoted.
  ** This method will backslash escape the following characters:
  ** '\n \r \f \t \\ $'.  If the quote character is the double quote,
  ** single quote, or backtick then it is escaped too.  Control chars
  ** less than 0x20 are escaped as '\uXXXX'.  If 'escapeUnicode' is
  ** true then any char over 0x7F is escaped as '\uXXXX'.
  **
  Str toCode(Int? quote := '"', Bool escapeUnicode := false)

  **
  ** Return this string as valid XML text.  The special control
  ** characters amp, lt, apos and quot are always escaped.  The
  ** gt char is escaped only if it is the first char or if preceded
  ** by the ']' char.  Also see `OutStream.writeXml` which is more
  ** efficient if streaming.
  **
  Str toXml()

  **
  ** Convenience for `Uri.fromStr` using this string.
  **
  Uri toUri()

  **
  ** Convenience for `Regex.fromStr` using this string.
  **
  Regex toRegex()

  **
  ** Create an input stream to read characters from this string.
  ** The input stream is designed only to read character data.  Attempts
  ** to perform binary reads will throw UnsupportedErr.
  **
  InStream in()

  **
  ** Get this string encoded into a buffer of bytes.
  **
  Buf toBuf(Charset charset := Charset.utf8)


}