/* -*- Mode: C; c-file-style: "gnu" -*-
   resolve.c -- load in classes that another depends on.
   Created: Chris Toshok <toshok@hungry.com>, 12-Aug-1997.
 */
/*
  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) 1997, 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 "log.h"
#include "resolve.h"
#include "ClazzFile.h"
#include "class-repository.h"
#include "array-class.h"
#include "util.h"
#include "jniint.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#define MYLOG "Resolve"

ClazzFile*
find_class_on_classpath(JNIEnv *env,
			const char *dotted_class_name,
			const char *class_name,
			ClasspathEntry *entries,
			int num_entries)
{
  char *full_class_name = NULL;
  int class_name_length = 0;
  ClazzFile *cf = NULL;
  int i;
  jboolean verbose_loading = JNI_FALSE;

  if (env && ((HungryJNIEnv*)env)->_vm->_verbose_flags & VERBOSE_CLASS)
    verbose_loading = JNI_TRUE;

  for (i = 0; i < num_entries; i ++)
    {
      int new_class_name_length = (strlen(class_name) 
				   + strlen(entries[i].path)
				   + strlen(".class") + 2);
      if (!full_class_name)
	{
	  full_class_name = (char*)malloc(new_class_name_length);
	  class_name_length = new_class_name_length;
	}
      else if (new_class_name_length > class_name_length)
	{
	  free(full_class_name);
	  full_class_name = (char*)malloc(new_class_name_length);
	  class_name_length = new_class_name_length;
	}

      if (entries[i].type == CP_ZIP ||
	  entries[i].type == CP_JAR)
	{
	  ZipDirectory *dir;

	  /* if there was an error reading the archive, pass over it. */
	  if (entries[i].zip.fd == -1)
	    continue;

	  strcpy(full_class_name, class_name);
	  strcat(full_class_name, ".class");

	  {
	    int first, last, middle;

	    first = 0;
	    last = entries[i].zip.count - 1;

	    do {
	      int r;
	      middle = (first + last) / 2;

	      dir = entries[i].zip.sorted_entries[middle];
	      r = strcmp(full_class_name, ZIPDIR_FILENAME(dir));
	      if (r < 0)
		{
		  last = middle - 1;
		  continue;
		}
	      else if (r > 0)
		{
		  first = middle + 1;
		  continue;
		}
	      else
		{
		  char *buf;
		  int length;
		  int malloced;

		  if (get_zipfile_entry(&entries[i].zip,
					dir,
					&buf, &length,
					&malloced) != -1)
		    {
		      cf = define_class(env, buf, length);
		      if (verbose_loading)
			printf("[Loaded %s from %s]\n", dotted_class_name, entries[i].path);
		    }

		  free_zipfile_entry(dir, buf, length, malloced);

		  break;
		}
	    } while (first <= last);
	  }
	}
      else if (entries[i].type == CP_DIR)
	{
	  strcpy(full_class_name, entries[i].path);
	  strcat(full_class_name, "/");
	  strcat(full_class_name, class_name);
	  strcat(full_class_name, ".class");
	  
	  cf = parse_class (env, full_class_name);

	  if (cf && verbose_loading)
	    printf("[Loaded %s from %s]\n", dotted_class_name, full_class_name);
	}

      if (cf != NULL)
	{
	  break;
	}
    }

  if (cf != NULL)
    {
      add_class_to_repository(env, cf, (char*)class_name);
    }

  if (full_class_name)
    free(full_class_name);

  return cf;
}

ClazzFile*
find_class(JNIEnv *env, const char *class_name)
{
  char *real_class_name = NULL;
  ClazzFile *result = NULL;

  assert(NULL != class_name);

  if (class_name[0] == '[')
    {
      /* for array classes, we just create a fake entry for them. */
      return createFakeArrayClass(env, (char*)class_name);
    }
  else if (class_name[0] == 'L' && class_name[strlen(class_name) - 1] == ';')
    {
      real_class_name = strdup(class_name + 1);
      
      real_class_name[strlen(real_class_name) - 1] = 0;
    }
  else /* normal case */
    real_class_name = strdup(class_name);

  dots_to_slashes(real_class_name);
  
  if (env
      && strlen(real_class_name) > 10
      && !strncmp(real_class_name, "java/lang/", 10))
    {
      if (!strcmp(real_class_name + 10, "String"))
	{
	  result = ((HungryJNIEnv*)env)->_vm->_string_cf;
	}
      else if (!strcmp(real_class_name + 10, "Class"))
	{
	  result = ((HungryJNIEnv*)env)->_vm->_class_cf;
	}
      else if (!strcmp(real_class_name + 10, "Object"))
	{
	  result = ((HungryJNIEnv*)env)->_vm->_object_cf;
	}
    }
  
  if (!result)
    result = find_class_in_repository(env, (char*)real_class_name);
  
  if (!result)
    {
      HungryJNIEnv *henv = (HungryJNIEnv*)env;
      HungryJavaVM *hvm = henv->_vm;
      ClasspathEntry *entries = hvm->_cp_entries;
      int num_entries = hvm->_num_cp_entries;
      
      result = find_class_on_classpath(env, class_name, real_class_name, entries, num_entries);
    }
  
  free (real_class_name);
  
  return result;
}
