// -*- c++ -*-

/*
 * Jakelib2 - General purpose C++ library
 * Copyright (C) 2001 Florian Wolff  (florian@donuz.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: String.h,v 1.74 2003/09/13 09:58:01 florian Exp $
 */

#ifndef INCLUDE_JAKELIB2_LANG_STRING_H
# define INCLUDE_JAKELIB2_LANG_STRING_H

namespace jakelib {
  namespace util {
    class Hashtable;
  }

  namespace io {
    class Writer;
  }

  namespace lang {
    class StringBuffer;

    /**
     * @brief The class <tt>String</tt> represents an immutable array of characters
     * providing methods for examining 
     * individual characters of the sequence, for comparing strings, for 
     * searching strings, for extracting substrings, and for
     * converting strings to upper or lower case.
     *
     * \c String represents a sequence of 16-bit-wide unicode characters.
     * The characters are stored in an array of <tt>unsigned short</tt> values
     * (like in UTF-16). The UTF-16-surrogates are not yet supported, i.e.
     * only unicode characters from \c \u0000 through \c \uffff are allowed.
     *
     * @see http://www.unicode.org
     *
     * @class String
     * @version $Id: String.h,v 1.74 2003/09/13 09:58:01 florian Exp $
     * @author Florian 'Overflo' Wolff (florian@donuz.de)
     */
    class String : public Object, Comparable
    {
      friend class StringBuffer;
      friend class jakelib::io::Writer;

    public:
      /**
       * @brief Constructs a new string from a null-terminated array
       * of bytes ( <tt>char*</tt> ).
       *
       * @param str The C-style string
       */
      String(const jbyte* str);
  
      /**
       * @brief Constructs a new string from a given array of bytes ( <tt>jbyte*</tt> ).
       *
       * @param str The byte array
       * @param offset Offset of the first byte
       * @param len The length in bytes (without trailing \c '\\0' )
       */
      String(jbyte* str, jint offset, jint len);
  
      /**
       * @brief Constructs a new string from a given array of bytes ( <tt>jbyte*</tt> ).
       *
       * @param str The byte array
       * @param offset Offset of the first byte
       * @param len The length in bytes (without trailing \c '\\0' )
       * @param enc The name of the encoding
       */
      String(jbyte* str, jint offset, jint len, String* enc);
  
      /**
       * @brief Constructs a new string from a given array of bytes ( <tt>jbyte*</tt> ).
       *
       * @param str The byte array
       * @param offset Offset of the first byte
       * @param len The length in bytes (without trailing \c '\\0' )
       * @param enc The name of the encoding
       */
      String(jbyte* str, jint offset, jint len, char* enc);
  
      /**
       * @brief Constructs a new string from a null-terminated array
       * of bytes ( <tt>char*</tt> ).
       *
       * @param str The C-style string
       * @param enc The name of the encoding
       */
      String(jbyte* str, String* enc);
  
      /**
       * @brief Constructs a new string from a null-terminated array
       * of bytes ( <tt>char*</tt> ).
       *
       * @param str The C-style string
       * @param enc The name of the encoding
       */
      String(jbyte* str, char* enc);
  
      /**
       * @brief Constructs a new string from a given array of characters ( <tt>jchar*</tt> ).
       *
       * @param str The char array
       * @param offset Offset of the first character
       * @param len Length in characters
       * @param len The length in bytes (without trailing \c '\\0' )
       */
      String(jchar* str, jint offset, jint len);
  
      /**
       * @brief Constructs a new and empty string.
       */
      String();
  
      /**
       * @brief Guess what: This is just a lousy destructor!
       */
      ~String();
  
      /**
       * @brief Returns a hashcode for this string.
       *
       * The hashcode for a 
       * <code>String</code> object is computed as
       * <blockquote><pre> 
       * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
       * </pre></blockquote>
       * using <code>int</code> arithmetic, where <code>s[i]</code> is the 
       * <i>i</i>th character of the string, <code>n</code> is the length of 
       * the string, and <code>^</code> indicates exponentiation. 
       * (The hash value of the empty string is zero.)
       */
      int hashCode();
  
      /**
       * @brief Returns the length of this string.
       *
       * @return The length of the sequence of characters represented by this object.
       */
      inline jint length() { return len; }
  
      /**
       * @brief Return the string.
       */
      inline String* toString() { return this; }
      
      /**
       * @brief Compares this object to the specified object.
       * The result is <tt>true</tt> if and only if the argument is not
       * <tt>null</tt> and is a <tt>String</tt> object that contains
       * the same string value as this object.
       *
       * @param obj The object to compare with.
       * @return <tt>true</tt> if the objects are the same; <tt>false</tt> otherwise.
       */
      jboolean equals(Object* obj);
  
      /**
       * @brief Compares this string to the specified C-style string.
       * The result is <tt>true</tt> if and only if the argument is not
       * <tt>null</tt> and contains the same character sequence
       * as this string.
       *
       * @param str The C-style string to compare with.
       * @return <tt>true</tt> if the strings are equal; <tt>false</tt> otherwise.
       */
      jboolean equals(jbyte* str);
  
      /**
       * @brief Compares this object to the specified object.
       * The result is <tt>true</tt> if and only if the argument is not
       * <tt>null</tt> and is a <tt>String</tt> object that contains
       * the same string value as this object.
       *
       * @param str The \c String to compare with.
       * @return <tt>true</tt> if the strings are equal; <tt>false</tt> otherwise.
       */
      jboolean equalsIgnoreCase(String* str);
  
      /**
       * @brief Compares this string case-insensitively to the specified C-style string.
       * The result is <tt>true</tt> if and only if the argument is not
       * <tt>null</tt> and contains the same character sequence
       * as this string.
       *
       * @param str The C-style string to compare with.
       * @return <tt>true</tt> if the strings are equal; <tt>false</tt> otherwise.
       */
      jboolean equalsIgnoreCase(jbyte* str);
  
      /**
       * @brief Returns a copy of this string where some character substitutions
       * have been made. <BR>
       * <TT>&lt;nl&gt;</TT> =&gt; <TT>\n</TT><BR>
       * <TT>&lt;rt&gt;</TT> =&gt; <TT>\r</TT><BR>
       * <TT>\</TT> =&gt; <TT>\\</TT><BR>
       * <TT>&lt;tab&gt;</TT> =&gt; <TT>\t</TT><BR>
       * <TT>"</TT> =&gt; <TT>\"</TT><BR>
       */
      String* toCppString();
      String* parseCppString();

      /**
       * @brief Converts all characters in this string to lower case.
       *
       * @return A Reference to the string itself.
       */
      String* toLowerCase();

      /**
       * @brief Converts all characters in this string to upper case.
       *
       * @return A Reference to the string itself.
       */
      String* toUpperCase();
  
      /**
       * @brief Removes blanks from both ends of the string.
       */
      String* trim();
  
      /**
       * @brief Returns the integer value of the string.
       *
       * @return Integer value
       */
      jint toInt();
  
      /**
       * @brief Returns the long integer value of the string.
       *
       * @return Long value
       */
      jlong toLong();
  
      /**
       * @brief Returns the short integer value of the string.
       *
       * @return Short value
       */
      jshort toShort();
  
      /**
       * @brief Returns the byte value of the string.
       *
       * @return Byte value
       */
      jbyte toByte();
  
      /**
       * @brief Returns a pointer to a latin1-encoded character sequence of this \c String.
       * 
       * This method is particularly useful if a \c String is to be
       * passed to a library or operating system function that takes
       * old-fashioned <tt>char*</tt> arguments.
       *
       * @return Pointer to the internal array of characters.
       */                   
      char* latin1();
  
      /**
       * @brief Returns a pointer to a UTF8-encoded character sequence of this \c String.
       * 
       * This method is particularly useful if a \c String is to be
       * passed to a library or operating system function that takes
       * UTF8-encoded <tt>char*</tt> arguments (like GTK+).
       *
       * @return Pointer to the internal array of characters.
       */                   
      char* utf8();
  
      /**
       * @brief Returns a pointer to the internal, null-terminated
       * UTF16-encoded character sequence of this \c String.
       *
       * @return Pointer to the internal array of characters.
       */                   
      jchar* utf16();
  
      /**
       * @brief Finds the first instance of a character in this \c String, starting at
       * a given index.  If starting index is less than \c 0, the search
       * starts at the beginning of this \c String.  If the starting index
       * is greater than the length of this \c String, \c -1 is returned.
       *
       * @param ch character to find
       * @param fromIndex index to start the search
       *
       * @return location (base \c 0) of the character, or \c -1 if not found
       */
      int indexOf(jchar ch, int fromIndex = 0);

      /**
       * @brief Finds the first instance of a \c String in this \c String, starting at
       * a given index.  If starting index is less than \c 0, the search
       * starts at the beginning of this \c String.  If the starting index
       * is greater than the length of this \c String, \c -1 is returned.
       *
       * @param str String to find
       * @param fromIndex index to start the search
       *
       * @return location (base \c 0) of the \c String, or \c -1 if not found
       */
      int indexOf(String* str, int fromIndex = 0);
      int indexOf(jbyte* str, int fromIndex = 0);

      /**
       * @brief Finds the last instance of a \c String in this \c String.
       *
       * @param str String to find
       * 
       * @return location (base \c 0) of the \c String, or \c -1 if not found
       */
      int lastIndexOf(String* str);
      int lastIndexOf(jbyte*);

      /**
       * @brief Finds the last instance of a \c String in this \c String, starting at
       * a given index.  If starting index is greater than the maximum valid
       * index, then the search begins at the end of this \c String.  If the 
       * starting index is less than zero, \c -1 is returned.
       *
       * @param str String to find
       * @param fromIndex index to start the search
       *
       * @return location (base \c 0) of the String, or \c -1 if not found
       */
      int lastIndexOf(String* str, int fromIndex);
      int lastIndexOf(char* str, int fromIndex);
  
      /**
       * @brief Tests if this string starts with the specified prefix.
       *
       * @param   prefix The prefix.
       * @return  <tt>true</tt> if the character sequence represented by the
       *          argument is a prefix of the character sequence represented by
       *          this string; <tt>false</tt> otherwise.      
       *          Note also that <tt>true</tt> will be returned if the 
       *          argument is an empty string or is equal to this 
       *          string object. 
       */
      jboolean startsWith(String* prefix);
      jboolean startsWith(jbyte* prefix);

      /**
       * Predicate which determines if this String contains the given prefix,
       * beginning comparison at toffset. The result is false if toffset is
       * negative or greater than this.length(), otherwise it is the same as
       * <code>this.subString(toffset).startsWith(prefix)</code>.
       *
       * @param prefix String to compare
       * @param toffset offset for this String where comparison starts
       * @return true if this String starts with prefix
       * @exception NullPointerException if prefix is null
       * @see #regionMatches(boolean, int, String, int, int)
       */
      jboolean startsWith(String* prefix, jint toffset);

      /**
       * @brief Tests if this string ends with the specified suffix.
       *
       * @param suffix The suffix.
       * @return  <tt>true</tt> if the character sequence represented by the
       *          argument is a suffix of the character sequence represented by
       *          this string; <tt>false</tt> otherwise.      
       *          Note also that <tt>true</tt> will be returned if the 
       *          argument is an empty string or is equal to this 
       *          string object. 
       */
      jboolean endsWith(String* suffix);
      jboolean endsWith(jbyte* suffix);
  
      /**
       * @brief Creates a substring of this \c String, starting at a specified index
       * and ending at one character before a specified index.
       *
       * @param beginIndex index to start substring (base \c 0)
       * @param endIndex index after the last character to be 
       *   copied into the substring
       * 
       * @return new \c String which is a substring of this \c String
       *
       * @exception IllegalArgumentException
       *   if (beginIndex < 0 || endIndex < 0 || endIndex < beginIndex)
       */
      String* substring(int beginIndex, int endIndex);

      /**
       * @brief Creates a substring of this String, starting at a specified index
       * and ending at the end of this String.
       *
       * @param beginIndex index to start substring (base 0)
       * 
       * @return new String which is a substring of this String
       *
       * @exception StringIndexOutOfBoundsException 
       *   if (beginIndex < 0 || beginIndex > this.length())
       */
      String* substring(int beginIndex);
  
      /**
       * @brief Returns the character located at the specified index within
       * this String.
       *
       * @param index position of character to return (base 0)
       *
       * @return character located at position \c index or zero if \c index 
       * is beyond the \c String's end
       *
       * @exception IndexOutOfBoundsException if (index < 0)
       */
      jchar charAt(int index);

      /**
       * Copies characters from this String starting at a specified start index,
       * ending at a specified stop index, to a character array starting at
       * a specified destination begin index. dst==null indicates that destination
       * was not supplied. In this case, function will allocate a jchars buffer of size
       * [sizeof(jchar) * (srcEnd - srcBegin)] bytes on the free store, copy its
       * [srcBegin]-[srcEnd] contents into it and return the pointer.
       *
       * @param srcBegin index to begin copying characters from this String
       * @param srcEnd index after the last character to be copied from this String
       * @param dst pointer to jchars object which this String is copied into - can
       * be null, can be omitted (if omitted, default value is null)
       * @param dstBegin index to start writing characters into dst - can be omitted
       * (if omitted, default value is 0)
       * @exception StringIndexOutOfBoundsException if any indices are out of bounds
       */
      //jchars* getChars(jint srcBegin, jint srcEnd, jchars* dst=null, jint dstBegin=0);
   
      /**
       * @brief Replaces every instances of a character in this \c String with
       * a new character.
       *
       * @param oldChar the old character to replace
       * @param newChar the new character to put in place of the old character
       *
       * @return new String with all instances of \c oldChar replaced with \c newChar
       */
      String* replace(jchar oldChar, jchar newChar);
  
      /**
       * @brief Predicate which determines if this \c String matches another \c String
       * starting at a specified offset for each \c String and continuing
       * for a specified length.
       *
       * @param ignoreCase \c true if case should be ignored in comparision
       * @param toffset index to start comparison at for this String
       * @param other String to compare region to this String
       * @param ooffset index to start comparison at for `other'
       * @param len number of characters to compare
       *
       * @return true if regions match, false otherwise.
       */
      jboolean regionMatches(jboolean ignoreCase, int toffset, String* other,
                             int ooffset, int len);

      /**
       * @brief Predicate which determines if this \c String matches another \c String
       * starting at a specified offset for each \c String and continuing
       * for a specified length.
       *
       * @param toffset index to start comparison at for this String
       * @param other String to compare region to this String
       * @param ooffset index to start comparison at for `other'
       * @param len number of characters to compare
       *
       * @return true if regions match (case sensitive), false otherwise.
       */
      jboolean regionMatches(int toffset, String* other,
                             int ooffset, int len);

      String* plus(jlong l);
      String* plus(jint i);
      String* plus(jbyte c);
      String* plus(jchar c);
      String* plus(jdouble d);
      String* plus(const jbyte* str);
      String* plus(Object* obj);

      /**
       * Returns a copy of this \c String where the first
       * occurrence of \c %i (\c i: \c 0 .. \c 9) is replaced by
       * the given substitution.
       */
      String* arg(int idx, String *subst);
      String* arg(int idx, const jbyte* str);
      String* arg(int idx, jlong l);
      String* arg(int idx, jint l);
      String* arg(int idx, jdouble d);
      String* arg(int idx, jbyte c);
      String* arg(int idx, jchar c);
  
      int compareTo(Object* obj);
      int compareTo(String* obj);

      /* #if defined (JAKELIB_WCHAR_T) */
      /*         /** */
      /*          *  */
      /*         wchar_t* wx(); */

      /*         wchar_t *wcharBuffer; */
      /* #endif */

      /**
       * C-style \c printf() String factory.
       *
       * \b Example:
       * \code
       * String* str = String::printf(`"Hello %S, it's %2i:%02i."`, `"world"`, 15, 45);
       * \endcode
       *
       * <b>Formatter codes:</b>
       * \li <tt>\%i</tt> Integer (\c jint)
       * \li <tt>\%l</tt> Long (\c jlong)
       * \li <tt>\%s</tt> C-style string (\c jbyte)
       * \li <tt>\%S</tt> Jakelib String (\c jakelib::lang::String*)
       * \li <tt>\%f</tt> Float (\c jfloat)
       * \li <tt>\%d</tt> Double (\c jdouble)
       * \li <tt>\%c</tt> Character (\c jchar)
       * \li <tt>\%\%</tt> Percent sign (\c %)
       */
      static String* printf(String* fstring, ...);

      /**
       * Returns the string representation of the given \c jint value.
       */
      static String* valueOf(jint i);

      /**
       * Returns the string representation of the given \c jlong value.
       */
      static String* valueOf(jlong l);

      /**
       * Returns the string representation of the given \c jfloat value.
       */
      static String* valueOf(jfloat f);

      /**
       * Returns the string representation of the given \c double value.
       */
      static String* valueOf(jdouble d);

      /**
       * Returns the string representation of the given \c jchar value.
       */
      static String* valueOf(jchar c);

      /**
       * @brief Called by \c Object::initJakelib2().
       * Do not use otherwise.
       */
      static void initStringClass();

      /** An empty String. */
      static String* emptyString;

      /** The \c null String. */
      static String* nullString;

    protected:

      /**
       * Internal constructor.
       */
      String(String* str);

      /** @brief Contains the length of the string in characters. */
      int len;

      /** @brief Contains the max. capacity of the string in characters. */
      int capacity;

      /** @brief Contains a pointer the the array of charaters containing the string data. */
      jchar* buffer;

      /** @brief Holds the latin1-encoded string (created on demand). */
      char* latin1Buffer;

      /** @brief Holds the URF8-encoded string (created on demand). */
      char* utf8Buffer;

      static int initialCapacity;
      static int increment;

      void init(int initialCapacity);
    
      void ensureCapacity(int minimumCapacity);
  
      /**
       * Appends the given \c String to this \c String.
       */
      void append(String* str);

      /**
       * Appends a c-style string (\c jbyte*) to this \c String.
       * The string should be ISO-8859-1.
       */
      void append(const jbyte* str);

      /**
       * Appends an 8-bit character (\c jbyte) to this \c String.
       * The character should be ISO-8859-1.
       */
      void append(jbyte c);

      /**
       * Appends a wide character (\c jchar) to this \c String.
       */
      void append(jchar c);

      /**
       * Used by \c StringBuffer and \c Writer only.
       * Returns a pointer to the internal array of characters.
       */
      jchar* protectedGetChars();

    private:
      int cachedHashCode;

      JAKELIB_DECLARE_CLASS(String);
    };

    JAKELIB_DECLARE_ARRAY(String);
  }
}


#endif

