diff --git a/build.gradle b/build.gradle index 0cc7bd840..b421f0183 100644 --- a/build.gradle +++ b/build.gradle @@ -137,8 +137,8 @@ dependencies { implementation "net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT" implementation "net.kyori:adventure-platform-bukkit:4.0.0-SNAPSHOT" implementation 'net.kyori:adventure-api:4.8.1' -\ -// Dynamically Loaded + + // Dynamically Loaded implementation 'it.unimi.dsi:fastutil:8.5.4' implementation 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' implementation 'org.zeroturnaround:zt-zip:1.14' @@ -146,4 +146,5 @@ dependencies { implementation 'org.ow2.asm:asm:9.2' implementation 'com.google.guava:guava:30.1.1-jre' + } \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java b/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java new file mode 100644 index 000000000..e8896350d --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java @@ -0,0 +1,33 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine; + +import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; +import lombok.Data; + +@Data +public class IrisExecutionEnvironment implements EngineExecutionEnvironment { + private final Engine engine; + + IrisExecutionEnvironment(Engine engine) + { + this.engine = engine; + } +} diff --git a/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java b/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java new file mode 100644 index 000000000..91538ff60 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/scripting/EngineExecutionEnvironment.java @@ -0,0 +1,31 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.scripting; + +import com.volmit.iris.engine.framework.Engine; + +public interface EngineExecutionEnvironment +{ + Engine getEngine(); + + default void close() + { + + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/BSFDeclaredBean.java b/src/main/java/com/volmit/iris/util/bsf/BSFDeclaredBean.java new file mode 100644 index 000000000..f68a98435 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/BSFDeclaredBean.java @@ -0,0 +1,39 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.bsf; + +/** + * BSFDeclaredBeans are used internally by BSF to encapsulate information being + * passed between a BSFManager and its various BSFEngines. Note that the + * constructor is not public because this is not a public class. + * + * @author Matthew J. Duftler + * @author Sanjiva Weerawarana + */ +public class BSFDeclaredBean { + public String name; + public Object bean; + public Class type; + + BSFDeclaredBean(String name, Object bean, Class type) { + this.name = name; + this.bean = bean; + this.type = type; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/BSFEngine.java b/src/main/java/com/volmit/iris/util/bsf/BSFEngine.java new file mode 100644 index 000000000..6a3b7247d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/BSFEngine.java @@ -0,0 +1,253 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.bsf; + +import java.beans.PropertyChangeListener; +import java.util.Vector; + +import com.volmit.iris.util.bsf.util.CodeBuffer; + +/** + * This is the view of a scripting engine assumed by the Bean Scripting + * Framework. This interface is used when an application decides to + * run some script under application control. (This is the reverse of + * the more common situation, which is that of the scripting language + * calling into the application.) + *

+ * When a scripting engine is first fired up, the initialize() + * method is called right after construction. + *

+ * A scripting engine must provide two access points for applications + * to call into them: via function calls and via expression evaluation. + * It must also support loading scripts. + *

+ * A scripting engine is a property change listener and will be notified + * when any of the relevant properties of the manager change. (See + * BSFManager to see which of its properties are bound.) + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + */ +public interface BSFEngine extends PropertyChangeListener { + + /** + * This is used by an application to invoke an anonymous function. An + * anonymous function is a multi-line script which when evaluated will + * produce a value. These are separated from expressions and scripts + * because the prior are spsed to be good 'ol expressions and scripts + * are not value returning. We allow anonymous functions to have parameters + * as well for completeness. + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param funcBody the multi-line, value returning script to evaluate + * @param paramNames the names of the parameters above assumes + * @param arguments values of the above parameters + * + * @exception BSFException if anything goes wrong while doin' it. + */ + public Object apply( + String source, + int lineNo, + int columnNo, + Object funcBody, + Vector paramNames, + Vector arguments) + throws BSFException; + /** + * This is used by an application to call into the scripting engine + * to make a function/method call. The "object" argument is the object + * whose method is to be called, if that applies. For non-OO languages, + * this is typically ignored and should be given as null. For pretend-OO + * languages such as VB, this would be the (String) name of the object. + * The arguments are given in the args array. + * + * @param object object on which to make the call + * @param name name of the method / procedure to call + * @param args the arguments to be given to the procedure + * + * @exception BSFException if anything goes wrong while eval'ing a + * BSFException is thrown. The reason indicates the problem. + */ + public Object call(Object object, String name, Object[] args) + throws BSFException; + /** + * This is used by an application to compile an anonymous function. See + * comments in apply for more hdetails. + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param funcBody the multi-line, value returning script to evaluate + * @param paramNames the names of the parameters above assumes + * @param arguments values of the above parameters + * @param cb the CodeBuffer to compile into + * + * @exception BSFException if anything goes wrong while doin' it. + */ + public void compileApply( + String source, + int lineNo, + int columnNo, + Object funcBody, + Vector paramNames, + Vector arguments, + CodeBuffer cb) + throws BSFException; + /** + * This is used by an application to compile a value-returning expression. + * The expr may be string or some other type, depending on the language. + * The generated code is dumped into the CodeBuffer. + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param expr the expression to compile + * @param cb the CodeBuffer to compile into + * + * @exception BSFException if anything goes wrong while compiling a + * BSFException is thrown. The reason indicates the problem. + */ + public void compileExpr( + String source, + int lineNo, + int columnNo, + Object expr, + CodeBuffer cb) + throws BSFException; + /** + * This is used by an application to compile some script. The + * script may be string or some other type, depending on the + * language. The generated code is dumped into the CodeBuffer. + * + * @param source (context info) the source of this script + * (e.g., filename) + * @param lineNo (context info) the line number in source for script + * @param columnNo (context info) the column number in source for script + * @param script the script to compile + * @param cb the CodeBuffer to compile into + * + * @exception BSFException if anything goes wrong while compiling a + * BSFException is thrown. The reason indicates the problem. + */ + public void compileScript( + String source, + int lineNo, + int columnNo, + Object script, + CodeBuffer cb) + throws BSFException; + /** + * Declare a bean after the engine has been started. Declared beans + * are beans that are named and which the engine must make available + * to the scripts it runs in the most first class way possible. + * + * @param bean the bean to declare + * + * @exception BSFException if the engine cannot do this operation + */ + public void declareBean(BSFDeclaredBean bean) throws BSFException; + /** + * This is used by an application to evaluate an expression. The + * expression may be string or some other type, depending on the + * language. (For example, for BML it'll be an org.w3c.dom.Element + * object.) + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param expr the expression to evaluate + * + * @exception BSFException if anything goes wrong while eval'ing a + * BSFException is thrown. The reason indicates the problem. + */ + public Object eval(String source, int lineNo, int columnNo, Object expr) + throws BSFException; + /** + * This is used by an application to execute some script. The + * expression may be string or some other type, depending on the + * language. Returns nothing but if something goes wrong it excepts + * (of course). + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute + * + * @exception BSFException if anything goes wrong while exec'ing a + * BSFException is thrown. The reason indicates the problem. + */ + public void exec(String source, int lineNo, int columnNo, Object script) + throws BSFException; + /** + * This is used by an application to execute some script, as though + * one were interacting with the language in an interactive session. + * The expression may be string or some other type, depending on the + * language. Returns nothing but if something goes wrong it excepts (of + * course). + * + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute + * + * @exception BSFException if anything goes wrong while exec'ing a + * BSFException is thrown. The reason indicates the problem. + */ + public void iexec(String source, int lineNo, int columnNo, Object script) + throws BSFException; + + /** + * This method is used to initialize the engine right after construction. + * This method will be called before any calls to eval or call. At this + * time the engine should capture the current values of interesting + * properties from the manager. In the future, any changes to those + * will be mirrored to me by the manager via a property change event. + * + * @param mgr The BSFManager that's hosting this engine. + * @param lang Language string which this engine is handling. + * @param declaredBeans Vector of BSFDeclaredObject containing beans + * that should be declared into the language runtime at init + * time as best as possible. + * + * @exception BSFException if anything goes wrong while init'ing a + * BSFException is thrown. The reason indicates the problem. + */ + public void initialize(BSFManager mgr, String lang, Vector declaredBeans) + throws BSFException; + /** + * Graceful termination + */ + public void terminate(); + /** + * Undeclare a previously declared bean. + * + * @param bean the bean to undeclare + * + * @exception BSFException if the engine cannot do this operation + */ + public void undeclareBean(BSFDeclaredBean bean) throws BSFException; +} diff --git a/src/main/java/com/volmit/iris/util/bsf/BSFException.java b/src/main/java/com/volmit/iris/util/bsf/BSFException.java new file mode 100644 index 000000000..2ab480d05 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/BSFException.java @@ -0,0 +1,71 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.bsf; + +/** + * If something goes wrong while doing some scripting stuff, one of these + * is thrown. The integer code indicates what's wrong and the message + * may give more details. The reason one exception with multiple meanings + * (via the code) [instead of multiple exception types] is used is due to + * the interest to keep the run-time size small. + * + * @author Sanjiva Weerawarana + */ +public class BSFException extends Exception { + public static final int REASON_INVALID_ARGUMENT = 0; + public static final int REASON_IO_ERROR = 10; + public static final int REASON_UNKNOWN_LANGUAGE = 20; + public static final int REASON_EXECUTION_ERROR = 100; + public static final int REASON_UNSUPPORTED_FEATURE = 499; + public static final int REASON_OTHER_ERROR = 500; + + int reason; + Throwable targetThrowable; + + public BSFException (int reason, String msg) { + super (msg); + this.reason = reason; + } + public BSFException (int reason, String msg, Throwable t) { + this (reason, msg); + targetThrowable = t; + } + public BSFException (String msg) { + this (REASON_OTHER_ERROR, msg); + } + public int getReason () { + return reason; + } + public Throwable getTargetException () { + return targetThrowable; + } + public void printStackTrace () { + if (targetThrowable != null) { + String msg = getMessage (); + + if (msg != null && !msg.equals (targetThrowable.getMessage ())) { + System.err.print (msg + ": "); + } + + targetThrowable.printStackTrace (); + } else { + super.printStackTrace (); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/BSFManager.java b/src/main/java/com/volmit/iris/util/bsf/BSFManager.java new file mode 100644 index 000000000..67179a07a --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/BSFManager.java @@ -0,0 +1,913 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.bsf; + +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.MissingResourceException; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + +import com.volmit.iris.util.bsf.util.CodeBuffer; +import com.volmit.iris.util.bsf.util.ObjectRegistry; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class is the entry point to the bean scripting framework. An + * application wishing to integrate scripting to a Java app would + * place an instance of a BSFManager in their code and use its services + * to register the beans they want to make available for scripting, + * load scripting engines, and run scripts. + *

+ * BSFManager serves as the registry of available scripting engines + * as well. Loading and unloading of scripting engines is + * supported as well. Each BSFManager loads one engine per language. + * Several BSFManagers can be created per JVM. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @author Sam Ruby + * @author Olivier Gruber (added original debugging support) + * @author Don Schwarz (added support for registering languages dynamically) + */ +public class BSFManager { + // version string is in the form "abc.yyyymmdd" where + // "abc" represents a dewey decimal number (three levels, each between 0 and 9), + // and "yyyy" a four digit year, "mm" a two digit month, "dd" a two digit day. + // + // Example: "240.20060925" stands for: BSF version "2.4.0" as of "2006-09-25" + protected static String version="240.20061006"; + + // table of registered scripting engines + protected static Hashtable registeredEngines = new Hashtable(); + + // mapping of file extensions to languages + protected static Hashtable extn2Lang = new Hashtable(); + + // table of scripting engine instances created by this manager. + // only one instance of a given language engine is created by a single + // manager instance. + protected Hashtable loadedEngines = new Hashtable(); + + // table of registered beans for use by scripting engines. + protected ObjectRegistry objectRegistry = new ObjectRegistry(); + + // prop change support containing loaded engines to inform when any + // of my interesting properties change + protected PropertyChangeSupport pcs; + + // the class loader to use if a class loader is needed. Default is + // he who loaded me (which may be null in which case its Class.forName). + // protected ClassLoader classLoader = getClass().getClassLoader(); + protected ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // rgf, 2006-01-05 + + // temporary directory to use to dump temporary files into. Note that + // if class files are dropped here then unless this dir is in the + // classpath or unless the classloader knows to look here, the classes + // will not be found. + protected String tempDir = "."; + + // classpath used by those that need a classpath + protected String classPath; + + // stores BSFDeclaredBeans representing objects + // introduced by a client of BSFManager + protected Vector declaredBeans = new Vector(); + + private Log logger = LogFactory.getLog(this.getClass().getName()); + + ////////////////////////////////////////////////////////////////////// + // + // pre-register engines that BSF supports off the shelf + // + ////////////////////////////////////////////////////////////////////// + + static { + try { + Enumeration e = BSFManager.class.getClassLoader().getResources("org/apache/bsf/Languages.properties"); + while (e.hasMoreElements()) { + URL url = (URL)e.nextElement(); + InputStream is = url.openStream(); + + Properties p = new Properties(); + p.load(is); + + for (Enumeration keys = p.propertyNames(); keys.hasMoreElements();) { + + String key = (String) keys.nextElement(); + String value = p.getProperty(key); + String className = value.substring(0, value.indexOf(",")); + + + + + // get the extensions for this language + String exts = value.substring(value.indexOf(",")+1, value.length()); + StringTokenizer st = new StringTokenizer(exts, "|"); + String[] extensions = new String[st.countTokens()]; + for (int i = 0; st.hasMoreTokens(); i++) { + extensions[i] = ((String) st.nextToken()).trim(); + } + + registerScriptingEngine(key, className, extensions); + } + } + } catch (IOException ex) { + + ex.printStackTrace(); + System.err.println("Error reading Languages file " + ex); + } catch (NoSuchElementException nsee) { + + nsee.printStackTrace(); + System.err.println("Syntax error in Languages resource bundle"); + } catch (MissingResourceException mre) { + + mre.printStackTrace(); + System.err.println("Initialization error: " + mre.toString()); + } + } + + public BSFManager() { + pcs = new PropertyChangeSupport(this); + } + + + /** Returns the version string of BSF. + * + * @return version string in the form "abc.yyyymmdd" where + "abc" represents a dewey decimal number (three levels, each between 0 and 9), and + "yyyy" a four digit year, "mm" a two digit month, + "dd" a two digit day. + * +
Example: "240.20061006" + stands for: BSF version 2.4.0 as of 2006-10-06. + * + * + * @since 2006-01-17 + */ + public static String getVersion() { + + return version; + } + + /** + * Apply the given anonymous function of the given language to the given + * parameters and return the resulting value. + * + * @param lang language identifier + * @param source (context info) the source of this expression + (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param funcBody the multi-line, value returning script to evaluate + * @param paramNames the names of the parameters above assumes + * @param arguments values of the above parameters + * + * @exception BSFException if anything goes wrong while running the script + */ + public Object apply(String lang, + String source, + int lineNo, + int columnNo, + Object funcBody, + Vector paramNames, + Vector arguments) + throws BSFException { + logger.debug("BSFManager:apply"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object funcBodyf = funcBody; + final Vector paramNamesf = paramNames; + final Vector argumentsf = arguments; + Object result = null; + + try { + final Object resultf = + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return e.apply(sourcef, lineNof, columnNof, + funcBodyf, paramNamesf, argumentsf); + } + }); + result = resultf; + } catch (PrivilegedActionException prive) { + + logger.error("Exception: ", prive); + throw (BSFException) prive.getException(); + } + + return result; + } + + /** + * Compile the application of the given anonymous function of the given + * language to the given parameters into the given CodeBuffer. + * + * @param lang language identifier + * @param source (context info) the source of this expression + (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param funcBody the multi-line, value returning script to evaluate + * @param paramNames the names of the parameters above assumes + * @param arguments values of the above parameters + * @param cb code buffer to compile into + * + * @exception BSFException if anything goes wrong while running the script + */ + public void compileApply(String lang, + String source, + int lineNo, + int columnNo, + Object funcBody, + Vector paramNames, + Vector arguments, + CodeBuffer cb) + throws BSFException { + logger.debug("BSFManager:compileApply"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object funcBodyf = funcBody; + final Vector paramNamesf = paramNames; + final Vector argumentsf = arguments; + final CodeBuffer cbf = cb; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + e.compileApply(sourcef, lineNof, columnNof, + funcBodyf, paramNamesf, + argumentsf, cbf); + return null; + } + }); + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } + } + + /** + * Compile the given expression of the given language into the given + * CodeBuffer. + * + * @param lang language identifier + * @param source (context info) the source of this expression + (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param expr the expression to compile + * @param cb code buffer to compile into + * + * @exception BSFException if any error while compiling the expression + */ + public void compileExpr(String lang, + String source, + int lineNo, + int columnNo, + Object expr, + CodeBuffer cb) + throws BSFException { + logger.debug("BSFManager:compileExpr"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object exprf = expr; + final CodeBuffer cbf = cb; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + e.compileExpr(sourcef, lineNof, columnNof, exprf, cbf); + return null; + } + }); + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } + } + + /** + * Compile the given script of the given language into the given + * CodeBuffer. + * + * @param lang language identifier + * @param source (context info) the source of this script + (e.g., filename) + * @param lineNo (context info) the line number in source for script + * @param columnNo (context info) the column number in source for script + * @param script the script to compile + * @param cb code buffer to compile into + * + * @exception BSFException if any error while compiling the script + */ + public void compileScript(String lang, + String source, + int lineNo, + int columnNo, + Object script, + CodeBuffer cb) + throws BSFException { + logger.debug("BSFManager:compileScript"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object scriptf = script; + final CodeBuffer cbf = cb; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + e.compileScript(sourcef, lineNof, columnNof, + scriptf, cbf); + return null; + } + }); + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } + } + + /** + * Declare a bean. The difference between declaring and registering + * is that engines are spsed to make declared beans "pre-available" + * in the scripts as far as possible. That is, if a script author + * needs a registered bean, he needs to look it up in some way. However + * if he needs a declared bean, the language has the responsibility to + * make those beans avaialable "automatically." + *

+ * When a bean is declared it is automatically registered as well + * so that any declared bean can be gotton to by looking it up as well. + *

+ * If any of the languages that are already running in this manager + * says they don't like this (by throwing an exception) then this + * method will simply quit with that exception. That is, any engines + * that come after than in the engine enumeration will not even be + * told about this new bean. + *

+ * So, in general its best to declare beans before the manager has + * been asked to load any engines because then the user can be informed + * when an engine rejects it. Also, its much more likely that an engine + * can declare a bean at start time than it can at any time. + * + * @param beanName name to declare bean as + * @param bean the bean that's being declared + * @param type the type to represent the bean as + * + * @exception BSFException if any of the languages that are already + * running decides to throw an exception when asked to + * declare this bean. + */ + public void declareBean(String beanName, Object bean, Class type) + throws BSFException { + logger.debug("BSFManager:declareBean"); + + registerBean(beanName, bean); + + BSFDeclaredBean tempBean = new BSFDeclaredBean(beanName, bean, type); + declaredBeans.addElement(tempBean); + + Enumeration enginesEnum = loadedEngines.elements(); + BSFEngine engine; + while (enginesEnum.hasMoreElements()) { + engine = (BSFEngine) enginesEnum.nextElement(); + engine.declareBean(tempBean); + } + } + + /** + * Evaluate the given expression of the given language and return the + * resulting value. + * + * @param lang language identifier + * @param source (context info) the source of this expression + (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param expr the expression to evaluate + * + * @exception BSFException if anything goes wrong while running the script + */ + public Object eval(String lang, + String source, + int lineNo, + int columnNo, + Object expr) + throws BSFException { + logger.debug("BSFManager:eval"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object exprf = expr; + Object result = null; + + try { + final Object resultf = + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return e.eval(sourcef, lineNof, columnNof, exprf); + } + }); + result = resultf; + } catch (PrivilegedActionException prive) { + + logger.error("Exception: ", prive); + throw (BSFException) prive.getException(); + } + + return result; + } + + ////////////////////////////////////////////////////////////////////// + // + // Convenience functions for exec'ing and eval'ing scripts directly + // without loading and dealing with engines etc.. + // + ////////////////////////////////////////////////////////////////////// + + /** + * Execute the given script of the given language. + * + * @param lang language identifier + * @param source (context info) the source of this expression + (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute + * + * @exception BSFException if anything goes wrong while running the script + */ + public void exec(String lang, + String source, + int lineNo, + int columnNo, + Object script) + throws BSFException { + logger.debug("BSFManager:exec"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object scriptf = script; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + e.exec(sourcef, lineNof, columnNof, scriptf); + return null; + } + }); + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } + } + + /** + * Execute the given script of the given language, attempting to + * emulate an interactive session w/ the language. + * + * @param lang language identifier + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute + * + * @exception BSFException if anything goes wrong while running the script + */ + public void iexec(String lang, + String source, + int lineNo, + int columnNo, + Object script) + throws BSFException { + logger.debug("BSFManager:iexec"); + + final BSFEngine e = loadScriptingEngine(lang); + final String sourcef = source; + final int lineNof = lineNo, columnNof = columnNo; + final Object scriptf = script; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + e.iexec(sourcef, lineNof, columnNof, scriptf); + return null; + } + }); + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } + } + + /** + * Get classLoader + */ + public ClassLoader getClassLoader() { + logger.debug("BSFManager:getClassLoader"); + return classLoader; + } + + /** + * Get classPath + */ + public String getClassPath() { + logger.debug("BSFManager:getClassPath"); + if (classPath == null) { + try { + classPath = System.getProperty("java.class.path"); + } catch (Throwable t) { + + logger.debug("Exception :", t); + // prolly a security exception .. so no can do + } + } + return classPath; + } + + /** + * Determine the language of a script file by looking at the file + * extension. + * + * @param fileName the name of the file + * + * @return the scripting language the file is in if the file extension + * is known to me (must have been registered via + * registerScriptingEngine). + * + * @exception BSFException if file's extension is unknown. + */ + public static String getLangFromFilename(String fileName) + throws BSFException { + int dotIndex = fileName.lastIndexOf("."); + + if (dotIndex != -1) { + String extn = fileName.substring(dotIndex + 1); + String langval = (String) extn2Lang.get(extn); + String lang = null; + int index, loops = 0; + + if (langval != null) { + while ((index = langval.indexOf(":", 0)) != -1) { + // Great. Multiple language engines registered + // for this extension. + // Try to find first one that is in our classpath. + lang = langval.substring(0, index); + langval = langval.substring(index + 1); + loops++; + + // Test to see if in classpath + try { + String engineName = + (String) registeredEngines.get(lang); + Class.forName(engineName); + } catch (ClassNotFoundException cnfe) { + + // Bummer. + lang = langval; + continue; + } + + // Got past that? Good. + break; + } + if (loops == 0) { lang = langval; } + } + + if (lang != null && lang != "") { + return lang; + } + } + throw new BSFException(BSFException.REASON_OTHER_ERROR, + "file extension missing or unknown: " + + "unable to determine language for '" + + fileName + + "'"); + } + + /** + * Return the current object registry of the manager. + * + * @return the current registry. + */ + public ObjectRegistry getObjectRegistry() { + return objectRegistry; + } + + /** + * Get tempDir + */ + public String getTempDir() { + return tempDir; + } + + /** + * Determine whether a language is registered. + * + * @param lang string identifying a language + * + * @return true iff it is + */ + public static boolean isLanguageRegistered(String lang) { + return (registeredEngines.get(lang) != null); + } + + ////////////////////////////////////////////////////////////////////// + // + // Bean scripting framework services + // + ////////////////////////////////////////////////////////////////////// + + /** + * Load a scripting engine based on the lang string identifying it. + * + * @param lang string identifying language + * @exception BSFException if the language is unknown (i.e., if it + * has not been registered) with a reason of + * REASON_UNKNOWN_LANGUAGE. If the language is known but + * if the interface can't be created for some reason, then + * the reason is set to REASON_OTHER_ERROR and the actual + * exception is passed on as well. + */ + public BSFEngine loadScriptingEngine(String lang) throws BSFException { + logger.debug("BSFManager:loadScriptingEngine"); + + // if its already loaded return that + BSFEngine eng = (BSFEngine) loadedEngines.get(lang); + if (eng != null) { + return eng; + } + + // is it a registered language? + String engineClassName = (String) registeredEngines.get(lang); + if (engineClassName == null) { + logger.error("unsupported language: " + lang); + throw new BSFException(BSFException.REASON_UNKNOWN_LANGUAGE, + "unsupported language: " + lang); + } + + // create the engine and initialize it. if anything goes wrong + // except. + try { + Class engineClass = + (classLoader == null) + ? Class.forName(engineClassName) + : classLoader.loadClass(engineClassName); + final BSFEngine engf = (BSFEngine) engineClass.newInstance(); + final BSFManager thisf = this; + final String langf = lang; + final Vector dbf = declaredBeans; + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + engf.initialize(thisf, langf, dbf); + return null; + } + }); + eng = engf; + loadedEngines.put(lang, eng); + pcs.addPropertyChangeListener(eng); + return eng; + } catch (PrivilegedActionException prive) { + + logger.error("Exception :", prive); + throw (BSFException) prive.getException(); + } catch (Throwable t) { + + logger.error("Exception :", t); + throw new BSFException(BSFException.REASON_OTHER_ERROR, + "unable to load language: " + lang, + t); + } + } + + /** + * return a handle to a bean registered in the bean registry by the + * application or a scripting engine. Returns null if bean is not found. + * + * @param beanName name of bean to look up + * + * @return the bean if its found or null + */ + public Object lookupBean(String beanName) { + logger.debug("BSFManager:lookupBean"); + + try { + return ((BSFDeclaredBean)objectRegistry.lookup(beanName)).bean; + } catch (IllegalArgumentException e) { + + logger.debug("Exception :", e); + return null; + } + } + + /** + * Registering a bean allows a scripting engine or the application to + * access that bean by name and to manipulate it. + * + * @param beanName name to register under + * @param bean the bean to register + */ + public void registerBean(String beanName, Object bean) { + logger.debug("BSFManager:registerBean"); + + BSFDeclaredBean tempBean; + + if(bean == null) { + tempBean = new BSFDeclaredBean(beanName, null, null); + } else { + + tempBean = new BSFDeclaredBean(beanName, bean, bean.getClass()); + } + objectRegistry.register(beanName, tempBean); + } + + /** + * Register a scripting engine in the static registry of the + * BSFManager. + * + * @param lang string identifying language + * @param engineClassName fully qualified name of the class interfacing + * the language to BSF. + * @param extensions array of file extensions that should be mapped to + * this language type. may be null. + */ + public static void registerScriptingEngine(String lang, + String engineClassName, + String[] extensions) { + registeredEngines.put(lang, engineClassName); + if (extensions != null) { + for (int i = 0; i < extensions.length; i++) { + String langstr = (String) extn2Lang.get(extensions[i]); + langstr = (langstr == null) ? lang : lang + ":" + langstr; + extn2Lang.put(extensions[i], langstr); + } + } + } + + /** + * Set the class loader for those that need to use it. Default is he + * who loaded me or null (i.e., its Class.forName). + * + * @param classLoader the class loader to use. + */ + public void setClassLoader(ClassLoader classLoader) { + logger.debug("BSFManager:setClassLoader"); + + pcs.firePropertyChange("classLoader", this.classLoader, classLoader); + this.classLoader = classLoader; + } + + /** + * Set the classpath for those that need to use it. Default is the value + * of the java.class.path property. + * + * @param classPath the classpath to use + */ + public void setClassPath(String classPath) { + logger.debug("BSFManager:setClassPath"); + + pcs.firePropertyChange("classPath", this.classPath, classPath); + this.classPath = classPath; + } + + /** + * Set the object registry used by this manager. By default a new + * one is created when the manager is new'ed and this overwrites + * that one. + * + * @param objectRegistry the registry to use + */ + public void setObjectRegistry(ObjectRegistry objectRegistry) { + logger.debug("BSFManager:setObjectRegistry"); + + this.objectRegistry = objectRegistry; + } + + /** + * Temporary directory to put stuff into (for those who need to). Note + * that unless this directory is in the classpath or unless the + * classloader knows to look in here, any classes here will not + * be found! BSFManager provides a service method to load a class + * which uses either the classLoader provided by the class loader + * property or, if that fails, a class loader which knows to load from + * the tempdir to try to load the class. Default value of tempDir + * is "." (current working dir). + * + * @param tempDir the temporary directory + */ + public void setTempDir(String tempDir) { + logger.debug("BSFManager:setTempDir"); + + pcs.firePropertyChange("tempDir", this.tempDir, tempDir); + this.tempDir = tempDir; + } + + /** + * Gracefully terminate all engines + */ + public void terminate() { + logger.debug("BSFManager:terminate"); + + Enumeration enginesEnum = loadedEngines.elements(); + BSFEngine engine; + while (enginesEnum.hasMoreElements()) { + engine = (BSFEngine) enginesEnum.nextElement(); + engine.terminate(); + } + + loadedEngines = new Hashtable(); + } + + /** + * Undeclare a previously declared bean. This removes the bean from + * the list of declared beans in the manager as well as asks every + * running engine to undeclared the bean. As with above, if any + * of the engines except when asked to undeclare, this method does + * not catch that exception. Quietly returns if the bean is unknown. + * + * @param beanName name of bean to undeclare + * + * @exception BSFException if any of the languages that are already + * running decides to throw an exception when asked to + * undeclare this bean. + */ + public void undeclareBean(String beanName) throws BSFException { + logger.debug("BSFManager:undeclareBean"); + + unregisterBean(beanName); + + BSFDeclaredBean tempBean = null; + boolean found = false; + for (Iterator i = declaredBeans.iterator(); i.hasNext();) { + tempBean = (BSFDeclaredBean) i.next(); + if (tempBean.name.equals(beanName)) { + found = true; + break; + } + } + + if (found) { + declaredBeans.removeElement(tempBean); + + Enumeration enginesEnum = loadedEngines.elements(); + while (enginesEnum.hasMoreElements()) { + BSFEngine engine = (BSFEngine) enginesEnum.nextElement(); + engine.undeclareBean(tempBean); + } + } + } + + /** + * Unregister a previously registered bean. Silent if name is not found. + * + * @param beanName name of bean to unregister + */ + public void unregisterBean(String beanName) { + logger.debug("BSFManager:unregisterBean"); + + objectRegistry.unregister(beanName); + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/util/bsf/Languages.properties b/src/main/java/com/volmit/iris/util/bsf/Languages.properties new file mode 100644 index 000000000..6c5e83281 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/Languages.properties @@ -0,0 +1,50 @@ +# +# Iris is a World Generator for Minecraft Bukkit Servers +# Copyright (c) 2021 Arcane Arts (Volmit Software) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# List of script types and their associated scripting engines +# +# languageDescriptor = engineClass, ext1|ext2|... {, codebaseURL, ...} +# +# where exti are extensions for the language. Note that we leave +# all the engines enabled now and allow them to fail at load time. +# This way engines can be added by just adding to the classpath +# without having to edit this file. Cheating, really, but it works. +# +javascript = com.volmit.iris.util.bsf.engines.javascript.JavaScriptEngine, js +jacl = com.volmit.iris.util.bsf.engines.jacl.JaclEngine, jacl +netrexx = com.volmit.iris.util.bsf.engines.netrexx.NetRexxEngine, nrx +java = com.volmit.iris.util.bsf.engines.java.JavaEngine, java +javaclass = com.volmit.iris.util.bsf.engines.javaclass.JavaClassEngine, class +bml = org.apache.bml.ext.BMLEngine, bml +vbscript = com.volmit.iris.util.bsf.engines.activescript.ActiveScriptEngine, vbs +jscript = com.volmit.iris.util.bsf.engines.activescript.ActiveScriptEngine, jss +perlscript = com.volmit.iris.util.bsf.engines.activescript.ActiveScriptEngine, pls +perl = com.volmit.iris.util.bsf.engines.perl.PerlEngine, pl +jpython = com.volmit.iris.util.bsf.engines.jpython.JPythonEngine, py +jython = com.volmit.iris.util.bsf.engines.jython.JythonEngine, py +lotusscript = com.volmit.iris.util.bsf.engines.lotusscript.LsEngine, lss +xslt = com.volmit.iris.util.bsf.engines.xslt.XSLTEngine, xslt +pnuts = pnuts.ext.PnutsBSFEngine, pnut +beanbasic = com.volmit.iris.util.bsf.engines.beanbasic.BeanBasicEngine, bb +beanshell = bsh.util.BeanShellBSFEngine, bsh +ruby = org.jruby.javasupport.bsf.JRubyEngine, rb +judoscript = com.judoscript.BSFJudoEngine, judo|jud +groovy = org.codehaus.groovy.bsf.GroovyEngine, groovy|gy +objectscript = oscript.bsf.ObjectScriptEngine, os +prolog = ubc.cs.JLog.Extras.BSF.JLogBSFEngine, plog|prolog +rexx = org.rexxla.bsf.engines.rexx.RexxEngine, rex | rexx | cls | rxj | rxs diff --git a/src/main/java/com/volmit/iris/util/bsf/Main.java b/src/main/java/com/volmit/iris/util/bsf/Main.java new file mode 100644 index 000000000..10fbfe6a6 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/Main.java @@ -0,0 +1,190 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.bsf; + +import java.awt.Frame; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.util.Hashtable; + +import com.volmit.iris.util.bsf.util.CodeBuffer; +import com.volmit.iris.util.bsf.util.IOUtils; + +/** + * This is the main driver for BSF to be run on the command line + * to eval/exec/compile scripts directly. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @author Sam Ruby + */ +public class Main { + private static String ARG_IN = "-in"; + private static String ARG_LANG = "-lang"; + private static String ARG_MODE = "-mode"; + private static String ARG_OUT = "-out"; + private static String ARG_VAL_EVAL = "eval"; + private static String ARG_VAL_EXEC = "exec"; + private static String ARG_VAL_COMPILE = "compile"; + private static String DEFAULT_IN_FILE_NAME = ""; + private static String DEFAULT_MODE = ARG_VAL_EVAL; + private static String DEFAULT_CLASS_NAME = "Test"; + + /** + * Static driver to be able to run BSF scripts from the command line. + * + * @param args command line arguments + * + * @exception IOException if any I/O error while loading script + */ + public static void main(String[] args) throws IOException { + try { + if ((args.length == 0) || (args.length % 2 != 0)) { + printHelp(); + System.exit(1); + } + + Hashtable argsTable = new Hashtable(); + + argsTable.put(ARG_OUT, DEFAULT_CLASS_NAME); + argsTable.put(ARG_MODE, DEFAULT_MODE); + + for (int i = 0; i < args.length; i += 2) { + argsTable.put(args[i], args[i + 1]); + } + + String inFileName = (String) argsTable.get(ARG_IN); + String language = (String) argsTable.get(ARG_LANG); + + if (language == null) { + if (inFileName != null) { + language = BSFManager.getLangFromFilename(inFileName); + } else { + throw new BSFException( + BSFException.REASON_OTHER_ERROR, + "unable to determine language"); + } + } + + Reader in; + + if (inFileName != null) { + in = new FileReader(inFileName); + } else { + in = new InputStreamReader(System.in); + inFileName = DEFAULT_IN_FILE_NAME; + } + + BSFManager mgr = new BSFManager(); + String mode = (String) argsTable.get(ARG_MODE); + + if (mode.equals(ARG_VAL_COMPILE)) { + String outClassName = (String) argsTable.get(ARG_OUT); + FileWriter out = new FileWriter(outClassName + ".java"); + PrintWriter pw = new PrintWriter(out); + + CodeBuffer cb = new CodeBuffer(); + cb.setClassName(outClassName); + mgr.compileScript( + language, + inFileName, + 0, + 0, + IOUtils.getStringFromReader(in), + cb); + cb.print(pw, true); + out.close(); + } else { + if (mode.equals(ARG_VAL_EXEC)) { + mgr.exec(language, inFileName, 0, 0, IOUtils.getStringFromReader(in)); + } else { /* eval */ + Object obj = mgr.eval(language, inFileName, 0, 0, IOUtils.getStringFromReader(in)); + + + // Try to display the result. + + if (obj instanceof java.awt.Component) { + Frame f; + if (obj instanceof Frame) { + f = (Frame) obj; + } else { + f = new Frame ("BSF Result: " + inFileName); + f.add ((java.awt.Component) obj); + } + // Add a window listener to quit on closing. + f.addWindowListener( + new WindowAdapter () { + public void windowClosing (WindowEvent e) { + System.exit (0); + } + } + ); + f.pack (); + // f.show(); // javac 1.5 warns to use f.show(), Apache build scripts abort as a result :( + f.setVisible(true); // available since Java 1.1 + } else { + System.err.println("Result: " + obj); + + } + + System.err.println("Result: " + obj); + } + } + } catch (BSFException e) { + e.printStackTrace(); + } + } + + private static void printHelp() { + System.err.println("Usage:"); + System.err.println(); + System.err.println(" java " + Main.class.getName() + " [args]"); + System.err.println(); + System.err.println(" args:"); + System.err.println(); + System.err.println( + " [-in fileName] default: " + DEFAULT_IN_FILE_NAME); + System.err.println( + " [-lang languageName] default: " + + ""); + System.err.println( + " [-mode (eval|exec|compile)] default: " + DEFAULT_MODE); + System.err.println(); + System.err.println( + " Additional args used only if -mode is " + "set to \"compile\":"); + System.err.println(); + System.err.println( + " [-out className] default: " + DEFAULT_CLASS_NAME); + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/jacl/BSFCommand.java b/src/main/java/com/volmit/iris/util/bsf/engines/jacl/BSFCommand.java new file mode 100644 index 000000000..9c1702d35 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/jacl/BSFCommand.java @@ -0,0 +1,107 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.jacl; + +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.EngineUtils; + +import tcl.lang.Command; +import tcl.lang.Interp; +import tcl.lang.ReflectObject; +import tcl.lang.TCL; +import tcl.lang.TclException; +import tcl.lang.TclObject; + +// class used to add "bsf" command to the Jacl runtime +class BSFCommand implements Command { + BSFManager mgr; + BSFEngine jengine; + + BSFCommand (BSFManager mgr, BSFEngine jengine) { + this.mgr = mgr; + this.jengine = jengine; + } + public void cmdProc (Interp interp, + TclObject argv[]) throws TclException { + if (argv.length < 2) { + interp.setResult ("invalid # of args; usage: bsf " + + "lookupBean|registerBean|unregisterBean|addEventListener args"); + throw new TclException (TCL.ERROR); + } + + String op = argv[1].toString (); + + if (op.equals ("lookupBean")) { + if (argv.length != 3) { + interp.setResult ("invalid # of args; usage: bsf " + + "lookupBean name-of-bean"); + throw new TclException (TCL.ERROR); + } + + String beanName = argv[2].toString (); + Object bean = mgr.lookupBean (beanName); + if (bean == null) { + interp.setResult ("unknown object: " + beanName); + throw new TclException (TCL.ERROR); + } + interp.setResult (ReflectObject.newInstance (interp, bean.getClass (), + bean)); + + } else if (op.equals ("registerBean")) { + if (argv.length != 4) { + interp.setResult ("invalid # of args; usage: bsf " + + "registerBean name-of-bean bean"); + throw new TclException (TCL.ERROR); + } + mgr.registerBean (argv[2].toString (), + ReflectObject.get (interp, argv[3])); + interp.setResult (""); + + } else if (op.equals ("unregisterBean")) { + if (argv.length != 3) { + interp.setResult ("invalid # of args; usage: bsf " + + "unregisterBean name-of-bean"); + throw new TclException (TCL.ERROR); + } + mgr.unregisterBean (argv[2].toString ()); + interp.setResult (""); + + } else if (op.equals ("addEventListener")) { + if (argv.length != 6) { + interp.setResult ("invalid # of args; usage: bsf " + + "addEventListener object event-set-name filter " + + "script-to-run"); + throw new TclException (TCL.ERROR); + } + try { + // usage: bsf addEventListener object event-set filter script + String filter = argv[4].toString (); + filter = filter.equals ("") ? null : filter; + EngineUtils.addEventListener (ReflectObject.get (interp, argv[2]), + argv[3].toString (), filter, + jengine, mgr, "", 0, 0, + argv[5].toString ()); + } catch (BSFException e) { + e.printStackTrace (); + interp.setResult ("got BSF exception: " + e.getMessage ()); + throw new TclException (TCL.ERROR); + } + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/jacl/JaclEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/jacl/JaclEngine.java new file mode 100644 index 000000000..57cdfbbf0 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/jacl/JaclEngine.java @@ -0,0 +1,136 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.jacl; + +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; + +import tcl.lang.Interp; +import tcl.lang.ReflectObject; +import tcl.lang.TclDouble; +import tcl.lang.TclException; +import tcl.lang.TclInteger; +import tcl.lang.TclObject; +import tcl.lang.TclString; + +/** + * This is the interface to Scriptics's Jacl (Tcl) from the + * Bean Scripting Framework. + *

+ * + * @author Sanjiva Weerawarana + */ + +public class JaclEngine extends BSFEngineImpl { + /* the Jacl interpretor object */ + private Interp interp; + + /** + * + * @param method The name of the method to call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + public Object call (Object obj, String method, Object[] args) + throws BSFException { + StringBuffer tclScript = new StringBuffer (method); + if (args != null) { + for( int i = 0 ; i < args.length ; i++ ) { + tclScript.append (" "); + tclScript.append (args[i].toString ()); + } + } + return eval ("", 0, 0, tclScript.toString ()); + } + /** + * Declare a bean + */ + public void declareBean (BSFDeclaredBean bean) throws BSFException { + String expr = "set " + bean.name + " [bsf lookupBean \"" + bean.name + + "\"]"; + eval ("", 0, 0, expr); + } + /** + * This is used by an application to evaluate a string containing + * some expression. + */ + public Object eval (String source, int lineNo, int columnNo, + Object oscript) throws BSFException { + String script = oscript.toString (); + try { + interp.eval (script); + TclObject result = interp.getResult(); + Object internalRep = result.getInternalRep(); + + // if the object has a corresponding Java type, unwrap it + if (internalRep instanceof ReflectObject) + return ReflectObject.get(interp,result); + if (internalRep instanceof TclString) + return result.toString(); + if (internalRep instanceof TclDouble) + return new Double(TclDouble.get(interp,result)); + if (internalRep instanceof TclInteger) + return new Integer(TclInteger.get(interp,result)); + + return result; + } catch (TclException e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "error while eval'ing Jacl expression: " + + interp.getResult (), e); + } + } + /** + * Initialize the engine. + */ + public void initialize (BSFManager mgr, String lang, + Vector declaredBeans) throws BSFException { + super.initialize (mgr, lang, declaredBeans); + + // create interpreter + interp = new Interp(); + + // register the extension that user's can use to get at objects + // registered by the app + interp.createCommand ("bsf", new BSFCommand (mgr, this)); + + // Make java functions be available to Jacl + try { + interp.eval("jaclloadjava"); + } catch (TclException e) { + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "error while loading java package: " + + interp.getResult (), e); + } + + int size = declaredBeans.size (); + for (int i = 0; i < size; i++) { + declareBean ((BSFDeclaredBean) declaredBeans.elementAt (i)); + } + } + + /** + * Undeclare a previously declared bean. + */ + public void undeclareBean (BSFDeclaredBean bean) throws BSFException { + eval ("", 0, 0, "set " + bean.name + " \"\""); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/java/JavaEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/java/JavaEngine.java new file mode 100644 index 000000000..7b2405449 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/java/JavaEngine.java @@ -0,0 +1,352 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.java; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.CodeBuffer; +import com.volmit.iris.util.bsf.util.EngineUtils; +import com.volmit.iris.util.bsf.util.JavaUtils; +import com.volmit.iris.util.bsf.util.MethodUtils; +import com.volmit.iris.util.bsf.util.ObjInfo; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is the interface to Java from the + * Bean Scripting Framework. + *

+ * The Java code must be written script-style -- that is, just the body of + * the function, without class or method headers or footers. + * The JavaEngine will generate those via a "boilerplate" wrapper: + *

+ * 
+ * import java.lang.*;
+ * import java.util.*;
+ * public class $$CLASSNAME$$ {
+ *   static public Object BSFJavaEngineEntry(com.volmit.iris.util.bsf.BSFManager bsf) {
+ *     // Your code will be placed here
+ *   }
+ * }
+ * 
+ * 
+ * $$CLASSNAME$$ will be replaced by a generated classname of the form + * BSFJava*, and the bsf parameter can be used to retrieve application + * objects registered with the Bean Scripting Framework. + *

+ * If you use the placeholder string $$CLASSNAME$$ elsewhere + * in your script -- including within text strings -- BSFJavaEngine will + * replace it with the generated name of the class before the Java code + * is compiled. + *

+ *

Hazards:

+ *

+ * NOTE that it is your responsibility to convert the code into an acceptable + * Java string. If you're invoking the JavaEngine directly (as in the + * JSPLikeInJava example) that means \"quoting\" characters that would + * otherwise cause trouble. + *

+ * ALSO NOTE that it is your responsibility to return an object, or null in + * lieu thereof! + *

+ * Since the code has to be compiled to a Java classfile, invoking it involves + * a fair amount of computation to load and execute the compiler. We are + * currently making an attempt to manage that by caching the class + * after it has been loaded, but the indexing is fairly primitive. It has + * been suggested that the Bean Scripting Framework may want to support + * preload-and-name-script and execute-preloaded-script-by-name options to + * provide better control over when and how much overhead occurs. + *

+ * @author Joe Kesselman + */ +public class JavaEngine extends BSFEngineImpl { + Class javaclass = null; + static Hashtable codeToClass = new Hashtable(); + static String serializeCompilation = ""; + static String placeholder = "$$CLASSNAME$$"; + String minorPrefix; + + private Log logger = LogFactory.getLog(this.getClass().getName()); + + /** + * Create a scratchfile, open it for writing, return its name. + * Relies on the filesystem to provide us with uniqueness testing. + * NOTE THAT uniqueFileOffset continues to count; we don't want to + * risk reusing a classname we have previously loaded in this session + * even if the classfile has been deleted. + */ + private int uniqueFileOffset = -1; + + private class GeneratedFile { + File file = null; + FileOutputStream fos = null; + String className = null; + GeneratedFile(File file, FileOutputStream fos, String className) { + this.file = file; + this.fos = fos; + this.className = className; + } + } + + /** + * Constructor. + */ + public JavaEngine () { + // Do compilation-possible check here?????????????? + } + + public Object call (Object object, String method, Object[] args) + throws BSFException + { + throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, + "call() is not currently supported by JavaEngine"); + } + + public void compileScript (String source, int lineNo, int columnNo, + Object script, CodeBuffer cb) throws BSFException { + ObjInfo oldRet = cb.getFinalServiceMethodStatement (); + + if (oldRet != null && oldRet.isExecutable ()) { + cb.addServiceMethodStatement (oldRet.objName + ";"); + } + + cb.addServiceMethodStatement (script.toString ()); + cb.setFinalServiceMethodStatement (null); + } + + /** + * This is used by an application to evaluate a string containing + * some expression. It should store the "bsf" handle where the + * script can get to it, for callback purposes. + *

+ * Note that Java compilation imposes serious overhead, + * but in exchange you get full Java performance + * once the classes have been created (minus the cache lookup cost). + *

+ * Nobody knows whether javac is threadsafe. + * I'm going to serialize access to protect it. + *

+ * There is no published API for invoking javac as a class. There's a trick + * that seems to work for Java 1.1.x, but it stopped working in Java 1.2. + * We will attempt to use it, then if necessary fall back on invoking + * javac via the command line. + */ + public Object eval (String source, int lineNo, int columnNo, + Object oscript) throws BSFException + { + Object retval = null; + String classname = null; + GeneratedFile gf = null; + + String basescript = oscript.toString(); + String script = basescript; // May be altered by $$CLASSNAME$$ expansion + + try { + // Do we already have a class exactly matching this code? + javaclass = (Class)codeToClass.get(basescript); + + if(javaclass != null) { + classname=javaclass.getName(); + } else { + gf = openUniqueFile(tempDir, "BSFJava",".java"); + if( gf == null) { + throw new BSFException("couldn't create JavaEngine scratchfile"); + } + // Obtain classname + classname = gf.className; + + // Write the kluge header to the file. + gf.fos.write(("import java.lang.*;"+ + "import java.util.*;"+ + "public class "+classname+" {\n" + + " static public Object BSFJavaEngineEntry(com.volmit.iris.util.bsf.BSFManager bsf) {\n") + .getBytes()); + + // Edit the script to replace placeholder with the generated + // classname. Note that this occurs _after_ the cache was checked! + int startpoint = script.indexOf(placeholder); + int endpoint; + if(startpoint >= 0) { + StringBuffer changed = new StringBuffer(); + for(; startpoint >=0; startpoint = script.indexOf(placeholder,startpoint)) { + changed.setLength(0); // Reset for 2nd pass or later + if(startpoint > 0) { + changed.append(script.substring(0,startpoint)); + } + changed.append(classname); + endpoint = startpoint+placeholder.length(); + if(endpoint < script.length()) { + changed.append(script.substring(endpoint)); + } + script = changed.toString(); + } + } + + // MJD - debug +// BSFDeclaredBean tempBean; +// String className; +// +// for (int i = 0; i < declaredBeans.size (); i++) { +// tempBean = (BSFDeclaredBean) declaredBeans.elementAt (i); +// className = StringUtils.getClassName (tempBean.bean.getClass ()); +// +// gf.fos.write ((className + " " + +// tempBean.name + " = (" + className + +// ")bsf.lookupBean(\"" + +// tempBean.name + "\");").getBytes ()); +// } + // MJD - debug + + // Copy the input to the file. + // Assumes all available -- probably mistake, but same as other engines. + gf.fos.write(script.getBytes()); + // Close the method and class + gf.fos.write(("\n }\n}\n").getBytes()); + gf.fos.close(); + + // Compile through Java to .class file + // May not be threadsafe. Serialize access on static object: + synchronized(serializeCompilation) { + JavaUtils.JDKcompile(gf.file.getPath(), classPath); + } + + // Load class. + javaclass = EngineUtils.loadClass(mgr, classname); + + // Stash class for reuse + codeToClass.put(basescript, javaclass); + } + + Object[] callArgs = {mgr}; + retval = internalCall(this,"BSFJavaEngineEntry",callArgs); + } + + + catch(Exception e) { + e.printStackTrace (); + throw new BSFException (BSFException.REASON_IO_ERROR, e.getMessage ()); + } finally { + // Cleanup: delete the .java and .class files + +// if(gf!=null && gf.file!=null && gf.file.exists()) +// gf.file.delete(); // .java file + + + if(classname!=null) { + // Generated class + File file = new File(tempDir+File.separatorChar+classname+".class"); +// if(file.exists()) +// file.delete(); + + // Search for and clean up minor classes, classname$xxx.class + file = new File(tempDir); // ***** Is this required? + minorPrefix = classname+"$"; // Indirect arg to filter + String[] minorClassfiles = file.list(new FilenameFilter() + { + // Starts with classname$ and ends with .class + public boolean accept(File dir,String name) { + return + (0 == name.indexOf(minorPrefix)) + && + (name.lastIndexOf(".class") == name.length()-6); + } + }); + for(int i = 0; i < minorClassfiles.length; ++i) { + file = new File(minorClassfiles[i]); +// file.delete(); + } + } + } + return retval; + } + + public void initialize (BSFManager mgr, String lang, + Vector declaredBeans) throws BSFException { + super.initialize (mgr, lang, declaredBeans); + } + /** + * Return an object from an extension. + * @param object Object on which to make the internal_call (ignored). + * @param method The name of the method to internal_call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + Object internalCall (Object object, String method, Object[] args) + throws BSFException + { + //***** ISSUE: Only static methods are currently supported + Object retval = null; + try { + if(javaclass != null) { + //***** This should call the lookup used in BML, for typesafety + Class[] argtypes = new Class[args.length]; + for(int i=0; i0;--i,++uniqueFileOffset) { + // Probably a timing hazard here... *************** + try { + className = prefix+uniqueFileOffset; + file = new File(directory+File.separatorChar+className+suffix); + if(file != null && !file.exists()) { + fos = new FileOutputStream(file); + } + } + catch(Exception e) { + // File could not be opened for write, or Security Exception + // was thrown. If someone else created the file before we could + // open it, that's probably a threading conflict and we don't + // bother reporting it. + if(!file.exists()) { + logger.error("openUniqueFile: unexpected ", e); + } + } + } + if(fos==null) { + logger.error("openUniqueFile: Failed "+max+"attempts."); + } else { + gf = new GeneratedFile(file,fos,className); + } + return gf; + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/javaclass/JavaClassEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/javaclass/JavaClassEngine.java new file mode 100644 index 000000000..c79007d87 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/javaclass/JavaClassEngine.java @@ -0,0 +1,73 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.javaclass; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.MethodUtils; + +/** + * This is the interface to scripts consisting of Java objects from the + * Bean Scripting Framework. + * + * @author Sanjiva Weerawarana + */ +public class JavaClassEngine extends BSFEngineImpl { + /** + * call the named method of the given object. If object is an instance + * of Class, then the call is a static call on that object. If not, its + * an instance method call or a static call (as per Java) on the given + * object. + */ + public Object call (Object object, String method, Object[] args) + throws BSFException { + // determine arg types + Class[] argTypes = null; + if (args != null) { + argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = (args[i] != null) ? args[i].getClass () : null; + } + } + + // now find method with the right signature, call it and return result + try { + Method m = MethodUtils.getMethod (object, method, argTypes); + return m.invoke (object, args); + } catch (Exception e) { + // something went wrong while invoking method + Throwable t = (e instanceof InvocationTargetException) ? + ((InvocationTargetException)e).getTargetException () : + null; + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "method invocation failed: " + e + + ((t==null)?"":(" target exception: "+t)), t); + } + } + /** + * This is used by an application to evaluate an object containing + * some expression - clearly not possible for compiled code .. + */ + public Object eval (String source, int lineNo, int columnNo, + Object oscript) throws BSFException { + throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, + "Java bytecode engine can't evaluate expressions"); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/javascript/JavaScriptEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/javascript/JavaScriptEngine.java new file mode 100644 index 000000000..12051a811 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/javascript/JavaScriptEngine.java @@ -0,0 +1,233 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.javascript; + +import java.util.Iterator; +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.BSFFunctions; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.EvaluatorException; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.NativeJavaObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.WrappedException; +import org.mozilla.javascript.Wrapper; + +/** + * This is the interface to Netscape's Rhino (JavaScript) from the + * Bean Scripting Framework. + *

+ * The original version of this code was first written by Adam Peller + * for use in LotusXSL. Sanjiva took his code and adapted it for BSF. + * + * @author Adam Peller + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @author Norris Boyd + */ +public class JavaScriptEngine extends BSFEngineImpl { + /** + * The global script object, where all embedded functions are defined, + * as well as the standard ECMA "core" objects. + */ + private Scriptable global; + + /** + * Return an object from an extension. + * @param object Object on which to make the call (ignored). + * @param method The name of the method to call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + public Object call(Object object, String method, Object[] args) + throws BSFException { + + Object retval = null; + Context cx; + + try { + cx = Context.enter(); + + // REMIND: convert arg list Vectors here? + + Object fun = global.get(method, global); + // NOTE: Source and line arguments are nonsense in a call(). + // Any way to make these arguments *sensible? + if (fun == Scriptable.NOT_FOUND) + throw new EvaluatorException("function " + method + + " not found.", "none", 0); + + cx.setOptimizationLevel(-1); + cx.setGeneratingDebug(false); + cx.setGeneratingSource(false); + cx.setOptimizationLevel(0); + cx.setDebugger(null, null); + + retval = + ((Function) fun).call(cx, global, global, args); + +// ScriptRuntime.call(cx, fun, global, args, global); + + if (retval instanceof Wrapper) + retval = ((Wrapper) retval).unwrap(); + } + catch (Throwable t) { + handleError(t); + } + finally { + Context.exit(); + } + return retval; + } + + public void declareBean(BSFDeclaredBean bean) throws BSFException { + if ((bean.bean instanceof Number) || + (bean.bean instanceof String) || + (bean.bean instanceof Boolean)) { + global.put(bean.name, global, bean.bean); + } + else { + // Must wrap non-scriptable objects before presenting to Rhino + Scriptable wrapped = Context.toObject(bean.bean, global); + global.put(bean.name, global, wrapped); + } + } + + /** + * This is used by an application to evaluate a string containing + * some expression. + */ + public Object eval(String source, int lineNo, int columnNo, Object oscript) + throws BSFException { + + String scriptText = oscript.toString(); + Object retval = null; + Context cx; + + try { + cx = Context.enter(); + + cx.setOptimizationLevel(-1); + cx.setGeneratingDebug(false); + cx.setGeneratingSource(false); + cx.setOptimizationLevel(0); + cx.setDebugger(null, null); + + retval = cx.evaluateString(global, scriptText, + source, lineNo, + null); + + if (retval instanceof NativeJavaObject) + retval = ((NativeJavaObject) retval).unwrap(); + + } + catch (Throwable t) { // includes JavaScriptException, rethrows Errors + handleError(t); + } + finally { + Context.exit(); + } + return retval; + } + + private void handleError(Throwable t) throws BSFException { + if (t instanceof WrappedException) + t = ((WrappedException) t).getWrappedException(); + + String message = null; + Throwable target = t; + + if (t instanceof JavaScriptException) { + message = t.getLocalizedMessage(); + + // Is it an exception wrapped in a JavaScriptException? + Object value = ((JavaScriptException) t).getValue(); + if (value instanceof Throwable) { + // likely a wrapped exception from a LiveConnect call. + // Display its stack trace as a diagnostic + target = (Throwable) value; + } + } + else if (t instanceof EvaluatorException || + t instanceof SecurityException) { + message = t.getLocalizedMessage(); + } + else if (t instanceof RuntimeException) { + message = "Internal Error: " + t.toString(); + } + else if (t instanceof StackOverflowError) { + message = "Stack Overflow"; + } + + if (message == null) + message = t.toString(); + + if (t instanceof Error && !(t instanceof StackOverflowError)) { + // Re-throw Errors because we're supposed to let the JVM see it + // Don't re-throw StackOverflows, because we know we've + // corrected the situation by aborting the loop and + // a long stacktrace would end up on the user's console + throw (Error) t; + } + else { + throw new BSFException(BSFException.REASON_OTHER_ERROR, + "JavaScript Error: " + message, + target); + } + } + + /** + * Initialize the engine. + * Put the manager into the context-manager + * map hashtable too. + */ + public void initialize(BSFManager mgr, String lang, Vector declaredBeans) + throws BSFException { + + super.initialize(mgr, lang, declaredBeans); + + // Initialize context and global scope object + try { + Context cx = Context.enter(); + global = new ImporterTopLevel(cx); + Scriptable bsf = Context.toObject(new BSFFunctions(mgr, this), global); + global.put("bsf", global, bsf); + + for(Iterator it = declaredBeans.iterator(); it.hasNext();) { + declareBean((BSFDeclaredBean) it.next()); + } + } + catch (Throwable t) { + + } + finally { + Context.exit(); + } + } + + public void undeclareBean(BSFDeclaredBean bean) throws BSFException { + global.delete(bean.name); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/jython/JythonEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/jython/JythonEngine.java new file mode 100644 index 000000000..2ed1928b7 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/jython/JythonEngine.java @@ -0,0 +1,244 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.jython; + +import java.io.ByteArrayInputStream; +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.BSFFunctions; +import org.python.core.Py; +import org.python.core.PyException; +import org.python.core.PyJavaInstance; +import org.python.core.PyObject; +import org.python.util.InteractiveInterpreter; + +/** + * This is the interface to Jython (http://www.jython.org/) from BSF. + * It's derived from the JPython 1.x engine + * + * @author Sanjiva Weerawarana + * @author Finn Bock + * @author Chuck Murcko + */ + +public class JythonEngine extends BSFEngineImpl { + BSFPythonInterpreter interp; + + /** + * call the named method of the given object. + */ + public Object call (Object object, String method, Object[] args) + throws BSFException { + try { + PyObject[] pyargs = Py.EmptyObjects; + + if (args != null) { + pyargs = new PyObject[args.length]; + for (int i = 0; i < pyargs.length; i++) + pyargs[i] = Py.java2py(args[i]); + } + + if (object != null) { + PyObject o = Py.java2py(object); + return unwrap(o.invoke(method, pyargs)); + } + + PyObject m = interp.get(method); + + if (m == null) + m = interp.eval(method); + if (m != null) { + return unwrap(m.__call__(pyargs)); + } + + return null; + } catch (PyException e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "exception from Jython:\n" + e, e); + } + } + + /** + * Declare a bean + */ + public void declareBean (BSFDeclaredBean bean) throws BSFException { + interp.set (bean.name, bean.bean); + } + + /** + * Evaluate an anonymous function (differs from eval() in that apply() + * handles multiple lines). + */ + public Object apply (String source, int lineNo, int columnNo, + Object funcBody, Vector paramNames, + Vector arguments) throws BSFException { + try { + /* We wrapper the original script in a function definition, and + * evaluate the function. A hack, no question, but it allows + * apply() to pretend to work on Jython. + */ + StringBuffer script = new StringBuffer(byteify(funcBody.toString())); + int index = 0; + script.insert(0, "def bsf_temp_fn():\n"); + + while (index < script.length()) { + if (script.charAt(index) == '\n') { + script.insert(index+1, '\t'); + } + index++; + } + + interp.exec (script.toString ()); + + Object result = interp.eval ("bsf_temp_fn()"); + + if (result != null && result instanceof PyJavaInstance) + result = ((PyJavaInstance)result).__tojava__(Object.class); + return result; + } catch (PyException e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "exception from Jython:\n" + e, e); + } + } + + /** + * Evaluate an expression. + */ + public Object eval (String source, int lineNo, int columnNo, + Object script) throws BSFException { + try { + Object result = interp.eval (byteify(script.toString ())); + if (result != null && result instanceof PyJavaInstance) + result = ((PyJavaInstance)result).__tojava__(Object.class); + return result; + } catch (PyException e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "exception from Jython:\n" + e, e); + } + } + + /** + * Execute a script. + */ + public void exec (String source, int lineNo, int columnNo, + Object script) throws BSFException { + try { + interp.exec (byteify(script.toString ())); + } catch (PyException e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "exception from Jython:\n" + e, e); + } + } + + /** + * Execute script code, emulating console interaction. + */ + public void iexec (String source, int lineNo, int columnNo, + Object script) throws BSFException { + String scriptStr = byteify(script.toString()); + int newline = scriptStr.indexOf("\n"); + + if (newline > -1) + scriptStr = scriptStr.substring(0, newline); + + try { + if (interp.buffer.length() > 0) + interp.buffer.append("\n"); + interp.buffer.append(scriptStr); + if (!(interp.runsource(interp.buffer.toString()))) + interp.resetbuffer(); + } catch (PyException e) { + interp.resetbuffer(); + throw new BSFException(BSFException.REASON_EXECUTION_ERROR, + "exception from Jython:\n" + e, e); + } + } + + /** + * Initialize the engine. + */ + public void initialize (BSFManager mgr, String lang, + Vector declaredBeans) throws BSFException { + super.initialize (mgr, lang, declaredBeans); + + // create an interpreter + interp = new BSFPythonInterpreter (); + + // ensure that output and error streams are re-directed correctly + interp.setOut(System.out); + interp.setErr(System.err); + + // register the mgr with object name "bsf" + interp.set ("bsf", new BSFFunctions (mgr, this)); + + // Declare all declared beans to the interpreter + int size = declaredBeans.size (); + for (int i = 0; i < size; i++) { + declareBean ((BSFDeclaredBean) declaredBeans.elementAt (i)); + } + } + + /** + * Undeclare a previously declared bean. + */ + public void undeclareBean (BSFDeclaredBean bean) throws BSFException { + interp.set (bean.name, null); + } + + public Object unwrap(PyObject result) { + if (result != null) { + Object ret = result.__tojava__(Object.class); + if (ret != Py.NoConversion) + return ret; + } + return result; + } + + private String byteify (String orig) { + // Ugh. Jython likes to be fed bytes, rather than the input string. + ByteArrayInputStream bais = + new ByteArrayInputStream(orig.getBytes()); + StringBuffer s = new StringBuffer(); + int c; + + while ((c = bais.read()) >= 0) { + s.append((char)c); + } + + return s.toString(); + } + + private class BSFPythonInterpreter extends InteractiveInterpreter { + + public BSFPythonInterpreter() { + super(); + } + + // Override runcode so as not to print the stack dump + public void runcode(PyObject code) { + try { + this.exec(code); + } catch (PyException exc) { + throw exc; + } + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/netrexx/NetRexxEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/netrexx/NetRexxEngine.java new file mode 100644 index 000000000..40ea27466 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/netrexx/NetRexxEngine.java @@ -0,0 +1,497 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.netrexx; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.BSFFunctions; +import com.volmit.iris.util.bsf.util.EngineUtils; +import com.volmit.iris.util.bsf.util.MethodUtils; +import com.volmit.iris.util.bsf.util.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is the interface to NetRexx from the + * Bean Scripting Framework. + *

+ * The NetRexx code must be written script-style, without a "class" or + * "properties" section preceeding the executable code. The NetRexxEngine will + * generate a prefix for this code: + *

+ * 
+ * class $$CLASSNAME$$;
+ * method BSFNetRexxEngineEntry(bsf=com.volmit.iris.util.bsf.BSFManager) public static;
+ * 
+ * 
+ * $$CLASSNAME$$ will be replaced by a generated classname of the form + * BSFNetRexx*, and the bsf parameter can be used to retrieve application + * objects registered with the Bean Scripting Framework. + *

+ * If you use the placeholder string $$CLASSNAME$$ elsewhere + * in your script -- including within text strings -- BSFNetRexxEngine will + * replace it with the generated name of the class before the NetRexx code + * is compiled. + *

+ * If you need to use full NetRexx functionality, we recommend that your + * NetRexx script define and invoke a "minor class", with or without the + * "dependent" keyword as suits your needs. You'll have to use $$CLASSNAME$$ + * in naming the minor class, since the name of the main class is synthesized; + * for example, to create the minor class "bar" you'd write + * "class $$CLASSNAME$$.Bar". + *

+ *

Hazards:

+ *

+ * Since NetRexx has to be _compiled_ to a Java classfile, invoking it involves + * a fair amount of computation to load and execute the compiler. We are + * currently making an attempt to manage that by caching the class + * after it has been loaded, but the indexing is fairly primitive; we + * hash against the script string to find the class for it. + *

+ * Minor-class .class files are now being deleted after the major class loads. + * This coould potentially cause problems. + * + * @author Joe Kesselman + * @author Sanjiva Weerawarana + */ +public class NetRexxEngine extends BSFEngineImpl +{ + BSFFunctions mgrfuncs; + static Hashtable codeToClass=new Hashtable(); + static String serializeCompilation=""; + static String placeholder="$$CLASSNAME$$"; + String minorPrefix; + + private Log logger = LogFactory.getLog(this.getClass().getName()); + + /** + * Create a scratchfile, open it for writing, return its name. + * Relies on the filesystem to provide us with uniqueness testing. + * NOTE THAT uniqueFileOffset continues to count; we don't want to + * risk reusing a classname we have previously loaded in this session + * even if the classfile has been deleted. + * + * I've made the offset static, due to concerns about reuse/reentrancy + * of the NetRexx engine. + */ + private static int uniqueFileOffset=0; + private class GeneratedFile + { + File file=null; + FileOutputStream fos=null; + String className=null; + GeneratedFile(File file,FileOutputStream fos,String className) + { + this.file=file; + this.fos=fos; + this.className=className; + } + } + + // rexxclass used to be an instance variable, on the theory that + // each NetRexxEngine was an instance of a specific script. + // BSF is currently reusing Engines, so caching the class + // no longer makes sense. + // Class rexxclass; + + /** + * Constructor. + */ + public NetRexxEngine () + { + /* + The following line is intended to cause the constructor to + throw a NoClassDefFoundError if the NetRexxC.zip dependency + is not resolved. + + If this line was not here, the problem would not surface until + the actual processing of a script. We want to know all is well + at the time the engine is instantiated, not when we attempt to + process a script. + */ + + new netrexx.lang.BadArgumentException(); + } + /** + * Return an object from an extension. + * @param object object from which to call our static method + * @param method The name of the method to call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + public Object call (Object object, String method, Object[] args) + throws BSFException + { + throw new BSFException(BSFException.REASON_UNSUPPORTED_FEATURE, + "NetRexx doesn't currently support call()", + null); + } + /** + * Invoke a static method. + * @param rexxclass Class to invoke the method against + * @param method The name of the method to call. + * @param args an array of arguments to be + * passed to the extension, which may be either + * Vectors of Nodes, or Strings. + */ + Object callStatic(Class rexxclass, String method, Object[] args) + throws BSFException + { + //***** ISSUE: Currently supports only static methods + Object retval = null; + try + { + if (rexxclass != null) + { + //***** This should call the lookup used in BML, for typesafety + Class[] argtypes=new Class[args.length]; + for(int i=0;i + * Note that NetRexx compilation imposes serious overhead -- 11 seconds for + * the first compile, about 3 thereafter -- but in exchange you get + * Java-like speeds once the classes have been created (minus the cache + * lookup cost). + *

+ * Nobody knows whether javac is threadsafe. + * I'm going to serialize access to the compilers to protect it. + */ + public Object execEvalShared (String source, int lineNo, int columnNo, + Object oscript,boolean returnsObject) + throws BSFException + { + Object retval=null; + String classname=null; + GeneratedFile gf=null; + + // Moved into the exec process; see comment above. + Class rexxclass=null; + + String basescript=oscript.toString(); + String script=basescript; // May be altered by $$CLASSNAME$$ expansion + + try { + // Do we already have a class exactly matching this code? + rexxclass=(Class)codeToClass.get(basescript); + + if(rexxclass!=null) + + { + logger.debug("NetRexxEngine: Found pre-compiled class" + + " for script '" + basescript + "'"); + classname=rexxclass.getName(); + } + else + { + gf=openUniqueFile(tempDir,"BSFNetRexx",".nrx"); + if(gf==null) + throw new BSFException("couldn't create NetRexx scratchfile"); + + // Obtain classname + classname=gf.className; + + // Decide whether to declare a return type + String returnsDecl=""; + if(returnsObject) + returnsDecl="returns java.lang.Object"; + + // Write the kluge header to the file. + // ***** By doing so we give up the ability to use Property blocks. + gf.fos.write(("class "+classname+";\n") + .getBytes()); + gf.fos.write( + ("method BSFNetRexxEngineEntry(bsf=com.volmit.iris.util.bsf.util.BSFFunctions) "+ + " public static "+returnsDecl+";\n") + .getBytes()); + + // Edit the script to replace placeholder with the generated + // classname. Note that this occurs _after_ the cache was + // checked! + int startpoint,endpoint; + if((startpoint=script.indexOf(placeholder))>=0) + { + StringBuffer changed=new StringBuffer(); + for(; + startpoint>=0; + startpoint=script.indexOf(placeholder,startpoint)) + { + changed.setLength(0); // Reset for 2nd pass or later + if(startpoint>0) + changed.append(script.substring(0,startpoint)); + changed.append(classname); + endpoint=startpoint+placeholder.length(); + if(endpoint0;) + { + file=new File(minor_classfiles[--i]); + file.delete(); + } + } + } + + return retval; + } + public void initialize(BSFManager mgr, String lang,Vector declaredBeans) + throws BSFException + { + super.initialize(mgr, lang, declaredBeans); + mgrfuncs = new BSFFunctions (mgr, this); + } +private GeneratedFile openUniqueFile(String directory,String prefix,String suffix) + { + File file=null,obj=null; + FileOutputStream fos=null; + int max=1000; // Don't try forever + GeneratedFile gf=null; + int i; + String className = null; + for(i=max,++uniqueFileOffset; + fos==null && i>0; + --i,++uniqueFileOffset) + { + // Probably a timing hazard here... *************** + try + { + className = prefix+uniqueFileOffset; + file=new File(directory+File.separatorChar+className+suffix); + obj=new File(directory+File.separatorChar+className+".class"); + if(file!=null && !file.exists() & obj!=null & !obj.exists()) + fos=new FileOutputStream(file); + } + catch(Exception e) + { + // File could not be opened for write, or Security Exception + // was thrown. If someone else created the file before we could + // open it, that's probably a threading conflict and we don't + // bother reporting it. + if(!file.exists()) + { + logger.error("openUniqueFile: unexpected "+e); + } + } + } + if(fos==null) + logger.error("openUniqueFile: Failed "+max+"attempts."); + else + gf=new GeneratedFile(file,fos,className); + return gf; + } + + public void undeclareBean (BSFDeclaredBean bean) throws BSFException {} +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTEngine.java b/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTEngine.java new file mode 100644 index 000000000..893ab62c3 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTEngine.java @@ -0,0 +1,193 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.xslt; + +import java.io.File; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; +import java.util.Vector; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamSource; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.BSFEngineImpl; +import com.volmit.iris.util.bsf.util.BSFFunctions; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xpath.objects.XObject; +import org.w3c.dom.Node; + +/** + * Xerces XSLT interface to BSF. Requires Xalan and Xerces from Apache. + * + * This integration uses the BSF registry to pass in any src document + * and stylesheet base URI that the user may wish to set. + * + * @author Sanjiva Weerawarana + * @author Sam Ruby + * + * Re-implemented for the Xalan 2 codebase + * + * @author Victor J. Orlikowski + */ +public class XSLTEngine extends BSFEngineImpl { + TransformerFactory tFactory; + Transformer transformer; + + Log logger = LogFactory.getLog(this.getClass().getName()); + + /** + * call the named method of the given object. + */ + public Object call (Object object, String method, Object[] args) + throws BSFException { + throw new BSFException (BSFException.REASON_UNSUPPORTED_FEATURE, + "BSF:XSLTEngine can't call methods"); + } + + /** + * Declare a bean by setting it as a parameter + */ + public void declareBean (BSFDeclaredBean bean) throws BSFException { + transformer.setParameter (bean.name, new XObject (bean.bean)); + } + + /** + * Evaluate an expression. In this case, an expression is assumed + * to be a stylesheet of the template style (see the XSLT spec). + */ + public Object eval (String source, int lineNo, int columnNo, + Object oscript) throws BSFException { + // get the style base URI (the place from where Xerces XSLT will + // look for imported/included files and referenced docs): if a + // bean named "xslt:styleBaseURI" is registered, then cvt it + // to a string and use that. Otherwise use ".", which means the + // base is the directory where the process is running from + Object sbObj = mgr.lookupBean ("xslt:styleBaseURI"); + String styleBaseURI = (sbObj == null) ? "." : sbObj.toString (); + + // Locate the stylesheet. + StreamSource styleSource; + + styleSource = + new StreamSource(new StringReader(oscript.toString ())); + styleSource.setSystemId(styleBaseURI); + + try { + transformer = tFactory.newTransformer(styleSource); + } catch (Exception e) { + logger.error("Exception from Xerces XSLT:", e); + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "Exception from Xerces XSLT: " + e, e); + } + + // get the src to work on: if a bean named "xslt:src" is registered + // and its a Node, then use it as the source. If its not a Node, then + // if its a URL parse it, if not treat it as a file and make a URL and + // parse it and go. If no xslt:src is found, use an empty document + // (stylesheet is treated as a literal result element stylesheet) + Object srcObj = mgr.lookupBean ("xslt:src"); + Object xis = null; + if (srcObj != null) { + if (srcObj instanceof Node) { + xis = new DOMSource((Node)srcObj); + } else { + try { + String mesg = "as anything"; + if (srcObj instanceof Reader) { + xis = new StreamSource ((Reader) srcObj); + mesg = "as a Reader"; + } else if (srcObj instanceof File) { + xis = new StreamSource ((File) srcObj); + mesg = "as a file"; + } else { + String srcObjstr=srcObj.toString(); + xis = new StreamSource (new StringReader(srcObjstr)); + if (srcObj instanceof URL) { + mesg = "as a URL"; + } else { + ((StreamSource) xis).setPublicId (srcObjstr); + mesg = "as an XML string"; + } + } + + if (xis == null) { + throw new Exception ("Unable to get input from '" + + srcObj + "' " + mesg); + } + } catch (Exception e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "BSF:XSLTEngine: unable to get " + + "input from '" + srcObj + "' as XML", e); + } + } + } else { + // create an empty document - real src must come into the + // stylesheet using "doc(...)" [see XSLT spec] or the stylesheet + // must be of literal result element type + xis = new StreamSource(); + } + + // set all declared beans as parameters. + for (int i = 0; i < declaredBeans.size (); i++) { + BSFDeclaredBean b = (BSFDeclaredBean) declaredBeans.elementAt (i); + transformer.setParameter (b.name, new XObject (b.bean)); + } + + // declare a "bsf" parameter which is the BSF handle so that + // the script can do BSF stuff if it wants to + transformer.setParameter ("bsf", + new XObject (new BSFFunctions (mgr, this))); + + // do it + try { + DOMResult result = new DOMResult(); + transformer.transform ((StreamSource) xis, result); + return new XSLTResultNode (result.getNode()); + } catch (Exception e) { + throw new BSFException (BSFException.REASON_EXECUTION_ERROR, + "exception while eval'ing XSLT script" + e, e); + } + } + + /** + * Initialize the engine. + */ + public void initialize (BSFManager mgr, String lang, + Vector declaredBeans) throws BSFException { + super.initialize (mgr, lang, declaredBeans); + + tFactory = TransformerFactory.newInstance(); + } + + /** + * Undeclare a bean by setting he parameter represeting it to null + */ + public void undeclareBean (BSFDeclaredBean bean) throws BSFException { + // Cannot clear only one parameter in Xalan 2, so we set it to null + if ((transformer.getParameter (bean.name)) != null) { + transformer.setParameter (bean.name, null); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTResultNode.java b/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTResultNode.java new file mode 100644 index 000000000..612ded3f7 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/engines/xslt/XSLTResultNode.java @@ -0,0 +1,33 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.engines.xslt; + +import org.w3c.dom.Node; + +public class XSLTResultNode +{ + Node node; + + public XSLTResultNode(Node node) + { + this.node = node; + } + public Node getNode() + { + return node; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/BSFClassLoader.java b/src/main/java/com/volmit/iris/util/bsf/util/BSFClassLoader.java new file mode 100644 index 000000000..557838e8d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/BSFClassLoader.java @@ -0,0 +1,74 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Hashtable; + +/** + * This class loader knows to load a class from the tempDir dir + * of the environment of the given manager. + * + * @author Sanjiva Weerawarana + */ +class BSFClassLoader extends ClassLoader { + Hashtable cache = new Hashtable (); + String tempDir = "."; + + // note the non-public constructor - this is only avail within + // this package. + BSFClassLoader () { + } + public synchronized Class loadClass (String name, boolean resolve) + throws ClassNotFoundException { + Class c = (Class) cache.get (name); + if (c == null) { + // is it a system class + try { + c = findSystemClass (name); + cache.put (name, c); + return c; + } catch (ClassNotFoundException e) { + // nope + } + try { + byte[] data = loadClassData (name); + c = defineClass (name, data, 0, data.length); + cache.put (name, c); + } catch (Exception e) { + e.printStackTrace (); + throw new ClassNotFoundException ("unable to resolve class '" + + name + "'"); + } + } + if (resolve) + resolveClass (c); + return c; + } + private byte[] loadClassData (String name) throws Exception { + String fileName = tempDir + File.separatorChar + name + ".class"; + FileInputStream fi = new FileInputStream (fileName); + byte[] data = new byte[fi.available ()]; + fi.read (data); + fi.close(); + return data; + } + public void setTempDir (String tempDir) { + this.tempDir = tempDir; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/BSFEngineImpl.java b/src/main/java/com/volmit/iris/util/bsf/util/BSFEngineImpl.java new file mode 100644 index 000000000..d38dead04 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/BSFEngineImpl.java @@ -0,0 +1,203 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.beans.PropertyChangeEvent; +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFDeclaredBean; +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; + +/** + * This is a base implementation of the BSFEngine interface which + * engine implementations may choose to extend to get the basic + * methods of the interface implemented. + *

+ * + * @author Sanjiva Weerawarana + * @author Olivier Gruber (added original debugging support) + */ + +public abstract class BSFEngineImpl implements BSFEngine { + + protected BSFManager mgr; // my manager + protected String lang; // my language string + protected Vector declaredBeans; // BSFDeclaredBeans + protected String classPath; + protected String tempDir; + protected ClassLoader classLoader; + + /** + * Default impl of apply - calls eval ignoring parameters and returns + * the result. + */ + public Object apply(String source, int lineNo, int columnNo, + Object funcBody, Vector paramNames, Vector arguments) + throws BSFException { + return eval(source, lineNo, columnNo, funcBody); + } + + /** + * Default impl of compileApply - calls compileExpr ignoring parameters. + */ + public void compileApply(String source, int lineNo, int columnNo, + Object funcBody, Vector paramNames, + Vector arguments, CodeBuffer cb) + throws BSFException { + compileExpr(source, lineNo, columnNo, funcBody, cb); + } + + /** + * Default impl of compileExpr - generates code that'll create a new + * manager, evaluate the expression, and return the value. + */ + public void compileExpr(String source, int lineNo, int columnNo, + Object expr, CodeBuffer cb) throws BSFException { + ObjInfo bsfInfo = cb.getSymbol("bsf"); + + if (bsfInfo == null) { + bsfInfo = new ObjInfo(BSFManager.class, "bsf"); + cb.addFieldDeclaration("com.volmit.iris.util.bsf.BSFManager bsf = " + + "new com.volmit.iris.util.bsf.BSFManager();"); + cb.putSymbol("bsf", bsfInfo); + } + + String evalString = bsfInfo.objName + ".eval(\"" + lang + "\", "; + evalString += "request.getRequestURI(), " + lineNo + ", " + columnNo; + evalString += "," + StringUtils.lineSeparator; + evalString += StringUtils.getSafeString(expr.toString()) + ")"; + + ObjInfo oldRet = cb.getFinalServiceMethodStatement(); + + if (oldRet != null && oldRet.isExecutable()) { + cb.addServiceMethodStatement(oldRet.objName + ";"); + } + + cb.setFinalServiceMethodStatement(new ObjInfo(Object.class, + evalString)); + + cb.addServiceMethodException("com.volmit.iris.util.bsf.BSFException"); + } + + /** + * Default impl of compileScript - generates code that'll create a new + * manager, and execute the script. + */ + public void compileScript(String source, int lineNo, int columnNo, + Object script, CodeBuffer cb) + throws BSFException { + ObjInfo bsfInfo = cb.getSymbol("bsf"); + + if (bsfInfo == null) { + bsfInfo = new ObjInfo(BSFManager.class, "bsf"); + cb.addFieldDeclaration("com.volmit.iris.util.bsf.BSFManager bsf = " + + "new com.volmit.iris.util.bsf.BSFManager();"); + cb.putSymbol("bsf", bsfInfo); + } + + String execString = bsfInfo.objName + ".exec(\"" + lang + "\", "; + execString += "request.getRequestURI(), " + lineNo + ", " + columnNo; + execString += "," + StringUtils.lineSeparator; + execString += StringUtils.getSafeString(script.toString()) + ")"; + + ObjInfo oldRet = cb.getFinalServiceMethodStatement(); + + if (oldRet != null && oldRet.isExecutable()) { + cb.addServiceMethodStatement(oldRet.objName + ";"); + } + + cb.setFinalServiceMethodStatement(new ObjInfo(void.class, execString)); + + cb.addServiceMethodException("com.volmit.iris.util.bsf.BSFException"); + } + + public void declareBean(BSFDeclaredBean bean) throws BSFException { + throw new BSFException(BSFException.REASON_UNSUPPORTED_FEATURE, + "language " + lang + + " does not support declareBean(...)."); + } + + /** + * Default impl of execute - calls eval and ignores the result. + */ + public void exec(String source, int lineNo, int columnNo, Object script) + throws BSFException { + eval(source, lineNo, columnNo, script); + } + + /** + * Default impl of interactive execution - calls eval and ignores the result. + */ + public void iexec(String source, int lineNo, int columnNo, Object script) + throws BSFException { + eval(source, lineNo, columnNo, script); + } + + /** + * initialize the engine; called right after construction by + * the manager. Declared beans are simply kept in a vector and + * that's it. Subclasses must do whatever they want with it. + */ + public void initialize(BSFManager mgr, String lang, Vector declaredBeans) + throws BSFException { + + this.mgr = mgr; + this.lang = lang; + this.declaredBeans = declaredBeans; + + // initialize my properties from those of the manager. It'll send + // propagate change events to me + this.classPath = mgr.getClassPath(); + this.tempDir = mgr.getTempDir(); + this.classLoader = mgr.getClassLoader(); + } + + /** + * Receive property change events from the manager and update my fields + * as needed. + * + * @param e PropertyChange event with the change data + */ + public void propertyChange(PropertyChangeEvent e) { + String name = e.getPropertyName(); + Object value = e.getNewValue(); + + if (name.equals("classPath")) { + classPath = (String) value; + } + else if (name.equals("tempDir")) { + tempDir = (String) value; + } + else if (name.equals("classLoader")) { + classLoader = (ClassLoader) value; + } + } + + public void terminate() { + mgr = null; + declaredBeans = null; + classLoader = null; + } + + public void undeclareBean(BSFDeclaredBean bean) throws BSFException { + throw new BSFException(BSFException.REASON_UNSUPPORTED_FEATURE, + "language " + lang + + " does not support undeclareBean(...)."); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessor.java b/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessor.java new file mode 100644 index 000000000..b59baca29 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessor.java @@ -0,0 +1,92 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.event.EventProcessor; + +/** + * This is used to support binding scripts to be run when an event + * occurs. + * + * @author Sanjiva Weerawarana + */ +public class BSFEventProcessor implements EventProcessor { + BSFEngine engine; + BSFManager manager; + String filter; + String source; + int lineNo; + int columnNo; + Object script; + + /** + * Package-protected constructor makes this class unavailable for + * public use. + */ + BSFEventProcessor (BSFEngine engine, BSFManager manager, String filter, + String source, int lineNo, int columnNo, Object script) + throws BSFException { + this.engine = engine; + this.manager = manager; + this.filter = filter; + this.source = source; + this.lineNo = lineNo; + this.columnNo = columnNo; + this.script = script; + } + ////////////////////////////////////////////////////////////////////////// + // + // event is delegated to me by the adapters using this. inFilter is + // in general the name of the method via which the event was received + // at the adapter. For prop/veto change events, inFilter is the name + // of the property. In any case, in the event processor, I only forward + // those events if for which the filters match (if one is specified). + + public void processEvent (String inFilter, Object[] evtInfo) { + try { + processExceptionableEvent (inFilter, evtInfo); + } catch (RuntimeException re) { + // rethrow this .. I don't want to intercept run-time stuff + // that can in fact occur legit + throw re; + } catch (Exception e) { + // should not occur + System.err.println ("BSFError: non-exceptionable event delivery " + + "threw exception (that's not nice): " + e); + e.printStackTrace (); + } + } + ////////////////////////////////////////////////////////////////////////// + // + // same as above, but used when the method event method may generate + // an exception which must go all the way back to the source (as in + // the vetoableChange case) + + public void processExceptionableEvent (String inFilter, Object[] evtInfo) throws Exception + { + if ((filter != null) && !filter.equals (inFilter)) { + // ignore this event + return; + } + + // run the script + engine.exec (source, lineNo, columnNo, script); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessorReturningEventInfos.java b/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessorReturningEventInfos.java new file mode 100644 index 000000000..a8aac16b1 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/BSFEventProcessorReturningEventInfos.java @@ -0,0 +1,164 @@ +/* + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * Sanjiva Weerawarana and others at International Business Machines + * Corporation. For more information on the Apache Software Foundation, + * please see . + */ + +package com.volmit.iris.util.bsf.util; + +import java.util.Vector; + +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; +import com.volmit.iris.util.bsf.util.event.EventProcessor; + +/* + * Copyright (C) 2001-2006 Rony G. Flatscher + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + */ + +/** + * + * This is used to support binding scripts to be run when an event occurs, + * forwarding the arguments supplied to the event listener. It is an adapted + * version of com.volmit.iris.util.bsf.util.BSFEventProcessor. + * + * @author Rony G. Flatscher, but most of the code copied from + * com.volmit.iris.util.bsf.util.BSFEventProcessor by Sanjiva Weerawarana + */ +public class BSFEventProcessorReturningEventInfos implements EventProcessor { + BSFEngine engine; + + BSFManager manager; + + String filter; + + String source; + + int lineNo; + + int columnNo; + + Object script; + + Object dataFromScriptingEngine; // ---rgf, 2006-02-24: data coming from the + // script engine, could be + + // e.g. an object reference to forward event with received arguments to + + /** + * Package-protected constructor makes this class unavailable for public + * use. + * + * @param dataFromScriptingEngine + * this contains any object supplied by the scripting engine and + * gets sent back with the supplied script. This could be used + * e.g. for indicating which scripting engine object should be + * ultimately informed of the event occurrence. + */ + BSFEventProcessorReturningEventInfos(BSFEngine engine, BSFManager manager, + String filter, String source, int lineNo, int columnNo, + Object script, Object dataFromScriptingEngine) throws BSFException { + this.engine = engine; + this.manager = manager; + this.filter = filter; + this.source = source; + this.lineNo = lineNo; + this.columnNo = columnNo; + this.script = script; + this.dataFromScriptingEngine = dataFromScriptingEngine; + } + + // //////////////////////////////////////////////////////////////////////// + // + // event is delegated to me by the adapters using this. inFilter is + // in general the name of the method via which the event was received + // at the adapter. For prop/veto change events, inFilter is the name + // of the property. In any case, in the event processor, I only forward + // those events if for which the filters match (if one is specified). + + public void processEvent(String inFilter, Object[] evtInfo) { + try { + processExceptionableEvent(inFilter, evtInfo); + } catch (RuntimeException re) { + // rethrow this .. I don't want to intercept run-time stuff + // that can in fact occur legit + throw re; + } catch (Exception e) { + // should not occur + System.err.println("BSFError: non-exceptionable event delivery " + + "threw exception (that's not nice): " + e); + e.printStackTrace(); + } + } + + // //////////////////////////////////////////////////////////////////////// + // + // same as above, but used when the method event method may generate + // an exception which must go all the way back to the source (as in + // the vetoableChange case) + + public void processExceptionableEvent(String inFilter, Object[] evtInfo) + throws Exception { + + // System.err.println(this+": inFilter=["+inFilter+"], + // filter=["+filter+"]"); + if ((filter != null) && !filter.equals(inFilter)) { + // ignore this event + return; + } + + // run the script + // engine.exec (source, lineNo, columnNo, script); + + // create the parameter vectors for engine.apply() + Vector paramNames = new Vector(), paramValues = new Vector(); + + // parameter # 1 + // supply the parameters as an array object as sent to the event object + // listener + // (usually the first entry is the sent event object) + paramNames.add("eventParameters"); + paramValues.add(evtInfo); + + // parameter # 2 + // supply the data object received from the scripting engine to be sent + // with the event + paramNames.add("dataFromScriptingEngine"); + paramValues.add(this.dataFromScriptingEngine); // can be null as well + + // parameter # 3 + // event filter in place + paramNames.add("inFilter"); + paramValues.add(inFilter); // event name that has occurred + + // parameter # 4 + // event filter in place + paramNames.add("eventFilter"); + paramValues.add(this.filter); // can be null as well + + // parameter # 5 + // BSF manager instance (e.g. allows access to its registry) + paramNames.add("BSFManager"); + paramValues.add(this.manager); + + engine.apply(source, lineNo, columnNo, this.script, paramNames, + paramValues); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/BSFFunctions.java b/src/main/java/com/volmit/iris/util/bsf/util/BSFFunctions.java new file mode 100644 index 000000000..08ded56ec --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/BSFFunctions.java @@ -0,0 +1,54 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; + +/** + * This is a utility that engine implementors may use as the Java + * object they expose in the scripting language as "bsf". This has + * essentially a subset of the methods in BSFManager plus some + * stuff from the utils. Currently used by Javascript (Rhino) & BML. + * + * @author Sanjiva Weerawarana + */ +public class BSFFunctions { + BSFManager mgr; + BSFEngine engine; + + public BSFFunctions (BSFManager mgr, BSFEngine engine) { + this.mgr = mgr; + this.engine = engine; + } + public void addEventListener (Object src, String eventSetName, + String filter, Object script) + throws BSFException { + EngineUtils.addEventListener (src, eventSetName, filter, engine, + mgr, "", 0, 0, script); + } + public Object lookupBean (String name) { + return mgr.lookupBean (name); + } + public void registerBean (String name, Object bean) { + mgr.registerBean (name, bean); + } + public void unregisterBean (String name) { + mgr.unregisterBean (name); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/Bean.java b/src/main/java/com/volmit/iris/util/bsf/util/Bean.java new file mode 100644 index 000000000..d6fdd752f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/Bean.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +/** + * A Bean is the class used to represent a bean: it holds a + * type and a value. This is needed because otherwise we can't represent + * the types of null-valued beans (or primitives) correctly. This was + * originally in the BML player. + * + * @author Sanjiva Weerawarana + */ +public class Bean { + // type of this bean + public Class type; + + // its current value (mebbe null) + public Object value; + + public Bean (Class type, Object value) { + this.type = type; + this.value = value; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/CodeBuffer.java b/src/main/java/com/volmit/iris/util/bsf/util/CodeBuffer.java new file mode 100644 index 000000000..e38a12582 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/CodeBuffer.java @@ -0,0 +1,479 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Hashtable; +import java.util.Stack; +import java.util.Vector; + +import com.volmit.iris.util.bsf.util.cf.CodeFormatter; + +/** + * A CodeBuffer object is used as a code repository for generated Java code. + * It provides buffers which correspond to the various sections of a Java class. + * + * @author Matthew J. Duftler + */ +public class CodeBuffer +{ + private StringWriter fieldDeclSW = new StringWriter(), + methodDeclSW = new StringWriter(), + initializerSW = new StringWriter(), + constructorSW = new StringWriter(), + serviceMethodSW = new StringWriter(); + + private PrintWriter fieldDeclPW = new PrintWriter(fieldDeclSW), + methodDeclPW = new PrintWriter(methodDeclSW), + initializerPW = new PrintWriter(initializerSW), + constructorPW = new PrintWriter(constructorSW), + serviceMethodPW = new PrintWriter(serviceMethodSW); + + private Stack symbolTableStack = new Stack(); + private Hashtable symbolTable = new Hashtable(), + usedSymbolIndices = new Hashtable(); + + private ObjInfo finalStatementInfo; + private CodeBuffer parent; + + + { + symbolTableStack.push(symbolTable); + } + + // New stuff... + private Vector imports = new Vector(), + constructorArguments = new Vector(), + constructorExceptions = new Vector(), + serviceMethodExceptions = new Vector(), + implementsVector = new Vector(); + private String packageName = null, + className = "Test", + serviceMethodName = "exec", + extendsName = null; + private Class serviceMethodReturnType = void.class; + + public CodeBuffer() + { + } + public CodeBuffer(CodeBuffer parent) + { + this.parent = parent; + } + public void addConstructorArgument(ObjInfo arg) + { + constructorArguments.addElement(arg); + } + public void addConstructorException(String exceptionName) + { + if (!constructorExceptions.contains(exceptionName)) + { + constructorExceptions.addElement(exceptionName); + } + } + public void addConstructorStatement(String statement) + { + constructorPW.println(statement); + } + public void addFieldDeclaration(String statement) + { + fieldDeclPW.println(statement); + } + public void addImplements(String importName) + { + if (!implementsVector.contains(importName)) + { + implementsVector.addElement(importName); + } + } + public void addImport(String importName) + { + if (!imports.contains(importName)) + { + imports.addElement(importName); + } + } + public void addInitializerStatement(String statement) + { + initializerPW.println(statement); + } + public void addMethodDeclaration(String statement) + { + methodDeclPW.println(statement); + } + public void addServiceMethodException(String exceptionName) + { + if (!serviceMethodExceptions.contains(exceptionName)) + { + serviceMethodExceptions.addElement(exceptionName); + } + } + public void addServiceMethodStatement(String statement) + { + serviceMethodPW.println(statement); + } + // Used internally by merge(...). + private void appendIfNecessary(PrintWriter pw, StringBuffer buf) + { + if (buf.length() > 0) + { + pw.print(buf.toString()); + } + } + public String buildNewSymbol(String prefix) + { + Integer nextNum = getSymbolIndex(prefix); + + if (nextNum == null) + { + nextNum = new Integer(0); + } + + int iNextNum = nextNum.intValue(); + String symbol = prefix + "_" + iNextNum; + + while (getSymbol(symbol) != null) + { + iNextNum++; + symbol = prefix + "_" + iNextNum; + } + + putSymbolIndex(prefix, new Integer(iNextNum + 1)); + + return symbol; + } + public void clearSymbolTable() + { + symbolTable = new Hashtable(); + symbolTableStack = new Stack(); + symbolTableStack.push(symbolTable); + + usedSymbolIndices = new Hashtable(); + } + public String getClassName() + { + return className; + } + public Vector getConstructorArguments() + { + return constructorArguments; + } + public StringBuffer getConstructorBuffer() + { + constructorPW.flush(); + + return constructorSW.getBuffer(); + } + public Vector getConstructorExceptions() + { + return constructorExceptions; + } + public String getExtends() + { + return extendsName; + } + public StringBuffer getFieldBuffer() + { + fieldDeclPW.flush(); + + return fieldDeclSW.getBuffer(); + } + public ObjInfo getFinalServiceMethodStatement() + { + return finalStatementInfo; + } + public Vector getImplements() + { + return implementsVector; + } + public Vector getImports() + { + return imports; + } + public StringBuffer getInitializerBuffer() + { + initializerPW.flush(); + + return initializerSW.getBuffer(); + } + public StringBuffer getMethodBuffer() + { + methodDeclPW.flush(); + + return methodDeclSW.getBuffer(); + } + public String getPackageName() + { + return packageName; + } + public StringBuffer getServiceMethodBuffer() + { + serviceMethodPW.flush(); + + return serviceMethodSW.getBuffer(); + } + public Vector getServiceMethodExceptions() + { + return serviceMethodExceptions; + } + public String getServiceMethodName() + { + return serviceMethodName; + } + public Class getServiceMethodReturnType() + { + if (finalStatementInfo != null) + { + return finalStatementInfo.objClass; + } + else if (serviceMethodReturnType != null) + { + return serviceMethodReturnType; + } + else + { + return void.class; + } + } + public ObjInfo getSymbol(String symbol) + { + ObjInfo ret = (ObjInfo)symbolTable.get(symbol); + + if (ret == null && parent != null) + ret = parent.getSymbol(symbol); + + return ret; + } + Integer getSymbolIndex(String prefix) + { + if (parent != null) + { + return parent.getSymbolIndex(prefix); + } + else + { + return (Integer)usedSymbolIndices.get(prefix); + } + } + public Hashtable getSymbolTable() + { + return symbolTable; + } + public void merge(CodeBuffer otherCB) + { + Vector otherImports = otherCB.getImports(); + + for (int i = 0; i < otherImports.size(); i++) + { + addImport((String)otherImports.elementAt(i)); + } + + appendIfNecessary(fieldDeclPW, otherCB.getFieldBuffer()); + appendIfNecessary(methodDeclPW, otherCB.getMethodBuffer()); + appendIfNecessary(initializerPW, otherCB.getInitializerBuffer()); + appendIfNecessary(constructorPW, otherCB.getConstructorBuffer()); + appendIfNecessary(serviceMethodPW, otherCB.getServiceMethodBuffer()); + + ObjInfo oldRet = getFinalServiceMethodStatement(); + + if (oldRet != null && oldRet.isExecutable()) + { + addServiceMethodStatement(oldRet.objName + ";"); + } + + setFinalServiceMethodStatement(otherCB.getFinalServiceMethodStatement()); + } + public void popSymbolTable() + { + symbolTableStack.pop(); + symbolTable = (Hashtable)symbolTableStack.peek(); + } + public void print(PrintWriter out, boolean formatOutput) + { + if (formatOutput) + { + new CodeFormatter().formatCode(new StringReader(toString()), out); + } + else + { + out.print(toString()); + } + + out.flush(); + } + public void pushSymbolTable() + { + symbolTable = (Hashtable)symbolTableStack.push(new ScriptSymbolTable(symbolTable)); + } + public void putSymbol(String symbol, ObjInfo obj) + { + symbolTable.put(symbol, obj); + } + void putSymbolIndex(String prefix, Integer index) + { + if (parent != null) + { + parent.putSymbolIndex(prefix, index); + } + else + { + usedSymbolIndices.put(prefix, index); + } + } + public void setClassName(String className) + { + this.className = className; + } + public void setExtends(String extendsName) + { + this.extendsName = extendsName; + } + public void setFinalServiceMethodStatement(ObjInfo finalStatementInfo) + { + this.finalStatementInfo = finalStatementInfo; + } + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + public void setServiceMethodName(String serviceMethodName) + { + this.serviceMethodName = serviceMethodName; + } + public void setServiceMethodReturnType(Class serviceMethodReturnType) + { + this.serviceMethodReturnType = serviceMethodReturnType; + } + public void setSymbolTable(Hashtable symbolTable) + { + this.symbolTable = symbolTable; + } + public boolean symbolTableIsStacked() + { + return (symbolTable instanceof ScriptSymbolTable); + } + public String toString() + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ObjInfo ret = finalStatementInfo; + + if (packageName != null && !packageName.equals("")) + { + pw.println("package " + packageName + ";"); + pw.println(); + } + + if (imports.size() > 0) + { + for (int i = 0; i < imports.size(); i++) + { + pw.println("import " + imports.elementAt(i) + ";"); + } + + pw.println(); + } + + pw.println("public class " + className + + (extendsName != null && !extendsName.equals("") + ? " extends " + extendsName + : "") + + (implementsVector.size() > 0 + ? " implements " + + StringUtils.getCommaListFromVector(implementsVector) + : "") + ); + pw.println("{"); + + pw.print(getFieldBuffer().toString()); + + StringBuffer buf = getInitializerBuffer(); + + if (buf.length() > 0) + { + pw.println(); + pw.println("{"); + pw.print(buf.toString()); + pw.println("}"); + } + + buf = getConstructorBuffer(); + + if (buf.length() > 0) + { + pw.println(); + pw.println("public " + className + "(" + + (constructorArguments.size() > 0 + ? StringUtils.getCommaListFromVector(constructorArguments) + : "" + ) + ")" + + (constructorExceptions.size() > 0 + ? " throws " + + StringUtils.getCommaListFromVector(constructorExceptions) + : "" + ) + ); + pw.println("{"); + pw.print(buf.toString()); + pw.println("}"); + } + + buf = getServiceMethodBuffer(); + + if (buf.length() > 0 || ret != null) + { + pw.println(); + pw.println("public " + + StringUtils.getClassName(getServiceMethodReturnType()) + " " + + serviceMethodName + "()" + + (serviceMethodExceptions.size() > 0 + ? " throws " + + StringUtils.getCommaListFromVector(serviceMethodExceptions) + : "" + ) + ); + pw.println("{"); + + pw.print(buf.toString()); + + if (ret != null) + { + if (ret.isValueReturning()) + { + pw.println(); + pw.println("return " + ret.objName + ";"); + } + else if (ret.isExecutable()) + { + pw.println(ret.objName + ";"); + } + } + + pw.println("}"); + } + + pw.print(getMethodBuffer().toString()); + + pw.println("}"); + + pw.flush(); + + return sw.toString(); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/EngineUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/EngineUtils.java new file mode 100644 index 000000000..90e32f73f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/EngineUtils.java @@ -0,0 +1,379 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import com.volmit.iris.util.bsf.BSFEngine; +import com.volmit.iris.util.bsf.BSFException; +import com.volmit.iris.util.bsf.BSFManager; + +/** + * This class contains utilities that language integrators can use + * when implementing the BSFEngine interface. + * + * @author Sanjiva Weerawarana + * @author Sam Ruby + * @author Rony G. Flatscher (added addEventListenerReturningEventInfos) + */ +public class EngineUtils { + // the BSF class loader that knows how to load from the a specific + // temp directory + static BSFClassLoader bsfCL; + + // ---rgf, 2003-02-13, determine whether changing accessibility of Methods is possible + static boolean bMethodHasSetAccessible=false; + static { + Class mc=Method.class; // get the "Method" class object + Class arg[]={boolean.class}; // define an array with the primitive "boolean" pseudo class object + try { + mc.getMethod("setAccessible", arg ); // is this method available? + bMethodHasSetAccessible=true; // no exception, hence method exists + } + catch (Exception e) + { + bMethodHasSetAccessible=false;// exception occurred, hence method does not exist + } + } + + + /** + * Add a script as a listener to some event coming out of an object. The + * first two args identify the src of the event and the event set + * and the rest identify the script which should be run when the event + * fires. + * + * @param bean event source + * @param eventSetName name of event set from event src to bind to + * @param filter filter for events + * @param engine BSFEngine which can run this script + * @param manager BSFManager of the above engine + * @param source (context info) the source of this expression + * (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute when the event occurs + * + * @exception BSFException if anything goes wrong while running the script + */ + public static void addEventListener (Object bean, String eventSetName, + String filter, BSFEngine engine, + BSFManager manager, String source, + int lineNo, int columnNo, + Object script) throws BSFException { + BSFEventProcessor ep = new BSFEventProcessor (engine, manager, filter, + source, lineNo, columnNo, + script); + + try { + ReflectionUtils.addEventListener (bean, eventSetName, ep); + } catch (Exception e) { + e.printStackTrace (); + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "ouch while adding event listener: " + + e, e); + } + } + + + /** + * Add a script as a listener to some event coming out of an object. The + * first two args identify the src of the event and the event set + * and the rest identify the script which should be run when the event + * fires. The processing will use the engine's apply() method. + * + * @param bean event source + * @param eventSetName name of event set from event src to bind to + * @param filter filter for events + * @param engine BSFEngine which can run this script + * @param manager BSFManager of the above engine + * @param source (context info) the source of this expression (e.g., filename) + * @param lineNo (context info) the line number in source for expr + * @param columnNo (context info) the column number in source for expr + * @param script the script to execute when the event occurs + * @param dataFromScriptingEngine + * this contains any object supplied by the scripting engine and gets sent + * back with the supplied script, if the event occurs. + * This could be used e.g. for indicating to the scripting engine which + * scripting engine object/routine/function/procedure + * should be ultimately informed of the event occurrence. + * + * @exception BSFException if anything goes wrong while running the script + */ + public static void addEventListenerReturningEventInfos ( Object bean, + String eventSetName, + String filter, + BSFEngine engine, + BSFManager manager, + String source, + int lineNo, + int columnNo, + Object script, + Object dataFromScriptingEngine + ) throws BSFException + { + BSFEventProcessorReturningEventInfos ep = + new BSFEventProcessorReturningEventInfos (engine, + manager, + filter, + source, + lineNo, + columnNo, + script, + dataFromScriptingEngine + ); + + try { + ReflectionUtils.addEventListener (bean, eventSetName, ep); + } catch (Exception e) { + e.printStackTrace (); + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "ouch while adding event listener: " + + e, e); + } + } + + /** + * Finds and invokes a method with the given signature on the given + * bean. The signature of the method that's invoked is first taken + * as the types of the args, but if that fails, this tries to convert + * any primitive wrapper type args to their primitive counterparts + * to see whether a method exists that way. If it does, done. + * + * @param bean the object on which to invoke the method + * @param methodName name of the method + * @param args arguments to be given to the method + * + * @return the result of invoking the method, if any + * + * @exception BSFException if something goes wrong + */ + public static Object callBeanMethod (Object bean, String methodName, + Object[] args) throws BSFException { + Class[] argTypes = null; + // determine arg types. note that a null argtype + // matches any object type + + if (args != null) { + argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = (args[i] == null) ? null : args[i].getClass (); + } + } + + // we want to allow a static call to occur on an object, similar + // to what Java allows. So isStaticOnly is set to false. + boolean isStaticOnly = false; + Class beanClass = (bean instanceof Class) ? (Class)bean : + bean.getClass (); + + // now try to call method with the right signature + try { + Method m; + try { + m = MethodUtils.getMethod (beanClass, methodName, argTypes, + isStaticOnly); + } catch (NoSuchMethodException e) { + // ok, so that didn't work - now try converting any primitive + // wrapper types to their primitive counterparts + try { + // if args is null the NullPointerException will get caught + // below and the right thing'll happen .. ugly but works + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof Number) + { + if (args[i] instanceof Byte) argTypes[i] = byte.class; + else if (args[i] instanceof Integer) argTypes[i] = int.class; + else if (args[i] instanceof Long) argTypes[i] = long.class; + else if (args[i] instanceof Float) argTypes[i] = float.class; + else if (args[i] instanceof Double ) argTypes[i] = double.class; + else if (args[i] instanceof Short ) argTypes[i] = short.class; + } + else if (args[i] instanceof Boolean) argTypes[i] = boolean.class; + else if (args[i] instanceof Character) argTypes[i] = char.class; + } + + m = MethodUtils.getMethod (beanClass, methodName, argTypes, + isStaticOnly); + } catch (Exception e2) { + // throw the original + throw e; + } + } + + // call it, and return the result + try { + return m.invoke (bean, args); + } + catch (Exception e) // 2003-02-23, --rgf, maybe an IllegalAccessException? + { + if (e instanceof IllegalAccessException && + bMethodHasSetAccessible && + Modifier.isPublic(m.getModifiers()) ) // if a public method allow access to it + { + m.setAccessible(true); // allow unconditional access to method + return m.invoke (bean, args); + } + // re-throw the exception + throw e; + } + + } catch (Exception e) { + // something went wrong while invoking method + Throwable t = (e instanceof InvocationTargetException) ? + ((InvocationTargetException)e).getTargetException () : + null; + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "method invocation failed: " + e + + ((t==null) ? "" : + (" target exception: " + t)), t); + } + } + + /** + * Creates a new bean. The signature of the constructor that's invoked + * is first taken as the types of the args, but if that fails, this tries + * to convert any primitive wrapper type args to their primitive + * counterparts to see whether a method exists that way. If it does, done. + * + * @param className fully qualified name of class to instantiate + * @param args array of constructor args (or null if none) + * + * @return the created bean + * + * @exception BSFException if something goes wrong (@see + * org.apache.cs.util.MethodUtils for the real + * exceptions that can occur). + */ + public static Object createBean (String className, Object args[]) + throws BSFException { + Bean obj; + Class[] argTypes = null; + + if (args != null) { + argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = (args[i] != null) ? args[i].getClass () : null; + } + } + + try { + try { + obj = ReflectionUtils.createBean (null, className, + argTypes, args); + return obj.value; + } catch (NoSuchMethodException me) { + // ok, so that didn't work - now try converting any primitive + // wrapper types to their primitive counterparts + try { + // if args is null the NullPointerException will get caught + // below and the right thing'll happen .. ugly but works + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof Number) + argTypes[i] = byte.class; + else if (args[i] instanceof Boolean) + argTypes[i] = boolean.class; + else if (args[i] instanceof Character) + argTypes[i] = char.class; + } + obj = ReflectionUtils.createBean (null, className, + argTypes, args); + return obj.value; + } catch (Exception e) { + // throw the previous exception + throw me; + } + } + } catch (Exception e) { + throw new BSFException (BSFException.REASON_OTHER_ERROR, + e.getMessage (), e); + } + } + + /** + * Given a class return the type signature string fragment for it. + * That is, return "I" for int, "J" for long, ... etc.. + * + * @param cl class object for whom the signature fragment is needed. + * + * @return the string representing the type signature + */ + public static String getTypeSignatureString (Class cl) { + if (cl.isPrimitive ()) { + if (cl == boolean.class) + return "Z"; + else if (cl == byte.class) + return "B"; + else if (cl == char.class) + return "C"; + else if (cl == short.class) + return "S"; + else if (cl == int.class) + return "I"; + else if (cl == long.class) + return "J"; + else if (cl == float.class) + return "F"; + else if (cl == double.class) + return "D"; + else + return "V"; + } else { + StringBuffer sb = new StringBuffer ("L"); + sb.append (cl.getName ()); + sb.append (";"); + return sb.toString().replace ('.', '/'); + } + } + + /** + * Load a class using the class loader of given manager. If that fails + * try using a class loader that loads from the tempdir of the manager. + * + * @param mgr BSFManager who's classLoader and tempDir props are + * consulted + * @param name name of the class to load + * + * @return the loaded class + * + * @exception BSFException if something goes wrong. + */ + public static Class loadClass (BSFManager mgr, String name) + throws BSFException { + ClassLoader classLoader = mgr.getClassLoader (); + + try { + return (classLoader == null) ? + // Class.forName (name) + Thread.currentThread().getContextClassLoader().loadClass (name) + : classLoader.loadClass (name); + } catch (ClassNotFoundException e) { + // try to load it from the temp dir using my own class loader + try { + if (bsfCL == null) + bsfCL = new BSFClassLoader (); + bsfCL.setTempDir (mgr.getTempDir ()); + return bsfCL.loadClass (name); + } catch (ClassNotFoundException e2) { + throw new BSFException (BSFException.REASON_OTHER_ERROR, + "unable to load class '" + name + "':" + e, e); + } + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/IOUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/IOUtils.java new file mode 100644 index 000000000..45b5a0764 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/IOUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringWriter; + +/** + * This file is a collection of input/output utilities. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + */ +public class IOUtils { + // debug flag - generates debug stuff if true + static boolean debug = false; + + ////////////////////////////////////////////////////////////////////////// + + public static String getStringFromReader (Reader reader) throws IOException { + BufferedReader bufIn = new BufferedReader(reader); + StringWriter swOut = new StringWriter(); + PrintWriter pwOut = new PrintWriter(swOut); + String tempLine; + + while ((tempLine = bufIn.readLine()) != null) { + pwOut.println(tempLine); + } + + pwOut.flush(); + + return swOut.toString(); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/IndentWriter.java b/src/main/java/com/volmit/iris/util/bsf/util/IndentWriter.java new file mode 100644 index 000000000..7141bc041 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/IndentWriter.java @@ -0,0 +1,85 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; + +/** + * An IndentWriter object behaves the same as a + * PrintWriter object, with the additional capability + * of being able to print strings that are prepended with a specified + * amount of spaces. + * + * @author Matthew J. Duftler + */ +public class IndentWriter extends PrintWriter +{ + /** + * Forwards its arguments to the PrintWriter constructor + * with the same signature. + */ + public IndentWriter(OutputStream out) + { + super(out); + } + /** + * Forwards its arguments to the PrintWriter constructor + * with the same signature. + */ + public IndentWriter(OutputStream out, boolean autoFlush) + { + super(out, autoFlush); + } + /** + * Forwards its arguments to the PrintWriter constructor + * with the same signature. + */ + public IndentWriter(Writer out) + { + super(out); + } + /** + * Forwards its arguments to the PrintWriter constructor + * with the same signature. + */ + public IndentWriter(Writer out, boolean autoFlush) + { + super(out, autoFlush); + } + /** + * Print the text (indented the specified amount) without inserting a linefeed. + * + * @param numberOfSpaces the number of spaces to indent the text. + * @param text the text to print. + */ + public void print(int numberOfSpaces, String text) + { + super.print(StringUtils.getChars(numberOfSpaces, ' ') + text); + } + /** + * Print the text (indented the specified amount) and insert a linefeed. + * + * @param numberOfSpaces the number of spaces to indent the text. + * @param text the text to print. + */ + public void println(int numberOfSpaces, String text) + { + super.println(StringUtils.getChars(numberOfSpaces, ' ') + text); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.c b/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.c new file mode 100644 index 000000000..0c18b0cdc --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.c @@ -0,0 +1,228 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "JNIUtils.h" + +/***** + * + * NOTE: Whereever I think an exception may occur, I need to check + * whether it did and recover appropriately .. otherwise the behavior + * of JNI is undefined! + * + *****/ + +/* throw a BSFException with the given code and message. */ +void bsf_exception (JNIEnv *jenv, int code, char *msg) { + jclass bsfexceptclass = + (*jenv)->FindClass (jenv, "org/apache/bsf/BSFException"); + (*jenv)->ThrowNew (jenv, bsfexceptclass, msg); +} + +/* cvt a pointer to a Long object whose value is the pointer value */ +jobject bsf_pointer2longobj (JNIEnv *jenv, void *ptr) { + return bsf_makeLong (jenv, (long) ptr); +} + +/* cvt a Long object whose value is the pointer value to the pointer */ +void *bsf_longobj2pointer (JNIEnv *jenv, jobject lobj) { + jclass longclass = (*jenv)->FindClass (jenv, "java/lang/Long"); + jmethodID mi = (*jenv)->GetMethodID (jenv, longclass, "longValue", "()J"); + void *ptr = (void *) (*jenv)->CallLongMethod (jenv, lobj, mi); + return ptr; +} + +/* convert an object to a string obj */ +jstring bsf_obj2jstring (JNIEnv *jenv, jobject obj) { + jclass objclass = (*jenv)->GetObjectClass (jenv, obj); + jmethodID tostr = (*jenv)->GetMethodID (jenv, objclass, "toString", + "()Ljava/lang/String;"); + jstring strobj = (jstring) (*jenv)->CallObjectMethod (jenv, obj, tostr); + return strobj; +} + +/* cvt an object to a c-string .. wastes memory, but useful for debug */ +const char *bsf_obj2cstring (JNIEnv *jenv, jobject obj) { + return (*jenv)->GetStringUTFChars (jenv, + bsf_obj2jstring (jenv, obj), + 0); +} + +/* call the named method with the given args on the given bean */ +jobject bsf_createbean (JNIEnv *jenv, char *classname, jobjectArray args) { + jclass cl; + jmethodID mid; + jobject result; + + /* find the BSFUtils.createBean method ID if needed */ + cl = (*jenv)->FindClass (jenv, "org/apache/bsf/util/EngineUtils"); + mid = (*jenv)->GetStaticMethodID (jenv, cl, "createBean", + "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;"); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + return 0; + } + + result = (*jenv)->CallStaticObjectMethod (jenv, cl, mid, + (*jenv)->NewStringUTF (jenv, + classname), + args); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + /* I should really throw a BSF exception here and the caller should + check whether an exception was thrown and in that case return. + later. */ + return 0; + } else { + return result; + } +} + +/* call the named method with the given args on the given bean */ +jobject bsf_callmethod (JNIEnv *jenv, jobject target, + char *methodname, jobjectArray args) { + jclass cl; + jmethodID mid; + jobject result; + + /* find the BSFUtils.callBeanMethod method ID if needed */ + cl = (*jenv)->FindClass (jenv, "org/apache/bsf/util/EngineUtils"); + mid = (*jenv)->GetStaticMethodID (jenv, cl, "callBeanMethod", + "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;"); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + return 0; + } + result = (*jenv)->CallStaticObjectMethod (jenv, cl, mid, target, + (*jenv)->NewStringUTF (jenv, + methodname), + args); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + /* I should really throw a BSF exception here and the caller should + check whether an exception was thrown and in that case return. + later. */ + return 0; + } else { + return result; + } +} + +/* return the named bean from the given mgr's bean registry */ +jobject bsf_lookupbean (JNIEnv *jenv, jobject mgr, char *beanname) { + jmethodID lookupMethod; + jobject result; + + jclass bsfmgrclass = (*jenv)->GetObjectClass (jenv, mgr); + lookupMethod = + (*jenv)->GetMethodID (jenv, bsfmgrclass, "lookupBean", + "(Ljava/lang/String;)Ljava/lang/Object;"); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + return 0; + } + + result = (*jenv)->CallObjectMethod (jenv, mgr, lookupMethod, + (*jenv)->NewStringUTF (jenv, beanname)); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + /* I should really throw a BSF exception here and the caller should + check whether an exception was thrown and in that case return. + later. */ + return 0; + } else { + return result; + } +} + +/* return the type signature string component for the given type: + I for ints, J for long, ... */ +char *bsf_getTypeSignatureString (JNIEnv *jenv, jclass objclass) { + jclass cl = 0; + jmethodID mid = 0; + jstring str; + + cl = (*jenv)->FindClass (jenv, "org/apache/bsf/util/EngineUtils"); + mid = (*jenv)->GetStaticMethodID (jenv, cl, "getTypeSignatureString", + "(Ljava/lang/Class;)Ljava/lang/String;"); + if ((*jenv)->ExceptionOccurred (jenv)) { + (*jenv)->ExceptionDescribe (jenv); + (*jenv)->ExceptionClear (jenv); + return 0; + } + str = (jstring) (*jenv)->CallStaticObjectMethod (jenv, cl, mid, objclass); + return (char *) bsf_obj2cstring (jenv, str); +} + +/* make objects from primitives */ + +jobject bsf_makeBoolean (JNIEnv *jenv, int val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Boolean"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(Z)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jboolean) val); +} + +jobject bsf_makeByte (JNIEnv *jenv, int val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Byte"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(B)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jbyte) val); +} + +jobject bsf_makeShort (JNIEnv *jenv, int val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Short"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(S)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jshort) val); +} + +jobject bsf_makeInteger (JNIEnv *jenv, int val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Integer"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(I)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jint) val); +} + +jobject bsf_makeLong (JNIEnv *jenv, long val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Long"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(J)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jlong) val); +} + +jobject bsf_makeFloat (JNIEnv *jenv, float val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Float"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(F)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jfloat) val); +} + +jobject bsf_makeDouble (JNIEnv *jenv, double val) { + jclass classobj = (*jenv)->FindClass (jenv, "java/lang/Double"); + jmethodID constructor = + (*jenv)->GetMethodID (jenv, classobj, "", "(D)V"); + return (*jenv)->NewObject (jenv, classobj, constructor, (jdouble) val); +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.h b/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.h new file mode 100644 index 000000000..ffc83f65c --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/JNIUtils.h @@ -0,0 +1,62 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#if defined(__cplusplus) +extern "C" { +#endif + +/* throw a BSFException with the given message */ +extern void bsf_exception (JNIEnv *jenv, int code, char *msg); + +/* cvt a pointer to a Long object whose value is the pointer value */ +extern jobject bsf_pointer2longobj (JNIEnv *jenv, void *ptr); + +/* cvt a Long object whose value is the pointer value to the pointer */ +extern void *bsf_longobj2pointer (JNIEnv *jenv, jobject lobj); + +/* convert an object to a string obj */ +jstring bsf_obj2jstring (JNIEnv *jenv, jobject obj); + +/* cvt an object to a c-string .. wastes memory, but useful for debug */ +const char *bsf_obj2cstring (JNIEnv *jenv, jobject obj); + +/* create an instance of the named class with the given args */ +extern jobject bsf_createbean (JNIEnv *jenv, char *methodname, + jobjectArray args); + +/* call the named method with the given args on the given bean */ +extern jobject bsf_callmethod (JNIEnv *jenv, jobject target, + char *methodname, jobjectArray args); + +/* return the named bean from the given mgr's bean registry */ +extern jobject bsf_lookupbean (JNIEnv *jenv, jobject mgr, char *beanname); + +/* return the type signature string component for the given type: + I for ints, J for long, ... */ +extern char *bsf_getTypeSignatureString (JNIEnv *jenv, jclass objclass); + +/* make objects from primitives */ +extern jobject bsf_makeBoolean (JNIEnv *jenv, int val); +extern jobject bsf_makeByte (JNIEnv *jenv, int val); +extern jobject bsf_makeShort (JNIEnv *jenv, int val); +extern jobject bsf_makeInteger (JNIEnv *jenv, int val); +extern jobject bsf_makeLong (JNIEnv *jenv, long val); +extern jobject bsf_makeFloat (JNIEnv *jenv, float val); +extern jobject bsf_makeDouble (JNIEnv *jenv, double val); +#if defined(__cplusplus) +} +#endif diff --git a/src/main/java/com/volmit/iris/util/bsf/util/JavaUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/JavaUtils.java new file mode 100644 index 000000000..f978a6548 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/JavaUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class JavaUtils { + // Temporarily copied from JavaEngine... + + private static Log logger; + + static { + logger = LogFactory.getLog((com.volmit.iris.util.bsf.util.JavaUtils.class) + .getName()); + } + + public static boolean JDKcompile(String fileName, String classPath) { + String option = (logger.isDebugEnabled()) ? "-g" : "-O"; + String args[] = { "javac", option, "-classpath", classPath, fileName }; + + logger.debug("JavaEngine: Compiling " + fileName); + logger.debug("JavaEngine: Classpath is " + classPath); + + try { + Process p = java.lang.Runtime.getRuntime().exec(args); + p.waitFor(); + return (p.exitValue() != 0); + } catch (IOException e) { + logger.error("ERROR: IO exception during exec(javac).", e); + } catch (SecurityException e) { + logger.error("ERROR: Unable to create subprocess to exec(javac).", + e); + } catch (InterruptedException e) { + logger.error("ERROR: Wait for exec(javac) was interrupted.", e); + } + return false; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/MethodUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/MethodUtils.java new file mode 100644 index 000000000..b3161a897 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/MethodUtils.java @@ -0,0 +1,517 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Enumeration; +import java.util.Vector; + +/** + * This file is a collection of reflection utilities for dealing with + * methods and constructors. + * + * @author Sanjiva Weerawarana + * @author Joseph Kesselman + */ +public class MethodUtils { + + /** Internal Class for getEntryPoint(). Implements 15.11.2.2 MORE + SPECIFIC rules. + + Retains a list of methods (already known to match the + arguments). As each method is added, we check against past entries + to determine which if any is "more specific" -- defined as having + _all_ its arguments (not just a preponderance) be + method-convertable into those of another. If such a relationship + is found, the more-specific method is retained and the + less-specific method is discarded. At the end, if this has yielded + a single winner it is considered the Most Specific Method and + hence the one that should be invoked. Otherwise, a + NoSuchMethodException is thrown. + + PERFORMANCE VERSUS ARCHITECTURE: Arguably, this should "have-a" + Vector. But the code is 6% smaller, and possibly faster, if we + code it as "is-a" Vector. Since it's an inner class, nobody's + likely to abuse the privilage. + + Note: "Static" in the case of an inner class means "Does not + reference instance data in the outer class", and is required since + our caller is a static method. */ + private static class MoreSpecific + extends Vector + { + /** Submit an entry-point to the list. May be discarded if a past + entry is more specific, or may cause others to be discarded it + if is more specific. + + newEntry: Method or Constructor under consideration. + */ + void addItem (Object newEntry) + { + if(size()==0) + addElement(newEntry); + else + { + Class[] newargs=entryGetParameterTypes(newEntry); + boolean keep=true; + for (Enumeration e = elements(); + keep & e.hasMoreElements() ; + ) + { + Object oldEntry=e.nextElement(); + // CAVEAT: Implicit references to enclosing class! + Class[] oldargs=entryGetParameterTypes(oldEntry); + if(areMethodConvertable(oldargs,newargs)) + removeElement(oldEntry); // New more specific; discard old + else if(areMethodConvertable(newargs,oldargs)) + keep=false; // Old more specific; discard new + // Else they're tied. Keep both and hope someone beats both. + } + if(keep) + addElement(newEntry); + } + } + + /** Obtain the single Most Specific entry-point. If there is no clear + winner, or if the list is empty, throw NoSuchMethodException. + + Arguments describe the call we were hoping to resolve. They are + used to throw a nice verbose exception if something goes wrong. + */ + Object getMostSpecific(Class targetClass,String methodName, + Class[] argTypes,boolean isStaticReference) + throws NoSuchMethodException + { + if(size()==1) + return firstElement(); + if(size()>1) + { + StringBuffer buf=new StringBuffer(); + Enumeration e=elements(); + buf.append(e.nextElement()); + while(e.hasMoreElements()) + buf.append(" and ").append(e.nextElement()); + throw new NoSuchMethodException (callToString(targetClass, + methodName, + argTypes, + isStaticReference)+ + " is ambiguous. It matches "+ + buf.toString()); + } + return null; + } + } + + /** Convenience method: Test an entire parameter-list/argument-list pair + for isMethodConvertable(), qv. + */ + static private boolean areMethodConvertable(Class[] parms,Class[] args) + { + if(parms.length!=args.length) + return false; + + for(int i=0;i0) { + if(false) + { + // ????? Sanjiva has an ArrayToString method. Using it would + // save a few bytes, at cost of giving up some reusability. + } + else + { + buf.append(StringUtils.getClassName(argTypes[0])); + for (int i = 1; i < argTypes.length; i++) { + buf.append(",").append(StringUtils.getClassName(argTypes[i])); + } + } + } + else + buf.append("[none]"); + buf.append(")"); + return buf.toString(); + } + /** Utility function: obtain common data from either Method or + Constructor. (In lieu of an EntryPoint interface.) */ + static int entryGetModifiers(Object entry) + { + return (entry instanceof Method) + ? ((Method)entry).getModifiers() + : ((Constructor)entry).getModifiers(); + } + // The common lookup code would be much easier if Method and + // Constructor shared an "EntryPoint" Interface. Unfortunately, even + // though their APIs are almost identical, they don't. These calls + // are a workaround... at the cost of additional runtime overhead + // and some extra bytecodes. + // + // (A JDK bug report has been submitted requesting that they add the + // Interface; it would be easy, harmless, and useful.) + + /** Utility function: obtain common data from either Method or + Constructor. (In lieu of an EntryPoint interface.) */ + static String entryGetName(Object entry) + { + return (entry instanceof Method) + ? ((Method)entry).getName() + : ((Constructor)entry).getName(); + } + /** Utility function: obtain common data from either Method or + Constructor. (In lieu of an EntryPoint interface.) */ + static Class[] entryGetParameterTypes(Object entry) + { + return (entry instanceof Method) + ? ((Method)entry).getParameterTypes() + : ((Constructor)entry).getParameterTypes(); + } + /** Utility function: obtain common data from either Method or + Constructor. (In lieu of an EntryPoint interface.) */ + static String entryToString(Object entry) + { + return (entry instanceof Method) + ? ((Method)entry).toString() + : ((Constructor)entry).toString(); + } + ////////////////////////////////////////////////////////////////////////// + + /** Class.getConstructor() finds only the entry point (if any) + _exactly_ matching the specified argument types. Our implmentation + can decide between several imperfect matches, using the same + search algorithm as the Java compiler. + + Note that all constructors are static by definition, so + isStaticReference is true. + + @exception NoSuchMethodException if constructor not found. + */ + static public Constructor getConstructor(Class targetClass, Class[] argTypes) + throws SecurityException, NoSuchMethodException + { + return (Constructor) getEntryPoint(targetClass,null,argTypes,true); + } + ////////////////////////////////////////////////////////////////////////// + + /** + * Search for entry point, per Java Language Spec 1.0 + * as amended, verified by comparison against compiler behavior. + * + * @param targetClass Class object for the class to be queried. + * @param methodName Name of method to invoke, or null for constructor. + * Only Public methods will be accepted. + * @param argTypes Classes of intended arguments. Note that primitives + * must be specified via their TYPE equivalents, + * rather than as their wrapper classes -- Integer.TYPE + * rather than Integer. "null" may be passed in as an + * indication that you intend to invoke the method with + * a literal null argument and therefore can accept + * any object type in this position. + * @param isStaticReference If true, and if the target is a Class object, + * only static methods will be accepted as valid matches. + * + * @return a Method or Constructor of the appropriate signature + * + * @exception SecurityException if security violation + * @exception NoSuchMethodException if no such method + */ + static private Object getEntryPoint(Class targetClass, + String methodName, + Class[] argTypes, + boolean isStaticReference) + throws SecurityException, NoSuchMethodException + { + // 15.11.1: OBTAIN STARTING CLASS FOR SEARCH + Object m=null; + + // 15.11.2 DETERMINE ARGUMENT SIGNATURE + // (Passed in as argTypes array.) + + // Shortcut: If an exact match exists, return it. + try { + if(methodName!=null) + { + m=targetClass.getMethod (methodName, argTypes); + if(isStaticReference && + !Modifier.isStatic(entryGetModifiers(m)) ) + { + throw + new NoSuchMethodException (callToString (targetClass, + methodName, + argTypes, + isStaticReference)+ + " resolved to instance " + m); + } + return m; + } + else + return targetClass.getConstructor (argTypes); + + } catch (NoSuchMethodException e) { + // no-args has no alternatives! + if(argTypes==null || argTypes.length==0) + { + throw + new NoSuchMethodException (callToString (targetClass, + methodName, + argTypes, + isStaticReference)+ + " not found."); + } + // Else fall through. + } + + // Well, _that_ didn't work. Time to search for the Most Specific + // matching function. NOTE that conflicts are possible! + + // 15.11.2.1 ACCESSIBLE: We apparently need to gather from two + // sources to be sure we have both instance and static methods. + Object[] methods; + if(methodName!=null) + { + methods=targetClass.getMethods(); + } + else + { + methods=targetClass.getConstructors(); + } + if(0==methods.length) + { + throw new NoSuchMethodException("No methods!"); + } + + MoreSpecific best=new MoreSpecific(); + for(int i=0;i=primTypes.length) + return false; // Off the end + + for(argscore=0;argscore=primTypes.length) + return false; // Off the end + + // OK if ordered AND NOT char-to-smaller-than-int + return (argscore2) ); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/ObjInfo.java b/src/main/java/com/volmit/iris/util/bsf/util/ObjInfo.java new file mode 100644 index 000000000..2546f86e3 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/ObjInfo.java @@ -0,0 +1,88 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +/** + * An ObjInfo object is used by a compiler to track the name and + * type of a bean. + * + * @author Matthew J. Duftler + */ +public class ObjInfo +{ + static private String QUOTE_CHARS = "\'\"", + EXEC_CHARS = "(="; + public String objName; + public Class objClass; + + public ObjInfo(Class objClass, String objName) + { + this.objClass = objClass; + this.objName = objName; + } + public boolean isExecutable() + { + char[] chars = objName.toCharArray(); + char openingChar = ' '; + boolean inString = false, + inEscapeSequence = false; + + for (int i = 0; i < chars.length; i++) + { + if (inEscapeSequence) + { + inEscapeSequence = false; + } + else if (QUOTE_CHARS.indexOf(chars[i]) != -1) + { + if (!inString) + { + openingChar = chars[i]; + inString = true; + } + else + { + if (chars[i] == openingChar) + { + inString = false; + } + } + } + else if (EXEC_CHARS.indexOf(chars[i]) != -1) + { + if (!inString) + { + return true; + } + } + else if (inString && chars[i] == '\\') + { + inEscapeSequence = true; + } + } + + return false; + } + public boolean isValueReturning() + { + return (objClass != void.class && objClass != Void.class); + } + public String toString() + { + return StringUtils.getClassName(objClass) + " " + objName; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/ObjectRegistry.java b/src/main/java/com/volmit/iris/util/bsf/util/ObjectRegistry.java new file mode 100644 index 000000000..af6b72267 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/ObjectRegistry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.util.Hashtable; + +/** + * The ObjectRegistry is used to do name-to-object reference lookups. + * If an ObjectRegistry is passed as a constructor argument, then this + * ObjectRegistry will be a cascading registry: when a lookup is + * invoked, it will first look in its own table for a name, and if it's not + * there, it will cascade to the parent ObjectRegistry. + * All registration is always local. [??] + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + */ +public class ObjectRegistry { + Hashtable reg = new Hashtable (); + ObjectRegistry parent = null; + + public ObjectRegistry () { + } + public ObjectRegistry (ObjectRegistry parent) { + this.parent = parent; + } + // lookup an object: cascade up if needed + public Object lookup (String name) throws IllegalArgumentException { + Object obj = reg.get (name); + + if (obj == null && parent != null) { + obj = parent.lookup (name); + } + + if (obj == null) { + throw new IllegalArgumentException ("object '" + name + "' not in registry"); + } + + return obj; + } + // register an object + public void register (String name, Object obj) { + reg.put (name, obj); + } + // unregister an object (silent if unknown name) + public void unregister (String name) { + reg.remove (name); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/ReflectionUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/ReflectionUtils.java new file mode 100644 index 000000000..ae9e0c36a --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/ReflectionUtils.java @@ -0,0 +1,421 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.beans.BeanInfo; +import java.beans.Beans; +import java.beans.EventSetDescriptor; +import java.beans.FeatureDescriptor; +import java.beans.IndexedPropertyDescriptor; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.volmit.iris.util.bsf.util.event.EventAdapter; +import com.volmit.iris.util.bsf.util.event.EventAdapterRegistry; +import com.volmit.iris.util.bsf.util.event.EventProcessor; +import com.volmit.iris.util.bsf.util.type.TypeConvertor; +import com.volmit.iris.util.bsf.util.type.TypeConvertorRegistry; + +/** + * This file is a collection of reflection utilities. There are utilities + * for creating beans, getting bean infos, setting/getting properties, + * and binding events. + * + * @author Sanjiva Weerawarana + * @author Joseph Kesselman + */ +public class ReflectionUtils { + + ////////////////////////////////////////////////////////////////////////// + + /** + * Add an event processor as a listener to some event coming out of an + * object. + * + * @param source event source + * @param eventSetName name of event set from event src to bind to + * @param processor event processor the event should be delegated to + * when it occurs; either via processEvent or + * processExceptionableEvent. + * + * @exception IntrospectionException if unable to introspect + * @exception IllegalArgumentException if event set is unknown + * @exception IllegalAccessException if the event adapter class or + * initializer is not accessible. + * @exception InstantiationException if event adapter instantiation fails + * @exception InvocationTargetException if something goes wrong while + * running add event listener method + */ + public static void addEventListener (Object source, String eventSetName, + EventProcessor processor) + throws IntrospectionException, IllegalArgumentException, + IllegalAccessException, InstantiationException, + InvocationTargetException { + // find the event set descriptor for this event + BeanInfo bi = Introspector.getBeanInfo (source.getClass ()); + EventSetDescriptor esd = (EventSetDescriptor) + findFeatureByName ("event", eventSetName, bi.getEventSetDescriptors ()); + + if (esd == null) // no events found, maybe a proxy from OpenOffice.org? + { + throw new IllegalArgumentException ("event set '" + eventSetName + + "' unknown for source type '" + source.getClass () + "'"); + } + + // get the class object for the event + Class listenerType=esd.getListenerType(); // get ListenerType class object from EventSetDescriptor + + // find an event adapter class of the right type + Class adapterClass = EventAdapterRegistry.lookup (listenerType); + if (adapterClass == null) { + throw new IllegalArgumentException ("event adapter for listener type " + + "'" + listenerType + "' (eventset " + + "'" + eventSetName + "') unknown"); + } + + // create the event adapter and give it the event processor + EventAdapter adapter = (EventAdapter) adapterClass.newInstance (); + adapter.setEventProcessor (processor); + + // bind the adapter to the source bean + Method addListenerMethod; + Object[] args; + if (eventSetName.equals ("propertyChange") || + eventSetName.equals ("vetoableChange")) { + // In Java 1.2, beans may have direct listener adding methods + // for property and vetoable change events which take the + // property name as a filter to be applied at the event source. + // The filter property of the event processor should be used + // in this case to support the source-side filtering. + // + // ** TBD **: the following two lines need to change appropriately + addListenerMethod = esd.getAddListenerMethod (); + args = new Object[] {adapter}; + } + else + { + addListenerMethod = esd.getAddListenerMethod (); + args = new Object[] {adapter}; + } + addListenerMethod.invoke (source, args); + } + ////////////////////////////////////////////////////////////////////////// + + + /** + * Create a bean using given class loader and using the appropriate + * constructor for the given args of the given arg types. + + * @param cld the class loader to use. If null, Class.forName is used. + * @param className name of class to instantiate + * @param argTypes array of argument types + * @param args array of arguments + * + * @return the newly created bean + * + * @exception ClassNotFoundException if class is not loaded + * @exception NoSuchMethodException if constructor can't be found + * @exception InstantiationException if class can't be instantiated + * @exception IllegalAccessException if class is not accessible + * @exception IllegalArgumentException if argument problem + * @exception InvocationTargetException if constructor excepted + * @exception IOException if I/O error in beans.instantiate + */ + public static Bean createBean (ClassLoader cld, String className, + Class[] argTypes, Object[] args) + throws ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, + IOException { + if (argTypes != null) { + // find the right constructor and use that to create bean + Class cl = (cld != null) ? cld.loadClass (className) + : Thread.currentThread().getContextClassLoader().loadClass (className); // rgf, 2006-01-05 + // : Class.forName (className); + + Constructor c = MethodUtils.getConstructor (cl, argTypes); + return new Bean (cl, c.newInstance (args)); + } else { + // create the bean with no args constructor + Object obj = Beans.instantiate (cld, className); + return new Bean (obj.getClass (), obj); + } + } + ////////////////////////////////////////////////////////////////////////// + + /** + * Create a bean using given class loader and using the appropriate + * constructor for the given args. Figures out the arg types and + * calls above. + + * @param cld the class loader to use. If null, Class.forName is used. + * @param className name of class to instantiate + * @param args array of arguments + * + * @return the newly created bean + * + * @exception ClassNotFoundException if class is not loaded + * @exception NoSuchMethodException if constructor can't be found + * @exception InstantiationException if class can't be instantiated + * @exception IllegalAccessException if class is not accessible + * @exception IllegalArgumentException if argument problem + * @exception InvocationTargetException if constructor excepted + * @exception IOException if I/O error in beans.instantiate + */ + public static Bean createBean (ClassLoader cld, String className, + Object[] args) + throws ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, + IOException { + Class[] argTypes = null; + if (args != null) { + argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = (args[i] != null) ? args[i].getClass () : null; + } + } + return createBean (cld, className, argTypes, args); + } + ////////////////////////////////////////////////////////////////////////// + + /** + * locate the item in the fds array whose name is as given. returns + * null if not found. + */ + private static + FeatureDescriptor findFeatureByName (String featureType, String name, + FeatureDescriptor[] fds) { + for (int i = 0; i < fds.length; i++) { + if (name.equals (fds[i].getName())) { + return fds[i]; + } + } + return null; + } + public static Bean getField (Object target, String fieldName) + throws IllegalArgumentException, IllegalAccessException { + // This is to handle how we do static fields. + Class targetClass = (target instanceof Class) + ? (Class) target + : target.getClass (); + + try { + Field f = targetClass.getField (fieldName); + Class fieldType = f.getType (); + + // Get the value and return it. + Object value = f.get (target); + return new Bean (fieldType, value); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException ("field '" + fieldName + "' is " + + "unknown for '" + target + "'"); + } + } + ////////////////////////////////////////////////////////////////////////// + + /** + * Get a property of a bean. + * + * @param target the object whose prop is to be gotten + * @param propName name of the property to set + * @param index index to get (if property is indexed) + * + * @exception IntrospectionException if unable to introspect + * @exception IllegalArgumentException if problems with args: if the + * property is unknown, or if the property is given an index + * when its not, or if the property is not writeable, or if + * the given value cannot be assigned to the it (type mismatch). + * @exception IllegalAccessException if read method is not accessible + * @exception InvocationTargetException if read method excepts + */ + public static Bean getProperty (Object target, String propName, + Integer index) + throws IntrospectionException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + // find the property descriptor + BeanInfo bi = Introspector.getBeanInfo (target.getClass ()); + PropertyDescriptor pd = (PropertyDescriptor) + findFeatureByName ("property", propName, bi.getPropertyDescriptors ()); + if (pd == null) { + throw new IllegalArgumentException ("property '" + propName + "' is " + + "unknown for '" + target + "'"); + } + + // get read method and type of property + Method rm; + Class propType; + if (index != null) { + // if index != null, then property is indexed - pd better be so too + if (!(pd instanceof IndexedPropertyDescriptor)) { + throw new IllegalArgumentException ("attempt to get non-indexed " + + "property '" + propName + + "' as being indexed"); + } + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd; + rm = ipd.getIndexedReadMethod (); + propType = ipd.getIndexedPropertyType (); + } else { + rm = pd.getReadMethod (); + propType = pd.getPropertyType (); + } + + if (rm == null) { + throw new IllegalArgumentException ("property '" + propName + + "' is not readable"); + } + + // now get the value + Object propVal = null; + if (index != null) { + propVal = rm.invoke (target, new Object[] {index}); + } else { + propVal = rm.invoke (target, null); + } + return new Bean (propType, propVal); + } + public static void setField (Object target, String fieldName, Bean value, + TypeConvertorRegistry tcr) + throws IllegalArgumentException, IllegalAccessException { + // This is to handle how we do static fields. + Class targetClass = (target instanceof Class) + ? (Class) target + : target.getClass (); + + try { + Field f = targetClass.getField (fieldName); + Class fieldType = f.getType (); + + // type convert the value if necessary + Object fieldVal = null; + boolean okeydokey = true; + if (fieldType.isAssignableFrom (value.type)) { + fieldVal = value.value; + } else if (tcr != null) { + TypeConvertor cvtor = tcr.lookup (value.type, fieldType); + if (cvtor != null) { + fieldVal = cvtor.convert (value.type, fieldType, value.value); + } else { + okeydokey = false; + } + } else { + okeydokey = false; + } + if (!okeydokey) { + throw new IllegalArgumentException ("unable to assign '" + value.value + + "' to field '" + fieldName + "'"); + } + + // now set the value + f.set (target, fieldVal); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException ("field '" + fieldName + "' is " + + "unknown for '" + target + "'"); + } + } + ////////////////////////////////////////////////////////////////////////// + + /** + * Set a property of a bean to a given value. + * + * @param target the object whose prop is to be set + * @param propName name of the property to set + * @param index index to set (if property is indexed) + * @param value the property value + * @param valueType the type of the above (needed when its null) + * @param tcr type convertor registry to use to convert value type to + * property type if necessary + * + * @exception IntrospectionException if unable to introspect + * @exception IllegalArgumentException if problems with args: if the + * property is unknown, or if the property is given an index + * when its not, or if the property is not writeable, or if + * the given value cannot be assigned to the it (type mismatch). + * @exception IllegalAccessException if write method is not accessible + * @exception InvocationTargetException if write method excepts + */ + public static void setProperty (Object target, String propName, + Integer index, Object value, + Class valueType, TypeConvertorRegistry tcr) + throws IntrospectionException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + // find the property descriptor + BeanInfo bi = Introspector.getBeanInfo (target.getClass ()); + PropertyDescriptor pd = (PropertyDescriptor) + findFeatureByName ("property", propName, bi.getPropertyDescriptors ()); + if (pd == null) { + throw new IllegalArgumentException ("property '" + propName + "' is " + + "unknown for '" + target + "'"); + } + + // get write method and type of property + Method wm; + Class propType; + if (index != null) { + // if index != null, then property is indexed - pd better be so too + if (!(pd instanceof IndexedPropertyDescriptor)) { + throw new IllegalArgumentException ("attempt to set non-indexed " + + "property '" + propName + + "' as being indexed"); + } + IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd; + wm = ipd.getIndexedWriteMethod (); + propType = ipd.getIndexedPropertyType (); + } else { + wm = pd.getWriteMethod (); + propType = pd.getPropertyType (); + } + + if (wm == null) { + throw new IllegalArgumentException ("property '" + propName + + "' is not writeable"); + } + + // type convert the value if necessary + Object propVal = null; + boolean okeydokey = true; + if (propType.isAssignableFrom (valueType)) { + propVal = value; + } else if (tcr != null) { + TypeConvertor cvtor = tcr.lookup (valueType, propType); + if (cvtor != null) { + propVal = cvtor.convert (valueType, propType, value); + } else { + okeydokey = false; + } + } else { + okeydokey = false; + } + if (!okeydokey) { + throw new IllegalArgumentException ("unable to assign '" + value + + "' to property '" + propName + "'"); + } + + // now set the value + if (index != null) { + wm.invoke (target, new Object[] {index, propVal}); + } else { + wm.invoke (target, new Object[] {propVal}); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/ScriptSymbolTable.java b/src/main/java/com/volmit/iris/util/bsf/util/ScriptSymbolTable.java new file mode 100644 index 000000000..358e365eb --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/ScriptSymbolTable.java @@ -0,0 +1,44 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.util.Hashtable; + +/** + * An ScriptSymbolTable object is used by a CodeBuffer + * object to implement nested scopes. + * + * @author Matthew J. Duftler + */ +class ScriptSymbolTable extends Hashtable +{ + private Hashtable parentTable; + + ScriptSymbolTable(Hashtable parentTable) + { + this.parentTable = parentTable; + } + public synchronized Object get(Object key) + { + Object ret = super.get(key); + + if (ret == null && parentTable != null) + ret = parentTable.get(key); + + return ret; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/StringUtils.java b/src/main/java/com/volmit/iris/util/bsf/util/StringUtils.java new file mode 100644 index 000000000..b512ffc76 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/StringUtils.java @@ -0,0 +1,412 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util; + +import java.beans.Introspector; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Deals with strings (probably need to elaborate some more). + * + * @author Matthew J. Duftler + */ +public class StringUtils +{ + public static final String lineSeparator = + System.getProperty("line.separator", "\n"); + public static final String lineSeparatorStr = cleanString(lineSeparator); + + public static String classNameToVarName(String className) + { + // Might represent an array. + int arrayDim = 0; + + while (className.endsWith("[]")) + { + className = className.substring(0, className.length() - 2); + arrayDim++; + } + + int iLastPeriod = className.lastIndexOf('.'); + String varName = Introspector.decapitalize( + iLastPeriod != -1 + ? className.substring(iLastPeriod + 1) + : className); + + if (arrayDim > 0) + { + varName += "_" + arrayDim + "D"; + } + + return getValidIdentifierName(varName); + } + // Ensure that escape sequences are passed through properly. + public static String cleanString(String str) + { + if (str == null) + return null; + else + { + char[] charArray = str.toCharArray(); + StringBuffer sBuf = new StringBuffer(); + + for (int i = 0; i < charArray.length; i++) + switch (charArray[i]) + { + case '\"' : sBuf.append("\\\""); + break; + case '\\' : sBuf.append("\\\\"); + break; + case '\n' : sBuf.append("\\n"); + break; + case '\r' : sBuf.append("\\r"); + break; + default : sBuf.append(charArray[i]); + break; + } + + return sBuf.toString(); + } + } + /** + * Get a string consisting of numberOfChars theChars. + * + * @return a string consisting of numberOfChars theChars. + */ + public static String getChars(int numberOfChars, char theChar) + { + if (numberOfChars <= 0) + return ""; + + StringBuffer sRet = new StringBuffer(numberOfChars); + + for (int i = 0; i < numberOfChars; i++) + sRet.append(theChar); + + return sRet.toString(); + } + /* + This method will return the correct name for a class object representing + a primitive, a single instance of a class, as well as n-dimensional arrays + of primitives or instances. This logic is needed to handle the string returned + from Class.getName(). If the class object represents a single instance (or + a primitive), Class.getName() returns the fully-qualified name of the class + and no further work is needed. However, if the class object represents an + array (of n dimensions), Class.getName() returns a Descriptor (the Descriptor + grammar is defined in section 4.3 of the Java VM Spec). This method will + parse the Descriptor if necessary. + */ + public static String getClassName(Class targetClass) + { + String className = targetClass.getName(); + + return targetClass.isArray() ? parseDescriptor(className) : className; + } + public static String getCommaListFromVector(Vector sourceVector) + { + StringBuffer strBuf = new StringBuffer(); + + for (int i = 0; i < sourceVector.size(); i++) + { + strBuf.append((i > 0 ? ", " : "") + + sourceVector.elementAt(i)); + } + + return strBuf.toString(); + } + /* + Returns a Reader for reading from the specified resource, if the resource + points to a stream. + */ + public static Reader getContentAsReader(URL url) throws SecurityException, + IllegalArgumentException, + IOException + { + if (url == null) + { + throw new IllegalArgumentException("URL cannot be null."); + } + + try + { + Object content = url.getContent(); + + if (content == null) + { + throw new IllegalArgumentException("No content."); + } + + if (content instanceof InputStream) + { + Reader in = new InputStreamReader((InputStream)content); + + if (in.ready()) + { + return in; + } + else + { + throw new FileNotFoundException(); + } + } + else + { + throw new IllegalArgumentException((content instanceof String) + ? (String)content + : "This URL points to a: " + + StringUtils.getClassName(content.getClass())); + } + } + catch (SecurityException e) + { + throw new SecurityException("Your JVM's SecurityManager has disallowed this."); + } + catch (FileNotFoundException e) + { + throw new FileNotFoundException("This file was not found: " + url); + } + } + /* + Shorthand for: IOUtils.getStringFromReader(getContentAsReader(url)). + */ + public static String getContentAsString(URL url) throws SecurityException, + IllegalArgumentException, + IOException + { + return IOUtils.getStringFromReader(getContentAsReader(url)); + } + // Handles multi-line strings. + public static String getSafeString(String scriptStr) + { + BufferedReader in = new BufferedReader(new StringReader(scriptStr)); + StringBuffer strBuf = new StringBuffer(); + String tempLine, + previousLine = null; + + try + { + while ((tempLine = in.readLine()) != null) + { + if (previousLine != null) + { + strBuf.append("\"" + previousLine + lineSeparatorStr + "\" +" + + lineSeparator); + } + + previousLine = cleanString(tempLine); + } + } + catch (IOException e) + { + } + + strBuf.append("\"" + (previousLine != null ? previousLine : "") + "\"" + + lineSeparator); + + return strBuf.toString(); + } + /* + */ + public static URL getURL(URL contextURL, String spec) throws MalformedURLException + { + return getURL(contextURL, spec, 1); + } + /* + The recursiveDepth argument is used to insure that the algorithm gives up + after hunting 2 levels up in the contextURL's path. + */ + private static URL getURL(URL contextURL, String spec, int recursiveDepth) + throws MalformedURLException + { + URL url = null; + + try + { + url = new URL(contextURL, spec); + + try + { + url.openStream(); + } + catch (IOException ioe1) + { + throw new MalformedURLException("This file was not found: " + url); + } + } + catch (MalformedURLException e1) + { + url = new URL("file", "", spec); + + try + { + url.openStream(); + } + catch (IOException ioe2) + { + if (contextURL != null) + { + String contextFileName = contextURL.getFile(); + String parentName = new File(contextFileName).getParent(); + + if (parentName != null && recursiveDepth < 3) + { + return getURL(new URL("file", "", parentName + '/'), + spec, + recursiveDepth + 1); + } + } + + throw new MalformedURLException("This file was not found: " + url); + } + } + + return url; + } + public static String getValidIdentifierName(String identifierName) + { + if (identifierName == null || identifierName.length() == 0) + return null; + + StringBuffer strBuf = new StringBuffer(); + + char[] chars = identifierName.toCharArray(); + + strBuf.append(Character.isJavaIdentifierStart(chars[0]) + ? chars[0] + : '_' + ); + + for (int i = 1; i < chars.length; i++) + { + strBuf.append(Character.isJavaIdentifierPart(chars[i]) + ? chars[i] + : '_' + ); + } + + return strBuf.toString(); + } + public static boolean isValidIdentifierName(String identifierName) + { + if (identifierName == null || identifierName.length() == 0) + return false; + + char[] chars = identifierName.toCharArray(); + + if (!Character.isJavaIdentifierStart(chars[0])) + return false; + + for (int i = 1; i < chars.length; i++) + if (!Character.isJavaIdentifierPart(chars[i])) + return false; + + return true; + } + public static boolean isValidPackageName(String packageName) + { + if (packageName == null) + return false; + else if (packageName.length() == 0) + // Empty is ok. + return true; + + StringTokenizer strTok = new StringTokenizer(packageName, ".", true); + + // Should have an odd number of tokens (including '.' delimiters). + if (strTok.countTokens() % 2 != 1) + return false; + + // Must start with a valid identifier name. + if (!isValidIdentifierName(strTok.nextToken())) + return false; + + // ... followed by 0 or more of ".ValidIdentifier". + while (strTok.hasMoreTokens()) + { + // Must be a '.'. + if (!strTok.nextToken().equals(".")) + return false; + + // Must be a valid identifier name. + if (strTok.hasMoreTokens()) + { + if (!isValidIdentifierName(strTok.nextToken())) + return false; + } + else + return false; + } + + return true; + } + /* + See the comment above for getClassName(targetClass)... + */ + private static String parseDescriptor(String className) + { + char[] classNameChars = className.toCharArray(); + int arrayDim = 0; + int i = 0; + + while (classNameChars[i] == '[') + { + arrayDim++; + i++; + } + + StringBuffer classNameBuf = new StringBuffer(); + + switch (classNameChars[i++]) + { + case 'B' : classNameBuf.append("byte"); + break; + case 'C' : classNameBuf.append("char"); + break; + case 'D' : classNameBuf.append("double"); + break; + case 'F' : classNameBuf.append("float"); + break; + case 'I' : classNameBuf.append("int"); + break; + case 'J' : classNameBuf.append("long"); + break; + case 'S' : classNameBuf.append("short"); + break; + case 'Z' : classNameBuf.append("boolean"); + break; + case 'L' : classNameBuf.append(classNameChars, + i, classNameChars.length - i - 1); + break; + } + + for (i = 0; i < arrayDim; i++) + classNameBuf.append("[]"); + + return classNameBuf.toString(); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/cf/CFDriver.java b/src/main/java/com/volmit/iris/util/bsf/util/cf/CFDriver.java new file mode 100644 index 000000000..90352395b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/cf/CFDriver.java @@ -0,0 +1,196 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.cf; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; + +/** + * This is an example of how a CodeFormatter bean can be used. + *

+ * The CFDriver is a stand-alone tool that will instantiate a + * CodeFormatter bean, configure it according to your + * command-line arguments, and invoke the formatting. Since the + * default source of input is stdin, and the default + * target for output is stdout, a CFDriver + * can also be used as a filter. + * + * @see CodeFormatter + * + * @version 1.0 + * @author Matthew J. Duftler + */ +public class CFDriver +{ + /** + * Not used. + */ + public CFDriver() + { + } + /** + * A driver for CodeFormatter. + *

+ * Usage: + *

+	*  java org.apache.cf.CFDriver [args]
+	*

+ * args: + *

+ * [-in fileName] default: <STDIN> + * [-out fileName] default: <STDOUT> + * [-maxLine length] default: 74 + * [-step size] default: 2 + * [-delim group] default: (+ + * [-sdelim group] default: , + *

+ */ + public static void main(String[] argv) + { + if (argv.length % 2 == 0) + { + String inFile = null, + outFile = null, + maxLine = null, + indStep = null, + delim = null, + sDelim = null; + Reader in = null; + Writer out = null; + CodeFormatter cf = new CodeFormatter(); + + for (int i = 0; i < argv.length; i += 2) + { + if (argv[i].startsWith("-i")) + inFile = argv[i + 1]; + else if (argv[i].startsWith("-o")) + outFile = argv[i + 1]; + else if (argv[i].startsWith("-m")) + maxLine = argv[i + 1]; + else if (argv[i].startsWith("-st")) + indStep = argv[i + 1]; + else if (argv[i].startsWith("-d")) + delim = argv[i + 1]; + else if (argv[i].startsWith("-sd")) + sDelim = argv[i + 1]; + } + + if (inFile != null) + { + try + { + in = new FileReader(inFile); + } + catch (FileNotFoundException e) + { + printError("Cannot open input file: " + inFile); + + return; + } + } + else + { + in = new InputStreamReader(System.in); + } + + if (outFile != null) + { + try + { + out = new FileWriter(outFile); + } + catch (IOException e) + { + printError("Cannot open output file: " + outFile); + + return; + } + } + else + { + out = new OutputStreamWriter(System.out); + } + + if (maxLine != null) + { + try + { + cf.setMaxLineLength(Integer.parseInt(maxLine)); + } + catch (NumberFormatException nfe) + { + printError("Not a valid integer: " + maxLine); + + return; + } + } + + if (indStep != null) + { + try + { + cf.setIndentationStep(Integer.parseInt(indStep)); + } + catch (NumberFormatException nfe) + { + printError("Not a valid integer: " + indStep); + + return; + } + } + + if (delim != null) + cf.setDelimiters(delim); + + if (sDelim != null) + cf.setStickyDelimiters(sDelim); + + cf.formatCode(in, out); + } + else + printHelp(); + } + private static void printError(String errMsg) + { + System.err.println("ERROR: " + errMsg); + } + private static void printHelp() + { + System.out.println("Usage:"); + System.out.println(); + System.out.println(" java " + CFDriver.class.getName() + " [args]"); + System.out.println(); + System.out.println(" args:"); + System.out.println(); + System.out.println(" [-in fileName] default: "); + System.out.println(" [-out fileName] default: "); + System.out.println(" [-maxLine length] default: " + + CodeFormatter.DEFAULT_MAX); + System.out.println(" [-step size] default: " + + CodeFormatter.DEFAULT_STEP); + System.out.println(" [-delim group] default: " + + CodeFormatter.DEFAULT_DELIM); + System.out.println(" [-sdelim group] default: " + + CodeFormatter.DEFAULT_S_DELIM); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/cf/CodeFormatter.java b/src/main/java/com/volmit/iris/util/bsf/util/cf/CodeFormatter.java new file mode 100644 index 000000000..de3e17d2a --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/cf/CodeFormatter.java @@ -0,0 +1,372 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.cf; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +import com.volmit.iris.util.bsf.util.IndentWriter; +import com.volmit.iris.util.bsf.util.StringUtils; + +/** + * A CodeFormatter bean is used to format raw Java code. It + * indents, word-wraps, and replaces tab characters with an amount of space + * characters equal to the size of the indentationStep property. + * To create and use a CodeFormatter, you simply instantiate a + * new CodeFormatter bean, and invoke + * formatCode(Reader source, Writer target) with appropriate + * arguments. + * + * @version 1.0 + * @author Matthew J. Duftler + */ +public class CodeFormatter +{ + /** + * The default maximum line length. + */ + public static final int DEFAULT_MAX = 74; + /** + * The default size of the indentation step. + */ + public static final int DEFAULT_STEP = 2; + /** + * The default set of delimiters. + */ + public static final String DEFAULT_DELIM = "(+"; + /** + * The default set of sticky delimiters. + */ + public static final String DEFAULT_S_DELIM = ","; + + // Configurable Parameters + private int maxLineLength = DEFAULT_MAX; + private int indentationStep = DEFAULT_STEP; + private String delimiters = DEFAULT_DELIM; + private String stickyDelimiters = DEFAULT_S_DELIM; + + // Global Variables + private int indent; + private int hangingIndent; + private int origIndent; + private boolean inCPP_Comment; + + private void addTok(StringBuffer targetBuf, StringBuffer tokBuf, + IndentWriter out) + { + int tokLength = tokBuf.length(), + targetLength = targetBuf.length(); + + if (indent + targetLength + tokLength > maxLineLength) + { + if (targetLength == 0) + { + out.println(indent, tokBuf.toString()); + indent = hangingIndent; + targetBuf.setLength(0); + + return; + } + else + { + out.println(indent, targetBuf.toString().trim()); + indent = hangingIndent; + targetBuf.setLength(0); + } + } + + targetBuf.append(tokBuf.toString()); + + return; + } + /** + * Formats the code read from source, and writes the formatted + * code to target. + * + * @param source where to read the unformatted code from. + * @param target where to write the formatted code to. + */ + public void formatCode(Reader source, Writer target) + { + String line; + BufferedReader in = new BufferedReader(source); + IndentWriter out = new IndentWriter(new BufferedWriter(target), true); + + try + { + origIndent = 0; + inCPP_Comment = false; + + while ((line = in.readLine()) != null) + { + line = line.trim(); + + if (line.length() > 0) + { + indent = origIndent; + hangingIndent = indent + indentationStep; + printLine(line, out); + } + else + out.println(); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + /** + * Gets the set of delimiters. + * + * @return the set of delimiters. + * @see #setDelimiters + */ + public String getDelimiters() + { + return delimiters; + } + /** + * Gets the size of the indentation step. + * + * @return the size of the indentation step. + * @see #setIndentationStep + */ + public int getIndentationStep() + { + return indentationStep; + } + /** + * Gets the maximum line length. + * + * @return the maximum line length. + * @see #setMaxLineLength + */ + public int getMaxLineLength() + { + return maxLineLength; + } + /** + * Gets the set of sticky delimiters. + * + * @return the set of sticky delimiters. + * @see #setStickyDelimiters + */ + public String getStickyDelimiters() + { + return stickyDelimiters; + } + private void printLine(String line, IndentWriter out) + { + char[] source = line.toCharArray(); + char ch; + char quoteChar = ' '; + boolean inEscapeSequence = false; + boolean inString = false; + StringBuffer tokBuf = new StringBuffer(), + targetBuf = new StringBuffer(hangingIndent + line.length()); + + for (int i = 0; i < source.length; i++) + { + ch = source[i]; + + if (inEscapeSequence) + { + tokBuf.append(ch); + inEscapeSequence = false; + } + else + { + if (inString) + { + switch (ch) + { + case '\\' : + tokBuf.append('\\'); + inEscapeSequence = true; + break; + case '\'' : + case '\"' : + tokBuf.append(ch); + + if (ch == quoteChar) + { + addTok(targetBuf, tokBuf, out); + tokBuf.setLength(0); + inString = false; + } + break; + case 9 : // pass thru tab characters... + tokBuf.append(ch); + break; + default : + if (ch > 31) + tokBuf.append(ch); + break; + } + } + else // !inString + { + if (inCPP_Comment) + { + tokBuf.append(ch); + + if (ch == '/' && i > 0 && source[i - 1] == '*') + inCPP_Comment = false; + } + else + { + switch (ch) + { + case '/' : + tokBuf.append(ch); + + if (i > 0 && source[i - 1] == '/') + { + String tokStr = tokBuf.append(source, + i + 1, + source.length - (i + 1)).toString(); + + out.println(indent, targetBuf.append(tokStr).toString()); + + return; + } + break; + case '*' : + tokBuf.append(ch); + + if (i > 0 && source[i - 1] == '/') + inCPP_Comment = true; + break; + case '\'' : + case '\"' : + addTok(targetBuf, tokBuf, out); + tokBuf.setLength(0); + tokBuf.append(ch); + quoteChar = ch; + inString = true; + break; + case 9 : // replace tab characters... + tokBuf.append(StringUtils.getChars(indentationStep, ' ')); + break; + case '{' : + tokBuf.append(ch); + origIndent += indentationStep; + break; + case '}' : + tokBuf.append(ch); + origIndent -= indentationStep; + + if (i == 0) + indent = origIndent; + break; + default : + if (ch > 31) + { + if (delimiters.indexOf(ch) != -1) + { + addTok(targetBuf, tokBuf, out); + tokBuf.setLength(0); + tokBuf.append(ch); + } + else if (stickyDelimiters.indexOf(ch) != -1) + { + tokBuf.append(ch); + addTok(targetBuf, tokBuf, out); + tokBuf.setLength(0); + } + else + tokBuf.append(ch); + } + break; + } + } + } + } + } + + if (tokBuf.length() > 0) + addTok(targetBuf, tokBuf, out); + + String lastLine = targetBuf.toString().trim(); + + if (lastLine.length() > 0) + out.println(indent, lastLine); + } + /** + * Sets the set of delimiters; default set is "(+". + *

+ * Each character represents one delimiter. If a line is ready to be + * word-wrapped and a delimiter is encountered, the delimiter will + * appear as the first character on the following line. + * A quotation mark, " or ', opening a string + * is always a delimiter, whether you specify it or not. + * + * @param newDelimiters the new set of delimiters. + * @see #getDelimiters + */ + public void setDelimiters(String newDelimiters) + { + delimiters = newDelimiters; + } + /** + * Sets the size of the indentation step; default size is 2. + *

+ * This is the number of spaces that lines will be indented (when appropriate). + * + * @param newIndentationStep the new size of the indentation step. + * @see #getIndentationStep + */ + public void setIndentationStep(int newIndentationStep) + { + indentationStep = (newIndentationStep < 0 ? 0 : newIndentationStep); + } + /** + * Sets the (desired) maximum line length; default length is + * 74. + *

+ * If a token is longer than the requested maximum line length, + * then the line containing that token will obviously be longer + * than the desired maximum. + * + * @param newMaxLineLength the new maximum line length. + * @see #getMaxLineLength + */ + public void setMaxLineLength(int newMaxLineLength) + { + maxLineLength = (newMaxLineLength < 0 ? 0 : newMaxLineLength); + } + /** + * Sets the set of sticky delimiters; default set is ",". + *

+ * Each character represents one sticky delimiter. If a line is ready + * to be word-wrapped and a sticky delimiter is encountered, the sticky + * delimiter will appear as the last character on the current line. + * A quotation mark, " or ', closing a string + * is always a sticky delimiter, whether you specify it or not. + * + * @param newStickyDelimiters the new set of sticky delimiters. + * @see #getStickyDelimiters + */ + public void setStickyDelimiters(String newStickyDelimiters) + { + stickyDelimiters = newStickyDelimiters; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapter.java new file mode 100644 index 000000000..d5faaf2fd --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event; + +/** + * EventAdapter is the interface that all event adapters must + * implement in order to work with the automatic event adapter generation + * model. This interface requires that the adapter implement a method + * that allows setting the event processor delegated to process the event + * after the adapter has received the event from the event source. The + * task of any event adapter is to receive the event and then delegate it + * to the event processor assigned to it, using either + * eventProcessor.processEvent or eventProcessor.processExceptionableEvent. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @see EventProcessor + */ +public interface EventAdapter { + public void setEventProcessor (EventProcessor eventProcessor); +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterImpl.java b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterImpl.java new file mode 100644 index 000000000..5d2297388 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event; + +/** + * EventAdapterImpl is a default implementation of the EventAdapter + * interface that specific event adapters may choose to subclass from + * instead of implementing the interface themselves. Saves 5 lines of code + * mebbe. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @see EventAdapter + */ +public class EventAdapterImpl implements EventAdapter { + protected EventProcessor eventProcessor; + + public void setEventProcessor (EventProcessor eventProcessor) { + this.eventProcessor = eventProcessor; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterRegistry.java b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterRegistry.java new file mode 100644 index 000000000..1c9d6a7e9 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/EventAdapterRegistry.java @@ -0,0 +1,103 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event; + +import java.util.Hashtable; + +import com.volmit.iris.util.bsf.util.event.generator.EventAdapterGenerator; + +/** + * The EventAdapterRegistry is the registry of event adapters. + * If a desired adapter is not found, the adapter will be dynamically + * generated when lookup is attempted. Set the dynamic property + * to false to disable this feature. + *

+ * This implementation first looks for an adapter in its lookup table + * and if it doesn't find one looks for a standard implementation of + * that adapter in the com.volmit.iris.util.bsf.util.event.adapters package with a + * standard naming convention. The naming convention it assumes is the + * following: for event listener type a.b.c.FooListener, + * it loads an adapter of type + * com.volmit.iris.util.bsf.util.event.adapters.a_b_c_FooAdapter. + * If both the loading and the dynamic generation fail, then a + * null is returned. + *

+ * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @see EventAdapter + */ +public class EventAdapterRegistry { + private static Hashtable reg = new Hashtable (); + private static ClassLoader cl = null; + private static String adapterPackage = "com.volmit.iris.util.bsf.util.event.adapters"; + private static String adapterSuffix = "Adapter"; + private static boolean dynamic = true; + + public static Class lookup (Class listenerType) { + String key = listenerType.getName().replace ('.', '_'); + Class adapterClass = (Class) reg.get (key); + + if (adapterClass == null) { + String en = key.substring (0, key.lastIndexOf ("Listener")); + String cn = adapterPackage + "." + en + adapterSuffix; + + try { + // Try to resolve one. + // adapterClass = (cl != null) ? cl.loadClass (cn) : Class.forName (cn); + adapterClass = (cl != null) ? cl.loadClass (cn) + : Thread.currentThread().getContextClassLoader().loadClass (cn); // rgf, 2006-01-05 + + } catch (ClassNotFoundException e) { + if (dynamic) { + // Unable to resolve one, try to generate one. + adapterClass = // if second argument is set to 'true', then the class file will be stored in the filesystem + EventAdapterGenerator.makeEventAdapterClass (listenerType, false); + } + } + + if (adapterClass != null) { + reg.put (key, adapterClass); + } + } + + return adapterClass; + } + public static void register (Class listenerType, Class eventAdapterClass) { + String key = listenerType.getName().replace('.', '_'); + reg.put (key, eventAdapterClass); + } + /** + * Class loader to use to load event adapter classes. + */ + public static void setClassLoader (ClassLoader cloader) { + cl = cloader; + } + /** + * Indicates whether or not to dynamically generate adapters; default is + * true. + *

+ * If the dynamic property is set to true, and the + * ClassLoader is unable to resolve an adapter, one will be + * dynamically generated. + * + * @param dynamic whether or not to dynamically generate adapters. + */ + public static void setDynamic (boolean dynamic) { + EventAdapterRegistry.dynamic = dynamic; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/EventProcessor.java b/src/main/java/com/volmit/iris/util/bsf/util/event/EventProcessor.java new file mode 100644 index 000000000..7b5db8ac7 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/EventProcessor.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event; + + +/** + * EventProcessor is the interface that event adapters use to + * delegate events they received to be delivered to the appropriate target. + * They can simply deliver the event using processEvent or, if the event + * can be excepted to, via processExceptionableEvent (in which case the + * adapter is expected to forward on an exception to the source bean). + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @see EventAdapter + */ +public interface EventProcessor { + public void processEvent (String filter, Object[] eventInfo); + public void processExceptionableEvent (String filter, Object[] eventInfo) + throws Exception; +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ActionAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ActionAdapter.java new file mode 100644 index 000000000..2ff05d93e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ActionAdapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_ActionAdapter extends EventAdapterImpl + implements ActionListener { + + public void actionPerformed (ActionEvent e) { + eventProcessor.processEvent ("actionPerformed", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_AdjustmentAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_AdjustmentAdapter.java new file mode 100644 index 000000000..b19e2092e --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_AdjustmentAdapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_AdjustmentAdapter extends EventAdapterImpl + implements AdjustmentListener { + + public void adjustmentValueChanged (AdjustmentEvent e) { + eventProcessor.processEvent ("adjustmentValueChanged", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ComponentAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ComponentAdapter.java new file mode 100644 index 000000000..2d5a0add4 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ComponentAdapter.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_ComponentAdapter extends EventAdapterImpl + implements ComponentListener { + + public void componentHidden (ComponentEvent e) { + eventProcessor.processEvent ("componentHidden", new Object[]{e}); + } + public void componentMoved (ComponentEvent e) { + eventProcessor.processEvent ("componentMoved", new Object[]{e}); + } + public void componentResized (ComponentEvent e) { + eventProcessor.processEvent ("componentResized", new Object[]{e}); + } + public void componentShown (ComponentEvent e) { + eventProcessor.processEvent ("componentShown", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ContainerAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ContainerAdapter.java new file mode 100644 index 000000000..74c935d5b --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ContainerAdapter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_ContainerAdapter extends EventAdapterImpl + implements ContainerListener { + + public void componentAdded (ContainerEvent e) { + eventProcessor.processEvent ("componentAdded", new Object[]{e}); + } + public void componentRemoved (ContainerEvent e) { + eventProcessor.processEvent ("componentRemoved", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_FocusAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_FocusAdapter.java new file mode 100644 index 000000000..a7740188d --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_FocusAdapter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_FocusAdapter extends EventAdapterImpl + implements FocusListener { + + public void focusGained (FocusEvent e) { + eventProcessor.processEvent ("focusGained", new Object[]{e}); + } + public void focusLost (FocusEvent e) { + eventProcessor.processEvent ("focusLost", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ItemAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ItemAdapter.java new file mode 100644 index 000000000..9f7508110 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_ItemAdapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_ItemAdapter extends EventAdapterImpl + implements ItemListener { + + public void itemStateChanged (ItemEvent e) { + eventProcessor.processEvent ("itemStateChanged", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_KeyAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_KeyAdapter.java new file mode 100644 index 000000000..21c3a7e2c --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_KeyAdapter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_KeyAdapter extends EventAdapterImpl + implements KeyListener { + + public void keyPressed (KeyEvent e) { + eventProcessor.processEvent ("keyPressed", new Object[]{e}); + } + public void keyReleased (KeyEvent e) { + eventProcessor.processEvent ("keyReleased", new Object[]{e}); + } + public void keyTyped (KeyEvent e) { + eventProcessor.processEvent ("keyTyped", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseAdapter.java new file mode 100644 index 000000000..a0ec4950f --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_MouseAdapter extends EventAdapterImpl + implements MouseListener { + + public void mouseClicked (MouseEvent e) { + eventProcessor.processEvent ("mouseClicked", new Object[]{e}); + } + public void mouseEntered (MouseEvent e) { + eventProcessor.processEvent ("mouseEntered", new Object[]{e}); + } + public void mouseExited (MouseEvent e) { + eventProcessor.processEvent ("mouseExited", new Object[]{e}); + } + public void mousePressed (MouseEvent e) { + eventProcessor.processEvent ("mousePressed", new Object[]{e}); + } + public void mouseReleased (MouseEvent e) { + eventProcessor.processEvent ("mouseReleased", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseMotionAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseMotionAdapter.java new file mode 100644 index 000000000..ec80769e3 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_MouseMotionAdapter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_MouseMotionAdapter extends EventAdapterImpl + implements MouseMotionListener { + + public void mouseDragged(MouseEvent e) { + eventProcessor.processEvent ("mouseDragged", new Object[]{e}); + } + public void mouseMoved(MouseEvent e) { + eventProcessor.processEvent ("mouseMoved", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_TextAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_TextAdapter.java new file mode 100644 index 000000000..f0ab8010a --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_TextAdapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_TextAdapter extends EventAdapterImpl + implements TextListener { + + public void textValueChanged (TextEvent e) { + eventProcessor.processEvent ("textValueChanged", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_WindowAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_WindowAdapter.java new file mode 100644 index 000000000..866387eb3 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_awt_event_WindowAdapter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_awt_event_WindowAdapter extends EventAdapterImpl + implements WindowListener { + + public void windowActivated (WindowEvent e) { + eventProcessor.processEvent ("windowActivated", new Object[]{e}); + } + public void windowClosed (WindowEvent e) { + eventProcessor.processEvent ("windowClosed", new Object[]{e}); + } + public void windowClosing (WindowEvent e) { + eventProcessor.processEvent ("windowClosing", new Object[]{e}); + } + public void windowDeactivated (WindowEvent e) { + eventProcessor.processEvent ("windowDeactivated", new Object[]{e}); + } + public void windowDeiconified (WindowEvent e) { + eventProcessor.processEvent ("windowDeiconified", new Object[]{e}); + } + public void windowIconified (WindowEvent e) { + eventProcessor.processEvent ("windowIconified", new Object[]{e}); + } + public void windowOpened (WindowEvent e) { + eventProcessor.processEvent ("windowOpened", new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_PropertyChangeAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_PropertyChangeAdapter.java new file mode 100644 index 000000000..9adbf4884 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_PropertyChangeAdapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_beans_PropertyChangeAdapter extends EventAdapterImpl + implements PropertyChangeListener { + + public void propertyChange(PropertyChangeEvent e) { + eventProcessor.processEvent (e.getPropertyName(), new Object[]{e}); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_VetoableChangeAdapter.java b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_VetoableChangeAdapter.java new file mode 100644 index 000000000..aafa64a98 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/adapters/java_beans_VetoableChangeAdapter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.adapters; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import com.volmit.iris.util.bsf.util.event.EventAdapterImpl; + +public class java_beans_VetoableChangeAdapter extends EventAdapterImpl + implements VetoableChangeListener { + + public void vetoableChange (PropertyChangeEvent e) throws PropertyVetoException { + try + { + eventProcessor.processExceptionableEvent (e.getPropertyName(), new Object[]{e}); + } + catch (PropertyVetoException ex) + { + throw ex; + } + catch (Exception ex) + { + } + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/generator/AdapterClassLoader.java b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/AdapterClassLoader.java new file mode 100644 index 000000000..59dec6c66 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/AdapterClassLoader.java @@ -0,0 +1,102 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.generator; + +import java.util.Hashtable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class AdapterClassLoader extends ClassLoader +{ + private static Hashtable classCache = new Hashtable(); + private Class c; + + private Log logger = LogFactory.getLog(this.getClass().getName()); + + public AdapterClassLoader() + { + super(); + } + public synchronized Class defineClass(String name, byte[] b) + { + if ((c = getLoadedClass(name)) == null) + { + c = defineClass(name.replace('/','.'), b, 0, b.length); // rgf, 2006-02-03 + put(name, c); + } + else + { + logger.error("AdapterClassLoader: " + c + + " previously loaded. Can not redefine class."); + } + + return c; + } + final protected Class findClass(String name) + { + return get(name); + } + final protected Class get(String name) + { + return (Class)classCache.get(name); + } + public synchronized Class getLoadedClass(String name) + { + Class c = findLoadedClass(name); + + if (c == null) + { + try + { + c = findSystemClass(name); + } + catch (ClassNotFoundException e) + { + } + } + + if (c == null) + { + c = findClass(name); + } + + return c; + } + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = getLoadedClass(name); + + if (c != null && resolve) + { + resolveClass(c); + } + + return c; + } + // Not in JDK 1.1, only in JDK 1.2. +// public AdapterClassLoader(ClassLoader loader) +// { +// super(loader); +// } + + final protected void put(String name, Class c) + { + classCache.put(name, c); + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/generator/ByteUtility.java b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/ByteUtility.java new file mode 100644 index 000000000..1412696cf --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/ByteUtility.java @@ -0,0 +1,331 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.generator; + +/** + * Byte handling utilities + * + * 5 April 1999 - functions to append standard types to byte arrays + * functions to produce standard types from byte arrays + * + * @author Richard F. Boehme + * + */ +public class ByteUtility +{ + public static byte[] addBytes(byte[] array,byte[] value) + { + if( null != array ) + { + byte newarray[] = new byte[array.length + value.length]; + System.arraycopy(array,0,newarray,0,array.length); + System.arraycopy(value,0,newarray,array.length,value.length); + array = newarray; + } + else + { + array = value; + } + return array; + } + public static byte[] addBytes(byte[] array, byte value) + { + if( null != array ) + { + byte newarray[] = new byte[array.length + 1]; + System.arraycopy(array,0,newarray,0,array.length); + newarray[newarray.length-1] = value; + array = newarray; + } + else + { + array = new byte[1]; + array[0] = value; + } + return array; + } + public static byte[] addBytes(byte[] array, int value) + { + if( null != array ) + { + byte newarray[] = new byte[array.length + 3]; + System.arraycopy(array,0,newarray,0,array.length); + newarray[newarray.length-3] = (byte) (( value >> 16 ) & 0xFF); + newarray[newarray.length-2] = (byte) (( value >> 8 ) & 0xFF); + newarray[newarray.length-1] = (byte) ( value & 0xFF); + array = newarray; + } + else + { + array = new byte[3]; + array[0] = (byte) (( value >> 16 ) & 0xFF); + array[1] = (byte) (( value >> 8 ) & 0xFF); + array[2] = (byte) ( value & 0xFF); + } + return array; + } + public static byte[] addBytes(byte[] array, long value) + { + if( null != array ) + { + byte newarray[] = new byte[array.length + 4]; + System.arraycopy(array,0,newarray,0,array.length); + newarray[newarray.length-4] = (byte) (( value >> 24 ) & 0xFF); + newarray[newarray.length-3] = (byte) (( value >> 16 ) & 0xFF); + newarray[newarray.length-2] = (byte) (( value >> 8 ) & 0xFF); + newarray[newarray.length-1] = (byte) ( value & 0xFF); + array = newarray; + } + else + { + array = new byte[4]; + array[0] = (byte) (( value >> 24 ) & 0xFF); + array[1] = (byte) (( value >> 16 ) & 0xFF); + array[2] = (byte) (( value >> 8 ) & 0xFF); + array[3] = (byte) (value & 0xFF); + } + return array; + } + public static byte[] addBytes(byte[] array,String value) + { + if( null != value ) + { + if( null != array) + { + byte newarray[] = new byte[array.length + value.length()]; + System.arraycopy(array,0,newarray,0,array.length); + System.arraycopy(value.getBytes(),0,newarray,array.length,value.length()); + array = newarray; + } + else + { + array = value.getBytes(); + } + } + return array; + } + public static byte[] addBytes(byte[] array, short value) + { + if( null != array) + { + byte newarray[] = new byte[array.length + 2]; + System.arraycopy(array,0,newarray,0,array.length); + newarray[newarray.length-2] = (byte) (( value >> 8 ) & 0xFF); + newarray[newarray.length-1] = (byte) ( value & 0xFF); + array = newarray; + } + else + { + array = new byte[2]; + array[0] = (byte) (( value >> 8 ) & 0xFF); + array[1] = (byte) ( value & 0xFF); + } + return array; + } + public static double byteArrayToDouble(byte high[], byte low[]) + { + double temp = 0; + // high bytes + temp += (((long)high[0]) & 0xFF) << 56; + temp += (((long)high[1]) & 0xFF) << 48; + temp += (((long)high[2]) & 0xFF) << 40; + temp += (((long)high[3]) & 0xFF) << 32; + // low bytes + temp += (((long)low[0]) & 0xFF) << 24; + temp += (((long)low[1]) & 0xFF) << 16; + temp += (((long)low[2]) & 0xFF) << 8; + temp += (((long)low[3]) & 0xFF); + return temp; + } + public static double byteArrayToDouble(byte value[]) + { + byte high[] = new byte[4]; + byte low[] = new byte[4]; + high[0] = value[0]; + high[1] = value[1]; + high[2] = value[2]; + high[3] = value[3]; + low[0] = value[4]; + low[1] = value[5]; + low[2] = value[6]; + low[3] = value[7]; + return byteArrayToDouble(high,low); + } + public static float byteArrayToFloat(byte value[]) + { + float temp = 0; + temp += (((int)value[0]) & 0xFF) << 24; + temp += (((int)value[1]) & 0xFF) << 16; + temp += (((int)value[2]) & 0xFF) << 8; + temp += (((int)value[3]) & 0xFF); + return temp; + } + public static int byteArrayToInt(byte value[]) + { + int temp = 0; + temp += (((int)value[0]) & 0xFF) << 24; + temp += (((int)value[1]) & 0xFF) << 16; + temp += (((int)value[2]) & 0xFF) << 8; + temp += (((int)value[3]) & 0xFF); + return temp; + } + public static long byteArrayToLong(byte value[]) + { + byte high[] = new byte[4]; + byte low[] = new byte[4]; + high[0] = value[0]; + high[1] = value[1]; + high[2] = value[2]; + high[3] = value[3]; + low[0] = value[4]; + low[1] = value[5]; + low[2] = value[6]; + low[3] = value[7]; + return byteArrayToLong(high,low); + } + public static long byteArrayToLong(byte high[], byte low[]) + { + long temp = 0; + // high bytes + temp += (((long)high[0]) & 0xFF) << 56; + temp += (((long)high[1]) & 0xFF) << 48; + temp += (((long)high[2]) & 0xFF) << 40; + temp += (((long)high[3]) & 0xFF) << 32; + // low bytes + temp += (((long)low[0]) & 0xFF) << 24; + temp += (((long)low[1]) & 0xFF) << 16; + temp += (((long)low[2]) & 0xFF) << 8; + temp += (((long)low[3]) & 0xFF); + return temp; + } + // make the following loops with check on array length ***************** + public static short byteArrayToShort(byte value[]) + { + short temp = 0; + temp += (((int)value[0]) & 0xFF) << 8; + temp += (((int)value[1]) & 0xFF); + return temp; + } + public static String byteToHexString(byte value) + { + String temp = null; + + switch( (value & 0xF0) >> 4 ) + { + case 0: + temp = "0"; + break; + case 1: + temp = "1"; + break; + case 2: + temp = "2"; + break; + case 3: + temp = "3"; + break; + case 4: + temp = "4"; + break; + case 5: + temp = "5"; + break; + case 6: + temp = "6"; + break; + case 7: + temp = "7"; + break; + case 8: + temp = "8"; + break; + case 9: + temp = "9"; + break; + case 10: + temp = "A"; + break; + case 11: + temp = "B"; + break; + case 12: + temp = "C"; + break; + case 13: + temp = "D"; + break; + case 14: + temp = "E"; + break; + case 15: + temp = "F"; + break; + } + switch( (value & 0x0F) ) + { + case 0: + temp += "0"; + break; + case 1: + temp += "1"; + break; + case 2: + temp += "2"; + break; + case 3: + temp += "3"; + break; + case 4: + temp += "4"; + break; + case 5: + temp += "5"; + break; + case 6: + temp += "6"; + break; + case 7: + temp += "7"; + break; + case 8: + temp += "8"; + break; + case 9: + temp += "9"; + break; + case 10: + temp += "A"; + break; + case 11: + temp += "B"; + break; + case 12: + temp += "C"; + break; + case 13: + temp += "D"; + break; + case 14: + temp += "E"; + break; + case 15: + temp += "F"; + break; + } + return temp; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/generator/Bytecode.java b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/Bytecode.java new file mode 100644 index 000000000..f89846113 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/Bytecode.java @@ -0,0 +1,105 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.generator; + +/** + * Bytecode handling utilities + * + * Handle standard byte arrays as defined in Java VM and Class File + * + * 5 April 1999 - functions to append Class File byte subarrays + * into a Class File byte array + * + * @author Richard F. Boehme + * + */ +public class Bytecode +{ + // Constant Pool Item Codes + public static final byte C_Utf8 = 0x01; // 1 + public static final byte C_Integer = 0x03; // 3 + public static final byte C_Float = 0x04; // 4 + public static final byte C_Long = 0x05; // 5 + public static final byte C_Double = 0x06; // 6 + public static final byte C_Class = 0x07; // 7 + public static final byte C_String = 0x08; // 8 + public static final byte C_FieldRef = 0x09; // 9 + public static final byte C_MethodRef = 0x0A; // 10 + public static final byte C_InterfaceMethodRef = 0x0B; // 11 + public static final byte C_NameAndType = 0x0C; // 12 + +//public static byte[] addDouble(byte[] array,double value) +//{ +// array = ByteUtility.addBytes(array,C_Double); +// array = ByteUtility.addBytes(array,value); +// return array; +//} + + public static byte[] addClass(byte[] array,short value) + { return addRef(C_Class,array,value); } + public static byte[] addFieldRef(byte[] array,short value1,short value2) + { return addRef(C_FieldRef,array,value1,value2); } + public static byte[] addInteger(byte[] array,int value) + { + array = ByteUtility.addBytes(array,C_Integer); + array = ByteUtility.addBytes(array,value); + return array; + } + public static byte[] addInterfaceMethodRef(byte[] array,short value1,short value2) + { return addRef(C_InterfaceMethodRef,array,value1,value2); } +//public static byte[] addFloat(byte[] array,float value) +//{ +// array = ByteUtility.addBytes(array,C_Float); +// array = ByteUtility.addBytes(array,value); +// return array; +//} + + public static byte[] addLong(byte[] array,long value) + { + array = ByteUtility.addBytes(array,C_Long); + array = ByteUtility.addBytes(array,value); + return array; + } + public static byte[] addMethodRef(byte[] array,short value1,short value2) + { return addRef(C_MethodRef,array,value1,value2); } + public static byte[] addNameAndType(byte[] array,short value1,short value2) + { return addRef(C_NameAndType,array,value1,value2); } + public static byte[] addRef(byte refType,byte[] array,short value) + { + array = ByteUtility.addBytes(array,refType); + array = ByteUtility.addBytes(array,value); + return array; + } + // Generic Bytecode Methods + public static byte[] addRef(byte refType,byte[] array,short value1,short value2) + { + array = ByteUtility.addBytes(array,refType); + array = ByteUtility.addBytes(array,value1); + array = ByteUtility.addBytes(array,value2); + return array; + } + public static byte[] addString(byte[] array,short value) + { return addRef(C_String,array,value); } + // Constant Pool Item Methods + public static byte[] addUtf8(byte[] array,String value) + { + array = ByteUtility.addBytes(array,C_Utf8); + array = ByteUtility.addBytes(array,(short)value.length()); + array = ByteUtility.addBytes(array,value); + return array; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/event/generator/EventAdapterGenerator.java b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/EventAdapterGenerator.java new file mode 100644 index 000000000..c38dcbe74 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/event/generator/EventAdapterGenerator.java @@ -0,0 +1,568 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.event.generator; + +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** EventAdapterGenerator + * + * Generate an "Event Adapter" dynamically during program execution + * + **/ +public class EventAdapterGenerator +{ + public static AdapterClassLoader ldr = new AdapterClassLoader(); + static Class EVENTLISTENER = null; + static String CLASSPACKAGE = "org/apache/bsf/util/event/adapters/"; + static String WRITEDIRECTORY = null; + + // starting 8 bytes of all Java Class files + static byte CLASSHEADER[]; + // constant pool items found in all event adapters + static short BASECPCOUNT; // number of cp items + 1 ( cp item # 0 reserved for JVM ) + static byte BASECP[]; // + // some bytes in the middle of the class file (see below) + static byte FIXEDCLASSBYTES[]; + // the initialization method, noargs constructor + static byte INITMETHOD[]; + + private static Log logger; + + /* The static initializer */ + static + { + logger = LogFactory.getLog( + (com.volmit.iris.util.bsf.util.event.generator.EventAdapterGenerator.class).getName()); + + String USERCLASSPACKAGE = System.getProperty("DynamicEventClassPackage", + ""); + + if (!USERCLASSPACKAGE.equals("")) + { + CLASSPACKAGE = USERCLASSPACKAGE; + } + + if(CLASSPACKAGE.length() > 0 ) + { + CLASSPACKAGE = CLASSPACKAGE.replace('\\','/'); + if(!CLASSPACKAGE.endsWith("/")) + { CLASSPACKAGE = CLASSPACKAGE+"/"; } + } + WRITEDIRECTORY = System.getProperty("DynamicEventClassWriteDirectory",CLASSPACKAGE); + if(WRITEDIRECTORY.length() > 0 ) + { + WRITEDIRECTORY = WRITEDIRECTORY.replace('\\','/'); + if(!WRITEDIRECTORY.endsWith("/")) + { WRITEDIRECTORY = WRITEDIRECTORY+"/"; } + } + try + // { EVENTLISTENER = Class.forName("java.util.EventListener"); } + { EVENTLISTENER = Thread.currentThread().getContextClassLoader().loadClass ("java.util.EventListener"); } // rgf, 2006-01-05 + catch(ClassNotFoundException ex) + { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + + + // start of the Java Class File + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xCA); // magic + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xFE); // magic + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xBA); // magic + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xBE); // magic + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(short)3); // minor version + CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(short)45); // major version + + // Start the constant pool for base items in all event adapter classes + BASECPCOUNT = 17; // number of cp items + 1 ( cp item # 0 reserved for JVM ) + + // cp item 01 + BASECP = Bytecode.addUtf8(BASECP,"()V"); + + // cp item 02 + BASECP = Bytecode.addUtf8(BASECP,""); + + // cp item 03 + BASECP = Bytecode.addUtf8(BASECP,"Code"); + + // cp item 04 + BASECP = Bytecode.addUtf8(BASECP,"eventProcessor"); + + // cp item 05 + BASECP = Bytecode.addUtf8(BASECP,"java/lang/Object"); + + // cp item 06 + BASECP = Bytecode.addUtf8(BASECP,"org/apache/bsf/util/event/EventAdapterImpl"); + + // cp item 07 + BASECP = Bytecode.addUtf8(BASECP,"org/apache/bsf/util/event/EventProcessor"); + + // cp item 08 + BASECP = Bytecode.addUtf8(BASECP,"(Ljava/lang/String;[Ljava/lang/Object;)V"); + + // cp item 09 + BASECP = Bytecode.addUtf8(BASECP,"Lorg/apache/bsf/util/event/EventProcessor;"); + + // cp item 10 + BASECP = Bytecode.addClass(BASECP,(short)5); // Class "java/lang/Object" + + // cp item 11 + BASECP = Bytecode.addClass(BASECP,(short)6); // Class "org/apache/bsf/util/event/EventAdapterImpl" + + // cp item 12 + BASECP = Bytecode.addClass(BASECP,(short)7); // Class "org/apache/bsf/util/event/EventProcessor" + + // cp item 13 + BASECP = Bytecode.addNameAndType(BASECP,(short)2,(short)1); // "" "()V" + + // cp item 14 + BASECP = Bytecode.addNameAndType(BASECP,(short)4,(short)9); // "eventProcessor" "Lorg/apache/bsf/util/event/EventProcessor;" + + // cp item 15 + BASECP = Bytecode.addFieldRef(BASECP,(short)11,(short)14); + + // cp item 16 + BASECP = Bytecode.addMethodRef(BASECP,(short)11,(short)13); + + // fixed bytes in middle of class file + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)0x21); // access_flags (fixed) + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)20); // this_class (fixed) + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)11); // super_class (fixed) + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)1); // interface_count (fixed) + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)19); // interfaces (fixed) + FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)0); // field_count (fixed) + + // initialization method, constructor + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1); // access_flags + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)2); // name_index "" + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1); // descriptor_index "()V" + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1); // attribute_count + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)3); // attribute_name_index "Code" + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(long)17); // attribute_length + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1); // max_stack + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1); // max_locals + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(long)5); // code_length + //code + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0x2A); // aload_0 + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0xB7); // invokespecial + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)16); // method_ref index + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0xB1); // return + // exception table + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)0); // exception_table_length + INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)0); // attributes_count + + } + + /* methods that take an EventListener Class Type to create an EventAdapterClass */ + public static Class makeEventAdapterClass(Class listenerType,boolean writeClassFile) + { + logger.info("EventAdapterGenerator"); + + if( EVENTLISTENER.isAssignableFrom(listenerType) ) + { + boolean exceptionable = false; + boolean nonExceptionable = false; + byte constantPool[] = null; + short cpBaseIndex; + short cpCount = 0; + short cpExceptionBaseIndex; + short exceptionableCount; + short nonExceptionableCount; + + /* Derive Names */ + String listenerTypeName = listenerType.getName(); + logger.info("ListenerTypeName: "+listenerTypeName); + String adapterClassName = + CLASSPACKAGE+ + (listenerTypeName.endsWith("Listener") + ? listenerTypeName.substring(0, listenerTypeName.length() - 8) + : listenerTypeName).replace('.', '_') + + "Adapter"; + String finalAdapterClassName = adapterClassName; + Class cached = null; + int suffixIndex = 0; + + do + { + if (null != (cached = ldr.getLoadedClass(finalAdapterClassName))) + { + logger.info("cached: "+cached); + try + { + if (!listenerType.isAssignableFrom(cached)) + finalAdapterClassName = adapterClassName + "_" + suffixIndex++; + else + return cached; + } + catch(VerifyError ex) + { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + return cached; + } + } + } + while (cached != null); + + String eventListenerName = listenerTypeName.replace('.', '/'); + + /* method stuff */ + java.lang.reflect.Method lms[] = listenerType.getMethods(); + + /* ****************************************************************************************** */ + // Listener interface + // Class name + cpCount += 4; + + // cp item 17 + constantPool = Bytecode.addUtf8(constantPool,eventListenerName); + + // cp item 18 + constantPool = Bytecode.addUtf8(constantPool,finalAdapterClassName); + + // cp item 19 + constantPool = Bytecode.addClass(constantPool,(short)17); + + // cp item 20 + constantPool = Bytecode.addClass(constantPool,(short)18); + + // do we have nonExceptionalble event, exceptionable or both + for (int i = 0 ; i < lms.length ; ++i) + { + Class exceptionTypes[] = lms[i].getExceptionTypes(); + if( 0 < exceptionTypes.length) + { exceptionable = true; } + else + { nonExceptionable = true; } + }/* End for*/ + + /* ****************************************************************************************** */ + // optional inclusion of nonexceptional events affects exceptional events indices + + nonExceptionableCount = 0; + if(nonExceptionable) + { + nonExceptionableCount = 3; + cpCount += nonExceptionableCount; + + // cp item 21 + constantPool = Bytecode.addUtf8(constantPool,"processEvent"); + + // cp item 22 + constantPool = Bytecode.addNameAndType(constantPool,(short)21,(short)8); + + + // cp item 23 + constantPool = Bytecode.addInterfaceMethodRef(constantPool,(short)12,(short)22); + } + + /* ****************************************************************************************** */ + // optional inclusion of exceptional events affects CP Items which follow for specific methods + + exceptionableCount = 0; + if(exceptionable) + { + int classIndex = BASECPCOUNT + cpCount + 1; + int nameIndex = BASECPCOUNT + cpCount + 0; + int natIndex = BASECPCOUNT + cpCount + 3; + + exceptionableCount = 5; + cpCount += exceptionableCount; + + // cp item 24 or 21 + constantPool = Bytecode.addUtf8(constantPool,"processExceptionableEvent"); + + // cp item 25 or 22 + constantPool = Bytecode.addUtf8(constantPool,"java/lang/Exception"); + + // cp item 26 or 23 + constantPool = Bytecode.addClass(constantPool,(short)classIndex); + + // cp item 27 or 24 + constantPool = Bytecode.addNameAndType(constantPool,(short)nameIndex,(short)8); + + // cp item 28 or 25 + constantPool = Bytecode.addInterfaceMethodRef(constantPool,(short)12,(short)natIndex); + + } + + // base index for method cp references + cpBaseIndex = (short)(BASECPCOUNT + cpCount); + logger.debug("cpBaseIndex: " + cpBaseIndex); + + for (int i = 0 ; i < lms.length ; ++i) + { + String eventMethodName = lms[i].getName(); + String eventName = lms[i].getParameterTypes()[0].getName().replace('.','/'); + cpCount += 3; + // cp items for event methods + constantPool = Bytecode.addUtf8(constantPool,eventMethodName); + constantPool = Bytecode.addUtf8(constantPool,("(L" + eventName + ";)V")); + constantPool = Bytecode.addString(constantPool,(short)(BASECPCOUNT+cpCount-3)); + }/* End for*/ + + boolean propertyChangeFlag[] = new boolean[lms.length]; + int cpIndexPCE = 0; + for (int i = 0 ; i < lms.length ; ++i) + { + String eventName = lms[i].getParameterTypes()[0].getName().replace('.','/'); + // cp items for PropertyChangeEvent special handling + if(eventName.equalsIgnoreCase("java/beans/PropertyChangeEvent")) + { + propertyChangeFlag[i] = true; + if( 0 == cpIndexPCE ) + { + constantPool = Bytecode.addUtf8(constantPool,eventName); + constantPool = Bytecode.addUtf8(constantPool,"getPropertyName"); + constantPool = Bytecode.addUtf8(constantPool,"()Ljava/lang/String;"); + constantPool = Bytecode.addClass(constantPool,(short)(BASECPCOUNT + cpCount)); + constantPool = Bytecode.addNameAndType(constantPool, + (short)(BASECPCOUNT + cpCount + 1), + (short)(BASECPCOUNT + cpCount + 2)); + constantPool = Bytecode.addMethodRef(constantPool, + (short)(BASECPCOUNT + cpCount + 3), + (short)(BASECPCOUNT + cpCount + 4)); + cpCount += 6; + cpIndexPCE = BASECPCOUNT + cpCount - 1; + + } + } + else + { propertyChangeFlag[i] = false; } + }/* End for*/ + + cpExceptionBaseIndex = (short)(BASECPCOUNT + cpCount); + logger.debug("cpExceptionBaseIndex: " + cpExceptionBaseIndex); + + int excpIndex[][] = new int[lms.length][]; + for (int i = 0 ; i < lms.length ; ++i) + { + Class exceptionTypes[] = lms[i].getExceptionTypes(); + excpIndex[i] = new int[exceptionTypes.length]; + for ( int j = 0 ; j < exceptionTypes.length ; j++) + { + constantPool = Bytecode.addUtf8(constantPool,exceptionTypes[j].getName().replace('.', '/')); + constantPool = Bytecode.addClass(constantPool,(short)(BASECPCOUNT+cpCount)); + excpIndex[i][j] = BASECPCOUNT + cpCount + 1; + cpCount += 2; + } + }/* End for*/ + /* end constant pool */ + + /* ************************************************************************************************ */ + // put the Class byte array together + + /* start */ + byte newClass[] = CLASSHEADER; // magic, version (fixed) + short count = (short)(BASECPCOUNT + cpCount); + newClass = ByteUtility.addBytes(newClass,count); // constant_pool_count (variable) + newClass = ByteUtility.addBytes(newClass,BASECP); // constant_pool (fixed) + newClass = ByteUtility.addBytes(newClass,constantPool); // constant_pool (variable) + newClass = ByteUtility.addBytes(newClass,FIXEDCLASSBYTES); // see FIXEDCLASSBYTES (fixed) + newClass = ByteUtility.addBytes(newClass,(short)(lms.length+1)); // method_count (variable) + newClass = ByteUtility.addBytes(newClass,INITMETHOD); // constructor (fixed) + // methods + + /* ****************************************************************************************** */ + /* loop over listener methods from listenerType */ + for (int i = 0 ; i < lms.length ; ++i) + { + newClass = ByteUtility.addBytes(newClass,(short)1); // access_flags (fixed) + newClass = ByteUtility.addBytes(newClass,(short)(cpBaseIndex+3*i+0)); // name_index (variable) + newClass = ByteUtility.addBytes(newClass,(short)(cpBaseIndex+3*i+1)); // descriptor_index (variable) + newClass = ByteUtility.addBytes(newClass,(short)1); // attribute_count (fixed) + newClass = ByteUtility.addBytes(newClass,(short)3); // attribute_name_index code(fixed) + + // Code Attribute Length + int length = 32; + if( 0 < excpIndex[i].length ) + { length += 5 + 8 * ( 1 + excpIndex[i].length ); } + if(propertyChangeFlag[i]) + { length += 2; } + newClass = ByteUtility.addBytes(newClass,(long)length); // attribute_length (variable) + + // start code attribute + newClass = ByteUtility.addBytes(newClass,(short)6); // max_stack (fixed) + newClass = ByteUtility.addBytes(newClass,(short)3); // max_locals (fixed) + + // Code Length + length = 20; + if(exceptionable && 0 < excpIndex[i].length) + { length += 5; } + if(propertyChangeFlag[i]) + { length += 2; } + newClass = ByteUtility.addBytes(newClass,(long)length); // code_length (variable) + + // start code + newClass = ByteUtility.addBytes(newClass,(byte)0x2A); // aload_0 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xB4); // getfield (fixed) + newClass = ByteUtility.addBytes(newClass,(short)15); // index (fixed) + + + if(propertyChangeFlag[i]) + { // the propertyName is passed as the first parameter + newClass = ByteUtility.addBytes(newClass,(byte)0x2B); // aload_1 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xB6); // invokevirtual (fixed) + newClass = ByteUtility.addBytes(newClass,(short)cpIndexPCE); // methodref (variable) + } + else + { // the eventMethodName is passed as the first parameter + // Target for method invocation. + newClass = ByteUtility.addBytes(newClass,(byte)0x12); // ldc (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)(cpBaseIndex+3*i+2)); // index (byte) (variable) + } + + newClass = ByteUtility.addBytes(newClass,(byte)0x04); // iconst_1 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xBD); // anewarray (fixed) + newClass = ByteUtility.addBytes(newClass,(short)10); // Class java/lang/Object (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x59); // dup (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x03); // iconst_0 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x2B); // aload_1 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x53); // aastore (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xB9); // invokeinterface (fixed) + + // index to processEvent or processExceptionableEvent method + length = 23; // actually an index into cp + if(exceptionable && nonExceptionable) + { // interface method index + if( 0 < lms[i].getExceptionTypes().length ) + { length += 5; } + } + else if(exceptionable) + { length += 2; } + newClass = ByteUtility.addBytes(newClass,(short)length); // index (process??????...) (variable) + + newClass = ByteUtility.addBytes(newClass,(byte)0x03); // iconst_0 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x00); // noop (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xB1); // return (fixed) + + if(exceptionable && 0 < excpIndex[i].length) + { // exception code + newClass = ByteUtility.addBytes(newClass,(byte)0x4D); // astore_2 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x2C); // aload_2 (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xBF); // athrow (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0x57); // pop (fixed) + newClass = ByteUtility.addBytes(newClass,(byte)0xB1); // return (fixed) + // end code + + // exception table + length = excpIndex[i].length; + newClass = ByteUtility.addBytes(newClass,(short)(1+length)); // exception_table_length (variable) + for( int j = 0 ; j < length ; j++ ) + { // catch exception types and rethrow + newClass = ByteUtility.addBytes(newClass,(short)0); // start_pc (fixed) + if(propertyChangeFlag[i]) + { + newClass = ByteUtility.addBytes(newClass,(short)21); // end_pc (fixed) + newClass = ByteUtility.addBytes(newClass,(short)22); // handler_pc (fixed) + } + else + { + newClass = ByteUtility.addBytes(newClass,(short)19); // end_pc (fixed) + newClass = ByteUtility.addBytes(newClass,(short)20); // handler_pc (fixed) + } + newClass = ByteUtility.addBytes(newClass,(short)excpIndex[i][j]); // catch_type (variable) + } + // catch "exception" and trap it + newClass = ByteUtility.addBytes(newClass,(short)0); // start_pc (fixed) + if(propertyChangeFlag[i]) + { + newClass = ByteUtility.addBytes(newClass,(short)21); // end_pc (fixed) + newClass = ByteUtility.addBytes(newClass,(short)25); // handler_pc (fixed) + } + else + { + newClass = ByteUtility.addBytes(newClass,(short)19); // end_pc (fixed) + newClass = ByteUtility.addBytes(newClass,(short)23); // handler_pc (fixed) + } + if(nonExceptionable) + { newClass = ByteUtility.addBytes(newClass,(short)26); } // catch_type (fixed) + else // or + { newClass = ByteUtility.addBytes(newClass,(short)23); } // catch_type (fixed) + } + else + { newClass = ByteUtility.addBytes(newClass,(short)0); } // exception_table_length (fixed) + // attributes on the code attribute (none) + newClass = ByteUtility.addBytes(newClass,(short)0); // attribute_count (fixed) + // end code attribute + + + }/* End for*/ + // Class Attributes (none for this) + newClass = ByteUtility.addBytes(newClass,(short)0); // attribute_count (fixed) + /* done */ + + logger.debug("adapterName: " + finalAdapterClassName); + logger.debug("cpCount: " + count + " = " + BASECPCOUNT + " + " + cpCount); + logger.debug("methodCount: " + (lms.length+1)); + // output to disk class file + /* ****************************************************************************************** */ + + // now create the class and load it + // return the Class. + + if (writeClassFile) + { + try + { + // removed "WRITEDIRECTORY+", as this path is already part of 'finalAdapterClassName' + FileOutputStream fos = new FileOutputStream(finalAdapterClassName+".class"); + fos.write(newClass); + fos.close(); + } + catch(IOException ex) + { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + + try + { + Class ret = ldr.loadClass(finalAdapterClassName); + logger.debug("EventAdapterGenerator: " + + ret.getName() + + " dynamically generated"); + return ret; + } + catch (ClassNotFoundException ex) + { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + } + + try + { + Class ret = ldr.defineClass(finalAdapterClassName,newClass); + logger.debug("EventAdapterGenerator: " + + ret.getName() + + " dynamically generated"); + return ret; + } + catch(Exception ex) + { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + } + return null; + } +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertor.java b/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertor.java new file mode 100644 index 000000000..9ded872a1 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertor.java @@ -0,0 +1,31 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.type; + +/** + * A TypeConvertor is used to convert an object of one type to + * one of another type. The convertor is invoked with the class of the + * from object, the desired class, and the from object itself. The + * convertor must return a new object of the desired class. + * + * @author Sanjiva Weerawarana + * @see TypeConvertorRegistry + */ +public interface TypeConvertor { + public Object convert (Class from, Class to, Object obj); + public String getCodeGenString (); +} diff --git a/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertorRegistry.java b/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertorRegistry.java new file mode 100644 index 000000000..693e464ee --- /dev/null +++ b/src/main/java/com/volmit/iris/util/bsf/util/type/TypeConvertorRegistry.java @@ -0,0 +1,206 @@ +/* + * Copyright 2004,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.volmit.iris.util.bsf.util.type; + +import java.awt.Color; +import java.awt.Font; +import java.util.Hashtable; + +/** + * The TypeConvertorRegistry is the registry of type convertors. + * It has lookup and register capabilities based on the types to be + * converted as well as by some object key. + * + * @author Sanjiva Weerawarana + * @author Matthew J. Duftler + * @see TypeConvertorRegistry + */ +public class TypeConvertorRegistry { + Hashtable reg = new Hashtable (); + Hashtable keyedReg = new Hashtable (); + + // register some standard convertors at construction time + public TypeConvertorRegistry () { + // no-op convertors: cvt from primitive wrappers to the object wrapper + TypeConvertor tc = new TypeConvertor () { + public Object convert (Class from, Class to, Object obj) { + return obj; + } + + public String getCodeGenString() { + return "(Class from, Class to, Object obj) {\n" + + "return obj;\n" + + "}"; + } + }; + register (Boolean.class, boolean.class, tc); + register (boolean.class, Boolean.class, tc); + register (Byte.class, byte.class, tc); + register (byte.class, Byte.class, tc); + register (Character.class, char.class, tc); + register (char.class, Character.class, tc); + register (Short.class, short.class, tc); + register (short.class, Short.class, tc); + register (Integer.class, int.class, tc); + register (int.class, Integer.class, tc); + register (Long.class, long.class, tc); + register (long.class, Long.class, tc); + register (Float.class, float.class, tc); + register (float.class, Float.class, tc); + register (Double.class, double.class, tc); + register (double.class, Double.class, tc); + + // object to string: the registry special cases this one as the backup + // if the target is string and there is no special convertor available + // otherwise + tc = new TypeConvertor () { + public Object convert (Class from, Class to, Object obj) { + return (obj == null) ? "(null)" : obj.toString (); + } + + public String getCodeGenString() { + return "(Class from, Class to, Object obj) {\n" + + "return (obj == null) ? \"(null)\" : obj.toString ();\n" + + "}"; + } + }; + register (Object.class, String.class, tc); + + // convert strings to various primitives (both their object versions + // and wrappers for primitive versions) + tc = new TypeConvertor () { + public Object convert (Class from, Class to, Object obj) { + String str = (String) obj; + if (to == Boolean.class || to == boolean.class) { + return Boolean.valueOf (str); + } else if (to == Byte.class || to == byte.class) { + return Byte.valueOf (str); + } else if (to == Character.class || to == char.class) { + return new Character (str.charAt (0)); + } else if (to == Short.class || to == short.class) { + return Short.valueOf (str); + } else if (to == Integer.class || to == int.class) { + return Integer.valueOf (str); + } else if (to == Long.class || to == long.class) { + return Long.valueOf (str); + } else if (to == Float.class || to == float.class) { + return Float.valueOf (str); + } else if (to == Double.class || to == double.class) { + return Double.valueOf (str); + } else { + return null; + } + } + + public String getCodeGenString() { + return "(Class from, Class to, Object obj) {\n" + + "String str = (String) obj;\n" + + "if (to == Boolean.class || to == boolean.class) {\n" + + "return Boolean.valueOf (str);\n" + + "} else if (to == Byte.class || to == byte.class) {\n" + + "return Byte.valueOf (str);\n" + + "} else if (to == Character.class || to == char.class) {\n" + + "return new Character (str.charAt (0));\n" + + "} else if (to == Short.class || to == short.class) {\n" + + "return Short.valueOf (str);\n" + + "} else if (to == Integer.class || to == int.class) {\n" + + "return Integer.valueOf (str);\n" + + "} else if (to == Long.class || to == long.class) {\n" + + "return Long.valueOf (str);\n" + + "} else if (to == Float.class || to == float.class) {\n" + + "return Float.valueOf (str);\n" + + "} else if (to == Double.class || to == double.class) {\n" + + "return Double.valueOf (str);\n" + + "} else {\n" + + "return null;\n" + + "}\n" + + "}"; + } + }; + register (String.class, boolean.class, tc); + register (String.class, Boolean.class, tc); + register (String.class, byte.class, tc); + register (String.class, Byte.class, tc); + register (String.class, char.class, tc); + register (String.class, Character.class, tc); + register (String.class, short.class, tc); + register (String.class, Short.class, tc); + register (String.class, int.class, tc); + register (String.class, Integer.class, tc); + register (String.class, long.class, tc); + register (String.class, Long.class, tc); + register (String.class, float.class, tc); + register (String.class, Float.class, tc); + register (String.class, double.class, tc); + register (String.class, Double.class, tc); + + // strings to fonts + tc = new TypeConvertor () { + public Object convert (Class from, Class to, Object obj) { + return Font.decode ((String) obj); + } + + public String getCodeGenString() { + return "(Class from, Class to, Object obj) {\n" + + "return Font.decode ((String) obj);\n" + + "}"; + } + }; + register (String.class, Font.class, tc); + + // strings to colors + tc = new TypeConvertor () { + public Object convert (Class from, Class to, Object obj) { + return Color.decode ((String) obj); + } + + public String getCodeGenString() { + return "(Class from, Class to, Object obj) {\n" + + "return Color.decode ((String) obj);\n" + + "}"; + } + }; + register (String.class, Color.class, tc); + } + // lookup a convertor + public TypeConvertor lookup (Class from, Class to) { + String key = from.getName () + " -> " + to.getName (); + TypeConvertor tc = (TypeConvertor) reg.get (key); + if (tc == null) { + if (from != void.class + && from != Void.class + && to == String.class) { + // find the object -> string convertor + return lookup (Object.class, String.class); + } + } + return tc; + } + // lookup a convertor by key + public TypeConvertor lookupByKey (Object key) { + return (TypeConvertor) keyedReg.get (key); + } + // register a convertor + public void register (Class from, Class to, TypeConvertor convertor) { + String key = from.getName () + " -> " + to.getName (); + reg.put (key, convertor); + } + // register a convertor by key + public void registerByKey (Object key, TypeConvertor convertor) { + keyedReg.put (key, convertor); + } +}