-- This file is  free  software, which  comes  along  with  SmartEiffel. This
-- software  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. You can modify it as you want, provided
-- this header is kept unaltered, and a notification of the changes is added.
-- You  are  allowed  to  redistribute  it and sell it, alone or as a part of
-- another product.
--       Copyright (C) 1994-2002 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
--
expanded class CHARACTER
--
-- Note: An Eiffel CHARACTER is mapped as a C char or as a Java Byte.
--

inherit
   HASHABLE
   COMPARABLE
      undefine
	 is_equal
      redefine
         out_in_tagged_out_memory, fill_tagged_out_memory,
	 infix ">=", infix "<=", infix ">"
      end

feature

   to_integer: INTEGER_8 is
         -- Sign-extended conversion.
      external "SmartEiffel"
      end

   code: INTEGER is
         -- ASCII code of Current. (No Sign-extended conversion.)
      external "SmartEiffel"
      ensure
         Result.in_range(Minimum_character_code,Maximum_character_code)
      end

   infix "<" (other: CHARACTER): BOOLEAN is
         -- Comparison using `code'.
      do
         Result := code < other.code
      end

   infix "<=" (other: CHARACTER): BOOLEAN is
         -- Comparison using `code'.
      do
         Result := code <= other.code
      end

   infix ">" (other: CHARACTER): BOOLEAN is
         -- Comparison using `code'.
      do
         Result := code > other.code
      end

   infix ">=" (other: CHARACTER): BOOLEAN is
         -- Comparison using `code'.
      do
         Result := code >= other.code
      end

   value, decimal_value: INTEGER_8 is
         -- Gives the value of a decimal digit.
      require
         is_digit
      do
         Result := code.to_integer_8 - 48
      ensure
         Result.in_range(0,9)
      end

   binary_value: INTEGER_8 is
         -- Gives the value of a binary digit.
      require
         is_binary_digit
      do
         Result := code.to_integer_8 - 48
      ensure
         Result.in_range(0,1)
      end

   octal_value: INTEGER_8 is
         -- Gives the value of an octal digit.
      require
         is_octal_digit
      do
         Result := code.to_integer_8 - 48
      ensure
         Result.in_range(0,7)
      end

   hexadecimal_value: INTEGER_8 is
         -- Gives the value of an hexadecimal digit.
      require
         is_hexadecimal_digit
      do
         Result := code.to_integer_8
         if code < ('A').code then
            Result := Result - 48
         elseif code < ('a').code then
            Result := Result - 55
         else
            Result := Result - 87
         end
      ensure
         Result.in_range(0,15)
      end

   same_as(other: CHARACTER): BOOLEAN is
         -- Case insensitive comparison.
         -- No difference between upper/lower case letters.
      do
         if item = other then
            Result := True
         else
            inspect
               code
            when 65 .. 90 then
               Result := code = other.code - 32
            when 97 .. 122 then
               Result := code = other.code + 32
            else
            end
         end
      ensure
         Result implies to_lower = other or to_upper = other
      end 

   to_upper: CHARACTER is
         -- Conversion to the corresponding upper case.
      do
         if code < 97 then
            Result := item
         elseif code > 122 then
            Result := item
         else
            Result := (code - 32).to_character
         end
      end

   to_lower: CHARACTER is
         -- Conversion to the corresponding lower case.
      do
         if code < 65 then
            Result := item
         elseif code > 90 then
            Result := item
         else
            Result := (code + 32).to_character
         end
      end

   is_letter: BOOLEAN is
         -- Is it a letter ('a' .. 'z' or 'A' .. 'Z') ?
      do
         if item >= 'a' then
            Result := item <= 'z'
         elseif item >= 'A' then
            Result := item <= 'Z'
         end
      ensure
         Result = in_range('A','Z') or in_range('a','z')
      end

   is_digit, is_decimal_digit: BOOLEAN is
         -- Belongs to '0'..'9'.
      do
         if item >= '0' then
            Result := item <= '9'
         end
      ensure
        Result = in_range('0','9')
      end

   is_binary_digit: BOOLEAN is
         -- Belongs to '0'..'1'.
      do
         if item >= '0' then
            Result := item <= '1'
         end
      ensure
         Result = in_range('0','1')
      end

   is_octal_digit: BOOLEAN is
         -- Belongs to '0'..'7'.
      do
         if item >= '0' then
            Result := item <= '7'
         end
      ensure
         Result = in_range('0','7')
      end

   is_hexadecimal_digit: BOOLEAN is
         -- Is it one character of "0123456789abcdefABCDEF" ?
      do
         if is_digit then
            Result := True
         elseif item >= 'a' then
            Result := item <= 'f'
         elseif item >= 'A' then
            Result := item <= 'F'
         end
      ensure
         Result =  (once "0123456789abcdefABCDEF").has(Current)
      end

   is_lower: BOOLEAN is
         -- Is it some lowercase letter ('a'..'z')?
      do
         inspect
            item
         when 'a'..'z' then
            Result := True
         else
         end
      end

   is_upper: BOOLEAN is
         -- Is it some uppercase letter ('A'..'Z')?
      do
         inspect
            item
         when 'A'..'Z' then
            Result := True
         else
         end
      end

   is_separator: BOOLEAN is
         -- True when character is a separator.
      do
         inspect
            item
         when ' ','%T','%N','%R','%U', '%F' then
            Result := True
         else
         end
      end

   is_letter_or_digit: BOOLEAN is
	 -- Is it a letter (see `is_letter') or a digit (see `is_digit') ?
      do
         Result := is_letter or else is_digit
      ensure
         definition: Result = (is_letter or is_digit)
      end

   is_ascii: BOOLEAN is
         -- Is character a 8-bit ASCII character?
      do
         inspect
            item
         when '%U'..'%/255/' then
            Result := True
         else
         end
      end

   is_bit: BOOLEAN is
         -- True for `0' and `1'.
      do
         inspect
            item
         when '0','1' then
            Result := True
         else
         end
      end

   next: CHARACTER is
         -- Give the next character (the following `code')
      do
         Result := (code + 1).to_character
      end

   previous: CHARACTER is
         -- Give the previous character (the `code' before)
      require
         code > 0
      do
         Result := (code - 1).to_character
      end

feature -- Conversions:

   to_bit: BIT Character_bits is
      external "SmartEiffel"
      end

   to_hexadecimal: STRING is
         -- Create a new STRING giving the `code' in hexadecimal.
         -- For example :
         --    (255).to_character.to_hexadecimal gives "FF".
         -- Note: see `to_hexadecimal_in' to save memory.
      do
         create Result.make(2)
         to_hexadecimal_in(Result)
      ensure
         Result.count = 2
      end

   to_hexadecimal_in(str: STRING) is
         -- Append the equivalent of `to_hexadecimal' at the end of
         -- `str'. Thus you can save memory because no other
         -- STRING is allocate for the job.
      local
         c: CHARACTER
      do
         c := ((to_bit and 11110000B) |>> 4).to_character
         inspect
            c.code
         when 0 .. 9 then
            str.extend((('0').code + c.code).to_character)
         else
            str.extend(((('A').code - 10) + c.code).to_character)
         end
         c := (to_bit and 00001111B).to_character
         inspect
            c.code
         when 0 .. 9 then
            str.extend((('0').code + c.code).to_character)
         else
            str.extend(((('A').code - 10) + c.code).to_character)
         end
      ensure
         str.count = 2 + old str.count
      end

feature -- Object Printing:

   out_in_tagged_out_memory, fill_tagged_out_memory is
      do
         tagged_out_memory.extend(item)
      end

feature -- Hashing:

   hash_code: INTEGER is
      do
         Result := code
      end

feature -- Miscellaneous:

   is_alpha: BOOLEAN is
	 -- See `is_letter' (yes this is just a call to `is_letter').
	 -- Isn't `is_letter' better English ;-)
      do
	 Result := is_letter
      ensure
	 Result = is_letter
      end

feature -- For compatibility with the obsolete CHARACTER_REF style:
   
   item: CHARACTER
   
   set_item(item_value: like item) is
      do
	 item := item_value
      ensure
	 item = item_value
      end

end -- CHARACTER