2017-05-14

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

<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 Create a UNO Old-Style Service

-Hypothesizer

Proceeding to the item, No. 6, in order to create a UNO old-style service, we create two UNO interfaces, 'thebiasplanet.uno.hiunoextensionsunoextension.XScolder' and 'thebiasplanet.uno.hiunoextensionsunoextension.XPraiser'.

-Rebutter

The UNO old-style service is a UNO service of a UNO component that implements multiple UNO interfaces, right?

-Hypothesizer

Strictly speaking, the UNO new-style service also implements multiple UNO interfaces including 'com.sun.star.lang.XServiceInfo'. The UNO old-style service implements multiple UNO interfaces that are meant to be directly used by users of the UNO old-style service.

-Rebutter

I see.

-Hypothesizer

In the UNO data types project, we create UNOIDL files, 'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XScolder.idl' and 'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XPraiser.idl' under the 'source' directory, and write these in them.

// # Change the defined variable name START
#ifndef __thebiasplanet_uno_hiunoextensionsunoextension_XScolder_idl__
#define __thebiasplanet_uno_hiunoextensionsunoextension_XScolder_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 XScolder: com::sun::star::uno::XInterface
 {
  // # Add methods START
  string scold ( [in] string p_name)
    raises (com::sun::star::lang::IllegalArgumentException);
  // # Add methods END
 };
}; }; };

#endif
// # Change the defined variable name START
#ifndef __thebiasplanet_uno_hiunoextensionsunoextension_XPraiser_idl__
#define __thebiasplanet_uno_hiunoextensionsunoextension_XPraiser_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 XPraiser: com::sun::star::uno::XInterface
 {
  // # Add methods START
  string praise ( [in] string p_name)
    raises (com::sun::star::lang::IllegalArgumentException);
  // # Add methods END
 };
}; }; };

#endif
-Rebutter

Ah-ha.

-Hypothesizer

Then, in the UNO extension project, we create a UNO component Java source file, 'java/thebiasplanet/uno/heyunoextensionsunoextension/ScolderPraiserUnoComponent.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.XServiceInfo;
import com.sun.star.lang.XInitialization;
import com.sun.star.uno.XComponentContext;
// # Add necessary classes and interfaces START
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.sun.star.lang.IllegalArgumentException;
import thebiasplanet.coreutilities.messaging.Publisher;
// # Add necessary classes and interfaces END

// # Change the class name
public class ScolderPraiserUnoComponent
  extends WeakBase
  implements XServiceInfo, XInitialization,
  // # Specify the UNO interface to implement
  XScolder, XPraiser {
 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.ScolderPraiserService");
  // # 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;
 // # 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 ScolderPraiserUnoComponent (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.format ("%s (%d %s)", (String) p_arguments[0], System.identityHashCode (this), DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format (LocalDateTime.now()));
    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 scold (String p_name)
   throws IllegalArgumentException {
  if (p_name == null) {
   throw new IllegalArgumentException ("The first argument can't be null.");
  }
  return String.format ("Don't do that! %s, %s!", message, p_name);
 }
 
 public String praise (String p_name)
   throws IllegalArgumentException {
  if (p_name == null) {
   throw new IllegalArgumentException ("The first argument can't be null.");
  }
  return Publisher.getMessage (String.format ("Wonderful! %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;
 }
}
-Rebutter

Um, it implements those two UNO interfaces.

-Hypothesizer

Then, we add the UNO old-style service into the 'global UNO services' provider, 'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoExtensionGlobalServicesProvider'. In the static block, we add this.

  ScolderPraiserUnoComponent.setThisClassToServicesProvider (UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP);
-Rebutter

OK.

-Hypothesizer

Then, we add this code into the UNO components setting file to set the third UNO component.

  <implementation name="thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserUnoComponent">
   <!-- # Add service names -->
   <service name="thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserService"/>
  </implementation>
-Rebutter

OK.

We Create a 'Global UNO Service' Instances Factory for the UNO Old-Style Service

-Hypothesizer

We create a 'global UNO service' instances factory for the UNO old-style service.

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

// # Change the defined variable name START
#ifndef __thebiasplanet_uno_heyunoextensionsunoextension_ScolderPraiserService_idl__
#define __thebiasplanet_uno_heyunoextensionsunoextension_ScolderPraiserService_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
 service ScolderPraiserService: XScolder {
  // # Add constructors START
  create1 ( [in] string p_message)
    raises (com::sun::star::lang::IllegalArgumentException);
  // # Add constructors END
 };
}; }; };

#endif
-Rebutter

Um, we specified one of the two UNO interfaces, 'thebiasplanet.uno.hiunoextensionsunoextension.XScolder'.

-Hypothesizer

Yes. That determines the return type of all of the factory methods. We can't specify the return type per factory method.

-Rebutter

Ah-ha. So, we can create the 'global UNO service' instances factory for the UNO old-style service, just by choosing a UNO interface to be used as the return type of the factory methods.

-Hypothesizer

That's right.

-Hypothesizer

Now, we can build the UNO data types project, and build and register the UNO extension.

-Rebutter

They have been built and registered successfully.

We Experiment to Create Instances of the UNO Old-Style Service by the 'Global UNO Service' Instances Factory

-Hypothesizer

To test that we can create instances of the UNO old-style service by the 'global UNO service' instances factory, we add this code at the last of the previous LibreOffice Basic Sub.

 Dim l_scolderPraiserService1 As Variant
 l_scolderPraiserService1 = thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserService.create1 ("Well")
 Msgbox (l_scolderPraiserService1.scold ("guys"))
 Msgbox (l_scolderPraiserService1.praise ("guys"))
-Rebutter

The Sub worked as expected.

-Hypothesizer

Thus, we have demonstrated how we can create a UNO old-style service, create a 'global UNO service' instances factory for the UNO old-style service, and create instances of the UNO old-style service by the 'global UNO service' instances factory.

We Experiment to Directly Cast an Instance of a Made-in-Java UNO Object into a UNO Interface Without Using the 'UnoRuntime.queryInterface' Method

-Hypothesizer

Let's proceed to the item, No. 7. When we have a UNO proxy for a UNO object in another language environment, in order to get a UNO proxy of another UNO interface of the same UNO object, we have to use the 'UnoRuntime.queryInterface' method. However, when we get a reference to a UNO object in the language environment of our code, the reference isn't to a UNO proxy, but to the original UNO object. So, we can just cast the reference to another UNO interface without using the 'UnoRuntime.queryInterface' method.

-Rebutter

That sounds reasonable.

-Hypothesizer

In the UNO data types project, we modify the UNOIDL file, 'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XHeyUnoExtensions.idl' and add a method, 'getInnerPraiser' like this.

  XPraiser getInnerPraiser ( [in] string p_message)
    raises (com::sun::star::lang::IllegalArgumentException);

This method creates an instance of the aforementioned old-style UNO service, and return the instance in the type, 'thebiasplanet.uno.heyunoextensionsunoextension.XPraiser'.

-Rebutter

I see.

-Hypothesizer

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

 public XPraiser getInnerPraiser (String p_message)
   throws IllegalArgumentException {
  XScolder l_innerScolderPraiser1 = null;
  XPraiser l_innerScolderPraiser2 = null;
  try {
   l_innerScolderPraiser1 = ScolderPraiserService.create1 (componentContext, p_message);
   l_innerScolderPraiser2 = (XPraiser) l_innerScolderPraiser1;
  }
  catch (IllegalArgumentException l_exception) {
   throw l_exception;
  }
  return l_innerScolderPraiser2;
 }
-Rebutter

The 'global UNO service' instances factory for the UNO old-style service returns the instance of the old-style UNO service in the type, 'thebiasplanet.uno.heyunoextensionsunoextension.XScolder', and the instance is directly cast to the UNO interface, 'thebiasplanet.uno.heyunoextensionsunoextension.XPraiser', by the Java cast.

-Hypothesizer

Yes.

Let's build the UNO data types project, and build and register the UNO extension.

To test, we add this code at the last of the previous LibreOffice Basic Sub.

 Dim l_scolderPraiserService2 As Variant
 l_scolderPraiserService2 = l_heyUnoExtensionsService.getInnerPraiser ("Well")
 Msgbox (l_scolderPraiserService2.scold ("guys"))
 Msgbox (l_scolderPraiserService2.praise ("guys"))
-Rebutter

Ah-ha, it works fine.

-Hypothesizer

Thus, we have demonstrated that we can directly cast an instance of a made-in-Java UNO object into a UNO interface without using the 'UnoRuntime.queryInterface' method.

Main body END

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