/* -*- Mode: C; c-file-style: "gnu" -*-
   array.c -- native methods for java/lang/reflect/Array.
   Created: Chris Toshok <toshok@hungry.com>, 12-Feb-1998.
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library 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
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "jniint.h"
#include <assert.h>

JNIEXPORT jint JNICALL
Java_java_lang_reflect_Array_getLength(JNIEnv *env,
				       jclass cls,
				       jobject array)
{
  return (*env)->GetArrayLength(env, array);
}

JNIEXPORT jobject JNICALL
Java_java_lang_reflect_Array_get(JNIEnv *env,
				 jclass cls,
				 jobject array,
				 jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.get()");
      return NULL;
    }

  return (*env)->GetObjectArrayElement(env, array, index);
}

JNIEXPORT jboolean JNICALL
Java_java_lang_reflect_Array_getBoolean(JNIEnv *env,
					jclass cls,
					jobject array,
					jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jboolean result;
  jboolean *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getBoolean");
      return JNI_FALSE;
    }

  elements = (*env)->GetBooleanArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseBooleanArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jbyte JNICALL
Java_java_lang_reflect_Array_getByte(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jbyte result;
  jbyte *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getByte");
      return 0;
    }

  elements = (*env)->GetByteArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseByteArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jchar JNICALL
Java_java_lang_reflect_Array_getChar(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jshort result;
  jchar *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getShort");
      return 0;
    }

  elements = (*env)->GetCharArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseCharArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jshort JNICALL
Java_java_lang_reflect_Array_getShort(JNIEnv *env,
				      jclass cls,
				      jobject array,
				      jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jshort result;
  jshort *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getShort");
      return 0;
    }

  elements = (*env)->GetShortArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseShortArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jint JNICALL
Java_java_lang_reflect_Array_getInt(JNIEnv *env,
				    jclass cls,
				    jobject array,
				    jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jint result;
  jint *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getInt");
      return 0;
    }

  elements = (*env)->GetIntArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseIntArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jlong JNICALL
Java_java_lang_reflect_Array_getLong(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jlong result;
  jlong *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getLong");
      return 0;
    }

  elements = (*env)->GetLongArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseLongArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jfloat JNICALL
Java_java_lang_reflect_Array_getFloat(JNIEnv *env,
				      jclass cls,
				      jobject array,
				      jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jfloat result;
  jfloat *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getFloat");
      return 0;
    }

  elements = (*env)->GetFloatArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseFloatArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT jdouble JNICALL
Java_java_lang_reflect_Array_getDouble(JNIEnv *env,
				       jclass cls,
				       jobject array,
				       jint index)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jdouble result;
  jdouble *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.getDouble");
      return 0;
    }

  elements = (*env)->GetDoubleArrayElements(env, array, NULL);

  result = elements[index];

  (*env)->ReleaseDoubleArrayElements(env, array, elements, 0);

  return result;
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_set(JNIEnv *env,
				 jclass cls,
				 jobject array,
				 jint index,
				 jobject value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.set()");
      return;
    }

  (*env)->SetObjectArrayElement(env, array, index, value);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setBoolean(JNIEnv *env,
					jclass cls,
					jobject array,
					jint index,
					jboolean value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jboolean *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setBoolean");
      return;
    }

  elements = (*env)->GetBooleanArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseBooleanArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setByte(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index,
				     jbyte value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jbyte *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setByte");
      return;
    }

  elements = (*env)->GetByteArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseByteArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setChar(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index,
				     jchar value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jchar *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setChar");
      return;
    }

  elements = (*env)->GetCharArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseCharArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setShort(JNIEnv *env,
				      jclass cls,
				      jobject array,
				      jint index,
				      jshort value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jshort *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setShort");
      return;
    }

  elements = (*env)->GetShortArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseShortArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setInt(JNIEnv *env,
				    jclass cls,
				    jobject array,
				    jint index,
				    jint value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jint *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setInt");
      return;
    }

  elements = (*env)->GetIntArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseIntArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setLong(JNIEnv *env,
				     jclass cls,
				     jobject array,
				     jint index,
				     jlong value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jlong *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setLong");
      return;
    }

  elements = (*env)->GetLongArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseLongArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setFloat(JNIEnv *env,
				      jclass cls,
				      jobject array,
				      jint index,
				      jfloat value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jfloat *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setFloat");
      return;
    }

  elements = (*env)->GetFloatArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseFloatArrayElements(env, array, elements, 0);
}

JNIEXPORT void JNICALL
Java_java_lang_reflect_Array_setDouble(JNIEnv *env,
				       jclass cls,
				       jobject array,
				       jint index,
				       jdouble value)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jdouble *elements;
  jint length = (*env)->GetArrayLength(env, array);

  if (index < 0 || index > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/ArrayIndexOutOfBoundsException",
		      "in java/lang/reflect/Array.setDouble");
      return;
    }

  elements = (*env)->GetDoubleArrayElements(env, array, NULL);

  elements[index] = value;

  (*env)->ReleaseDoubleArrayElements(env, array, elements, 0);
}

/*
 * I'm not sure if the params are already checked before this private
 * method are called.  Someone should investigate. [pere 1998-07-06]
 */
JNIEXPORT jobject JNICALL
Java_java_lang_reflect_Array_newArray(JNIEnv *env,
				      jclass cls,
				      jclass componentType,
				      jint length)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *cf;
  char buf[200];

  if ( NULL == componentType )
    {
      HVM_ExceptionThrow(henv, "java/lang/NullPointerException",
		      "in java/lang/reflect/Array.newArray");
      return NULL;
    }

  if (0 > length)
    {
      HVM_ExceptionThrow(henv, "java/lang/NegativeArraySizeException",
		      "in java/lang/reflect/Array.newArray");
      return NULL;
    }

  cf = jclass_to_clazzfile(henv, componentType);

  PR_snprintf(buf, sizeof(buf), "[L%s;", cf->class_name);

  return HVM_ArrayNew(henv, length, HVM_ClassFind(henv, buf));
}

/*
 * I'm not sure if the params are already checked before this private
 * method are called.  Someone should investigate. [pere 1998-07-06]
 */
JNIEXPORT jobject JNICALL
Java_java_lang_reflect_Array_multiNewArray(JNIEnv *env,
					   jclass cls,
					   jclass componentType,
					   jintArray dimensions)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jint dims;
  jint *n_elems;
  int i;
  jobject retval = NULL;
  char buf[200];
  ClazzFile *cf;

  if ( NULL == componentType )
    {
      HVM_ExceptionThrow(henv, "java/lang/NullPointerException",
		      "in java/lang/reflect/Array.multiNewArray");
      return NULL;
    }

  dims = (*env)->GetArrayLength(env, dimensions);
  if (0 >= dims)
    {
      HVM_ExceptionThrow(henv, "java/lang/IllegalArgumentException",
		      "in java/lang/reflect/Array.multiNewArray");
      return NULL;
    }

  n_elems = (*env)->GetIntArrayElements(env, dimensions, JNI_FALSE);

  assert(NULL != n_elems);
  for (i = 0; i < dims; i++)
    {
      if (0 >= n_elems[i])
	{
	  HVM_ExceptionThrow(henv, "java/lang/NegativeArraySizeException",
			     "in java/lang/reflect/Array.multiNewArray");
	  (*env)->ReleaseIntArrayElements(env, dimensions, n_elems, 0);
	  return NULL;
	}
    }

  cf = jclass_to_clazzfile(henv, componentType);

  PR_snprintf(buf, sizeof(buf), "[L%s;", cf->class_name);

  retval = HVM_ArrayNewMulti(henv, n_elems, dims, HVM_ClassFind(henv, buf));

  (*env)->ReleaseIntArrayElements(env, dimensions, n_elems, 0);

  return retval;
}
