//
// module zum laden und ausführen von Java code in mupad
//

// include und library Pfade der Java
//original: MMG( windows: coption = "/IC:\\\j2sdk1.4.2_06\\include /IC:\\\j2sdk1.4.2_06\\include\\win32" )
MMG( windows: coption = "/IC:\\Programme\\Java\\jdk1.5.0\\include /IC:\\Programme\\Java\\jdk1.5.0\\include\\win32" )

// Original
//MMG( windows: coption = "/IZ:\\igor\\user\\tonner\\windows\\java142_05\\include /IZ:\\igor\\user\\tonner\\windows\\java142_05\\include\\win32" )

// Offler:
//MMG( linux: coption = "-I/usr/local/java/j2sdk-1.4.1/include -I/usr/local/java/j2sdk-1.4.1/include/linux" )

// VMWare:
//MMG( linux:   coption = "-I/home/j2sdk1.4.2/include -I/home/j2sdk1.4.2/include/linux" )

// Own:
//MMG ( linux: coption = "-I/automount/wiwianka/user/maas/MUPAD/sun/j2sdk1.4.2_02/include -I/automount/wiwianka/user/maas/MUPAD/sun/j2sdk1.4.2_02/include/linux" )


MMG( generate = "debug" )


#include <stdio.h>
#include <stdlib.h>
#include "reflect.cpp"  // So incredibly neccessary, I do not dare to say more :)

#define MAX_PATH_LIST_LEN 2048

#if defined WIN32
#  define JVM_DLL "jvm.dll"
#  define PATH_SEPARATOR ';'
#  include <windows.h>
   static HINSTANCE dll = NULL;
   static FARPROC initAdr;
#  define loadDLL(d)      LoadLibrary(d)
#  define getDLLAdress(h) GetProcAddress(h,"JNI_CreateJavaVM")
#  define freeDLL(h)      FreeLibrary(h)
#elif defined __linux__
#  define JVM_DLL "libjvm.so"
#  define PATH_SEPARATOR ':'
#  include <dlfcn.h>
#  include <errno.h>
#  include <string.h>
   static void *dll = NULL;
   static void *initAdr;
#  define loadDLL(d)      dlopen(d,RTLD_NOW + RTLD_GLOBAL) //(RTLD_GLOBAL)
#  define getDLLAdress(h) dlsym(h,"JNI_CreateJavaVM")
#  define freeDLL(h)      dlclose(h)
#endif

#define MATCH_FIRST  1
#define MATCH_MAX    2 // The default.
#define MATCH_BEST   3
#define MATCH_NATIVE 4

// Global JVM variables
static JavaVM *jvm = NULL;
static JNIEnv *env = NULL;

static jboolean killed = JNI_FALSE;
static jboolean isRedirected = JNI_FALSE;
static int generalMatching = MATCH_MAX;                                         // Predefined as Max match.

/** JVM abort hook called */
void myAbortHook() {
  MFprintf("'abort' was invoked by the JVM. JVM terminated abnormally.\n");
  MFprintf("To work with java from MuPAD again, you must remove the MuPAD core\n");
  MFprintf("from memory and restart a new one.\n");
  killed = JNI_TRUE;
}

/** JVM exit hook called */
void myExitHook(jint status) {
  MFprintf("'exit' was invoked by the JVM. The JVM is shutting down now.\n");
  MFprintf("It was called with %d\n", status);
  MFprintf("To work with java from MuPAD again, you must remove the MuPAD core\n");
  MFprintf("from memory and restart a new one.\n");
  killed = JNI_TRUE;
}

/**
  * A native method, that calls the MuPAD-Kernel with a given calculation and returns a string (by now)
  * @param query The calculation to be sent to MuPAD
  */
JNIEXPORT jstring JNICALL Java_calcMuPAD(JNIEnv* env, jclass thisClass, jstring query) {

  char* utf_string;
  char *result;
  long trapError = 0;


  if (jvm == NULL || env == NULL) {
     MFerror("No JVM started");
  }

  utf_string = JHelper::GetNativeChars(env, query);

  if (utf_string == NULL) {
    result = "";
    return JSystem::NewPlatformString(env, result);
  }

  if (_debug>0) MFprintf("utf_string was created.\n");
  if (_debug>0) MFprintf("Java::MuPAD_JNI::calcMuPAD : Your query is '%s'\n", utf_string);

  MTcell res = MFtrap(MF(utf_string), &trapError);
  if (trapError) {
    result = "Query can not be calculated.";
  } else {
    result = MFexpr2text(res);
    MFfree(res);
  }
  return JSystem::NewPlatformString(env, result);
}

MFUNC(console, MCnop) {
  if (_debug>0) MFprintf("In console\n");

  if (isRedirected == JNI_FALSE)
  {
    MFerror("console: Output is not redirected.\n");
  }

  jclass hClass = env->GetObjectClass(outClass);
  if (hClass == NULL)
  {
    MFerror("console: Could not reload outClass.\n");
  }

  jmethodID mid = env->GetMethodID(hClass, "showWindow", "()V");                    // show it...
  if (mid == NULL)
  {
    MFerror("console: Could not get method show().\n");
  }
  env->CallVoidMethod(outClass, mid, JNI_TRUE);

  JSystem::checkForException(env);
  MFcopy(MVnull);
} MFEND

/** setOut
 * redirects the output of the JVM.
 * @param env    The ubiquitous pointer to JNI.
 * @return true if the class could be loaded, false otherwise.
 */
jboolean setOut(JNIEnv *env, jboolean toFile) {
  jmethodID mid;

  if (_debug>1) MFprintf("setOut: started.\n");

  jclass hClass = env->FindClass("MuPADConsole");
  if (hClass == NULL) {
    if (_debug>1) MFprintf("setOut: Class not found.\n");
    return JNI_FALSE;
  } else {
    if (_debug>1) MFprintf("setOut: Class found.\n");
  }

  if (_debug>1) MFprintf("setOut: Get method start.\n");
  mid = env->GetMethodID(hClass, "<init>", "(Z)V");                    // Instanciate the class
  if (_debug>1) MFprintf("setOut: Have the midof start.\n");

  if (mid == NULL) {
    if (_debug>1) MFprintf("setOut: Could not load constructor.\n");
    return JNI_FALSE;
  }
  jobject obj = env->NewObject(hClass, mid, toFile);

  outClass = env->NewGlobalRef(obj);
  env->DeleteLocalRef(hClass);
  return JNI_TRUE;
}

/**
 * starts a JVM.
 */
static jint initJVM (char *classPath, char *libraryPath, char *dllPath, jboolean useSecurityManager, jboolean useFile, MTcell mupParam) {
  jboolean worked;
  JNIEnv *jenv = NULL;
  jint counter = 4;

  if (_debug>0) MFprintf("initJVM started.\n");
  if (jvm != NULL)
  {
     if (killed == JNI_TRUE) {
         MFprintf("The java virtual machine crashed.\n");
         MFprintf("To work with java from MuPAD again, you must remove the MuPAD core\nfrom memory and restart a new one.\n");
         return(-1);
     }
     if (_debug>0) MFprintf("JVM != NULL. Another start was attempted.\n");
     /* Since it is already started just return true */
     return(0);
  }

  if (_debug>0) MFprintf("initJVM : using %s to load DLL...\n", dllPath);
  if (_debug>0) MFprintf("initJVM : classpath='%s'\n", classPath);
  if (_debug>0) MFprintf("initJVM : librarypath='%s'\n", libraryPath);

  // ****************************************
  // Step 1 : Set up the options for the VM *
  // ****************************************

  JavaVMInitArgs vm_args;

  if (MFdom(mupParam) == DOM_LIST)
  {
    counter += MFnopsList(mupParam);
    if (_debug>0) MFprintf("initJVM : using additional %d options.\n", (counter-4));
  }

  JavaVMOption options[20]; // more than enough for anybody !!!
  options[0].optionString = "abort";
  options[0].extraInfo    = (void *) &myAbortHook;
  options[1].optionString = "exit";
  options[1].extraInfo    = (void *) &myExitHook;
  options[2].optionString = classPath;
  options[3].optionString = libraryPath;

  if (counter > 4)
  {
    for (int i=4; i<counter; i++)
    {
      // The char is not freed, because it is put into the JVM and will be automatically freed when the process is done.
      char *optionStr =  MFstring(MFop(mupParam, i-4));
      if (_debug>0) MFprintf("initJVM : options[%d].optionString = %s.\n", i, optionStr);
      options[i].optionString = optionStr;
    }
  }

  vm_args.version  = JNI_VERSION_1_4;
  vm_args.options  = options;
  vm_args.nOptions = counter;
  vm_args.ignoreUnrecognized = JNI_TRUE;

  // ***************************
  // Step 2 : Start the JavaVM *
  // ***************************

  if (_debug>0) MFprintf("Loading dll\n");
  dll = loadDLL(dllPath);
  if (dll == NULL) {
     MFprintf("Error: loadDLL failed [initJVM]\n");
     return(-1);
  }

  if (_debug>0) MFprintf("getting memory adress dll\n");
  initAdr = getDLLAdress(dll);
  if ( initAdr == NULL ) {
     MFprintf("Error: getDLLAdress failed [initJVM]\n");
     freeDLL(dll);
     return(-1);
  }

  if (_debug>0) MFprintf("starting the jvm.dll\n");
  jint result = ((jint (*)(JavaVM**, JNIEnv**, JavaVMInitArgs*)) initAdr)( &jvm, &jenv, &vm_args );

  if (result < 0) {
     MFprintf("Error: JNI_CreateJavaVM failed %d [initJVM]\n", result);
     freeDLL(dll);
     jvm = NULL;
     jenv = NULL;
  } else {
    // Now create a thread using jenv and transform this one into env (used throughout the code...)
    result = jvm->AttachCurrentThread((void**) &env, NULL);
  }

  /* Check for the correct encoding of strings  */
  JSystem::InitEncodingFlag(env);

  /* Initialize the Exception class */
  jExceptionClass = env->FindClass("java/lang/Exception");
  if (jExceptionClass == NULL)
  {
    MFprintf("Warning: Cannot load Exception class [initJVM]\n");
  } else {
    if (_debug>0) MFprintf("Exception class available.\n");

    jExceptionID = env->GetMethodID(jExceptionClass, "toString", "()Ljava/lang/String;");
    if (jExceptionID == NULL)
    {
      MFprintf("Warning: Cannot define Exception method getMessage [initJVM]\n");
    } else {
      if (_debug>0) MFprintf("predefined exception method getMessage successfuly.\n");
    }
  }

  JSystem::checkForException(env);

  /* Redirect output */
  if (_debug>0) MFprintf("Redirecting output.\n");
  worked = setOut(env, useFile);
  if (worked == JNI_TRUE) {
    if (_debug>0) MFprintf("All output will be redirected to ");
    if (useFile == JNI_TRUE)
    {
      if (_debug>0) MFprintf("File.\n");
        isRedirected = JNI_FALSE;
    }
    else {
      if (_debug>0) MFprintf("MuPAD-Console.\n");
      isRedirected = JNI_TRUE;
    }
    //exceptionNotCaught = JNI_TRUE;
  } else {
    MFprintf("No redirection possible.\n");
  }

  JSystem::checkForException(env);

  /* Create the security manager */
  if (useSecurityManager == JNI_TRUE)
  {
    if (_debug>0) MFprintf("Creating Security Manager.\n");
    // Set security manager
    worked = JSystem::addMuPADSecurityListener(env);
    if (worked == JNI_TRUE) {
      if (_debug>0) MFprintf("No class should be able to call System.exit(x) now...\n");
    } else {
      MFprintf("Warning: Cannot hook Security Manager [initJVM]");
    }

    JSystem::checkForException(env);
  } else {
   if (_debug>0) MFprintf("Security manager was explicitly not loaded.\n");
  }

  jobject obj = JSystem::startList(env);
  // Now make this object reachable throughout the whole code.
  javaList = env->NewGlobalRef(obj);

  return( result );
}

/**
 * getStatus
 * @return As long as the jvm is running 0. <br>
 *         If the jvm is not instantiated 1. <br>
 *         If the jvm was destroyed (but is not removed...) -1. <br>
 */
MFUNC(getStatus, MCnop) {
 if (_debug>0) MFprintf("getStatus was invoked.\n");
 if (killed) {
   if (_debug>0) MFprintf("getStatus: -1 (jvm was destroyed).\n");
   MFreturn ( MFint(-1) );
 }
 if (jvm == NULL || env == NULL) {
   if (_debug>0) MFprintf("getStatus: 1 (jvm is not instanciated).\n");
   MFreturn (MFint(1));
 }
 if (_debug>0) MFprintf("getStatus: 0 (jvm is up and running).\n");
 MFreturn (MFint(0));
} MFEND

/** match
  * @param int The default match method. Possible values are:
  *            1. first match  : take the first possible method out of the array.
  *            2. max match    : use a ranking system to rank the methods:
  *                              Integer        : BigInteger = 5, Long = 4,
  *                                               Int = 3, Short = 2, Byte = 1
  *                              Floating point : BigDecimal = 3, Double = 2,
  *                                               Float = 1
  *                              Text           : String = 2, Char = 1
  *                              Other          : Boolean = 1, Array = 100, unknown = 1, object = 1
  *            3. best match   : use the parameter with the smallest deviation
  *                              to the request
  *                              transfer numbers and check the size. According
  *                              to the size use the next smallest possible java
  *                              type
  *            4. native match : use the method which signature is given.
  *
  * @return the result of the invoked method or FAIL if something went wrong.
  */
MFUNC(match, MCnop) {
  if (MVnargs == 0) {
    MFreturn(MFint(generalMatching));
  }

  MFargCheck( 1, DOM_INT );
  if (MFint(MFarg(1)) > 4)
    generalMatching = MATCH_MAX;
  else
    generalMatching = MFint(MFarg(1));

  MFreturn(MFint(generalMatching));
} MFEND

/** invoke
  * mandatory:
  * ----------
  * @param String1   the class that contains the method.
  * @param String2   the method that is to be used.
  * @param List      the list of arguments.
  * @param Boolean   if TRUE the method that was chosen will be displayed, if FALSE, not.
  * @param Boolean   if TRUE the method is a constructor. If FALSE its a method.
  * @param Boolean   if an already defined object is to be used instead of a new "clean" object.
  * @param String3   The internal name for the stored object (for example MuPAD_JNI_00000000) (optional)
  * @param List      the list of possible datatypes for matching.
  * facultative:
  * ------------
  * @param int       The matching that is to be chosen (optional)
  * @param String4   The signature of the method (optional)
  * @return the result of the invoked method or FAIL if something went wrong.
  */
MFUNC(invoke, MCnop) {
  int j,k, fittingMethod;
  jint index;
  jint jindex;
  const char *methSig = NULL;
  char *tmpChar;
  jvalue retValue;                                                              // The result will be stored in this.
  jboolean showMethodChosen = false;                                                // If a match was specified, the chosen method will be shown.
  int matchingMethod = generalMatching;
  jobject otherObject = NULL;
  jboolean useStoredObject;
  jobject theStoredObject = NULL;
  //jthrowable exc;
  char *useObjectName;
  int chooseParameter = 0;  // If only integers, than floats should not be chosen at all.

  /* **********************************************************************************************
  1st part:
  Checking the different paramters
  ************************************************************************************************* */
  if (_debug>0) MFprintf("1st part ****************************************************************\n");
  if (_debug>0) MFprintf("invoke started...\n");
  if (jvm == NULL || env == NULL) {
     MFerror("No JVM started");
  }

  MFargCheck( 1, DOM_STRING );                                                  // First one has to be of type String
  MFargCheck( 2, DOM_STRING );                                                  // Second one has to be of type String
  MFargCheck( 3, DOM_LIST );                                                    // Third one has to be a list

  MFargCheck( 4, DOM_BOOL );                                                    // Fourth one has to be a boolean
  MFargCheck( 5, DOM_BOOL );                                                    // Fifth one has to be a boolean

  MFargCheck( 6, DOM_BOOL );                                                    // Sixth one has to be a boolean
  MFargCheck( 7, DOM_STRING );                                                  // Seventh one has to be a string
  useStoredObject = MFbool(MFarg(6));

  if (useStoredObject == JNI_TRUE) {
    useObjectName = MFstring(MFarg(7));
  } else {
     useObjectName = NULL;
  }

  MFargCheck( 8, DOM_LIST );                                                    // Eigth one has to be a list

  if (MVnargs > 8) {                                                            // If a 6th paramter is stated
    if (MFdom(MFarg(9)) == DOM_INT) {
      matchingMethod = MFint(MFarg(9));                                         // and matchingMethod gets it.
      if (_debug>0) MFprintf("Matching: %d\n", matchingMethod);
    } else {
      // So the default is native and someone just stated the signature...
      MFargCheck( 9, DOM_STRING );                                              // It has to be of type String
      methSig = MFstring(MFarg(9));                                             // Assign signature to methSig
    }
  }

  if ((MVnargs > 9) && (matchingMethod == MATCH_NATIVE)) {                      // Only if native was chosen as 6th parameter.
    MFargCheck( 10, DOM_STRING );                                                // It has to be of type String
    methSig = MFstring(MFarg(10));                                               // Assign signature to methSig
    if (_debug>0) MFprintf("Matching signature: '%s'\n" , methSig);
  }

  // Put all the parameters in variables for easier referencing:
  const char *param1 = MFstring(MFarg(1));                                      // Store first parameter in param1
  const char *param2 = MFstring(MFarg(2));                                      // Store second parameter in param2
  const MTcell param3 = MFarg(3);                                               // Store array in MuPAD datatype param3
  const int nParams = MFnops(param3);                                           // Store number of parameters
  showMethodChosen = MFbool(MFarg(4));
  isConstructor = MFbool(MFarg(5));
  const MTcell possibleMatches = MFarg(8);                                      // The list containing the possible matches

  if (isConstructor == JNI_TRUE)
  {
    if (MVnargs < 9)
    {
      matchingMethod = MATCH_BEST;
      if (_debug>0) MFprintf("Got a constructor and no explicit signature. Therefore set match to BEST\n");
    }
  }

  if (_debug>0) MFprintf("MVnargs:%d\n", MVnargs);
  if (_debug>0) MFprintf("param 1:%s\n", param1);
  if (_debug>0) MFprintf("param 2:%s\n", param2);

  if (_debug>0) MFprintf("param 4:%d\n", showMethodChosen);
  if (_debug>0) MFprintf("param 5:%d\n", isConstructor);
  if (_debug>0) MFprintf("param 6:%d\n", useStoredObject);
  if (_debug>0) MFprintf("param 7:%s\n", useObjectName);
  if (_debug>0) MFprintf("param 8:%d\n", matchingMethod);
  if (_debug>0) MFprintf("param 9:%s\n", methSig);

  if (_debug>0) MFprintf("Method : %s\n" , param2);
  if (_debug>0) MFprintf("Number of arguments : %d\n" , nParams);
  for (j = 0; j < nParams; j++) {
    if (_debug>0) MFprintf("Argument %d = ", j);
    if (_debug>0) MFout( MFop(param3,j) );
  }

  /* **********************************************************************************************
  2nd part:
  Locating the class
  ************************************************************************************************* */
  if (_debug>0) MFprintf("2nd part ****************************************************************\n");
  jclass mClass;
  if (useStoredObject == JNI_TRUE)
  {
    if (_debug>0) MFprintf("Using the object %s\n", useObjectName);
    theStoredObject = JSystem::HO_getObject(env, useObjectName); // put the string object in the array
    if (theStoredObject == NULL)
    {
      MFerror("Sorry. The stated object is not available.\n");
    }
  }

  mClass = env->FindClass(param1);

  if (mClass == NULL) {                                                         // Class could not be located
    //if (exceptionNotCaught) {
      JSystem::checkForException(env);
    //}
    if (showErrors == JNI_TRUE) MFprintf("Error: Class not found '%s'.\n", param1);
    MFreturn( MFcopy(MVfail) );                                                 // return error
  }

  jobjectArray classMethods;
  if (isConstructor == JNI_TRUE)
  {
    classMethods = JClass::getConstructors(env, mClass, JNI_TRUE);   // constructors (also super-class constructors)
  }
  else
  {
    classMethods = JClass::getMethods(env, mClass, JNI_FALSE);       // only methods of the class itself (no superclass methods)
  }

  if (classMethods == NULL)
  {
    //if (exceptionNotCaught) {
      JSystem::checkForException(env);
    //}
    MFprintf("Class has no methods?\n");
    MFreturn( MFcopy(MVfail) );                                                 // return error
  }

  int nMethods;
  if (isConstructor == JNI_TRUE)
  {
    if (_debug > 0) MFprintf("Class has constructors...\n");
    nMethods = env->GetArrayLength(classMethods);                             // get the number of declared methods
    if (_debug>0) MFprintf("Class has %d constructors\n" , nMethods);
  }
  else {
    if (_debug > 0) MFprintf("Class has methods...\n");
    nMethods = env->GetArrayLength(classMethods);                             // get the number of declared methods
    if (_debug>0) MFprintf("Class has %d methods\n" , nMethods);
  }

  int *validMethodsArray = (int *) MFcmalloc(nMethods * sizeof(int));
  int *paramValueArray = (int *) MFcmalloc(nMethods * nParams * sizeof(int));
  int *paramJavaArray = (int *) MFcmalloc(nMethods * nParams * sizeof(int));

  for (j=0; j < nMethods; j++) {                                                // Preinitialize the Array with 0's
    validMethodsArray[j] = 0;
  }
  for (j=0; j < nMethods*nParams; j++) {                                       // Preinitialize the Array with 0's
    paramValueArray[j] = 0;
  }
  for (j=0; j < nMethods*nParams; j++) {                                       // Preinitialize the Array with 0's
    paramJavaArray[j] = 0;
  }

/*
  // To test where a memory leak happens (already here !!!)
    if (1==1)
  {
    env->DeleteLocalRef(classMethods);
    MFcfree(validMethodsArray);
    MFcfree(paramValueArray);
    MFcfree(paramJavaArray);
    //JSystem::garbageCollect(env);
    MFreturn(MFcopy(MVnull) );
  }
*/

  bool foundMethod = false;
  MTcell tempParam;                                                             // for the current MuPAD parameter

  for (j=0; j < nMethods; j++) {
    jboolean doesMatch = false;                                                 // to store the result of the check
    jobject method = env->GetObjectArrayElement(classMethods, j);               // get the j'th class in method.
    tmpChar = JMethod::getName(env, method);
    if (_debug>0) MFprintf("strcmp(%s, %s) = %d\n", param2, tmpChar, strcmp(param2, tmpChar));

    if ((strcmp(param2, tmpChar) == 0)                                          // Check for simmilarity of method names
         && (nParams == JMethod::getParameterNumber(env, method))) {            // Check for similarity of parameter numbers
      // go through the parameters and check if they are a number or an object
      // and how the signatures are. If number matches number and object matches
      // object, okay. Otherwise do not add it to the list.

      // a small speedup for match = native type
      // or if there are no parameters stated (only one method possible anyway!).
      // It was taken out of the matchingMethod for speedup.
      if (nParams == 0) {                                                       // or number of parameters are 0
        validMethodsArray[j] = 1;                                               // method at position j is the right one
        foundMethod = true;                                                     // found the method
        break;                                                                  // This is the real speedup :)
      } else {
        jclass mParam;                                                          // for the current JNI data type
        MTcell theMupadParam;
       for (k=0; k < nParams; k++) {
          mParam = JMethod::getSpecialParameter(env, method, k);                // get the class of the k's parameter.

          //char *sign = JClass::getSignature(env, mParam);                       // and get the signature
          tempParam = MFgetList(&possibleMatches, k);
          theMupadParam = MFgetList(&param3, k);
          index = 0;
          jindex=0;

          // The separation is only done because it might change in the future and then it is more concise

          if (matchingMethod == MATCH_FIRST) {                                  // first match
            if (_debug>0) MFprintf("invoke: %d,1) Calling JHelper::matchMethod with first match", j);
            if (_debug>0) MFout( tempParam );
            //doesMatch = JHelper::matchMethod(env, sign, tempParam, &index, &jindex);
            doesMatch = JHelper::matchMethod(env, mParam, theMupadParam, tempParam, &index, &jindex);
          } else if (matchingMethod == MATCH_MAX) {                             // max match
            if (_debug>0) MFprintf("invoke: %d,2) Calling JHelper::matchMethod with max match", j); //, MFexpr2text(tempParam));
            if (_debug>0) MFout( tempParam );
            //doesMatch = JHelper::matchMethod(env, sign, tempParam, &index, &jindex);
            doesMatch = JHelper::matchMethod(env, mParam, theMupadParam, tempParam, &index, &jindex);
          } else if (matchingMethod == MATCH_BEST) {                            // best match
            if (_debug>0) MFprintf("invoke: %d,3) Calling JHelper::matchMethod with best match", j); //, MFexpr2text(tempParam));
            if (_debug>0) MFout( tempParam );
            //doesMatch = JHelper::matchMethod(env, sign, tempParam, &index, &jindex);
            doesMatch = JHelper::matchMethod(env, mParam, theMupadParam, tempParam, &index, &jindex);
          } else if (matchingMethod == MATCH_NATIVE) {                          // native match
              // If the signatures do not match, there is no need
              // to check the parameters.
              if (strcmp(methSig, JMethod::getSignature(env, method, NULL)) == 0) {
                if (_debug>0) MFprintf("invoke: %d,4) Calling JHelper::matchMethod with native match", j); //, MFexpr2text(tempParam));
                if (_debug>0) MFout( tempParam );
                //doesMatch = JHelper::matchMethod(env, sign, tempParam, &index, &jindex);
                doesMatch = JHelper::matchMethod(env, mParam, theMupadParam, tempParam, &index, &jindex);
              }
          } else {                                                              // match wrong !
            MFprintf("Matching method %d is not defined.\n", matchingMethod);   // Should never occur, since it is handed by MuPAD.
            MFreturn( MFcopy(MVfail) );
          }
          if (_debug>0) MFprintf("invoke: Signature does match.");
          if (doesMatch == JNI_TRUE) {
            if (_debug>0) MFprintf("TRUE)\n");
            paramValueArray[(j*nParams)+k] = jindex;                             // [j][k] = index
            paramJavaArray[(j*nParams)+k] = index;                             // [j][k] = jindex
            if (_debug>0) MFprintf("invoke: The [methodNr=%d][indexNr=%d]'s parameter got the value %d (Check=%d)\n", j, k, index, paramValueArray[(j*nParams)+k]);
            validMethodsArray[j] = 1;                                           // This method is a possible candidate.
          } else {
            // If a method has multiple parameters and one does not match,
            // the whole method can not be used !
            validMethodsArray[j] = 0;                                           // Reset the check.
            if (_debug>0) MFprintf("FALSE)\n");
            // Other parameters are of no interest any more...
            break;                                                              // to make sure it leaves the loop
          }
          //MFcfree(sign);

        } // for (int k=0...
      } // else
    } // if ((strcmp(param2 ...
    MFcfree(tmpChar);
    env->DeleteLocalRef(method);
  } // for (j=0; ...

  /* **********************************************************************************************
  3rd part:
  Matching the classes according to their parameters
  Here comes the check for the kind of match
   Step 1. : Check for name and parameter number simmilarity
   Step 2. : Put the candidate into a jobjectArray for further usage
   Step 3. : According to the parameters handed by MuPAD sort out methods that
             can not be used, although the parameter number was correct.
   Step 4. : According to the match do the following:
             1. first match  : take the first possible method out of the array.
             2. max match    : use a ranking system to rank the methods:
                               Integer        : BigInteger = 5, Long = 4,
                                                Int = 3, Short = 2, Byte = 1
                               Floating point : BigDecimal = 3, Double = 2,
                                                Float = 1
                               Text           : String = 2, Char = 1
                               Other          : Boolean = 1, Array = 100, unknown = 1, object = 1
             3. best match   : use the parameter with the smallest deviation
                               to the request
                               transfer numbers and check the size. According
                               to the size use the next smallest possible java
                               type
             4. native match : use the method which signature is given. (works)
             other possibilities:
             5. defined match: get the desired n'th match of the list.
             6. random match : get any method of the list (randomly)
  ************************************************************************************************* */
  if (_debug>0) MFprintf("3rd part ****************************************************************\n");
  if (nParams == 0) {
    if (_debug>0) MFprintf("No parameters...\n");
  }

  for (j=0; j < nMethods; j++) {
    if (validMethodsArray[j] == 1) {
      foundMethod = true;
    }
  }

  if (foundMethod == false) {                                                   // declared method could not be found in the specified class
    MFprintf("Method '%s' in class '%s' not available or parameters do not match:\n%s(%s).\n",param2, param1, param2, MFexpr2text(param3));
    //delete validMethodsArray;                                                   // free memory
    //delete paramValueArray;
    MFcfree(validMethodsArray);
    MFcfree(paramValueArray);
    MFcfree(paramJavaArray);
    MFreturn( MFcopy(MVfail) );                                                 // return error
  }

  jobject methodObj = NULL;                                                     // The chosen method will be put in this.

  for (k = 0; k < nMethods; k++) {
    methodObj = env->GetObjectArrayElement(classMethods, k);
    char *theMethodName = JMethod::getName(env, methodObj);
    if (_debug>0) MFprintf("Method %s %d.[%d] = ", theMethodName, k, validMethodsArray[k]);
    MFcfree(theMethodName);
    env->DeleteLocalRef(methodObj);
    for (j = 0; j < nParams; j++) {
      if (_debug>0) MFprintf("[%d,%d]", paramJavaArray[(k*nParams)+j], paramValueArray[(k*nParams)+j]);
    }
    if (_debug>0) MFprintf("\n");
  }
  if (_debug>0) MFprintf("\n");


  // Now check all the valid methods according to the chosen matching
  // Since match=4 can have only one match by definition, it will be
  // the first one found.
  if ((matchingMethod == MATCH_FIRST) || (matchingMethod == MATCH_NATIVE)) {
    for (j=0; j < nMethods; j++) {                                              // go through the list
      if (validMethodsArray[j] == 1) {                                          // if the j'th method is marked with 1
        methodObj = env->GetObjectArrayElement(classMethods, j);                // use it.
        fittingMethod = j;                                                      // adjust the array jumping point
        break;                                                                  // and get out (speedup)
      }
    }
    if (_debug>0) MFprintf("The chosen (first/native) method is %d.\n", fittingMethod);
  }
  // max match : use a ranking system to rank the methods:
  //             Integer        : BigInteger = 5, Long = 4,
  //                              Int = 3, Short = 2, Byte = 1
  //             Floating point : BigDecimal = 3, Double = 2,
  //                              Float = 1
  //             Text           : String = 2, Char = 1
  else if (matchingMethod == MATCH_MAX) {                                       // max match
    if (_debug>0) MFprintf("Started max method choosing...\n");
    int bMatch = 0;                                                             // stores the best ranking
    fittingMethod = -1;                                                         // the location within the array
    for (j=0; j < nMethods; j++) {
      if (validMethodsArray[j] == 1) {
        jobject method = env->GetObjectArrayElement(classMethods, j);
        // Calculate Ranking
        if (nParams == 0) {                                                     // check if the method has parameters
         fittingMethod = j;                                                     // if not, only one method is allowed anyways
         break;                                                                 // therefore leave now.
        }

        int rank = 0;
        for (k=0; k < nParams;k++) {
          rank += paramValueArray[(j*nParams)+k];                               // rank = paramValueArray[j][k]
        }
        if (_debug>0) MFprintf("The ranking for method %d with %d Parameter(s) is [%d].\n",j, nParams, rank);
        if (rank > bMatch) {
          bMatch = rank;
          fittingMethod = j;
        }
        env->DeleteLocalRef(method);
      }
    } // for (j=0...
    if (_debug>0) MFprintf("The max ranking method is %d with %d parameter(s).\n", fittingMethod, nParams);
    if (fittingMethod != -1) {                                                  // if types were mismatched, this one does of course not work!
      methodObj = env->GetObjectArrayElement(classMethods, fittingMethod);      // get the best method
    }
  }  // if (matchingMethod == 2)
  else if (matchingMethod == MATCH_BEST) {
    if (_debug>0) MFprintf("Started best method choosing...\n");
    int bMatch = 100000;                                                        // stores the best ranking (set to an arbitrary, very high init value)
    fittingMethod = -1;                                                         // the location within the array
    for (j=0; j < nMethods; j++) {
      if (validMethodsArray[j] == 1) {
        jobject method = env->GetObjectArrayElement(classMethods, j);
        // Calculate Ranking (the lower the better, since a minimum match is preferred !)
        int rank = 0;

        if (nParams == 0) {                                                     // check if the method has parameters
         fittingMethod = j;                                                     // if not, only one method is allowed anyways
         break;                                                                 // therefore leave now.
        }

        for (k=0; k < nParams;k++) {
          rank += paramValueArray[(j*nParams)+k];                               // rank = paramValueArray[j][k]
        }
        if (_debug>0) MFprintf("The ranking for method %d with %d Parameter(s) is [%d].\n",j, nParams, rank);
        if (rank < bMatch) {
          bMatch = rank;
          fittingMethod = j;
        }
        env->DeleteLocalRef(method);
      }
    } // for (j=0...
    if (_debug>0) MFprintf("The best ranking method with %d parameters is %d.\n", nParams, fittingMethod);
    if (fittingMethod != -1) {                                                  // if types were mismatched, this one does of course not work!
      methodObj = env->GetObjectArrayElement(classMethods, fittingMethod);      // get the best method
    }
  } else {
    if (_debug>0) MFprintf("matching %d is not implemented yet.\n", matchingMethod);
  }

  if (methodObj == NULL) {                                                      // declared method could not be found in the specified class
    if (_debug>0) MFprintf("Method '%s' in class '%s' not available or parameters do not match.\n",param2, param1);
//    delete validMethodsArray;                                                   // free memory
//    delete paramValueArray;                                                     // free memory
    MFcfree(validMethodsArray);
    MFcfree(paramValueArray);
    MFcfree(paramJavaArray);
    MFreturn( MFcopy(MVfail) );                                                 // return error
  }
  tmpChar = JMethod::toString(env, methodObj);
  if (_debug>0) MFprintf("Using method '%s'\n", tmpChar);
  MFcfree(tmpChar);

  env->DeleteLocalRef(classMethods);

  /* **********************************************************************************************
  4th part:
  convert MuPAD types according to match into Java data-types
  ************************************************************************************************* */
  if (_debug>0) MFprintf("4th part ****************************************************************\n");
  jvalue *argVals = (jvalue*) MFcmalloc(sizeof(jvalue) * nParams);                 // Allocate memory
  if (nParams > 0) {                                                            // If there are no parameters, skip over the creation process.
    if (NULL == argVals) {
      if (_debug>0) MFprintf("No more memory...\n");
      MFcfree(validMethodsArray);
      MFcfree(paramValueArray);
      MFcfree(paramJavaArray);
      MFreturn( MFcopy(MVfail) );                                               // return error
    }
    if (_debug>0) MFprintf("Method has %d parameter(s).\n", nParams);

    // Conversion types :
    // MuPAD - JNI      - C
    // bool    jboolean   bool
    // int     jbyte      byte
    // string  jstring    char (Kovertieren !!!)
    // int     jshort     short
    // int     jint       int
    // long    jlong      long
    // float   jfloat     float
    // double  jdouble    double
    // -       -          void
    // ?       jobject    ?


    // Maybe its fitting method (throwing the error)!!!
    for (int counter=0; counter < nParams; counter++) {
      if (_debug>0) MFprintf("Inside loop (%d) with %d entries ...\n", counter, MFnopsList(param3));
      tempParam = MFgetList(&param3, counter);

      if (_debug>0) MFprintf("Inside loop (%d) with %d entries ...\n", counter, MFnopsList(param3));
      if (_debug>0) MFprintf("Using method[%d] with value %d.\n",fittingMethod, paramJavaArray[(fittingMethod*nParams)+counter]);
      if (paramJavaArray[(fittingMethod*nParams)+counter] > JAVA_ARRAY) {      //It has to be an array !
       // Basically the same as with primitive parameters, but this one steps through
       // every array.
        jobjectArray jobA;
        if (_debug>0) MFprintf("Before calling the array creation.\n");
        jboolean createWorked = JHelper::createMultiJNIArray(env, tempParam, paramJavaArray[(fittingMethod*nParams)+counter], &jobA);
        if (_debug>0) MFprintf("Returned from calling the array creation.\n");
        argVals[counter].l = jobA;
        // If a env->DeleteLocalRef(jobA) is inserted here, the result will be a complete rubbish !
        if (createWorked == JNI_FALSE) {
          MFcfree(validMethodsArray);
          MFcfree(paramJavaArray);
          MFcfree(paramValueArray);
          env->DeleteLocalRef(jobA);                                            // To free memory
          MFprintf("Creation of array did not work. Returning...\n");
          MFreturn(MFcopy(MVfail));
         }
        if (_debug>0) MFprintf("Parameter %d array done.\n", counter);
      } else
      switch (paramJavaArray[(fittingMethod*nParams)+counter]) {               // paramValueArray[fittingMethod][counter]
        case JAVA_BOOLEAN : //MFprintf("Parameter %d: boolean.\n", counter);    // boolean
                 argVals[counter].z = (jboolean) MFbool(tempParam);             // put the boolean in the array
                 break;
        case JAVA_CHAR : //MFprintf("Parameter %d: char.\n", counter);          // char
                 //const char *paramS = MFstring(MFgetList(&param3, j));
                 argVals[counter].c = (jchar) MFstring(tempParam)[0];           // put the jchar in the array
                 break;
        case JAVA_BYTE : //MFprintf("Parameter %d: byte.\n", counter);          // byte
                 argVals[counter].b = (jbyte) MFint(tempParam);                 // convert parameter to byte and put it in jvalue list
                 break;
        case JAVA_SHORT : //MFprintf("Parameter %d: short.\n", counter);        // short
                 argVals[counter].s = (jshort) MFint(tempParam);                // convert parameter to short and put it in jvalue list
                 break;
        case JAVA_INT : //MFprintf("Parameter %d: integer.\n", counter);        // integer
                 argVals[counter].i = (jint) MFint(tempParam);                  // convert parameter to integer and put it in jvalue list
                 break;
        case JAVA_LONG : //MFprintf("Parameter %d: long.\n", counter);          // long (64bit!)
                 //MFprintf("Value (again..)='%s' ...\n", tempParam);
                 //testLong = (jlong) JHelper::text2jlong(env, MFexpr2text(tempParam));
                 //MFprintf("Put into the object : '%s'\n", JHelper::jlong2text(env, testLong));
                 argVals[counter].j = JHelper::text2jlong(env, MFexpr2text(tempParam));
                 break;
        case JAVA_FLOAT : //MFprintf("Parameter %d: float.\n", counter);        // float
                 //MFprintf("It's value: %f.\n", MFfloat(tempParam));
                 argVals[counter].f = (jfloat) MFfloat(tempParam);              // convert parameter to float and put it in jvalue list
                 break;
        case JAVA_DOUBLE : //MFprintf("Parameter %d: double.\n", counter);      // double
                 argVals[counter].d = (jdouble) MFdouble(tempParam);            // convert parameter to double and put it in jvalue list
                 break;
        case JAVA_BIGINTEGER: //MFprintf("Parameter %d: BigInteger.\n", counter);// BigInteger
                 argVals[counter].l = (jobject) JHelper::text2object(env, MFexpr2text(tempParam), "java/math/BigInteger");
                 break;
        case JAVA_BIGDECIMAL: //MFprintf("Parameter %d: BigDecimal.\n", counter);// BigDecimal
                 argVals[counter].l = (jobject) JHelper::text2object(env, MFexpr2text(tempParam), "java/math/BigDecimal");
                 break;
        case JAVA_STRING: //MFprintf("Parameter %d: string.\n", counter);       // string
                 argVals[counter].l = (jobject) JSystem::NewPlatformString(env, MFstring(tempParam)); // put the string object in the array
                 //argVals[counter].l = (jobject) env->NewStringUTF(MFstring(tempParam)); // put the string object in the array
                 break;
        case JAVA_OBJECT: //MFprintf("Parameter %d: object.\n", counter);       // string
                 if (strcmp(MFstring(tempParam), "@NULL") == 0)
                 {
                   //MFprintf("Setting the %d's argument to NULL.\n", counter);
                   argVals[counter].l = NULL;
                 }
                 else
                 {
                   argVals[counter].l = NULL; // null since there is no information...
                 }
                 break;
        default : //MFprintf("Parameter %d: unknown.\n", counter);       // any Object
                 otherObject = JSystem::HO_getObject(env, MFstring(tempParam)); // put the string object in the array
                 if (otherObject == NULL)
                 {
                   MFprintf("The object that was requested does not exist in the list...\n");
                   MFcfree(validMethodsArray);
                   MFcfree(paramJavaArray);
                   MFcfree(paramValueArray);
                   if (argVals != NULL)
                     MFcfree(argVals);
                   MFreturn(MFcopy(MVfail));
                   break;
                 }
                 if (_debug>0) MFprintf("Got an object...\n");
                 tmpChar = JClass::getSignature(env, env->GetObjectClass(otherObject));
                 if (_debug>0) MFprintf("Signature is called : %s\n", tmpChar);
                 MFcfree(tmpChar);
                 argVals[counter].l = otherObject; // (jclass)
                 break;
      }

    } // for (j=0;
  } else {
    argVals = NULL;                                                             // Since 0 * k = 0 memory leaks are avoided !
  }

  if (_debug>0) MFprintf("After the creation of the parameters and before deleting...\n");
  /* Free the memory used for creating the array */
  MFcfree(validMethodsArray);
  MFcfree(paramJavaArray);
  MFcfree(paramValueArray);


  /* **********************************************************************************************
  5th part:
  registering the native method
  ************************************************************************************************* */
  // Remember : the method is stored in methodObj
  //            the class is stored in mClass
  if (_debug>0) MFprintf("5th part ****************************************************************\n");
  jmethodID mid;
  jint retType = 0;                                                             // to store the return type of the method
  tmpChar = JClass::toString(env, mClass);
  if (_debug>0) MFprintf("Class is called '%s'\n" , tmpChar);
  MFcfree(tmpChar);
  if (_debug>0) MFprintf("Before getting the signature.\n");
  const char *signature = JMethod::getSignature(env, methodObj, &retType);
  if (_debug>0) MFprintf("Signature is %s\n", signature);
  if (_debug>0) MFprintf("Return type coding '%d'\n", retType);

  // Registering the native method "Java_MuPAD_JNI_calcMuPAD"
  if (JHelper::hasNativeMethod(env, mClass) == JNI_TRUE) {
    if (_debug>0) MFprintf("Now registering method calcMuPAD so the class can use it.\n");
    JNINativeMethod jni_methods[] = {"calcMuPAD",
                                     "(Ljava/lang/String;)Ljava/lang/String;",
                                    (void*)Java_calcMuPAD };
   env->RegisterNatives(mClass, jni_methods, 1);
  }
  /* **********************************************************************************************
  6th part:
  invoking the method
  ************************************************************************************************* */
  if (_debug>0) MFprintf("6th part ****************************************************************\n");

  // MFprintf("Please switch to JAVA-Program...\n");
  // Check if the method is static or non-static and make the call accordingly to the return value:
  if (JModifier::isStatic(env, JMethod::getModifiers(env, methodObj)) == JNI_TRUE) {   // Jup, its static
    if (_debug>0) MFprintf("Going to invoke a static method.\n");
/*
    // To test where the memory leak occurs.
    jobject test = NULL;
    retValue.l = test;
    retType = JAVA_VOID;
*/
    if (showMethodChosen) {
      tmpChar = JMethod::toString(env, methodObj);
      MFprintf("Using static method '%s'\n", tmpChar);
      MFcfree(tmpChar);
    }
    if (_debug>1) MFprintf("Before getting static methodID.\n");
    mid = env->GetStaticMethodID(mClass, param2, signature);                           // Get the methodID of the method
    if (NULL == mid) {                                                                 // Uups, it can not be located
      // This should NEVER be happening, because the existence
      // was already checked earlier in the procedure.
      MFprintf("Can not locate static method %s? Probably out of memory...\n", param2);
      MFcfree(argVals);
      MFcfree(signature);
      MFreturn(MFcopy(MVfail));
    }
    // method exists, it is static. We are all happy.
    if (_debug>1) MFprintf("Now invoking static method.\n");

    switch (retType) {
      case JAVA_BOOLEAN : retValue.z = env->CallStaticBooleanMethodA(mClass, mid, argVals); break;   // boolean
      case JAVA_CHAR : retValue.c = env->CallStaticCharMethodA(mClass, mid, argVals); break;      // char
      case JAVA_BYTE : retValue.b = env->CallStaticByteMethodA(mClass, mid, argVals); break;      // byte
      case JAVA_SHORT : retValue.s = env->CallStaticShortMethodA(mClass, mid, argVals); break;     // short
      case JAVA_INT : retValue.i = env->CallStaticIntMethodA(mClass, mid, argVals); break;       // int
      case JAVA_LONG : retValue.j = env->CallStaticLongMethodA(mClass, mid, argVals);
               //MFprintf("Got long number %d ...\n", retValue.j);
               //MFprintf("Converted to String : '%s' ...\n", JHelper::jlong2text(env, retValue.j));
               break;      // long
      case JAVA_FLOAT : retValue.f = env->CallStaticFloatMethodA(mClass, mid, argVals); break;     // float
      case JAVA_DOUBLE : retValue.d = env->CallStaticDoubleMethodA(mClass, mid, argVals); break;    // double
      case JAVA_VOID : env->CallStaticVoidMethodA(mClass, mid, argVals); break;                   // void
      default : //MFprintf("Invoking static default jobject Method...\n");
                retValue.l = (jobject) env->CallStaticObjectMethodA(mClass, mid, argVals); break;   // either something with an array or an object type
    }
    //if (exceptionNotCaught) {
      JSystem::checkForException(env);
    //}

  } else {                                                                      // Nope, it is not.
    if (_debug>0) MFprintf("Going to invoke a non-static method.\n");
    if (showMethodChosen) {
      tmpChar = JMethod::toString(env, methodObj);
      MFprintf("Now going to invoke non static method '%s'\n", tmpChar);
      MFcfree(tmpChar);
    }
    // First the basic constructor has to be invoked to avoid problems

    if (isConstructor == JNI_TRUE)
    {
      if (_debug>0) MFprintf("Before getting construcor with signature %s.\n", signature);
      mid = env->GetMethodID(mClass, "<init>", signature);                        // Get the methodID of the constructor
      if (_debug>0) MFprintf("After getting constructor methodID.\n");
      if (NULL == mid) {                                                          // Uups, it can not be located
        // This should NEVER be happening, because the existence
        // was already checked earlier in the procedure.
        MFprintf("Can not locate constructor without parameters.\n");
        MFprintf("Create an object and use option UseObject=object instead.\n");
        MFcfree(argVals);
        MFcfree(signature);
        MFreturn(MFcopy(MVfail));
      }
      retValue.l = env->NewObjectA(mClass, mid, argVals);               // Instanciate the class using a constructor with no arguments (default)
      if (_debug>0) MFprintf("After creating the object.\n");
      retType = JAVA_UNKNOWN;
    }
    else
    {
      if (theStoredObject == NULL)
      {
        if (_debug>0) MFprintf("Before getting construcor methodID.\n");
        mid = env->GetMethodID(mClass, "<init>", "()V");                            // Get the methodID of the constructor
        if (_debug>0) MFprintf("After getting constructor methodID.\n");
        if (NULL == mid) {                                                          // Uups, it can not be located
          // This should NEVER be happening, because the existence
          // was already checked earlier in the procedure.
          MFprintf("Can not locate constructor without parameters.\n");
          MFprintf("Create an object and use option UseObject=object instead.\n");
          MFreturn(MFcopy(MVfail));
        }
        // This also automatically initializes the class unlike env->AllocObject()...
        jobject obj = env->NewObject(mClass, mid, NULL);                            // Instanciate the class using a constructor with no arguments (default)
        mid = env->GetMethodID(mClass, param2, signature);                          // Get the methodID of the method

        if (NULL == mid) {                                                          // Uups, it can not be located
          // This should NEVER be happening, because the existence
          // was already checked earlier in the procedure.
          MFprintf("Can not locate non-static method %s? Probably out of memory...\n", param2);
          MFcfree(argVals);
          MFcfree(signature);
          MFreturn(MFcopy(MVfail));
        }

        // method exists, it is not static. We are even more happy.

        switch (retType) {
          case JAVA_BOOLEAN : retValue.z = env->CallBooleanMethodA(obj, mid, argVals); break;   // boolean
          case JAVA_CHAR : retValue.c = env->CallCharMethodA(obj, mid, argVals); break;     // char
          case JAVA_BYTE : retValue.b = env->CallByteMethodA(obj, mid, argVals); break;     // byte
          case JAVA_SHORT : retValue.s = env->CallShortMethodA(obj, mid, argVals); break;    // short
          case JAVA_INT : retValue.i = env->CallIntMethodA(obj, mid, argVals); break;      // int
          case JAVA_LONG : retValue.j = env->CallLongMethodA(obj, mid, argVals); break;     // long
          case JAVA_FLOAT : retValue.f = env->CallFloatMethodA(obj, mid, argVals); break;    // float
          case JAVA_DOUBLE : retValue.d = env->CallDoubleMethodA(obj, mid, argVals); break;   // double
          case JAVA_VOID : env->CallVoidMethodA(obj, mid, argVals); break;                  // void
          default : retValue.l = (jobject) env->CallObjectMethodA(obj, mid, argVals); break;   // either something with an array or an object type
        }

        // finalize class (and therefore garbage-collect)
        // does not always seem to be a good idea.
        /*
        if (!env->ExceptionOccurred()) {
          mid = env->GetMethodID(mClass, "finalize", "()V");                            // Get the methodID of the constructor
          env->CallVoidMethodA(obj, mid, NULL);
        }
        */
        env->DeleteLocalRef(obj);
      }
      else
      {
        if (_debug>0) MFprintf("Getting object class.\n");
        jclass obj = env->GetObjectClass(theStoredObject);
        if (_debug>0) MFprintf("Getting object mid.\n");
        mid = env->GetMethodID(obj, param2, signature);                          // Get the methodID of the method

        if (NULL == mid) {                                                          // Uups, it can not be located
          // This should NEVER be happening, because the existence
          // was already checked earlier in the procedure.
          MFprintf("Can not locate non-static method %s? Probably out of memory...\n", param2);
          MFcfree(argVals);
          MFcfree(signature);
          MFreturn(MFcopy(MVfail));
        }
        if (_debug>0) MFprintf("Invoking theStoredObject with mid.\n");
        switch (retType) {
          case JAVA_BOOLEAN : retValue.z = env->CallBooleanMethodA(theStoredObject, mid, argVals); break;   // boolean
          case JAVA_CHAR : retValue.c = env->CallCharMethodA(theStoredObject, mid, argVals); break;     // char
          case JAVA_BYTE : retValue.b = env->CallByteMethodA(theStoredObject, mid, argVals); break;     // byte
          case JAVA_SHORT : retValue.s = env->CallShortMethodA(theStoredObject, mid, argVals); break;    // short
          case JAVA_INT : retValue.i = env->CallIntMethodA(theStoredObject, mid, argVals); break;      // int
          case JAVA_LONG : retValue.j = env->CallLongMethodA(theStoredObject, mid, argVals); break;     // long
          case JAVA_FLOAT : retValue.f = env->CallFloatMethodA(theStoredObject, mid, argVals); break;    // float
          case JAVA_DOUBLE : retValue.d = env->CallDoubleMethodA(theStoredObject, mid, argVals); break;   // double
          case JAVA_VOID : env->CallVoidMethodA(theStoredObject, mid, argVals); break;                  // void
          default : //MFprintf("Invoking non static default jobject Method...\n");
                    retValue.l = (jobject) env->CallObjectMethodA(theStoredObject, mid, argVals); break;   // either something with an array or an object type
        }

        // Just a try...
        //if (_debug>0) MFprintf("before replacing object with theStoredObject.\n");
        //JSystem::HO_replaceObject(env, useObjectName, theStoredObject);

        // finalize class (and therefore garbage-collect)
        /*
        if (!env->ExceptionOccurred()) {
          mid = env->GetMethodID(obj, "finalize", "()V");                            // Get the methodID of the constructor
          env->CallVoidMethodA(theStoredObject, mid, NULL);
        }
        */
        env->DeleteLocalRef(theStoredObject);
      }
    }
  } // else
  if (_debug>0) MFprintf("Very close to the ending...\n");

  JSystem::checkForException(env);

  if (_debug>0) MFprintf("Before freeing argVals...\n");
  MFcfree(signature);
  MFcfree(argVals);
  env->DeleteLocalRef(mClass);
  if (otherObject != NULL)
  {
    env->DeleteLocalRef(otherObject);
  }

  if (_debug>0) MFprintf("After freeing argVals...\n");
  if (_debug>0) MFprintf("Before checking retType...\n");

  MTcell mupad_result;

  if (retType > 100) {
    if ((jobject)retValue.l == NULL)
    {
      mupad_result = MFcopy(MVnull);
    } else {
    mupad_result = JHelper::createMultiMuPADList(env, (jobjectArray)(jobject)retValue.l, retType);
    env->DeleteLocalRef(retValue.l);
    }
    MFreturn ( mupad_result )        // returns the (multiple) lists created by Java
  }

  jstring tmpString;                                                            // for the unicode string (and therefore the jchar)
  jboolean createObjectWorked;

  env->DeleteLocalRef(methodObj);

  // If it came to here, there are DEFINITELY no arrays involved...
  switch (retType) {
    case JAVA_BOOLEAN :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFbool(retValue.z);
      MFreturn( mupad_result );
      break;                  // returns TRUE or FALSE
    case JAVA_CHAR :
      //env->DeleteLocalRef(methodObj);
      tmpString = env->NewString(&retValue.c, 1);                // transform the jchar to a unicode string
      tmpChar = JHelper::GetNativeChars(env, tmpString);
      mupad_result = MFstring(tmpChar);
      env->DeleteLocalRef(tmpString);
      MFcfree(tmpChar);
      MFreturn( mupad_result );
     break;                                                             // returns the correct NULL-terminated char.
    case JAVA_BYTE :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFint((int)retValue.b);
      MFreturn( mupad_result );
      break;                 // byte is smaller than int (ok)
    case JAVA_SHORT :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFint((int)retValue.s);
      MFreturn( mupad_result );
      break;                // short is smaller than int (ok)
    case JAVA_INT :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFint((int)retValue.i);
      MFreturn( mupad_result );
      break;                  // this is the int
    // Since java uses 64bit for a long, and c++ only 32, it
    // has to be converted to text before returning.
    case JAVA_LONG :
      //env->DeleteLocalRef(methodObj);
      tmpChar = JHelper::jlong2text(env, retValue.j);
      mupad_result = MFtext2expr(tmpChar);
      MFcfree(tmpChar);
      MFreturn( mupad_result );
      break;
    case JAVA_FLOAT :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFfloat(retValue.f);
      MFreturn( mupad_result );
      break;                   // this is a true float
    case JAVA_DOUBLE :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFdouble(retValue.d);
      MFreturn( mupad_result );
      break;                           // this is a true double
    case JAVA_VOID :
      //env->DeleteLocalRef(methodObj);
      mupad_result = MFcopy(MVnull);
      MFreturn( mupad_result );
      break;                         // void returns nothing
    case JAVA_BIGINTEGER:
      methodObj = (jobject)retValue.l;                      // a BigInteger
      if (methodObj == NULL)
      {
        // The return value is problematic !!!
        MFprintf("The return value was NULL...\n");
        MFreturn(MFcopy(MVnull));
      }
      tmpChar = JHelper::object2text(env, methodObj);
      mupad_result = MFtext2expr(tmpChar);
      env->DeleteLocalRef(methodObj);
      MFcfree(tmpChar);
      MFreturn( mupad_result );
      break;                           // returns the correct NULL-terminated char.
    case JAVA_BIGDECIMAL:
      methodObj = (jobject)retValue.l;                      // a BigDecimal
      if (methodObj == NULL)
      {
        // The return value is problematic !!!
        MFprintf("The return value was NULL...\n");
        MFreturn(MFcopy(MVnull));
      }

      tmpChar = JHelper::object2text(env, methodObj);
      mupad_result = MFtext2expr(tmpChar);
      env->DeleteLocalRef(methodObj);
      MFcfree(tmpChar);
      MFreturn( mupad_result );
      break;                           // returns the correct NULL-terminated char.
    case JAVA_STRING:
      tmpString = (jstring)retValue.l;                          // a String
      if (tmpString == NULL)
      {
        // The return value is problematic !!!
        MFprintf("The return value was NULL...\n");
        MFreturn(MFcopy(MVnull));
      }

      tmpChar = JHelper::GetNativeChars(env, tmpString);
      mupad_result = MFstring(tmpChar);
      //env->DeleteLocalRef(methodObj);
      MFcfree(tmpChar);
      MFreturn( mupad_result );
      break;                                   // returns the correct NULL-terminated char.
    default: //savedReference = env->NewGlobalRef((jobject)retValue.l);         // save result in a global reference...
             // later there has to be a list or something...
             // maybe a MFjava that holds a reference to a java object...
             // also this globlaReference is never to be freed from memory !!!
             // This produces memory leaks. How to avoid something in MuPAD like
             // a := java::Test::getObject();
             // a := 5;
             // First a java and then another value is assigned to a.
             //MFreturn( MFstring("@MUPAD001") );                                // return @ + name in the list.
             //break;
             methodObj = (jobject)retValue.l;
             if (methodObj == NULL)
             {
               // The return value is problematic !!!
               MFprintf("The return value was NULL...\n");
               MFreturn(MFcopy(MVnull));
             }

             tmpChar = (char *) MFcmalloc(20 * sizeof(char));
             sprintf(tmpChar, "@MuPAD_JNI_%08d", oH_counter);
             if (_debug>0) MFprintf("Before creating createObjectWorked with MuPAD_JNI_%d...\n", oH_counter);
             createObjectWorked = JSystem::addToList(env, tmpChar, methodObj);
             if (_debug>0) MFprintf("After creating creatObjectWorked...\n");
             if (createObjectWorked == JNI_TRUE) {
               if (_debug>0) MFprintf("The creation of the object did work. It is stored as %s\n", tmpChar);
               // tmpChar is not being fred !!!
               oH_counter++;
               mupad_result = MFstring(tmpChar);
               env->DeleteLocalRef(methodObj);
               MFcfree(tmpChar);
               MFreturn( mupad_result );
               break;
             } else {
               MFprintf("The creation of the object failed.\n");
               env->DeleteLocalRef(methodObj);
               MFcfree(tmpChar);
               MFreturn( MFstring(param2) );
               break;
             }
  }
  // Never reached
  MFreturn(MFcopy(MVfail));
} MFEND

/**
  * list
  * @param String1 the name of the class.
  * @param String2 the access specifier (public/protected/private).
  * @param boolean If TRUE, superclasses are involved, too. Then only the public methods are shown.
  * @param boolean If TRUE, a java box opens and displays the properties.
  * @return a list containing three lists [[constructors],[methods],[fields]]
  */
MFUNC(list, MCnop) {
  char *mName;
  int arrayLength, i;
  jobjectArray dataArray;                                                       // to store the array of objects
  jobject data;                                                                 // for a single object

  if (jvm == NULL || env == NULL) {
    MFerror("No JVM started");
  } else if (killed) {
    MFprintf("Although machine was killed it does not recognize it.\nThis is a well known bug from SUN!.\n");
    //jvm->DestroyJavaVM();
    //jvm = NULL;
    //env = NULL;
    MFerror("Please (re)connect the notebook in MuPAD...");
  }

  if (_debug>0) MFprintf("listMethods: jvm is running...\n");

  jclass pClass = env->FindClass(MFstring(MFarg(1)));                           // pointer to the class we deal with

  if (pClass == NULL) {
    if (showErrors == JNI_TRUE) {
      JSystem::checkForException(env);
      MFprintf("Error: Class not found '%s'.", MFstring(MFarg(1)));
    } else {
      env->ExceptionClear();  // If no exception was thrown it has no effects anyways.
    }
    MFreturn( MFcopy(MVfail) );
  }

  /*
   * Create MuPAD list for constructors
   */
  if (_debug>0) MFprintf("creating constructors...\n");
  dataArray = JClass::getConstructors(env, pClass, JNI_FALSE);                  // Store the array of available constructors
  arrayLength = env->GetArrayLength(dataArray);                                 // Get the number of objects

  MTcell constructorList = MFnewList(arrayLength);
  for (i=0; i < arrayLength; i++) {                                             // go through all method objects
    data = env->GetObjectArrayElement(dataArray, i);                            // get the i-th object from the list
    mName = JMethod::toString(env, data);
    MFopSet(constructorList, i, MFstring(mName));
    MFcfree(mName);
    env->DeleteLocalRef(data);
  }
  MFsig(constructorList);
  env->DeleteLocalRef(dataArray);
  if (_debug>0) MFprintf("constructors available %d\n", MFnops(constructorList));

  /*
   * Create MuPAD list for methods (only those of the class itself [including private])
   */
  if (_debug>0) MFprintf("creating methods...\n");
  dataArray = JClass::getMethods(env, pClass, JNI_FALSE);                       // Store the array of available methods
  arrayLength = env->GetArrayLength(dataArray);                                 // Get the number of objects

  MTcell methodList = MFnewList(arrayLength);
  for (i=0; i < arrayLength; i++) {                                             // go through all method objects
    data = env->GetObjectArrayElement(dataArray, i);                            // get the i-th object from the list
    mName = JMethod::toString(env, data);
    MFopSet(methodList, i, MFstring(mName));
    MFcfree(mName);
    env->DeleteLocalRef(data);
  }
  MFsig(methodList);
  env->DeleteLocalRef(dataArray);
  if (_debug>0) MFprintf("methods available %d\n", MFnops(methodList));

  /*
   * Create MuPAD list for fields (only those of the class itself [including private])
   */
  if (_debug>0) MFprintf("creating fields...\n");
  dataArray = JClass::getFields(env, pClass, JNI_FALSE);                        // Store the array of available fields
  arrayLength = env->GetArrayLength(dataArray);                                 // Get the number of objects

  MTcell fieldList = MFnewList(arrayLength);
  for (i=0; i < arrayLength; i++) {                                             // go through all method objects
    data = env->GetObjectArrayElement(dataArray, i);                            // get the i-th object from the list
    mName = JMethod::toString(env, data);
    MFopSet(fieldList, i, MFstring(mName));
    MFcfree(mName);
    env->DeleteLocalRef(data);
  }
  MFsig(fieldList);
  env->DeleteLocalRef(dataArray);
  if (_debug>0) MFprintf("fields available %d\n", MFnops(fieldList));


  /*
   * Create MuPAD list for methods (also superclasses [only public])
   */
  if (_debug>0) MFprintf("creating methods2...\n");
  dataArray = JClass::getMethods(env, pClass, JNI_TRUE);                        // Store the array of available methods
  arrayLength = env->GetArrayLength(dataArray);                                 // Get the number of objects

  MTcell methodList2 = MFnewList(arrayLength);
  for (i=0; i < arrayLength; i++) {                                             // go through all method objects
    data = env->GetObjectArrayElement(dataArray, i);                            // get the i-th object from the list
    mName = JMethod::toString(env, data);
    MFopSet(methodList2, i, MFstring(mName));
    MFcfree(mName);
    env->DeleteLocalRef(data);
  }
  MFsig(methodList2);
  env->DeleteLocalRef(dataArray);
  if (_debug>0) MFprintf("methods2 available %d\n", MFnops(methodList2));

  /*
   * Create MuPAD list for fields (also superclasses [only public])
   */
  if (_debug>0) MFprintf("creating fields2...\n");
  dataArray = JClass::getFields(env, pClass, JNI_TRUE);                        // Store the array of available fields
  arrayLength = env->GetArrayLength(dataArray);                                 // Get the number of objects

  MTcell fieldList2 = MFnewList(arrayLength);
  for (i=0; i < arrayLength; i++) {                                             // go through all method objects
    data = env->GetObjectArrayElement(dataArray, i);                            // get the i-th object from the list
    mName = JMethod::toString(env, data);
    MFopSet(fieldList2, i, MFstring(mName));
    MFcfree(mName);
    env->DeleteLocalRef(data);
  }
  MFsig(fieldList2);
  env->DeleteLocalRef(dataArray);
  if (_debug>0) MFprintf("fields2 available %d\n", MFnops(fieldList2));

  env->DeleteLocalRef(pClass);

  MTcell outerList = MFnewList(5);
  MFopSet(outerList, 0, constructorList);
  MFopSet(outerList, 1, methodList);
  MFopSet(outerList, 2, fieldList);
  MFopSet(outerList, 3, methodList2);
  MFopSet(outerList, 4, fieldList2);

  MFsig(outerList);

  MFreturn(outerList);

} MFEND

/**
  * start
  * starts a java virtual machine for MuPAD.
  * @param String1 the classpath.
  * @param String2 the library path.
  * @param String3 the dll path.
  * @return true if it worked, false otherwise.
  */
MFUNC(start, MCnop) {

  char *cp;
  char *lp;
  char *dp;

  jboolean useSecurityManager;
  jboolean writeLogToFile;       // Default is to write in window (FALSE).
  MTcell addParam;

  if (_debug>0) MFprintf("started start.\n");


  MFargCheck( 1, DOM_STRING );
  MFargCheck( 2, DOM_STRING );
  MFargCheck( 3, DOM_STRING );
  MFargCheck( 4, DOM_BOOL );
  MFargCheck( 5, DOM_BOOL );

  cp = MFstring(MFarg(1));
  lp = MFstring(MFarg(2));
  dp = MFstring(MFarg(3));
  useSecurityManager = MFbool(MFarg(4));
  writeLogToFile = MFbool(MFarg(5));

  if (MVnargs >= 6) {
    MFargCheck( 6, DOM_LIST );
    addParam = MFarg(6);
  } else {
    addParam = MFcopy(MVnull);
  }

  // Start JavaVM
  MTcell started = MFbool(0==initJVM(cp, lp, dp, useSecurityManager, writeLogToFile, addParam));
  MFreturn( started );
  //MFreturn( MFbool(0==initJVM(cp, lp, dp, ldp)) );
} MFEND

/**
  * setDebug
  * sets the debug mode according to the parameter
  * @param Integer The debug - value.
  * @return The new debug value (if greater than 2 it will be 2)
  */
MFUNC(debug, MCnop)
{
  if (MVnargs >= 1) {
    MFargCheck( 1, DOM_INT );
    _debug = MFint(MFarg(1));
    if (_debug > 3)
      _debug = 3;
  }

  MFreturn(MFint(_debug))

} MFEND


/** stop
 * stops a JVM and unloads it.
 * This tells the JVM to stop. Right now (since about 1998) this does not work.
 * This is a bug known by sun.
 * read http://developer.java.sun.com/developer/bugParade/bugs/4093633.html
 * for moore information on this bug.
 */
MFUNC(stop, MCnop)
{

  if (_debug>0) MFprintf("started stop.\n");
  if (_debug>0) MFprintf("Before getting status.\n");
  if ( killed == JNI_FALSE) {
    if (jvm->DetachCurrentThread() != 0) {
      MFprintf("Could not detach main thread. Try to kill the JVM anyway.\n");
    }
    jvm->DestroyJavaVM();
  }
  // unload the dll
  if (dll != NULL) {
    if (_debug>0) MFprintf("Before freeing dll.\n");
    if (!freeDLL(dll)) {
#if defined WIN32
      DWORD dw = GetLastError();
      MFprintf("FreeLibrary failed: GetLastError returned %u\n", dw);
#elif defined __linux__
      MFprintf("dlClose failed: returned was %s\n", strerror(errno));
#endif
    } else
      MFprintf("FreeLibrary was okay.\n");
//    if (!FreeLibrary(dll)) {
//      DWORD dw = GetLastError();
//      MFprintf("FreeLibrary failed: GetLastError returned %u\n", dw);
//    } else
//      MFprintf("FreeLibrary was okay.\n");
//    if (!FreeLibrary(dll)) {
//      DWORD dw = GetLastError();
//      MFprintf("FreeLibrary failed: GetLastError returned %u\n", dw);
//    } else
//      MFprintf("FreeLibrary was okay.\n");
  }
  if (_debug>0) MFprintf("Before resetting pointers.\n");
  // set pointers to NULL.
  jvm = NULL;
  env = NULL;
  dll = NULL;

  // and also reset the flag killed
  killed = JNI_FALSE;
  if (_debug>0) MFprintf("Before returning.\n");
  // Always returns true.
  MFreturn( MFbool(0==0) );                                                     // Return true
} MFEND

MFUNC(callHO_getList, MCnop) {
  jboolean showInJava = JNI_FALSE;

  if (MVnargs == 1)
    showInJava = MFbool(MFarg(1));

  MTcell mupad_result = JSystem::HO_getList(env, showInJava);

  MFreturn( mupad_result );
} MFEND

MFUNC(callHO_getSignature, MCnop) {
  MFargCheck( 1, DOM_STRING );
  char *param1 = MFstring(MFarg(1));
  char *sign = JSystem::HO_getSignature(env, param1);
  if (sign == NULL)
  {
    MFprintf("No Java Object could be mapped to %s\n", param1);
    MFreturn( MFcopy(MVfail) );
  }
  if (_debug>0) MFprintf("Got as signature %s\n", sign);
  MTcell mupad_result = MFstring(sign);
  MFcfree(sign);
  MFreturn( mupad_result );
} MFEND

MFUNC(callHO_getClassname, MCnop) {
  MFargCheck( 1, DOM_STRING );
  char *param1 = MFstring(MFarg(1));
  if (_debug>0) MFprintf("In callHO_removeObject with parameter %s\n", param1);
  char *sign = JSystem::HO_getClassname(env, param1);
  if (sign == NULL)
  {
    MFprintf("No Java Object could be mapped to %s\n", param1);
    MFreturn( MFcopy(MVfail) );
  }
  if (_debug>0) MFprintf("Got as classname %s\n", sign);
  MTcell mupad_result = MFstring(sign);
  MFcfree(sign);
  MFreturn( mupad_result );
} MFEND

MFUNC(callHO_removeObject, MCnop) {
  MFargCheck( 1, DOM_STRING );
  char *param1 = MFstring(MFarg(1));
  if (_debug>0) MFprintf("In callHO_removeObject with parameter %s\n", param1);
  JSystem::HO_removeObject(env, param1);
  MFreturn( MFcopy(MVnull) );
} MFEND

MFUNC(callHO_clearList, MCnop) {
  if (_debug>0) MFprintf("In callHO_clearList\n");
  JSystem::HO_clearList(env);
  MFreturn( MFcopy(MVnull) );
} MFEND

MFUNC(garbageCollect, MCnop) {
  if (_debug>0) MFprintf("In garbageCollect\n");
  JSystem::garbageCollect(env);
  MFreturn( MFcopy(MVnull) );
} MFEND

MFUNC(finalize, MCnop) {
  if (_debug>0) MFprintf("In finalize\n");
  JSystem::runFinalization(env);
  MFreturn( MFcopy(MVnull) );
} MFEND

MFUNC(setShowErrors, MCnop) {
  MFargCheck( 1, DOM_BOOL );
  if (_debug>0) MFprintf("In setShowErrors\n");
  showErrors = MFbool(MFarg(1));
  MFreturn( MFcopy(MVnull) );
} MFEND


MFUNC(returnFourtyTwo, MCnop) {
  MFreturn( MFint(42) );
} MFEND

