package org.perl.inline.java ;
import java.lang.reflect.* ;
import java.util.* ;
import java.io.* ;
public class InlineJavaPerlNatives extends InlineJavaPerlCaller {
static private boolean inited = false ;
static private Map registered_classes = Collections.synchronizedMap(new HashMap()) ;
static private Map registered_methods = Collections.synchronizedMap(new HashMap()) ;
protected InlineJavaPerlNatives() throws InlineJavaException {
init() ;
RegisterPerlNatives(this.getClass()) ;
}
static protected void init() throws InlineJavaException {
init("install") ;
}
synchronized static protected void init(String mode) throws InlineJavaException {
InlineJavaPerlCaller.init() ;
if (! inited){
try {
String perlnatives_so = GetBundle().getString("inline_java_perlnatives_so_" + mode) ;
File f = new File(perlnatives_so) ;
if (! f.exists()){
throw new InlineJavaException("Can't initialize PerlNatives " +
"functionnality: PerlNatives extension (" + perlnatives_so +
") can't be found") ;
}
try {
Class ste_class = Class.forName("java.lang.StackTraceElement") ;
}
catch (ClassNotFoundException cnfe){
throw new InlineJavaException("Can't initialize PerlNatives " +
"functionnality: Java 1.4 or higher required (current is " +
System.getProperty("java.version") + ").") ;
}
// Load the Natives shared object
InlineJavaUtils.debug(2, "loading shared library " + perlnatives_so) ;
System.load(perlnatives_so) ;
inited = true ;
}
catch (MissingResourceException mre){
throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ;
}
}
}
// This method actually does the real work of registering the methods.
synchronized private void RegisterPerlNatives(Class c) throws InlineJavaException {
if (registered_classes.get(c) == null){
InlineJavaUtils.debug(3, "registering natives for class " + c.getName()) ;
Constructor constructors[] = c.getDeclaredConstructors() ;
Method methods[] = c.getDeclaredMethods() ;
registered_classes.put(c, c) ;
for (int i = 0 ; i < constructors.length ; i++){
Constructor x = constructors[i] ;
if (Modifier.isNative(x.getModifiers())){
RegisterMethod(c, "new", x.getParameterTypes(), c) ;
}
}
for (int i = 0 ; i < methods.length ; i++){
Method x = methods[i] ;
if (Modifier.isNative(x.getModifiers())){
RegisterMethod(c, x.getName(), x.getParameterTypes(), x.getReturnType()) ;
}
}
}
}
private void RegisterMethod(Class c, String mname, Class params[], Class rt) throws InlineJavaException {
String cname = c.getName() ;
InlineJavaUtils.debug(3, "registering native method " + mname + " for class " + cname) ;
// Check return type
if ((! Object.class.isAssignableFrom(rt))&&(rt != void.class)){
throw new InlineJavaException("Perl native method " + mname + " of class " + cname + " can only have Object or void return types (not " + rt.getName() + ")") ;
}
// fmt starts with the return type, which for now is Object only (or void).
StringBuffer fmt = new StringBuffer("L") ;
StringBuffer sign = new StringBuffer("(") ;
for (int i = 0 ; i < params.length ; i++){
String code = InlineJavaClass.FindJNICode(params[i]) ;
sign.append(code) ;
char ch = code.charAt(0) ;
char f = ch ;
if (f == '['){
// Arrays are Objects...
f = 'L' ;
}
fmt.append(new String(new char [] {f})) ;
}
sign.append(")") ;
sign.append(InlineJavaClass.FindJNICode(rt)) ;
InlineJavaUtils.debug(3, "signature is " + sign) ;
InlineJavaUtils.debug(3, "format is " + fmt) ;
// For now, no method overloading so no signature necessary
String meth = cname + "." + mname ;
String prev = (String)registered_methods.get(meth) ;
if (prev != null){
throw new InlineJavaException("There already is a native method '" + mname + "' registered for class '" + cname + "'") ;
}
registered_methods.put(meth, fmt.toString()) ;
// call the native method to hook it up
RegisterMethod(c, mname, sign.toString()) ;
}
// This native method will call RegisterNative to hook up the magic
// method implementation for the method.
native private void RegisterMethod(Class c, String name, String signature) throws InlineJavaException ;
// This method will be called from the native side. We need to figure
// out who this method is and then look in up in the
// registered method list and return the format.
private String LookupMethod() throws InlineJavaException {
InlineJavaUtils.debug(3, "entering LookupMethod") ;
String caller[] = GetNativeCaller() ;
String meth = caller[0] + "." + caller[1] ;
String fmt = (String)registered_methods.get(meth) ;
if (fmt == null){
throw new InlineJavaException("Native method " + meth + " is not registered") ;
}
InlineJavaUtils.debug(3, "exiting LookupMethod") ;
return fmt ;
}
private Object InvokePerlMethod(Object args[]) throws InlineJavaException, InlineJavaPerlException {
InlineJavaUtils.debug(3, "entering InvokePerlMethod") ;
String caller[] = GetNativeCaller() ;
String pkg = caller[0] ;
String method = caller[1] ;
// Transform the Java class name into the Perl package name
StringTokenizer st = new StringTokenizer(pkg, ".") ;
StringBuffer perl_sub = new StringBuffer() ;
// Starting with "::" means that the package is relative to the caller package
while (st.hasMoreTokens()){
perl_sub.append("::" + st.nextToken()) ;
}
perl_sub.append("::" + method) ;
for (int i = 0 ; i < args.length ; i++){
InlineJavaUtils.debug(3, "InvokePerlMethod argument " + i + " = " + args[i]) ;
}
Object ret = CallPerlSub(perl_sub.toString(), args) ;
InlineJavaUtils.debug(3, "exiting InvokePerlMethod") ;
return ret ;
}
// This method must absolutely be called by a method DIRECTLY called
// by generic_perl_native
private String[] GetNativeCaller() throws InlineJavaException {
InlineJavaUtils.debug(3, "entering GetNativeCaller") ;
Class ste_class = null ;
try {
ste_class = Class.forName("java.lang.StackTraceElement") ;
}
catch (ClassNotFoundException cnfe){
throw new InlineJavaException("Can't load class java.lang.StackTraceElement") ;
}
Throwable exec_point = new Throwable() ;
try {
Method m = exec_point.getClass().getMethod("getStackTrace", new Class [] {}) ;
Object stack = m.invoke(exec_point, new Object [] {}) ;
if (Array.getLength(stack) <= 2){
throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (call stack too short)") ;
}
Object ste = Array.get(stack, 2) ;
m = ste.getClass().getMethod("isNativeMethod", new Class [] {}) ;
Boolean is_nm = (Boolean)m.invoke(ste, new Object [] {}) ;
if (! is_nm.booleanValue()){
throw new InlineJavaException("Improper use of InlineJavaPerlNatives.GetNativeCaller (caller is not native)") ;
}
m = ste.getClass().getMethod("getClassName", new Class [] {}) ;
String cname = (String)m.invoke(ste, new Object [] {}) ;
m = ste.getClass().getMethod("getMethodName", new Class [] {}) ;
String mname = (String)m.invoke(ste, new Object [] {}) ;
InlineJavaUtils.debug(3, "exiting GetNativeCaller") ;
return new String [] {cname, mname} ;
}
catch (NoSuchMethodException nsme){
throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " +
nsme.getMessage()) ;
}
catch (IllegalAccessException iae){
throw new InlineJavaException("Error manipulating java.lang.StackTraceElement classes: " +
iae.getMessage()) ;
}
catch (InvocationTargetException ite){
// None of the methods invoked throw exceptions, so...
throw new InlineJavaException("Exception caught while manipulating java.lang.StackTraceElement classes: " +
ite.getTargetException()) ;
}
}
}