1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.script.framework.provider.beanshell;
24 
25 import com.sun.star.uno.XComponentContext;
26 import com.sun.star.lang.XMultiComponentFactory;
27 import com.sun.star.lang.XMultiServiceFactory;
28 import com.sun.star.lang.XSingleServiceFactory;
29 import com.sun.star.registry.XRegistryKey;
30 import com.sun.star.comp.loader.FactoryHelper;
31 import com.sun.star.frame.XModel;
32 import com.sun.star.uno.Type;
33 import com.sun.star.uno.Any;
34 
35 import com.sun.star.reflection.InvocationTargetException;
36 
37 import java.util.StringTokenizer;
38 
39 import java.net.URL;
40 
41 import bsh.Interpreter;
42 
43 import com.sun.star.document.XScriptInvocationContext;
44 import com.sun.star.script.provider.XScript;
45 import com.sun.star.script.provider.ScriptErrorRaisedException;
46 import com.sun.star.script.provider.ScriptExceptionRaisedException;
47 import com.sun.star.script.provider.ScriptFrameworkErrorException;
48 import com.sun.star.script.provider.ScriptFrameworkErrorType;
49 
50 
51 import com.sun.star.script.framework.provider.*;
52 import com.sun.star.script.framework.log.*;
53 import com.sun.star.script.framework.container.ScriptMetaData;
54 
55 public class ScriptProviderForBeanShell
56 {
57     public static class _ScriptProviderForBeanShell extends ScriptProvider
58     {
_ScriptProviderForBeanShell(XComponentContext ctx)59         public _ScriptProviderForBeanShell(XComponentContext ctx)
60         {
61             super (ctx, "BeanShell");
62         }
63 
getScript( String scriptURI )64         public XScript getScript( /*IN*/String scriptURI )
65             throws com.sun.star.uno.RuntimeException,
66                    ScriptFrameworkErrorException
67         {
68             ScriptMetaData scriptData = null;
69             try
70             {
71                 scriptData = getScriptData( scriptURI );
72                 ScriptImpl script = new ScriptImpl( m_xContext, scriptData, m_xModel, m_xInvocContext );
73                 return script;
74             }
75             catch ( com.sun.star.uno.RuntimeException re )
76             {
77                 throw new ScriptFrameworkErrorException( "Failed to create script object: " + re.getMessage(),
78                     null, scriptData.getLanguageName(), language, ScriptFrameworkErrorType.UNKNOWN );
79             }
80         }
81 
hasScriptEditor()82         public boolean hasScriptEditor()
83         {
84             return true;
85         }
86 
getScriptEditor()87         public ScriptEditor getScriptEditor()
88         {
89             return ScriptEditorForBeanShell.getEditor();
90         }
91     }
92 
93     /**
94      * Returns a factory for creating the service.
95      * This method is called by the <code>JavaLoader</code>
96      * <p>
97      *
98      * @param  implName      the name of the implementation for which a service is desired
99      * @param  multiFactory  the service manager to be used if needed
100      * @param  regKey        the registryKey
101      * @return               returns a <code>XSingleServiceFactory</code> for creating
102      *                          the component
103      * @see                  com.sun.star.comp.loader.JavaLoader
104      */
__getServiceFactory( String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey )105     public static XSingleServiceFactory __getServiceFactory( String implName,
106             XMultiServiceFactory multiFactory,
107             XRegistryKey regKey )
108     {
109         XSingleServiceFactory xSingleServiceFactory = null;
110 
111         if ( implName.equals( ScriptProviderForBeanShell._ScriptProviderForBeanShell.class.getName() ) )
112         {
113             xSingleServiceFactory = FactoryHelper.getServiceFactory(
114                 ScriptProviderForBeanShell._ScriptProviderForBeanShell.class,
115                 "com.sun.star.script.ScriptProviderForBeanShell",
116                 multiFactory,
117                 regKey );
118         }
119 
120         return xSingleServiceFactory;
121     }
122 }
123 
124 class ScriptImpl implements XScript
125 {
126     private ScriptMetaData metaData;
127     private XComponentContext m_xContext;
128     private XMultiComponentFactory m_xMultiComponentFactory;
129     private XModel m_xModel;
130     private XScriptInvocationContext m_xInvocContext;
131 
ScriptImpl( XComponentContext ctx, ScriptMetaData metaData, XModel xModel, XScriptInvocationContext xContext )132     ScriptImpl( XComponentContext ctx, ScriptMetaData metaData, XModel xModel,
133             XScriptInvocationContext xContext ) throws com.sun.star.uno.RuntimeException
134     {
135         this.metaData = metaData;
136         this.m_xContext = ctx;
137         this.m_xModel = xModel;
138         this.m_xInvocContext = xContext;
139 
140         try
141         {
142             this.m_xMultiComponentFactory = m_xContext.getServiceManager();
143         }
144         catch ( Exception e )
145         {
146             LogUtils.DEBUG( LogUtils.getTrace( e ) );
147             throw new com.sun.star.uno.RuntimeException(
148                 "Error constructing  ScriptImpl [beanshell]: "
149                 + e.getMessage() );
150         }
151 
152         LogUtils.DEBUG("ScriptImpl [beanshell] script data = " + metaData );
153     }
154         /**
155          *                          documentStorageID and document reference
156          *                          for use in script name resolving
157          *
158          * @param aParams           All parameters; pure, out params are
159          *                          undefined in sequence, i.e., the value
160          *                          has to be ignored by the callee
161          *
162          * @param aOutParamIndex    Out indices
163          *
164          * @param aOutParam         Out parameters
165          *
166          * @returns                 The value returned from the function
167          *                          being invoked
168          *
169          * @throws IllegalArgumentException If there is no matching script name
170          *
171          * @throws CannotConvertException   If args do not match or cannot
172          *                                  be converted the those of the
173          *                                  invokee
174          *
175          * @throws InvocationTargetException If the running script throws
176          *                                   an exception this information
177          *                                   is captured and rethrown as
178          *                                   this exception type.
179          */
180 
invoke( Object[] aParams, short[][] aOutParamIndex, Object[][] aOutParam )181         public Object invoke( /*IN*/Object[] aParams,
182                             /*OUT*/short[][] aOutParamIndex,
183                             /*OUT*/Object[][] aOutParam )
184             throws ScriptFrameworkErrorException,
185                 InvocationTargetException
186         {
187             // Initialise the out paramters - not used at the moment
188             aOutParamIndex[0] = new short[0];
189             aOutParam[0] = new Object[0];
190 
191 
192             ClassLoader cl = null;
193             URL sourceUrl = null;
194             try {
195                 cl = ClassLoaderFactory.getURLClassLoader( metaData );
196                 sourceUrl = metaData.getSourceURL();
197             }
198             catch ( java.net.MalformedURLException mfu )
199             {
200                 // Framework error
201                 throw new ScriptFrameworkErrorException(
202                     mfu.getMessage(), null,
203                     metaData.getLanguageName(), metaData.getLanguage(),
204                     ScriptFrameworkErrorType.MALFORMED_URL );
205             }
206             catch ( NoSuitableClassLoaderException nsc )
207             {
208                 // Framework error
209                 throw new ScriptFrameworkErrorException(
210                     nsc.getMessage(), null,
211                     metaData.getLanguageName(), metaData.getLanguage(),
212                     ScriptFrameworkErrorType.UNKNOWN );
213             }
214             // Set class loader to be used for class files
215             // and jar files
216             Thread.currentThread().setContextClassLoader(cl);
217             Interpreter interpreter = new Interpreter();
218 
219             interpreter.getNameSpace().clear();
220             // Set class loader to be used by interpreter
221             // to look for classes by source e.g. interpreter
222             // will use this classloader to search classpath
223             // for source file ( bla.java ) on import or reference
224             interpreter.setClassLoader(cl);
225             try {
226                 interpreter.set("XSCRIPTCONTEXT",
227                     ScriptContext.createContext(m_xModel, m_xInvocContext,
228                         m_xContext, m_xMultiComponentFactory));
229 
230                 interpreter.set("ARGUMENTS", aParams);
231             }
232             catch (bsh.EvalError e) {
233                 // Framework error setting up context
234                 throw new ScriptFrameworkErrorException(
235                     e.getMessage(), null,
236                     metaData.getLanguageName(), metaData.getLanguage(),
237                     ScriptFrameworkErrorType.UNKNOWN );
238             }
239 
240             try {
241                 String source = null;
242                 Object result = null;
243 
244                 ScriptEditorForBeanShell editor =
245                     ScriptEditorForBeanShell.getEditor(
246                        sourceUrl );
247 
248                 if ( editor != null )
249                 {
250                     result = editor.execute();
251 
252                     if (result == null)
253                     {
254                         return new Any(new Type(), null);
255                     }
256                     return result;
257                 }
258 
259                 metaData.loadSource();
260                 source = metaData.getSource();
261 
262                 if ( source == null || source.length() == 0 )
263                 {
264                 	throw new ScriptFrameworkErrorException(
265                         "Failed to read script", null,
266                     	metaData.getLanguageName(), metaData.getLanguage(),
267                     	ScriptFrameworkErrorType.NO_SUCH_SCRIPT );
268                 }
269                 result = interpreter.eval( source );
270 
271                 if (result == null)
272                 {
273                     return new Any(new Type(), null);
274                 }
275                 return result;
276             }
277             catch ( bsh.ParseException pe )
278             {
279                 throw new InvocationTargetException( "Beanshell failed to parse " + metaData.getLanguageName(), null, processBshException( pe, metaData.getLanguageName() ) );
280             }
281             catch ( bsh.TargetError te )
282             {
283                 throw new InvocationTargetException( "Beanshell uncaught exception for " + metaData.getLanguageName(), null, processBshException( te, metaData.getLanguageName() ) );
284             }
285             catch ( bsh.EvalError ex )
286             {
287                 throw new InvocationTargetException( "Beanshell error for " + metaData.getLanguageName(), null, processBshException( ex, metaData.getLanguageName() ) );
288             }
289             catch ( Exception e )
290             {
291                 throw new ScriptFrameworkErrorException(
292                     "Failed to read script", null,
293                     metaData.getLanguageName(), metaData.getLanguage(),
294                     ScriptFrameworkErrorType.UNKNOWN );
295             }
296         }
raiseEditor( int lineNum )297         private void raiseEditor( int lineNum )
298         {
299             ScriptEditorForBeanShell editor = null;
300             try
301             {
302                 URL sourceUrl = metaData.getSourceURL();
303                 editor = ScriptEditorForBeanShell.getEditor( sourceUrl );
304                 if ( editor == null )
305                 {
306                     editor = ScriptEditorForBeanShell.getEditor();
307                     editor.edit(
308                         ScriptContext.createContext(m_xModel, m_xInvocContext,
309                             m_xContext, m_xMultiComponentFactory), metaData );
310                     editor = ScriptEditorForBeanShell.getEditor( sourceUrl );
311                 }
312                 if ( editor != null )
313                 {
314                     editor.indicateErrorLine( lineNum );
315                 }
316             }
317             catch( Exception ignore )
318             {
319             }
320         }
321 
processBshException( bsh.EvalError e, String script )322         private ScriptErrorRaisedException processBshException( bsh.EvalError e, String script  )
323         {
324             LogUtils.DEBUG("Beanshell error RAW message " + e.getMessage());
325             String message = e.getMessage();
326             int usefullInfoIndex = message.lastIndexOf("\' :" );
327             int lineNum = e.getErrorLineNumber();
328 
329             raiseEditor( lineNum );
330 
331             //String stackTrace = te.getScriptStackTrace();  // never seems to have any info??
332             if ( usefullInfoIndex > -1 )
333             {
334                 message = message.substring( usefullInfoIndex + 2 );
335             }
336             if ( e instanceof bsh.TargetError )
337             {
338                 LogUtils.DEBUG("got instance of  TargetError");
339                 if ( usefullInfoIndex == -1 )
340                 {
341                     message =  ( ( bsh.TargetError)e ).getTarget().getMessage();
342                 }
343                 String wrappedException = "";
344                 String full = e.toString();
345                 int index = full.indexOf( "Target exception:" );
346                 if ( index > -1 )
347                 {
348                     String toParse = full.substring( index );
349                     LogUtils.DEBUG("About to parse " + toParse );
350                     StringTokenizer tokenizer = new StringTokenizer( full.substring( index ),":" );
351                     if ( tokenizer.countTokens() > 2 )
352                     {
353                         LogUtils.DEBUG("First token = " + (String)tokenizer.nextElement());
354                         wrappedException = (String)tokenizer.nextElement();
355                         LogUtils.DEBUG("wrapped exception = = " + wrappedException );
356                     }
357                 }
358                 ScriptExceptionRaisedException se = new ScriptExceptionRaisedException( message);
359                 se.lineNum = lineNum;
360                 se.scriptName = script;
361                 se.exceptionType = wrappedException;
362                 se.language = "BeanShell";
363                 LogUtils.DEBUG("UnCaught Exception error: " );
364                 LogUtils.DEBUG("\tscript: " + script );
365                 LogUtils.DEBUG("\tline: " + lineNum );
366                 LogUtils.DEBUG("\twrapped exception: " + wrappedException );
367                 LogUtils.DEBUG("\tmessage: " + message );
368                 return se;
369             }
370             else
371             {
372                 LogUtils.DEBUG("Error or ParseError Exception error: " );
373                 LogUtils.DEBUG("\tscript: " + script );
374                 LogUtils.DEBUG("\tline: " + lineNum );
375                 LogUtils.DEBUG("\tmessage: " + message );
376                 return new ScriptErrorRaisedException( message, null, script, "BeanShell", lineNum );
377             }
378         }
379 }
380