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