2017-05-21

22: To Develop the Second Sample UNO Extension (LibreOffice Extension or Apache OpenOffice Extension) in a Different Structure, Part Four

<The previous article in this series | The table of contents of this series | The next article in this series>

Main body START

To Know How to Handle (Read or Write) LibreOffice or Apache OpenOffice Writer or Calc Documents via Extensions from Java or Macro Programs

We Experiment to Create a Singleton Factory of the UNO Old-Style Service

-Hypothesizer

Although the reference documents claim that we can't create singleton factories of UNO old-style services, in fact, yes, we can.

-Rebutter

Ah-ha.

-Hypothesizer

In the UNO data types project, we create a UNOIDL file, 'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/ScolderPraiserServiceSingleton.idl', and write this in it.

// # Change the defined variable name START
#ifndef __thebiasplanet_uno_heyunoextensionsunoextension_ScolderPraiserServiceSingleton_idl__
#define __thebiasplanet_uno_heyunoextensionsunoextension_ScolderPraiserServiceSingleton_idl__
// # Change the defined variable name END

// # Change the interface idl
#include "thebiasplanet/uno/heyunoextensionsunoextension/XScolder.idl"

// # Change the module name
module thebiasplanet { module uno { module heyunoextensionsunoextension {
 // # Change the service name and the interface name
 singleton ScolderPraiserServiceSingleton: XScolder;
}; }; };

#endif
-Rebutter

Is this the singleton factory?

-Hypothesizer

Yes.

-Rebutter

How is this associated with the UNO old-style service?

-Hypothesizer

In fact, the singleton factory just takes out the instance from the component context, and the property name in the component context is '/singletons/thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserServiceSingleton'.

-Rebutter

Um? So, the name of the singleton factory is reflected on the name of the property. . . . Then, the next inevitable question is, "How will we set our desired instance to the property?"

-Hypothesizer

Actually, I couldn't find any description in the reference documents about how we are supposed to do that. So, I did that in my way.

I understand that the component context is read only, and couldn't find out how to create a new instance of component context with additional properties specified. So, I created a sub class of the component context class.

-Rebutter

Oh.

-Hypothesizer

In the UNO utilities project, we create a Java source file, 'java/thebiasplanet/unoutilities/connectionshandling/UnoComponentContext.java', and write this in it.

package thebiasplanet.unoutilities.connectionshandling;

import java.util.Map;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lang.XMultiComponentFactory;

public class UnoComponentContext implements XComponentContext {
 private XComponentContext originalComponentContext = null;
 private Map <String, Object> extraNameValueMap = null;
 
 public UnoComponentContext (XComponentContext p_originalComponentContext, Map <String, Object> p_extraNameValueMap) throws com.sun.star.uno.Exception {
  if (p_originalComponentContext == null) {
   throw new com.sun.star.uno.Exception ("Original component context is required.");
  }
  originalComponentContext = p_originalComponentContext;
  extraNameValueMap = p_extraNameValueMap;
 }
 
 @Override
 public final Object getValueByName (String p_name) {
  if (extraNameValueMap != null && extraNameValueMap.containsKey (p_name)) {
   return extraNameValueMap.get (p_name);
  }
  return originalComponentContext.getValueByName (p_name);
 }
 
 @Override
 public final XMultiComponentFactory getServiceManager () {
  return originalComponentContext.getServiceManager ();
 }
 
 public final boolean isFromTheSameOrigin (UnoComponentContext p_unoComponentContext) {
  if ((p_unoComponentContext ==  null) || (extraNameValueMap.get ("identification") == null)) {
   return false;
  }
  if (extraNameValueMap.get ("identification").equals (p_unoComponentContext.getValueByName ("identification"))) {
   return true;
  }
  else {
   return false;
  }
 }
}
-Rebutter

Well, now, we can create a component context instance specifying additional properties.

-Hypothesizer

We create a customized component context instance when an instance of the UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', is created, and pass around the component context instance.

-Rebutter

So, it's a singleton only if the component context instance is used.

-Hypothesizer

That's right. In order to pass around the component context instance, we will add a method that takes out the component context instance, in the UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions', like this.

  com::sun::star::uno::XComponentContext getComponentContext ();
-Rebutter

Hmm.

-Hypothesizer

We also add a method, 'getSingletonInnerPraiser', that returns the singleton, in the same UNO interface, like this.

  XPraiser getSingletonInnerPraiser ();
-Rebutter

OK.

-Hypothesizer

Then, in the UNO extension project, we modify the UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', in the constructor, like this.

  componentContextExtraNameValueMap = new HashMap <String, Object> ();
  try {
   componentContext = new UnoComponentContext (p_componentContext, componentContextExtraNameValueMap);
  }
  catch (com.sun.star.uno.Exception l_exception) {
  }
  componentContextExtraNameValueMap.put ("/singletons/thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserServiceSingleton", ScolderPraiserService.create1 (componentContext, "Well"));
-Rebutter

We create the customized component context instance and hold the instance in the UNO object.

-Hypothesizer

And we add the method, 'getComponentContext', in the UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', like this.

 public XComponentContext getComponentContext (){
  return componentContext;
 }
-Rebutter

Ah-ha.

-Hypothesizer

We also add the method, 'getSingletonInnerPraiser', in the UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', like this.

 public XPraiser getSingletonInnerPraiser ()
   throws IllegalArgumentException {
  XScolder l_singletonInnerScolderPraiser1 = null;
  XPraiser l_singletonInnerScolderPraiser2 = null;
  try {
   l_singletonInnerScolderPraiser1 = ScolderPraiserServiceSingleton.get (componentContext);
   l_singletonInnerScolderPraiser2 = (XPraiser) l_singletonInnerScolderPraiser1;
  }
  catch (IllegalArgumentException l_exception) {
   throw l_exception;
  }
  return l_singletonInnerScolderPraiser2;
 }
-Rebutter

We can get the singleton by the method, 'get'.

-Hypothesizer

We build the three projects, the UNO data types project, the UNO utilities project, and the UNO extension project, and add this code at the last of the previous LibreOffice Basic Sub.

 Dim l_scolderPraiserServiceSingleton1 As Variant
 Dim l_scolderPraiserServiceSingleton2 As Variant
 Dim l_scolderPraiserServiceSingleton3 As Variant
 l_scolderPraiserServiceSingleton1 = l_heyUnoExtensionsService.getSingletonInnerPraiser ()
 Msgbox (l_scolderPraiserServiceSingleton1.scold ("guys"))
 Msgbox (l_scolderPraiserServiceSingleton1.praise ("guys"))
 l_scolderPraiserServiceSingleton2 = l_heyUnoExtensionsService.getSingletonInnerPraiser ()
 Msgbox (l_scolderPraiserServiceSingleton2.scold ("guys"))
 Msgbox (l_scolderPraiserServiceSingleton2.praise ("guys"))
 Dim l_componentContext As Variant
 l_componentContext = l_heyUnoExtensionsService.getComponentContext () l_scolderPraiserServiceSingleton3 = thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserServiceSingleton.get (l_componentContext)
 Msgbox (l_scolderPraiserServiceSingleton3.scold ("guys"))
 Msgbox (l_scolderPraiserServiceSingleton3.praise ("guys"))
 Dim l_scolderPraiserService3 As Variant
 l_scolderPraiserService3 = GetProcessServiceManager ().createInstanceWithArgumentsAndContext ("thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserService", l_args (), l_componentContext)
 Msgbox (l_scolderPraiserService3.scold ("guys"))
 Msgbox (l_scolderPraiserService3.praise ("guys"))

Note that we get the singleton three times. As returns of the methods, 'scold' and 'praise', contain the identity hash code of the UNO object (gotten by the 'System.identityHashCode' method), we can know whether we are really getting the singleton.

-Rebutter

Well, the number in the parentheses in the message is the identity hash code. . . . As we execute the macro Sub, the identity hash codes for the three messages are the same.

-Hypothesizer

'l_scolderPraiserServiceSingleton1' and 'l_scolderPraiserServiceSingleton2' have been gotten from the method, 'getSingletonInnerPraiser', and 'l_scolderPraiserServiceSingleton3' has been gotten directly from the singleton factory, but however they have been gotten, they are the same object.

And I added the code for 'l_scolderPraiserService3' for comparison. 'l_scolderPraiserService3' has been gotten from the global UNO service manager, and isn't a singleton, as you can see from the identity hash code.

-Rebutter

I see.

-Hypothesizer

Thus, we have demonstrated how we can create a singleton factory of the UNO old-style service.

Main body END

<The previous article in this series | The table of contents of this series | The next article in this series>