ludc
2025-01-16 986aa62ed00bee39363bab41b4eeb8259d446efd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package org.jbpm.pvm.internal.script;
 
import java.io.*;
import java.util.Map;
import javax.script.*;
import bsh.*;
import static javax.script.ScriptContext.*;
 
/*
    Notes
    This engine supports open-ended pluggable scriptcontexts
*/
public class BshScriptEngine extends AbstractScriptEngine
    implements Compilable, Invocable
{
    // The BeanShell global namespace for the interpreter is stored in the
    // engine scope map under this key.
    static final String engineNameSpaceKey = "org_beanshell_engine_namespace";
 
    private BshScriptEngineFactory factory;
    private bsh.Interpreter interpreter;
 
    public BshScriptEngine() {
        this( null );
    }
 
    public BshScriptEngine( BshScriptEngineFactory factory ) 
    {
        this.factory = factory;
        getInterpreter(); // go ahead and prime the interpreter now
    }
 
    protected Interpreter getInterpreter()
    {
        if ( interpreter == null ) {
            this.interpreter = new bsh.Interpreter();
            interpreter.setNameSpace(null); // should always be set by context
        }
 
        return interpreter;
    }
 
    public Object eval( String script, ScriptContext scriptContext )
        throws ScriptException
    {
        return evalSource( script, scriptContext );
    }
 
    public Object eval( Reader reader, ScriptContext scriptContext )
        throws ScriptException
    {
        return evalSource( reader, scriptContext );
    }
 
    /*
        This is the primary implementation method.
        We respect the String/Reader difference here in BeanShell because
        BeanShell will do a few extra things in the string case... e.g.
        tack on a trailing ";" semicolon if necessary.
    */
    private Object evalSource( Object source, ScriptContext scriptContext )
        throws ScriptException
    {
        bsh.NameSpace contextNameSpace = getEngineNameSpace( scriptContext );
        Interpreter bsh = getInterpreter();
        bsh.setNameSpace( contextNameSpace );
 
        // This is a big hack, convert writer to PrintStream
        bsh.setOut( new PrintStream(
            new WriterOutputStream( scriptContext.getWriter() ) ) );
        bsh.setErr( new PrintStream(
            new WriterOutputStream( scriptContext.getErrorWriter() ) ) );
 
        try {
            if ( source instanceof Reader )
                return bsh.eval( (Reader) source );
            else
                return bsh.eval( (String) source );
        } catch ( ParseException e ) {
            // explicit parsing error
            throw new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
        } catch ( TargetError e ) {
            // The script threw an application level exception
            // set it as the cause ?
            ScriptException se = new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
            se.initCause( e.getTarget() );
            throw se;
        } catch ( EvalError e ) {
            // The script couldn't be evaluated properly
            throw new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
        } catch ( InterpreterError e ) {
            // The interpreter had a fatal problem
            throw new ScriptException( e.toString() );
        }
    }
 
 
 
    /*
        Check the context for an existing global namespace embedded
        in the script context engine scope.  If none exists, ininitialize the
        context with one.
    */
    private static NameSpace getEngineNameSpace( ScriptContext scriptContext )
    {
        NameSpace ns = (NameSpace)scriptContext.getAttribute(
            engineNameSpaceKey, ENGINE_SCOPE );
 
        if ( ns == null )
        {
            // Create a global namespace for the interpreter
            Map engineView = new ScriptContextEngineView( scriptContext );
            ns = new ExternalNameSpace(
                null/*parent*/, "javax_script_context", engineView );
 
            scriptContext.setAttribute( engineNameSpaceKey, ns, ENGINE_SCOPE );
        }
 
        return ns;
    }
 
    public Bindings createBindings()
    {
        return new SimpleBindings();
    }
 
    public ScriptEngineFactory getFactory()
    {
        if ( factory == null )
            factory = new BshScriptEngineFactory();
        return factory;
    }
 
    /**
     * Compiles the script (source represented as a <code>String</code>) for later
     * execution.
     *
     * @param script The source of the script, represented as a
     * <code>String</code>.
     *
     * @return An subclass of <code>CompiledScript</code> to be executed later
     *         using one of the <code>eval</code> methods of <code>CompiledScript</code>.
     *
     * @throws ScriptException if compilation fails.
     * @throws NullPointerException if the argument is null.
     */
 
    public CompiledScript compile( String script ) throws
        ScriptException
    {
        return compile( new StringReader( script ) );
    }
 
    /**
     * Compiles the script (source read from <code>Reader</code>) for later
     * execution.  Functionality is identical to <code>compile(String)</code> other
     * than the way in which the source is passed.
     *
     * @param script The reader from which the script source is obtained.
     *
     * @return An implementation of <code>CompiledScript</code> to be executed
     *         later using one of its <code>eval</code> methods of
     *         <code>CompiledScript</code>.
     *
     * @throws ScriptException if compilation fails.
     * @throws NullPointerException if argument is null.
     */
    public CompiledScript compile( Reader script ) throws
        ScriptException
    {
        // todo
        throw new Error("unimplemented");
    }
 
    /**
     * Calls a procedure compiled during a previous script execution, which is
     * retained in the state of the <code>ScriptEngine<code>.
     *
     * @param name The name of the script method to be called.
     * @param thiz thiz is an instance of the script class returned by a previous execution or
     * invocation, the named method is called through that instance.
     * @param args Arguments to pass to the procedure.  The rules for converting
     * the arguments to scripting variables are implementation-specific.
     *
     * @return The value returned by the method.  The rules for converting the
     *         scripting variable returned by the procedure to a Java Object are
     *         implementation-specific.
     *
     * @throws javax.script.ScriptException if an error occurrs during invocation
     * of the method.
     * @throws NoSuchMethodException if method with given name or matching argument
     * types cannot be found.
     * @throws NullPointerException if method name is null.
     */
    public Object invokeMethod( Object thiz, String name, Object... args ) throws ScriptException, NoSuchMethodException
    {
        if ( ! (thiz instanceof bsh.This) )
            throw new ScriptException( "Illegal objec type: " +thiz.getClass() );
 
        bsh.This bshObject = (bsh.This)thiz;
 
        try {
            return bshObject.invokeMethod( name, args );
        } catch ( ParseException e ) {
            // explicit parsing error
            throw new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
        } catch ( TargetError e ) {
            // The script threw an application level exception
            // set it as the cause ?
            ScriptException se = new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
            se.initCause( e.getTarget() );
            throw se;
        } catch ( EvalError e ) {
            // The script couldn't be evaluated properly
            throw new ScriptException(
                e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber() );
        } catch ( InterpreterError e ) {
            // The interpreter had a fatal problem
            throw new ScriptException( e.toString() );
        }
    }
 
    /**
     * Used to call top-level procedures defined in scripts.
     *
     * @param name Name of the procedure
     * @param args Arguments to pass to the procedure
     *
     * @return The value returned by the procedure
     *
     * @throws javax.script.ScriptException if an error occurrs during invocation
     * of the method.
     * @throws NoSuchMethodException if procedure with given name or matching
     * argument types cannot be found.
     * @throws NullPointerException if procedure name is null.
     */
    public Object invokeFunction( String name, Object... args )
        throws ScriptException, NoSuchMethodException
    {
        return invokeMethod( getGlobal(), name, args );
    }
 
        /**
     * Returns an implementation of an interface using procedures compiled in the
     * interpreter. The methods of the interface may be implemented using the
     * <code>invokeFunction</code> method.
     *
     * @param clasz The <code>Class</code> object of the interface to return.
     *
     * @return An instance of requested interface - null if the requested interface
     *         is unavailable, i. e. if compiled methods in the
     *         <code>ScriptEngine</code> cannot be found matching the ones in the
     *         requested interface.
     *
     * @throws IllegalArgumentException if the specified <code>Class</code> object
     * does not exist or is not an interface.
     */
    public <T> T getInterface( Class<T> clasz )
    {
        try {
            return (T) getGlobal().getInterface( clasz );
        } catch ( UtilEvalError utilEvalError ) {
            utilEvalError.printStackTrace();
            return null;
        }
    }
 
    /**
     * Returns an implementation of an interface using member functions of a
     * scripting object compiled in the interpreter. The methods of the interface
     * may be implemented using invokeMethod(Object, String, Object...) method.
     *
     * @param thiz The scripting object whose member functions are used to
     * implement the methods of the interface.
     * @param clasz The <code>Class</code> object of the interface to return.
     *
     * @return An instance of requested interface - null if the requested
     *         interface is unavailable, i. e. if compiled methods in the
     *         <code>ScriptEngine</code> cannot be found matching the ones in the
     *         requested interface.
     *
     * @throws IllegalArgumentException if the specified <code>Class</code> object
     * does not exist or is not an interface, or if the specified Object is null
     * or does not represent a scripting object.
     */
    public <T> T getInterface( Object thiz, Class<T> clasz )
    {
        if ( !(thiz instanceof bsh.This) )
            throw new IllegalArgumentException(
                "invalid object type: "+thiz.getClass() );
 
        try {
            bsh.This bshThis = (bsh.This)thiz;
            return (T) bshThis.getInterface( clasz );
        } catch ( UtilEvalError utilEvalError ) {
            utilEvalError.printStackTrace( System.err );
            return null;
        }
    }
 
    private bsh.This getGlobal()
    {
        // requires 2.0b5 to make getThis() public
        return getEngineNameSpace( getContext() ).getThis( getInterpreter() );
    }
 
    /*
        This is a total hack.  We need to introduce a writer to the
        Interpreter.
    */
    class WriterOutputStream extends OutputStream
    {
        Writer writer;
        WriterOutputStream( Writer writer )
        {
            this.writer = writer;
        }
 
        public void write( int b ) throws IOException
        {
            writer.write(b);
        }
 
        public void flush() throws IOException
        {
            writer.flush();
        }
 
        public void close() throws IOException
        {
            writer.close();
        }
    }
 
}