summaryrefslogtreecommitdiff
path: root/Lib/java
diff options
context:
space:
mode:
authorMarvin Greenberg <public.marvin@gmail.com>2013-10-22 20:31:14 +0100
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2013-10-22 20:44:35 +0100
commit6736e74127180f012dab11379a2159cd073461d4 (patch)
tree07f0d845670f9d77ce303a9e1fdc5f139b1f7dbe /Lib/java
parentec1d5a5be1c7cdaaa8dedc3ba76a5792127cef0f (diff)
downloadswig-6736e74127180f012dab11379a2159cd073461d4.tar.gz
Add feature director:except for improved director exception handling in Java
Closes #91
Diffstat (limited to 'Lib/java')
-rw-r--r--Lib/java/director.swg238
-rw-r--r--Lib/java/std_string.i3
2 files changed, 239 insertions, 2 deletions
diff --git a/Lib/java/director.swg b/Lib/java/director.swg
index f32fda350..8c7dca294 100644
--- a/Lib/java/director.swg
+++ b/Lib/java/director.swg
@@ -193,6 +193,242 @@ namespace Swig {
};
}
-#endif /* __cplusplus */
+// Default exception handler for all director methods.
+// Can override this for specific methods
+// This just documents the default. It is not actually attached as a feature
+// for efficiency reasons (and is defined in java.cxx) It can be overridden
+// as a feature for specific methods or globally.
+
+//%feature("director:except") %{
+// jthrowable $error = jenv->ExceptionOccurred();
+// if ($error) {
+// jenv->ExceptionClear();
+// $directorthrowshandlers
+// throw Swig::DirectorException(jenv, $error);
+// }
+//%}
+
+// Define some utility methods and classes. Cannot use fragments since
+// these may be used by %features and in directorthrows typemaps
+
+#include <exception>
+
+// Simple holder for exception messages, allowing access to a c-style string
+namespace Swig {
+ struct JavaString {
+ JavaString(JNIEnv * jenv, jstring jstr):jenv_(jenv), jstr_(jstr), cstr_(0) {
+ if (jenv_ && jstr_) {
+ // Get the null-terminated c-string out, and hold it
+ cstr_ = (const char *) jenv_->GetStringUTFChars(jstr_, NULL);
+ }
+ }
+ ~JavaString() {
+ if (jenv_ && jstr_ && cstr_) {
+ jenv_->ReleaseStringUTFChars(jstr_, cstr_);
+ }
+ }
+ const char *cstr() {
+ return cstr_ ? cstr_ : "";
+ }
+
+ private:
+ JNIEnv * jenv_;
+ jstring jstr_;
+ const char * cstr_;
+
+ // non-copyable
+ JavaString(const JavaString &);
+ JavaString & operator=(const JavaString &);
+ };
+
+ struct JavaExceptionMessage {
+ JavaExceptionMessage(JNIEnv * jenv, jthrowable excp) :
+ jstrholder_(jenv,exceptionMsgJstr(jenv,excp)) {
+ }
+ ~JavaExceptionMessage() {
+ }
+ const char *message() {
+ return jstrholder_.cstr();
+ }
+
+ private:
+ JavaString jstrholder_;
+
+ // non-copyable
+ JavaExceptionMessage(const JavaExceptionMessage &);
+ JavaExceptionMessage & operator=(const JavaExceptionMessage &);
+
+ // Static method to initialize jstrholder_
+ static jstring exceptionMsgJstr(JNIEnv * jenv, jthrowable excp) {
+ jstring jmsg = NULL;
+ if (jenv && excp) {
+ jenv->ExceptionClear(); // Cannot invoke methods with pending exception
+ jclass thrwclz = jenv->GetObjectClass(excp);
+ if (thrwclz) {
+ // if no getMessage() or other exception, no msg available.
+ jmethodID getThrowableMsgMethodID =
+ jenv->GetMethodID(thrwclz, "getMessage", "()Ljava/lang/String;");
+ if (getThrowableMsgMethodID && !jenv->ExceptionCheck()) {
+ // if problem accessing exception message string, no msg available.
+ jmsg = (jstring)
+ jenv->CallObjectMethod(excp, getThrowableMsgMethodID);
+ }
+ }
+ if (jmsg == NULL && jenv->ExceptionCheck()) {
+ jenv->ExceptionClear();
+ }
+ }
+ return jmsg;
+ }
+ };
+
+
+ ////////////////////////////////////
+
+ bool ExceptionMatches(JNIEnv * jenv, jthrowable excp,
+ const char *clzname) {
+ jboolean matches = false;
+
+ if (excp && jenv && clzname) {
+ // Have to clear exceptions for correct behavior. Code around
+ // ExceptionMatches should restore pending exception if
+ // desired - already have throwable.
+ jenv->ExceptionClear();
+
+ jclass clz = jenv->FindClass(clzname);
+ if (clz && ! jenv->ExceptionCheck()) {
+ jclass classclz = jenv->GetObjectClass(clz);
+ jmethodID isInstanceMethodID =
+ jenv->GetMethodID(classclz, "isInstance", "(Ljava/lang/Object;)Z");
+ if (isInstanceMethodID) {
+ matches = (jboolean)
+ jenv->CallBooleanMethod(clz, isInstanceMethodID, excp);
+ }
+ }
+ // This may happen if user typemaps or director:except
+ // features call ExceptionMatches incorrectly, typically with
+ // an invalid clzname argument. Uncommenting the debug lines
+ // may help to diagnose.
+ // As is, this leaves the underlying case as a pending exception
+ // which may not be that clear (e.g. ClassNotFoundException)
+
+ // if (jenv->ExceptionCheck()) {
+ // JavaExceptionMessage jstrmsg(jenv,jenv->ExceptionOccurred());
+ // std::cerr << "Error: ExceptionMatches: class '" <<
+ // clzname << "' : " << jstrmsg.message() << std::endl;
+ // }
+ }
+ return matches;
+ }
+
+ // Provide the class name to allow reconstruction of the original exception
+ struct DirectorException : std::exception {
+
+ // Construct a DirectorException from a java throwable
+ DirectorException(JNIEnv* jenv,jthrowable excp) : classname_(0), msg_(0) {
+ jstring jstr_classname = NULL;
+ jmethodID mid_getName = NULL;
+ jclass thrwclz;
+ jclass clzclz;
+
+ if (excp) {
+ // Get the exception class, like Exception
+ thrwclz = jenv->GetObjectClass(excp);
+ if (thrwclz) {
+ // Get the java.lang.Class class
+ clzclz = jenv->GetObjectClass(thrwclz);
+ if (clzclz) {
+ mid_getName = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
+ if (mid_getName) {
+ // Get the excp class name
+ jstr_classname = (jstring)(jenv->CallObjectMethod(thrwclz, mid_getName));
+ }
+ }
+ }
+ }
+ // Copy strings, since no guarantee jenv will be active when handled
+ // If classname_ is 0, returned as "UnknownException"
+ if (jstr_classname) {
+ JavaString classname(jenv, jstr_classname);
+ classname_ = copypath(classname.cstr());
+ }
+ JavaExceptionMessage exceptionmsg(jenv, excp);
+ msg_ = copystr(exceptionmsg.message());
+ }
+
+ // Throw as a wrapped Runtime Error explicitly.
+ DirectorException(const char * msg) : classname_(0), msg_(0) {
+ classname_ = copypath("java/lang/RuntimeError");
+ msg_ = copystr(msg);
+ }
+
+ ~DirectorException() throw() {
+ delete[] classname_;
+ delete[] msg_;
+ }
+
+ // If there was problem finding classname, keep track of error
+ // On raiseJavaException will be mapped to a RuntimeException
+ const char* classname() const throw() {
+ return classname_ ? classname_ : "UnknownException";
+ }
+ const char* what() const throw() {
+ return msg_ ? msg_ : "";
+ }
+
+ // Python director code provides static raise() methods
+ // Omitted here: Seems less good than
+ // throw DirectorException(jenv, excp)
+ // from which compiler can infer a C++ exception is thrown.
+
+ // Reconstruct and raise the Java Exception that caused the DirectorException
+ void raiseJavaException(JNIEnv* jenv) {
+ if (jenv) {
+ jenv->ExceptionClear();
+
+ jmethodID strCtorID = 0;
+ jclass excpclz = jenv->FindClass(classname());
+
+ if (excpclz) {
+ strCtorID = jenv->GetMethodID(excpclz,"<init>","(Ljava/lang/String;)V");
+ }
+
+ if (strCtorID) {
+ // If exception has a string constructor, throw an instance
+ jenv->ThrowNew(excpclz, what());
+ } else {
+ // Else, throw a runtime
+ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what() );
+ }
+ }
+ }
+
+ private:
+ const char * classname_;
+ const char * msg_;
+
+ static const char * copypath(const char * srcmsg) {
+ return copystr(srcmsg, 1);
+ }
+ static const char * copystr(const char * srcmsg, int pathrepl=0) {
+ char * target = 0;
+ if (srcmsg) {
+ int msglen = 1+strlen(srcmsg); //+1 for null terminator
+ target = new char[msglen];
+ strncpy(target, srcmsg, msglen);
+ }
+ // If pathrepl, replace any '.' with '/'
+ if (pathrepl) {
+ for(char *c=target; *c; ++c) {
+ if ('.' == *c) *c = '/';
+ }
+ }
+ return target;
+ }
+ };
+
+}
+
+#endif /* __cplusplus */
diff --git a/Lib/java/std_string.i b/Lib/java/std_string.i
index f178e6d43..6b9cb90a4 100644
--- a/Lib/java/std_string.i
+++ b/Lib/java/std_string.i
@@ -38,7 +38,8 @@ class string;
%typemap(directorout) string
%{ if(!$input) {
- SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+ if (!jenv->ExceptionCheck())
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
return $null;
}
const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0);