// Test to check the exception classes in the throws attribute of the typemaps and except feature is working

%module java_throws

// throw is invalid in C++17 and later, only SWIG to use it
#define TESTCASE_THROW1(T1) throw(T1)
%{
#define TESTCASE_THROW1(T1)
%}

// Exceptions are chosen at random but are ones which have to have a try catch block to compile
%typemap(in, throws="	 ClassNotFoundException") int num { 
    $1 = (int)$input;
}
%typemap(freearg, throws="InstantiationException  ") int num "/*not written*/"
%typemap(argout, throws="CloneNotSupportedException		") int num "/*not written*/"
%typemap(check, throws="NoSuchFieldException") int num {
    if ($input == 10) {
        jenv->ExceptionClear();
        jclass excep = jenv->FindClass("java/lang/NoSuchFieldException");
        if (excep)
            jenv->ThrowNew(excep, "Value of 10 not acceptable");
        return $null;
    }
}

// Duplicate exceptions should be removed from the generated throws clause
%typemap(out, throws="IllegalAccessException, NoSuchFieldException,   CloneNotSupportedException    ") short { 
    $result = (jshort)$1; 
}

%inline %{
short full_of_exceptions(int num) {
    return 0;
}
%}


%typemap(throws, throws="IllegalAccessException") int {
    (void)$1;
    jclass excep = jenv->FindClass("java/lang/IllegalAccessException");
    if (excep) {
        jenv->ThrowNew(excep, "Test exception");
    }
    return $null;
}
%inline %{
bool throw_spec_function(int value) TESTCASE_THROW1(int) { throw (int)0; }
%}

%catches(int) catches_function(int value);
%inline %{
bool catches_function(int value) { throw (int)0; }
%}

// Check newfree typemap throws attribute
%newobject makeTestClass;
%typemap(newfree, throws="NoSuchMethodException") TestClass* "/*not written*/"
%inline %{
class TestClass {
    int x;
public:
    TestClass(int xx) : x(xx) {}
};
TestClass* makeTestClass() { return new TestClass(1000); }
%}


// javain typemap throws attribute
// Will only compile if the fileFunction has a java.io.IOException throws clause as getCanonicalPath() throws this exception
%typemap(jstype) char* someFileArgument "java.io.File"
%typemap(javain, throws="java.io.IOException") char* someFileArgument "$javainput.getCanonicalPath()"

%inline %{
void fileFunction(char* someFileArgument) {}
%}


// javout typemap throws attribute
%typemap(javaout, throws="java.io.IOException") int {
    int returnValue=$jnicall;
    if (returnValue==0) throw new java.io.IOException("some IOException");
    return returnValue;
  }

%inline %{
int ioTest() { return 0; }
%}

// except feature (%javaexception) specifying a checked exception class for the throws clause
%typemap(javabase) MyException "Throwable"
%typemap(javacode) MyException %{
  public static final long serialVersionUID = 0x52151000; // Suppress ecj warning
%}
%inline %{
    struct MyException {
        MyException(const char *msg) {}
    };
%}

%define JAVAEXCEPTION(METHOD)
%javaexception("MyException") METHOD %{
try {
    $action
} catch (MyException) {
    jclass excep = jenv->FindClass("java_throws/MyException");
    if (excep)
        jenv->ThrowNew(excep, "exception message");
    return $null;
}
%}
%enddef

JAVAEXCEPTION(FeatureTest::FeatureTest)
JAVAEXCEPTION(FeatureTest::method)
JAVAEXCEPTION(FeatureTest::staticMethod)

%inline %{
    struct FeatureTest {
        static void staticMethod() {
            throw MyException("no message");
        }
        void method() {
            throw MyException("no message");
        }
    };
%}

%include <swiginterface.i>
%interface_impl(InterfaceTest);
JAVAEXCEPTION(imethod)

%inline %{
    struct InterfaceTest {
        virtual void imethod(bool raise) = 0;
        virtual ~InterfaceTest() {}
    };

    struct InterfaceTestImpl : InterfaceTest {
        void imethod(bool raise) {
            if (raise)
                throw MyException("raise message");
        }
    };
%}

// Mixing except feature and typemaps when both generate a class for the throws clause
%typemap(in, throws="ClassNotFoundException") int both { 
    $1 = (int)$input;
}
%javaexception("MyException , NoSuchFieldException") globalFunction %{
try {
    $action
} catch (MyException) {
    jclass excep = jenv->FindClass("java_throws/MyException");
    if (excep)
        jenv->ThrowNew(excep, "exception message");
    return $null;
}
%}

%inline %{
    void globalFunction(int both) {
        throw MyException("no message");
    }
%}

// Test %nojavaexception
%javaexception("MyException") %{
/* global exception handler */
try {
    $action
} catch (MyException) {
    jclass excep = jenv->FindClass("java_throws/MyException");
    if (excep)
        jenv->ThrowNew(excep, "exception message");
    return $null;
}
%}

%nojavaexception *::noExceptionPlease();
%nojavaexception NoExceptTest::NoExceptTest();

// Need to handle the checked exception in NoExceptTest.delete()
%typemap(javafinalize) SWIGTYPE %{
  @SuppressWarnings({"deprecation", "removal"})
  protected void finalize() {
    try {
      delete();
    } catch (MyException e) {
      throw new RuntimeException(e);
    }
  }
%}

%typemap(javabody) SWIGTYPE %{
  private transient long swigCPtr;
  protected transient boolean swigCMemOwn;

  protected $javaclassname(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
  }

  protected static long getCPtr($javaclassname obj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }

  protected static long swigRelease($javaclassname obj) {
    long ptr = 0;
    if (obj != null) {
      if (!obj.swigCMemOwn)
        throw new RuntimeException("Cannot release ownership as memory is not owned");
      ptr = obj.swigCPtr;
      obj.swigCMemOwn = false;
      try {
        obj.delete();
      } catch (MyException e) {
        throw new RuntimeException(e);
      }
    }
    return ptr;
  }
%}


%inline %{
struct NoExceptTest {
  unsigned int noExceptionPlease() { return 123; }
  unsigned int exceptionPlease() { return 456; }
  ~NoExceptTest() {}
};
%}

// Turn global exceptions off (for the implicit destructors/constructors)
%nojavaexception;

