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 basicrunner;
24 
25 
26 import com.sun.star.beans.PropertyValue;
27 import com.sun.star.beans.XPropertySet;
28 import com.sun.star.connection.ConnectionSetupException;
29 import com.sun.star.container.ContainerEvent;
30 import com.sun.star.container.XContainer;
31 import com.sun.star.container.XContainerListener;
32 import com.sun.star.container.XNameContainer;
33 import com.sun.star.frame.XComponentLoader;
34 import com.sun.star.frame.XDesktop;
35 import com.sun.star.lang.WrappedTargetException;
36 import com.sun.star.lang.XComponent;
37 import com.sun.star.lang.XMultiServiceFactory;
38 import com.sun.star.lang.XServiceInfo;
39 import com.sun.star.lang.XSingleServiceFactory;
40 import com.sun.star.lang.XTypeProvider;
41 import com.sun.star.uno.Type;
42 import com.sun.star.uno.UnoRuntime;
43 import com.sun.star.util.XChangesBatch;
44 import java.util.Hashtable;
45 import lib.TestParameters;
46 
47 import share.LogWriter;
48 
49 
50 /**
51  * This class is a java-part of BASIC-java interaction "driver"
52  * It is used to call Star-Basic's function from java using
53  * basic's part of "driver" where listeners are implemented.
54  * The instance of the BasicHandler should be added to the MSF that will be
55  * used for loading BASIC's part of "driver".<br>
56  * After opening basic's document it creates an instance of the
57  * HandlerContainer using BasicHandler. HandlerContainer is a UNO
58  * XContainer and XNameContainer.
59  * Only one instance of BasicHandler can be used at the moment.
60  * @see com.sun.star.lang.XServiceInfo
61  * @see com.sun.star.lang.XSingleServiceFactory
62  */
63 public class BasicHandler implements XServiceInfo, XSingleServiceFactory {
64     /**
65      * serviceName is the name of service that can be created in BASIC.
66      */
67     static final String serviceName =
68                             "com.sun.star.jsuite.basicrunner.BasicHandler";
69 
70     /**
71      * <code>container</code> is a SHARED variable (between BASIC and Java).
72      * It is used for interacting.
73      */
74     static private HandlerContainer container = null;
75 
76     /**
77      * Contains a writer to log an information about the interface testing, to
78      * allows for tests to access it.
79      */
80     static private LogWriter log;
81 
82     /**
83      * <code>oHandlerDoc</code> is a referrence to BASIC's document.
84      */
85     static private XComponent oHandlerDoc = null;
86 
87     /**
88      * <code>xMSF</code> is a MultiServiceFactory currently used by
89      * BasicHandler.
90      */
91     static private XMultiServiceFactory xMSF = null;
92 
93     /**
94      * Interface being tested now.
95      */
96     static private BasicIfcTest TestedInterface = null;
97 
98     /**
99      * Ab enhanced scheme of timeouts can be used with BASIC tests.
100      * A small timeout can be used zo wait for changes in the test status.
101      * <code>respFlag</code> is set to <code>true</code> when a BASIC test
102      * writes any log information.
103      */
104     static private boolean respFlag = false;
105 
106     /**
107      * <code>iBasicTimeout</code> is the amount of milliseconds that
108      * the BasicHandler will wait for a response from tests
109      * (finish to execute a method or add log information)
110      * before it decides that SOffice is dead.
111      */
112     static private int iBasicTimeout = 10000;
113 
114 
115 
116     /**
117      * Creates an instance of a HandlerContainer. This instance is used from
118      * BASIC.
119      * @param tParam The test parameters.
120      */
BasicHandler(TestParameters tParam)121     public BasicHandler(TestParameters tParam) {
122         if (tParam.get("soapi.test.basic.debugFile") != null) {
123             iBasicTimeout = 0; // Debug mode.
124         }
125         container = new HandlerContainer(this);
126     }
127 
128     /**
129      * Set the tested interface and a log writer.
130      * @param ifc The test of an interface
131      * @param log A log writer.
132      */
setTestedInterface(BasicIfcTest ifc, LogWriter log)133     public void setTestedInterface(BasicIfcTest ifc, LogWriter log) {
134         this.log = log;
135         TestedInterface = ifc;
136     }
137 
138     /**
139      * Is called when BASIC signals that it has performed the test of a method.
140      * @param methodName The name of the method.
141      * @bResult The result of the test.
142      */
methodTested(String methodName, boolean bResult)143     synchronized void methodTested(String methodName, boolean bResult) {
144         respFlag = true;
145         TestedInterface.methodTested(methodName, bResult);
146         notify() ;
147     }
148 
149     /**
150      * Is called when BASIC sends a signal to write some log information.
151      * @param info The string to write.
152      */
Log(String info)153     synchronized public void Log(String info) {
154         respFlag = true;
155         log.println(info);
156         notify() ;
157     }
158 
159     /**
160      * Is called by BasicIfcTest to find out if this BasicHandler uses the
161      * correct MultiServiceFactory.
162      * @param xMSF The MultiServiceFactory
163      * @see com.sun.star.lang.XMultiServiceFactory
164      * @return True, if xMSF is equal to the MultiServiceFactory of this class.
165      */
isUptodate(XMultiServiceFactory xMSF)166     public boolean isUptodate(XMultiServiceFactory xMSF) {
167         return xMSF.equals(this.xMSF);
168     }
169 
170 
171     /**
172      * Establishes a connection between BASIC and Java.
173      * If required, hte BASIC part of the "driver" is loaded.
174      * @param sBasicBridgeURL The URL of the basic bridge document
175      *                                           (BasicBridge.sxw)
176      * @param tParam The test parameters.
177      * @param xMSF The MultiServiceFactory
178      * @param log The log writer.
179      * @see com.sun.star.lang.XMultiServiceFactory
180      * @throws ConnectionSetupException Exception is thrown, if no connection could be made.
181      */
Connect(String sBasicBridgeURL, TestParameters tParam, XMultiServiceFactory xMSF, LogWriter log)182     public synchronized void Connect(String sBasicBridgeURL,
183                             TestParameters tParam, XMultiServiceFactory xMSF,
184                             LogWriter log) throws ConnectionSetupException {
185         this.log = log;
186         try {
187             this.xMSF = xMSF;
188             Object oInterface = xMSF.createInstance(
189                                                 "com.sun.star.frame.Desktop");
190             XDesktop oDesktop = (XDesktop) UnoRuntime.queryInterface(
191                                                 XDesktop.class, oInterface);
192             XComponentLoader oCLoader = (XComponentLoader)
193                                         UnoRuntime.queryInterface(
194                                         XComponentLoader.class, oDesktop);
195 
196             // load BasicBridge with MarcoEceutionMode = Always-no warn
197             //PropertyValue[] DocArgs = null;
198             PropertyValue[] DocArgs = new PropertyValue[1];
199             PropertyValue DocArg = new PropertyValue();
200             DocArg.Name = "MacroExecutionMode";
201             DocArg.Value = new Short(
202                     com.sun.star.document.MacroExecMode.ALWAYS_EXECUTE_NO_WARN);
203             DocArgs[0] = DocArg;
204 
205             // configure Office to allow to execute macos
206             PropertyValue [] ProvArgs = new PropertyValue [1];
207             PropertyValue Arg = new PropertyValue();
208             Arg.Name = "nodepath";
209             Arg.Value = "/org.openoffice.Office.Common/Security";
210             ProvArgs[0] = Arg;
211 
212             Object oProvider = xMSF.createInstance(
213                         "com.sun.star.configuration.ConfigurationProvider");
214 
215             XMultiServiceFactory oProviderMSF = (XMultiServiceFactory)
216                                     UnoRuntime.queryInterface(
217                                     XMultiServiceFactory.class, oProvider);
218 
219             Object oSecure = oProviderMSF.createInstanceWithArguments(
220                         "com.sun.star.configuration.ConfigurationUpdateAccess",
221                         ProvArgs);
222 
223             XPropertySet oSecureProps = (XPropertySet)
224                         UnoRuntime.queryInterface(XPropertySet.class, oSecure);
225 
226             Object oScripting = oSecureProps.getPropertyValue("Scripting");
227             XPropertySet oScriptingSettings = (XPropertySet)
228                     UnoRuntime.queryInterface(XPropertySet.class, oScripting);
229 
230             oScriptingSettings.setPropertyValue("Warning", Boolean.FALSE);
231             oScriptingSettings.setPropertyValue("OfficeBasic", new Integer(2));
232 
233             XChangesBatch oSecureChange = (XChangesBatch)
234                     UnoRuntime.queryInterface(XChangesBatch.class, oSecure);
235             oSecureChange.commitChanges();
236 
237             // As we want to have some information about a debugFile
238             // BEFORE connection is established
239             // we pass the information about it in frame name.
240             String sFrameName = (String)tParam.get(
241                                                 "soapi.test.basic.debugFile");
242             if (sFrameName == null) sFrameName = "BasicRunner";
243 
244             oHandlerDoc = oCLoader.loadComponentFromURL(sBasicBridgeURL,
245                                                     sFrameName, 40, DocArgs);
246 
247             do {
248                 respFlag = false ;
249                 wait(10000); // waiting for basic response for 10 seconds.
250             } while (respFlag && !container.hasByName("BASIC_Done")) ;
251 
252             if (!container.hasByName("BASIC_Done")) {
253                 throw new ConnectionSetupException("Connection timed out.");
254             }
255         } catch (Exception e) {
256             System.out.println("Exception: " + e.toString());
257             throw new ConnectionSetupException();
258         }
259 
260         log.println("Java-BASIC connection established!");
261     }
262 
263     /**
264      * Overloads perform(Strin fName, Object params) for convenience.
265      * @return A proprty value as result.
266      *
267     public synchronized PropertyValue perform(String fName)
268                                                         throws BasicException {
269         return perform(fName, "");
270     }
271 */
272     /**
273      * Perform a test of a method.
274      * @param fName The name of the method to test.
275      * @param params The test parameters.
276      * @return A proprty value as result of the test.
277      * @throws BasicException The method could not be executed.
278      */
perform(String fName, Object params)279     public synchronized PropertyValue perform(String fName, Object params)
280                                                         throws BasicException {
281         try {
282             container.callBasicFunction(fName, params);
283 
284             do {
285                 respFlag = false;
286                 // waiting for basic response for iBasicTimeout milliseconds.
287                 wait(iBasicTimeout);
288             } while(respFlag && !container.hasByName("BASIC_Done"));
289 
290         } catch (InterruptedException e) {
291             System.out.println("The operation " + fName + " was interrupted.");
292         } catch (com.sun.star.lang.DisposedException de) {
293             System.out.println("## Office is disposed");
294         }
295 
296         if (!container.hasByName("BASIC_Done")) {
297             System.out.println("Operation timed out.");
298                 throw new BasicException(
299                             "Operation timed out.");
300         }
301 
302         Object res = container.getByName("BASIC_Done") ;
303         container.removeByName("BASIC_Done");
304 
305         if (!(res instanceof PropertyValue)) {
306             if (res == null) {
307                 System.out.println(
308                             "BasicBridge returns null");
309                 throw new BasicException(
310                             "BasicBridge returns null");
311             } else {
312                 System.out.println(
313                             "BasicBridge returns wrong type: " + res.getClass());
314                 throw new BasicException(
315                             "BasicBridge returns wrong type: " + res.getClass());
316             }
317         }
318 
319         PropertyValue result = (PropertyValue) res ;
320 
321         if ((result.Value instanceof String) && (((String)result.Value)).startsWith("Exception")) {
322             throw new BasicException((String)result.Value);
323         }
324 
325         return result;
326     }
327 
328     /**
329      * Returns true, if name is a supported service of this class.
330      * @param name The service name.
331      * @return True, if the service is supported.
332      */
supportsService(String name)333     public boolean supportsService(String name) {
334         return serviceName.equals(name);
335     }
336 
337     /**
338      * Return all supported service names.
339      * @return All supported services.
340      */
getSupportedServiceNames()341     public String[] getSupportedServiceNames() {
342         return new String[] {serviceName};
343     }
344 
345     /**
346      * Get the implementation name.
347      * @return Implementation name.
348      */
getImplementationName()349     public String getImplementationName() {
350         return getClass().getName();
351     }
352 
353     /**
354      * Create an instance of HandlerContainer.
355      * Arguments are not supported here, so they will be ignored.
356      * @param args The arguments.
357      * @return The instance.
358      */
createInstanceWithArguments(Object[] args)359     public Object createInstanceWithArguments(Object[] args) {
360         return container;
361     }
362 
363     /**
364      * Create an instance of HandlerContainer.
365      * @return The instance.
366      */
createInstance()367     public Object createInstance() {
368         return createInstanceWithArguments(null);
369     }
370 
371     /**
372      * Dispose the BASIC document.
373      */
dispose()374     public synchronized void dispose() {
375         try {
376             if (oHandlerDoc != null) {
377                 //oHandlerDoc.dispose();
378                 util.DesktopTools.closeDoc(oHandlerDoc);
379                 wait(1000);
380             }
381         } catch (Exception e) {
382             System.out.println("Exception: " + e.toString());
383         }
384     }
385 }
386 
387 
388 /**
389  * This class handles the communication between Java and BASIC.
390  * @see com.sun.star.container.XContainer
391  * @see com.sun.star.container.XNameContainer
392  * @see com.sun.star.lang.XTypeProvider
393  */
394 class HandlerContainer implements XContainer, XNameContainer, XTypeProvider{
395 
396     /** Container for parameters.
397      **/
398     Hashtable container = new Hashtable(20);
399     /**
400      * An array of listeners for container events.
401      * @see com.sun.star.container.XContainerListener
402      */
403     static XContainerListener[] listener = null;
404 
405     /** The BasicHandler belonging to this handler. **/
406     BasicHandler parent = null;
407 
408     /**
409      * Constructor with the parent BasicHandler.
410      * @param par The BasicHandler.
411      */
HandlerContainer(BasicHandler par)412     public HandlerContainer(BasicHandler par) {
413         parent = par;
414     }
415 
416     /**
417      * Call a BASIC function, meaning a test method.
418      * @param fName The method name.
419      * @param args Arguments for the method.
420      */
callBasicFunction(String fName, Object args)421     public void callBasicFunction(String fName, Object args) {
422         // BASIC's listener should be called ONLY in this case.
423         if (container.containsKey(fName)) {
424             container.remove(fName);
425         }
426         container.put(fName, args);
427         if (listener != null) {
428             ContainerEvent event = new ContainerEvent();
429             event.Element = fName;
430             for (int i=0; i<listener.length; i++){
431                 if (listener[i] != null) {
432                     listener[i].elementInserted(event);
433                 }
434             }
435         }
436     }
437 
438     /**
439      * Insert an object into the container.
440      * @param name The key for the object.
441      * @param object The object to insert.
442      * @throws IllegalArgumentException Throws this exception when trying to insert null.
443      */
insertByName(String name, Object object)444     public void insertByName(String name, Object object) throws com.sun.star.lang.IllegalArgumentException, com.sun.star.container.ElementExistException, com.sun.star.lang.WrappedTargetException {
445 
446         // BASIC and Java can insert into the container.
447         if (container.containsKey(name)) {
448             container.remove(name);
449         }
450         container.put(name, object);
451 
452         PropertyValue result = null ;
453 
454         if (object instanceof PropertyValue) {
455             result = (PropertyValue)object;
456             if (name.equals("BASIC_Done")) {
457                 synchronized (parent) {
458                     parent.notify();
459                 }
460             } else if (name.equals("BASIC_MethodTested")) {
461                 parent.methodTested(result.Name,
462                                 ((Boolean)result.Value).booleanValue());
463             }
464         } else if (name.equals("BASIC_Log")) {
465             parent.Log(object.toString());
466         }
467     }
468 
469     /**
470      * Remove the object with this name from the container.
471      * @param name The key.
472      */
removeByName(String name)473     public void removeByName(String name) {
474         container.remove(name) ;
475     }
476 
477     /**
478      * Unsupported method.
479      * @param name The name of the key.
480      * @param value The value.
481      * @throws WrappedTargetException Throws this exception when called falsely.
482      */
replaceByName(String name, Object value)483     public void replaceByName(String name, Object value)
484                                           throws WrappedTargetException {
485         throw new WrappedTargetException("Unsupported");
486     }
487 
488     /**
489      * Has a value for this key.
490      * @param name The name of a key.
491      * @return True, if name exists as key in the container.
492      */
hasByName(String name)493     public boolean hasByName(String name) {
494         return container.containsKey(name);
495     }
496 
497     /**
498      * Get an object by its key.
499      * @param name The name of the key.
500      * @return The object of this key.
501      */
getByName(String name)502     public Object getByName(String name) {
503         return container.get(name);
504     }
505 
506     /**
507      * Get all key names.
508      * @return All names of keys.
509      */
getElementNames()510     public String[] getElementNames() {
511         String[] res = new String[container.size()];
512         return (String[])container.keySet().toArray(res);
513     }
514 
515     /**
516      * Is the xcontainer empty?
517      * @return True, if the container has elements.
518      */
hasElements()519     public boolean hasElements() {
520         return !container.isEmpty();
521     }
522 
523     /**
524      * Get the type of this class.
525      * @return The type of this class.
526      */
getElementType()527     public Type getElementType() {
528         try {
529             return new Type(String.class);
530         } catch (Exception e) {
531             return null;
532         }
533     }
534 
535     /**
536      * Get the implementation id of this class.
537      * @return A unique id for this class
538      * @see com.sun.star.lang.XTypeProvider
539      */
getImplementationId()540     public byte[] getImplementationId() {
541         return toString().getBytes();
542     }
543 
544     /**
545      * Get all types of this class.
546      * @return All implemented UNO types.
547      */
getTypes()548     public Type[] getTypes() {
549         Class interfaces[] = getClass().getInterfaces();
550         Type types[] = new Type[interfaces.length];
551         for(int i = 0; i < interfaces.length; ++ i) {
552             types[i] = new Type(interfaces[i]);
553         }
554         return types;
555     }
556 
557     /**
558      * Add a listener
559      * @param xListener The listener.
560      */
addContainerListener(XContainerListener xListener)561     public void addContainerListener(XContainerListener xListener){
562         int length = 0;
563         if (listener != null)
564             length = listener.length;
565 
566         XContainerListener[] mListener =
567                         new XContainerListener[length+1];
568         for (int i=0; i<length-1; i++) {
569             mListener[i] = listener[i];
570             // listener already added
571             if (((Object)xListener).equals(listener[i]))
572                 return;
573         }
574         mListener[length] = xListener;
575         listener = mListener;
576     }
577 
578     /**
579      * Remove a listener
580      * @param xListener The listener.
581      */
removeContainerListener(XContainerListener xListener)582     public void removeContainerListener(XContainerListener xListener){
583         if (listener != null && listener.length != 0) {
584             int length = listener.length;
585             XContainerListener[] mListener =
586                                     new XContainerListener[length-1];
587             boolean found = false;
588             int j=0;
589             for (int i=0; i<length-1; i++) {
590                 if (!((Object)xListener).equals(listener[j])) {
591                     mListener[i] = listener[j];
592                 }
593                 else {
594                     j++;
595                     found = true;
596                 }
597                 j++;
598             }
599             if (!found) {
600                 if (((Object)xListener).equals(listener[length-1]))
601                     listener = mListener;
602             }
603             else
604                 listener = mListener;
605 
606         }
607     }
608 }
609