「逆引きリファレンス」カテゴリーアーカイブ

Curl ORB性能検証ライブラリ

Curl ORB for Curl 0.6には、性能テストを行うためのライブラリが追加されています。

Curl ORB for Curlの中のパッケージ、 com.curlap.orb.clientがそれに該当します。

ここでは、パッケージの中の各クラスの概要についてご説明いたします。メソッド、コンストラクタ等はアクセス修飾詞がパッケージ、パブリックのみご紹介しています。

詳細につきましては、javadocをご参照ください。

クラスの紹介(概要)

ClientUtil
ORBへのリクエスト/レスポンスのインターフェースです。内部的にこのクラスを通じてHTTP通信を行います。

HTTPSessionClient
セッション単位でのORB呼び出し用のクラスです。

ApplicationContextClient
Spring等のDIコンテナとの連携用のクラスです。

ORBClientException
クライアント側の例外をハンドリングするクラスです。

ORBServerException
サーバ側の例外をハンドリングするクラスです。

ORBDefaultServerUrl
デフォルトのORB呼び出しのためのURLが記述されています。

デフォルトのORB用URL     :DEFAULT_SERVER_URL = “http://localhost:8080/curl-orb-server”
インスタンス作成用コンテキスト :NEW_INSTANCE_CONTEXT_NAME = “/new-instance”
メソッド呼び出し用コンテキスト  :INVOKE_HTTPSESSION_NAME = “/invoke-http-session”
DIコンテナ用コンテキスト      :INVOKE_APPLICATIONCONTEXT_NAME = “/invoke-application-context”
インスタンス廃棄用コンテキスト  :DESTROY_CONTEXT_NAME = “/destroy-instance”

クラスの紹介(詳細)

ClientUtilクラス(直接呼び出すことはありません)

パッケージ

com.curlap.orb.client

スーパークラス

なし

フィールド

HttpClient

コンストラクタ

ClientUtil()
ClientUtilクラスをインスタンス化します。

メソッド

requestToORB (管理するイベントを登録)
引数1(request:InstanceManagementRequest):リクエスト内容
引数2(uri:String):curl ORB サーバのURL
戻り値:Object リクエストに対してのレスポンス

HTTPSessionClientクラス

パッケージ

com.curlap.orb.client

スーパークラス

なし

フィールド

なし

コンストラクタ

HTTPSessionClient (指定したクラスをインスタンス化)
引数1(serverUrl:String):*オプション。ORBサーバのURLを指定。
デフォルトは“http://localhost:8080/curl-orb-server”
引数2(contextName:String) :*オプション。インスタンス作成用コンテキストを指定。
デフォルトは“/new-instance”
引数3(className:String) :*必須。インスタンス化したいクラス名
引数4(args:Object[]) :*必須。インスタンス化したいクラス必要な引数

呼び出し方
1.HttpSessionClient(java.lang.String className, java.lang.Object[] args)
2.HttpSessionClient(java.lang.String contextName, java.lang.String className, java.lang.Object[] args)
3.HttpSessionClient(java.lang.String serverUrl, java.lang.String contextName, java.lang.String className, java.lang.Object[] args)

メソッド

invokeMethod (インスタンス化したクラスのメソッドを呼び出す)
引数1(contextName:String):*オプション。インスタンス作成用コンテキストを指定。
デフォルトは“invoke-http-session”
引数2(methodName:String):*必須。呼び出したいメソッド名
引数3(args:Object[]):*必須。。呼び出すメソッドに必要な引数
引数4(boolean isStream):*オプション。trueに設定するとデータの返却がStream形式になります。
戻り値:Object リクエストしたメソッド対してのレスポンス

呼び出し方
1.invokeMethod(java.lang.String methodName, java.lang.Object[] args)
2.invokeMethod(java.lang.String methodName, java.lang.Object[] args, boolean isStream)
3.invokeMethod(java.lang.String contextName, java.lang.String methodName, java.lang.Object[] args)
4.invokeMethod(java.lang.String contextName, java.lang.String methodName, java.lang.Object[] args, boolean isStream)

destroy (インスタンス化したクラスの破棄)
引数1(contextName:String):*オプション。インスタンス作成用コンテキストを指定
デフォルトは“/destroy-instance”

呼び出し方
1.destroy(java.lang.String contextName)
2.destroy()

invokeStaticMethod (staticメソッドを呼び出す)
引数1(
serverUrl:String): *オプション。ORBサーバのURLを指定。
デフォルトは”http://localhost:8080/curl-orb-server”
引数2(contextName:String):*オプション。インスタンス作成用コンテキストを指定。
デフォルトは“invoke-http-session”
引数3(className:String):*必須。呼び出したいクラス名。
引数3(methodName:String):*必須。呼び出したいメソッド名。
引数4(args:Object[]):*必須。呼び出すメソッドに必要な引数。
引数5(boolean isStream):*オプション。trueに設定するとデータの返却がStream形式になります。
戻り値:Object リクエストしたメソッド対してのレスポンス

呼び出し方
1.invokeStaticMethod(java.lang.String className, java.lang.String methodName, java.lang.Object[] args)
2.invokeStaticMethod(java.lang.String className, java.lang.String methodName, java.lang.Object[] args, boolean isStream)
3.invokeStaticMethod(java.lang.String contextName, java.lang.String className, java.lang.String methodName, java.lang.Object[] args)
4.invokeStaticMethod(java.lang.String contextName, java.lang.String className, java.lang.String methodName, java.lang.Object[] args, boolean isStream)
5.invokeStaticMethod(java.lang.String serverUrl, java.lang.String contextName, java.lang.String className, java.lang.String methodName, java.lang.Object[] args)
6.invokeStaticMethod(java.lang.String serverUrl, java.lang.String contextName, java.lang.String className, java.lang.String methodName, java.lang.Object[] args, boolean isStream)

ApplicationContextClientクラス

パッケージ

com.curlap.orb.client

スーパークラス

なし

フィールド

なし

コンストラクタ

ApplicationContextClient:(ORBサーバのURLを指定)
引数1(serverUrl:String):*オプション。ORBサーバのURLを指定。
デフォルトは“http://localhost:8080/curl-orb-server”

呼び出し方
1.ApplicationContextClient(java.lang.String serverUrl)
2.ApplicationContextClient()

メソッド

invokeMethod(メソッドの呼び出し)
引数1(contextName:String):*オプション。インスタンス作成用コンテキストを指定。
デフォルトは“/invoke-application-context”
引数2(componentName:String):*必須。呼び出したいコンポーネント名。
引数3(methodName:String):*必須。呼び出したいメソッド名。
引数4(args:Object[]):*必須。呼び出したいメソッドに必要な引数。
引数5(boolean isStream):*オプション。trueに設定するとデータの返却がStream形式になります。
戻り値:Object リクエストに対してのレスポンス

呼び出し方
1.invokeMethod(java.lang.String componentName, java.lang.String methodName, java.lang.Object[] args)
2.invokeMethod(java.lang.String componentName, java.lang.String methodName, java.lang.Object[] args, boolean isStream)
3.invokeMethod(java.lang.String contextName, java.lang.String componentName, java.lang.String methodName, java.lang.Object[] args)
4.invokeMethod(java.lang.String contextName, java.lang.String componentName, java.lang.String methodName, java.lang.Object[] args, boolean isStream)

ORBClientException

パッケージ

com.curlap.orb.client

スーパークラス

Exception

フィールド

なし

コンストラクタ

ORBClientException:(クライアント側でのスローできる例外を定義します)
ORBClientException()

例外を新たに構築します。

呼び出し方
1.ORBClientException()
2.ORBClientException(java.lang.String message)
3.ORBClientException(java.lang.String message, java.lang.Throwable rootCause)
4.ORBClientException(java.lang.Throwable rootCause)

ORBServerException

パッケージ

com.curlap.orb.client

スーパークラス

Exception

フィールド

なし

コンストラクタ

ApplicationContextClient:(ORBサーバのURLを指定)
ORBServerException()

例外を新たに構築します

呼び出し方
1.ORBServerException()
2.ORBServerException(ExceptionContent exceptionContent)
3.ORBServerException(java.lang.String message)
4.ORBServerException(java.lang.String message, java.lang.Throwable rootCause)
5.ORBServerException(java.lang.Throwable rootCause)

メソッド

getExceptionContent(例外の返却)

呼び出し方
getExceptionContent()  

サーバURLの指定

デフォルトサーバURLの指定

サーバサイドのCurl ORBのデフォルトURLはhttp://localhost:8080/curl-orb-serverです。もし変更したい場合は、{set-default-server-url}プロシージャを利用してください。
例えば以下のように記載します。

{import * from COM.CURL.ORB}

{set-default-server-url {url “http://hogehoge:8888/test-server“}}

通常これは、フレームワーク等で、アプリケーション起動の最初に記述することとなります。

サービスクラスごとにサーバURLを指定

下図のようにサービスURLを呼び出すときに変更することができます。

server-url.jpg

これをするには、生成されたコードのコンストラクタに以下のようなserver-urlというキーワード引数があり、こちらを指定することで違うURLにアクセスすることができます。

|| 生成されたコードです。
{define-class public Foo
{inherits HttpSessionClient}

|| constructor
{constructor public {default server-url:#Url = null}
….

以下は指定例です。

def foo = {Foo server-url = {url http://test.curlap.com/test}}

その他 TIPS

クライアントキャッシュ

0.8からクライアントキャッシュ機能が追加されました。クライアント内でキャッシュすることで、サーバ通信の負荷を低減することができます。

キャッシュの種類にはメモリ or ディスク及びLRU[Least Recently Used](LRUCache) or すべて保存(SimpleCache)を選択することができます。

これを利用するためには、ダウンロードしたディレクトリの「ソースディレクトリ/tests/test-cases/cache-case.scurl」が参考になります。

|| 例.
def cache = {create-simple-cache “test”, spec = {CacheSpec disk-persistent? = true}}

{if not {cache.exists? “hoge”} then
set cache[“hoge”] = 1000
else
{output cache[“hoge”]}
}

パフォーマンス改善アノテーション

@DoNotShareアノテーションを利用することで、サービスクラスの戻り値が大量データかつ同一データが少ない場合に改善する可能性がございます。

@DoNotShare
public List<LargeDto> getLargeDto(int num) {
….
}

このアノテーションは、従来バイナリデータ通信をする際に同一データが存在すると、2件目以降はリンク情報を持つだけで、実データを保持しないようになっております。これにより、高速かつコンパクトなデータ通信を実現していますが、同一データが少ない場合は、実際にアプリケーションによっては高速になる場合もあれば、遅くなる場合もございます。当機能はこれを解決する可能性のあるパラメータです。但し、どれほど高速になるかはアプリケーションやデータの種類によって変わりますので、当アノテーションをメソッドに付与して、検証をしていただく必要があります。

データ圧縮

通信時、データを圧縮することができます。これにより大幅にトラフィック量が減ることになります。これは、HTTP圧縮の仕組みを利用します。

リクエスト時、HTTPリクエストヘッダー部(Accept-Encodingにgzip)を追加します。クライアントでは、HTTPレスポンスヘッダーにContent-Encodingがgzipであれば、解凍します。

サーバで圧縮する際は、WebサーバのHTTP圧縮機能を使います。例えばTomcatで実施する際は、server.xmlを以下のように設定します。

<Connector
acceptCount=”100″
compressableMimeType=”application/octet-stream,application/x-javascript,text/html,text/xml,text/javascript,text/css,text/plain”
compression=”on”
compressionMinSize=”2048″
connectionTimeout=”20000″
disableUploadTimeout=”true”
enableLookups=”false”
maxHttpHeaderSize=”8192″
maxSpareThreads=”75″
maxThreads=”150″
minSpareThreads=”25″
noCompressionUserAgents=”gozilla,traviata”
port=”8080″
redirectPort=”8443″
/>

これで、HTTP圧縮機能を利用することができます。

また、クライアントで圧縮機能を利用するか否かは、インターセプターを利用します。インターセプターを作成し、build-http-request-headersメソッドをオーバーライドし、HTTPヘッダーに追加します。

{method protected open {build-http-request-headers
headers:#HttpRequestHeaders
}:#HttpRequestHeaders
{return
{if-non-null h = headers then
{h.set “Accept-Encoding“, “gzip“}
h
else
{HttpRequestHeaders “Accept-Encoding“, “gzip“}} 
}
}

詳しくはインターセプター利用のページを参照ください。

Google App Engine for Javaとの連携

Google App Engine for Javaを利用したクラウド上でORBを動かす記事をCodeZine上に公開しております。

web.xmlの設定

ORBではweb.xmlに4つのサーブレットと1つのフィルターを設定する必要があります。4つのサーブレットは、ダウンロードしたディレクトリにあるものをそのままお使いください。フィルター(DefaultFilter)は、以下のようになっています。

<!– DefaultFilter –>
<filter>
<filter-name>DefaultFilter</filter-name>
<filter-class>com.curlap.orb.servlet.DefaultInstanceManagementFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DefaultFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

もしServlet2.5以上をご利用の方は、以下のようにurl-patternを4つのサーブレットのみに指定してください。(Servlet2.4では複数のurl-patternを指定できないです。)そうしますと、ORBへのリクエストのみこのフィルターを通るようになります。

<!– DefaultFilter –>
<filter>
<filter-name>DefaultFilter</filter-name>
<filter-class>com.curlap.orb.servlet.DefaultInstanceManagementFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DefaultFilter</filter-name>
<url-pattern>/invoke-application-context</url-pattern>
<url-pattern>/new-instance</url-pattern>
<url-pattern>/invoke-http-session</url-pattern>
<url-pattern>/destroy-instance</url-pattern>

</filter-mapping>

また、コード生成の際に、サーバ側でclassファイルやjarファイルを読み込みますが、その読み込みを絞り込むことができます。これには以下のようにcontext-paramのcom.curlap.orb.generator.filterを指定してください。(カンマ区切りで指定します。)

<context-param>
<param-name>com.curlap.orb.generator.filter</param-name>
<param-value>org.apache.commons.,org.springframework.,org.seasar.,ognl.,org.aopalliance.,junit.</param-value>
</context-param>

さらにバージョン0.7からは、WEB-INF/lib配下にあるjarファイルやWEB-INF/classesをコード生成の再にロードするか否かを選択できるようになりました。これにより、不要なクラスをローディングしないようにできます。以下のようにcom.curlap.org.generator.load-filetypeにclassを設定しますと、WEB-INF/classesのみをロード、jarであればWEB-INF/libのみ、bothであれば両方をロードします。

<context-param>
<param-name>com.curlap.orb.generator.load-filetype</param-name>
<param-value>class</param-value>
</context-param>

Curlドキュメントのインストール

ダウンロードしたディレクトリのcurl/COM.CURL.ORB-V0.8-docにmanifest.mcurlがあります。これをCurlIDEのメニュー→ヘルプ→ドキュメントインストールで取り込むことができます。

staticメソッド及びフィールドの扱い

Curl ORBのコード生成ツールでは、Javaで定義したstaticなメソッド及びフィールドは、Curlのクラスプロシージャクラス変数として生成されます。

例えば、Javaのサービスクラスは以下のように生成されます。(クラスプロシージャはdefine-procとして、クラス変数は、letとして生成。)

Java

public class HogeImpl {
public static String str;

public static int getBarBar() {
return 210;
}
}

Curl

{import * from COM.CURL.ORB}

{define-class public HogeImpl {inherits ApplicationContextClient}

let public str:#String

{constructor public {default}
{construct-super.ApplicationContextClient “hoge1”}
}

{define-proc public {get-bar-bar}:int
{return {ApplicationContextClient.invoke-static “hoge1”, “getBarBar”}}
}
}

これは以下のようにアクセスできます。
・クラスプロシージャ : クラス名.メソッド名
・クラス変数 : クラス名.フィールド名

{do
|| クラス変数
{output HogeImpl.str}

|| クラスプロシージャ
{output HogeImple.get-bar-bar}}
}

Curl ORBのコード生成ツールを利用して、非同期通信のメソッドを生成することができます。(通常は同期通信を行うメソッドがコード生成ツールから生成されます。)

非同期通信用メソッドの生成

コード生成ツールのクラス編集画面のMethods欄に”async?“というチェックボックスがあります。これをチェックしますと非同期通信用のメソッドとしてコードが生成されます。

orb-async.jpg

以下が生成されたコードです。

{import * from COM.CURL.ORB}

{define-class public Hello {inherits ApplicationContextClient}

{constructor public {default}
{construct-super.ApplicationContextClient “hello”}

{method public {say-hello v0:String, …:EventHandler}:AsyncWorker
{return {self.async-invoke “sayHello”, arguments = {FastArray v0}, {splice …}}}
}

}

非同期での通信のため、処理を記述する際は結果を待つことなく次の処理に進んでしまいます。(例えば、以下の例ですとsay-helloメソッドを実行した後、終了結果を待たずに{output “xxx”}という行が実行されます。)そこで終了した際の処理を記述するため、生成されたコードの引数”…:EventHandler”に終了時のイベントハンドラーをセットします。この際、AsyncCallbackEvent (COM.CURL.ORB.SERVLETパッケージ内)というイベントを使用します。これによりsay-helloが終了しますと{on e:AsyncCallbackEvent do ….}の中の処理が実行されます。

{import * from COM.CURL.ORB}

{do
def hello = {Hello}
{hello.say-hello 
“this is test”,
{on e:AsyncCallbackEvent do
{if-non-null ex = e.exception then
|| Exceptionが発生した際の処理を記述
{dump ex}
else
|| 成功した際の処理を記述
|| 結果はAsyncCallbackEventのobjプロパティで取得できます。
def result = e.obj asa #String
}
}
}
{output “xxx”}
}

AsyncCallbackEventexceptionプロパティとobjプロパティを持っております。
処理中にExceptionが発生した場合は、このexception:#Exceptionをチェックすることができます。

戻り値を受け取る際は、obj:anyプロパティを参照しますと結果が受け取れます。(通常は上記コードのようにキャストすることとなります。

ヘッダー情報の送信

Curl ORBは、サーバへデータを送信する際、クライアントから任意の付加情報を送信可能です。また、アプリケーション全体に共通なヘッダ情報(グローバルヘッダー)とインスタンスの生成やメソッド実行毎に付与できるヘッダ情報(ローカルヘッダー)が利用可能となっています。

使用するAPI

set-global-headerプロシージャ 
get-global-headerプロシージャ 
clear-global-headerプロシージャ 
HttpSessionClientのコンストラクタ及び各メソッドのrequest-header:HashTableキーワード引数
ApplicationContextClientの各メソッドのrequest-header:HashTableキーワード引数
(COM.CURLAP.ORBパッケージ)

以下にサンプルコードを掲載しておきます。

|| グローバルヘッダーに値をセット
{set-global-header “global-k”, “global-v”}

|| ローカルヘッダーとして値をセット
def orb = {HttpSessionORBClient “tests1.Foo”, request-header = {HashTable “test-header-k”, “test-header-v”}}

※当ヘッダー送信機能はインターセプターを利用することで、実現可能です。(今後インターセプターを推奨としていく予定です。)

サービスクラスをクライアントでシンプルなコンテナ管理

サービスクラスをクライアントでコンテナ管理することで、サービスクラスをシングルトンとして扱うことができます。これには、set-service-to-containerプロシージャを利用して登録し、get-service-from-containerでオブジェクトを取得できます。コンテナ上では、シングルトンとして扱われ、無駄なオブジェクトを生成する必要がなくなります。

また、この際、サービスクラスごとにサーバURLが異なる場合に、サーバURLもserver-urlというキーワード引数を用いて登録しておくことができます。

|| サービスクラスの登録
||   引数は、1.任意の名称(String)、2.クラス(ClassType)、3.server-url = サーバURL(URL)

{set-service-to-container “ServiceA”, Foo}
{set-service-to-container “ServiceB”, Hoge, server-url = {url “http://nothing-url.com“}}

|| サービスの取り出し
||  引数は登録時に指定した任意の名称(String)

def foo = {get-service-from-container “ServiceA”}
def hoge = {get-service-from-container “ServiceB”}

注意)同一の名称で登録することはできません。
注意)サービスクラス(DI)のみ管理できます。 ※サービスクラス(HttpSession)は管理できません。
注意)シングルトンとして扱いたくない場合は、利用しないでください。

Curl Sonntag とは

 

Curl Sonntag とは

Curl Sonntag (カールゾンターク)とは、画面単位でプログラムを整理するためのCurlによるMVCフレームワークで、オープンソースとして提供しております。(ライセンス:Apache2.0)ソースコード及びバイナリファイルは、SourceForgeからダウンロードすることができます。

SourceForge.net(ダウンロード)

Curl Sonntagを使った開発では、1画面毎に以下の3種類のクラスを作ります。

  • ScreenGraphic (グラフィック)
  • Screen (スクリーン)
  • ScreenCommand (コマンド)

 

son1.jpg

アプリケーションのUIに応じて、1つのウィンドウを複数の画面として開発することもできます。

例えば、以下のようなヘッダー・メニュー・ボディに分かれたレイアウト構成の場合は、

son2.jpg

タブペインの場合は、

son3.jpg

のように画面を分割定義することができます。 

グラフィックとは(ScreenGraphic)

デザインをする人が、画面レイアウトを定義するクラスとなります。

  • ロジックは基本的に持たない
  • 動的なレイアウトはスクリーン(Screen)で実装

グラフィックを作成するには、VLE(ビジュアル・レイアウト・エディタ)で作成したクラスか、グラフィカルオブジェクト(FrameやCanvasを継承したもの)のようなPure Curlクラスで定義します。具体的には、以下のようなクラス定義となります。

|| グラフィカルオブジェクト
{define-class public LoginGraphic {inherits Frame}
  ・・・
}

|| VLEレイアウト
{define-layout-class public OrderEntryLayout
  ・・・
}

 

スクリーンとは(Screen)

画面毎のアプリケーションロジックを持つクラスとなります。このスクリーンクラス内で、画面の初期化やグラフィックへのアクセスなどを記述し、グラフィックの操作をカプセル化します。

スクリーンを作成するには、フレームワークのクラスを継承したクラスを定義します。例えば、VLEで作成したグラフィックの場合は「inherits {VScreen-of XxxxLayout}」のように、VLEで作成せずスクラッチでコーディングしたグラフィックの場合は「inherits {Screen-of XxxxGraphic}」のようにVScreen-ofまたはScreen-ofを継承します。(Xxxxはグラフィッククラス名です)

|| グラフィッククラスがグラフィカルオブジェクト
{define-class public LoginScreen
  {inherits {Screen-of LoginGraphic}}
  ・・・
}

|| グラフィッククラスがVLEレイアウト
{define-class public OrderEntryScreen
  {inherits {VScreen-of OrderEntryLayout}}
  ・・・
}

 

コマンドとは(ScreenCommand)

画面上の操作に応じた処理ごと(ボタンを押下したときの処理、イベントハンドラなど)にひとつずつ作成するもので、ロジックを記述します。

コマンドを作成するには、フレームワークのマクロ(define-screen-command)で定義します。

{define-screen-command
    login of screen:LoginScreen
 do
    ・・・|| ここから処理を記述
}

{define-screen-command
    login of screen:LoginScreen
    {enabled? ・・・}
 do
    ・・・|| ここから処理を記述
}

 

Curl Sonntag チュートリアル

Curl Sonntag チュートリアル

ここでは簡単なサンプルプログラムをもとに Curl Sonntag の基本的な使い方を説明します。サンプルプログラムは以下のリンクからダウンロードできます。(サンプルには pcurl 版の Curl Sonntag 0.6 が含まれています)

サンプルプログラム

サンプルの実行

ダウンロードした zip ファイルを特権のある任意のフォルダに解凍します。中に含まれる start.dcurl を実行(ダブルクリック)すると以下のようなログイン画面(を模したウィンドウ)が起動します。

son-ex1-1.png

 

 

 

 

 

ユーザIDに「sonntag」、パスワードに「curl」と入力してログインボタンを押すと画面遷移します。

son-ex1-2.png

 

 

 

 

 

以下からこのサンプルプログラムのソースコードを説明していきます。

はじめに

Sonntag では画面毎に以下の3種類のクラスを作成します。

  • スクリーングラフィック
  • スクリーン
  • スクリーンコマンド

スクリーングラフィックは、静的な画面レイアウトを表すクラスです。アプリケーションロジックは記述しません。アプリケーションロジックはスクリーンとスクリーンコマンドに持たせます。スクリーンはスクリーングラフィックと1対1で定義するクラスです。このクラスには、スクリーングラフィック上のコントロールにアクセス(値の取得、設定など)する処理や、動的に変わるレイアウトの処理などを記述します。画面上のボタンが押された場合の処理など、アプリケーションにおける主要なロジックについてはそれぞれをスクリーンコマンドとして定義します。

フレームワークパッケージのインポート

以下はサンプルプログラムに含まれる screens/load.scurl の内容です。SONNTAG.EX1.SCREENS というパッケージを定義しています。このパッケージ中に上述した3種類のクラスが含まれています。Sonntag を利用するために COM.CURLAP.SONNTAG.STANDARD パッケージをインポートしています。

{curl 6.0 package}
{curl-file-attributes character-encoding = “shift-jis”}
{package SONNTAG.EX1.SCREENS,
    {compiler-directives careful? = true}
}

{import * from COM.CURLAP.SONNTAG.STANDARD}

{include ・・・(略)}

スクリーングラフィックの定義

画面の作成する場合は、最初にスクリーングラフィックを定義します。スクリーングラフィックは一般的な Curl プログラムと同様、以下のいずれかの方法で定義できます。(Sonntag 固有のスーパークラスなどを継承する必要はありません)

  • Frame や Dialog などを継承して Curl のグラフィッククラスとして定義する
  • VLE を使ってレイアウトを作成する

screens/login/graphic.scurl ファイルにログイン画面のスクリーングラフィックが定義されています。Ex1LoginGraphic という名前の Frame のサブクラスです。

{define-class public Ex1LoginGraphic {inherits Frame}
  {constructor public {default}
    ・・・(略)
  }
}

このクラスのコンストラクタでは、ユーザIDやパスワードの入力フィールドおよびログインボタン、クリアボタンを生成・配置しています。

{define-class public Ex1LoginGraphic {inherits Frame}

  {constructor public {default}
    def vbox =
        {spaced-vbox
            margin = 5pt,
            {Table
                columns = 2,
                “ユーザID”, {TextField},
                “パスワード”, {PasswordField}
            },
            {spaced-hbox
                {CommandButton
                    width = 3cm,
                    label = “ログイン”
                },
                {CommandButton
                    width = 3cm,
                    label = “クリア”
                }
            }
        }
    {self.add vbox}
  }
 
}

このように、なるべくスクリーングラフィックはレイアウトのコードのみとなるように作成します。(上記のコードは、後ほど説明するコードを省略してあるため screens/login/graphic.scurl ファイルの内容とは若干異なります) 

スクリーンの定義

次は screens/login/screen.scurl に定義されたスクリーンクラスです。Sonntag が提供する Screen-of というクラスを継承しています。Screen-of はパラメータ化クラスで、引数にスクリーングラフィッククラス(この例では Ex1LoginGraphic)を指定しています。

{define-class public Ex1LoginScreen
  {inherits {Screen-of Ex1LoginGraphic}}

  ・・・(略)
}

補足: スクリーングラフィックを VLE で作成した場合は Screen-of の代わりに VScreen-of というスーパークラスを使用します。VScreen-of の場合も同様に、パラメータで VLE のレイアウトクラスを指定します。Sonntag で VLE を利用する方法については、今後別の記事で解説する予定です。

スクリーンからスクリーングラフィックへのアクセス

スクリーンはスクリーングラフィックに対する論理的なインターフェースを提供するように作成します。ログイン画面の例では、スクリーングラフィック上のユーザIDとパスワードのフィールドに対する入力値を取得するためのメソッド(Ex1LoginScreen.get-inputs メソッド)を定義しています。

このメソッドを作成するためにまず、Ex1LoginGraphic クラス上の TextField と PasswordField に name を設定しています。 

{define-class public Ex1LoginGraphic {inherits Frame}

  {constructor public {default}
    def vbox =
        {spaced-vbox
            margin = 5pt,
            {Table
                columns = 2,
                “ユーザID”,
                {TextField name = “userid-tf”},
                “パスワード”,
                {PasswordField name = “password-pf”}
            },
             ・・・(略)
  }
 
}

 スクリーンからはこの名前を使ってコントロールにアクセスできます。Ex1LoginScreen クラスのコンストラクタで、スーパークラスで定義された find-graphic-by-name というメソッドを使ってフィールドにユーザIDとパスワードのフィールドをセットしています。 

{define-class public Ex1LoginScreen
  {inherits {Screen-of Ex1LoginGraphic}}

  field userid-tf:TextField
  field password-pf:PasswordField

  {constructor public {default}
    set self.userid-tf = {self.find-graphic-by-name
                             “userid-tf”
                         }
asa TextField
    set self.password-pf = {self.find-graphic-by-name
                               “password-pf”
                           }
asa PasswordField
  }
  ・・・(略) 
}

このフィールドを使用して Ex1LoginScreen.get-inputs メソッドは以下のように定義されます。

  {method {get-inputs}:(userid:String, password:String)
    {return
        self.userid-tf.value,
        self.password-pf.value
    }
  }

スクリーンコマンドの定義

3種類目のスクリーンコマンドは screens/login/commands.scurl ファイルに定義されています。このファイルには、ログイン画面のログインボタンとクリアボタンに対応して、2つのスクリーンコマンドが含まれます。ここでは、先ほど説明した Ex1LoginScreen.get-inputs メソッドを使用するログインボタンのスクリーンコマンドについて見ていきます。

スクリーンコマンドは define-screen-command という Sonntag のマクロを使用して定義します。ログインボタンの処理に対応するスクリーンコマンドの定義は以下のようになっています。 

||| Ex1LoginScreen の “login” スクリーンコマンドの定義
{define-screen-command
    login of screen:Ex1LoginScreen
 do
  ・・・(コマンドの処理)
}

このマクロの構文は {define-screen-command コマンド名 of [変数名:]スクリーンクラス do ・・・} です。(詳細は API リファレンスを参照してください) 上記の例は、「login」という名前の Ex1LoginScreen に関連付けされたスクリーンコマンドの定義になります。do キーワード以降には任意の Curl ステートメントを記述できます。このステートメント中では、指定した変数名(上記の例では「screen」)でスクリーンを参照することができます。「login」コマンドでは以下の赤字部分でスクリーンのメソッドを呼び出しています。 

{define-screen-command
    login of screen:Ex1LoginScreen
 do
    || Ex1LoginScreen.get-inputsメソッドを呼出して
    || ユーザIDとパスワードの入力値を取得する

    def (userid, password) = {screen.get-inputs}
    || ユーザID:sonntag、パスワード:curlでログイン
    {if userid == “sonntag” and
        password == “curl”
     then
        || 次画面に遷移する
        {screen.replace-with Ex1NextScreen}
     else
        {popup-message
            “ユーザID「sonntag」、パスワード「curl」を入力してください。”
        }
    }

}

2番目の赤字部分で呼び出している replace-with というメソッドはスクリーンのスーパークラスで定義されているものです。このメソッドはスクリーンを引数にとり(正確には ScreenFeed を引数にとる。API リファレンス参照)、その画面に表示を切り替えます。

スクリーングラフィックからスクリーンコマンドへのバインド

以上で説明したスクリーンコマンドは、スクリーングラフィック上の CommandButton に以下のような記述を加えることによってボタン押下時に実行されるようになります。

{define-class public Ex1LoginGraphic {inherits Frame}
  {constructor public {default}
    ・・・(略)
            {spaced-hbox
                {CommandButton
                    width = 3cm,
                    label = “ログイン”,
                    {bind-screen-command “login”}
                },
                {CommandButton
                    width = 3cm,
                    label = “クリア”,
                    {bind-screen-command “clear”}
                }
            }
    ・・・(略)
  }
}

bind-screen-command というマクロの呼び出しを CommandButton のコンストラクタ引数に含めます。マクロの引数で、スクリーンコマンドの名前を文字列で指定します。

スクリーンの表示(スクリーンスロット)

最後に、Sonntag を利用して作成した画面を表示する方法について説明します。Sonntag は画面を表示するためのスクリーンスロットというオブジェクトを提供しています。start.dcurl 内では、以下のように独立型アプレット用の View を作成している箇所で ScreenSlot というクラスを使用しています。

{View
    {Dialog
        {ScreenSlot Ex1LoginScreen}
    },
    ・・・(略),
    {on WindowClose do
        {exit}
    }
}

 ScreenSlot はスクリーンを表示するために使用できる BaseFrame です。上記では、コンストラクタ引数で Ex1LoginScreen を指定しており、これによってログイン画面が表示されます。(上述の replace-with メソッドと同様 ScreenSlot も ScreenFeed を受け取ります。API リファレンスを参照してください)

 

API リファレンスのインストール

Curl Sonntag の API リファレンスは以下の手順でインストールできます。

  1. SourceForge のサイトから Sonntag をダウンロードする。
  2. Curl IDE または Curl ドキュメント・ビューワのメニューから「ヘルプ→ドキュメンテーションのインストール」を選択する。
  3. ダイアログの「インストール」ボタンを押して、ダウンロードしたファイルに含まれる document/manifest.mcurl を選択する。

Curl ドキュメント・ビューワの目次に「COM.CURLAP.SONNTAG.0.6」が追加され、API の検索や参照ができるようになります。

Sonntag で VLE を利用する

Sonntag で VLE を利用する

VLE で作成したレイアウトを Sonntag のスクリーングラフィックとして利用することができます。この場合、Sonntag の仕組みによってアプリケーションロジックをスクリーンとスクリーンコマンドに分離でき、VLE で作成するレイアウトクラスにイベントハンドラなどを記述する必要がなくなります。

VLE で画面レイアウトを作成する

VLE で作成した画面レイアウトを用意します。(VLE の利用方法は「レイアウトの作成」などを参考にしてください)

Sonntag で利用するために、まずは作成したレイアウトクラスとそのコンストラクタを public にする必要があります。ソースファイルを開いて、以下の赤字のように public の記述を追加してください。

{define-layout-class public SampleLayout
    || Begin meta-data DO NOT EDIT
    ・・・(略)
    {constructor public {default}
        {self.initialize-components}
    }
}

スクリーンを定義する

VLE で作成したレイアウトクラスをスクリーングラフィックとして利用する場合は、VScreen-of というクラスを継承してスクリーンを定義します。VScreen-of はパラメータ化クラスで、以下のようにスクリーングラフィック(VLE で作成したレイアウトクラス。この例では SampleLayout)をパラメータで指定します。 

{define-class public SampleScreen
  {inherits {VScreen-of SampleLayout}}
  ・・・(略)
}

このように作成したスクリーンは、チュートリアルで説明したスクリーンと同様、スクリーンスロットを利用して以下のように表示することができます。

{ScreenSlot SampleScreen}

VLE レイアウト上のオブジェクトを参照する

スクリーンクラス(VScreen-of のサブクラス)では layout ゲッターを使用して VLE で作成したレイアウトクラスのオブジェクトを参照できます。よって、VLE 上でデザイン名(design-name)を指定したオブジェクトをスクリーンクラス内から参照する構文は以下のようになります。

self.layout.design-name

例えば以下は、デザイン名が foo-tf というコントロールの value に “FOO” という値をセットするコードです。

set self.layout.foo-tf.value = “FOO”

チュートリアルで説明した find-graphic-by-name メソッドを上記で示した方法の代わりに使用することもできます。この場合は VLE 上のオブジェクトに「デザイン名」ではなく「名前」プロパティを設定します。

Curl Lib APIリファレンスのインストール

APIリファレンスのCurlのドキュメントに追加するには、以下の手順で実施します。

  1. Curl Libをダウンロードします。(curl-lib_0.5_bin.zipをダウンロードしてください。)
  2. Curl IDEを起動
  3. メニューからヘルプ→ドキュメンテーションのインストールを選択
  4. インストールボタンを押下し、ダウンロードしたファイルの展開後のディレクトリから、COM.CURLAP.LIB.mcurlを選択します。
  5. ダイアログの一覧にCOM.CURLAP.LIBが追加されます。

これで、APIリファレンスを利用することが可能となります。
(Curl ドキュメント・ビューワを開き、目次の一覧からCOM.CURLAP.LIBを参照)

 

Curl Lib利用方法

Curl Libをプロジェクトで利用するには、以下の手順でデリゲートする必要があります。

  1. Curl Libをダウンロードし、展開する。
  2. Curl IDEで、メニューからプロジェクト→デリゲート先の追加を選択。
  3. 展開したCurl LibディレクトリのCOM.CURLAP.LIB/manifest.mcurlを選択し、開くボタンを押下。

これで、Curl LibのAPIが利用できます。(もちろん、importはしてください。)

 

 

Array、配列(FastArray)ユーティリティ

ArrayUtil

Array-ofやFastArray-ofのユーティリティとして、ArrayUtilというクラスがあります。これを用いることで、標準APIには含まれないAPIを利用することが可能となります。

ArrayUtil.output

Arrayの全データを出力します。

        def x = {{Array-of String} “apple”, “orange”, “pine”}
        {ArrayUtil.output x}

#{apple orange pine}と表示されます。

ArrayUtil.unique / ArrayUtil.unique-clone

Array内のデータで重複をなくします。例えば、”apple”, “orange”, “pine”, “orange”, “pine”という5つのデータが存在する場合、重複を排除し、、”apple”, “orange”, “pine”という3つになります。

        def x = {{Array-of String} “apple”, “orange”, “pine”, “orange”, “pine”}
        {ArrayUtil.unique x} || -> “apple”, “orange”, “pine”

ArrayUtil.contains?

Array内にデータが含まれるか否かをチェックします。

        def x = {{Array-of String} “apple”, “orange”, “pine”, “orange”, “pine”}
        {ArrayUtil.contains? x, “apple”} || -> true

ArrayUtil.grep / ArrayUtil.grep-clone

Array内のデータを正規表現にマッチするものだけに絞り込みます。

        def x = {{Array-of String} “a01”, “a02”, “b01”, “b02”, “c01”}
        {ArrayUtil.grep x, “a.*”} || -> “a01”, “a01”

似たようなAPIでArrayUtil.filter-by-prefixやArrayUtil.filter-by-suffixというものがあります。詳細はCurlドキュメンテーションを参照ください。

ArrayUtil.max / ArrayUtil.min / ArrayUtil.sum / ArrayUtil.avg

Arrayの中の最大値、最小値、合計、平均を結果として返します。

        def x = {{Array-of int} 1, 2, 3, 4, 5}
        {ArrayUtil.max x} || 5
        {ArrayUtil.min x} || 1
        {ArrayUtil.sum x} || 15
        {ArrayUtil.avg x}  || 3

その他、Array-of、FastArray-ofのための便利APIが多く用意されています。詳細は、Curlドキュメンテーションをインストールしてご参照ください。(例えば、和集合、差集合などのunion / union-all / intersect / minusなど)

パッケージ名:COM.CURLAP.LIB.COLLECTIONS 

Stringユーティリティ(日本語、文字列操作など)

StringUtilユーティリティ

StringUtil.hiragana-to-katakana / StringUtil.katakana-to-hiragana

ひらがな・カタカナ変換、カタカナ・ひらがな変換

{StringUtil.hiragana-to-katakana “ぱぴぷぺぽ”} || == “パピプペポ”
{StringUtil.katakana-to-hiragana “パピプペポ”} || == “ぱぴぷぺぽ”

StringUtil.full-to-half / StringUtil.half-to-full

全角・半角変換、半角・全角変換

{StringUtil.full-to-half “qwertyuiopasdfghjklzxcvbnm”} || == “qwertyuiopasdfghjklzxcvbnm”
{StringUtil.half-to-full “qwertyuiopasdfghjklzxcvbnm”} || == “qwertyuiopasdfghjklzxcvbnm”

StringUtil.rjust / StringUtil.pad-right

文字のパディングをします。 

{StringUtil.pad-right “abc”, 5} ||== “abc00”

{StringUtil.rjust “abc”, 5, padding = ‘0’} || == “abc00”

StringUtil.separate-by-name

文字列を分割します。その他、指定位置で分割するStringUtil.separateプロシージャなどもあります。

def (h1, t1) = {StringUtil.separate-by-char “key=value”, ‘=’}
|| h1 == “key”
|| t1 == “value”

StringUtil.insert

文字列の指定位置に新たな文字列を埋め込むことができます。

{StringUtil.insert “20090910”, “/”, 4, 6} ||== “2009/09/10”

そのStringUtilには豊富なAPIが備わっています。詳しくはCurlドキュメンテーションをインストールし、ご参照ください。

パッケージ名:COM.CURLAP.LIB.LANG

DateTimeユーティリティ(和暦・西暦変換、うるう年など)

DateTimeユーティリティ

拡張ライブラリのDateTime(日付型)のユーティリティ群です。

DateTimeUtil.is-uruu?

うるう年か否かをチェック

{DateTimeUtil.is-uruu? 2004}

DateTimeUtil.add / DateTimeUtil.minus / DateTimeUtil.modify

日付の足し算、引き算、修正

        || add
        {DateTimeUtil.add mon, day = 3}

        || minus
        {DateTimeUtil.minus mon, day = 2}
        {DateTimeUtil.minus mon, day = 1}
        
        || modify
        {DateTimeUtil.modify mon, day = 5}

DateTimeUtil.wareki-to-seireki / DateTimeUtil.seireki-to-wareki

和暦・西暦変換 

{DateTimeUtil.wareki-to-seireki Wareki.heisei, 21} || 2009

set (wareki1, nen1, wareki2, nen2) = {DateTimeUtil.seireki-to-wareki 1988}
 || wareki1 == Wareki.syouwa
 || nen1 == 63
 || wareki2 == Wareki.undefined
 || nen2 == -1

DateTimeUtil.elapsed-days / DateTimeUtil.elapsed-hours

経過時間(2つのDateTimeの差分)

def dt1 = {DateTime year = 2008, month = 1, day = 1}
def dt2 = {DateTime year = 2008, month = 2, day = 1}
{DateTimeUtil.elapsed-days dt1, dt2} ||== 31
 
def dt1 = {DateTime year = 2008, month = 1, day = 1, hour = 0}
def dt2 = {DateTime year = 2008, month = 1, day = 1, hour = 12}
{DateTimeUtil.elapsed-hours dt1, dt2} ||== 12

DateTimeUtil.last-day-of-month

DateTimeの月末を取得

def dt = {DateTime year = 2009, month = 2, day = 2}
{DateTimeUtil.last-day-of-month dt} || == 2009年2月28日

その他、便利APIが多く用意されています。詳細は、Curlドキュメンテーションをインストールしてご参照ください。

パッケージ名:COM.CURLAP.LIB.LANG

 

RecordSetユーティリティ

 

RecordSetやRecordField、RecordData、Recordに対する操作は多くのロジックで使われます。そのため、ユーティリティを作成しました。

RecordSetUtil

RecordSetUtil.append-arrays / RecordSetUtil.append-hashtables

RecordSetにArrayデータを追加します。(HashTableデータを追加する場合は、append-hashtablesを使います。)

        def rs = {RecordSet
                     {RecordFields
                         {RecordField “col1”, domain = int},
                         {RecordField “col2”, domain = String}
                     }
                 }
        def data1 = {{Array-of Array}
                        {Array 1, “test1”},
                        {Array 2, “test2”},
                        {Array 3, “test3”}
                    }
        {RecordSetUtil.append-arrays rs, data1}

RecordSetUtil.to-arrays / RecordSetUtil.to-hashtables

上記とは逆にRecordSetからArrayデータを生成します。(HashTableデータを生成する場合は、to-hashtablesを使います。)

def arrays = {RecordSetUtil.to-arrays rs}

RecordSetUtil.write-to-stream

RecordSetをOutputStreamに書き出します。

    {with-open-streams
        out = {SerializeOutputStream
                  {write-open-byte
                      url,
                      create? = create?,
                      create-mode = create-mode,
                      error-if-exists? = error-if-exists?
                  }
              }
     do
        {RecordSetUtil.write-to-stream out, rs}
    }

逆に呼び出す場合は、read-from-streamを利用します。

その他にも、RecordSetUtilにAPIがあります。詳細は、Curlドキュメンテーションをインストールして、ご参照ください。

RecordFieldUtil

RecordFieldUtil.auto-increment

MySQLなどが持つ、自動的に数値をカウントアップしてくれる機能であるauto_incrementを以下のように記述できます。

        def rs = {RecordSet
                     {RecordFields
                         {RecordField
                             “col1”, domain = int,
                             default-value = {RecordFieldUtil.auto-increment}
                         },
                         {RecordField
                             “col2”, domain = String
                         }
                     }
                 }

その他にも、RecordFieldUtilにAPIがあります。詳細は、Curlドキュメンテーションをインストールして、ご参照ください。

RecordDataUtil

RecordDataUtil.create

普通のクラスからRecordDataを生成します。(クラスのフィールド名がRecordFieldのカラム名となります。) 下記サンプルではFooクラスからRecordDataを生成しています。

{define-class public Foo
  field public col1:int
  field public col2:String
  field public col3:DateTime

  {constructor public {default col1:int, col2:String, col3:DateTime}
    set self.col1 = col1
    set self.col2 = col2
    set self.col3 = col3
  }
}

 

def rs = {RecordSet
                {RecordFields
                     {RecordField “col1”, domain = int},
                     {RecordField “col2”, domain = String},
                     {RecordField “col3”, domain = DateTime}
                }
           }
def foos =
      {{Array-of Foo}
           {Foo 1, “val1”, {DateTime year=2009, month=1, day=1}},
           {Foo 2, “val2”, {DateTime year=2009, month=2, day=2}},
           {Foo 3, “val3”, {DateTime year=2009, month=3, day=3}}
      }
{for foo in foos do
       {rs.append {RecordDataUtil.create foo}}
}

 

RecordUtil

RecordUtil.set-valeu

RecordDataUtil.createとは逆に、Recordの値をFooクラスなどのデータクラスのフィールドへ値をセットします。

        {for r in rs do
            def foo = {Foo 0, “”, {DateTime year=2008, month=1, day=1}}
            {RecordUtil.set-values r, foo}
        }

その他にも、RecordUtilにAPIがあります。詳細は、Curlドキュメンテーションをインストールして、ご参照ください。

パッケージ名:COM.CURLAP.LIB.DATA-ACCESS

 

HashTableユーティリティ

HashTableUtil

HashTable-ofのユーティリティとして、HashTableUtilというクラスがあります。これを用いることで、標準APIには含まれないAPIを利用することが可能となります。

HashTableUtil.output

HashTableの全データを出力します。

        def x = {{HashTable-of int, String} 1, “apple”, 2, “orange”, 3, “pine”}
        {HashTableUtil.output x}

1=apple
2=
orange
3=pine
と表示されます。

HashTableUtil.concat

2つのHashTableを結合します。 

        def h1 = {{HashTable-of int, String} 0, “apple”, 1, “banana”}
        def h2 = {{HashTable-of int, String} 2, “orange”, 3, “banana”, 0, “pine”}
        {HashTableUtil.concat h1, h2}

その他、HashTable-ofのための便利APIが多く用意されています。詳細は、Curlドキュメンテーションをインストールしてご参照ください。

パッケージ名:COM.CURLAP.LIB.COLLECTIONS 

マルチキーHashTable

拡張ライブラリの中に、複数のキーを保持できるHashTableクラスが含まれています。

MultiKeyHashTable-2-of クラス

複数のキー(2個)を保持できるHashTableです。 

        def hash1 = {{MultiKeyHashTable-2-of String, int, String}
                        “k1”, 10, “apple”,
                        “k2”, 10, “banana”
                    }
        {hash1.set “k3”, 20, “orange”}  || セット
        set hash1[“k4”, 20] = “blueberry”  || セット

        def v1 = {hash1.get “k1”, 10, 20} || ゲット
        def v2 = hash1[“k2”, 10, 30] || ゲット
       

MultiKeyHashTable-3-of

複数のキー(3個)を保持できるHashTableです。使い方はMultiKeyHashTable-2-ofとキーの数以外は同等です。

MultiHashTable-of

キーは1つですが、重複したキーを保持できるHashTableです。 

        def hash = {{MultiHashTable-of String, String}
                       “k1”, “apple”, “k2”, “banana”, “k1”, “blueberry”, “k1”, “melon”
                   }
        def val-array = {hash.get “k1”} || キーが”k1″の値すべて返ってきます。

パッケージ名:COM.CURLAP.LIB.COLLECTIONS 

順序HashTableと順序Set

OrderedHashTable-of

通常のHashTableは順番を持ちませんが、OrderedHashTable-ofは順序を持ちます。順番(ID)から値を取得するには、get-by-indexメソッドを利用します。

        def hash = {{OrderedHashTable-of String, String}
                       “k1”, “apple”, “k2”, “banana”, “k3”, “blueberry”
                   }
        {hash.set “k4”, “melon”}
       
        let (key:String, value:String) = {hash.get-by-index 0}
        set (key, value) = {hash.get-by-index 1}

 

 

OrderedSet-of

OrderedSet-ofは順序を持ったSet-ofです。こちらも同様に、順番(ID)から値を取得するには、get-by-indexを利用します。 

        def s = {{OrderedSet-of String} “apple”, “banana”, “orange”}
        let v:String = “”
       
        set v = {s.get-member-by-index 0}
        set v = {s.get-member-by-index 2}

パッケージ名:COM.CURLAP.LIB.COLLECTIONS 

 

 

 

arrayマクロを利用した簡単Array-of定義

Array-ofやFastArray-ofは型パラメータが必要なため記述量が多くなってしまいます。arrayマクロとfast-arrayマクロは型パラメータの記述を省略して配列を生成するためのマクロです。

arrayマクロ

このマクロは、与えられた初期値から型パラメータを推測して、適切な Array-of 配列の定義に置換されます。

|| 従来の書き方
def x = {{Array-of int} 1, 2, 3}

|| arrayマクロ利用(パラメータがintと推測されます)
|| 以下は上記と同等です。

def x = {array 1, 2, 3}

また、コンパイル時型の異なる引数が与えられた場合も、なるべく具体的な型の配列の生成を試みます。以下はサンプルです。

def x = {array {TextField}, {PasswordField}, {ComboBox}}

これは{Array-of any}ではなく、{Array-of {MultiUIValueControlFrame-of String}}となります。

fast-arrayマクロ

これはarrayマクロのFastArray-of版です。

 

 

hashtableマクロを利用した簡単HashTable-of定義

HashTable-ofの定義はジェネリクスを記述しないといけないので、ちょっとめんどくさいという方のために、hashtableマクロを提供します。この機能はarrayマクロと類似しています。

hashtableマクロ

このマクロは、初期値を与えると、その初期値からジェネリクスを推測し、型定義を自動的にしてくれます。

|| 従来の書き方
def x = {{HashTable-of String, int} “k1”, 1, “k2”, 2, “k3”, 3}

|| hashtableマクロ利用(Stringとintというジェネリクスを推測してくれます。)
|| 以下は上記と同等です。

def x = {hashtable “k1”, 1, “k2”, 2, “k3”, 3}

また、コンパイル時型の異なる引数が与えられた場合も、なるべく具体的な型の配列の生成を試みてくれます。以下はサンプルです。

def x = {hashtable {TextField}, 1, {PasswordField}, 2, {ComboBox}, 3}

これは{HashTable-of any, int}を返すのではなく、{HashTable-of {MultiUIValueControlFrame-of String}, int}を生成します。

 

 

collectマクロを利用したArray生成

collectマクロ

コンテナ (Array-of、HashTable-of, Set-of、 String、列挙型など) から新しい配列(Array-of オブジェクト) を生成します。生成される配列の要素型はコンパイル時に決定されます。

以下の例で、resultsは numbers の各要素を 1.5 倍した要素からなる{Array-of double} になります。

def numbers = {{Array-of int} 0, 5, 10, 15}
def results = {collect n in numbers do
                        n * 1.5
                   }

以下の例は、continue を使用して結果の配列に含める要素を選別しています。 

def numbers = {{Array-of int} 0, 1, 2, 3, 4, 5}
def results = {collect n in numbers do
                         || 偶数は含めない
                         {if (n mod 2) == 0 then
                               {continue}
                         }
                         n
                  }

パッケージ名:COM.CURLAP.LIB.COLLECTIONS