【ユーザー体験記】Road to Appeon PowerBuilder 2017

マイグレーション

こんにちは、サポート部の明石です。

GWはいかがお過ごしでしたか?私は、親が引っ越しをしたのでお祝いを兼ねて帰省していました。部屋は狭くなったものの新しい設備もあり生活しやすくなったと喜んでいました。

さて当ブログでも一部ご紹介しましたが、Appeon社からリリースされたAppeon PowerBuilder 2017では多くの新機能が追加されていますね。ただし過去バージョンのPowerBuilder資産をご利用の場合は、まず資産の引っ越し、すなわちマイグレーションを行う必要があります。比較的スムーズにマイグレーションは可能であるものの、Appeon PowerBuilder 2017ではこれまでのバージョンとは仕様が変更されたものもあります。

すでにAppeon PowerBuilder 2017 へマイグレーションを実施されているユーザー様も増えていますが、これから本格的にマイグレーションを実施するユーザー様もまだまだ多いかと思います。そこで今回は、弊社テクニカルブログでのご紹介となりますが、すでにAppeon PowerBuilder 2017 R3 日本語版へマイグレーションを実施していただいたPowerBuilderユーザーである神戸様(仮名)のご厚意により、Appeon PowerBuilder 2017 日本語版で仕様変更された文字列操作関数に関して情報提供をいただきましたので、この場を借りてご紹介したいと思います。神戸様は、代替関数としてグローバル関数を用意し置き換えを行われたそうですが、ご利用されているサンプルコードに関してもご提供していただけましたので併せてご紹介させていただきます。

 


仕様変更された文字列操作関数とサンプルコード

まずはじめに、PowerBuilder 12.6 以前とPowerBuilder 2017 日本語版で仕様変更された文字列操作関数と概要は以下となります。

  • MidA
    • MidA( “Aいa”, 3, 2 )のようにマルチバイト文字を分断する位置を指定した場合、12.6 以前の戻り値は “い” となりますが、2017 R2 以降の日本語版では “「a” となります。
  • RightA
    • RightA( “Aいa”, 2 )のようにマルチバイト文字を分断する位置を指定した場合、12.6 以前の戻り値は “a” となりますが、2017 R2 以降の日本語版では “「a” となります。
  • FillA
    • FillA( “Aいa”, 6 )のように繰り返した結果がマルチバイト文字を分断する場所で終わる場合、12.6 以前の戻り値は “AいaA” となりますが、2017 R2 以降の日本語版では空文字( “” ) となります。
  • PosA
    • PosA( “東京”,“結” )のようにマルチバイト文字を検索する場合、12.6 以前は文字で処理していたため戻り値は “0” となりますが、2017 R2 以降の日本語版では「Shift-JISコードの一致」で処理され戻り値は “2” となります。
  • Upper、Lower、WordCap
    • 12.6 以前は半角英字のみ大文字または小文字に変換する仕様でしたが、2017 R2 以降の日本語版では大文字と小文字の区別がある文字(英字およびギリシア文字等)はすべて変換する仕様となります。

 

上記文字列操作関数の仕様変更に対して、PowerBuilder 12.6 以前の日本語版と同等の戻り値となるよう作成されたサンプルコードは以下をご提供いただきました。

例 : 関数名 ( 代替関数名 )

戻り値の型 代替関数名 (引数の型 引数名, 引数の型 引数名, ・・・)
グローバル関数の処理内容

 

MidA ( f_mida )

string f_mida (string as_string, long al_pos, long al_len)
IF LenA(as_string) > 0 AND al_pos > 1 THEN
	IF LenA(LeftA(as_string, al_pos - 1)) <> al_pos - 1 THEN
		al_pos = al_pos - 1
	END IF
END IF

RETURN MidA(as_string, al_pos, al_len)

RightA ( f_righta )

string f_righta (string as_string, long al_len)
long ll_pos, ll_lena

ll_lena = LenA(as_string)

IF ll_lena > -1 THEN
	ll_pos = ll_lena - al_len
	IF LenA(LeftA(as_string, ll_pos)) <> ll_pos THEN
		al_len = al_len - 1
	END IF
END IF

RETURN RightA(as_string, al_len)

FillA ( f_filla )

string f_filla (string as_chars, long al_len)
long ll_rept, ll_mod, i
string ls_return

IF IsNull(as_chars) OR Trim(as_chars) = "" THEN RETURN ""

IF len(as_chars) < 0 THEN RETURN ""
	
ll_rept = Int(al_len / LenA(as_chars))
ll_mod = Mod(al_len, LenA(as_chars))

ls_return = Fill(as_chars, Len(as_chars) * ll_rept)

IF ll_mod > 0 THEN
	ls_return += LeftA(as_chars, ll_mod)
END IF

RETURN ls_return

PosA ( f_posa )

long f_posa (string as_str1, string as_str2, long al_start)
string ls_tmp
long ll_pos

IF al_start < 1 THEN RETURN 0

IF LenA(LeftA(as_str1, al_start - 1)) <> al_start - 1 THEN
	al_start = al_start + 1
END IF

ls_tmp = MidA(as_str1, al_start)
ll_pos = Pos(ls_tmp, as_str2)

IF ll_pos = 0 THEN RETURN 0

RETURN LenA(Left(ls_tmp, ll_pos - 1)) + al_start

Upper ( f_upper )

string f_upper (string as_str)
char lc_chr[]
long ll_asc, ll_idx, ll_len

lc_chr = as_str
ll_len = Len(as_str)
FOR ll_idx = 1 TO ll_len
	ll_asc = Asc(lc_chr[ll_idx])
	IF ll_asc > 96 AND ll_asc < 123 THEN
		lc_chr[ll_idx] = Char(ll_asc - 32)
	END IF
NEXT

RETURN lc_chr

Lower ( f_lower )

string f_lower (string as_str)
char lc_chr[]
long ll_asc, ll_idx, ll_len

lc_chr = as_str
ll_len = Len(as_str)
FOR ll_idx = 1 TO ll_len
	ll_asc = Asc(lc_chr[ll_idx])
	IF ll_asc > 64 AND ll_asc < 91 THEN
		lc_chr[ll_idx] = Char(ll_asc + 32)
	END IF
NEXT

RETURN lc_chr

WordCap ( f_wordcap )

string f_wordcap (string as_str)
char lc_chr[]
long ll_idx, ll_len, ll_asc
boolean lb_cap

lc_chr = as_str
lb_cap = TRUE
ll_len = Len(as_str)
FOR ll_idx = 1 TO ll_len
	ll_asc = asc(lc_chr[ll_idx])
	IF lb_cap THEN
		IF ll_asc > 96 AND ll_asc < 123 THEN
			lc_chr[ll_idx] = Char(ll_asc - 32)
		END IF
	ELSE
		IF ll_asc > 64 AND ll_asc < 91 THEN
			lc_chr[ll_idx] = Char(ll_asc + 32)
		END IF
	END IF
	lb_cap = (lc_chr[ll_idx] = " ")
NEXT

RETURN lc_chr

いかがでしょうか?比較的少ないコード量で対応ができていることが分かり驚きました。

 


グローバル関数のオーバーロード!?

「明石さん、ちなみに上記の対応方法だけではなく、私どもでは一部裏技を使っているんですよ。」とは、神戸様。

「グローバル関数で代替関数を作って置き換える場合に、MidA 関数の引数{length}などはオプションじゃないですか。そういう関数は、通常引数が異なるパターン分のグローバル関数を用意するか、呼び出し元関数の引数が同じになるように修正する必要がありますよね?」と、神戸様にご説明いただきました。

確かにそうですね。私どもでも引数の数でグローバル関数を分けるか引数の数を統一する必要があると思っています。今回で言うと、MidA 関数とPosA 関数が該当しますよね。

MidA 関数の構文

string MidA (string, start, {length})

PosA 関数の構文

Long PosA (string1, string2, {start})

「でも、この対応って意外に大変じゃないですか?そんな時、グローバル関数でもユーザーオブジェクトみたいにオーバーロードが使えると、引数の数が異なっても同じ名前が定義できるから楽に対応ができると思いませんか?」と神戸様がニヤリ。。

オーバーロード?ですか?確かに、ユーザーオブジェクトなどでは、オーバーロードが使えたと思いますがグローバル関数でも利用できましたか?

調べてみましたがPowerBuilderオンラインヘルプにはグローバル関数はオーバーロードできない( You can overload or override object functions only — you cannot overload global functions.) と記載されていますが?

「確かに。ヘルプにはオーバーロードできないと記載されていますね、私どももそこまでの確認はしておりませんでした。ですがある裏技を使うとグローバル関数でもオーバーロードができるんですよ。」と神戸様がピシャリ!!!

と、いうわけで。非オフィシャルな方法となりますが、どうしても複数のグローバル関数を用意して対応することが困難なユーザー様向けに、神戸様からちょっとした裏技的な方法としてグローバル関数のオーバーロード方法についてもご教授いただきましたので併せてご紹介いたします。ただし、この方法を検討される場合は、以下の免責事項に了承の上でご利用ください。


当ブログに掲載している情報には注意を払っておりますが、掲載された内容の正確性について日本コンピュータシステム株式会社では一切保証できません。そのためユーザー様の責任において十分な検証を行っていただいた上で使用してください。また、PowerServerへデプロイしたところ利用できなかった点、今後のPowerBuilderなどでの利用状況についても不明である点は留意してください。


 

グローバル関数でオーバーロードを作成する場合は、

  • 対象となるグローバル関数を選択した状態で右クリックから「ソースの編集(U)」を選択します。
  • ソース エディタ上にソースが表示されるため、そのソースに対して追加の処理を記述します。

例えば、PosAの代替関数として” f_PosA “という引数が3つのグローバル関数が作成されているとします。

例 : f_PosA 関数

global type f_posa from function_object
end type

forward prototypes
global function long f_posa (string as_str1, string as_str2, long al_start)
end prototypes

global function long f_posa (string as_str1, string as_str2, long al_start);
// ***************************************************************************
// 関数名	:f_posa
// 処理内容	:指定された文字列を検索し、最初に検索した文字列の先頭文字の位置を取得します
// ***************************************************************************
String Ls_tmp
Long Ll_pos

IF al_start < 1 THEN RETURN 0

IF LenA(LeftA(as_str1, al_start - 1)) <> al_start - 1 THEN
	al_start = al_start + 1
END IF

Ls_tmp = MidA(as_str1, al_start)
Ll_pos = Pos(Ls_tmp, as_str2)

IF Ll_pos = 0 THEN RETURN 0

RETURN LenA(Left(Ls_tmp, Ll_pos - 1)) + al_start
end function

このグローバル関数に対して、引数が2つの場合で実行される処理を追加します。

  • ” forward prototypes “に同じ関数名で引数が2つの関数を定義します。
  • 定義した関数の処理を追加します。

例 : f_PosA 関数(オーバーロードの処理追加後)

global type f_posa from function_object
end type

forward prototypes
global function long f_posa (string as_str1, string as_str2, long al_start)
global function long f_posa (string as_str1, string as_str2)
end prototypes

global function long f_posa (string as_str1, string as_str2, long al_start);
// ***************************************************************************
// 関数名	:f_posa
// 処理内容	:指定された文字列を検索し、最初に検索した文字列の先頭文字の位置を取得します
// ***************************************************************************
String Ls_tmp
Long Ll_pos

IF al_start < 1 THEN RETURN 0

IF LenA(LeftA(as_str1, al_start - 1)) <> al_start - 1 THEN
	al_start = al_start + 1
END IF

Ls_tmp = MidA(as_str1, al_start)
Ll_pos = Pos(Ls_tmp, as_str2)

IF Ll_pos = 0 THEN RETURN 0

RETURN LenA(Left(Ls_tmp, Ll_pos - 1)) + al_start
end function

global function long f_posa (string as_str1, string as_str2);
// ***************************************************************************
// 関数名	:f_posa
// 処理内容	:指定された文字列を検索し、最初に検索した文字列の先頭文字の位置を取得します(オプション指定無)
// ***************************************************************************
String Ls_tmp
Long Ll_pos

Ls_tmp = MidA(as_str1, 1)
Ll_pos = Pos(Ls_tmp, as_str2)

IF Ll_pos = 0 THEN RETURN 0

RETURN LenA(Left(Ls_tmp, Ll_pos - 1)) + 1
end function

この状態で保存するとグローバル関数でもオーバーロードが使用できるようになります。例えば、下記スクリプトのように同じ関数名で引数が異なる2つのグローバル関数を呼び出した場合の戻り値を確認してみます。

MessageBox("f_PosAの引数2" , f_PosA( "東京" , "東" ))
MessageBox("f_PosAの引数3" , f_PosA( "東京" , "東" , 1 ))
引数が2つ

f_PosA(“東京”, “東”)

引数が3つ

f_PosA(“東京”, “東”, 1)

 

結果はこの通り。なるほどどちらの処理も実行されメッセージが表示されることが確認できました。(凄い!!!私の中でレペゼンPowerBuilderと呼ばせていただきます!!!)

※神戸様からのご伝言ですが、オーバーロードの対応をしたグローバル関数に関して、PowerBuilder IDEから「ソースの編集」以外で開いた場合にその関数がオーバーロードの対応をした関数かどうか判断できないため、コメントや設計書などに記述するなどの対応もお忘れなく。との事です。

 


最後に

今回、Appeon PowerBuilder 2017 日本語版で仕様が変更された文字列操作関数に関して、グローバル関数を利用して代替関数を用意し置き換えする方法について、色々な情報をご提供いただきました。ただし、このサンプルはあくまでユーザー様のご厚意でご提供いただきましたサンプルとなりますので、ご利用を検討されるユーザー様の方でも十分な検証を行った上でご利用ください。

また弊社でも代替関数による対応は、コストや影響面などを考慮すると比較的容易に対応が可能な方法であると考えております。しかしながらグローバル関数をデータウィンドウ内で呼び出すと、場合によってはオリジナルの関数と比較してレスポンスが悪化することも想定されます。そのため利用する用途に併せて置き換えを検討してください。

以上、今回はユーザー様の利用事例をご紹介させていただきました。

テクニカルブログ 一覧を見る
PowerBuilder マイグレーション
PowerBuilder学習、動画で始めちゃう?