text: /*
   * File: strlib.h
   * --------------
   * This file exports several useful string functions that are not
   * included in the C++ string library.
   * 
   * @version 2016/08/03
   * - modified readGenericValue not to throw error() on parse failures
   *   (needed to support idiomatic silent-failing >> operators)
   * @version 2015/10/26
   * - added charToInteger/integerToChar functions
   * @version 2015/08/02
   * - added htmlEncode/Decode functions (not 100% perfect but works for common cases)
   * @version 2014/10/19
   * - alphabetized functions
   * - added several 'inPlace' variants of existing functions that return strings
   * @version 2014/10/08
   * - removed dependency on 'using namespace' statement
   */
  
  #ifndef _strlib_h
  #define _strlib_h
  
  #include <iostream>
  #include <sstream>
  #include <string>
  #include <vector>
  
  /*
   * Returns the string "true" if b is true, or "false" if b is false.
   */
  std::string boolToString(bool b);
  
  /*
   * Returns the string "true" if b is nonzero, or "false" if b is 0.
   */
  std::string boolToString(int b);
  
  /*
   * Returns an integer equivalent of the given numeric character.
   * For example, charToInteger('3') returns the int 3.
   * If the character is not '0' through '9', throws an error.
   */
  int charToInteger(char c);
  
  /*
   * Returns a single-character string containing the given character.
   * For example, charToString('Q') returns the string "Q".
   */
  std::string charToString(char c);
  
  /*
   * Function: endsWith
   * Usage: if (endsWith(str, suffix)) ...
   * -------------------------------------
   * Returns <code>true</code> if the string <code>str</code> ends with
   * the specified suffix, which may be either a string or a character.
   */
  bool endsWith(const std::string& str, const std::string& suffix);
  bool endsWith(const std::string& str, char suffix);
  
  /*
   * Function: equalsIgnoreCase
   * Usage: if (equalsIgnoreCase(s1, s2)) ...
   * ----------------------------------------
   * Returns <code>true</code> if <code>s1</code> and <code>s2</code> are
   * equal discounting differences in case.
   */
  bool equalsIgnoreCase(const std::string& s1, const std::string& s2);
  
  /*
   * Function: htmlDecode
   * Usage: string s = htmlDecode(s2);
   * ---------------------------------
   * Converts the given string from an HTML-encoded version to its decoded
   * equivalent; the opposite of htmlEncode. Any escaped HTML character entities
   * will be replaced by their unescaped equivalents.
   * For example, <code>htmlEncode("&lt;p class=&quot;abc&quot;&gt;I love you &amp; me&lt;/p&gt;")
   * returns "<p class=\"abc\">I love you & me</p>".
   */
  std::string htmlDecode(const std::string& s);
  
  /*
   * Function: htmlEncode
   * Usage: string s = htmlEncode(s2);
   * ---------------------------------
   * Converts the given string into an HTML-encoded equivalent version, with
   * any relevant HTML character entities replaced by escaped equivalents.
   * For example, <code>htmlEncode("<p class=\"abc\">I love you & me</p>") returns
   * "&lt;p class=&quot;abc&quot;&gt;I love you &amp; me&lt;/p&gt;".
   */
  std::string htmlEncode(const std::string& s);
  
  /*
   * Function: integerToChar
   * Usage: char ch = integerToChar(n);
   * ----------------------------------
   * Converts an integer into the corresponding numeric character.
   * For example, calling <code>integerToChar(3)</code> returns
   * the char <code>'3'</code>.
   * If the given integer is not a single-digit number from 0-9 inclusive,
   * throws an error.
   */
  char integerToChar(int n);
  
  /*
   * Function: integerToString
   * Usage: string s = integerToString(n);
   * -------------------------------------
   * Converts an integer into the corresponding string of digits.
   * For example, calling <code>integerToString(123)</code> returns
   * the string <code>"123"</code>.
   */
  std::string integerToString(int n);
  
  /*
   * Function: longToString
   * Usage: string s = longToString(n);
   * ----------------------------------
   * Converts an integer into the corresponding string of digits.
   * For example, calling <code>longToString(123)</code> returns
   * the string <code>"123"</code>.
   */
  std::string longToString(long n);
  
  /*
   * Function: realToString
   * Usage: string s = realToString(d);
   * ----------------------------------
   * Converts a floating-point number into the corresponding string form.
   * For example, calling <code>realToString(23.45)</code> returns
   * the string <code>"23.45"</code>.
   */
  std::string realToString(double d);
  std::string doubleToString(double d);   // alias
  
  /*
   * Function: startsWith
   * Usage: if (startsWith(str, prefix)) ...
   * ---------------------------------------
   * Returns <code>true</code> if the string <code>str</code> starts with
   * the specified prefix, which may be either a string or a character.
   */
  bool startsWith(const std::string& str, char prefix);
  bool startsWith(const std::string& str, const std::string& prefix);
  
  /*
   * Returns true if the given substring occurs somewhere in s.
   */
  bool stringContains(const std::string& s, const std::string& substring);
  
  /*
   * Returns the index of the start of the first occurrence of the given substring
   * in s, if it occurs in s.  If it does not occur, returns -1.
   * This function is very similar to string.find, but find returns string::npos
   * when the string is not found.
   */
  int stringIndexOf(const std::string& s, const std::string& substring, int startIndex = 0);
  
  /*
   * Returns true if the given string is either "true" or "false".
   */
  bool stringIsBool(const std::string& str);
  
  /*
   * Returns true if the given string could be converted to an integer
   * successfully by the stringToInteger function, which will be true if
   * the string has the format of an integer such as "1234" or "-8".
   * Optionally accepts a radix (base) parameter if base-10 is not desired.
   */
  bool stringIsInteger(const std::string& str, int radix = 10);
  
  /*
   * Returns true if the given string could be converted to a long
   * successfully by the stringToLong function, which will be true if
   * the string has the format of an integer such as "1234" or "-8".
   * Optionally accepts a radix (base) parameter if base-10 is not desired.
   */
  bool stringIsLong(const std::string& str, int radix = 10);
  
  /*
   * Returns true if the given string could be converted to an real number
   * successfully by the stringToReal function, which will be true if
   * the string has the format of a real number such as "3.14" or "-46".
   */
  bool stringIsReal(const std::string& str);
  bool stringIsDouble(const std::string& str);   // alias
  
  /*
   * Combines the elements of the given STL vector into a single string,
   * with the given delimiter separating neighboring elements, and returns it.
   * For example, joining the elements of the vector
   * {"Hi", "there", "", "Jim"} with the delimiter "?" returns "Hi?there??Jim".
   */
  std::string stringJoin(const std::vector<std::string>& v, const std::string& delimiter = "\n");
  
  /*
   * Returns the index of the start of the last occurrence of the given substring
   * in s, if it occurs in s.  If it does not occur, returns -1.
   * This function is very similar to string.rfind, but rfind returns string::npos
   * when the string is not found.
   */
  int stringLastIndexOf(const std::string& s, const std::string& substring, int startIndex = 0);
  
  /*
   * Returns a new string formed by replacing any occurrences of the given 'old'
   * text with the given replacement text in 'str'.
   * Note that this is NOT a regular expression replacement; it looks for the
   * 'old' string literally.  If you want regular expressions, see regexpr.h.
   * The 'inPlace' variant modifies an existing string rather than returning a new one,
   * and returns the number of occurrences of 'old' were replaced.
   */
  std::string stringReplace(const std::string& str, const std::string& old, const std::string& replacement, int limit = -1);
  int stringReplaceInPlace(std::string& str, const std::string& old, const std::string& replacement, int limit = -1);
  
  /*
   * Returns an STL vector whose elements are strings formed by splitting the
   * given string 'str' by the given separator.
   * For example, splitting "Hi there  Jim!" on " " returns
   * {"Hi", "there", "", "Jim!"}.
   */
  std::vector<std::string> stringSplit(const std::string& str, const std::string& delimiter, int limit = -1);
  
  /*
   * If str is "true", returns the bool value true.
   * If str is "false", returns the bool value false.
   * If str is any other value, this function throws an error.
   */
  bool stringToBool(const std::string& str);
  
  /*
   * Converts a single-character string into its corresponding char value.
   * For example, stringToChar("hello") returns the char 'h'.
   * Throws an error if the given string does not contain exactly 1 character.
   */
  char stringToChar(const std::string& str);
  
  /*
   * Function: stringToInteger
   * Usage: int n = stringToInteger(str);
   * ------------------------------------
   * Converts a string of digits into an integer.  If the string is not a
   * legal integer or contains extraneous characters other than whitespace,
   * <code>stringToInteger</code> calls <code>error</code> with an
   * appropriate message.
   * The function accepts an optional radix (base); for example,
   * stringToInteger("234", 16) assumes that the string is in base-16 and
   * returns 2*16*16 + 3*16 + 4 = 564.
   */
  int stringToInteger(const std::string& str, int radix = 10);
  
  /*
   * Function: stringToLong
   * Usage: long n = stringToLong(str);
   * ----------------------------------
   * Converts a string of digits into a long.  If the string is not a
   * legal long or contains extraneous characters other than whitespace,
   * <code>stringToLong</code> calls <code>error</code> with an
   * appropriate message.
   * The function accepts an optional radix (base); for example,
   * stringToLong("234", 16) assumes that the string is in base-16 and
   * returns 2*16*16 + 3*16 + 4 = 564.
   */
  long stringToLong(const std::string& str, int radix = 10);
  
  /*
   * Function: stringToReal
   * Usage: double d = stringToReal(str);
   * ------------------------------------
   * Converts a string representing a real number into its corresponding
   * value.  If the string is not a legal floating-point number or contains
   * extraneous characters other than whitespace, <code>stringToReal</code>
   * calls <code>error</code> with an appropriate message.
   */
  double stringToReal(const std::string& str);
  double stringToDouble(const std::string& str);   // alias
  
  /*
   * Function: toLowerCase
   * Usage: string s = toLowerCase(str);
   * -----------------------------------
   * Returns a new string in which all uppercase characters have been converted
   * into their lowercase equivalents.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string toLowerCase(const std::string& str);
  void toLowerCaseInPlace(std::string& str);
  
  /*
   * Function: toUpperCase
   * Usage: string s = toUpperCase(str);
   * -----------------------------------
   * Returns a new string in which all lowercase characters have been converted
   * into their uppercase equivalents.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string toUpperCase(const std::string& str);
  void toUpperCaseInPlace(std::string& str);
  
  /*
   * Function: trim
   * Usage: string trimmed = trim(str);
   * ----------------------------------
   * Returns a new string after removing any whitespace characters
   * from the beginning and end of the argument.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string trim(const std::string& str);
  void trimInPlace(std::string& str);
  
  /*
   * Function: trimEnd
   * Usage: string trimmed = trimEnd(str);
   * -------------------------------------
   * Returns a new string after removing any whitespace characters
   * from the end of the argument.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string trimEnd(const std::string& str);
  void trimEndInPlace(std::string& str);
  
  /*
   * Function: trimStart
   * Usage: string trimmed = trimStart(str);
   * ---------------------------------------
   * Returns a new string after removing any whitespace characters
   * from the beginning of the argument.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string trimStart(const std::string& str);
  void trimStartInPlace(std::string& str);
  
  /*
   * Returns a URL-decoded version of the given string, where any %xx character
   * codes are converted back to the equivalent characters.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string urlDecode(const std::string& str);
  void urlDecodeInPlace(std::string& str);
  
  /*
   * Returns a URL-encoded version of the given string, where most non-
   * alphabetic characters are replaced by %xx character codes.
   * The 'inPlace' version modifies an existing string rather than returning a new one.
   */
  std::string urlEncode(const std::string& str);
  void urlEncodeInPlace(std::string& str);
  
  /* Private section */
  
  /**********************************************************************/
  /* Note: Everything below this point in the file is logically part    */
  /* of the implementation and should not be of interest to clients.    */
  /**********************************************************************/
  
  /*
   * Friend function: readQuotedString
   * Usage: readQuotedString(infile, str);
   * -------------------------------------
   * Reads the next string from infile into the reference parameter str.
   * If the first character (other than whitespace) is either a single
   * or a double quote, this function reads characters up to the
   * matching quote, processing standard escape sequences as it goes.
   * If not, readString reads characters up to any of the characters
   * in the string STRING_DELIMITERS in the implementation file.
   */
  bool readQuotedString(std::istream& is, std::string& str, bool throwOnError = true);
  
  /*
   * Friend function: writeQuotedString
   * Usage: writeQuotedString(outfile, str, forceQuotes);
   * ----------------------------------------------------
   * Writes the string str to outfile surrounded by double quotes, converting
   * special characters to escape sequences, as necessary.  If the optional
   * parameter forceQuotes is explicitly set to false, quotes are included
   * in the output only if necessary.
   */
  void writeQuotedString(std::ostream& os, const std::string& str,
                         bool forceQuotes = true);
  
  /*
   * Friend function: stringNeedsQuoting
   * Usage: if (stringNeedsQuoting(str)) ...
   * ---------------------------------------
   * Checks whether the string needs quoting in order to be read correctly.
   */
  bool stringNeedsQuoting(const std::string& str);
  
  /*
   * Friend function: writeGenericValue
   * Usage: writeGenericValue(os, value, forceQuotes);
   * -------------------------------------------------
   * Writes a generic value to the output stream.  If that value is a string,
   * this function uses writeQuotedString to write the value.
   */
  template <typename ValueType>
  void writeGenericValue(std::ostream& os, const ValueType& value, bool) {
      os << value;
  }
  
  template <>
  inline void writeGenericValue(std::ostream& os, const std::string& value,
                                bool forceQuotes) {
      writeQuotedString(os, value, forceQuotes);
  }
  
  template <typename ValueType>
  inline std::string genericValueToString(const ValueType& value,
                                          bool forceQuotes = false) {
      std::ostringstream os;
      writeGenericValue(os, value, forceQuotes);
      return os.str();
  }
  
  template <>
  inline std::string genericValueToString(const std::string& value,
                                          bool forceQuotes) {
      std::ostringstream os;
      writeQuotedString(os, value, forceQuotes);
      return os.str();
  }
  
  /*
   * Friend function: readGenericValue
   * Usage: readGenericValue(is, value);
   * -----------------------------------
   * Reads a generic value from the input stream.  If that value is a string,
   * this function uses readQuotedString to read the value.
   */
  template <typename ValueType>
  bool readGenericValue(std::istream& is, ValueType& value) {
      return (bool) (is >> value);
  }
  
  template <>
  inline bool readGenericValue(std::istream& is, std::string& value) {
      return readQuotedString(is, value, /* throwOnError */ false);
  }
  
  #endif
  