<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
We create the second sample UNO extension project, 'heyUnoExtensionsUnoExtension'. We create the project directory directly under the development directory.
Then, we create the project-specific Gradle build script or Ant build file directly under the project directory. The contents of the Gradle build script is this.
// # Change the target name
ext.TARGET_NAME = "thebiasplanet.heyunoextensionsunoextension.uno"
apply ("from": "../commonBuild01.gradle")
// # Change the default task
defaultTasks ("registerUnoExtension")
ext ({
// Add this if necessary
//CHECKSTYLE = "ON"
// Add this if necessary
//JAR_DEPLOY_DIRECTORY_NAME = "?"
// Add this if necessary
//WAR_DEPLOY_DIRECTORY_NAME = "?"
// # Change this if necessary
INCLUDED_JAR_FILE_NAMES = []
// # Change this if necessary
OTHER_CLASSES_PATHS = [JAVA_FILES_BASE_DIRECTORY_NAME, CLASSES_BASE_DIRECTORY_NAME, "../unoAdditionalDataTypesToDisclose/target/thebiasplanet.unoadditionaldatatypes.uno.jar", "../unoUtilitiesToDisclose/target/thebiasplanet.unoutilities.jar", "../coreUtilitiesToDisclose/target/thebiasplanet.coreutilities.jar", LIBREOFFICE_CLASSES_BASE_DIRECTORY_NAME + "/unoil.jar", LIBREOFFICE_CLASSES_BASE_DIRECTORY_NAME + "/jurt.jar", LIBREOFFICE_CLASSES_BASE_DIRECTORY_NAME + "/ridl.jar", LIBREOFFICE_CLASSES_BASE_DIRECTORY_NAME + "/juh.jar"]
REFERENCED_PROJECT_DIRECTORY_NAMES = ["../coreUtilitiesToDisclose", "../unoUtilitiesToDisclose", "../unoAdditionalDataTypesToDisclose"]
})
apply ("from": "../commonBuild02.gradle")
Note that the UNO data types project Jar file, the UNO utilities project Jar file, and the core utilities project Jar file are referenced, but they won't be included in the Jar file of this project.
Ah, in the first sample UNO extension, the UNO utilities project Jar file is included in the Jar file of the sample UNO extension. The difference comes from the difference of the places where the UNO utilities project Jar file is set in the Gradle build scripts.
Yes. As we have registered those three Jar files directly into LibreOffice, we don't need to include those Jar files in UNO extensions any more. However, of course, we need to reference those Jar file at build times. Thus, those Jar files are set at that place in the Gradle build script.
All right.
According to the item, No. 4, We create a UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent', that implements the UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions'.
The UNO interface has been already created in the UNO data types project.
Yes. We create the 'source' directory directly under the sample UNO extension project directory, and create a Java class source file, 'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoComponent.java'. The contents of the source file is this.
// # Change the package name
package thebiasplanet.uno.heyunoextensionsunoextension;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.XInitialization;
import com.sun.star.uno.XComponentContext;
// # Add necessary classes and interfaces START
import com.sun.star.lang.IllegalArgumentException;
// # Add necessary classes and interfaces END
// # Change the class name
public class HeyUnoExtensionsUnoComponent
extends WeakBase
implements XServiceInfo, XInitialization,
// # Specify the UNO interface to implement
XHeyUnoExtensions {
private static final Set <String> SERVICE_NAMES_SET = new HashSet <String> ();
private static final Class thisClass = new Object () { }.getClass ().getEnclosingClass ();
static {
// # Add service names START
SERVICE_NAMES_SET.add ("thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsService");
// # Add service names END
}
static final String [] SERVICE_NAMES_ARRAY = SERVICE_NAMES_SET.toArray (new String [SERVICE_NAMES_SET.size ()]);
private XComponentContext componentContext = null;
// # Add member variables START
private String message = null;
private Map <String, Object> componentContextExtraNameValueMap = null;
// # Add member variables END
static void setThisClassToServicesProvider (Map <String, Object []> p_unoComponentClassNameToUnoComponentClassAndServiceNamesArrayMap) {
p_unoComponentClassNameToUnoComponentClassAndServiceNamesArrayMap.put (thisClass.getName (), new Object [] {thisClass, SERVICE_NAMES_ARRAY});
}
// # Change the class name
public HeyUnoExtensionsUnoComponent (XComponentContext p_componentContext)
throws IllegalArgumentException {
componentContext = p_ componentContext;
}
public final void initialize (java.lang.Object [] p_arguments)
throws com.sun.star.uno.Exception {
// # Write the initialization START
if (p_arguments != null && p_arguments.length == 1) {
if (p_arguments[0] instanceof String) {
message = (String) p_arguments[0];
if (message == null) {
throw new IllegalArgumentException ("The first argument can't be null.");
}
}
else {
throw new IllegalArgumentException("The first argument must be a String instance.");
}
}
else {
throw new IllegalArgumentException("The number of arguments must be 1.");
}
// # Write the initialization END
}
// # Add methods of the implemented UNO interface START
public String sayHey (String p_name)
throws IllegalArgumentException {
if (p_name == null) {
throw new IllegalArgumentException ("The first argument can't be null.");
}
return String.format ("%s, %s!", message, p_name);
}
// # Add methods of the implemented UNO interface END
// # Add other member methods START
// # Add other member methods END
public String getImplementationName () {
return thisClass.getName ();
}
public final boolean supportsService (String p_serviceName) {
return SERVICE_NAMES_SET.contains (p_serviceName);
}
public final String [] getSupportedServiceNames () {
return SERVICE_NAMES_ARRAY;
}
}
Basically, the contents are the same with the first sample UNO extension's.
At this point, yes. We will add codes into this file later.
Then, we create the 'global UNO services' provider. The Java source file is 'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoExtensionGlobalServicesProvider.java'. The contents of the file is this.
// # Change the package name
package thebiasplanet.uno.heyunoextensionsunoextension;
import java.util.Map;
import java.util.HashMap;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import thebiasplanet.unoutilities.serviceshandling.GlobalUnoServicesProviderUtility;
// # Change the class name
public class HeyUnoExtensionsUnoExtensionGlobalServicesProvider {
private static final Map <String, Object []> UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP = new HashMap <String, Object []> ();
static {
// # Add UNO component classes START
HeyUnoExtensionsUnoComponent.setThisClassToServicesProvider (UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP);
// # Add UNO component classes END
}
public static XSingleComponentFactory __getComponentFactory (String p_unoComponentClassName) {
return GlobalUnoServicesProviderUtility.getSingleComponentFactory (UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP, p_unoComponentClassName);
}
public static boolean __writeRegistryServiceInfo (XRegistryKey p_registryKey) {
return GlobalUnoServicesProviderUtility.writeServicesInformationToRegistry (UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP, p_registryKey);
}
}
That won't require any explanation if we remember what are described in the previous articles.
OK.
Then, we create resource files, 'MANIFEST.MF.addition', 'thebiasplanet.heyunoextensionsunoextension.uno.components', and 'manifest.xml'.
I won't show the contents of 'MANIFEST.MF.addition' here because they are obvious.
The contents of 'thebiasplanet.heyunoextensionsunoextension.uno.components' is this.
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://openoffice.org/2010/uno-components">
<!-- # Change the jar file uri -->
<component loader="com.sun.star.loader.Java2" uri="thebiasplanet.heyunoextensionsunoextension.uno.jar">
<!-- # Add UNO component class names -->
<implementation name="thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent">
<!-- # Add service names -->
<service name="thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsService"/>
</implementation>
</component>
</components>
The contents of 'manifest.xml' is this.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">
<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
<!-- # Change the components file path -->
<manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-components" manifest:full-path="thebiasplanet.heyunoextensionsunoextension.uno.components"/>
</manifest:manifest>
Note that it doesn't specify any UNO data types registry file because this project doesn't have any.
I see.
We won't create any 'global UNO service' instances factory this time to demonstrate that it isn't necessary if we instantiate the global UNO service through the global UNO service manager.
OK.
Now, we can build and register the sample UNO extension. As usual, let's do that by executing the 'gradle' command or the 'ant' command.
After that, we create a LibreOffice Basic macro Sub like this.
Sub testsUnoExtension2
Dim l_args (0)
l_args (0) = "Good morning"
Dim l_heyUnoExtensionsService As Variant
l_heyUnoExtensionsService = GetProcessServiceManager ().createInstanceWithArguments ("thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsService", l_args ())
Msgbox (l_heyUnoExtensionsService.sayHey ("guys"))
End Sub
Hmm, that's how we instantiate a global UNO service through the global UNO service manager.
Yes. Let's execute the macro Sub.
Well, the global UNO service was successfully instantiated.
Thus, we demonstrated that what we call 'global UNO service' instances factories are just factories, not 'UNO new-style services' themselves as described in the reference documents.
According to the item, No. 5, we create another UNO component, but won't register it as a UNO service.
Hmm.
We will instantiate the UNO component not through the global UNO service manager, but through a method added to the first UNO component, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'.
I see.
First, we create the UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XRanter', for the second UNO component.
In the UNO data types project, we create a UNOIDL file, 'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XRanter.idl' under the 'source' directory, and write this in it.
// # Change the defined variable name START
#ifndef __thebiasplanet_uno_hiunoextensionsunoextension_XRanter_idl__
#define __thebiasplanet_uno_hiunoextensionsunoextension_XRanter_idl__
// # Change the defined variable name END
#include <com/sun/star/uno/XInterface.idl>
// # Add necessary idls START
#include <com/sun/star/lang/IllegalArgumentException.idl>
// # Add necessary idls END
// # Change the module name
module thebiasplanet { module uno { module heyunoextensionsunoextension {
// # Change the interface name and the parent interface
interface XRanter: com::sun::star::uno::XInterface
{
// # Add methods START
string rant ( [in] string p_name)
raises (com::sun::star::lang::IllegalArgumentException);
// # Add methods END
};
}; }; };
#endif
Well, there seems to be nothing that requires explanations.
Second, we add a method, 'getInnerRanter', into the UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions', like this.
XRanter getInnerRanter ( [in] string p_message)
raises (com::sun::star::lang::IllegalArgumentException);
The method returns an instance of the second UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XRanter'.
Third, we build the UNO data types project.
OK.
Fourth, in the UNO extension project, we create the second UNO component Java source file, 'java/thebiasplanet/uno/heyunoextensionsunoextension/RanterUnoComponent.java', and write this in it.
// # Change the package name
package thebiasplanet.uno.heyunoextensionsunoextension;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.lang.XInitialization;
import com.sun.star.uno.XComponentContext;
// # Add necessary classes and interfaces START
import com.sun.star.lang.IllegalArgumentException;
// # Add necessary classes and interfaces END
// # Change the class name
public class RanterUnoComponent
extends WeakBase
implements XInitialization,
// # Specify the UNO interface to implement
XRanter {
private static final Class thisClass = new Object () { }.getClass ().getEnclosingClass ();
private XComponentContext componentContext = null;
// # Add member variables START
private String message = null;
// # Add member variables END
// # Change the class name
public RanterUnoComponent (XComponentContext p_componentContext)
throws IllegalArgumentException {
componentContext = p_componentContext;
}
public final void initialize (java.lang.Object [] p_arguments)
throws com.sun.star.uno.Exception {
// # Write the initialization START
if (p_arguments != null && p_arguments.length == 1) {
if (p_arguments[0] instanceof String) {
message = (String) p_arguments[0];
if (message == null) {
throw new IllegalArgumentException ("The first argument can't be null.");
}
}
else {
throw new IllegalArgumentException("The first argument must be a String instance.");
}
}
else {
throw new IllegalArgumentException("The number of arguments must be 1.");
}
// # Write the initialization END
}
// # Add methods of the implemented UNO interface START
public String rant (String p_name)
throws IllegalArgumentException {
if (p_name == null) {
throw new IllegalArgumentException ("The first argument can't be null.");
}
return String.format ("%s? No way! Go to hell, %s!", message, p_name);
}
// # Add methods of the implemented UNO interface END
// # Add other member methods START
// # Add other member methods END
}
Well, as this UNO component isn't registered as a UNO service, it doesn't implement 'com.sun.star.lang.XServiceInfo', and some codes that exist in the first UNO component isn't required.
Fifth, we add this code into the first UNO component Java source file, 'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoComponent.java', to add the 'getInnerRanter' method.
public XRanter getInnerRanter (String p_message)
throws IllegalArgumentException {
RanterUnoComponent l_innerRanter = new RanterUnoComponent (componentContext);
try {
l_innerRanter.initialize (new String [] {p_message});
}
catch (IllegalArgumentException l_exception) {
throw l_exception;
}
catch (com.sun.star.uno.Exception l_exception) {
}
return l_innerRanter;
}
Oh, the method instantiates the second UNO component by the new operator.
Yes.
And sixth, we add this code into the UNO components setting file to set the second UNO component.
<implementation name="thebiasplanet.uno.heyunoextensionsunoextension.RanterUnoComponent">
</implementation>
As it isn't registered as a global UNO service, there is no 'service' tag in it.
Now, we can build and register the UNO extension.
To test the modification, we add this code at the last of the previous LibreOffice Basic Sub.
Dim l_ranter As Variant
l_ranter = l_heyUnoExtensionsService.getInnerRanter ("Good afternoon")
Msgbox (l_ranter.rant ("bro"))
Um. The Sub worked as expected.
Thus, we have demonstrated how to create and use a UNO component without registering it as a UNO service.
<The previous article in this series | The table of contents of this series | The next article in this series>