<The previous article in this series | The table of contents of this series | The next article in this series>
To Know How to Handle (Read or Write) LibreOffice or Apache OpenOffice Writer or Calc Documents via Extensions from Java or Macro Programs
Although the reference documents claim that we can't create singleton factories of UNO old-style services, in fact, yes, we can.
Ah-ha.
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
Is this the singleton factory?
Yes.
How is this associated with the UNO old-style service?
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'.
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?"
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.
Oh.
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;
}
}
}
Well, now, we can create a component context instance specifying additional properties.
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.
So, it's a singleton only if the component context instance is used.
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 ();
Hmm.
We also add a method, 'getSingletonInnerPraiser', that returns the singleton, in the same UNO interface, like this.
XPraiser getSingletonInnerPraiser ();
OK.
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"));
We create the customized component context instance and hold the instance in the UNO object.
And we add the method, 'getComponentContext', in the UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', like this.
public XComponentContext getComponentContext (){
return componentContext;
}
Ah-ha.
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;
}
We can get the singleton by the method, 'get'.
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.
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.
'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.
I see.
Thus, we have demonstrated how we can create a singleton factory of the UNO old-style service.
<The previous article in this series | The table of contents of this series | The next article in this series>