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