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