summaryrefslogtreecommitdiff
path: root/Java/sources/org/perl/inline/java
diff options
context:
space:
mode:
Diffstat (limited to 'Java/sources/org/perl/inline/java')
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaArray.java100
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCallback.java158
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java141
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaCastException.java7
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaClass.java554
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaException.java7
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaHandle.java130
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java15
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java257
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlException.java25
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java107
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java231
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaPerlObject.java73
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaProtocol.java879
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaServer.java337
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaServerThread.java69
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaThrown.java14
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java45
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java192
-rw-r--r--Java/sources/org/perl/inline/java/InlineJavaUtils.java175
20 files changed, 3516 insertions, 0 deletions
diff --git a/Java/sources/org/perl/inline/java/InlineJavaArray.java b/Java/sources/org/perl/inline/java/InlineJavaArray.java
new file mode 100644
index 0000000..c397ab4
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaArray.java
@@ -0,0 +1,100 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.lang.reflect.Array ;
+
+
+class InlineJavaArray {
+ private InlineJavaClass ijc ;
+
+
+ InlineJavaArray(InlineJavaClass _ijc){
+ ijc = _ijc ;
+ }
+
+
+ Object CreateArray(Class c, StringTokenizer st) throws InlineJavaException {
+ StringBuffer sb = new StringBuffer(st.nextToken()) ;
+ sb.replace(0, 1, "") ;
+ sb.replace(sb.length() - 1, sb.length(), "") ;
+
+ StringTokenizer st2 = new StringTokenizer(sb.toString(), ",") ;
+ ArrayList al = new ArrayList() ;
+ while (st2.hasMoreTokens()){
+ al.add(al.size(), st2.nextToken()) ;
+ }
+
+ int size = al.size() ;
+ int dims[] = new int[size] ;
+ for (int i = 0 ; i < size ; i++){
+ dims[i] = Integer.parseInt((String)al.get(i)) ;
+ InlineJavaUtils.debug(4, "array dimension: " + (String)al.get(i)) ;
+ }
+
+ Object array = null ;
+ try {
+ array = Array.newInstance(c, dims) ;
+
+ ArrayList args = new ArrayList() ;
+ while (st.hasMoreTokens()){
+ args.add(args.size(), st.nextToken()) ;
+ }
+
+ // Now we need to fill it. Since we have an arbitrary number
+ // of dimensions, we can do this recursively.
+
+ PopulateArray(array, c, dims, args) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments to array constructor for class " + c.getName() + " are incompatible: " + e.getMessage()) ;
+ }
+
+ return array ;
+ }
+
+
+ void PopulateArray (Object array, Class elem, int dims[], ArrayList args) throws InlineJavaException {
+ if (dims.length > 1){
+ int nb_args = args.size() ;
+ int nb_sub_dims = dims[0] ;
+ int nb_args_per_sub_dim = nb_args / nb_sub_dims ;
+
+ int sub_dims[] = new int[dims.length - 1] ;
+ for (int i = 1 ; i < dims.length ; i++){
+ sub_dims[i - 1] = dims[i] ;
+ }
+
+ for (int i = 0 ; i < nb_sub_dims ; i++){
+ // We want the args from i*nb_args_per_sub_dim ->
+ ArrayList sub_args = new ArrayList() ;
+ for (int j = (i * nb_args_per_sub_dim) ; j < ((i + 1) * nb_args_per_sub_dim) ; j++){
+ sub_args.add(sub_args.size(), (String)args.get(j)) ;
+ }
+ PopulateArray(((Object [])array)[i], elem, sub_dims, sub_args) ;
+ }
+ }
+ else{
+ String msg = "In creation of array of " + elem.getName() + ": " ;
+ try {
+ for (int i = 0 ; i < dims[0] ; i++){
+ String arg = (String)args.get(i) ;
+
+ Object o = ijc.CastArgument(elem, arg) ;
+ Array.set(array, i, o) ;
+ if (o != null){
+ InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o.toString()) ;
+ }
+ else{
+ InlineJavaUtils.debug(4, "setting array element " + String.valueOf(i) + " to " + o) ;
+ }
+ }
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallback.java b/Java/sources/org/perl/inline/java/InlineJavaCallback.java
new file mode 100644
index 0000000..da23afe
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCallback.java
@@ -0,0 +1,158 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Callback to Perl...
+*/
+class InlineJavaCallback {
+ private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private String pkg = null ;
+ private InlineJavaPerlObject obj = null ;
+ private String method = null ;
+ private Object args[] = null ;
+ private Class cast = null ;
+ private Object response = null ;
+ private boolean response_set = false ;
+
+
+ InlineJavaCallback(String _pkg, String _method, Object _args[], Class _cast) {
+ this(null, _pkg, _method, _args, _cast) ;
+ }
+
+
+ InlineJavaCallback(InlineJavaPerlObject _obj, String _method, Object _args[], Class _cast) {
+ this(_obj, null, _method, _args, _cast) ;
+ if (obj == null){
+ throw new NullPointerException() ;
+ }
+ }
+
+
+ private InlineJavaCallback(InlineJavaPerlObject _obj, String _pkg, String _method, Object _args[], Class _cast) {
+ obj = _obj ;
+ pkg = _pkg ;
+ method = _method ;
+ args = _args ;
+ cast = _cast ;
+
+ if (method == null){
+ throw new NullPointerException() ;
+ }
+ if (cast == null){
+ cast = java.lang.Object.class ;
+ }
+ }
+
+
+ private String GetCommand(InlineJavaProtocol ijp) throws InlineJavaException {
+ String via = null ;
+ if (obj != null){
+ via = "" + obj.GetId() ;
+ }
+ else if (pkg != null){
+ via = pkg ;
+ }
+ StringBuffer cmdb = new StringBuffer("callback " + via + " " + method + " " + cast.getName()) ;
+ if (args != null){
+ for (int i = 0 ; i < args.length ; i++){
+ cmdb.append(" " + ijp.SerializeObject(args[i], null)) ;
+ }
+ }
+ return cmdb.toString() ;
+ }
+
+
+ void ClearResponse(){
+ response = null ;
+ response_set = false ;
+ }
+
+
+ Object GetResponse(){
+ return response ;
+ }
+
+
+ synchronized Object WaitForResponse(Thread t){
+ while (! response_set){
+ try {
+ InlineJavaUtils.debug(3, "waiting for callback response in " + t.getName() + "...") ;
+ wait() ;
+ }
+ catch (InterruptedException ie){
+ // Do nothing, return and wait() some more...
+ }
+ }
+ InlineJavaUtils.debug(3, "got callback response") ;
+ Object resp = response ;
+ response = null ;
+ response_set = false ;
+ return resp ;
+ }
+
+
+ synchronized void NotifyOfResponse(Thread t){
+ InlineJavaUtils.debug(3, "notifying that callback has completed in " + t.getName()) ;
+ notify() ;
+ }
+
+
+ synchronized void Process() throws InlineJavaException, InlineJavaPerlException {
+ Object ret = null ;
+ try {
+ InlineJavaProtocol ijp = new InlineJavaProtocol(ijs, null) ;
+ String cmd = GetCommand(ijp) ;
+ InlineJavaUtils.debug(2, "callback command: " + cmd) ;
+
+ Thread t = Thread.currentThread() ;
+ String resp = null ;
+ while (true) {
+ InlineJavaUtils.debug(3, "packet sent (callback) is " + cmd) ;
+ if (! ijs.IsJNI()){
+ // Client-server mode.
+ InlineJavaServerThread ijt = (InlineJavaServerThread)t ;
+ ijt.GetWriter().write(cmd + "\n") ;
+ ijt.GetWriter().flush() ;
+
+ resp = ijt.GetReader().readLine() ;
+ }
+ else{
+ // JNI mode
+ resp = ijs.jni_callback(cmd) ;
+ }
+ InlineJavaUtils.debug(3, "packet recv (callback) is " + resp) ;
+
+ StringTokenizer st = new StringTokenizer(resp, " ") ;
+ String c = st.nextToken() ;
+ if (c.equals("callback")){
+ boolean thrown = new Boolean(st.nextToken()).booleanValue() ;
+ String arg = st.nextToken() ;
+ InlineJavaClass ijc = new InlineJavaClass(ijs, ijp) ;
+ ret = ijc.CastArgument(cast, arg) ;
+
+ if (thrown){
+ throw new InlineJavaPerlException(ret) ;
+ }
+
+ break ;
+ }
+ else{
+ // Pass it on through the regular channel...
+ InlineJavaUtils.debug(3, "packet is not callback response: " + resp) ;
+ cmd = ijs.ProcessCommand(resp, false) ;
+
+ continue ;
+ }
+ }
+ }
+ catch (IOException e){
+ throw new InlineJavaException("IO error: " + e.getMessage()) ;
+ }
+
+ response = ret ;
+ response_set = true ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java
new file mode 100644
index 0000000..a1d7ab1
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCallbackQueue.java
@@ -0,0 +1,141 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Queue for callbacks to Perl...
+*/
+class InlineJavaCallbackQueue {
+ // private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private ArrayList queue = new ArrayList() ;
+ private boolean wait_interrupted = false ;
+ private boolean stream_opened = false ;
+
+
+ InlineJavaCallbackQueue(){
+ }
+
+
+ synchronized void EnqueueCallback(InlineJavaCallback ijc){
+ queue.add(ijc) ;
+ notify() ;
+ }
+
+
+ synchronized private InlineJavaCallback DequeueCallback(){
+ if (GetSize() > 0){
+ return (InlineJavaCallback)queue.remove(0) ;
+ }
+ return null ;
+ }
+
+
+ synchronized int WaitForCallback(double timeout){
+ long secs = (long)Math.floor(timeout) ;
+ double rest = timeout - ((double)secs) ;
+ long millis = (long)Math.floor(rest * 1000.0) ;
+ rest = (rest * 1000.0) - ((double)millis) ;
+ int nanos = (int)Math.floor(rest * 1000000.0) ;
+
+ return WaitForCallback((secs * 1000) + millis, nanos) ;
+ }
+
+
+ /*
+ Blocks up to the specified time for the next callback to arrive.
+ Returns -1 if the wait was interrupted voluntarily, 0 on timeout or
+ > 0 if a callback has arrived before the timeout expired.
+ */
+ synchronized int WaitForCallback(long millis, int nanos){
+ wait_interrupted = false ;
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "waiting for callback request (" + millis + " millis, " +
+ nanos + " nanos) in " + t.getName() + "...") ;
+
+ if (! stream_opened){
+ return -1 ;
+ }
+
+ while ((stream_opened)&&(! wait_interrupted)&&(IsEmpty())){
+ try {
+ wait(millis, nanos) ;
+ // If we reach this code, it means the either we timed out
+ // or that we were notify()ed.
+ // In the former case, we must break out and return 0.
+ // In the latter case, either the queue will not be empty or
+ // wait_interrupted will be set. We must therefore also break out.
+ break ;
+ }
+ catch (InterruptedException ie){
+ // Do nothing, return and wait() some more...
+ }
+ }
+ InlineJavaUtils.debug(3, "waiting for callback request finished " + t.getName() + "...") ;
+
+ if (wait_interrupted){
+ return -1 ;
+ }
+ else {
+ return GetSize() ;
+ }
+ }
+
+
+ /*
+ Waits indefinetely for the next callback to arrive and executes it.
+ Return true on success of false if the wait was interrupted voluntarily.
+ */
+ synchronized boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException {
+ int rc = WaitForCallback(0, 0) ;
+ if (rc == -1){
+ // Wait was interrupted
+ return false ;
+ }
+
+ // DequeueCallback can't return null because we explicetely
+ // waited until a callback was there.
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "processing callback request in " + t.getName() + "...") ;
+ InlineJavaCallback ijc = DequeueCallback() ;
+ ijc.Process() ;
+ ijc.NotifyOfResponse(t) ;
+
+ return true ;
+ }
+
+
+ private boolean IsEmpty(){
+ return (GetSize() == 0) ;
+ }
+
+
+ void OpenCallbackStream(){
+ stream_opened = true ;
+ }
+
+
+ synchronized void CloseCallbackStream(){
+ stream_opened = false ;
+ InterruptWaitForCallback() ;
+ }
+
+
+ boolean IsStreamOpen(){
+ return stream_opened ;
+ }
+
+
+ int GetSize(){
+ return queue.size() ;
+ }
+
+
+ synchronized private void InterruptWaitForCallback(){
+ Thread t = Thread.currentThread() ;
+ InlineJavaUtils.debug(3, "interrupting wait for callback request in " + t.getName() + "...") ;
+ wait_interrupted = true ;
+ notify() ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaCastException.java b/Java/sources/org/perl/inline/java/InlineJavaCastException.java
new file mode 100644
index 0000000..b799689
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaCastException.java
@@ -0,0 +1,7 @@
+package org.perl.inline.java ;
+
+class InlineJavaCastException extends InlineJavaException {
+ InlineJavaCastException(String m){
+ super(m) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaClass.java b/Java/sources/org/perl/inline/java/InlineJavaClass.java
new file mode 100644
index 0000000..bd57769
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaClass.java
@@ -0,0 +1,554 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.lang.reflect.* ;
+
+
+class InlineJavaClass {
+ private InlineJavaServer ijs ;
+ private InlineJavaProtocol ijp ;
+
+ static private HashMap class2jni_code = new HashMap() ;
+ static {
+ class2jni_code.put(byte.class, "B") ;
+ class2jni_code.put(short.class, "S") ;
+ class2jni_code.put(int.class, "I") ;
+ class2jni_code.put(long.class, "J") ;
+ class2jni_code.put(float.class, "F") ;
+ class2jni_code.put(double.class, "D") ;
+ class2jni_code.put(boolean.class, "Z") ;
+ class2jni_code.put(char.class, "C") ;
+ class2jni_code.put(void.class, "V") ;
+ } ;
+
+ static private HashMap class2wrapper = new HashMap() ;
+ static {
+ class2wrapper.put(byte.class, java.lang.Byte.class) ;
+ class2wrapper.put(short.class, java.lang.Short.class) ;
+ class2wrapper.put(int.class, java.lang.Integer.class) ;
+ class2wrapper.put(long.class, java.lang.Long.class) ;
+ class2wrapper.put(float.class, java.lang.Float.class) ;
+ class2wrapper.put(double.class, java.lang.Double.class) ;
+ class2wrapper.put(boolean.class, java.lang.Boolean.class) ;
+ class2wrapper.put(char.class, java.lang.Character.class) ;
+ class2wrapper.put(void.class, java.lang.Void.class) ;
+ } ;
+
+ static private HashMap name2class = new HashMap() ;
+ static {
+ name2class.put("byte", byte.class) ;
+ name2class.put("short", short.class) ;
+ name2class.put("int", int.class) ;
+ name2class.put("long", long.class) ;
+ name2class.put("float", float.class) ;
+ name2class.put("double", double.class) ;
+ name2class.put("boolean", boolean.class) ;
+ name2class.put("char", char.class) ;
+ name2class.put("void", void.class) ;
+ name2class.put("B", byte.class) ;
+ name2class.put("S", short.class) ;
+ name2class.put("I", int.class) ;
+ name2class.put("J", long.class) ;
+ name2class.put("F", float.class) ;
+ name2class.put("D", double.class) ;
+ name2class.put("Z", boolean.class) ;
+ name2class.put("C", char.class) ;
+ name2class.put("V", void.class) ;
+ } ;
+
+
+ InlineJavaClass(InlineJavaServer _ijs, InlineJavaProtocol _ijp){
+ ijs = _ijs ;
+ ijp = _ijp ;
+ }
+
+
+ /*
+ Makes sure a class exists
+ */
+ static Class ValidateClass(String name) throws InlineJavaException {
+ Class pc = FindType(name) ;
+ if (pc != null){
+ return pc ;
+ }
+
+ try {
+ Class c = Class.forName(name, true, InlineJavaServer.GetInstance().GetUserClassLoader()) ;
+ return c ;
+ }
+ catch (ClassNotFoundException e){
+ throw new InlineJavaException("Class " + name + " not found") ;
+ }
+ }
+
+
+ /*
+ Remove L...; from a class name if it has been extracted from an Array class name.
+ */
+ static String CleanClassName(String name){
+ if (name != null){
+ int l = name.length() ;
+ if ((l > 2)&&(name.charAt(0) == 'L')&&(name.charAt(l - 1) == ';')){
+ name = name.substring(1, l - 1) ;
+ }
+ }
+ return name ;
+ }
+
+
+ static private Class ValidateClassQuiet(String name){
+ try {
+ return ValidateClass(name) ;
+ }
+ catch (InlineJavaException ije){
+ return null ;
+ }
+ }
+
+ /*
+ This is the monster method that determines how to cast arguments
+ */
+ Object [] CastArguments(Class [] params, ArrayList args) throws InlineJavaException {
+ Object ret[] = new Object [params.length] ;
+
+ for (int i = 0 ; i < params.length ; i++){
+ // Here the args are all strings or objects (or undef)
+ // we need to match them to the prototype.
+ Class p = params[i] ;
+ InlineJavaUtils.debug(4, "arg " + String.valueOf(i) + " of signature is " + p.getName()) ;
+
+ ret[i] = CastArgument(p, (String)args.get(i)) ;
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ This is the monster method that determines how to cast arguments
+ */
+ Object CastArgument(Class p, String argument) throws InlineJavaException {
+ Object ret = null ;
+
+ ArrayList tokens = new ArrayList() ;
+ StringTokenizer st = new StringTokenizer(argument, ":") ;
+ for (int j = 0 ; st.hasMoreTokens() ; j++){
+ tokens.add(j, st.nextToken()) ;
+ }
+ if (tokens.size() == 1){
+ tokens.add(1, "") ;
+ }
+ String type = (String)tokens.get(0) ;
+
+ // We need to separate the primitive types from the
+ // reference types.
+ boolean num = ClassIsNumeric(p) ;
+ if ((num)||(ClassIsString(p))){
+ Class ap = p ;
+ if (ap == java.lang.Number.class){
+ InlineJavaUtils.debug(4, "specializing java.lang.Number to java.lang.Double") ;
+ ap = java.lang.Double.class ;
+ }
+ else if (ap.getName().equals("java.lang.CharSequence")){
+ InlineJavaUtils.debug(4, "specializing java.lang.CharSequence to java.lang.String") ;
+ ap = java.lang.String.class ;
+ }
+
+ if (type.equals("undef")){
+ if (num){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " 0") ;
+ ret = ijp.CreateObject(ap, new Object [] {"0"}, new Class [] {String.class}) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ ret = null ;
+ InlineJavaUtils.debug(4, "args is undef -> forcing to " + ap.getName() + " " + ret) ;
+ InlineJavaUtils.debug(4, " result is " + ret) ;
+ }
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to " + ap.getName()) ;
+ try {
+ ret = ijp.CreateObject(ap, new Object [] {arg}, new Class [] {String.class}) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ catch (NumberFormatException e){
+ throw new InlineJavaCastException("Can't convert " + arg + " to " + ap.getName()) ;
+ }
+ }
+ else if (type.equals("double")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ // We have native double bytes in arg.
+ long l = 0 ;
+ char c[] = arg.toCharArray() ;
+ for (int i = 0 ; i < 8 ; i++){
+ l += (((long)c[i]) << (8 * i)) ;
+ }
+ double d = Double.longBitsToDouble(l) ;
+ ret = new Double(d) ;
+ }
+ else {
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else if (ClassIsBool(p)){
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to bool false") ;
+ ret = new Boolean("false") ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar (" + arg + ") -> forcing to bool") ;
+ if ((arg.equals(""))||(arg.equals("0"))){
+ arg = "false" ;
+ }
+ else{
+ arg = "true" ;
+ }
+ ret = new Boolean(arg) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else if (ClassIsChar(p)){
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to char '\0'") ;
+ ret = new Character('\0') ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else if (type.equals("scalar")){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ InlineJavaUtils.debug(4, "args is scalar -> forcing to char") ;
+ char c = '\0' ;
+ if (arg.length() == 1){
+ c = arg.toCharArray()[0] ;
+ }
+ else if (arg.length() > 1){
+ throw new InlineJavaCastException("Can't convert " + arg + " to " + p.getName()) ;
+ }
+ ret = new Character(c) ;
+ InlineJavaUtils.debug(4, " result is " + ret.toString()) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert reference to " + p.getName()) ;
+ }
+ }
+ else {
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+ // We know that what we expect here is a real object
+ if (type.equals("undef")){
+ InlineJavaUtils.debug(4, "args is undef -> forcing to null") ;
+ ret = null ;
+ }
+ else if (type.equals("scalar")){
+ // Here if we need a java.lang.Object.class, it's probably
+ // because we can store anything, so we use a String object.
+ if (p == java.lang.Object.class){
+ String arg = ijp.Decode((String)tokens.get(1)) ;
+ ret = arg ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't convert primitive type to " + p.getName()) ;
+ }
+ }
+ else if (type.equals("java_object")){
+ // We need an object and we got an object...
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+
+ String c_name = (String)tokens.get(1) ;
+ String objid = (String)tokens.get(2) ;
+
+ Class c = ValidateClass(c_name) ;
+
+ if (DoesExtend(c, p) > -1){
+ InlineJavaUtils.debug(4, " " + c.getName() + " is a kind of " + p.getName()) ;
+ // get the object from the hash table
+ int id = Integer.parseInt(objid) ;
+ Object o = ijs.GetObject(id) ;
+ ret = o ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't cast a " + c.getName() + " to a " + p.getName()) ;
+ }
+ }
+ else{
+ InlineJavaUtils.debug(4, "class " + p.getName() + " is reference") ;
+
+ String pkg = (String)tokens.get(1) ;
+ pkg = pkg.replace('/', ':') ;
+ String objid = (String)tokens.get(2) ;
+
+
+ if (DoesExtend(p, org.perl.inline.java.InlineJavaPerlObject.class) > -1){
+ InlineJavaUtils.debug(4, " Perl object is a kind of " + p.getName()) ;
+ int id = Integer.parseInt(objid) ;
+ ret = new InlineJavaPerlObject(pkg, id) ;
+ }
+ else{
+ throw new InlineJavaCastException("Can't cast a Perl object to a " + p.getName()) ;
+ }
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Returns the number of levels that separate a from b
+ */
+ static int DoesExtend(Class a, Class b){
+ return DoesExtend(a, b, 0) ;
+ }
+
+
+ static int DoesExtend(Class a, Class b, int level){
+ InlineJavaUtils.debug(4, "checking if " + a.getName() + " extends " + b.getName()) ;
+
+ if (a == b){
+ return level ;
+ }
+
+ Class parent = a.getSuperclass() ;
+ if (parent != null){
+ InlineJavaUtils.debug(4, " parent is " + parent.getName()) ;
+ int ret = DoesExtend(parent, b, level + 1) ;
+ if (ret != -1){
+ return ret ;
+ }
+ }
+
+ // Maybe b is an interface a implements it?
+ Class inter[] = a.getInterfaces() ;
+ for (int i = 0 ; i < inter.length ; i++){
+ InlineJavaUtils.debug(4, " interface is " + inter[i].getName()) ;
+ int ret = DoesExtend(inter[i], b, level + 1) ;
+ if (ret != -1){
+ return ret ;
+ }
+ }
+
+ return -1 ;
+ }
+
+
+ /*
+ Finds the wrapper class for the passed primitive type.
+ */
+ static Class FindWrapper(Class p){
+ Class w = (Class)class2wrapper.get(p) ;
+ if (w == null){
+ w = p ;
+ }
+
+ return w ;
+ }
+
+
+ /*
+ Finds the primitive type class for the passed primitive type name.
+ */
+ static Class FindType (String name){
+ return (Class)name2class.get(name) ;
+ }
+
+
+ static String FindJNICode(Class p){
+ if (! Object.class.isAssignableFrom(p)){
+ return (String)class2jni_code.get(p) ;
+ }
+ else {
+ String name = p.getName().replace('.', '/') ;
+ if (p.isArray()){
+ return name ;
+ }
+ else{
+ return "L" + name + ";" ;
+ }
+ }
+ }
+
+
+ static boolean ClassIsPrimitive(Class p){
+ String name = p.getName() ;
+
+ if ((ClassIsNumeric(p))||(ClassIsString(p))||(ClassIsChar(p))||(ClassIsBool(p))){
+ InlineJavaUtils.debug(4, "class " + name + " is primitive") ;
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ /*
+ Determines if class is of numerical type.
+ */
+ static private HashMap numeric_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Byte.class,
+ java.lang.Short.class,
+ java.lang.Integer.class,
+ java.lang.Long.class,
+ java.lang.Float.class,
+ java.lang.Double.class,
+ java.lang.Number.class,
+ byte.class,
+ short.class,
+ int.class,
+ long.class,
+ float.class,
+ double.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ numeric_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsNumeric (Class p){
+ return (numeric_classes.get(p) != null) ;
+ }
+
+
+ static private HashMap double_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Double.class,
+ double.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ double_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsDouble (Class p){
+ return (double_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is String or StringBuffer
+ */
+ static private HashMap string_classes = new HashMap() ;
+ static {
+ Class csq = ValidateClassQuiet("java.lang.CharSequence") ;
+ Class [] list = {
+ java.lang.String.class,
+ java.lang.StringBuffer.class,
+ csq
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ string_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsString (Class p){
+ return (string_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is Char
+ */
+ static private HashMap char_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Character.class,
+ char.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ char_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsChar (Class p){
+ return (char_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Class is Bool
+ */
+ static private HashMap bool_classes = new HashMap() ;
+ static {
+ Class [] list = {
+ java.lang.Boolean.class,
+ boolean.class,
+ } ;
+ for (int i = 0 ; i < list.length ; i++){
+ bool_classes.put(list[i], new Boolean(true)) ;
+ }
+ }
+ static boolean ClassIsBool (Class p){
+ return (bool_classes.get(p) != null) ;
+ }
+
+
+ /*
+ Determines if a class is not of a primitive type or of a
+ wrapper class.
+ */
+ static boolean ClassIsReference (Class p){
+ String name = p.getName() ;
+
+ if (ClassIsPrimitive(p)){
+ return false ;
+ }
+
+ InlineJavaUtils.debug(4, "class " + name + " is reference") ;
+
+ return true ;
+ }
+
+
+ static boolean ClassIsArray (Class p){
+ String name = p.getName() ;
+
+ if ((ClassIsReference(p))&&(name.startsWith("["))){
+ InlineJavaUtils.debug(4, "class " + name + " is array") ;
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsPublic (Class p){
+ int pub = p.getModifiers() & Modifier.PUBLIC ;
+ if (pub != 0){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsHandle (Class p){
+ if ((ClassIsReadHandle(p))||(ClassIsWriteHandle(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsReadHandle (Class p){
+ if ((java.io.Reader.class.isAssignableFrom(p))||
+ (java.io.InputStream.class.isAssignableFrom(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ static boolean ClassIsWriteHandle (Class p){
+ if ((java.io.Writer.class.isAssignableFrom(p))||
+ (java.io.OutputStream.class.isAssignableFrom(p))){
+ return true ;
+ }
+
+ return false ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaException.java b/Java/sources/org/perl/inline/java/InlineJavaException.java
new file mode 100644
index 0000000..7e2f3cd
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaException.java
@@ -0,0 +1,7 @@
+package org.perl.inline.java ;
+
+public class InlineJavaException extends Exception {
+ public InlineJavaException(String s) {
+ super(s) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaHandle.java b/Java/sources/org/perl/inline/java/InlineJavaHandle.java
new file mode 100644
index 0000000..681cf18
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaHandle.java
@@ -0,0 +1,130 @@
+package org.perl.inline.java ;
+
+
+import java.util.* ;
+import java.io.* ;
+
+
+public class InlineJavaHandle {
+ private static final String charset = "ISO-8859-1" ;
+
+
+ static String read(Object o, int len) throws InlineJavaException, IOException {
+ String ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.Reader){
+ char buf[] = new char[len] ;
+ int rc = ((java.io.Reader)o).read(buf) ;
+ if (rc != -1){
+ ret = new String(buf) ;
+ }
+ }
+ else {
+ byte buf[] = new byte[len] ;
+ int rc = ((java.io.InputStream)o).read(buf) ;
+ if (rc != -1){
+ ret = new String(buf, charset) ;
+ }
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't read from non-readhandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static String readLine(Object o) throws InlineJavaException, IOException {
+ String ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.BufferedReader){
+ ret = ((java.io.BufferedReader)o).readLine() ;
+ }
+ else {
+ throw new InlineJavaException("Can't read line from non-buffered Reader or InputStream") ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't read line from non-readhandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static Object makeBuffered(Object o) throws InlineJavaException, IOException {
+ Object ret = null ;
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.BufferedReader){
+ ret = (java.io.BufferedReader)o ;
+ }
+ else if (o instanceof java.io.Reader){
+ ret = new BufferedReader((java.io.Reader)o) ;
+ }
+ else {
+ ret = new BufferedReader(new InputStreamReader((java.io.InputStream)o, charset)) ;
+ }
+ }
+ else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.BufferedWriter){
+ ret = (java.io.BufferedWriter)o ;
+ }
+ else if (o instanceof java.io.Writer){
+ ret = new BufferedWriter((java.io.Writer)o) ;
+ }
+ else {
+ ret = new BufferedWriter(new OutputStreamWriter((java.io.OutputStream)o, charset)) ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't make non-handle object buffered (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static int write(Object o, String str) throws InlineJavaException, IOException {
+ int ret = -1 ;
+ if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.Writer){
+ ((java.io.Writer)o).write(str) ;
+ ret = str.length() ;
+ }
+ else {
+ byte b[] = str.getBytes(charset) ;
+ ((java.io.OutputStream)o).write(b) ;
+ ret = b.length ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't write to non-writehandle object (" + o.getClass().getName() + ")") ;
+ }
+
+ return ret ;
+ }
+
+
+ static void close(Object o) throws InlineJavaException, IOException {
+ if (InlineJavaClass.ClassIsReadHandle(o.getClass())){
+ if (o instanceof java.io.Reader){
+ ((java.io.Reader)o).close() ;
+ }
+ else {
+ ((java.io.InputStream)o).close() ;
+ }
+ }
+ else if (InlineJavaClass.ClassIsWriteHandle(o.getClass())){
+ if (o instanceof java.io.Writer){
+ ((java.io.Writer)o).close() ;
+ }
+ else {
+ ((java.io.OutputStream)o).close() ;
+ }
+ }
+ else {
+ throw new InlineJavaException("Can't close non-handle object (" + o.getClass().getName() + ")") ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java
new file mode 100644
index 0000000..768bb1a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaInvocationTargetException.java
@@ -0,0 +1,15 @@
+package org.perl.inline.java ;
+
+class InlineJavaInvocationTargetException extends InlineJavaException {
+ private Throwable t ;
+
+
+ InlineJavaInvocationTargetException(String m, Throwable _t){
+ super(m) ;
+ t = _t ;
+ }
+
+ Throwable GetThrowable(){
+ return t ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java
new file mode 100644
index 0000000..c389358
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlCaller.java
@@ -0,0 +1,257 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ Callback to Perl...
+*/
+public class InlineJavaPerlCaller {
+ private InlineJavaServer ijs = InlineJavaServer.GetInstance() ;
+ private Thread creator = null ;
+ static private Map thread_callback_queues = Collections.synchronizedMap(new HashMap()) ;
+ static private ResourceBundle resources = null ;
+ static private boolean inited = false ;
+
+
+ /*
+ Only thread that communicate with Perl are allowed to create PerlCallers because
+ this is where we get the thread that needs to be notified when the callbacks come in.
+ */
+ public InlineJavaPerlCaller() throws InlineJavaException {
+ init() ;
+ Thread t = Thread.currentThread() ;
+ if (ijs.IsThreadPerlContact(t)){
+ creator = t ;
+ }
+ else{
+ throw new InlineJavaException("InlineJavaPerlCaller objects can only be created by threads that communicate directly with Perl") ;
+ }
+ }
+
+
+ synchronized static protected void init() throws InlineJavaException {
+ if (! inited){
+ try {
+ resources = ResourceBundle.getBundle("InlineJava") ;
+
+ inited = true ;
+ }
+ catch (MissingResourceException mre){
+ throw new InlineJavaException("Error loading InlineJava.properties: " + mre.getMessage()) ;
+ }
+ }
+ }
+
+
+ static protected ResourceBundle GetBundle(){
+ return resources ;
+ }
+
+ /* Old interface */
+ /**
+ * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[])}
+ */
+ public Object CallPerl(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerl(pkg, method, args, null) ;
+ }
+
+
+ /* Old interface */
+ /**
+ * @deprecated As of 0.48, replaced by {@link #CallPerlSub(String,Object[],Class)}
+ */
+ public Object CallPerl(String pkg, String method, Object args[], String cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ (String)null, pkg + "::" + method, args,
+ (cast == null ? null : InlineJavaClass.ValidateClass(cast))) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlSub(String sub, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlSub(sub, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlSub(String sub, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ (String)null, sub, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlMethod(obj, method, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlMethod(InlineJavaPerlObject obj, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ obj, method, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlStaticMethod(String pkg, String method, Object args[]) throws InlineJavaException, InlineJavaPerlException {
+ return CallPerlStaticMethod(pkg, method, args, null) ;
+ }
+
+
+ /* New interface */
+ public Object CallPerlStaticMethod(String pkg, String method, Object args[], Class cast) throws InlineJavaException, InlineJavaPerlException {
+ InlineJavaCallback ijc = new InlineJavaCallback(
+ pkg, method, args, cast) ;
+ return CallPerl(ijc) ;
+ }
+
+
+ public Object eval(String code) throws InlineJavaPerlException, InlineJavaException {
+ return eval(code, null) ;
+ }
+
+
+ public Object eval(String code, Class cast) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_eval", new Object [] {code}, cast) ;
+ }
+
+
+ public Object require(String module_or_file) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module_or_file}) ;
+ }
+
+
+ public Object require_file(String file) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {file, new Boolean("true")}) ;
+ }
+
+
+ public Object require_module(String module) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlSub("Inline::Java::Callback::java_require", new Object [] {module, new Boolean("false")}) ;
+ }
+
+
+ private Object CallPerl(InlineJavaCallback ijc) throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (t == creator){
+ ijc.Process() ;
+ return ijc.GetResponse() ;
+ }
+ else {
+ // Enqueue the callback into the creator thread's queue and notify it
+ // that there is some work for him.
+ ijc.ClearResponse() ;
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ InlineJavaUtils.debug(3, "enqueing callback for processing for " + creator.getName() + " in " + t.getName() + "...") ;
+ q.EnqueueCallback(ijc) ;
+ InlineJavaUtils.debug(3, "notifying that a callback request is available for " + creator.getName() + " in " + t.getName()) ;
+
+ // Now we must wait until the callback is processed and get back the result...
+ return ijc.WaitForResponse(t) ;
+ }
+ }
+
+
+ public void OpenCallbackStream() throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.OpenCallbackStream() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ q.OpenCallbackStream() ;
+ }
+
+
+ /*
+ Blocks until either a callback arrives, timeout seconds has passed or the call is
+ interrupted by Interrupt?
+ */
+ public int WaitForCallback(double timeout) throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.WaitForCallback() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ if (timeout == 0.0){
+ // no wait
+ return q.GetSize() ;
+ }
+ else if (timeout == -1.0){
+ timeout = 0.0 ;
+ }
+
+ return q.WaitForCallback(timeout) ;
+ }
+
+
+ public boolean ProcessNextCallback() throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.ProcessNextCallback() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ return q.ProcessNextCallback() ;
+ }
+
+
+ public void CloseCallbackStream() throws InlineJavaException {
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ q.CloseCallbackStream() ;
+ }
+
+
+ public void StartCallbackLoop() throws InlineJavaException, InlineJavaPerlException {
+ Thread t = Thread.currentThread() ;
+ if (! ijs.IsThreadPerlContact(t)){
+ throw new InlineJavaException("InlineJavaPerlCaller.StartCallbackLoop() can only be called by threads that communicate directly with Perl") ;
+ }
+
+ InlineJavaCallbackQueue q = GetQueue(t) ;
+ InlineJavaUtils.debug(3, "starting callback loop for " + creator.getName() + " in " + t.getName()) ;
+ q.OpenCallbackStream() ;
+ while (q.IsStreamOpen()){
+ q.ProcessNextCallback() ;
+ }
+ }
+
+
+ public void StopCallbackLoop() throws InlineJavaException {
+ Thread t = Thread.currentThread() ;
+ InlineJavaCallbackQueue q = GetQueue(creator) ;
+ InlineJavaUtils.debug(3, "stopping callback loop for " + creator.getName() + " in " + t.getName()) ;
+ q.CloseCallbackStream() ;
+ }
+
+
+ /*
+ Here the prototype accepts Threads because the JNI thread
+ calls this method also.
+ */
+ static synchronized void AddThread(Thread t){
+ thread_callback_queues.put(t, new InlineJavaCallbackQueue()) ;
+ }
+
+
+ static synchronized void RemoveThread(InlineJavaServerThread t){
+ thread_callback_queues.remove(t) ;
+ }
+
+
+ static private InlineJavaCallbackQueue GetQueue(Thread t) throws InlineJavaException {
+ InlineJavaCallbackQueue q = (InlineJavaCallbackQueue)thread_callback_queues.get(t) ;
+
+ if (q == null){
+ throw new InlineJavaException("Can't find thread " + t.getName() + "!") ;
+ }
+ return q ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlException.java b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java
new file mode 100644
index 0000000..38fd631
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlException.java
@@ -0,0 +1,25 @@
+package org.perl.inline.java ;
+
+
+public class InlineJavaPerlException extends Exception {
+ private Object obj ;
+
+
+ public InlineJavaPerlException(Object o){
+ super(o.toString()) ;
+ obj = o ;
+ }
+
+ public InlineJavaPerlException(String s){
+ super(s) ;
+ obj = s ;
+ }
+
+ public Object GetObject(){
+ return obj ;
+ }
+
+ public String GetString(){
+ return (String)obj ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java
new file mode 100644
index 0000000..6a24ce2
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlInterpreter.java
@@ -0,0 +1,107 @@
+package org.perl.inline.java ;
+
+
+import java.util.* ;
+import java.io.* ;
+
+
+/*
+ InlineJavaPerlInterpreter
+
+ This singleton class creates a PerlInterpreter object. To this object is bound
+ an instance of InlineJavaServer that will allow communication with Perl.
+
+ All communication with Perl must be done via InlineJavaPerlCaller in order to insure
+ thread synchronization. Therefore all Perl actions will be implemented via functions
+ in Inline::Java::PerlInterperter so that they can be called via InlineJavaPerlCaller
+*/
+public class InlineJavaPerlInterpreter extends InlineJavaPerlCaller {
+ static private boolean inited = false ;
+ static InlineJavaPerlInterpreter instance = null ;
+ static boolean test = false ;
+ static String libperl_so = "" ;
+
+
+ protected InlineJavaPerlInterpreter() throws InlineJavaPerlException, InlineJavaException {
+ init() ;
+
+ InlineJavaUtils.debug(2, "constructing perl interpreter") ;
+ construct() ;
+ InlineJavaUtils.debug(2, "perl interpreter constructed") ;
+
+ if (! libperl_so.equals("")){
+ evalNoReturn("require DynaLoader ;") ;
+ evalNoReturn("DynaLoader::dl_load_file(\"" + libperl_so + "\", 0x01) ;") ;
+ }
+ if (test){
+ evalNoReturn("use blib ;") ;
+ }
+ evalNoReturn("use Inline::Java::PerlInterpreter ;") ;
+ }
+
+
+ synchronized static public InlineJavaPerlInterpreter create() throws InlineJavaPerlException, InlineJavaException {
+ if (instance == null){
+ // Here we create a temporary InlineJavaServer instance in order to be able to instanciate
+ // ourselves. When we create InlineJavaPerlInterpreter, the instance will be overriden.
+ InlineJavaUtils.debug(2, "creating temporary JNI InlineJavaServer") ;
+ InlineJavaServer.jni_main(InlineJavaUtils.get_debug(), false) ;
+ InlineJavaUtils.debug(2, "temporary JNI InlineJavaServer created") ;
+ InlineJavaUtils.debug(2, "creating InlineJavaPerlInterpreter") ;
+ instance = new InlineJavaPerlInterpreter() ;
+ InlineJavaUtils.debug(2, "InlineJavaPerlInterpreter created") ;
+ }
+ return instance ;
+ }
+
+
+ synchronized static protected void init() throws InlineJavaException {
+ init("install") ;
+ }
+
+
+ synchronized static protected void init(String mode) throws InlineJavaException {
+ InlineJavaPerlCaller.init() ;
+ if (! inited){
+ test = (mode.equals("test") ? true : false) ;
+ try {
+ String perlinterpreter_so = GetBundle().getString("inline_java_perlinterpreter_so_" + mode) ;
+ File f = new File(perlinterpreter_so) ;
+ if (! f.exists()){
+ throw new InlineJavaException("Can't initialize PerlInterpreter " +
+ "functionnality: PerlInterpreter extension (" + perlinterpreter_so +
+ ") can't be found") ;
+ }
+
+ // Load the PerlInterpreter shared object
+ InlineJavaUtils.debug(2, "loading shared library " + perlinterpreter_so) ;
+ System.load(perlinterpreter_so) ;
+ InlineJavaUtils.debug(2, "shared library " + perlinterpreter_so + " loaded") ;
+
+ libperl_so = GetBundle().getString("inline_java_libperl_so") ;
+
+ inited = true ;
+ }
+ catch (MissingResourceException mre){
+ throw new InlineJavaException("Error loading InlineJava.properties resource: " + mre.getMessage()) ;
+ }
+ }
+ }
+
+
+ synchronized static private native void construct() ;
+
+
+ synchronized static private native void evalNoReturn(String code) throws InlineJavaPerlException ;
+
+
+ synchronized static private native void destruct() ;
+
+
+ synchronized static public void destroy() {
+ if (instance != null){
+ destruct() ;
+ instance = null ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java
new file mode 100644
index 0000000..5682e6a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlNatives.java
@@ -0,0 +1,231 @@
+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()) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java
new file mode 100644
index 0000000..b57caab
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaPerlObject.java
@@ -0,0 +1,73 @@
+package org.perl.inline.java ;
+
+
+/*
+ InlineJavaPerlObject
+*/
+public class InlineJavaPerlObject extends InlineJavaPerlCaller {
+ private int id = 0 ;
+ private String pkg = null ;
+
+
+ /*
+ Creates a Perl Object by calling
+ pkg->new(args) ;
+ */
+ public InlineJavaPerlObject(String _pkg, Object args[]) throws InlineJavaPerlException, InlineJavaException {
+ pkg = _pkg ;
+ InlineJavaPerlObject stub = (InlineJavaPerlObject)CallPerlStaticMethod(pkg, "new", args, getClass()) ;
+ id = stub.GetId() ;
+ stub.id = 0 ;
+ }
+
+
+ /*
+ This is just a stub for already existing objects
+ */
+ InlineJavaPerlObject(String _pkg, int _id) throws InlineJavaException {
+ pkg = _pkg ;
+ id = _id ;
+ }
+
+
+ int GetId(){
+ return id ;
+ }
+
+
+ public String GetPkg(){
+ return pkg ;
+ }
+
+
+ public Object InvokeMethod(String name, Object args[]) throws InlineJavaPerlException, InlineJavaException {
+ return InvokeMethod(name, args, null) ;
+ }
+
+
+ public Object InvokeMethod(String name, Object args[], Class cast) throws InlineJavaPerlException, InlineJavaException {
+ return CallPerlMethod(this, name, args, cast) ;
+ }
+
+
+ public void Dispose() throws InlineJavaPerlException, InlineJavaException {
+ Dispose(false) ;
+ }
+
+
+ protected void Dispose(boolean gc) throws InlineJavaPerlException, InlineJavaException {
+ if (id != 0){
+ CallPerlSub("Inline::Java::Callback::java_finalize", new Object [] {new Integer(id), new Boolean(gc)}) ;
+ }
+ }
+
+
+ protected void finalize() throws Throwable {
+ try {
+ Dispose(true) ;
+ }
+ finally {
+ super.finalize() ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaProtocol.java b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java
new file mode 100644
index 0000000..e84c214
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaProtocol.java
@@ -0,0 +1,879 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+import java.io.* ;
+import java.lang.reflect.* ;
+
+
+/*
+ This is where most of the work of Inline Java is done. Here determine
+ the request type and then we proceed to serve it.
+*/
+class InlineJavaProtocol {
+ private InlineJavaServer ijs ;
+ private InlineJavaClass ijc ;
+ private InlineJavaArray ija ;
+ private String cmd ;
+ private String response = null ;
+
+ private final String encoding = "UTF-8" ;
+
+ static private Map member_cache = Collections.synchronizedMap(new HashMap()) ;
+ static private final String report_version = "V2" ;
+
+ InlineJavaProtocol(InlineJavaServer _ijs, String _cmd) {
+ ijs = _ijs ;
+ ijc = new InlineJavaClass(ijs, this) ;
+ ija = new InlineJavaArray(ijc) ;
+
+ cmd = _cmd ;
+ }
+
+
+ /*
+ Starts the analysis of the command line
+ */
+ void Do() throws InlineJavaException {
+ StringTokenizer st = new StringTokenizer(cmd, " ") ;
+ String c = st.nextToken() ;
+
+ if (c.equals("call_method")){
+ CallJavaMethod(st) ;
+ }
+ else if (c.equals("set_member")){
+ SetJavaMember(st) ;
+ }
+ else if (c.equals("get_member")){
+ GetJavaMember(st) ;
+ }
+ else if (c.equals("add_classpath")){
+ AddClassPath(st) ;
+ }
+ else if (c.equals("server_type")){
+ ServerType(st) ;
+ }
+ else if (c.equals("report")){
+ Report(st) ;
+ }
+ else if (c.equals("isa")){
+ IsA(st) ;
+ }
+ else if (c.equals("create_object")){
+ CreateJavaObject(st) ;
+ }
+ else if (c.equals("delete_object")){
+ DeleteJavaObject(st) ;
+ }
+ else if (c.equals("obj_cnt")){
+ ObjectCount(st) ;
+ }
+ else if (c.equals("cast")){
+ Cast(st) ;
+ }
+ else if (c.equals("read")){
+ Read(st) ;
+ }
+ else if (c.equals("make_buffered")){
+ MakeBuffered(st) ;
+ }
+ else if (c.equals("readline")){
+ ReadLine(st) ;
+ }
+ else if (c.equals("write")){
+ Write(st) ;
+ }
+ else if (c.equals("close")){
+ Close(st) ;
+ }
+ else if (c.equals("die")){
+ InlineJavaUtils.debug(1, "received a request to die...") ;
+ ijs.Shutdown() ;
+ }
+ else {
+ throw new InlineJavaException("Unknown command " + c) ;
+ }
+ }
+
+ /*
+ Returns a report on the Java classes, listing all public methods
+ and members
+ */
+ void Report(StringTokenizer st) throws InlineJavaException {
+ StringBuffer pw = new StringBuffer(report_version + "\n") ;
+
+ StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":") ;
+ st2.nextToken() ;
+
+ StringTokenizer st3 = new StringTokenizer(Decode(st2.nextToken()), " ") ;
+
+ ArrayList class_list = new ArrayList() ;
+ while (st3.hasMoreTokens()){
+ String c = st3.nextToken() ;
+ class_list.add(class_list.size(), c) ;
+ }
+
+ for (int i = 0 ; i < class_list.size() ; i++){
+ String name = (String)class_list.get(i) ;
+ Class c = ijc.ValidateClass(name) ;
+
+ InlineJavaUtils.debug(3, "reporting for " + c) ;
+
+ Class parent = c.getSuperclass() ;
+ String pname = (parent == null ? "null" : parent.getName()) ;
+ pw.append("class " + c.getName() + " " + pname + "\n") ;
+ Constructor constructors[] = c.getConstructors() ;
+ Method methods[] = c.getMethods() ;
+ Field fields[] = c.getFields() ;
+
+ boolean pub = ijc.ClassIsPublic(c) ;
+ if (pub){
+ // If the class is public and has no constructors,
+ // we provide a default no-arg constructors.
+ if (c.getDeclaredConstructors().length == 0){
+ String noarg_sign = InlineJavaUtils.CreateSignature(new Class [] {}) ;
+ pw.append("constructor " + noarg_sign + "\n") ;
+ }
+ }
+
+ boolean pn = InlineJavaPerlNatives.class.isAssignableFrom(c) ;
+ for (int j = 0 ; j < constructors.length ; j++){
+ Constructor x = constructors[j] ;
+ if ((pn)&&(Modifier.isNative(x.getModifiers()))){
+ continue ;
+ }
+ Class params[] = x.getParameterTypes() ;
+ String sign = InlineJavaUtils.CreateSignature(params) ;
+ Class decl = x.getDeclaringClass() ;
+ pw.append("constructor " + sign + "\n") ;
+ }
+
+ for (int j = 0 ; j < methods.length ; j++){
+ Method x = methods[j] ;
+ if ((pn)&&(Modifier.isNative(x.getModifiers()))){
+ continue ;
+ }
+ String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ;
+ String sign = InlineJavaUtils.CreateSignature(x.getParameterTypes()) ;
+ Class decl = x.getDeclaringClass() ;
+ pw.append("method" + stat + decl.getName() + " " + x.getName() + sign + "\n") ;
+ }
+
+ for (int j = 0 ; j < fields.length ; j++){
+ Field x = fields[(InlineJavaUtils.ReverseMembers() ? (fields.length - 1 - j) : j)] ;
+ String stat = (Modifier.isStatic(x.getModifiers()) ? " static " : " instance ") ;
+ Class decl = x.getDeclaringClass() ;
+ Class type = x.getType() ;
+ pw.append("field" + stat + decl.getName() + " " + x.getName() + " " + type.getName() + "\n") ;
+ }
+ }
+
+ SetResponse(pw.toString()) ;
+ }
+
+
+ void AddClassPath(StringTokenizer st) throws InlineJavaException {
+ while (st.hasMoreTokens()){
+ String path = Decode(st.nextToken()) ;
+ InlineJavaServer.GetInstance().GetUserClassLoader().AddClassPath(path) ;
+ }
+ SetResponse(null) ;
+ }
+
+
+ void ServerType(StringTokenizer st) throws InlineJavaException {
+ SetResponse(ijs.GetType()) ;
+ }
+
+
+ void IsA(StringTokenizer st) throws InlineJavaException {
+ String class_name = st.nextToken() ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ String is_it_a = st.nextToken() ;
+ Class d = ijc.ValidateClass(is_it_a) ;
+
+ SetResponse(new Integer(ijc.DoesExtend(c, d))) ;
+ }
+
+
+ void ObjectCount(StringTokenizer st) throws InlineJavaException {
+ SetResponse(new Integer(ijs.ObjectCount())) ;
+ }
+
+
+ /*
+ Creates a Java Object with the specified arguments.
+ */
+ void CreateJavaObject(StringTokenizer st) throws InlineJavaException {
+ String class_name = st.nextToken() ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ if (! ijc.ClassIsArray(c)){
+ ArrayList f = ValidateMethod(true, c, class_name, st) ;
+ Object p[] = (Object [])f.get(1) ;
+ Class clist[] = (Class [])f.get(2) ;
+
+ try {
+ Object o = CreateObject(c, p, clist) ;
+ SetResponse(o) ;
+ }
+ catch (InlineJavaInvocationTargetException ite){
+ Throwable t = ite.GetThrowable() ;
+ if (t instanceof InlineJavaException){
+ InlineJavaException ije = (InlineJavaException)t ;
+ throw ije ;
+ }
+ else{
+ SetResponse(new InlineJavaThrown(t)) ;
+ }
+ }
+ }
+ else{
+ // Here we send the type of array we want, but CreateArray
+ // exception the element type.
+ StringBuffer sb = new StringBuffer(class_name) ;
+ // Remove the ['s
+ while (sb.toString().startsWith("[")){
+ sb.replace(0, 1, "") ;
+ }
+ // remove the L and the ;
+ if (sb.toString().startsWith("L")){
+ sb.replace(0, 1, "") ;
+ sb.replace(sb.length() - 1, sb.length(), "") ;
+ }
+
+ Class ec = ijc.ValidateClass(sb.toString()) ;
+
+ InlineJavaUtils.debug(4, "array elements: " + ec.getName()) ;
+ Object o = ija.CreateArray(ec, st) ;
+ SetResponse(o) ;
+ }
+ }
+
+
+ /*
+ Calls a Java method
+ */
+ void CallJavaMethod(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String method = st.nextToken() ;
+
+ if ((ijc.ClassIsArray(c))&&(method.equals("getLength"))){
+ int length = Array.getLength(o) ;
+ SetResponse(new Integer(length)) ;
+ }
+ else{
+ ArrayList f = ValidateMethod(false, c, method, st) ;
+ Method m = (Method)f.get(0) ;
+ String name = m.getName() ;
+ Object p[] = (Object [])f.get(1) ;
+
+ try {
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().invoke(m, o, p) ;
+ SetResponse(ret, AutoCast(ret, m.getReturnType())) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to invoke method " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments for method " + name + " in class " + class_name + " are incompatible: " + e.getMessage()) ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ String type = t.getClass().getName() ;
+ String msg = t.getMessage() ;
+ InlineJavaUtils.debug(1, "method " + name + " in class " + class_name + " threw exception " + type + ": " + msg) ;
+ if (t instanceof InlineJavaException){
+ InlineJavaException ije = (InlineJavaException)t ;
+ throw ije ;
+ }
+ else{
+ SetResponse(new InlineJavaThrown(t)) ;
+ }
+ }
+ }
+ }
+
+
+ /*
+ */
+ Class AutoCast(Object o, Class want){
+ if (o == null){
+ return null ;
+ }
+ else {
+ Class got = o.getClass() ;
+ if (got.equals(want)){
+ return null ;
+ }
+ else {
+ boolean _public = (got.getModifiers() & Modifier.PUBLIC) != 0 ;
+ if ((_public)||(got.getPackage() == null)){
+ return null ;
+ }
+ else {
+ InlineJavaUtils.debug(3, "AutoCast: " + got.getName() + " -> " + want.getName()) ;
+ return want ;
+ }
+ }
+ }
+ }
+
+
+ /*
+ Returns a new reference to the current object, using the provided subtype
+ */
+ void Cast(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = ijs.GetObject(id) ;
+ Class c = ijc.ValidateClass(class_name) ;
+
+ SetResponse(o, c) ;
+ }
+
+
+ /*
+ */
+ void Read(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+ int len = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.read(o, len) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void MakeBuffered(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.makeBuffered(o) ;
+ if (ret != o){
+ int buf_id = ijs.PutObject(ret) ;
+ ret = new Integer(buf_id) ;
+ }
+ else {
+ ret = new Integer(id) ;
+ }
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void ReadLine(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaHandle.readLine(o) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void Write(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+ Object arg = ijc.CastArgument(Object.class, st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ int len = InlineJavaHandle.write(o, arg.toString()) ;
+ ret = new Integer(len) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ void Close(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.GetObject(id) ;
+ Object ret = null ;
+ try {
+ InlineJavaHandle.close(o) ;
+ }
+ catch (java.io.IOException e){
+ ret = new InlineJavaThrown(e) ;
+ }
+
+ SetResponse(ret) ;
+ }
+
+
+ /*
+ Sets a Java member variable
+ */
+ void SetJavaMember(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String member = st.nextToken() ;
+
+ if (ijc.ClassIsArray(c)){
+ int idx = Integer.parseInt(member) ;
+ Class type = ijc.ValidateClass(st.nextToken()) ;
+ String arg = st.nextToken() ;
+
+ String msg = "For array of type " + c.getName() + ", element " + member + ": " ;
+ try {
+ Object elem = ijc.CastArgument(type, arg) ;
+ InlineJavaServer.GetInstance().GetUserClassLoader().array_set(o, idx, elem) ;
+ SetResponse(null) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+ else{
+ ArrayList fl = ValidateMember(c, member, st) ;
+ Field f = (Field)fl.get(0) ;
+ String name = f.getName() ;
+ Object p = (Object)fl.get(1) ;
+
+ try {
+ InlineJavaServer.GetInstance().GetUserClassLoader().set(f, o, p) ;
+ SetResponse(null) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ;
+ }
+ }
+ }
+
+
+ /*
+ Gets a Java member variable
+ */
+ void GetJavaMember(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ String class_name = st.nextToken() ;
+ Object o = null ;
+ if (id > 0){
+ o = ijs.GetObject(id) ;
+
+ // Use the class sent by Perl (it might be casted)
+ // class_name = o.getClass().getName() ;
+ }
+
+ Class c = ijc.ValidateClass(class_name) ;
+ String member = st.nextToken() ;
+
+ if (ijc.ClassIsArray(c)){
+ int idx = Integer.parseInt(member) ;
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().array_get(o, idx) ;
+ Class eclass = ijc.ValidateClass(ijc.CleanClassName(class_name.substring(1))) ;
+ SetResponse(ret, AutoCast(ret, eclass)) ;
+ }
+ else{
+ ArrayList fl = ValidateMember(c, member, st) ;
+
+ Field f = (Field)fl.get(0) ;
+ String name = f.getName() ;
+ try {
+ Object ret = InlineJavaServer.GetInstance().GetUserClassLoader().get(f, o) ;
+ SetResponse(ret, AutoCast(ret, f.getType())) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to set member " + name + " in class " + class_name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Argument for member " + name + " in class " + class_name + " is incompatible: " + e.getMessage()) ;
+ }
+ }
+ }
+
+
+ /*
+ Deletes a Java object
+ */
+ void DeleteJavaObject(StringTokenizer st) throws InlineJavaException {
+ int id = Integer.parseInt(st.nextToken()) ;
+
+ Object o = ijs.DeleteObject(id) ;
+
+ SetResponse(null) ;
+ }
+
+
+ /*
+ Creates a Java Object with the specified arguments.
+ */
+ Object CreateObject(Class p, Object args[], Class proto[]) throws InlineJavaException {
+ p = ijc.FindWrapper(p) ;
+
+ String name = p.getName() ;
+ Object ret = null ;
+ try {
+ ret = InlineJavaServer.GetInstance().GetUserClassLoader().create(p, args, proto) ;
+ }
+ catch (NoSuchMethodException e){
+ throw new InlineJavaException("Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " not found: " + e.getMessage()) ;
+ }
+ catch (InstantiationException e){
+ throw new InlineJavaException("You are not allowed to instantiate object of class " + name + ": " + e.getMessage()) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("You are not allowed to instantiate object of class " + name + " using the constructor with signature " + InlineJavaUtils.CreateSignature(proto) + ": " + e.getMessage()) ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Arguments to constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " are incompatible: " + e.getMessage()) ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ String type = t.getClass().getName() ;
+ String msg = t.getMessage() ;
+ throw new InlineJavaInvocationTargetException(
+ "Constructor for class " + name + " with signature " + InlineJavaUtils.CreateSignature(proto) + " threw exception " + type + ": " + msg,
+ t) ;
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Makes sure a method exists
+ */
+ ArrayList ValidateMethod(boolean constructor, Class c, String name, StringTokenizer st) throws InlineJavaException {
+ ArrayList ret = new ArrayList() ;
+
+ // Extract signature
+ String signature = st.nextToken() ;
+
+ // Extract the arguments
+ ArrayList args = new ArrayList() ;
+ while (st.hasMoreTokens()){
+ args.add(args.size(), st.nextToken()) ;
+ }
+
+ String key = c.getName() + "." + name + signature ;
+ ArrayList ml = new ArrayList() ;
+ Class params[] = null ;
+
+ Member cached = (Member)member_cache.get(key) ;
+ if (cached != null){
+ InlineJavaUtils.debug(3, "method was cached") ;
+ ml.add(ml.size(), cached) ;
+ }
+ else{
+ Member ma[] = (constructor ? (Member [])c.getConstructors() : (Member [])c.getMethods()) ;
+ for (int i = 0 ; i < ma.length ; i++){
+ Member m = ma[i] ;
+
+ if (m.getName().equals(name)){
+ InlineJavaUtils.debug(3, "found a " + name + (constructor ? " constructor" : " method")) ;
+
+ if (constructor){
+ params = ((Constructor)m).getParameterTypes() ;
+ }
+ else{
+ params = ((Method)m).getParameterTypes() ;
+ }
+
+ // Now we check if the signatures match
+ String sign = InlineJavaUtils.CreateSignature(params, ",") ;
+ InlineJavaUtils.debug(3, sign + " = " + signature + "?") ;
+
+ if (signature.equals(sign)){
+ InlineJavaUtils.debug(3, "has matching signature " + sign) ;
+ ml.add(ml.size(), m) ;
+ member_cache.put(key, m) ;
+ break ;
+ }
+ }
+ }
+ }
+
+ // Now we got a list of matching methods (actually 0 or 1).
+ // We have to figure out which one we will call.
+ if (ml.size() == 0){
+ // Nothing matched. Maybe we got a default constructor
+ if ((constructor)&&(signature.equals("()"))){
+ ret.add(0, null) ;
+ ret.add(1, new Object [] {}) ;
+ ret.add(2, new Class [] {}) ;
+ }
+ else{
+ throw new InlineJavaException(
+ (constructor ? "Constructor " : "Method ") +
+ name + " for class " + c.getName() + " with signature " +
+ signature + " not found") ;
+ }
+ }
+ else if (ml.size() == 1){
+ // Now we need to force the arguments received to match
+ // the methods signature.
+ Member m = (Member)ml.get(0) ;
+ if (constructor){
+ params = ((Constructor)m).getParameterTypes() ;
+ }
+ else{
+ params = ((Method)m).getParameterTypes() ;
+ }
+
+ String msg = "In method " + name + " of class " + c.getName() + ": " ;
+ try {
+ ret.add(0, m) ;
+ ret.add(1, ijc.CastArguments(params, args)) ;
+ ret.add(2, params) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ Makes sure a member exists
+ */
+ ArrayList ValidateMember(Class c, String name, StringTokenizer st) throws InlineJavaException {
+ ArrayList ret = new ArrayList() ;
+
+ // Extract member type
+ String type = st.nextToken() ;
+
+ // Extract the argument
+ String arg = st.nextToken() ;
+
+ String key = type + " " + c.getName() + "." + name ;
+ ArrayList fl = new ArrayList() ;
+ Class param = null ;
+
+ Member cached = (Member)member_cache.get(key) ;
+ if (cached != null){
+ InlineJavaUtils.debug(3, "member was cached") ;
+ fl.add(fl.size(), cached) ;
+ }
+ else {
+ Field fa[] = c.getFields() ;
+ for (int i = 0 ; i < fa.length ; i++){
+ Field f = fa[(InlineJavaUtils.ReverseMembers() ? (fa.length - 1 - i) : i)] ;
+
+ if (f.getName().equals(name)){
+ InlineJavaUtils.debug(3, "found a " + name + " member") ;
+
+ param = f.getType() ;
+ String t = param.getName() ;
+ if (type.equals(t)){
+ InlineJavaUtils.debug(3, "has matching type " + t) ;
+ fl.add(fl.size(), f) ;
+ }
+ }
+ }
+ }
+
+ // Now we got a list of matching members.
+ // We have to figure out which one we will call.
+ if (fl.size() == 0){
+ throw new InlineJavaException(
+ "Member " + name + " of type " + type + " for class " + c.getName() +
+ " not found") ;
+ }
+ else {
+ // Now we need to force the arguments received to match
+ // the methods signature.
+
+ // If we have more that one, we use the last one, which is the most
+ // specialized
+ Field f = (Field)fl.get(fl.size() - 1) ;
+ member_cache.put(key, f) ;
+ param = f.getType() ;
+
+ String msg = "For member " + name + " of class " + c.getName() + ": " ;
+ try {
+ ret.add(0, f) ;
+ ret.add(1, ijc.CastArgument(param, arg)) ;
+ ret.add(2, param) ;
+ }
+ catch (InlineJavaCastException e){
+ throw new InlineJavaCastException(msg + e.getMessage()) ;
+ }
+ catch (InlineJavaException e){
+ throw new InlineJavaException(msg + e.getMessage()) ;
+ }
+ }
+
+ return ret ;
+ }
+
+
+ /*
+ This sets the response that will be returned to the Perl
+ script
+ */
+ void SetResponse(Object o) throws InlineJavaException {
+ SetResponse(o, null) ;
+ }
+
+
+ void SetResponse(Object o, Class p) throws InlineJavaException {
+ response = "ok " + SerializeObject(o, p) ;
+ }
+
+
+ String SerializeObject(Object o, Class p) throws InlineJavaException {
+ Class c = (o == null ? null : o.getClass()) ;
+
+ if ((c != null)&&(p != null)){
+ if (ijc.DoesExtend(c, p) < 0){
+ throw new InlineJavaException("Can't cast a " + c.getName() + " to a " + p.getName()) ;
+ }
+ else{
+ c = p ;
+ }
+ }
+
+ if (o == null){
+ return "undef:" ;
+ }
+ else if ((ijc.ClassIsNumeric(c))||(ijc.ClassIsChar(c))||(ijc.ClassIsString(c))){
+ if ((ijs.GetNativeDoubles())&&(ijc.ClassIsDouble(c))){
+ Double d = (Double)o ;
+ long l = Double.doubleToLongBits(d.doubleValue()) ;
+ char ca[] = new char[8] ;
+ for (int i = 0 ; i < 8 ; i++){
+ ca[i] = (char)((l >> (8 * i)) & 0xFF) ;
+ }
+ return "double:" + Encode(new String(ca)) ;
+ }
+ else {
+ return "scalar:" + Encode(o.toString()) ;
+ }
+ }
+ else if (ijc.ClassIsBool(c)){
+ String b = o.toString() ;
+ return "scalar:" + Encode((b.equals("true") ? "1" : "0")) ;
+ }
+ else {
+ if (! (o instanceof org.perl.inline.java.InlineJavaPerlObject)){
+ // Here we need to register the object in order to send
+ // it back to the Perl script.
+ boolean thrown = false ;
+ String type = "object" ;
+ if (o instanceof InlineJavaThrown){
+ thrown = true ;
+ o = ((InlineJavaThrown)o).GetThrowable() ;
+ c = o.getClass() ;
+ }
+ else if (ijc.ClassIsArray(c)){
+ type = "array" ;
+ }
+ else if (ijc.ClassIsHandle(c)){
+ type = "handle" ;
+ }
+ int id = ijs.PutObject(o) ;
+
+ return "java_" + type + ":" + (thrown ? "1" : "0") + ":" + String.valueOf(id) +
+ ":" + c.getName() ;
+ }
+ else {
+ return "perl_object:" + ((InlineJavaPerlObject)o).GetId() +
+ ":" + ((InlineJavaPerlObject)o).GetPkg() ;
+ }
+ }
+ }
+
+
+ byte[] DecodeToByteArray(String s){
+ return InlineJavaUtils.DecodeBase64(s.toCharArray()) ;
+ }
+
+
+ String Decode(String s) throws InlineJavaException {
+ try {
+ if (encoding != null){
+ return new String(DecodeToByteArray(s), encoding) ;
+ }
+ else {
+ return new String(DecodeToByteArray(s)) ;
+ }
+ }
+ catch (UnsupportedEncodingException e){
+ throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ;
+ }
+ }
+
+
+ String EncodeFromByteArray(byte bytes[]){
+ return new String(InlineJavaUtils.EncodeBase64(bytes)) ;
+ }
+
+
+ String Encode(String s) throws InlineJavaException {
+ try {
+ if (encoding != null){
+ return EncodeFromByteArray(s.getBytes(encoding)) ;
+ }
+ else {
+ return EncodeFromByteArray(s.getBytes()) ;
+ }
+ }
+ catch (UnsupportedEncodingException e){
+ throw new InlineJavaException("Unsupported encoding: " + e.getMessage()) ;
+ }
+ }
+
+
+ String GetResponse(){
+ return response ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaServer.java b/Java/sources/org/perl/inline/java/InlineJavaServer.java
new file mode 100644
index 0000000..1c62ef9
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaServer.java
@@ -0,0 +1,337 @@
+package org.perl.inline.java ;
+
+import java.net.* ;
+import java.io.* ;
+import java.util.* ;
+
+
+/*
+ This is the server that will answer all the requests for and on Java
+ objects.
+*/
+public class InlineJavaServer {
+ private static InlineJavaServer instance = null ;
+ private String host = null ;
+ private int port = 0 ;
+ private boolean shared_jvm = false ;
+ private boolean priv = false ;
+ private boolean native_doubles = false ;
+
+ private boolean finished = false ;
+ private ServerSocket server_socket = null ;
+ private InlineJavaUserClassLoader ijucl = null ;
+ private HashMap thread_objects = new HashMap() ;
+ private int objid = 1 ;
+ private boolean jni = false ;
+ private Thread creator = null ;
+ private int thread_count = 0 ;
+
+
+ // This constructor is used in JNI mode
+ private InlineJavaServer(int debug, boolean _native_doubles){
+ init(debug, _native_doubles) ;
+
+ jni = true ;
+ AddThread(creator) ;
+ }
+
+
+ // This constructor is used in server mode
+ // Normally one would then call RunMainLoop()
+ /* Note: Consider http://groups.google.com/group/perl.inline/tree/browse_frm/thread/aa7f5ce236f6d576/3db48a308a8175fb?rnum=1&hl=en&q=Congratulations+with+Inline%3A%3AJava+0.51&_done=%2Fgroup%2Fperl.inline%2Fbrowse_frm%2Fthread%2Faa7f5ce236f6d576%2Fd2de9cf38429c09c%3Flnk%3Dst%26q%3DCongratulations+with+Inline%3A%3AJava+0.51%26rnum%3D1%26hl%3Den%26#doc_3db48a308a8175fb before changing this prototype */
+ public InlineJavaServer(int debug, String _host, int _port, boolean _shared_jvm, boolean _priv, boolean _native_doubles){
+ init(debug, _native_doubles) ;
+
+ jni = false ;
+ host = _host ;
+ port = _port ;
+ shared_jvm = _shared_jvm ;
+ priv = _priv ;
+
+ try {
+ if ((host == null)||(host.equals(""))||(host.equals("ANY"))){
+ server_socket = new ServerSocket(port) ;
+ }
+ else {
+ server_socket = new ServerSocket(port, 0, InetAddress.getByName(host)) ;
+ }
+ }
+ catch (IOException e){
+ InlineJavaUtils.Fatal("Can't open server socket on port " + String.valueOf(port) +
+ ": " + e.getMessage()) ;
+ }
+ }
+
+
+ public void RunMainLoop(){
+ while (! finished){
+ try {
+ String name = "IJST-#" + thread_count++ ;
+ InlineJavaServerThread ijt = new InlineJavaServerThread(name, this, server_socket.accept(),
+ (priv ? new InlineJavaUserClassLoader() : ijucl)) ;
+ ijt.start() ;
+ if (! shared_jvm){
+ try {
+ ijt.join() ;
+ }
+ catch (InterruptedException e){
+ }
+ break ;
+ }
+ }
+ catch (IOException e){
+ if (! finished){
+ System.err.println("Main Loop IO Error: " + e.getMessage()) ;
+ System.err.flush() ;
+ }
+ }
+ }
+ }
+
+
+ private synchronized void init(int debug, boolean _native_doubles){
+ instance = this ;
+ creator = Thread.currentThread() ;
+ InlineJavaUtils.set_debug(debug) ;
+ native_doubles = _native_doubles ;
+
+ ijucl = new InlineJavaUserClassLoader() ;
+ }
+
+
+ static InlineJavaServer GetInstance(){
+ if (instance == null){
+ InlineJavaUtils.Fatal("No instance of InlineJavaServer has been created!") ;
+ }
+
+ return instance ;
+ }
+
+
+ InlineJavaUserClassLoader GetUserClassLoader(){
+ Thread t = Thread.currentThread() ;
+ if (t instanceof InlineJavaServerThread){
+ return ((InlineJavaServerThread)t).GetUserClassLoader() ;
+ }
+ else {
+ return ijucl ;
+ }
+ }
+
+
+ String GetType(){
+ return (shared_jvm ? "shared" : "private") ;
+ }
+
+
+ boolean GetNativeDoubles(){
+ return native_doubles ;
+ }
+
+
+ boolean IsJNI(){
+ return jni ;
+ }
+
+
+ /*
+ Since this function is also called from the JNI XS extension,
+ it's best if it doesn't throw any exceptions.
+ */
+ String ProcessCommand(String cmd) {
+ return ProcessCommand(cmd, true) ;
+ }
+
+
+ String ProcessCommand(String cmd, boolean addlf) {
+ InlineJavaUtils.debug(3, "packet recv is " + cmd) ;
+
+ String resp = null ;
+ if (cmd != null){
+ InlineJavaProtocol ijp = new InlineJavaProtocol(this, cmd) ;
+ try {
+ ijp.Do() ;
+ InlineJavaUtils.debug(3, "packet sent is " + ijp.GetResponse()) ;
+ resp = ijp.GetResponse() ;
+ }
+ catch (InlineJavaException e){
+ // Encode the error in default encoding since we don't want any
+ // Exceptions thrown here...
+ String err = "error scalar:" + ijp.EncodeFromByteArray(e.getMessage().getBytes()) ;
+ InlineJavaUtils.debug(3, "packet sent is " + err) ;
+ resp = err ;
+ }
+ }
+ else{
+ if (! shared_jvm){
+ // Probably connection dropped...
+ InlineJavaUtils.debug(1, "lost connection with client in single client mode. Exiting.") ;
+ System.exit(1) ;
+ }
+ else{
+ InlineJavaUtils.debug(1, "lost connection with client in shared JVM mode.") ;
+ return null ;
+ }
+ }
+
+ if (addlf){
+ resp = resp + "\n" ;
+ }
+
+ return resp ;
+ }
+
+
+ /*
+ This method really has no business here, but for historical reasons
+ it will remain here.
+ */
+ native String jni_callback(String cmd) ;
+
+
+ boolean IsThreadPerlContact(Thread t){
+ if (((jni)&&(t == creator))||
+ ((! jni)&&(t instanceof InlineJavaServerThread))){
+ return true ;
+ }
+
+ return false ;
+ }
+
+
+ synchronized Object GetObject(int id) throws InlineJavaException {
+ Object o = null ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ o = h.get(new Integer(id)) ;
+ if (o == null){
+ throw new InlineJavaException("Can't find object " + id + " for thread " +Thread.currentThread().getName()) ;
+ }
+ }
+
+ return o ;
+ }
+
+
+ synchronized int PutObject(Object o) throws InlineJavaException {
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ int id = objid ;
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ h.put(new Integer(objid), o) ;
+ objid++ ;
+ }
+
+ return id ;
+ }
+
+
+ synchronized Object DeleteObject(int id) throws InlineJavaException {
+ Object o = null ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ o = h.remove(new Integer(id)) ;
+ if (o == null){
+ throw new InlineJavaException("Can't find object " + id + " for thread " + Thread.currentThread().getName()) ;
+ }
+ }
+
+ return o ;
+ }
+
+
+ synchronized int ObjectCount() throws InlineJavaException {
+ int i = -1 ;
+ HashMap h = (HashMap)thread_objects.get(Thread.currentThread()) ;
+
+ if (h == null){
+ throw new InlineJavaException("Can't find thread " + Thread.currentThread().getName() + "!") ;
+ }
+ else{
+ i = h.values().size() ;
+ }
+
+ return i ;
+ }
+
+
+ public synchronized void StopMainLoop(){
+ if (! jni){
+ try {
+ finished = true ;
+ server_socket.close() ;
+ }
+ catch (IOException e){
+ System.err.println("Shutdown IO Error: " + e.getMessage()) ;
+ System.err.flush() ;
+ }
+ }
+ }
+
+
+ synchronized void Shutdown(){
+ StopMainLoop() ;
+ System.exit(0) ;
+ }
+
+
+ /*
+ Here the prototype accepts Threads because the JNI thread
+ calls this method also.
+ */
+ synchronized void AddThread(Thread t){
+ thread_objects.put(t, new HashMap()) ;
+ InlineJavaPerlCaller.AddThread(t) ;
+ }
+
+
+ synchronized void RemoveThread(InlineJavaServerThread t){
+ thread_objects.remove(t) ;
+ InlineJavaPerlCaller.RemoveThread(t) ;
+ }
+
+
+
+ /*
+ Startup
+ */
+ public static void main(String[] argv){
+ int debug = Integer.parseInt(argv[0]) ;
+ String host = argv[1] ;
+ int port = Integer.parseInt(argv[2]) ;
+ boolean shared_jvm = new Boolean(argv[3]).booleanValue() ;
+ boolean priv = new Boolean(argv[4]).booleanValue() ;
+ boolean native_doubles = new Boolean(argv[5]).booleanValue() ;
+
+ InlineJavaServer ijs = new InlineJavaServer(debug, host, port, shared_jvm, priv, native_doubles) ;
+ ijs.RunMainLoop() ;
+ System.exit(0) ;
+ }
+
+
+ /*
+ With PerlInterpreter this is called twice, but we don't want to create
+ a new object the second time.
+ */
+ public static InlineJavaServer jni_main(int debug, boolean native_doubles){
+ if (instance != null){
+ InlineJavaUtils.set_debug(debug) ;
+ InlineJavaUtils.debug(1, "recycling InlineJavaServer created by PerlInterpreter") ;
+ return instance ;
+ }
+ else {
+ return new InlineJavaServer(debug, native_doubles) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaServerThread.java b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java
new file mode 100644
index 0000000..e39e4e9
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaServerThread.java
@@ -0,0 +1,69 @@
+package org.perl.inline.java ;
+
+import java.io.* ;
+import java.net.* ;
+import java.util.* ;
+
+
+class InlineJavaServerThread extends Thread {
+ private InlineJavaServer ijs ;
+ private Socket client ;
+ private BufferedReader br ;
+ private BufferedWriter bw ;
+ private InlineJavaUserClassLoader ijucl ;
+
+
+ InlineJavaServerThread(String name, InlineJavaServer _ijs, Socket _client, InlineJavaUserClassLoader _ijucl) throws IOException {
+ super(name) ;
+ client = _client ;
+ ijs = _ijs ;
+ ijucl = _ijucl ;
+
+ InputStreamReader ir = new InputStreamReader(client.getInputStream()) ;
+ OutputStreamWriter or = new OutputStreamWriter(client.getOutputStream()) ;
+ br = new BufferedReader(ir) ;
+ bw = new BufferedWriter(or) ;
+ }
+
+
+ BufferedReader GetReader(){
+ return br ;
+ }
+
+
+ BufferedWriter GetWriter(){
+ return bw ;
+ }
+
+
+ InlineJavaUserClassLoader GetUserClassLoader(){
+ return ijucl ;
+ }
+
+
+ public void run(){
+ try {
+ ijs.AddThread(this) ;
+
+ while (true){
+ String cmd = br.readLine() ;
+
+ String resp = ijs.ProcessCommand(cmd) ;
+ if (resp != null){
+ bw.write(resp) ;
+ bw.flush() ;
+ }
+ else {
+ client.close() ;
+ break ;
+ }
+ }
+ }
+ catch (IOException e){
+ System.err.println("IO error: " + e.getMessage()) ;
+ }
+ finally {
+ ijs.RemoveThread(this) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaThrown.java b/Java/sources/org/perl/inline/java/InlineJavaThrown.java
new file mode 100644
index 0000000..38bee49
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaThrown.java
@@ -0,0 +1,14 @@
+package org.perl.inline.java ;
+
+
+class InlineJavaThrown {
+ Throwable t ;
+
+ InlineJavaThrown(Throwable _t){
+ t = _t ;
+ }
+
+ Throwable GetThrowable(){
+ return t ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java
new file mode 100644
index 0000000..0f2d97a
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLink.java
@@ -0,0 +1,45 @@
+import java.util.* ;
+import java.lang.reflect.* ;
+
+
+public class InlineJavaUserClassLink {
+ public InlineJavaUserClassLink(){
+ }
+
+
+ public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ return m.invoke(o, p) ;
+ }
+
+
+ public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException {
+ return f.get(o) ;
+ }
+
+
+ public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException {
+ f.set(o, p) ;
+ }
+
+
+ public Object array_get(Object o, Integer idx){
+ return Array.get(o, idx.intValue()) ;
+ }
+
+
+ public void array_set(Object o, Integer idx, Object elem) throws IllegalArgumentException {
+ Array.set(o, idx.intValue(), elem) ;
+ }
+
+
+ public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ // This will allow usage of the default no-arg constructor
+ if (proto.length == 0){
+ return p.newInstance() ;
+ }
+ else{
+ Constructor con = (Constructor)p.getConstructor(proto) ;
+ return con.newInstance(args) ;
+ }
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java
new file mode 100644
index 0000000..0105872
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUserClassLoader.java
@@ -0,0 +1,192 @@
+package org.perl.inline.java ;
+
+import java.net.* ;
+import java.util.* ;
+import java.io.* ;
+import java.lang.reflect.* ;
+
+
+/*
+ This is the ClassLoader that loads the users code. It is also
+ used to pass reflection calls to the InlineJavaUserClassLink
+ so that it will execute them.
+*/
+class InlineJavaUserClassLoader extends URLClassLoader {
+ private HashMap urls = new HashMap() ;
+
+ private Object link = null ;
+ private Method invoke = null ;
+ private Method get = null ;
+ private Method set = null ;
+ private Method array_get = null ;
+ private Method array_set = null ;
+ private Method create = null ;
+
+
+ public InlineJavaUserClassLoader(){
+ // Added Thread.currentThread().getContextClassLoader() so that the code works
+ // in Tomcat and possibly other embedded environments asa well.
+ super(new URL [] {}, Thread.currentThread().getContextClassLoader()) ;
+ }
+
+
+ public void AddClassPath(String path) throws InlineJavaException {
+ try {
+ File p = new File(path) ;
+ URL u = p.toURI().toURL() ;
+ if (urls.get(u) == null){
+ urls.put(u, "1") ;
+ addURL(u) ;
+ InlineJavaUtils.debug(2, "added " + u + " to classpath") ;
+ }
+ }
+ catch (MalformedURLException e){
+ throw new InlineJavaException("Can't add invalid classpath entry '" + path + "'") ;
+ }
+ }
+
+
+ synchronized private void check_link() throws InlineJavaException {
+ if (link == null){
+ try {
+ InlineJavaUtils.debug(1, "loading InlineJavaUserClassLink via InlineJavaUserClassLoader") ;
+ Class c = Class.forName("InlineJavaUserClassLink", true, this) ;
+ link = c.newInstance() ;
+
+ invoke = find_method(c, "invoke") ;
+ get = find_method(c, "get") ;
+ set = find_method(c, "set") ;
+ array_get = find_method(c, "array_get") ;
+ array_set = find_method(c, "array_set") ;
+ create = find_method(c, "create") ;
+ }
+ catch (Exception e){
+ throw new InlineJavaException("InlineJavaUserClassLoader can't load InlineJavaUserClassLink: invalid classpath setup (" +
+ e.getClass().getName() + ": " + e.getMessage() + ")") ;
+ }
+ }
+ }
+
+
+ private Method find_method(Class c, String name) throws InlineJavaException {
+ Method ml[] = c.getMethods() ;
+ for (int i = 0 ; i < ml.length ; i++){
+ if (ml[i].getName().equals(name)){
+ return ml[i] ;
+ }
+ }
+
+ throw new InlineJavaException("Can't find method '" + name +
+ "' in class InlineJavaUserClassLink") ;
+ }
+
+
+ private Object invoke_via_link(Method m, Object p[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ try {
+ return m.invoke(link, p) ;
+ }
+ catch (IllegalAccessException e){
+ throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalAccessException") ;
+ }
+ catch (IllegalArgumentException e){
+ throw new InlineJavaException("Can't invoke method from class InlineJavaUserClassLink: IllegalArgumentException") ;
+ }
+ catch (InvocationTargetException e){
+ Throwable t = e.getTargetException() ;
+ if (t instanceof NoSuchMethodException){
+ throw (NoSuchMethodException)t ;
+ }
+ else if (t instanceof InstantiationException){
+ throw (InstantiationException)t ;
+ }
+ else if (t instanceof IllegalAccessException){
+ throw (IllegalAccessException)t ;
+ }
+ if (t instanceof IllegalAccessException){
+ throw (IllegalAccessException)t ;
+ }
+ else if (t instanceof IllegalArgumentException){
+ throw (IllegalArgumentException)t ;
+ }
+ else if (t instanceof InvocationTargetException){
+ throw (InvocationTargetException)t ;
+ }
+ // Not sure if this is really necessary, but...
+ else if (t instanceof RuntimeException){
+ RuntimeException re = (RuntimeException)t ;
+ throw re ;
+ }
+ else{
+ // In theory this case is impossible.
+ throw new InlineJavaException("Unexpected exception of type '" +
+ t.getClass().getName() + "': " + t.getMessage()) ;
+ }
+ }
+ }
+
+
+ public Object invoke(Method m, Object o, Object p[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(invoke, new Object [] {m, o, p}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ return null ;
+ }
+
+
+ public Object get(Field f, Object o) throws IllegalAccessException, IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(get, new Object [] {f, o}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ return null ;
+ }
+
+
+ public void set(Field f, Object o, Object p) throws IllegalAccessException, IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ invoke_via_link(set, new Object [] {f, o, p}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ }
+
+
+ public Object array_get(Object o, int idx) throws InlineJavaException {
+ check_link() ;
+ try {
+ return invoke_via_link(array_get, new Object [] {o, new Integer(idx)}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (IllegalAccessException iae){/* Impossible */}
+ catch (IllegalArgumentException iae){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ return null ;
+ }
+
+
+ public void array_set(Object o, int idx, Object elem) throws IllegalArgumentException, InlineJavaException {
+ check_link() ;
+ try {
+ invoke_via_link(array_set, new Object [] {o, new Integer(idx), elem}) ;
+ }
+ catch (NoSuchMethodException me){/* Impossible */}
+ catch (InstantiationException ie){/* Impossible */}
+ catch (IllegalAccessException iae){/* Impossible */}
+ catch (InvocationTargetException e){/* Impossible */}
+ }
+
+
+ public Object create(Class p, Object args[], Class proto[]) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InlineJavaException {
+ check_link() ;
+ return invoke_via_link(create, new Object [] {p, args, proto}) ;
+ }
+}
diff --git a/Java/sources/org/perl/inline/java/InlineJavaUtils.java b/Java/sources/org/perl/inline/java/InlineJavaUtils.java
new file mode 100644
index 0000000..65426dd
--- /dev/null
+++ b/Java/sources/org/perl/inline/java/InlineJavaUtils.java
@@ -0,0 +1,175 @@
+package org.perl.inline.java ;
+
+import java.util.* ;
+
+
+/*
+ Creates a string representing a method signature
+*/
+class InlineJavaUtils {
+ private static int debug = 0 ;
+
+
+ public synchronized static void set_debug(int d){
+ debug = d ;
+ }
+
+
+ public static int get_debug(){
+ return debug ;
+ }
+
+
+ static String CreateSignature(Class param[]){
+ return CreateSignature(param, ", ") ;
+ }
+
+
+ static String CreateSignature(Class param[], String del){
+ StringBuffer ret = new StringBuffer() ;
+ for (int i = 0 ; i < param.length ; i++){
+ if (i > 0){
+ ret.append(del) ;
+ }
+ ret.append(param[i].getName()) ;
+ }
+
+ return "(" + ret.toString() + ")" ;
+ }
+
+
+ synchronized static void debug(int level, String s) {
+ if ((debug > 0)&&(debug >= level)){
+ StringBuffer sb = new StringBuffer() ;
+ for (int i = 0 ; i < level ; i++){
+ sb.append(" ") ;
+ }
+ System.err.println("[java][" + level + "]" + sb.toString() + s) ;
+ System.err.flush() ;
+ }
+ }
+
+
+ static void Fatal(String msg){
+ System.err.println(msg) ;
+ System.err.flush() ;
+ System.exit(1) ;
+ }
+
+
+ static boolean ReverseMembers() {
+ String v = System.getProperty("java.version") ;
+ boolean no_rev = ((v.startsWith("1.2"))||(v.startsWith("1.3"))) ;
+
+ return (! no_rev) ;
+ }
+
+
+
+ /*
+ Base64 stuff. This section conatins code by Christian d'Heureuse that is
+ licended under the LGPL. Used by permission:
+
+ From: Christian d'Heureuse <chdh@inventec.ch>
+ To: Patrick LeBoutillier <patrick.leboutillier@gmail.com>
+ Date: Aug 11, 2005 4:45 AM
+ Subject: Re: Base64Coder
+
+ > I was wondering if you can grant me permission to include your
+ > code in my project.
+
+ Yes, I grant you permission to include the Base64Coder class in your
+ project.
+
+ *
+ * A Base64 Encoder/Decoder.
+ *
+ * This class is used to encode and decode data in Base64 format
+ * as described in RFC 1521.
+ *
+ * <p>
+ * Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland.<br>
+ * License: This is "Open Source" software and released under the <a href="http://www.gnu.org/licenses/lgpl.html" target="_top">GNU/LGPL</a> license.
+ * It is provided "as is" without warranty of any kind. Please contact the author for other licensing arrangements.<br>
+ * Home page: <a href="http://www.source-code.biz" target="_top">www.source-code.biz</a><br>
+ *
+ * <p>
+ * Version history:<br>
+ * 2003-07-22 Christian d'Heureuse (chdh): Module created.<br>
+ * 2005-08-11 chdh: Lincense changed from GPL to LGPL.
+ *
+ */
+
+ // Mapping table from 6-bit nibbles to Base64 characters.
+ private static char[] map1 = new char[64];
+ static {
+ int i=0;
+ for (char c='A'; c<='Z'; c++) map1[i++] = c;
+ for (char c='a'; c<='z'; c++) map1[i++] = c;
+ for (char c='0'; c<='9'; c++) map1[i++] = c;
+ map1[i++] = '+'; map1[i++] = '/';
+ }
+
+ // Mapping table from Base64 characters to 6-bit nibbles.
+ private static byte[] map2 = new byte[128];
+ static {
+ for (int i=0; i<map2.length; i++) map2[i] = -1;
+ for (int i=0; i<64; i++) map2[map1[i]] = (byte)i;
+ }
+
+
+ public static char[] EncodeBase64(byte[] in){
+ int iLen = in.length;
+ int oDataLen = (iLen*4+2)/3; // output length without padding
+ int oLen = ((iLen+2)/3)*4; // output length including padding
+ char[] out = new char[oLen];
+ int ip = 0;
+ int op = 0;
+ while (ip < iLen) {
+ int i0 = in[ip++] & 0xff;
+ int i1 = ip < iLen ? in[ip++] & 0xff : 0;
+ int i2 = ip < iLen ? in[ip++] & 0xff : 0;
+ int o0 = i0 >>> 2;
+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
+ int o3 = i2 & 0x3F;
+ out[op++] = map1[o0];
+ out[op++] = map1[o1];
+ out[op] = op < oDataLen ? map1[o2] : '='; op++;
+ out[op] = op < oDataLen ? map1[o3] : '='; op++;
+ }
+ return out;
+ }
+
+
+ public static byte[] DecodeBase64(char[] in){
+ int iLen = in.length;
+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
+ while (iLen > 0 && in[iLen-1] == '=') iLen--;
+ int oLen = (iLen*3) / 4;
+ byte[] out = new byte[oLen];
+ int ip = 0;
+ int op = 0;
+ while (ip < iLen) {
+ int i0 = in[ip++];
+ int i1 = in[ip++];
+ int i2 = ip < iLen ? in[ip++] : 'A';
+ int i3 = ip < iLen ? in[ip++] : 'A';
+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int b0 = map2[i0];
+ int b1 = map2[i1];
+ int b2 = map2[i2];
+ int b3 = map2[i3];
+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int o0 = ( b0 <<2) | (b1>>>4);
+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
+ int o2 = ((b2 & 3)<<6) | b3;
+ out[op++] = (byte)o0;
+ if (op<oLen) out[op++] = (byte)o1;
+ if (op<oLen) out[op++] = (byte)o2;
+ }
+ return out;
+ }
+}