ca.weblite.objc.RuntimeUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-objc-bridge Show documentation
Show all versions of java-objc-bridge Show documentation
A thin bridge that allows for two-way communication from Java to Objective-C.
package ca.weblite.objc;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.ByReference;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.DoubleByReference;
import com.sun.jna.ptr.FloatByReference;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.ptr.ShortByReference;
import ca.weblite.nativeutils.NativeUtils;
/**
* A Java class with static methods that interact with the Objective-C runtime.
* These methods wrap the *very* low level methods provided by the Runtime class
* to provide a slightly higher level of abstraction.
*
* It is helpful to perform a static import of the methods in this class if you
* will be using the objc runtime library, as it contains many utility methods
* to deal with Objective-C primitives. E.g. the cls() method can return the
* Pointer to a class given its name. The clsName() returns the name of the
* class specified by a Pointer().
*
* There are also many variants of the msg() method for sendng messages to
* objects in the Objective-C runtime.
*
* @author shannah
* @see Objective-C Runtime Reference
* @version $Id: $Id
* @since 1.1
*/
public class RuntimeUtils {
/**
* Short reference to the runtime instance for interacting with Objective-C
*/
private static final Runtime rt = Runtime.INSTANCE;
private RuntimeUtils() {
}
static {
String libraryPath = "/libjcocoa.dylib";
try {
NativeUtils.loadLibraryFromJar(libraryPath);
init();
} catch (IOException ioException) {
throw new UncheckedIOException("Failed loading library " + libraryPath, ioException);
}
}
/**
* Returns a pointer to the class for specific class name.
*
* {@code
* Pointer nsObject = cls("NSObject");
* }
*
* @param name The name of the class to retrieve.
* @return The pointer to the class structure.
*/
public static Pointer cls(String name){
return rt.objc_lookUpClass(name);
}
/**
* Returns a pointer to a class given a Peerable object which wraps
* the pointer. Effectively this just calls peer.getPeer() and
* returns its value. A convenience method.
*
* @param peer The peer object that is wrapping the Objective-C class
* object.
* @return A Pointer to the Objective-C class.
*/
public static Pointer cls(Peerable peer){
return peer.getPeer();
}
/**
* Returns the name of an objective C class specified by the given class pointer.
*
* @param cls The pointer to the class whose name we wish to retrieve.
* @return The name of the class.
* @see Runtime#class_getName(Pointer)
*/
public static String clsName(Pointer cls){
return rt.class_getName(cls);
}
/**
* A wrapper for the clsName() method given a Peerable object that wraps
* the class pointer. This will return the class name.
*
* @param peer a {@link ca.weblite.objc.Peerable} object.
* @see Runtime#class_getName(Pointer)
* @return a {@link java.lang.String} object.
*/
public static String clsName(Peerable peer){
return clsName(peer.getPeer());
}
/**
* Returns a pointer to the selector specified by the given selector name.
*
* @param name a {@link java.lang.String} object.
* @return Pointer to an Objective-C message selector.
* @see Objective-C Selectors Reference
*/
public static Pointer sel(String name){
return rt.sel_getUid(name);
}
/**
* Returns a pointer to the selector that is wrapped by a Peerable object.
*
* @param peer a {@link ca.weblite.objc.Peerable} object.
* @return Pointer to a specified selector.
*/
public static Pointer sel(Peerable peer){
return peer.getPeer();
}
/**
* Returns the name of a selector.
*
* @param sel Pointer to a selector.
* @return The name of the selector.
*/
public static String selName(Pointer sel){
return rt.sel_getName(sel);
}
/**
* Returns the name of a selector.
*
* @return The name of the selector.
* @param peer a {@link ca.weblite.objc.Peerable} object.
*/
public static String selName(Peerable peer){
return selName(peer.getPeer());
}
/**
* Sends a message to a specified class using the given selector.
*
* @param cls The name of the class.
* @param msg The name of the selector.
* @param args The arguments passed to the method. These are passed raw
* and will not be coerced.
* @return The result of the message. This may be a pointer, if the message
* returns a pointer, or a value, if the method returns a BOOL, long, int,
* short, byte, or char. Note that you cannot use this method for
* messages that return float and double values. For these, you must
* use msgDouble(). This is because doubles and floats are handled using
* a different underlying runtime function (objc_msgSend_fpret). Alternatively
* you can use the variation of msg() that takes coerceInput and coerceOutput
* boolean values, as this will automatically choose the correct underlying
* message function depending on the return type reported by the message
* signature.
* @see #msgDouble(String, String, Object...)
*/
public static long msg(String cls, String msg, Object... args){
return msg(cls(cls), msg, args);
}
/**
* Sends a message to a specified class using the given selector.
*
* @param cls The name of the class.
* @param msg The name of the selector.
* @param args The arguments passed to the method. These are passed raw
* and will not be coerced.
* @return The result of the message. This may be a pointer, if the message
* returns a pointer, or a value, if the method returns a BOOL, long, int,
* short, byte, or char. Note that you cannot use this method for
* messages that return float and double values. For these, you must
* use msgDouble(). This is because doubles and floats are handled using
* a different underlying runtime function (objc_msgSend_fpret). Alternatively
* you can use the variation of msg() that takes coerceInput and coerceOutput
* boolean values, as this will automatically choose the correct underlying
* message function depending on the return type reported by the message
* signature.
* @see #msgDouble(String, Pointer, Object...)
*/
public static long msg(String cls, Pointer msg, Object... args){
return msg(cls(cls), msg, args);
}
/**
* Sends a message to a specified class using the given selector.
*
* @param msg The name of the selector.
* @param args The arguments passed to the method. These are passed raw
* and will not be coerced.
* @return The result of the message. This may be a pointer, if the message
* returns a pointer, or a value, if the method returns a BOOL, long, int,
* short, byte, or char. Note that you cannot use this method for
* messages that return float and double values. For these, you must
* use msgDouble(). This is because doubles and floats are handled using
* a different underlying runtime function (objc_msgSend_fpret). Alternatively
* you can use the variation of msg() that takes coerceInput and coerceOutput
* boolean values, as this will automatically choose the correct underlying
* message function depending on the return type reported by the message
* signature.
* @see #msgDouble(Pointer, String, Object...)
* @param receiver a {@link com.sun.jna.Pointer} object.
*/
public static long msg(Pointer receiver, String msg, Object... args){
return objc_msgSend(receiver, sel(msg), args);
}
/**
* Generate the Runtime class name suffix in RuntimeMappings that defines
* the appropriate version of objc_msgSend for the given arguments.
* @param args The arguments to check.
* @return The suffix. This will be a binary string in which a 1 in the i'th
* index corresponds with a Structure.ByValue parameter.
*/
private static String getArgsSuffix(Object... args) {
StringBuilder sb = new StringBuilder();
boolean foundStructByValue = false;
for (Object o : args) {
if (o instanceof Structure.ByValue) {
foundStructByValue = true;
sb.append("1");
} else {
sb.append("0");
}
}
if (foundStructByValue) {
return sb.toString();
}
return "";
}
/**
* Gets the parameter types a call to objc_msgSend should use, including the first two
* Pointer parameers.
* @param args The input arguments.
* @return The corresponding class types. The output array will be longer than the input array by two, because
* of the two Pointer parameters at the beginning.
*/
private static Class>[] getArgsParamTypes( Object... args) {
Class>[] out = new Class>[args.length+2];
out[0] = Pointer.class;
out[1] = Pointer.class;
for (int i=0; imust
* use msgDouble(). This is because doubles and floats are handled using
* a different underlying runtime function (objc_msgSend_fpret). Alternatively
* you can use the variation of msg() that takes coerceInput and coerceOutput
* boolean values, as this will automatically choose the correct underlying
* message function depending on the return type reported by the message
* signature.
* @see #msgDouble(Pointer, Pointer, Object...)
* @param receiver a {@link com.sun.jna.Pointer} object.
* @param selector a {@link com.sun.jna.Pointer} object.
*/
public static long msg(Pointer receiver, Pointer selector, Object... args){
long out = objc_msgSend(receiver, selector, args);
return out;
}
/**
* Wrapper around msg() that returns a Pointer. This should only be used for
* sending messages that return Pointers (or Objects).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return A Pointer return value of the message.
*/
public static Pointer msgPointer(Pointer receiver, Pointer selector, Object... args){
long res = msg(receiver, selector, args);
return new Pointer(res);
}
/**
* Wrapper around msg() that returns a Pointer. This should only be used for
* sending messages that return Pointers (or Objects).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return A Pointer return value of the message.
*/
public static Pointer msgPointer(Pointer receiver, String selector, Object... args){
return msgPointer(receiver, sel(selector), args);
}
/**
* Wrapper around msg() that returns a Pointer. This should only be used for
* sending messages that return Pointers (or Objects).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return A Pointer return value of the message.
*/
public static Pointer msgPointer(String receiver, Pointer selector, Object... args){
return msgPointer(cls(receiver), selector, args);
}
/**
* Wrapper around msg() that returns a Pointer. This should only be used for
* sending messages that return Pointers (or Objects).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return A Pointer return value of the message.
*/
public static Pointer msgPointer(String receiver, String selector, Object... args){
return msgPointer(cls(receiver), sel(selector), args);
}
/**
* Wrapper around msg() that returns an int. This should only be used for
* sending messages that return int-compatible numeric values. E.g.
* byte, bool, long, int, short. Do not use this if the message will return
* something else (like a float, double, string, or pointer). Narrowing
* primitive conversion will be applied; this will cause information loss
* if the {@code long} result does not fit in the {@code int} value range.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return An int value returned by the message.
*/
public static int msgInt(Pointer receiver, Pointer selector, Object... args){
long res = msg(receiver, selector, args);
return (int) res;
}
/**
* Wrapper around msg() that returns an int. This should only be used for
* sending messages that return int-compatible numeric values. E.g.
* byte, bool, long, int, short. Do not use this if the message will return
* something else (like a float, double, string, or pointer).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return An int value returned by the message.
*/
public static int msgInt(String receiver, Pointer selector, Object... args){
return msgInt(cls(receiver), selector, args);
}
/**
* Wrapper around msg() that returns an int. This should only be used for
* sending messages that return int-compatible numeric values. E.g.
* byte, bool, long, int, short. Do not use this if the message will return
* something else (like a float, double, string, or pointer).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return An int value returned by the message.
*/
public static int msgInt(String receiver, String selector, Object... args){
return msgInt(cls(receiver), sel(selector), args);
}
/**
* Wrapper around msg() that returns an int. This should only be used for
* sending messages that return int-compatible numeric values. E.g.
* byte, bool, long, int, short. Do not use this if the message will return
* something else (like a float, double, string, or pointer).
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return An int value returned by the message.
*/
public static int msgInt(Pointer receiver, String selector, Object... args){
return msgInt(receiver, sel(selector), args);
}
/**
* Wrapper around msg() that returns a boolean value. This should only be used
* for sending messages that return boolean-compatible numeric values. Essentially
* any non-zero value is interpreted (and returned) as true. Zero values are
* interpreted as false.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return Boolean return value of the message.
*/
public static boolean msgBoolean(Pointer receiver, Pointer selector, Object... args){
long res = msg(receiver, selector, args);
return res > 0L;
}
/**
* Wrapper around msg() that returns a boolean value. This should only be used
* for sending messages that return boolean-compatible numeric values. Essentially
* any non-zero value is interpreted (and returned) as true. Zero values are
* interpreted as false.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return Boolean return value of the message.
*/
public static boolean msgBoolean(String receiver, Pointer selector, Object... args){
return msgBoolean(cls(receiver), selector, args);
}
/**
* Wrapper around msg() that returns a boolean value. This should only be used
* for sending messages that return boolean-compatible numeric values. Essentially
* any non-zero value is interpreted (and returned) as true. Zero values are
* interpreted as false.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return Boolean return value of the message.
*/
public static boolean msgBoolean(String receiver, String selector, Object... args){
return msgBoolean(cls(receiver), sel(selector), args);
}
/**
* Wrapper around msg() that returns a boolean value. This should only be used
* for sending messages that return boolean-compatible numeric values. Essentially
* any non-zero value is interpreted (and returned) as true. Zero values are
* interpreted as false.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return Boolean return value of the message.
*/
public static boolean msgBoolean(Pointer receiver, String selector, Object... args){
return msgBoolean(receiver, sel(selector), args);
}
/**
* Wrapper around msg() that returns a string value. This should only be used
* for messages that return a CString. Do not use this for messages that
* return numeric values or Pointers to objects (e.g. NSString). Use the
* msg() variant that takes coerceInputs and coerceOutputs parameters if you
* want NSStrings to be automatically converted to java Strings.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return The string return value of the message.
*/
public static String msgString(Pointer receiver, Pointer selector, Object... args){
long res = msg(receiver, selector, args);
return new Pointer(res).getString(0);
}
/**
* Wrapper around msg() that returns a string value. This should only be used
* for messages that return a CString. Do not use this for messages that
* return numeric values or Pointers to objects (e.g. NSString). Use the
* msg() variant that takes coerceInputs and coerceOutputs parameters if you
* want NSStrings to be automatically converted to java Strings.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return The string return value of the message.
*/
public static String msgString(String receiver, Pointer selector, Object... args){
return msgString(cls(receiver), selector, args);
}
/**
* Wrapper around msg() that returns a string value. This should only be used
* for messages that return a CString. Do not use this for messages that
* return numeric values or Pointers to objects (e.g. NSString). Use the
* msg() variant that takes coerceInputs and coerceOutputs parameters if you
* want NSStrings to be automatically converted to java Strings.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return The string return value of the message.
*/
public static String msgString(String receiver, String selector, Object... args){
return msgString(cls(receiver), sel(selector), args);
}
/**
* Wrapper around msg() that returns a string value. This should only be used
* for messages that return a CString. Do not use this for messages that
* return numeric values or Pointers to objects (e.g. NSString). Use the
* msg() variant that takes coerceInputs and coerceOutputs parameters if you
* want NSStrings to be automatically converted to java Strings.
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args Arguments passed to the message.
* @return The string return value of the message.
*/
public static String msgString(Pointer receiver, String selector, Object... args){
return msgString(receiver, sel(selector), args);
}
/**
* Sends a message that returns a double value. This variant must be used
* if the method returns a float or a double. It doesn't actually wrap the
* primitive msg() method.. Rather it uses a different core Objective-C method,
* objc_msgSend_fpret().
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return The double return value of the message
*/
public static double msgDouble(Pointer receiver, Pointer selector, Object... args){
return objc_msgSend_fpret(receiver, selector, args);
}
/**
* Flag to check if this is apple silicon. (arm64)
*/
private static boolean isArm64 = System.getProperty("os.arch").equals("aarch64");
/**
* A wrapper for the objc_msgSend_fpret method. Since this method is not available
* in arm64, this will use a JNA mapping of objc_msgSend which returns double on that platform.
* On Intel, it will dispatch to the correct objc_msgSend_fpret mapping, according to parameter.
* @param receiver The receiver.
* @param selector The selector.
* @param args Arguments
* @return The result.
*/
private static double objc_msgSend_fpret(Pointer receiver, Pointer selector, Object... args) {
if (isArm64) {
switch (args.length) {
case 0:
return RuntmeArm64Extensions.INSTANCE.objc_msgSend(receiver, selector);
case 1:
return RuntmeArm64Extensions.INSTANCE.objc_msgSend(receiver, selector, args[0]);
case 2:
return RuntmeArm64Extensions.INSTANCE.objc_msgSend(receiver, selector, args[0], args[1]);
case 3:
return RuntmeArm64Extensions.INSTANCE.objc_msgSend(receiver, selector, args[0], args[1], args[2]);
case 4:
return RuntmeArm64Extensions.INSTANCE.objc_msgSend(receiver, selector, args[0], args[1], args[2], args[3]);
default:
throw new IllegalArgumentException("objc_msgSend does not support "+args.length+" arguments yet");
}
}
switch (args.length) {
case 0:
return rt.objc_msgSend_fpret(receiver, selector);
case 1:
return rt.objc_msgSend_fpret(receiver, selector, args[0]);
case 2:
return rt.objc_msgSend_fpret(receiver, selector, args[0], args[1]);
case 3:
return rt.objc_msgSend_fpret(receiver, selector, args[0], args[1], args[2]);
case 4:
return rt.objc_msgSend_fpret(receiver, selector, args[0], args[1], args[2], args[3]);
default:
throw new IllegalArgumentException("objc_msgSend_fpret does not support "+args.length+" arguments yet");
}
}
/**
* Sends a message that returns a double value. This variant must be used
* if the method returns a float or a double. It doesn't actually wrap the
* primitive msg() method.. Rather it uses a different core Objective-C method,
* objc_msgSend_fpret().
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return The double return value of the message
*/
public static double msgDouble(String receiver, Pointer selector, Object... args){
return msgDouble(cls(receiver), selector, args);
}
/**
* Sends a message that returns a double value. This variant must be used
* if the method returns a float or a double. It doesn't actually wrap the
* primitive msg() method.. Rather it uses a different core Objective-C method,
* objc_msgSend_fpret().
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return The double return value of the message
*/
public static double msgDouble(String receiver, String selector, Object... args){
return msgDouble(cls(receiver), sel(selector), args);
}
/**
* Sends a message that returns a double value. This variant must be used
* if the method returns a float or a double. It doesn't actually wrap the
* primitive msg() method.. Rather it uses a different core Objective-C method,
* objc_msgSend_fpret().
*
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments passed to the message.
* @return The double return value of the message
*/
public static double msgDouble(Pointer receiver, String selector, Object... args){
return msgDouble(receiver, sel(selector), args);
}
private static void sleep50() {
try {
Thread.sleep(500);
} catch (Exception ex){}
}
/**
* Sends a message with the option of coercing the inputs and outputs. This variant
* uses a higher level of abstraction than the standard msg() and msgXXX() methods.
* If coerceReturn and coerceArgs are set to true, then this method will
* convert the inputs from Java inputs to the corresponding Objective-C inputs.
* Further it will return values that are more appropriate for use in Java.
*
* Furthermore, this variant is smart about which variable of msg() to call
* behind the scenes. E.g. if the message signature specifies that this message
* returns a double, it will automatically use the double variant of msg().
*
* @param coerceReturn If true, then the return value will be mapped to an appropriate
* Java value using the TypeMapper class.
* @param coerceArgs If true, then the inputs will be mapped from Java to appropriate
* C values using the TypeMapper class.
* @param receiver The target of the message.
* @param selector The selector for the message.
* @param args The arguments to be passed in the message.
* @return The return value of the message. This may be a Pointer or a value depending
* on the return type of the message.
*/
public static Object msg(boolean coerceReturn, boolean coerceArgs, Pointer receiver, Pointer selector, Object... args){
Pointer methodSignature = msgPointer(receiver, "methodSignatureForSelector:", selector);
if (Pointer.nativeValue(methodSignature) == 0L) {
throw new RuntimeException(new NoSuchMethodException("Method cannot be found for signature "+Pointer.nativeValue(selector)));
}
int numArgs = (int)msg(methodSignature, "numberOfArguments");
if ( numArgs >=2 && numArgs != args.length+2 ){
throw new RuntimeException("Wrong argument count. The selector "+selName(selector)+" requires "+(numArgs-2)+" arguments, but received "+args.length);
}
long returnTypePtr = msg(methodSignature, "methodReturnType");
String returnTypeSignature = new Pointer(returnTypePtr).getString(0);
if ( numArgs == 0 && returnTypeSignature == null ){
return msg(receiver, selector, args);
}
if ( coerceArgs && args.length > 0 ){
for ( int i=0; i returnTypeSignature.length()-1 ){
break;
}
}
if ( offset > 0 ){
returnTypeSignature = returnTypeSignature.substring(offset);
}
char returnTypeFirstChar = returnTypeSignature.charAt(0);
if ( "[{(".indexOf(returnTypeFirstChar) ==-1 ){
// We are not returning a structure so we'll just
// do the message.
// We need to handle doubles and floats separately
if ( "df".indexOf(returnTypeFirstChar) != -1 ){
Object res = msgDouble(receiver, selector, args);
for ( int i=0; iObjective-C type encodings.
* @return The size of the array.
* @see Objective-C Type Encodings
*/
public static int arraySize(String signature){
int typeIndex = 2;
String digits = "0123456789";
while ( digits.indexOf(signature.charAt(typeIndex++)) != -1 ){}
return Integer.parseInt(signature.substring(1, typeIndex));
}
/**
* Returns the pointer to the Native peer for a Peerable object.
*
* @param peer a {@link ca.weblite.objc.Peerable} object.
* @return The Pointer to a native peer.
*/
public static Pointer addr(Peerable peer){
return peer.getPeer();
}
/**
* Sends a batch of messages in sequence.
*
* @param messages a {@link ca.weblite.objc.Message} object.
* @return a {@link java.lang.Object} object.
*/
public static Object msg(Message... messages){
for ( int i=0; i0 ){
messages[i].previous = messages[i-1];
}
if ( i< messages.length-1 ){
messages[i].next = messages[i+1];
}
}
for ( int i=0; i 0 ){
return messages[messages.length-1].result;
} else {
throw new RuntimeException("Message queue was empty");
}
}
/**
* Converts a Java string to an NSString object.
*
* @param str The Java string to convert.
* @return Pointer to the NSString that corresponds to this string.
*/
public static Pointer str(String str){
return msgPointer("NSString", "stringWithUTF8String:", str);
}
/**
* Converts A native NSString object to a Java string.
*
* @param str a {@link com.sun.jna.Pointer} object.
* @return A Java string.
*/
public static String str(Pointer str){
long ptr = msg(str, "UTF8String");
return new Pointer(ptr).getString(0);
}
/**
* Wraps the given value in the appropriate ByReference subclass according
* to the provided signature. This is a useful method in cases where
* you need to pass a parameter to a C-function by reference, but you don't
* necessarily know what type of data it is.
*
* @param val The value to be wrapped in a byReference.
* @param signature The signature (using Objective-C type encodings) of the value.
* @return A pointer to the reference that can be passed in a C function.
* @see Objective-C Type Encodings
*/
public static Pointer getAsReference(Object val, String signature){
return getAsReferenceWrapper(val, signature).getPointer();
}
/**
* Wraps the given value in the appropriate ByReference subclass according
* to the provided signature. This is a useful method in cases where
* you need to pass a parameter to a C-function by reference, but you don't
* necessarily know what type of data it is.
*
* @param val The value to be wrapped in a byReference.
* @param signature The signature (using Objective-C type encodings) of the value.
* @return The ByReference object that contains the pointer that can be passed
* to a c function.
* @see Objective-C Type Encodings
*/
public static ByReference getAsReferenceWrapper(Object val, String signature){
String prefixes = "rnNoORV";
int offset = 0;
while ( prefixes.indexOf(signature.charAt(offset)) != -1 ){
offset++;
if ( offset > signature.length()-1 ){
break;
}
}
if ( offset > 0 ){
signature = signature.substring(offset);
}
switch ( signature.charAt(0)){
case 'i':
case 'I':
int intVal;
if (val instanceof Number) {
intVal = ((Number) val).intValue();
} else if (val instanceof String) {
intVal = Integer.parseInt((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to int: "+val);
}
return new IntByReference(intVal);
case 's':
case 'S':
short shortVal;
if (val instanceof Number) {
shortVal = ((Number) val).shortValue();
} else if (val instanceof String) {
shortVal = Short.parseShort((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to short: "+val);
}
return new ShortByReference(shortVal);
case 'l':
case 'L':
case 'q':
case 'Q':
long longVal;
if (val instanceof Number) {
longVal = ((Number) val).longValue();
} else if (val instanceof String) {
longVal = Long.parseLong((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to long: "+val);
}
return new LongByReference(longVal);
case 'f':
float floatVal;
if (val instanceof Number) {
floatVal = ((Number) val).floatValue();
} else if (val instanceof String) {
floatVal = Float.parseFloat((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to float: "+val);
}
return new FloatByReference(floatVal);
case 'd':
double doubleVal;
if (val instanceof Number) {
doubleVal = ((Number) val).doubleValue();
} else if (val instanceof String) {
doubleVal = Double.parseDouble((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to double: "+val);
}
return new DoubleByReference(doubleVal);
case 'B':
case 'b':
case 'c':
case 'C':
byte byteVal;
if (val instanceof Boolean) {
byteVal = (byte) (Boolean.TRUE.equals(val) ? 1 : 0);
} else if (val instanceof Number) {
byteVal = ((Number) val).byteValue();
} else if (val instanceof String) {
byteVal = Byte.parseByte((String) val);
} else {
throw new RuntimeException("Attempt to pass ineligible value to byte: " + val);
}
return new ByteByReference(byteVal);
case 'v':
return null;
case '^':
default:
if (val instanceof Pointer) {
return new PointerByReference((Pointer)val);
} else if (val instanceof Long) {
return new PointerByReference(new Pointer((Long) val));
} else {
throw new RuntimeException("Don't know what to do for conversion of value "+val+" and signature "+signature);
}
}
}
/**
* Initializes the libjcocoa library. This is called when the class is first
* loaded. It sets up the JNI environment that will be used there forward.
*/
private static native void init();
/**
* Registers a Java object with the Objective-C runtime so that it can begin
* to receive messages from it. This will create an Objective-C proxy
* that passes messages to the Recipient object. This step is automatically
* handled by the NSObject class inside its init() method.
*
* @param client a {@link ca.weblite.objc.Recipient} object.
* @return a long.
*/
public static native long createProxy(Recipient client);
/**
* Returns the Java peer recipient for a native Objective-C object if it
* exists. This will return null if nsObject is not an WLProxy object
* that has been previously registered with a Recipient.
*
* @param nsObject a long.
* @return a {@link ca.weblite.objc.Recipient} object.
*/
public static native Recipient getJavaPeer(long nsObject);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy