// natCharacter.cc - Native part of Character class.

/* Copyright (C) 1998, 1999  Cygnus Solutions

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

#include <config.h>

#include <gcj/cni.h>
#include <jvm.h>
#include <java/lang/Character.h>

#include <java-chartables.h>



#define asize(x)  ((sizeof (x)) / sizeof (x[0]))

static jchar
to_lower_title (jchar ch)
{
  for (unsigned int i = 0; i < asize (title_to_upper_table); ++i)
    {
      // We can assume that the entries in the two tables are
      // parallel.  This is checked in the script.
      if (title_to_upper_table[i][1] == ch
	  || title_to_upper_table[i][0] == ch)
	return title_to_lower_table[i][1];
    }
  return ch;
}

static jchar
to_upper_title (jchar ch)
{
  for (unsigned int i = 0; i < asize (title_to_lower_table); ++i)
    {
      // We can assume that the entries in the two tables are
      // parallel.  This is checked in the script.
      if (title_to_lower_table[i][1] == ch
	  || title_to_lower_table[i][0] == ch)
	return title_to_upper_table[i][1];
    }
  return ch;
}

jboolean
java::lang::Character::isTitleCase (jchar ch)
{
  for (unsigned int i = 0; i < asize (title_to_lower_table); ++i)
    {
      if (title_to_lower_table[i][0] == ch)
	return true;
    }
  return false;
}

jchar
java::lang::Character::toTitleCase (jchar ch)
{
  // Both titlecase mapping tables have the same length.  This is
  // checked in the chartables script.
  for (unsigned int i = 0; i < asize (title_to_lower_table); ++i)
    {
      if (title_to_lower_table[i][0] == ch)
	return ch;
      if (title_to_lower_table[i][1] == ch)
	return title_to_lower_table[i][0];
      if (title_to_upper_table[i][1] == ch)
	return title_to_upper_table[i][0];
    }
  return toUpperCase (ch);
}

#ifdef COMPACT_CHARACTER

static int
table_search (const jchar table[][2], int table_len, jchar ch)
{
  int low, high, i, old;

  low = 0;
  high = table_len;
  i = high / 2;

  while (true)
    {
      if (ch < table[i][0])
	high = i;
      else if (ch > table[i][1])
	low = i;
      else
	return i;

      old = i;
      i = (high + low) / 2;
      if (i == old)
	break;
    }

  return -1;
}

jint
java::lang::Character::digit_value (jchar ch)
{
  int index = table_search (digit_table, asize (digit_table), ch);
  if (index == -1)
    return -1;

  jchar base = digit_table[index][0];
  // Tamil doesn't have a digit `0'.  So we special-case it here.
  if (base == TAMIL_DIGIT_ONE)
    return ch - base + 1;
  return ch - base;
}

jint
java::lang::Character::getNumericValue (jchar ch)
{
  jint d = digit (ch, 36);
  if (d != -1)
    return d;

  for (unsigned int i = 0; i < asize (numeric_table); ++i)
    {
      if (numeric_table[i] == ch)
	return numeric_value[i];
    }

  return -1;
}

jint
java::lang::Character::getType (jchar ch)
{
  int index = table_search (all_table, asize (all_table), ch);
  if (index != -1)
    return category_table[index];
  return UNASSIGNED;
}

jboolean
java::lang::Character::isLowerCase (jchar ch)
{
  if (ch >= 0x2000 && ch <= 0x2fff)
    return false;
  if (table_search (lower_case_table, asize (lower_case_table), ch) != -1)
    return true;

  int low, high, i, old;

  low = 0;
  high = asize (lower_anomalous_table);
  i = high / 2;

  while (true)
    {
      if (ch < lower_anomalous_table[i])
	high = i;
      else if (ch > lower_anomalous_table[i])
	low = i;
      else
	return true;

      old = i;
      i = (high + low) / 2;
      if (i == old)
	break;
    }

  return false;
}

jboolean
java::lang::Character::isSpaceChar (jchar ch)
{
  return table_search (space_table, asize (space_table), ch) != -1;
}

jboolean
java::lang::Character::isUpperCase (jchar ch)
{
  if (ch >= 0x2000 && ch <= 0x2fff)
    return false;
  return table_search (upper_case_table, asize (upper_case_table), ch) != -1;
}

jchar
java::lang::Character::toLowerCase (jchar ch)
{
  int index = table_search (upper_case_table, asize (upper_case_table), ch);
  if (index == -1)
    return to_lower_title (ch);
  return (jchar) (ch - upper_case_table[index][0]
		  + upper_case_map_table[index]);
}

jchar
java::lang::Character::toUpperCase (jchar ch)
{
  int index = table_search (lower_case_table, asize (lower_case_table), ch);
  if (index == -1)
    return to_upper_title (ch);
  return (jchar) (ch - lower_case_table[index][0]
		  + lower_case_map_table[index]);
}

#else /* COMPACT_CHARACTER */

jint
java::lang::Character::digit_value (jchar ch)
{
  if (type_table[ch] == DECIMAL_DIGIT_NUMBER)
    return attribute_table[ch];
  return -1;
}

jint
java::lang::Character::getNumericValue (jchar ch)
{
  jint d = digit (ch, 36);
  if (d != -1)
    return d;

  // Some characters require two attributes.  We special-case them here.
  if (ch >= ROMAN_START && ch <= ROMAN_END)
    return secondary_attribute_table[ch - ROMAN_START];
  if (type_table[ch] == LETTER_NUMBER || type_table[ch] == OTHER_NUMBER)
    return attribute_table[ch];
  return -1;
}

jint
java::lang::Character::getType (jchar ch)
{
  return type_table[ch];
}

jboolean
java::lang::Character::isLowerCase (jchar ch)
{
  if (ch >= 0x2000 && ch <= 0x2fff)
    return false;
  return type_table[ch] == LOWERCASE_LETTER;
}

jboolean
java::lang::Character::isSpaceChar (jchar ch)
{
  return (type_table[ch] == SPACE_SEPARATOR
	  || type_table[ch] == LINE_SEPARATOR
	  || type_table[ch] == PARAGRAPH_SEPARATOR);
}

jboolean
java::lang::Character::isUpperCase (jchar ch)
{
  if (ch >= 0x2000 && ch <= 0x2fff)
    return false;
  return type_table[ch] == UPPERCASE_LETTER;
}

jchar
java::lang::Character::toLowerCase (jchar ch)
{
  if (type_table[ch] == UPPERCASE_LETTER)
    return attribute_table[ch];
  return to_lower_title (ch);
}

jchar
java::lang::Character::toUpperCase (jchar ch)
{
  if (type_table[ch] == LOWERCASE_LETTER)
    return attribute_table[ch];
  return to_upper_title (ch);
}

#endif /* COMPACT_CHARACTER */
