JNIpp 1.0
JNI meets C++
EmailValidator.cpp
#include "EmailValidator.h"

/**************************************/

/* Email validator function which uses Pattern and Matcher
 *  classes implemented below.
 */


/* We compile email pattern only once.
 * Note that we use pointer to Pattern instead of advised PPattern.
 *
 * The problem with using PPattern is that destructor of g_emailPattern
 *  will be called on library unload *after* the JNI unload event
 *  JNI_OnUnload. So jni::GetEnv() which every jni:: function use will 
 *  fail and call jni::FatalError() to report a failure. jni::FatalError()
 *  will try to call jni::GetEnv()->FatalError() but since JNIEnv is not
 *  available jni::FatalError() will abort the whole process with abort().
 *
 * So we use (and leak) Pattern pointer.
 */
static Pattern* g_emailPattern=0;


bool ValidateEmail(const char* email) {
    if (!email || !*email) {
        return false;
    }
    if (!g_emailPattern) {
        // NOT THREAD SAFE!
        // Add locking to your taste.
        PPattern pattern=Pattern::Compile(
             "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@"
            "[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"
        );
        g_emailPattern=pattern.Detach();
    }
    
    // Compare with Java's "pattern.matcher(email).matches()".
    return g_emailPattern->CreateMatcher(email)->Matches();
}


/**************************************/

/* Let JB_ macros know the name of the class we are implementing.
 *
 * Each wrapper class implementation must start with a definition
 *  of the current class.
 *
 * Remember that JB_ macros are only usable when JB_CURRENT_CLASS 
 *  is defined. Otherwise you will get compiler errors.
 */
#define JB_CURRENT_CLASS Pattern


/* Define information about Java class we are going to call.
 *
 * Note that there are no commas between method definitions.
 * Also note that static methods are denoted with '+'.
 *
 * Fields are declared the same way - see JB_DEFINE_WRAPPER_CLASS()
 *  documentation.
 */
JB_DEFINE_WRAPPER_CLASS(
    "java/util/regex/Pattern"
    ,
    NoFields
    ,
    Methods
    (
        CompileTag,
        "+compile",
        "(Ljava/lang/String;)Ljava/util/regex/Pattern;"
    )
    (
        CreateMatcherTag,
        "matcher",
        "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"
    )
)

/* Wrapping constructor.
 * This constructor is used in implementation of PPattern::Wrap().
 */
Pattern::Pattern(const jni::LObject& pattern):
    java::Object(pattern)
{
}

PPattern Pattern::Compile(const char* regex) {
    return Compile(java::PString::New(regex));
}

/* 
 * Here is what happens in this method:
 * - JB_CALL_STATIC() calls JB_INIT_CLASS() to make sure class descriptor
 *    (defined by JB_DEFINE_CLASS) is initialized.
 * - JB_INIT_CLASS() gets Java class "java/util/regex/Pattern" and retrieves
 *    method/field ids for each method or field defined by JB_DEFINE_WRAPPER_CLASS().
 *    Initialization happens only once (via pthread_once).
 * - jni::CallStaticObjectMethod() is called with method id for the 'CompileTag'
 *    method. You can get method id for the tag with JB_METHOD_ID() macro.
 * - Result of method call is wrapped into the Pattern class. Note that for
 *    performance reasons PPattern::Wrap() doesn't check that you are wrapping
 *    object of correct class (java.util.regex.Pattern or derived).
 */
PPattern Pattern::Compile(java::PString regex) {
    return PPattern::Wrap(JB_CALL_STATIC(ObjectMethod,CompileTag,regex));
}

PMatcher Pattern::CreateMatcher(const char* input) const {
    return CreateMatcher(java::PString::New(input));
}

/* When calling non-static methods (or accessing fields) use
 * _THIS version of JB_ macros.
 */
PMatcher Pattern::CreateMatcher(java::PCharSequence input) const {
    return PMatcher::Wrap(JB_CALL_THIS(ObjectMethod,CreateMatcherTag,input));
}

/* Finished with implementation of Pattern class, undefine
 *  JB_CURRENT_CLASS. JB_ macros will not work past this line.
 */
#undef JB_CURRENT_CLASS


/**************************************/

/* Matcher class implementation. */

#define JB_CURRENT_CLASS Matcher

JB_DEFINE_WRAPPER_CLASS(
    "java/util/regex/Matcher"
    ,
    NoFields
    ,
    Methods
    (
        Matches,
        "matches",
        "()Z"
    )
)

Matcher::Matcher(const jni::LObject& matcher):
    java::Object(matcher)
{
}

/* Note that we call 'BoolMethod', which expands to jni::CallBoolMethod().
 * This is a version of traditional jni::CallBooleanMethod() which converts
 *  jboolean to bool.
 */
bool Matcher::Matches() const {
    return JB_CALL_THIS(BoolMethod,Matches);
}

#undef JB_CURRENT_CLASS


/**************************************/

#ifdef ONLY_FOR_DOXYGEN
// Wire this example to:
java::Object;
#endif