String Parameters


Next, we want to consider how to transfer strings to and from native methods. As you know, strings in the Java programming language are sequences of UTF-16 code points whereas C strings are null-terminated sequences of bytes, so strings are quite different in the two languages. The Java Native Interface has two sets of functions for manipulating strings, one that converts Java strings to "modified UTF-8" byte sequences and one that converts them to arrays of UTF-16 values, that is, to jchar arrays. (The UTF-8, "modified UTF-8", and UTF-16 formats were discussed in Volume 1, Chapter 12. Recall that the "modified UTF-8" encoding leaves ASCII characters unchanged, but all other Unicode characters are encoded as multibyte sequences.)

NOTE

The standard UTF-8 encoding and the "modified UTF-8" encoding differ only for "supplementary" characters with code higher than 0xFFFF. In the standard UTF-8 encoding, these characters are encoded as a 4-byte sequence. However, in the "modified" encoding, the character is first encoded as a pair of "surrogates" in the UTF-16 encoding, and then each surrogate is encoded with UTF-8, yielding a total of 6 bytes. This is clumsy, but it is a historical accidentthe JVM specification was written when Unicode was still limited to 16 bits.


If your C code already uses Unicode, you'll want to use the second set of conversion functions. On the other hand, if all your strings are restricted to ASCII characters, you can use the "modified UTF-8" conversion functions.

A native method with a String parameter actually receives a value of an opaque type called jstring. A native method with a return value of type String must return a value of type jstring. JNI functions read and construct these jstring objects. For example, the NewStringUTF function makes a new jstring object out of a char array that contains ASCII characters or, more generally, "modified UTF-8"-encoded byte sequences.

JNI functions have a somewhat odd calling convention. Here is a call to the NewStringUTF function.

 JNIEXPORT jstring JNICALL Java_HelloNative_getGreeting(JNIEnv* env, jclass cl) {    jstring jstr;    char greeting[] = "Hello, Native World\n";    jstr = (*env)->NewStringUTF(env, greeting);    return jstr; } 

All calls to JNI functions use the env pointer that is the first argument of every native method. The env pointer is a pointer to a table of function pointers (see Figure 11-1). Therefore, you must prefix every JNI call with (*env)-> to actually dereference the function pointer. Furthermore, env is the first parameter of every JNI function.

Figure 11-1. The env pointer


C++ NOTE

It is simpler to access JNI functions in C++. The C++ version of the JNIEnv class has inline member functions that take care of the function pointer lookup for you. For example, you can call the NewStringUTF function as

 jstr = env->NewStringUTF(greeting); 

Note that you omit the JNIEnv pointer from the parameter list of the call.


The NewStringUTF function lets you construct a new jstring. To read the contents of an existing jstring object, use the GetStringUTFChars function. This function returns a const jbyte* pointer to the "modified UTF-8" characters that describe the character string. Note that a specific virtual machine is free to choose this character encoding for its internal string representation, so you may get a character pointer into the actual Java string. Because Java strings are meant to be immutable, it is very important that you treat the const seriously and do not try to write into this character array. On the other hand, if the virtual machine uses UTF-16 or UTF-32 characters for its internal string representation, then this function call allocates a new memory block that will be filled with the "modified UTF-8" equivalents.

The virtual machine must know when you are finished using the string so that it can garbage-collect it. (The garbage collector runs in a separate thread, and it can interrupt the execution of native methods.) For that reason, you must call the ReleaseStringUTFChars function.

Alternatively, you can supply your own buffer to hold the string characters by calling the GetStringRegion or GetStringUTFRegion methods.

Finally, the GetStringUTFLength function returns the number of characters needed for the "modified UTF-8" encoding of the string.

NOTE

You can find the JNI API at http://java.sun.com/j2se/5.0/docs/guide/jni/spec/functions.html.



 Accessing Java Strings from C Code 

  • jstring NewStringUTF(JNIEnv* env, const char bytes[])

    returns a new Java string object from a "modified UTF-8" byte sequence, or NULL if the string cannot be constructed.

    Parameters:

    env

    The JNI interface pointer

     

    bytes

    The null-terminated "modified UTF-8" bytes


  • jsize GetStringUTFLength(JNIEnv* env, jstring string)

    returns the number of bytes required for the "modified UTF-8" encoding.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object


  • const jbyte* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy)

    returns a pointer to the "modified UTF-8" encoding of a string, or NULL if the character array cannot be constructed. The pointer is valid until ReleaseStringUTFChars is called.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object

     

    isCopy

    Points to a jboolean that is filled with JNI_TRUE if a copy is made; with JNI_FALSE otherwise


  • void ReleaseStringUTFChars(JNIEnv* env, jstring string, const jbyte bytes[])

    informs the virtual machine that the native code no longer needs access to the Java string through bytes.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object

     

    bytes

    A pointer returned by GetStringUTFChars


  • void GetStringRegion(JNIEnv *env, jstring string, jsize start, jsize length, jchar *buffer)

    copies a sequence of Unicode characters from a string to a user-supplied buffer.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object

     

    start

    The starting index

     

    length

    The number of characters to copy

     

    buffer

    The user-supplied buffer


  • void GetStringUTFRegion(JNIEnv *env, jstring string, jsize start, jsize length, jbyte *buffer)

    copies a sequence of "modified UTF-8" bytes from a string to a user-supplied buffer. The buffer must be long enough to hold the bytes. In the worst case, 3 x length bytes are copied.

  • jstring NewString(JNIEnv* env, const jchar chars[], jsize length)

    returns a new Java string object from a Unicode string, or NULL if the string cannot be constructed.

    Parameters:

    env

    The JNI interface pointer

     

    chars

    The null-terminated UTF16 string

     

    length

    The number of characters in the string


  • jsize GetStringLength(JNIEnv* env, jstring string)

    returns the number of characters in the string.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object


  • const jchar* GetStringChars(JNIEnv* env, jstring string, jboolean* isCopy)

    returns a pointer to the Unicode encoding of a string, or NULL if the character array cannot be constructed. The pointer is valid until ReleaseStringChars is called.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object

     

    isCopy

    Is either NULL or points to a jboolean that is filled with JNI_TRUE if a copy is made; with JNI_FALSE otherwise


  • void ReleaseStringChars(JNIEnv* env, jstring string, const jchar chars[])

    informs the virtual machine that the native code no longer needs access to the Java string through chars.

    Parameters:

    env

    The JNI interface pointer

     

    string

    A Java string object

     

    chars

    A pointer returned by GetStringChars


Calling sprint in a Native Method

Let us put these functions we just described to work and write a class that calls the C function sprintf. We would like to call the function as shown in Example 11-8.

Example 11-8. Printf2Test.java
  1. class Printf2Test  2. {  3.    public static void main(String[] args)  4.    {  5.       double price = 44.95;  6.       double tax = 7.75;  7.       double amountDue = price * (1 + tax / 100);  8.  9.       String s = Printf2.sprint("Amount due = %8.2f", amountDue); 10.       System.out.println(s); 11.    } 12. } 

Example 11-9 shows the class with the native sprint method.

Example 11-9. Printf2.java
 1. class Printf2 2. { 3.    public static native String sprint(String format, double x); 4. 5.    static 6.    { 7.       System.loadLibrary("Printf2"); 8.    } 9. } 

Therefore, the C function that formats a floating-point number has the prototype

[View full width]

JNIEXPORT jstring JNICALL Java_Printf2_sprint(JNIEnv* env, jclass cl, jstring format, jdouble x)

Example 11-10 shows the code for the C implementation. Note the calls to GetStringUTFChars to read the format argument, NewStringUTF to generate the return value, and ReleaseStringUTFChars to inform the virtual machine that access to the string is no longer required.

Example 11-10. Printf2.c
  1. #include "Printf2.h"  2. #include <string.h>  3. #include <stdlib.h>  4. #include <float.h>  5.  6. /**  7.    @param format a string containing a printf format specifier  8.    (such as "%8.2f"). Substrings "%%" are skipped.  9.    @return a pointer to the format specifier (skipping the '%') 10.    or NULL if there wasn't a unique format specifier 11. */ 12. char* find_format(const char format[]) 13. { 14.    char* p; 15.    char* q; 16. 17.    p = strchr(format, '%'); 18.    while (p != NULL && *(p + 1) == '%') /* skip %% */ 19.       p = strchr(p + 2, '%'); 20.    if (p == NULL) return NULL; 21.    /* now check that % is unique */ 22.    p++; 23.    q = strchr(p, '%'); 24.    while (q != NULL && *(q + 1) == '%') /* skip %% */ 25.       q = strchr(q + 2, '%'); 26.    if (q != NULL) return NULL; /* % not unique */ 27.    q = p + strspn(p, " -0+#"); /* skip past flags */ 28.    q += strspn(q, "0123456789"); /* skip past field width */ 29.    if (*q == '.') { q++; q += strspn(q, "0123456789"); } 30.       /* skip past precision */ 31.    if (strchr("eEfFgG", *q) == NULL) return NULL; 32.       /* not a floating point format */ 33.    return p; 34. } 35. 36. JNIEXPORT jstring JNICALL Java_Printf2_sprint(JNIEnv* env, jclass cl, 37.    jstring format, jdouble x) 38. { 39.    const char* cformat; 40.    char* fmt; 41.    jstring ret; 42. 43.    cformat = (*env)->GetStringUTFChars(env, format, NULL); 44.    fmt = find_format(cformat); 45.    if (fmt == NULL) 46.       ret = format; 47.    else 48.    { 49.       char* cret; 50.       int width = atoi(fmt); 51.       if (width == 0) width = DBL_DIG + 10; 52.       cret = (char*) malloc(strlen(cformat) + width); 53.       sprintf(cret, cformat, x); 54.       ret = (*env)->NewStringUTF(env, cret); 55.       free(cret); 56.    } 57.    (*env)->ReleaseStringUTFChars(env, format, cformat); 58.    return ret; 59. } 

In this function, we chose to keep the error handling simple. If the format code to print a floating-point number is not of the form %w.pc, where c is one of the characters e, E, f, g, or G, then what we simply do is not format the number. We show you later how to make a native method throw an exception.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net