Generate own UNO services with OO Basic

Shared Libraries
Forum rules
For sharing working examples of macros / scripts. These can be in any script language supported by OpenOffice.org [Basic, Python, Netbean] or as source code files in Java or C# even - but requires the actual source code listing. This section is not for asking questions about writing your own macros.
Post Reply
ms777
Volunteer
Posts: 177
Joined: Mon Oct 08, 2007 1:33 am

Generate own UNO services with OO Basic

Post by ms777 »

Hi,

just to start with the disclaimers:
  1. besides OO Basic, we need also a Beanshell Script
  2. it is limited to services implementing interfaces known to UNO
The following beanshell script will enable you to define own services, which later can be used just like all other services from UNO: they can be created using e.g. createUnoServices, they can be XRayed, etc.

A complete explanation of the beanshell code (listenercombiner.bsh) would be too much. Good thing is: To use it, you do not have to understand it. listenercombiner expects one single parameter, which is an array. Its first element is a string with the service name, under which you want to register the created service. The following arguments are listeners generated by createUnoListener. The short OO Basic example uses only two very simple listeners, but you can expand to whatever number you want.
The generated UNO service then has all methods of the listener available as you can see by calling the methods or XRaying the object.

So what is this good for ? The reason I wrote this is that it allows you to gain complete design freedom over the custom toolbars using OO Basic only. In order to show self designed elements in the toolbar you have to implement an UNO service and register it with XUIControllerRegistration.registerController.

So stay tuned for some examples showing fancy toolbars ...

In order to run this
  • for safety create a new calc document
  • install listenercombiner.bsh into the documents beanshell code, using a library named also listenercombiner
  • copy the basic code into some module in the same document.
ms777

Edit: Two examples are here: http://user.services.openoffice.org/en/ ... =21&t=4352

Edit May 26, 2008: I recently switched from OO 2.1 to OO 2.4 and found out that the beanshell code in this post does not work any longer. This is probably due to a change in the class loading happening in OO 2.3, which made the OO Beanshell incompatible with the 'normal' beanshell ( http://qa.openoffice.org/issues/show_bug.cgi?id=89978 ). In the next post, you find some code which works for OO2.3 (?) and certainly for OO 2.4.

The basic code:

Code: Select all

Sub Main
'the to-be-combined interfaces 
  listXNamed = createUnoListener("MS_", "com.sun.star.container.XNamed")
  listXDialog = createUnoListener("MS_", "com.sun.star.awt.XDialog")

' run the beanshell script
  oScript = ThisComponent.getScriptProvider("").getScript("vnd.sun.star.script:listenercombiner.listenercombiner.bsh?language=BeanShell&location=document")
  oScript.invoke(Array("ms777.verysimpleservice", listXNamed, listXDialog), Array(), Array())

' create the service
  oCombined = createUnoService("ms777.verysimpleservice")

' now play around...
  xray oCombined

  msgbox oCombined.getName()
  oCombined.setName("New name")
  oCombined.setTitle("new title")
  msgbox oCombined.getTitle()
  msgbox oCombined.execute()
  oCombined.endExecute
end sub


'the interface com.sun.star.container.XNamed
function MS_getName() as String
 msgbox "XNamed.getName called"
 MS_getName = "Hi there ..."
end function

sub MS_setName(s as String)
 msgbox "XNamed.setName called with " & s
end sub

'the interface com.sun.star.awt.XDialog
sub MS_setTitle(title as string)
  msgbox "XDialog.setTitle called with " & title
end sub 

function MS_getTitle() as string
  msgbox "XDialog.getTitle called"
  MS_getTitle = "some title here ..."
end function 

function MS_execute() as integer
  msgbox "XDialog.execute called"
  MS_execute = 42
end function 

sub MS_endExecute() 
  msgbox "XDialog.endExecute called"
end sub
The beanshell code (to be stored in the document's library listenercombiner as listenercombiner.bsh):

Code: Select all

package ms77731;

import com.sun.star.uno.Type;
import com.sun.star.uno.XInterface;
import com.sun.star.uno.XWeak;

import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.Any;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.IQueryInterface;

import com.sun.star.lang.XTypeProvider;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XInitialization;
import com.sun.star.container.XSet;
import com.sun.star.container.XContentEnumerationAccess;
import com.sun.star.container.XEnumeration;

import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.lib.uno.helper.ComponentBase;

import java.util.Vector;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.SecurityException;
import java.lang.NoSuchMethodException;

// This prevents an error message when executing the script a second time

xClassLoader = java.lang.ClassLoader.getSystemClassLoader();
try { xClass = xClassLoader.loadClass("ms77731.ListenerFactory");} 
catch (ClassNotFoundException e) {

System.out.println( "compiling ...");



public class ListenerFactory extends ComponentBase implements XServiceInfo, XSingleComponentFactory {
  Object[] _args = {};
  String sServiceName = "";

  public ListenerFactory(Object[] args) {
    sServiceName = AnyConverter.toString(args[0]);
    _args = args;
    for (i=0;i<_args.length;i++) { System.out.println( "ms777Listener constr  _args: "+_args[i]);      }
  }

 
//XServiceInfo
  public boolean supportsService( String sService ) { return ( sService.equals( sServiceName)); }
  public String getImplementationName() { return sServiceName;  }

  public String[] getSupportedServiceNames() { 
    String[] retValue= new String[] {sServiceName};
    return retValue;
  }


//XSingleComponentFactory
  public Object createInstanceWithContext(XComponentContext  Context ) {
    ListenerCombination lc = new ListenerCombination(_args);
    return lc.getProxy();
  }
  public Object createInstanceWithArgumentsAndContext(Object[] args, XComponentContext  Context ) {
    System.out.println( "ms777listener createInstanceWithContextAndArgs");
    Object oProxy = createInstanceWithContext(Context);
    XInitialization xi = UnoRuntime.queryInterface(XInitialization.class, oProxy);
    if (!(xi==null)) xi.initialize(args);
    return oProxy;
  }


  public class ListenerCombination extends ComponentBase   {

    Type[] _aType = {};
    byte[] _implementationId = {};

    Object[] _args = {};
    Object oProxy = null;
    ListenerCombination LF = null;
    XInterface[] _argInterface = {};

    InvocationHandler IH = new InvocationHandler() {
      public Object invoke(Object proxy, Method method, Object[] args) {
        System.out.println( "invoke start: method: " + method);
        Class[] parameterTypes = method.getParameterTypes();
        String name = method.getName();
        Method m = null;

        for (int iIF = -1; iIF < _argInterface.length; iIF++) {
          Object OF = LF;
          if (iIF>=0) OF = _argInterface[iIF];
          try {
            m = OF.getClass().getMethod(name, parameterTypes);
          } catch (NoSuchMethodException nse) { }
            catch (SecurityException se) { System.out.println("security exception");}
          if (m!=null) {
            Object oReturn = m.invoke(OF, args);
            System.out.println( "invoke end success: OF: " + OF + ", returned: " + oReturn);
            return oReturn;
          }
        }
        System.out.println( "invoke end: returned null");
        return null;
      };
    };

    public ListenerCombination(Object[] args) {
      super();
      LF = this;
      System.out.println( "in Constructor: superClass: " +   getClass().getSuperclass());

      _argInterface = new XInterface[args.length-1];
      for (int k=1; k < args.length; k++) {
          Type t  = new Type(args[k].getType().getTypeName());
          _argInterface[k-1] =  UnoRuntime.queryInterface(t, args[k]);
          System.out.println( "in Constructor: _argInterface: " +   _argInterface[k-1]);
      }

      Class[] aInterfaces = new Class[_argInterface.length+3];
      aInterfaces[0] = XComponent.class;    
      aInterfaces[1] = XWeak.class;    
      aInterfaces[2] = XTypeProvider.class;    
      for (k=0; k < _argInterface.length; k++) {
        Class c = _argInterface[k].getClass().getInterfaces()[0];
        System.out.println( "in Constructor: added Class: " + c );
        aInterfaces[k+3] = c;
      }
    
      _aType = new Type[aInterfaces.length];
      for (k=0; k < aInterfaces.length; k++) _aType[k] = new Type(aInterfaces[k]);

      byte[] ab = super.getImplementationId();
      _implementationId = new byte[ab.length+_argInterface.length];
      for (int k=0; k < ab.length; k++) _implementationId[k] = ab[k];
      for (int k=0; k < _argInterface.length; k++) _implementationId[k+ab.length] = (byte)(_argInterface.getClass().hashCode() & 0xff);
    
      oProxy = Proxy.newProxyInstance(getClass().getClassLoader(),  aInterfaces, IH);
    }

    public Object getProxy() {  return oProxy;  }

    public Type[] getTypes() {  return _aType;  } 

    public byte[] getImplementationId() { return _implementationId;  }  

  } // end of class ListenerCombination

}// end of class ListenerFactory

} // of try ClassNotFoundException


System.out.println( "after try ClassNotFoundException" );

Object TA = new ListenerFactory(ARGUMENTS);
//
//--------------------   create and register a factory for your class
//
System.out.println( "create and register factories start");

XComponentContext Context = XSCRIPTCONTEXT.getComponentContext();

xSM = Context.getValueByName("/singletons/com.sun.star.lang.theServiceManager");
xSMSet = UnoRuntime.queryInterface(XSet.class, xSM); 
XContentEnumerationAccess xSMContEnum = UnoRuntime.queryInterface(XContentEnumerationAccess.class, xSM); 

System.out.println( "create and register factories start 1, xSMContEnum: " + xSMContEnum);

String sService =  AnyConverter.toString(ARGUMENTS[0]);

XEnumeration xEnum = (XEnumeration) xSMContEnum.createContentEnumeration(sService);

if (!(xEnum==null)) {
  for( ; xEnum.hasMoreElements(); ) {
    Object el = xEnum.nextElement();
    xSMSet.remove(el);
    System.out.println( "create and register factories: removed one service with name: " + sService);
  }
}

xSMSet.insert(TA);
System.out.println( "create and register factories end");
return;
Last edited by ms777 on Mon May 26, 2008 10:29 pm, edited 1 time in total.
ms777
Volunteer
Posts: 177
Joined: Mon Oct 08, 2007 1:33 am

Re: Generate own UNO services with OO Basic

Post by ms777 »

Here is the Beanshell code which should work for OO 2.4:

Code: Select all

package ms77705;

import com.sun.star.uno.Type;
import com.sun.star.uno.XInterface;
import com.sun.star.uno.XWeak;

import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.Any;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.IQueryInterface;

import com.sun.star.lang.XTypeProvider;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XInitialization;
import com.sun.star.container.XSet;
import com.sun.star.container.XContentEnumerationAccess;
import com.sun.star.container.XEnumeration;

import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.lib.uno.helper.ComponentBase;

import java.util.Vector;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.SecurityException;
import java.lang.NoSuchMethodException;

// This prevents an error message when executing the script a second time

// The following two lines were working in OO 2.1
//xClassLoader = java.lang.ClassLoader.getSystemClassLoader();
//try { xClass = xClassLoader.loadClass("ms77731.ListenerFactory");} 

// These two lines now work in OO 2.3 (?), OO 2.4
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try { Class.forName("ms77705.ListenerFactory");} 


catch (ClassNotFoundException e) {

System.out.println( "compiling ...");



public class ListenerFactory extends ComponentBase implements XServiceInfo, XSingleComponentFactory {
  Object[] _args = {};
  String sServiceName = "";

  public ListenerFactory(Object[] args) {
    System.out.println( "ms777Listener constr  _args: "+_args);
    System.out.println( "ms777Listener constr  _args.len: "+_args.length);
    sServiceName = AnyConverter.toString(args[0]);
    _args = args;
    for (i=0;i<_args.length;i++) { System.out.println( "ms777Listener constr  _args: "+_args[i]);      }
  }

 
//XServiceInfo
  public boolean supportsService( String sService ) { return ( sService.equals( sServiceName)); }
  public String getImplementationName() { return sServiceName;  }

  public String[] getSupportedServiceNames() { 
    String[] retValue= new String[] {sServiceName};
    return retValue;
  }


//XSingleComponentFactory
  public Object createInstanceWithContext(XComponentContext  Context ) {
    System.out.println( "ms777listener createInstanceWithContext");
    Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
    ListenerCombination lc = new ListenerCombination(_args);
    return lc.getProxy();
  }
  public Object createInstanceWithArgumentsAndContext(Object[] args, XComponentContext  Context ) {
    System.out.println( "ms777listener createInstanceWithContextAndArgs");
    Object oProxy = createInstanceWithContext(Context);
    XInitialization xi = UnoRuntime.queryInterface(XInitialization.class, oProxy);
    if (!(xi==null)) xi.initialize(args);
    return oProxy;
  }


  public class ListenerCombination extends ComponentBase   {

    Type[] _aType = {};
    byte[] _implementationId = {};

    Object[] _args = {};
    Object oProxy = null;
    ListenerCombination LF = null;
    XInterface[] _argInterface = {};

    InvocationHandler IH = new InvocationHandler() {
      public Object invoke(Object proxy, Method method, Object[] args) {
        System.out.println( "invoke start: method: " + method);
        Class[] parameterTypes = method.getParameterTypes();
        String name = method.getName();
        Method m = null;

        for (int iIF = -1; iIF < _argInterface.length; iIF++) {
          Object OF = LF;
          if (iIF>=0) OF = _argInterface[iIF];
          try {
            m = OF.getClass().getMethod(name, parameterTypes);
          } catch (NoSuchMethodException nse) { }
            catch (SecurityException se) { System.out.println("security exception");}
          if (m!=null) {
            Object oReturn = m.invoke(OF, args);
            System.out.println( "invoke end success: OF: " + OF + ", returned: " + oReturn);
            return oReturn;
          }
        }
        System.out.println( "invoke end: returned null");
        return null;
      };
    };

    public ListenerCombination(Object[] args) {
      super();
      LF = this;
      System.out.println( "in Constructor: superClass: " +   getClass().getSuperclass());

      _argInterface = new XInterface[args.length-1];
      for (int k=1; k < args.length; k++) {
          Type t  = new Type(args[k].getType().getTypeName());
          _argInterface[k-1] =  UnoRuntime.queryInterface(t, args[k]);
          System.out.println( "in Constructor: _argInterface: " +   _argInterface[k-1]);
      }

      Class[] aInterfaces = new Class[_argInterface.length+3];
      aInterfaces[0] = XComponent.class;    
      aInterfaces[1] = XWeak.class;    
      aInterfaces[2] = XTypeProvider.class;    
      for (k=0; k < _argInterface.length; k++) {
        Class c = _argInterface[k].getClass().getInterfaces()[0];
        System.out.println( "in Constructor: added Class: " + c );
        aInterfaces[k+3] = c;
      }
    
      _aType = new Type[aInterfaces.length];
      for (k=0; k < aInterfaces.length; k++) _aType[k] = new Type(aInterfaces[k]);

      byte[] ab = super.getImplementationId();
      _implementationId = new byte[ab.length+_argInterface.length];
      for (int k=0; k < ab.length; k++) _implementationId[k] = ab[k];
      for (int k=0; k < _argInterface.length; k++) _implementationId[k+ab.length] = (byte)(_argInterface.getClass().hashCode() & 0xff);
    
      oProxy = Proxy.newProxyInstance(getClass().getClassLoader(),  aInterfaces, IH);
    }

    public Object getProxy() {  return oProxy;  }

    public Type[] getTypes() {  return _aType;  } 

    public byte[] getImplementationId() { return _implementationId;  }  

  } // end of class ListenerCombination

}// end of class ListenerFactory

} // of try ClassNotFoundException


System.out.println( "after try ClassNotFoundException" );

Object TA = new ListenerFactory(ARGUMENTS);
//
//--------------------   create and register a factory for your class
//
System.out.println( "create and register factories start");

XComponentContext Context = XSCRIPTCONTEXT.getComponentContext();

xSM = Context.getValueByName("/singletons/com.sun.star.lang.theServiceManager");
xSMSet = UnoRuntime.queryInterface(XSet.class, xSM); 
XContentEnumerationAccess xSMContEnum = UnoRuntime.queryInterface(XContentEnumerationAccess.class, xSM); 

System.out.println( "create and register factories start 1, xSMContEnum: " + xSMContEnum);

String sService =  AnyConverter.toString(ARGUMENTS[0]);

XEnumeration xEnum = (XEnumeration) xSMContEnum.createContentEnumeration(sService);

if (!(xEnum==null)) {
  for( ; xEnum.hasMoreElements(); ) {
    Object el = xEnum.nextElement();
    xSMSet.remove(el);
    System.out.println( "create and register factories: removed one service with name: " + sService);
  }
}

xSMSet.insert(TA);
System.out.println( "create and register factories end");
return;
Post Reply