ラベル UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する の投稿を表示しています。 すべての投稿を表示
ラベル UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する の投稿を表示しています。 すべての投稿を表示

2017年7月8日土曜日

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

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

Main body START

UNO旧スタイルサービスのシングルトンファクトリを作る実験をする

-Hypothesizer

UNO旧スタイルサービスのシングルトンファクトリは作れないと参考文書は主張しているが、実際には作れる。

-Rebutter

ははあ。

-Hypothesizer

UNOデータタイププロジェクトで、UNOIDLファイル、'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/ScolderPraiserServiceSingleton.idl'を作り、そこに以下を書く。

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

これがシングルトンファクトリなのか?

-Hypothesizer

そう。

-Rebutter

これがUNO旧スタイルサービスとどうやって関連付けられるのか?

-Hypothesizer

実のところ、シングルトンファクトリは、コンポーネントコンテキストからインスタンスをただ取り出すだけであり、コンポーネントコンテキスト内のプロパティ名が'/singletons/thebiasplanet.uno.heyunoextensionsunoextension.ScolderPraiserServiceSingleton'になる。

-Rebutter

うむ?それでは、シングルトンファクトリの名称がプロパティの名称に反映されるわけだ。. . . では、避けられない次の質問は、「そのプロパティに望みのインスタンスをどうやってセットするのか?」だ。

-Hypothesizer

実のところ、どのようにそれをするように想定されているのかの記述を参考文書に見つけられなかった。だから、自分流にやった。

コンポーネントコンテキストは読み取り専用のはずであり、また、追加のプロパティを指定してコンポーネントコンテキストの新たなインスタンスを生成する方法が私には見つけられなかった。そこで、コンポーネントコンテキストクラスのサブクラスを作った。

-Rebutter

ほう。

-Hypothesizer

UNOユーティリティプロジェクトで、Javaソースファイル、'java/thebiasplanet/unoutilities/connectionshandling/UnoComponentContext.java'を作り、そこに以下を書く。

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

ふーむ、これで、追加のプロパティを指定してコンポーネントコンテキストのインスタンスを生成できるようになった。

-Hypothesizer

我々は、UNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'のインスタンスが生成された時に、カスタマイズしたコンポーネントコンテキストインスタンスを生成し、このコンポーネントコンテキストインスタンスを渡し回す。

-Rebutter

すると、このコンポーネントコンテキストが使われる場合にのみシングルトンなわけだ。

-Hypothesizer

そのとおり。このコンポーネントコンテキストインスタンスを渡し回すために、UNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XHeyUnoExtensions'に、このコンポーネントコンテキストを取り出すメソッドを以下のように追加する。

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

ふーむ。

-Hypothesizer

また、同じUNOインターフェースに、シングルトンを戻すメソッド、'getSingletonInnerPraiser'も以下のように追加しよう。

  XPraiser getSingletonInnerPraiser ();
-Rebutter

オーケー。

-Hypothesizer

次に、UNO拡張機能プロジェクトで、UNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'のコンストラクタを以下のように変更する。

  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

カスタマイズしたコンポーネントコンテキストインスタンスを生成し、このインスタンスをUNOオブジェクト内に保持する。

-Hypothesizer

そして、UNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'にメソッド、'getComponentContext'を以下のように追加する。

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

ははあ。

-Hypothesizer

また、UNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'に、メソッド、'getSingletonInnerPraiser'も、以下のように追加する。

 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

シングルトンは、メソッド、'get'で得られるわけだ。

-Hypothesizer

3つのプロジェクト、UNOデータタイププロジェクト、UNOユーティリティプロジェクト、UNO拡張機能プロジェクトをビルドし、先ほどの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"))

シングルトンを3度得ることに注意してくれ。メソッド、'scold'および'praise'の戻りには、UNOオブジェクトのアイデンティティハッシュコード('System.identityHashCode' によって得た)が含まれるので、本当にシングルトンを得ているのかどうかを判別できる。

-Rebutter

ふーむ、メッセージ中の丸括弧内の番号がアイデンティティハッシュコードだ。. . . マクロSubを実行すると、3回のメッセージのアイデンティティハッシュコードが同じだ。

-Hypothesizer

'l_scolderPraiserServiceSingleton1'と'l_scolderPraiserServiceSingleton2'はメソッド、'getSingletonInnerPraiser'から得て、'l_scolderPraiserServiceSingleton3'はシングルトンファクトリから直接得た。しかし、それらがどのように得られたにしろ、それらは同じオブジェクトだ。

また、'l_scolderPraiserService3'についてのコードは、比較のために追加した。'l_scolderPraiserService3'は、グローバルUNOオブジェクトサービスマネージャーから得たものであり、アイデンティティハッシュコードから分かるとおり、シングルトンではない。

-Rebutter

なるほど。

-Hypothesizer

このようにして、我々は、UNO旧スタイルサービスのシングルトンファクトリを作る方法を実証した。

Main body END

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

2017年7月1日土曜日

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

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

Main body START

UNO旧スタイルサービスを作る

-Hypothesizer

項目6に進んで、UNO旧スタイルサービスを作るために、2つのUNOインターフェース、'thebiasplanet.uno.hiunoextensionsunoextension.XScolder'と'thebiasplanet.uno.hiunoextensionsunoextension.XPraiser'を作る。

-Rebutter

UNO旧スタイルサービスというのは、複数のUNOインターフェースを実装するUNOコンポーネントのUNOサービスのことだな?

-Hypothesizer

厳密に言えば、UNO新スタイルサービスも、'com.sun.star.lang.XServiceInfo'を含め複数のUNOインターフェースを実装している。UNO旧スタイルサービスでは、そのUNO旧スタイルサービスのユーザーが直接使うように意図されているUNOインターフェースが複数実装されている。

-Rebutter

なるほど。

-Hypothesizer

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

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

ははあ。

-Hypothesizer

次に、UNO拡張機能プロジェクトで、UNOコンポーネントのJavaソースファイル、'java/thebiasplanet/uno/heyunoextensionsunoextension/ScolderPraiserUnoComponent.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.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

ふむ、先ほどの2つのUNOインターフェースを実装している。

-Hypothesizer

次に、このUNO新スタイルサービスを'グローバルUNOサービス'プロバイダーである'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoExtensionGlobalServicesProvider'に追加する。スタティックブロックに以下を追加する。

  ScolderPraiserUnoComponent.setThisClassToServicesProvider (UNO_COMPONENT_CLASS_NAME_TO_UNO_COMPONENT_CLASS_AND_SERVICE_NAMES_ARRAY_MAP);
-Rebutter

オーケー。

-Hypothesizer

次に、この第3のUNOコンポーネントを設定するために、UNOコンポーネント設定ファイルに以下のコードを追加する。

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

オーケー。

UNO旧スタイルサービスの'グローバルUNOサービス'インスタンスファクトリを作る

-Hypothesizer

このUNO旧スタイルサービスの'グローバルUNOサービス'インスタンスファクトリを作る。

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

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

ふむ、先ほどの2つのUNOインターフェースの内の1つ、'thebiasplanet.uno.hiunoextensionsunoextension.XScolder'を指定した。

-Hypothesizer

そう。これが、全てのファクトリメソッドの戻りタイプを決定する。ファクトリメソッド毎に戻りタイプを指定することはできない。

-Rebutter

ははあ。すると、ファクトリメソッド群の戻りタイプとして使うUNOインターフェースを1つ選択しさえすれば、UNO旧スタイルサービスの'グローバルUNOサービス'インスタンスファクトリを作れるわけだ。

-Hypothesizer

そのとおりだ。

-Hypothesizer

これで、UNOデータタイププロジェクトをビルドし、UNO拡張機能をビルドおよび登録できる。

-Rebutter

ビルド・登録が正常に行なわれた。

'グローバルUNOサービス'インスタンスファクトリによってUNO旧スタイルサービスのインスタンスを生成する実験をする

-Hypothesizer

'グローバルUNOサービス'インスタンスファクトリによってUNO旧スタイルサービスのインスタンスを生成できることをテストするために、先程の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

Subは、予期通りに動作した。

-Hypothesizer

こうして、我々は、UNO旧スタイルサービスを作成し、このUNO旧スタイルサービスの'グローバルUNOサービス'インスタンスファクトリを作成し、この'グローバルUNOサービス'インスタンスファクトリによってUNO旧スタイルサービスのインスタンスを生成する方法を実証した。

Java内で生成されたUNOオブジェクトインスタンスを、'UnoRuntime.queryInterface'メソッドを使わずに、UNOインターフェースに直接キャストする実験をする

-Hypothesizer

項目7に進もう。別の言語環境内のUNOオブジェクトへのUNOプロキシを持っている時、同じUNOオブジェクトの別のUNOインターフェースのUNOプロキシを得るためには、'UnoRuntime.queryInterface'メソッドを使わなければならない。しかし、我々のコードの言語環境内のUNOオブジェクトへの参照を得る時、この参照は、UNOプロキシへのものではなく、オリジナルのUNOオブジェクトへのものだ。そのため、その参照を別のUNOインターフェースに、'UnoRuntime.queryInterface'メソッドを使うことなくただキャストできる。

-Rebutter

それは理に適っているようだ。

-Hypothesizer

UNOデータタイププロジェクトで、UNOIDLファイル、'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XHeyUnoExtensions.idl'を変更し、メソッド、'getInnerPraiser'を以下のように追加する。

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

このメソッドは、前述した旧スタイルUNOサービスのインスタンスを生成し、このインスタンスをタイプ、'thebiasplanet.uno.heyunoextensionsunoextension.XPraiser'で戻す。

-Rebutter

なるほど。

-Hypothesizer

次に、UNO拡張機能プロジェクトで、UNOコンポーネント、'thebiasplanet.uno.heyunoextensionsunoextension.HeyUnoExtensionsUnoComponent'にこのメソッドを以下のように実装する。

 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

この旧スタイルUNOサービスのグローバルUNOサービス'インスタンスファクトリは旧スタイルUNOサービスのインスタンスをタイプ、'thebiasplanet.uno.heyunoextensionsunoextension.XScolder'で戻し、このインスタンスがJavaのキャストによって、UNOインターフェース、'thebiasplanet.uno.heyunoextensionsunoextension.XPraiser'に直接キャストされている。

-Hypothesizer

そう。

UNOデータタイププロジェクトをビルドし、UNO拡張機能をビルドおよび登録しよう。

テストするために、先ほどの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

ははあ、正常に動作する。

-Hypothesizer

こうして、我々は、Java内で生成されたUNOオブジェクトインスタンスを、'UnoRuntime.queryInterface'メソッドを使わずに、UNOインターフェースに直接キャストできることを実証した。

Main body END

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

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

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

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

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

Main body START

第2のサンプルUNO拡張機能を開発する目的は何か?

-Hypothesizer

第1のUNO拡張機能を開発し理解したので、このUNO拡張機能の構造を変更し、いくつかの実験もしてみたい。実際には、我々は、第1のUNO拡張機能を変更するのではなく、第2のUNO拡張機能を別の構造で開発する。

-Rebutter

いいだろう。

-Hypothesizer

我々の目的は以下の事をすることだ。

  1. UNOデータタイプの定義を専用のプロジェクトに分離し、このプロジェクトのUNOデータタイプ「マージされた」レジストリファイルとJarファイルをLibreOfficeに直接登録する。
  2. UNOユーティリティJarファイルをLibreOfficeに直接登録する。
  3. コアユーティリティプロジェクトを作り、そのJarファイルをLibreOfficeに直接登録する。
  4. グローバルUNOサービスを、'グローバルUNOサービス'インスタンスファクトリなしで、作成・利用する実験をする。
  5. UNOサービスとして登録しないUNOコンポーネントを作成・利用する実験をする。
  6. UNO旧スタイルサービスを作り、このUNO旧スタイルサービスの'グローバルUNOサービス'インスタンスファクトリを作り、この'グローバルUNOサービス'インスタンスファクトリによってこのUNO旧スタイルサービスのインスタンスを生成する実験をする。
  7. Java内で生成されたUNOオブジェクトのインスタンスを、'UnoRuntime.queryInterface'メソッドを使わずに、あるUNOインターフェースに直接キャストする実験をする。
  8. UNO旧スタイルサービスのシングルトンファクトリを作る実験をする。

実は、最終形の構造物はここにある。

-Rebutter

オーケー。上記項目についてここでコメントすることはせず、1つ1つ実際にやっていこう。

UNOデータタイププロジェクトを作り、このプロジェクトのUNOデータタイプ「マージされた」レジストリファイルとJarファイルをLibreOfficeに直接登録する

-Hypothesizer

項目1にしたがって、我々は、UNOデータタイププロジェクト、'unoAdditionalDataTypesToDisclose'を作る。

このプロジェクトディレクトリを開発ディレクトリ(共通Gradleスクリプト群またはAntビルドファイル群が格納されているディレクトリ)直下に作る。

-Rebutter

つまり、このプロジェクトディレクトリを、第1のサンプルUNO拡張機能プロジェクトとディレクトリ階層上同レベルに作るわけだ。

-Hypothesizer

そうだ。我々は、プロジェクトディレクトリを常にこの同じレベルに作る。

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

// # Change the value
ext.TARGET_NAME = "thebiasplanet.unoadditionaldatatypes.uno"

apply ("from": "../commonBuild01.gradle")

// # Change the value
defaultTasks ("makeJar")
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, 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 = []
})

apply ("from": "../commonBuild02.gradle")

デフォルトタスクが'makeJar'であることに注意しよう。

Antビルドファイルについての説明はもうしない。Gradleビルドスクリプトについての説明から分かるだろうから。

-Rebutter

オーケー。

-Hypothesizer

次に、プロジェクトディレクトリ直下に'source'ディレクトリを作り、'source'ディレクトリ配下に、UNOインターフェースのUNOIDLファイル、'unoIdl/thebiasplanet/uno/heyunoextensionsunoextension/XHeyUnoExtensions.idl'を作る。

このUNOインターフェースUNOIDLファイルの内容は以下のとおりだ。

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

#endif
-Rebutter

うーん、基本的に、構造は、第1サンプルのものと同じだ。

-Hypothesizer

そうだ。しかし、我々は、後で、いくつかのメソッドを追加する。また、後で、UNOインターフェースもいくつか追加する。

-Rebutter

なるほど。

-Hypothesizer

これは最終形ではないが、ここでプロジェクトをビルドする。ターミナルを開いて、カレントディレクトリをプロジェクトディレクトリに移し、'gradle'コマンドまたは'ant'コマンドを実行する。

-Rebutter

ははあ、コマンドが正常終了した。

-Hypothesizer

ターゲットディレクトリに、UNOデータタイプ「マージされた」レジストリファイル、'thebiasplanet.unoadditionaldatatypes.uno.rdb'とJarファイル、'thebiasplanet.unoadditionaldatatypes.uno.jar'があるはずだ。

以下のようにして、これらのファイルへのシンボリックリンクをいくつかのLibreOfficeディレクトリに作る。

Linux上:

ターミナルを開き、以下のコマンドを実行する('???'を環境に応じて設定し、必要であれば、LibreOfficeのディレクトリを変更する)。

cd /usr/lib/libreoffice/program/types
sudo ln -s /???/unoAdditionalDataTypesToDisclose/target/thebiasplanet.unoadditionaldatatypes.uno.rdb ./thebiasplanet.unoadditionaldatatypes.uno.rdb
cd ../classes
sudo ln -s /???/unoAdditionalDataTypesToDisclose/target/thebiasplanet.unoadditionaldatatypes.uno.jar ./thebiasplanet.unoadditionaldatatypes.uno.jar

Windows上:

コマンドプロンプトを管理者として開き('スタート'メニューのコマンドプロンプトアイコンを右クリックし、'管理者として実行'をクリックする)、以下のコマンドを実行する('?' と'???'を環境に応じて設定し、必要であれば、LibreOfficeのドライブ文字とディレクトリを変更する)。

D:
cd \LibreOffice\program\types
mklink .\thebiasplanet.unoadditionaldatatypes.uno.rdb ?:\???\unoAdditionalDataTypesToDisclose\target\thebiasplanet.unoadditionaldatatypes.uno.rdb
cd ..\classes
mklink .\thebiasplanet.unoadditionaldatatypes.uno.jar ?:\???\unoAdditionalDataTypesToDisclose\target\thebiasplanet.unoadditionaldatatypes.uno.jar

LibreOfficeの'program'ディレクトリ配下のファイル(Linuxでは'fundamentalrc'、Windowsでは'fundamental.ini')を開き、'URE_MORE_JAVA_TYPES'パラメータにJarファイルのシンボリックリンクパス('${BRAND_BASE_DIR}/program/classes/thebiasplanet.unoadditionaldatatypes.uno.jar')を追加する。ファイルパスはスペースで区切られることに注意しよう。

LibreOfficeのプログラムプロセスを再起動すれば、これらのファイルがLibreOfficeに登録されているはずだ。

-Rebutter

UNOデータタイプ「マージされた」レジストリファイルについて、何らかのファイルに設定を入れる必要はないのか?

-Hypothesizer

ない。シンボリックリンクを作るだけで登録される。他方で、Jarファイルのオリジナルのパスをファイル、'fundamentalrc'または'fundamental.ini'に設定するのであれば、Jarファイルのシンボリックリンクを作る必要はない。

-Rebutter

なるほど。

-Hypothesizer

LibreOfficeがアップデートされるとき、これらの設定が失われる可能性があることに注意しょう。LinuxシェルスクリプトやWindowsバッチファイルでこれらの設定をしたほうがよいかもしれない。

-Rebutter

我々がアップデートした時は、これらの設定が失われたのか?

-Hypothesizer

そう。Linuxでは、'fundamentalrc'がデフォルトのファイルで置き換わり、Windowsでは、すべての設定が失われた。

-Rebutter

ふーむ。

-Hypothesizer

とにかく、UNOデータタイプをUNO拡張機能から独立して登録することで、任意のUNO拡張機能がこうしたUNOデータタイプにアクセスできるようになる。

-Rebutter

ははあ。

UNOユーティリティJarファイルをLibreOfficeに直接登録する

-Hypothesizer

項目2にしたがって、我々は、UNOユーティリティJarファイルに先ほどと同じことをする。

Linux上:

ターミナルを開き、以下のコマンドを実行する('???'を環境に応じて設定し、必要であれば、LibreOfficeのディレクトリを変更する)。

cd /usr/lib/libreoffice/program/classes
sudo ln -s /???/unoUtilitiesToDisclose/target/thebiasplanet.unoutilities.jar ./thebiasplanet.unoutilities.jar

WIndows上:

コマンドプロンプトを管理者として開き('スタート'メニューのコマンドプロンプトアイコンを右クリックし、'管理者として実行'をクリックする)、以下のコマンドを実行する('?' と'???'を環境に応じて設定し、必要であれば、LibreOfficeのドライブ文字とディレクトリを変更する)。

D:
cd \LibreOffice\program\classes
mklink .\thebiasplanet.unoutilities.jar ?:\???\unoUtilitiesToDisclose\target\thebiasplanet.unoutilities.jar

LibreOfficeの'program'ディレクトリ配下のファイル(Linuxでは'fundamentalrc'、Windowsでは'fundamental.ini')を開き、'URE_MORE_JAVA_TYPES'パラメータにJarファイルのシンボリックリンクパス('${BRAND_BASE_DIR}/program/classes/thebiasplanet.unoutilities.jar')を追加する。シンボリックリンクを作らないのであれば、Jarファイルのオリジナルのパスを追加すればよい。

-Rebutter

分かった。

コアユーティリティプロジェクトを作り、そのJarファイルをLibreOfficeに直接登録する

-Hypothesizer

項目3にしたがって、別のプロジェクトであるコアユーティリティプロジェクト、'coreUtilitiesToDisclose'を作成する。

-Rebutter

このプロジェクトは何だ?

-Hypothesizer

これは、UNOを使用しないユーティリティクラスを格納するプロジェクトだ。

-Rebutter

ふーむ、 . . .

-Hypothesizer

もちろん、技術的には、これらのクラスをUNOユーティリティプロジェクトに含めることはできる。しかし、管理上の観点から、これらは、UNOユーティリティプロジェクトから分離する。これらは、UNOを全然使用しないプロジェクトから使われる可能性があるから。

-Rebutter

なるほど。

-Hypothesizer

'source'ディレクトリ配下に、Javaクラスのソースファイル、'java/thebiasplanet/coreutilities/messaging/Publisher.java'を作った。これは、メッセージを組み立て、メッセージボックスを表示するスタティックメソッドクラスだ。このクラスの詳細を説明するのはやめておこう。それはここの論点ではないから。

プロジェクト別Gradleビルドスクリプトの説明もやめておこう。特別なことは何もないから。

-Rebutter

いいだろう。

-Hypothesizer

いつもの方法でプロジェクトをビルドするが、Jarファイルを別の方法で登録する。

-Rebutter

ふむ?

-Hypothesizer

LibreOfficeのプログラムプロセスで、メニュー項目、'Tools'('ツール')-'Options...'('オプション...')をクリックする−>新たに開いたダイアログボックスで、左側の'LibreOffice'-'Advanced'('詳細')を選択する−>右側で、'Class Path...'('クラスパス')ボタンをクリックする−>新たに開いたダイアログボックスで、'Add Archive...'('アーカイブを追加')をクリックする−>Jarファイルを選択する−>'OK'ボタンをクリックする−'OK'ボタンをクリックする。LibreOfficeプログラムプロセスは再起動しなければならない。

-Rebutter

Jarファイルを、UNOユーティリティJarファイルと同じ方法で登録できないのか?

-Hypothesizer

できる。ただし、先の方法で登録したJarファイルのJavaクラスファイルは後の方法で登録したJarファイルのJavaクラスファイルにアクセスできるが、反対はできないことに注意しよう。すべてUNOクラスは先の方法で登録されているので、後の方法で登録されたJarファイルのJavaクラスは、UNOの機能に全くアクセスできない。

-Rebutter

すると、後の方法で登録されたJarファイルのJavaクラスは、より広い範囲のJavaクラスからアクセス可能なわけだ。

-Hypothesizer

それが、コアユーティリティJarファイルを後の方法で我々が登録する理由だ。

Main body END

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

2017年6月17日土曜日

18: サンプルUNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を理解する、パート5

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

Main body START

ビルドプロセスでは何が起きるのか?

-Hypothesizer

これで、手作業で作る構造物はすべて作った。

-Rebutter

では、これらの構造物から、ビルドプロセスにて他の構造物がすべて作られるのか?

-Hypothesizer

そうだ。ビルドプロセスで何が起きるかを説明しよう。

中間構造物を格納する'intermediate'ディレクトリがプロジェクトディレクトリ内に作られる。例えば、Javaクラスファイルは中間構造物だ。なぜなら、最終的に、Javaクラスファイル群を格納するJarファイルを格納するUNO拡張機能ファイルは必要だが、裸のJavaクラスは必要ないから。

-Rebutter

ははあ。

-Hypothesizer

次に、各UNOIDLファイルがUNOデータタイプ「マージされていない」レジストリファイルにコンパイルされ、ディレクトリ、'intermediate/registry'配下に格納される。

-Rebutter

分かった。

-Hypothesizer

次に、ターゲット構造物を格納するディレクトリ、'target'がプロジェクトディレクトリ内に作られる。

次に、UNOデータタイプ「マージされていない」レジストリファイル群が、UNOデータタイプ「マージされた」レジストリファイルにマージされ、ディレクトリ、'target'配下に格納される。

-Rebutter

UNOデータタイプ「マージされた」レジストリファイルはターゲット構造物なのか?それはUNO拡張機能ファイルに含まれるだけではないのか?

-Hypothesizer

実のところ、このサンプルUNO拡張機能では、UNO拡張機能ファイルに含まれるだけだ。しかし、LibreOfficeに直接登録することもでき、将来的には、我々は、我々のすべてのUNOデータタイプを格納する単一のUNOデータタイプ「マージされた」レジストリファイルをビルドする単一のプロジェクトを作り、この単一のUNOデータタイプ「マージされた」レジストリファイルをLibreOfficeに登録するつもりだ。

-Rebutter

すると、将来的には、我々は、すべてのUNOIDLファイルを単一のプロジェクトに集めるのか?

-Hypothesizer

そうだ。これは、我々のUNOデータタイプを、我々のどのUNO拡張機能からもアクセス可能であるようにしたいからだ。

-Rebutter

なるほど。

-Hypothesizer

次に、Javaのクラスファイル群が、UNOデータタイプ「マージされた」レジストリファイルから作られる。これらは、ディレクトリ、'intermediate/classes'配下に格納される。

-Rebutter

それらは、UNOインターフェースと'グローバルUNOサービス'インスタンスファクトリだな?

-Hypothesizer

そうだ。

次に、Javaのソースファイルをコンパイルする。クラスファイルはディレクトリ、'intermediate/classes'配下に格納される。

-Rebutter

なるほど。

-Hypothesizer

次に、Javaクラスファイル群がJarファイルにアーカイブされるが、その際、マニフェストファイルには、'MANIFEST.MF.addition'の内容が書き込まれる。このJarファイルは、ディレクトリ、'target'配下に格納される。

このサンプル拡張機能ではJarファイルはターゲットファイルではなくUNO拡張機能ファイルに含まれるファイルであるが、先ほど述べたUNOデータタイププロジェクトではJarファイルがLibreOfficeに直接登録されるし、UNO拡張機能以外のプロジェクトではJarファイルは通常ターゲットファイルなので、Jarファイルはターゲットディレクトリ配下に格納している。

-Rebutter

ふーむ、つまり、ビルドシステムはプロジェクト間で共通なので、一部のプロジェクトでターゲットファイルになり得るものは、すべてのプロジェクトでターゲットファイルとして扱われているわけだ。

-Hypothesizer

そうだ。

次に、Jarファイル、UNOデータタイプ「マージされた」レジストリファイル、UNO拡張機能ファイルのマニフェストファイル、UNOコンポーネント設定ファイルが、UNO拡張機能ファイルにアーカイブされ、ディレクトリ、'target'配下に格納される。

-Rebutter

ははあ。

-Hypothesizer

次に、UNO拡張機能ファイルがLibreOfficeに登録される。

-Rebutter

なるほど。

Main body END

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

2017年6月10日土曜日

17: サンプルUNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を理解する、パート4

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

Main body START

UNO拡張機能ファイルのマニフェストファイルを作る

-Hypothesizer

それでは、UNO拡張機能ファイルのマニフェストファイルを作ろう。UNO拡張機能ファイルとは、UNO拡張機能の全体を格納するzipファイルのことだ。このマニフェストファイルは、UNO拡張機能にどのUNOデータタイプレジストリファイルが含まれているかを指定し、また、UNOコンポーネント設定ファイル(前記事で作った)の名前を指定する。

-Rebutter

ははあ。

-Hypothesizer

ファイルは、'source'ディレクトリ配下の'resource/'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 registry file path -->
 <manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB" manifest:full-path="thebiasplanet.hiunoextensionsunoextension.uno.rdb"/>
 <!-- # Change the components file path -->
 <manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-components" manifest:full-path="thebiasplanet.hiunoextensionsunoextension.uno.components"/>
</manifest:manifest>
-Rebutter

ふむ?ふむ。いいだろう。

登録するグローバルUNOサービスの'グローバルUNOサービス'インスタンスファクトリを作る

-Hypothesizer

実のところ、これは必須ではないのだが、我々は、LibreOfficeに登録するグローバルUNOサービスの'グローバルUNOサービス'インスタンスファクトリを作る。

-Rebutter

「LibreOfficeに登録するグローバルUNOサービス」というのは、'thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensions'のことだな?

-Hypothesizer

そう。グローバルUNOサービスはグローバルUNOサービスマネージャーを使ってインスタンス化することができ、もしそうするのであれば、この'グローバルUNOサービス'インスタンスファクトリは必要ない。'グローバルUNOサービス'インスタンスファクトリは、特定1つのグローバルUNOサービスのインスタンスのファクトリであり、Javaのスタティックメソッドクラスにマッピングされる。そうしたスタティックメソッドの1つを呼ぶだけで、グローバルUNOサービスのインスタンスを特定のUNOインターフェースのUNOプロキシとして得ることができるので、グローバルUNOサービスのインスタンスを得るのに、グローバルUNOサービスマネージャーを通してグローバルUNOサービスのインスタンスを得て、その後に特定のUNOインターフェースのUNOプロキシを得るよりも面倒が少ない。

-Rebutter

ははあ。

-Hypothesizer

'source'ディレクトリ配下にUNOIDLファイル、'unoIdl/thebiasplanet/uno/hiunoextensionsunoextension/HiUnoExtensions.idl'を作り、このファイルに以下を書く。

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

// # Change the interface idl
#include "thebiasplanet/uno/hiunoextensionsunoextension/XHiUnoExtensions.idl"

// # Change the module name
module thebiasplanet { module uno { module hiunoextensionsunoextension {
 // # Change the service name and the interface name
 service HiUnoExtensions: XHiUnoExtensions {
  // # Add constructors START
  create1 ( [in] string p_message)
    raises (com::sun::star::lang::IllegalArgumentException);
  // # Add constructors END
 };
}; }; };

#endif
-Rebutter

ふーむ、このファクトリの名前を'thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensions'としたのは必須なのか?

-Hypothesizer

そうだ。この名前は、グローバルUNOサービス名の名前と同じでなければならない。この2つの名前が同じであることでのみ、このファクトリはグローバルUNOサービスに結び付けられる。

-Rebutter

分かった。

-Hypothesizer

'#ifndef' ~ '#endif'のブロックは、その中のコードが複数回実行されないようにするためのものだ。この定義名は他の定義名と違っていれば何でもかまわない。

'#include'命令で、<>は、インクルードされるファイルがシステムインクルードディレクトリ配下の場合に使い、""は、インクルードされるファイルがユーザー独自のディレクトリ配下の場合に使う。

'XHiUnoExtensions'(フルネームでは'thebiasplanet::uno::hiunoextensionsunoextension::XHiUnoExtensions')は、このグローバルUNOサービスのファクトリメソッド群の戻りタイプだ。

-Rebutter

すると、このグローバルUNOサービスのインスタンスをこのUNOインターフェースタイプのUNOプロキシとして得るわけか?

-Hypothesizer

ブリッジを通してインスタンスを得る場合はそうだ。そうでない場合は、インスタンスは、UNOプロキシではなく、UNOコンポーネントインスタンスのオリジナルそのものになる。

UNOコンポーネントが複数のUNOインターフェースを実装している場合は、そうしたUNOインターフェースの1つを戻りデータタイプに指定することで、指定したデータタイプでインスタンスが得られる。UNO新スタイルサービスに対してだけファクトリが作れるというのは正しくないことに注意しよう。

-Rebutter

なるほど。

-Hypothesizer

'create1'は、ファクトリメソッドであり、Javaではスタティックメソッドにマッピングされる。引数と'raises'句についての説明は、UNOインターフェースに対して行なった説明と同じだ。ただし、ファクトリメソッドに'[out]'や'[inout]'は指定できないことには注意しよう。

他にもファクトリメソッドが欲しければ、最初のファクトリメソッドの後ろに追加すればよい。

-Rebutter

分かった。

-Hypothesizer

これで、手作業で作る構造物はすべて作った。

Main body END

References

  • Apache OpenOffice Wiki. (2014/01/02). Apache OpenOffice Developer's Guide. Retrieved from https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide

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

16: サンプルUNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を理解する、パート3

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

Main body START

'グローバルUNOサービス'プロバイダーを作る

-Hypothesizer

それでは、'グローバルUNOサービス'プロバイダーを作ろう。'グローバルUNOサービス'プロバイダーは、グローバルUNOサービスを登録する場合に必要なクラスだ。その場合、UNO拡張機能の中に1つの'グローバルUNOサービス'プロバイダーを含める。

-Rebutter

それは何をするのだ、具体的に?

-Hypothesizer

'グローバルUNOサービス'プロバイダーは、2つのスタティックメソッドを持っている。スタティックメソッド、'__writeRegistryServiceInfo'は、グローバルUNOサービスをLibreOfficeに登録する。そして、もう一方のメソッド、'__getComponentFactory'は、指定されたUNOコンポーネント名の'シングルUNOコンポーネント'UNOオブジェクトファクトリを戻す。

-Rebutter

'シングルUNOコンポーネント'UNOオブジェクトファクトリというのは何だ?

-Hypothesizer

この用語は参考文書で使われている用語ではないが、理解可能な説明をしようとすると、そういった用語が必要になるようだ。

'シングルUNOコンポーネント'UNOオブジェクトファクトリというのは、1つのUNOコンポーネント専用のUNOオブジェクトファクトリだ。

-Rebutter

それでは、'シングルUNOコンポーネント'UNOオブジェクトファクトリは、特定1つのUNOコンポーネントのみのインスタンスを作るのか?

-Hypothesizer

そう。グローバルUNOサービスマネージャーからグローバルUNOサービスのUNOオブジェクトを得る時、まず、そのグローバルUNOサービスのUNOコンポーネントの'シングルUNOコンポーネント'UNOオブジェクトファクトリを得て、次に、その'シングルUNOコンポーネント'UNOオブジェクトファクトリからUNOオブジェクトを得る。

-Rebutter

ははあ。

-Hypothesizer

これらのスタティックメソッドを我々が明示的に呼ぶことは通常ない。これらはUNOランタイムによって呼ばれるものだ。

-Rebutter

なるほど。

-Hypothesizer

我々の'グローバルUNOサービス'プロバイダーは 'thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensionsUnoExtensionGlobalServicesProvider'だから、我々は、'source'ディレクトリ配下に、Javaソースファイル、'java/thebiasplanet/uno/hiunoextensionsunoextension/HiUnoExtensionsUnoExtensionGlobalServicesProvider.java'を作り、このファイルに以下を書く。

// # Change the package name
package thebiasplanet.uno.hiunoextensionsunoextension;

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 HiUnoExtensionsUnoExtensionGlobalServicesProvider {
 private static final Map <String, Object []> IMPLEMENTATION_CLASS_NAME_TO_IMPLEMENTATION_CLASS_AND_SERVICE_NAMES_ARRAY_MAP = new HashMap <String, Object []> ();
 static {
  // # Add implementation classes START
  HiUnoExtensionsImplementation.setThisClassToServicesProvider (IMPLEMENTATION_CLASS_NAME_TO_IMPLEMENTATION_CLASS_AND_SERVICE_NAMES_ARRAY_MAP);
  // # Add implementation classes END
 }
 
 public static XSingleComponentFactory __getComponentFactory (String p_implementationName) {
  return GlobalUnoServicesProviderUtility.getSingleComponentFactory (IMPLEMENTATION_CLASS_NAME_TO_IMPLEMENTATION_CLASS_AND_SERVICE_NAMES_ARRAY_MAP, p_implementationName);
 }
 
 public static boolean __writeRegistryServiceInfo (XRegistryKey p_registryKey) {
  return GlobalUnoServicesProviderUtility.writeServicesInformationToRegistry (IMPLEMENTATION_CLASS_NAME_TO_IMPLEMENTATION_CLASS_AND_SERVICE_NAMES_ARRAY_MAP, p_registryKey);
 }
}
-Rebutter

ふーむ、 . . .

-Hypothesizer

スタティックメンバー、'IMPLEMENTATION_CLASS_NAME_TO_IMPLEMENTATION_CLASS_AND_SERVICE_NAMES_ARRAY_MAP'は、'グローバルUNOサービス'プロバイダーがUNOサービスを登録するのに必要な情報を格納する。この情報は、対象とするUNOコンポーネント(ここでは、1つのUNOコンポーネント、'thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensionsImplementation')から取り出される。

-Rebutter

それでは、別のUNOコンポーネントも含めたい場合は、このスタティックブロックの中で、そのUNOコンポーネントの'setThisClassToServicesProvider'メソッドを呼べばよいわけだ。

-Hypothesizer

そう。この情報は別に、このメンバーに格納しなければならないわけではない。2つのスタティックメソッドが適切に実装されればそれでよい。

スタティックメソッド内の処理はユーティリティクラス、'thebiasplanet.unoutilities.serviceshandling.GlobalUnoServicesProviderUtility'に移譲される。どの'グローバルUNOサービス'プロバイダーでも同じだからだ。

-Rebutter

なるほど。

Jarのマニフェストコンテンツファイルを作る

-Hypothesizer

'source'ディレクトリ配下にJarマニフェストコンテンツファイル、'resource/MANIFEST.MF.addition'を作り、このファイルに以下を書く。

Comment01: # Change the class name
RegistrationClassName: thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensionsUnoExtensionGlobalServicesProvider
UNO-Type-Path: <>

'MANIFEST.MF.addition'は、Jarマニフェストファイルに追加されるコンテンツを格納するファイルだ。

-Rebutter

うーむ、 . . . 'グローバルUNOサービス'プロバイダーが指定されている理由は理解できる。UNOランタイムは、我々のUNO拡張機能の中のどれが'グローバルUNOサービス'プロバイダーかを知らなければならない。

"UNO-Type-Path: <>"という行は何をしているのか?

-Hypothesizer

'UNO-Type-Path'は、このマニフェストファイルが属するJarファイルから使用されるUNOデータタイプを表わすJavaクラスを格納しているJarファイルを指定する。'<>'は、このマニフェストファイルが属するJarファイルを意味している。

-Rebutter

そうした追加のJarファイルはどこにあればよいのか?それらのJarファイルを我々のUNO拡張機能ファイルの中に入れなければならないのか?

-Hypothesizer

正直なところ、私は知らない。実のところ、共通のJarファイルを読み込むのに別の方法を我々は使うので、我々は、常に'<>'または''を指定する。後者は、追加のUNOデータタイプのためのJarファイルを何も使わないことを意味する。標準UNOデータタイプを格納するJarファイルを指定する必要はないことに注意しよう。そうしたJarファイルはデフォルトで読み込まれる。

-Rebutter

「標準UNOデータタイプ」というのは、LibreOfficeに含まれているUNOデータタイプのことか?

-Hypothesizer

そう。

-Rebutter

オーケー。

UNOコンポーネント設定ファイルを作る

-Hypothesizer

UNOコンポーネント設定ファイルを作る。これは、UNO拡張機能にどのUNOコンポーネントが含まれるかを指定するものだ。このファイルではまた、グローバルUNOサービスとして登録されるUNOコンポーネントにグローバルUNOサービス名を関連付ける。

ファイルは、'source'ディレクトリ配下の'resource/thebiasplanet.hiunoextensionsunoextension.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.hiunoextensionsunoextension.uno.jar">
  <!-- # Add service implementation class names -->
  <implementation name="thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensionsImplementation">
   <!-- # Add service names -->
   <service name="thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensions"/>
  </implementation>
 </component>
</components>
-Rebutter

このXMLファイルの構造からすると、1つのUNO拡張機能に複数のJarファイルを含められるようだな。

-Hypothesizer

おそらく。しかし、その推測を確かめたことはない。今のところ、複数のJarファイルを含める必要性が全然ないから。

-Rebutter

ははあ。'component'の中には複数の'implementation'タグを含められる。

-Hypothesizer

そう。注意しておくが、このXMLファイルでは、タグ、'component'はJarファイルを指しているが、我々の用語では、「UNOコンポーネント」は常にUNOインターフェースの実装を指している。 参考文書とUNOのソフトウェアそのものが、「コンポーネント」という用語を複数の種類の実体に使用しているので、そうした不一致が避けられない。

-Rebutter

いいだろう。

Main body END

References

  • Apache OpenOffice Wiki. (2014/01/02). Apache OpenOffice Developer's Guide. Retrieved from https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide

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

2017年5月27日土曜日

15: サンプルUNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を理解する、パート2

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

Main body START

UNOコンポーネントを作る

-Hypothesizer

それでは、UNOコンポーネントを作ろう。我々のUNOコンポーネントは'thebiasplanet.uno.hiunoextensionsunoextension.HiUnoExtensionsImplementation'だから、プロジェクトディレクトリの下にJavaのソースファイル、'source/java/thebiasplanet/uno/hiunoextensionsunoextension/HiUnoExtensionsImplementation.java'を作り、以下をこのファイルに書く。

// # Change the package name
package thebiasplanet.uno.hiunoextensionsunoextension;

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 com.sun.star.lang.IllegalArgumentException;
// # Add necessary classes and interfaces END

// # Change the class name
public class HiUnoExtensionsImplementation
  extends WeakBase
  implements XServiceInfo, XInitialization,
  // # Specify the UNO interface to implement
  XHiUnoExtensions {
 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.hiunoextensionsunoextension.HiUnoExtensions");
  // # 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_implementationClassNameToImplementationClassAndServiceNamesArrayMap) {
  p_implementationClassNameToImplementationClassAndServiceNamesArrayMap.put (thisClass.getName (), new Object [] {thisClass, SERVICE_NAMES_ARRAY});
 }
 
 // # Change the class name
 public HiUnoExtensionsImplementation (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 sayHi (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

ふーむ、 . . .

-Hypothesizer

このクラスは、'com.sun.star.lib.uno.helper.WeakBase'を継承するが、これは、'com.sun.star.lang.XTypeProvider'と'com.sun.star.uno.XWeak'を実装したUNOのヘルパークラスだ。

'com.sun.star.lang.XTypeProvider'は、我々のUNOコンポーネントがLibreOffice Basicマクロからアクセスされるならば実装しておく必要がある。LibreOffice Basicマクロは、UNOコンポーネントがどのUNOインターフェースを実装しているかを、'com.sun.star.lang.XTypeProvider'を通して知らなければならない。

-Rebutter

Javaプログラムでは必要ないのに、LibreOffice Basicマクロは何故、それらを知らなければならないのか?

-Hypothesizer

UNOプロキシはUNOインターフェースに対して作られることを思い出してくれ。Javaでは、別のUNOインターフェースに対するUNOプロキシが欲しい場合、そのUNOインターフェースを指定することでそのUNOプロキシを明示的に得る。LibreOffice Basicマクロでは、このプロセスはLibreOffice Basicランタイムによって処理されるが、LibreOffice Basicランタイムは、どのUNOインターフェースを問い合わせるべきかを'com.sun.star.lang.XTypeProvider'を通して知る。

-Rebutter

それでは、LibreOffice Basicマクロでは、 UNOインターフェース毎にUNOプロキシを明示的に取得しなくてもよいわけだ。

-Hypothesizer

そうだ。

'com.sun.star.uno.XWeak'は、実のところ、我々には特に実装する必要がない。このヘルパークラスを使うのは、このヘルパークラスが'com.sun.star.lang.XTypeProvider'を実装しており、'com.sun.star.lang.XTypeProvider'だけを実装しているヘルパークラスがないからだ。

-Rebutter

'com.sun.star.uno.XWeak'の役割は何だ?

-Hypothesizer

UNOコンポーネントが'com.sun.star.uno.XWeak'を実装したら、このUNOコンポーネントを弱い参照で参照できるようになる。弱い参照は、あるオブジェクトへの参照であるが、そのオブジェクトがガーベッジコレクトされるのを妨げない参照だ。

-Rebutter

すると、我々のUNOコンポーネントのインスタンスが弱い参照で参照されているが普通の(強い)参照で参照されていない場合、そのインスタンスはそのうちガーベッジコレクトされるわけだ。

-Hypothesizer

そう。

また、我々のUNOコンポーネントは、'com.sun.star.lang.XServiceInfo'、'com.sun.star.lang.XInitialization'、'thebiasplanet.uno.hiunoextensionsunoextension.XHiUnoExtensions'も実装する。

我々のUNOコンポーネントが'com.sun.star.lang.XServiceInfo'を実装するのは、UNOサービスとして登録されるからだ。そうでなければ、'com.sun.star.lang.XServiceInfo'を実装する必要はない。

メソッド、'getImplementationName'、'supportsService'、'getSupportedServiceNames'は、このインターフェースのメソッドだ。

メソッド、'getImplementationName'は、UNOコンポーネントクラスのフルネームを戻す。

メソッド、'supportsService'は、1つのStringインスタンスを受け取り、このStringインスタンスが、このUNOコンポーネントが登録されるUNO サービス名の1つと同値であるかを判断する。

メソッド、'getSupportedServiceNames'は、このUNOコンポーネントが登録されるUNOサービス名の配列を戻す。

-Rebutter

UNOコンポーネントは、複数のUNOのサービス名で登録できるようだ。それは有用なのだろうか?

-Hypothesizer

私には分からない。とにかく、メカニズム上、1つのUNOコンポーネントを複数のUNOサービス名で登録できるようになっている。

-Rebutter

なるほど。

-Hypothesizer

'com.sun.star.lang.XInitialization'については、我々のUNOコンポーネントがUNOサービスマネージャーを通してインスタンス化される時、このインターフェースによって、我々はそうしたインスタンスの初期化をカスタマイズできる。

メソッド、'initialize'がこのインターフェースのメソッドだ。我々のUNOコンポーネントがUNOサービスマネージャーを通してインスタンス化される時、このUNOコンポーネントのJavaコンストラクタが、コンポーネントコンテキストを引数として呼び出される。次に、'initialize'メソッドが、パラメータの配列を引数として呼ばれる。

-Rebutter

ははあ。

-Hypothesizer

そして、もちろん、我々のUNOコンポーネントはインターフェース、'thebiasplanet.uno.hiunoextensionsunoextension.XHiUnoExtensions'を実装する。

-Rebutter

勿論。

-Hypothesizer

スタティックファイナルなメンバー、'SERVICE_NAMES_SET'、'thisClass'、'SERVICE_NAMES_ARRAY'は、このUNOコンポーネントがUNOサービスとして登録されるために必要な情報を格納している。この情報は、これらのメンバーとして格納されている必要は特になく、先に述べたインターフェースが適切に実装されればそれでよい。

-Rebutter

'SERVICE_NAMES_SET'に追加するUNOサービス名を変更すればよいだけなわけだ。

-Hypothesizer

そう。

メンバー、'componentContext'は、コンポーネントコンテキストを格納している。コンポーネントコンテキストは、UNOオブジェクトに取っておく必要は必ずしもないが、多くの場合、ここに取っておくと役に立つ。コンポーネントコンテキストからグローバルUNOサービスマネージャーを取得できる。そこで、我々は、Javaコンストラクタの引数で渡されたコンポーネントコンテキストをそこに保存するのをデフォルトとする。

-Rebutter

いいだろう。

-Hypothesizer

メンバー、'message'は、このUNOコンポーネント独自のメンバーだ。他のUNOコンポーネントでは、それぞれ必要なメンバーで置き換える。

-Rebutter

なるほど。

-Hypothesizer

スタティックメソッド、'setThisClassToServicesProvider'は、このUNOコンポーネントの情報を'グローバルUNOサービス'プロバイダーに提供するメソッドだ。この情報は、このメソッドで提供する必要はなく、'グローバルUNOサービス'プロバイダーが知るべきことを知れればそれでよい。

-Rebutter

オーケー。

-Hypothesizer

そして、メソッド、'sayHi'は、インターフェース、'thebiasplanet.uno.hiunoextensionsunoextension.XHiUnoExtensions'のメソッドだ。

-Rebutter

そこが、我々のUNOコンポーネント独自のアルゴリズムを記述する場所なわけだ。

Main body END

References

  • Apache OpenOffice Wiki. (2014/01/02). Apache OpenOffice Developer's Guide. Retrieved from https://wiki.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide

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