/** @file

    $Id$

    $Date$
    $Author$

    @copy &copy; Marc W&auml;ckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

    $Log$
    Revision 1.7  2005/11/29 12:38:46  marc
     - make it compilable with gcc 4.0.2 and newer doxygen
     - added split and join

    Revision 1.6  2005/04/07 20:48:42  marc
    docu: new doxygen, new grouping

    Revision 1.5  2005/01/28 07:53:09  marc
    doc: hint regarding iomanip on string

    Revision 1.4  2004/12/20 13:24:08  marc
    from string conversion throws exception in case of failure

    Revision 1.3  2004/12/20 07:40:36  marc
    documentation improved, new grouping

    Revision 1.2  2004/12/16 13:09:47  marc
    inlines forgotten

    Revision 1.1  2004/10/07 09:31:30  marc
    new feature


*/
#ifndef __MRW__STRING__HPP__
#define __MRW__STRING__HPP__

#include <mrw/exception.hpp>
#include <string>
#include <sstream>
#include <list>

namespace mrw {
  
  /** @defgroup StdExt Extensions for C++ Standard Libraries

      @section stdextfeatures Features

      @subsection stdextstringfeatures Extensions to std::string

       - Shift operator to shift any kind of values into a string
         without the need for a stingstream.
       - Addition operators to add, means concatenate, any kind of
         value (e.g. integer) to a string without the need for a
         stringstream.
       - Function mrw::string to convert any type of variable to a
         string without the need for a stringstream.
       - Function mrw::to<> to convert a string to any type of
         variable without the need for a stringstream.

      @subsection stdextstlfeatures Extensions to STL containers

       - Shift operator to shift elements from and to all STL
         containers.

      @subsection stdextstreams Extensions for stream handling

       - Function mrw::getline to read a whole line from a stream (file)
         without the need of a buffer and without having to check
         whether the buffer was large enough.

      @section stdextmotivation Motivation
    
      There are some feature I am often missing in standard C++. They
      are relatively easy to obtain, but they could be even simpler. I
      am mainly a convinced C++ programmer, because I love
      simplicity. This means, to convert an integer to a string,
      something like this is not simple enough:

      @code
        int i;
        std::string s;
          [...]
        std::stringstream ss;
        ss<<"length is: "<<i<<"mm";
        ss>>s;
      @endcode

      Why can't it simply be:

      @code
        int i;
        std::string s;
          [...]
        s<<"length is: "<<i<<"mm";
      @endcode

      Or:

      @code
        int i;
        std::string s;
          [...]
        s += i; // convert i to string and append it
      @endcode

      Because we are using the great and powerful C++ language, it can
      be! That's why you need this module.

      In addition to the shift in and shift out operator for strings,
      you also get a shhift in and shift out operator for all STL
      container classes:

      @code
        std::list<int> l;
        l<<1<<2<<<3<<4;
        int i1, i2, i3, i4;
        l>>i1>>i2>>i3>>i4;
      @endcode

      And the possibility to add strings to integers and vice versa:
      
      @code
        std::string s("x");
        s = 1+s+2; // s=="1x2"
      @endcode

      @warning Please note that global namespace is polluted with some
      operators. If you don't want this, just don't include any of
      these include files files. There's no impact from this module,
      if you don't include a header, since all code is inline.

      @note The signature of some string functions has changed:
            They may now throw exceptions if conversion fails!
            (since 1.4.1)
  */
  //@{
  /** @defgroup stdextstring String extensions

      The string extensions give you a lot of new powerful operations:
       - convert anything to string:
         @code
         std::string s = mrw::string(15);
         @endcode
       - convert a string to something else:
         @code
         double d = mrw::to<double>("3.1415926535898");
         @endcode
       - shift values into a string:
         @code
         std::string s;
         s<<"length is: "<<i<<"mm";
         @endcode
       - read values from a string:
         @code
         std::string s("1 2 4 8");
         int i1, i2, i3, i4;
         s>>i1>>i2>>i3>>i4;
         @endcode
       - add all kind of integer and floating point numbers to a string:
         @code
         std::string s("hello");
         s += 4;
         s = 13.5 + s + 24.8;
         @endcode

      @note Please note that you can use the shift operator on
            strings, but the IO manipulators from @c iomanip don't
            work, nor @c std::endl and thelike.

      @note The signature of some string functions has changed:
            They may now throw exceptions if conversion fails!
            (since 1.4.1)
  */
  //@}

  /** @addtogroup stdextstring
      */
  //@{

  /** @brief convert any value to a std::string 

      @code
      std::string s = mrw::string(15);
      @endcode

      @param o a value to be converted to std::string
      @pre \#include <mrw/string.hpp>
      @pre T must support operator<< to a stream
  */
  template <typename T> std::string string(const T& o)
    throw(std::bad_exception) {
    std::stringstream ss;
    ss<<o;
    return ss.str();
  }

  /** @brief convert std::string to any value

      @code
      int i = mrw::to<int>("15");
      @endcode

      @throw mrw::invalid_argument if value can not be created from string
      @param s the string where a value of type @c T is extracted from
      @pre \#include <mrw/string.hpp>
      @pre T must support operator>> from a stream
      @pre operator>> from T must not throw anything else than std::exception

      @note The signature has changed:
            It may now throw exceptions if conversion fails!
            (since 1.4.1)
  */
  template <typename T> T to(const std::string& s) throw(std::exception) {
    T o;
    std::stringstream ss(s);
    if (!(ss>>o)) throw mrw::invalid_argument(s);
    return o;
  }

  /** @brief join a string from pieces

      @code
      std::list<std::string> l;
      l<<"hello"<<"world"; // needs mrw/list.hpp
      std::string hello_world(mrw::join(l));
      // hello_world is now "hello world"
      @endcode

      @param l the list of strings to join
      @param delimiter the delimiter between the joined strings
  */
  template<template<class STRING> class LIST>
    std::string join(const LIST<std::string>& l,
                     const std::string& delimiter=" ")
      throw(std::bad_exception) {
    std::string result;
    for (typename LIST<std::string>::const_iterator it(l.begin());
         it!=l.end(); ++it)
      result+=(result.size()?delimiter:"")+mrw::string(*it);
    return result;
  }

  /** @brief split a string into pieces

      @code
      std::string hello_world("hello world");
      std::list<std::string> l(mrw::split(hello_world));
      // l contains now "hello" and "world"
      @endcode

      @param text the text that has to be split into tokens
      @param greedy
        - @c true  don't generate empty tokens, if a delimiter is followed
                   by another delimiter, both are removed
        - @c false if several delimiters follow each other in the text,
                   eat them all and don't produce empty tokens
      @param delimiters a list of delimiters, each char in the string is a
                        delimiter
  */
  inline std::list<std::string> split(const std::string& text,
                                      bool greedy=true,
                                      const std::string& delimiters=" \n\t")
      throw(std::bad_exception) {
    std::list<std::string> res;
    for (std::string::size_type pos(0);
         pos<text.size(); ++pos) {
      std::string::size_type next(text.find_first_not_of(delimiters, pos));
      if (next!=pos && !greedy) {
        res.push_back("");
      } else {
        next = std::min(text.find_first_of(delimiters, pos), text.size());
        res.push_back(text.substr(pos, next-pos));
        pos = next;
      }
    }
    return res;
  }

  //@}

}

/** @addtogroup stdextstring
 */
//@{

/** @brief append any value to a string

    @code
    std::string s;
    s<<"length is: "<<i<<"mm";
    @endcode

    @param s the string, where o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
    @pre T must support operator<< to a stream
*/
template <typename T> std::string& operator<<(std::string& s, const T& o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief extract any value from a string

    @code
    std::string s1("length: 15 mm");
    string s2, s3;
    int i(0);
    s1>>s2>>is3;
    // now: s1=="" s2=="length:" i==15 s3=="mm"
    @endcode

    @throw mrw::invalid_argument if value can not be created from string
    @param s the string, from which o is extracted
    @param o the value to extract from s
    @note when something is extracted from a string, it is removed
    from the string, that means after every shift the string is
    shortened by the shifted element
    @pre \#include <mrw/string.hpp>
    @pre T must support operator>> from a stream

    @note The signature has changed:
          It may now throw exceptions if conversion fails!
          (since 1.4.1)
*/
template <typename T> std::string& operator>>(std::string& s, T& o)
  throw(std::exception) {
  std::stringstream ss(s);
  if (!(ss>>o)) throw mrw::invalid_argument(s);
  return (s=ss.tellg()>0?s.substr(ss.tellg()):"");
}

/** @brief add a @c unsigned short value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, unsigned short o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to a @c unsigned short value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(unsigned short o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}

/** @brief add an @c unsigned int value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, unsigned int o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to an @c unsigned int value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(unsigned int o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}

/** @brief add a @c unsigned long value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, unsigned long o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to a @c unsigned long value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(unsigned long o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}

/** @brief add a @c signed short value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, signed short o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to a @c signed short value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(signed short o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}

/** @brief add an @c signed int value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, signed int o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to an @c signed int value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(signed int o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}

/** @brief add a @c signed long value to a string

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(const std::string& s, signed long o)
  throw(std::bad_exception) {
  return s+mrw::string(o);
}

/** @brief append a string to a @c signed long value

    @code
    std::string s;
    s+"length is: "+i+"mm";
    @endcode

    @param s the string, where @c o is prepended
    @param o the value to prepend in front of @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string operator+(signed long o, const std::string& s)
  throw(std::bad_exception) {
  return mrw::string(o)+s;
}


/** @brief add a @c unsigned short value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, unsigned short o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief add an @c unsigned int value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, unsigned int o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief add a @c unsigned long value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, unsigned long o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief add a @c signed short value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, signed short o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief add an @c signed int value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, signed int o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

/** @brief add a @c signed long value to a string

    @code
    std::string s;
    s += o;
    @endcode

    @param s the string, where @c o is appended
    @param o the value to append to @c s
    @pre \#include <mrw/string.hpp>
*/
inline std::string& operator+=(std::string& s, signed long o)
  throw(std::bad_exception) {
  return s+=mrw::string(o);
}

//@}

#endif
