2017年6月24日土曜日

20: 第2のサンプルUNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を別の構造で開発する、パート2

<このシリーズの、前の記事 | このシリーズの目次 | このシリーズの、次の記事>

Main body START

第2のサンプルUNO拡張機能プロジェクトを作る

-Hypothesizer

第2のサンプルUNO拡張機能プロジェクト、'heyUnoExtensionsUnoExtension'を作る。プロジェクトディレクトリを開発ディレクトリ直下に作る。

次に、プロジェクト別GradleビルドスクリプトまたはAntビルドファイルをプロジェクトディレクトリ直下に作る。Gradleビルドスクリプトの内容は以下のとおりだ。

// # 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")

UNOデータタイププロジェクトJarファイル、UNO ユーティリティプロジェクトJarファイル、コアユーティリティプロジェクトJarファイルが参照されているが、これらはこのプロジェクトのJarファイルの中には格納されないことに注意しよう。

-Rebutter

ああ、第1のサンプルUNO拡張機能では、UNOユーティリティプロジェクトJarファイルはサンプルUNO拡張機能のJarファイルの中に格納されている。この違いは、UNOユーティリティプロジェクトJarファイルがGradleビルドスクリプトに設定されている位置から来ている。

-Hypothesizer

そう。これらのJarファイルをLibreOfficeに直接登録したから、これらのJarファイルをUNO拡張機能に含める必要はもうない。しかし、もちろん、これらのJarファイルをビルド時に参照する必要はある。そのため、これらのJarファイルはGradleビルドスクリプトのあの位置に設定されている。

-Rebutter

いいだろう。

グローバルUNOサービスを、'グローバルUNOサービス'インスタンスファクトリなしで、作成・利用する実験をする

-Hypothesizer

項目4にしたがって、UNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions'を実装するUNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'を作成する。

-Rebutter

このUNOインターフェースは、既に、UNOデータタイププロジェクトに作ってある。

-Hypothesizer

そう。'source'ディレクトリをサンプルUNO拡張機能プロジェクトディレクトリ直下に作成し、Javaクラスソースファイル、'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoComponent.java'を作成する。このソースファイルの内容は以下のとおりだ。

// # 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;
 }
}
-Rebutter

基本的に、内容は第1のサンプルUNO拡張機能のものと同じだ。

-Hypothesizer

この時点ではそうだ。後で、我々はこのファイルにコードを追加するだろう。

次に、'グローバルUNOサービス'プロバイダーを作る。そのJavaソースファイルは、'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoExtensionGlobalServicesProvider.java'だ。このファイルの内容は以下のとおりだ。

// # 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);
 }
}

前記事の記述を覚えていれば、これに説明は何もいらないだろう。

-Rebutter

オーケー。

-Hypothesizer

次に、リソースファイル、'MANIFEST.MF.addition'、'thebiasplanet.heyunoextensionsunoextension.uno.components'、'manifest.xml'を作る。

ここに'MANIFEST.MF.addition'の内容を示すのはやめよう。わかりきっているから。

'thebiasplanet.heyunoextensionsunoextension.uno.components'の内容は以下のとおりだ。

<?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>

'manifest.xml'の内容は以下のとおりだ。

<?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>

UNOデータタイプレジストリファイルを何も指定していないことに注意しよう。このプロジェクトにはないから。

-Rebutter

分かった。

-Hypothesizer

'グローバルUNOサービス'インスタンスファクトリはここでは作らない。グローバルUNOサービスをグローバルUNOサービスマネージャーをとおしてインスタンス化するのであれば、それが必要ないことを実証するためだ。

-Rebutter

オーケー。

-Hypothesizer

これで、サンプルUNO拡張機能をビルドして登録できる。いつものように、'gradle'コマンドか'ant'コマンドを実行して、これを行なおう。

その後、以下のような、LibreOffice BasicマクロのSubを作る。

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
-Rebutter

ふーむ、こうやって、グローバルUNOサービスをグローバルUNOサービスマネージャーをとおしてインスタンス化するのか。

-Hypothesizer

そう。このマクロSubを実行しよう。

-Rebutter

ふーむ、グローバルUNOサービスがインスタンス化できた。

-Hypothesizer

こうして、我々が'グローバルUNOサービス'インスタンスファクトリと呼んでいるものが単にファクトリであって、参考文書にかかれているような「UNO新スタイルサービス」そのものではないことを、我々は実証した。

UNOサービスとして登録しないUNOコンポーネントを作成・利用する実験をする

-Hypothesizer

項目5にしたがって、新たにUNOコンポーネントを作るが、これをUNOサービスとしては登録しないことにしよう。

-Rebutter

ふーむ。

-Hypothesizer

UNOコンポーネントを、グローバルUNOサービスマネージャーをとおしてではなく、第1のUNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'に追加するあるメソッドをとおして、インスタンス化する。

-Rebutter

分かった。

-Hypothesizer

第1に、第2のUNOコンポーネント用に、UNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XRanter'を作成する。

UNOデータタイププロジェクトで、'source'ディレクトリ配下に、UNOIDLファイル、'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XRanter.idl'を作成し、以下を書く。

// # 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
-Rebutter

ふーむ、説明が必要なことは何もないようだ。

-Hypothesizer

第2に、UNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions'にメソッド、'getInnerRanter'を、以下のように追加する。

  XRanter getInnerRanter ( [in] string p_message)
    raises (com::sun::star::lang::IllegalArgumentException);
-Rebutter

このメソッドは、第2のUNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XRanter'のインスタンスを戻す。

-Hypothesizer

第3に、UNOデータタイププロジェクトをビルドする。

-Rebutter

オーケー。

-Hypothesizer

第4に、UNO拡張機能プロジェクトで、第2のUNOコンポーネントのJavaソースファイル、'java/thebiasplanet/uno/heyunoextensionsunoextension/RanterUnoComponent.java'を作り、以下を書く。

// # 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
}
-Rebutter

ふーむ、このUNOコンポーネントは、UNOサービスとして登録されないので、'com.sun.star.lang.XServiceInfo'を実装せず、第1のUNOコンポーネントにあるコードの一部が必要でなくなった。

-Hypothesizer

第5に、第1のUNOコンポーネントJavaソースファイル、'java/thebiasplanet/uno/heyunoextensionsunoextension/HeyUnoExtensionsUnoComponent.java'に以下のコードを追加してメソッド、'getInnerRanter'を追加する。

 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;
  
 }
-Rebutter

ああ、このメソッドは、第2のUNOコンポーネントをnew演算子によってインスタンス化している。

-Hypothesizer

そう。

第6に、UNOコンポーネント設定ファイルに以下のコードを追加して、第2のUNOコンポーネントを設定する。

  <implementation name="thebiasplanet.uno.heyunoextensionsunoextension.RanterUnoComponent">
  </implementation>
-Rebutter

グローバルUNOサービスとして登録されないので、'service'タグが含まれていない。

-Hypothesizer

これで、UNO拡張機能をビルドして登録できる。

変更をテストするために、先ほどのLibreOffice Basic Subの最後に以下のコードを追加する。

 Dim l_ranter As Variant
 l_ranter = l_heyUnoExtensionsService.getInnerRanter ("Good afternoon")
 Msgbox (l_ranter.rant ("bro"))
-Rebutter

うむ。Subは予期通り動作した。

-Hypothesizer

こうして、UNOコンポーネントをUNOサービスとして登録せずに作成・利用する方法を我々は実証した。

Main body END

<このシリーズの、前の記事 | このシリーズの目次 | このシリーズの、次の記事>