Caché システムのカスタマイズ
この章では、標準の Caché インストールをカスタマイズして拡張するさまざまな方法を説明します。
各国言語サポートのシステム・クラスの使用法
最新のアプリケーションは、一般に、余分なエンジニアリングなしにさまざまな言語や地域に適応するよう設計されています。このプロセスはインターナショナライゼーションと呼ばれます。この目的のために、特定のコンポーネントを追加して、特定の地域や言語に対してアプリケーションを適応させるプロセスをローカライズといいます。
ユーザの言語、国、およびその他の特殊な可変情報を定義する一連のパラメータは、ロケールです。ロケールは、入力、出力、およびデータ処理に関する規約を指定します。これには以下が含まれます。
-
数の形式
-
日付と時刻の形式
-
通貨記号
-
文字のソート順
-
その他の文字セットへの文字列の自動変換
ロケールを指定する場合は、一般に、使用言語と地域 (あるいは他のバリエーション) を記述します。これらには、通常、ISO (International Standards Organization) の言語Opens in a new tabと地域Opens in a new tabの略称を指定します。例えば、en-us は米国で使用される英語を示し、en-gb は英国で使用される英語を示します。
1 つのミラーのすべてのメンバ上の Caché インスタンスは、同一のロケールと照合を持っている必要があります。詳細は、"Caché 高可用性ガイド" の “ミラーリング” の章を参照してください。
%SYS.NLS のクラス
Caché では、%SYS.NLS (NLS は各国言語サポートのことです) パッケージのクラスによるローカライズをサポートしています。これらのクラスには、Caché が必要とする、インターナショナライズされたプログラムをその実行環境に適応させるための情報が含まれます。このセクションでは、オプションを簡単に説明します。詳細は、各クラスのクラス・ドキュメントを参照してください。
これらのクラスを使用すると、システムまたはプロセスに現在設定されている値を取得できます。プロセスに関連付けられている値を変更すると、その変更は直ちに反映されます。システム設定を変更するには、アプリケーションで適切な値を指定して新しいロケールを定義し、そのロケールを使用して起動するよう Caché に指示する必要があります。
%SYS.NLS.Locale
%SYS.NLS.LocaleOpens in a new tab のプロパティには、参照が必要になる場合がある現在のロケールに関する情報があります。これらの値を変更しても、システムの動作には影響しません。
%SYS.NLS.Device
%SYS.NLS.DeviceOpens in a new tab クラスには、現在のデバイスのプロパティの一部が格納されます。ただし、対象となるデバイスが、オブジェクトをインスタンス化した際に使用していたデバイスであるとは限りません。
一般に、デバイスのプロパティは、そのデバイスが開いたときに設定されます。これにより正しい変換の使用が保証されます。変換テーブルは、デバイスが開いた後に変更することもできます。それには、このクラスのプロセス・インスタンスの XLTTable プロパティを変更しますが、明確な理由がない限り、このプロパティを変更することはお勧めできません。
%SYS.NLS.DeviceOpens in a new tab にある他のプロパティにより、変換中に発生したエラーを処理できます。既定では、現在のテーブルで文字を処理できない場合、エラーはトリガされず、問題の発生した文字が疑問符 (?) に変換されます。置換値または置換文字列と呼ばれるこの文字は、他の任意の文字列に変換できます。さらに、未定義の文字を暗黙的に変換する代わりに、エラーを発行させることもできます。この動作は 既定のアクション と呼ばれ、以下から選択できます。
-
0 — エラー生成
-
1 — 変換不能な文字を置換値に置換
-
2 — エラーを無視して変換不能な文字を渡す
このクラスのプロパティの入力および出力処理には、それぞれ次のような個別のプロパティがあります。
-
InpDefaultAction
-
InpReplacementValue
-
OutDefaultAction
-
OutReplacementValue
%SYS.NLS.Format
%SYS.NLS.FormatOpens in a new tab クラスには、$ZDATE() や関連する関数の動作に影響を与えるプロパティがあります。これらのプロパティの値は、現在のロケールに定義されている値から継承されますが、他のユーザに影響を与えずにプロセス・レベルで変更することも可能です。例えば、DateSeparator および TimeSeparator プロパティにはそれぞれ、日付項目と時間項目を区切る文字が保持されます。
これらの値を変更した場合の影響については、$ZDATE、$ZDATEH、および $FNUMBER のドキュメントの説明を参照してください。
%SYS.NLS.FormatOpens in a new tab クラスの Locale プロパティでは、現在のプロセスでの値の “外観” を制御できます。以下はその例です。
-
Locale が空の文字列の場合は、システムの既定の形式 (通常はアメリカ英語) が適用されます。
-
Locale がロケール名 (rusw や csy8 など) の場合は、指定したロケールの形式を使用します。
-
Locale が Current の場合は、システムの形式を使用します。
このプロパティは、オブジェクトをインスタンス化した後、または以下のように目的のロケールを %New() メソッドに渡すことにより変更できます。
Set fmt = ##class(%SYS.NLS.Format).%New("jpnw")
これらの変更は、現在のプロセスにのみ影響します。
%SYS.NLS.Table
%SYS.NLS.TableOpens in a new tab クラスは、システムの既定を示すオブジェクトや、テーブルのさまざまなカテゴリに対するプロセスの現在の設定を示すオブジェクトをインスタンス化します。NLS の基本的なメカニズムであるテーブルでは、アプリケーション・データを入力として受け入れ、順序付けして、指定のロケールに適した形式で表示することができます。%SYS.NLS.LocaleOpens in a new tab では、システム・オブジェクトのプロパティを変更してもシステムに影響を与えません。ただし、プロセス・オブジェクトからプロパティを変更すると、関連付けられている動作が直ちに変更されます。
NLS のテーブルは、入出力テーブルと内部テーブルに分類できます。各テーブル・タイプには、以下に示すような、独自の関連データがあります。
これらのテーブルは、システムが稼動する現在のロケールでサポートされている基本文字セットと、Caché 以外のエンティティでサポートされている他言語文字セットを変換します。例えば、ロケールの文字セットが Latin2 (より正確には ISO 8859-2) で、他言語文字セットが、ターミナルとの通信に一般に使用される UTF-8 だとします。この場合、出力には Latin2–to-UTF8 (Latin2 を UTF8 へ変換する) などのテーブルが使用され、入力には UTF8–to-Latin2 (UTF8 を Latin2 へ変換する) などの逆マッピング・テーブルが必要になります。
ここでは (入力用と出力用に) 2 つのテーブルを使用していますが、通常、これらのテーブルは互いを補完します。Caché では、処理を簡潔にするため、ロケール定義とシステムの既定については、1 組みの入出力テーブルに対して 1 つの名前を使用します。この名前には、通常、他言語文字セットの名前が使用されますが、暗黙の前提として、もう 1 つの名前はロケールの文字セットを使用した名前になります。ただし、カスタム・テーブルを作成する場合は、変換が行われることを示す任意の名前を選択することができます。
入出力テーブルはデバイスで使用されます。この場合、デバイスという単語は、Caché が外部とやり取りするインタフェースを示します。プロセス・インタフェースとシステム呼び出しインタフェースを含めて、このインタフェースでは変換が必要になります。
-
ターミナル
-
その他のターミナル接続
-
外部ファイル
-
磁気テープ
-
TCP/IP 接続
-
DSM-DDP および DTM-DCP システムへの接続
-
プリンタ
-
Caché プロセス
-
システム呼び出し
内部テーブルでも、現在のロケールの文字セットで構成される文字列を他の値にマップしますが、このテーブルは外部との通信を目的とはしていません。内部テーブルは、以下の文字を識別します。
-
パターン・マッチング
文字、数字、句読点など、特定のパターン・コードに一致する文字を識別します。
-
識別子
識別子テーブルは、識別子に使用できる文字を指定します。
-
大文字のアルファベット、小文字のアルファベット、およびタイトルで使用される大文字。
これらは構造的に入出力テーブルと似ています。このテーブルは、ある 1 つの文字セットを、偶然同じである別の文字セットにマップします。ただし、これらのテーブルは、入出力処理ではなく、$ZCONVERT() のコンテキストで使用されます。
-
照合順序
これらのテーブルは、ある文字列を、グローバル添え字での使用に適したその文字列の内部表現にマップします。言語が異なれば、ディクショナリ順序での照合ルールは異なります。これらのルールは照合テーブルにカプセル化されています。
-
$X/$Y アクション
これらのテーブルでは、文字を、特殊変数 $X および $Y とどのように関係するのかを示す値にマップします。文字を出力した後に $X および $Y を増分するのか、文字を印刷可能にするかどうかなど、$X/$Y テーブルにはこれらの答えが含まれます。
Caché のすべてのバージョンで使用可能な照合のリストは固定されています。既存の照合がニーズに適合しない場合は、インターシステムズのサポート窓口Opens in a new tabまでお問い合わせください。
%SYS.NLS の使用例
これらの例はすべて実行可能ですが、いずれの例にも [実行する] ボタンはありません。これらは現在のロケールに対するプロセス既定値を操作するからです。また、多くが管理特権や %SYS ネームスペースへの書き込みアクセス権を必要とします。これらの例を実行する場合は、インターシステムズのターミナル機能 (Windows) や TCP/IP 接続を介して、かつ適切な特権を持ったユーザとして、別のプロセスで実行してください。
現在のロケール情報の表示
この例では、現在のシステム・ロケールに関する情報が表示されます。
Set Info = ##class(%SYS.NLS.Locale).%New()
Set Items = "Name" _
"/Description" _
"/Country" _
"/CountryAbbr" _
"/Language" _
"/LanguageAbbr" _
"/Currency" _
"/CharacterSet"
Write !
For i = 1 : 1 : $LENGTH(Items, "/")
{
Set Item = $PIECE(Items, "/", i)
Write $JUSTIFY(Item, 15),": ", $PROPERTY(Info, Item), !
}
使用可能なロケールの表示
この例では、使用可能なロケールに関する情報が表示されます。
Znspace "%SYS"
Set locales = ##class(%Library.ResultSet).%New("Config.NLS.Locales:List")
If $IsObject(locales) {
Set locales.RuntimeMode = 1
Set sc = locales.Execute("*")
If $SYSTEM.Status.IsOK(sc) {
Write !
While locales.Next() {
Write locales.Data("Name"), " - ", locales.Data("Description"), !
}
}
}
システムおよびプロセス・テーブル・データの表示
この例を実行する前に一部のプロパティが外部で変更されている場合を除き、この例では、システム・テーブルとプロセス・テーブルに同じ値が表示されます。
Set IOTables = "Process" _
"/CacheTerminal" _
"/OtherTerminal" _
"/File" _
"/Magtape" _
"/TCPIP" _
"/DSMDDP" _
"/DTMDCP" _
"/SystemCall" _
"/Printer"
Set IntTables = "PatternMatch" _
"/Identifier" _
"/Uppercase" _
"/Lowercase" _
"/Titlecase" _
"/Collation" _
"/XYAction"
// iterate over the systems, and then the process data
For Type = "System", "Process"
{
Write !
Set Table = ##class(%SYS.NLS.Table).%New(Type)
Write "Type: ", Type, !
Write "I/O Tables", !
For i = 1 : 1 : $LENGTH(IOTables, "/")
{
Set PropName = $PIECE(IOTables, "/", i)
Write $JUSTIFY(PropName, 15), ": ", $PROPERTY(Table, PropName), !
}
Write "Internal Tables", !
For i = 1 : 1 : $LENGTH(IntTables, "/")
{
Set PropName = $PIECE(IntTables, "/", i)
Write $JUSTIFY(PropName, 15), ": ", $PROPERTY(Table, PropName), !
}
}
日付と時刻の表示の変更
%SYS.NLS.FormatOpens in a new tab クラスには、例えば日付項目と時間項目を区切る文字をそれぞれに保持する、DateSeparator および TimeSeparator プロパティが含まれます。米国の既定のロケールである enu8 (Unicode システムでは enuw) では、これらはそれぞれ、スラッシュ文字 (/) とコロン (:) になります。これらの変更方法について以下に例を示します。
// display the current defaults
// date is 10 April 2005
// time is 6 minutes 40 seconds after 11 in the morning
Write $ZDATE("60000,40000"), !
// now change the separators and display it again
Set fmt = ##class(%SYS.NLS.Format).%New()
Set fmt.DateSeparator = "_"
Set fmt.TimeSeparator = "^"
Write !, $ZDATE("60000,40000")
以下に示すこの例では、月の名前がアルファベットの連続する文字に変換されます (デモンストレーション用)。このためには、MonthName プロパティに、スペースで区切った月名一覧を設定します。リストの先頭文字はスペースです。
// get the format class instance
Set fmt = ##class(%SYS.NLS.Format).%New()
// define the month names
Set Names = " AAA BBB CCC DDD EEE FFF GGG HHH III JJJ KKK LLL"
Set fmt.MonthAbbr = Names
Set rtn = ##class(%SYS.NLS.Format).SetFormatItem("DATEFORMAT", 2)
// show the result
Write $ZDATE(60000, 2)
数字の表示方法の変更
%SYS.NLS.FormatOpens in a new tab 内の一部のプロパティは、$Number() が数字を解釈する方法を制御します。英語のロケールでは、整数と小数点以下の数字の区切りに小数点が使用され、数字を 3 桁ずつ区切るためにコンマが使用されています。これも変更可能です。
// give the baseline display
Write $Number("123,456.78"), !
Set fmt = ##class(%SYS.NLS.Format).%New()
// use "/" for groups of digits
Set fmt.NumericGroupSeparator = "."
// group digits in blocks of 4
Set fmt.NumericGroupSize = 4
// use ":" for separating integer and fractional parts
Set fmt.DecimalSeparator = ","
// try interpreting again
Write $Number("12.3456,78"), !
ファイルの変換の設定
以下の例は、ファイルに書き込まれたデータ表現をアプリケーションで制御しています。
// show the process default translation (RAW, no translation performed)
Set Tbl = ##class(%SYS.NLS.Table).%New("Process")
Write "Process default translation: ", Tbl.File, !
// create and open a temporary file
// use XML for the translation
Set TempName = ##class(%Library.File).TempFilename("log")
Set TempFile = ##class(%Library.File).%New(TempName)
Do TempFile.Open("WSNK\XML\")
Write "Temp file: ", TempFile.CanonicalNameGet(), !
// write a few characters to show the translation
// then close it
Do TempFile.WriteLine(("--" _ $CHAR(38) _ "--"))
Do TempFile.Close()
// now re-open it in raw mode and show content
Do TempFile.Open("RSK\RAW\")
Do TempFile.Rewind()
Set MaxChars = 50
Set Line = TempFile.Read(.MaxChars)
Write "Contents: """, Line, """", !
// finish
Do TempFile.Close()
Do ##class(%Library.File).Delete(TempName)
Set TempFile = ""
変換テーブルの詳細は、$ZCONVERT 関数のドキュメントにある “3 つのパラメータ形式 : エンコード変換” のセクションを参照してください。
Config.NLS クラス
%SYS.NLS はあらゆる場所で使用可能であり、一般的な使用を目的としていますが、これとは対照的に、Config.NLS のクラスは、%SYS ネームスペースでのみ、管理者権限を持っているユーザのみが使用できます。通常、カスタムのロケールおよびテーブルを作成する必要がある管理者は、管理ポータルで NLS ページを使用します。特別な要求を持つユーザを除いては、Config.NLS を使用する必要はありません。
Config.NLS パッケージには、以下の 3 つのクラスがあります。
-
Locales – 国または地理的地域に対するすべての定義および既定値を格納します。
-
Tables – テーブルに関する高レベルの説明を格納しますが、マッピング自体は格納しません。
-
SubTables – 固有の文字のマッピングを格納します。複数のテーブルで共有することができます。
Tables クラスと SubTables クラスが分かれている主な理由は、データの重複を避けるためです。異なる文字セットに対する Tables が、同一のマッピングを共有する (したがって、同一の SubTable を共有する) こともあります。また、Tables のクラスでは既定のアクションと置換値を定義します (上記の %SYS.NLS 内のこれらのプロパティに関する説明を参照)。したがって、別個の Tables が、同一の SubTable を共有している場合でも、これらの属性が異なっている場合があります。このような柔軟性があるため、Tables と SubTables の間の関係を適切に管理することは多少複雑になりますが、メリットも大きいので無駄ではありません。すべてのハウスキーピング処理が行われている管理ポータルと %SYS.NLS クラスでは、Tables と SubTables が分かれていることはユーザからはわかりません。ただし、Config.NLS を使用する際には、これを明示的に行う必要があります。
ユーザ定義のロケールおよびテーブルの名前付け規約
カスタム項目とシステム項目とを区別して、単純にアップグレードするには、項目名の先頭に y を使用します。例えば、XLT-yEBCDIC-Latin1 や XLT-Latin1-yEBCDIC のようにします。
この規約に準拠しないユーザ定義のテーブル、サブテーブルおよびロケールは、システムのアップグレード時に削除される場合があります。これを避けるには、ユーザ定義のテーブルとロケールを XML ファイルにエクスポートし、アップグレード後に再インポートします。
インターシステムズの SubTable をコピーして、カスタムの SubTable を作成した場合、このタスクを実行するユーティリティは、自動的に同じ名前を使用し、数字の接尾辞を付加します。したがって、Latin2-to-Unicode SubTable のコピーの名前は、XLT-Latin2-Unicode.0001 および XLT-Unicode-Latin2.0001 のようになります。
Config.NLS の使用の例
このセクションでは、以下の例について説明します。
使用可能なロケールのリスト
この例では、クエリを使用し、使用可能なロケール識別子と説明のリストを取得します。Caché のインストール時には、システムに適したロケール (8 ビット文字のみをサポートするシステムでは 8 ビットのロケール、マルチバイト文字をサポートするシステムでは Unicode ロケール) のみが使用可能になります。
// use the query in Config.NLS to get the locales
ZNspace "%SYS"
Set Query = ##class(%Library.ResultSet).%New("Config.NLS.Locales:List")
Set code = Query.Execute("*")
If (##class(%SYSTEM.Status).IsError(code))
{
Do ##class(%SYSTEM.Status).DisplayError(Code)
Quit
}
// display each of them in turn
Write "Available locales and descriptions", !
While (Query.Next(.code))
{
If (##class(%SYSTEM.Status).IsError(code))
{
Do ##class(%SYSTEM.Status).DisplayError(Code)
Quit
}
Write Query.Get("Name"), ": ", Query.Get("Description"), !
}
特定のロケールのテーブルのリスト
以下の例では、米国の英語 (使用可能な場合) に対する Unicode ロケールを構成するテーブルを示します。
ZNspace "%SYS"
// establish the locale identifier, try
// United States - English - Unicode
// United States - English - 8-bit
Set Loc = "enuw"
Do ##class(Config.NLS.Locales).Exists(Loc, .Ref, .Code)
If (##class(%SYSTEM.Status).IsError(Code))
{
Set Loc = "enu8"
Do ##class(Config.NLS.Locales).Exists(Loc, .Ref, .Code)
If (##class(%SYSTEM.Status).IsError(Code))
{
Do ##class(%SYSTEM.Status).DisplayError(Code)
Quit
}
}
// get the local array of table names
Write "Tables for locale: ", Loc, !
Do Ref.GetTables(.Tables)
Set Type = $ORDER(Tables(""))
While (Type '= "")
{
Set Name = $ORDER(Tables(Type, ""))
While (Name '= "")
{
Set Mod = $ORDER(Tables(Type, Name, ""))
While (Mod '= "")
{
Write Type, " - ", Name, " - ", Mod, !
Set Mod = $ORDER(Tables(Type, Name, Mod))
}
Set Name = $ORDER(Tables(Type, Name))
}
Set Type = $ORDER(Tables(Type))
}
カスタム・ロケールの作成
この例では、カスタム・テーブルでカスタム・ロケールを作成するためのテンプレートを提供します。このカスタム・テーブルでは、EBCDIC (米国で使用される一般的な形式) と Latin-1 (ISO-8859–1) の間の変換を実行します。詳細は、各クラスのドキュメントを参照してください。
その他のすべてのテーブルについては、最初に、文字のマッピングの定義を取得する必要があります。この例では、http://source.icu-project.orgOpens in a new tab (International Components for Unicode) の Web サイトのデータ・ファイルを使用します。該当するデータ・ファイルOpens in a new tabは、シャープ記号 (#) で始まるコメント行の後に、以下の形式の一連の変換定義行が続く、テキスト・ファイルです。
<Uuuuu> \xee |0
ファイルの抜粋を以下に示します。
# #UNICODE EBCDIC_US #_______ _________ <U0000> \x00 |0 <U0001> \x01 |0 <U0002> \x02 |0 <U0003> \x03 |0 <U0004> \x37 |0 <U0005> \x2D |0 ...
これらの行では、Unicode 文字 Uaaaa が EBCDIC 文字の \xbb にマッピングされることを示しています (ここで aaaa および bb は 16 進数の表現です)。このテーブルは逆変換が可能であり、EBCDIC 文字の \xbb を Unicode 文字の Uaaaa に再マッピングすることができると仮定しています。これにより、1 回のスキャンで、同じデータ・ファイルから両側 (つまり EBCDIC-to-Latin1 および Latin1-to-EBCDIC) を作成することができます。Unicode の範囲は 0 ~ 255 のみなので、これは実際には Latin-1 テーブルになります。
このプロセスでは、最初に SubTable オブジェクトを作成し、次に Table を作成し、最後に Locale を作成します。最初の手順として、このプロセスでは 2 つの SubTables オブジェクトを作成し、Name および Type プロパティを初期化して、定義ファイルから読み込んだデータで FromTo マッピング配列を埋めます。
SubTable の名前の形式は、Type–FromEncoding–ToEncoding です。通常の I/O 変換の Type は “XLT” であるので、SubTable の名前は XLT-yEBCDIC-Latin1 および XLT-yLatin1-EBCDIC になります。
以下のコードで SubTables オブジェクトを作成します。わかりやすくするために、ここでは省略していますが、実際のプログラムでは、何度もコードの整合性チェックが行われます。この例では、このサンプル・コードを何度も実行できるように、既存の以前のバージョンの同じオブジェクト (SubTables、Tables および Locales) を削除しています。厳密には、クラス・メソッド Exists() を使用して以前のオブジェクトの存在をチェックし、既に存在する場合は、別の処理を実行する必要があります。
// Names for the new SubTables (save for later)
Set nam1 = "XLT-Latin1-yEBCDIC"
Set nam2 = "XLT-yEBCDIC-Latin1"
// Delete existing SubTables instances with same ids
Do ##class(Config.NLS.SubTables).Delete(nam1)
Do ##class(Config.NLS.SubTables).Delete(nam2)
// Create two SubTable objects
Set sub1 = ##class(Config.NLS.SubTables).%New()
Set sub2 = ##class(Config.NLS.SubTables).%New()
// Set Name and Description
Set sub1.Name = nam1
Set sub1.Description = "ICU Latin-1->EBCDIC sub-table"
Set sub2.Name = nam2
Set sub2.Description = "ICU EBCDIC ->Latin-1 sub-table"
SubTables には、マルチバイトの変換を実行するかどうかを示す小さな整数である、type プロパティが含まれます。この例では、type は、シングルバイト・マッピングを示す 0 に設定されています。データ・ファイルに定義されていないコード・ポイント (文字) がそれ自体にマッピングされるように、このマッピングは初期化されています。
// Set Type (single-to-single)
Set sub1.Type = 0
Set sub2.Type = 0
// Initialize FromTo arrays
For i = 0 : 1 : 255
{
Do sub1.FromTo.SetAt(i, i)
Do sub2.FromTo.SetAt(i, i)
}
次に、アプリケーションがファイルを読み込みます。ファイルの定義が、既定のマッピングとして設定された定義をオーバーライドします。$ZHEX() 関数により、コードが 16 進数から 10 進数に変換されます。
// Assume file is in the mgr directory
Set file = "glibc-EBCDIC_US-2.1.2.ucm"
// Set EOF exit trap
Set $ZTRAP = "EOF"
// Make that file the default device
Open file
Use file
For
{
Read x
If x?1"<U"4AN1">".E
{
Set uni = $ZHEX($E(x,3,6)),ebcdic = $ZHEX($E(x,12,13))
Do sub1.FromTo.SetAt(ebcdic,uni)
Do sub2.FromTo.SetAt(uni,ebcdic)
}
}
EOF // No further data
Set $ZT = ""
Close file
// Save SubTable objects
Do sub1.%Save()
Do sub2.%Save()
これで文字のマッピングは完了です。次に、定義したばかりの SubTables オブジェクトを参照する Table オブジェクトを作成します。Table オブジェクトは実際には SubTables の記述子であり、プロパティはわずかしかありません。以下のコードによって、この 2 つが関連付けられます。
// Delete existing Tables instances with same ids
Do ##class(Config.NLS.SubTables).Delete("XLT", "Latin1", "yEBCDIC")
Do ##class(Config.NLS.SubTables).Delete("XLT", "yEBCDIC", "Latin1")
// Create two Table objects
Set tab1 = ##class(Config.NLS.Tables).%New()
Set tab2 = ##class(Config.NLS.Tables).%New()
// Set description
Set tab1.Description = "ICU loaded Latin-1 -> EBCDIC table"
Set tab2.Description = "ICU generated EBCDIC -> Latin-1 table"
// Set From/To encodings
Set tab1.NameFrom = "Latin1"
Set tab1.NameTo = "yEBCDIC"
Set tab2.NameFrom = "yEBCDIC"
Set tab2.NameTo = "Latin1"
// Set SubTable
Set tab1.SubTableName = nam1
Set tab2.SubTableName = nam2
// Set Type
Set tab1.Type = "XLT"
Set tab2.Type = "XLT"
// Set Default Action
// 1 = Replace with replacement value
Set tab1.XLTDefaultAction = 1
Set tab2.XLTDefaultAction = 1
// Set Replacement value of "?"
Set tab1.XLTReplacementValue = $ASCII("?")
Set tab2.XLTReplacementValue = $ASCII("?")
// Set Reversibility
// 1 = Reversible
// 2 = Generated
Set tab1.XLTReversibility = 1
Set tab2.XLTReversibility = 2
// Set Translation Type
// 0 = non-modal to non-modal
Set tab1.XLTType = 0
Set tab2.XLTType = 0
// Save Table objects
Do tab1.%Save()
Do tab2.%Save()
Tables が定義されたら、構築の最後の手順として、新しいテーブルを組み込むロケール・オブジェクトの定義を行います。アプリケーションで、空の Locale オブジェクトを作成し、各プロパティを埋め込みます。これは、Tables および SubTables の場合と同様です。ただし、Locale の場合はより大きく複雑です。このような単純な変更を行う最も簡単な方法は、既存のロケールをコピーして、必要箇所のみを変更することです。このプロセスでは、ソース・ロケールとして enu8 を使用し、yen8 という新しい名前を付けます。イニシャル y によって、これがカスタム・ロケールであることが明確になり、アップグレード時に削除されなくなります。
// Delete existing Locales instance with the same id
Do ##class(Config.NLS.Locales).Delete("yen8")
// Open source locale
Set oldloc = ##class(Config.NLS.Locales).%OpenId("enu8")
// Create clone
Set newloc = oldloc.%ConstructClone()
// Set new Name and Description
Set newloc.Name = "yen8"
Set newloc.Description = "New locale with EBCDIC table"
ロケールが適切に設定されると、プロセスでは、起動時にロードされる I/O テーブルのリストに EBCDIC テーブルを追加します。これは、以下のように、配列プロパティ XLTTables にノードを挿入することによって行われます。
XLTTables(<TableName>) = <components>
-
tablename により、このロケールの入力テーブルと出力テーブルのペアが特定されます。
この名前は y で始まる必要はないので、EBCDIC を使用します。
-
components は、以下のような 4 項目のリストです。
-
入力 “From” のエンコーディング
-
入力 “To” のエンコーディング
-
出力 “From” のエンコーディング
-
出力 “To” のエンコーディング
-
以下のコードで、使用可能なロケールのリストにテーブルを追加します。
// Add new table to locale
Set component = $LISTBUILD("yEBCDIC", "Latin1", "Latin1", "yEBCDIC")
Do newloc.XLTTables.SetAt(component, "EBCDIC")
このロケールを、EBCDIC 磁気テープで読み込む場合などに頻繁に使用する場合は、以下のコードで、このデバイスのクラスに対する既定値として設定します。
// Set default for Magnetic Tapes
Set newloc.TranMagTape = "EBCDIC"
// Save the changes
Do newloc.%Save()
Caché でロケールを使用できるようにするには、その内部形式にコンパイルする必要があります。これは、“ロケールの検証” と呼ばれることもあります。IsValid() クラス・メソッドは、詳細な分析を行い、ロケールが適切に定義されていない場合、人間が読むことのできるメッセージと共に、2 つの配列を返します。1 つはエラーの配列、もう 1 つは警告の配列です。
// Check locale consistency
If '##class(Config.NLS.Locales).IsValid("yen8", .Errors, .Warns)
{
Write !,"Errors: "
ZWrite Errors
Write !,"Warnings: "
ZWrite Warns
Quit
}
// Compile new locale
Set status = ##class(Config.NLS.Locales).Compile("yen8")
If (##class(%SYSTEM.Status).IsError(status))
{
Do $System.OBJ.DisplayError(status)
}
Else
{
Write !,"Locale yen8 successfully created."
}
%Library.GlobalEdit の使用によるグローバルの照合の設定
新規に作成された Caché グローバルの照合は、グローバルが作成されたデータベースの既定の照合に自動的に設定されます。Caché のインストールにより作成されたデータベースはすべて Caché 標準の照合に設定されます。ただし、USER は Caché と共にインストールされるロケールの既定の照合に設定されます
データベースの作成後、プロパティを編集してデータベースの既定の照合を変更することができます。Caché 標準、ロケールの既定の照合、またはインスタンスにロードされたその他の照合が選択可能です。データベースの既定の照合が一度設定されると、このデータベースで作成されたすべてのグローバルは、この既定の照合で作成されます。
また、Caché では、この動作をオーバーライドしてグローバルのカスタム照合を指定することもできます。これを行うには、%Library.GlobalEditOpens in a new tab クラスの Create() メソッドを使用して、希望の照合を指定します。
Set sc = ##class(%Library.GlobalEdit).Create(ns,
global,
collation,
growthblk,
ptrblock,
keep,
journal,
.exists)
以下はその説明です。
-
ns — ネームスペースを指定します。"" は現在のネームスペースを示し、^^directoryname は特定のディレクトリを参照します。
-
global — ^cz2 などの、先頭に ^ を含むグローバル名を指定します。
-
collation — 照合を指定します。照合は、サポート対象の照合のうちのどれか 1 つです。
-
growthblk — データの開始ブロックを指定します。
-
ptrblk — ポインタの開始ブロックを指定します。
-
keep — グローバルが削除されるときに、グローバルのディレクトリ・エントリを保持するかどうかを指定します。1 に設定すると、グローバルが削除されるときに照合、保護、およびジャーナル属性を保持します。
-
journal — この引数は関係がなくなったので、無視されます。
-
exists — グローバルが既に存在するかどうかを示す変数を、参照によって指定します。
グローバルが他のグローバルとは異なる照合を必要とする環境では、データベースを異なる照合ごとに設定し、グローバル・マッピングをネームスペースに追加して、各グローバルを必要な照合を持つデータベースにマップすることをお勧めします。このメソッドによって、特別に Create() メソッド呼び出しを使用したアプリケーション・コードを変更することなく、照合を混合して使用することができます。
サポート対象の照合
以下は、CreateGlobal^%DM サブルーチンの collation 引数で使用される、Caché でサポートされている照合です。
-
5 — Cache standard
-
10 — German1
-
11 — Portuguese1
-
12 — Polish1
-
13 — German2
-
14 — Spanish1
-
15 — Danish1
-
16 — Cyrillic1
-
17 — Greek1
-
18 — Czech1
-
19 — Czech2
-
20 — Portuguese2
-
21 — Finnish1
-
23 — Cyrillic2
-
24 — Polish2
-
27 — French1
-
28 — Finnish2
-
29 — Hungarian1
-
30 — German3
-
31 — Polish3
-
32 — Spanish2
-
33 — Danish2
-
34 — Greek2
-
35 — Finnish3
-
36 — Lithuanian1
-
41 — Danish3
-
44 — Czech3
-
45 — Hungarian2
-
47 — Spanish3
-
49 — Spanish4
-
51 — Spanish5
-
52 — Finnish4
インスタンスにロードされた照合など、類似したリストを参照するには、ターミナル・ウィンドウを開き、%SYS% ネームスペースに変更して、DO ^COLLATE.コマンドを入力します。
インストール済みロケールの既定の照合
Caché の新規インストールのロケールでは、既定の照合は常に最新バージョンです。つまり、数字の接尾語が最も大きい値のものになります (前のセクションのリスト参照)。例えば、スペイン語ロケールをインストールした場合、既定の照合は Spanish5 となります。照合の古いバージョンは既存のデータベースとの互換性のためにサポートされています。
Caché インスタンスがアップグレードされた場合、既定の照合は、更新済みロケールが新しい既定を使用しても保持されます。例えば、既存のインスタンスのロケールが既定の照合として Finnish3 を使用しており、更新済みインスタンスが Finnish4 を使用する場合、アップブレードは Finnish3 を既定として保存しますが、新規グローバルおよびデータベース用に Finnish4 を使用可能にします。
^%ZSTART ルーチンと ^%ZSTOP ルーチンによる開始動作と停止動作のカスタマイズ
Caché では、特定のイベントが発生した際に、カスタム・コードを実行できます。必要とされる手順は以下の 2 つです。
-
^%ZSTART ルーチン、^%ZSTOP ルーチン、またはそれらの両方を定義します。
それらのルーチンでは、特定の処理が開始または停止した際に実行するサブルーチンを定義できます。
^%ZSTART と ^%ZSTOP は、%SYS ネームスペースで定義する必要がありますが、既定以外のデータベースにマッピングできます。
-
管理ポータルを使用して、目的のサブルーチンを呼び出すように Caché を構成します。
具体的には、^%ZSTART ルーチンと ^%ZSTOP ルーチンを定義し、特定の名前を持つサブルーチンを含める場合、処理の開始時または終了時に、システムによって自動的にこれらのサブルーチンが呼び出されます。サブルーチン名は以下のとおりです。
-
SYSTEM — Caché がシステムとして開始または停止する際に実行されます。
-
LOGIN — ユーザが %Service_Console サービスまたは Service_Telnet サービスを使用してログインまたはログアウトを実行する際に実行されます。
-
JOB — JOB が開始または終了する際に実行されます。
-
CALLIN — 外部プログラムが CALLIN を開始または完了する際に実行されます。
例えば、LOGIN^%ZSTART が定義されていて、管理ポータルを使用してこのサブルーチンを有効にしている場合、ユーザがログインすると、LOGIN^%ZSTART がシステムによって自動的に呼び出されます。
これらのサブルーチンは、複雑な計算をしたり長時間実行するためのものではありません。ネットワーク・アクセスなどのように長期的に計算したり、潜在的に長時間わたって実行すると、呼び出されたルーチンが返されるまで動作の完了が遅れます。この場合、起動までに時間がかかりすぎるために、ユーザのログインに長い時間がかかるか、JOB の処理能力が低下する場合があります。
これらのサブルーチンは、通常の Caché 操作の一部として呼び出されます。つまり、電源障害など、Caché を異常終了させる外部イベントでは、^%ZSTOP への呼び出しが生成されません。
システムに ^%ZSTOP が実装され、アプリケーションに 1つ以上の $HALT ルーチンが実装されている場合、最後の $HALT が HALT コマンドで停止するまで、^%ZSTOP コードは実行されません。$HALT ルーチンが自身の HALT コマンドの発行に失敗すると、^%ZSTOP コードは実行されない可能性があります。
設計に関する考慮事項
^%ZSTART および ^%ZSTOP の実行環境には、ある程度制約があります。設計者は以下のような点に注意してください。
-
ルーチンは ObjectScript で記述しなければなりません。
-
^%ZSTART は基本的に、引数なしの新しいコマンドで開始されるように実行されるため、ユーザのローカル変数を初期化するなどのタスクを実行するためには使用できません。
-
任意のルーチン・エントリ・ポイントが呼び出されたときに、引数として渡される値はありません。さまざまな状況で異なるアルゴリズムが適用できる場合は、呼び出されたエントリ・ポイントはルーチンの外部、つまりグローバルやシステム変数などのデータを調査して、何を実行するかを決定する必要があります。
-
あらゆる条件下で、ルーチンがうまく動作することを確認してください。ルーチンは防御的に記述します。つまり、タスクの完了に必要なすべてのリソースがすぐ使用できる場所にあり、可能であれば演算処理が始まる前に予約されることがこれらのルーチンで確認されるようにします。発生したエラーはそのシステム関数の失敗として報告されるので、エラー抑制と処理の観点から設計を考えることが重要です。リソースが見つからない、あるいはエラーが起こる際に適切に回復できない場合、さまざまな結果がもたらされます。これには、Caché の開始の失敗、スタジオなどの主要な機能の動作の不具合やすぐには検知されないようなわずかな影響などが含まれます。これらのルーチンは、細心の注意を払って記述し、シミュレート条件下でデバッグし、プロダクション・システムで使用する前にシミュレート環境下でテストすることを推奨します。
-
以前の呼び出しの間に見つかった条件や、異なるエントリ・ポイントが有効であると想定しないでください。例えば、JOB^%ZSTART への連続呼び出し間に、次の呼び出しが発生する前に前回使用したファイルが削除される可能性もあります。
-
各エントリ・ポイントはタスクを効率的に実行します。タスクの一部が、潜在的に長時間実行するものであれば、ユーザのアプリケーションの別の部分によって後から処理するように、完了に十分な情報をキューに格納することをお勧めします。
-
統計的に使用する目的で、エントリ・ポイントでデータを永続的に保持させる場合、データの保持にはグローバルや外部ファイルなどを使用する必要があります。
-
ルーチンを実行する環境についての条件は、最小限にしてください。例えば、このようなルーチンの開発者は、プログラムが常に特定のジョブ番号で実行されるとは予測できません。設計者は、さまざまなエントリ・ポイントが特定の順序で呼び出されることを想定できません。Caché を実装する複数のプロセスを呼び出す順序が、確定的であることはほとんどありません。
-
ルーチンは、システムの開始中の特定の時点で呼び出されるとは限りません。開始中のイベントの配列は、リリースのたびに、または再開のたびに変更する可能性があります。
-
いくつかの例外を除き、ルーチンでは見つけた情報をそのままにしておく必要があります。例えば、サブルーチン内で、エントリ時と終了時に $IO の値を保存してリストアせずに再代入すると、ほとんど確実にエラーの原因になります。呼び出し元のルーチンは、このような変更があったことを知る方法がありません。また、呼び出し側で実行環境に対する変更を防ぐのは非常に困難です。したがって、呼び出される側のサブルーチンで、システム処理のコンテキストを妨害しなような対策を講じる必要があります。
この変更不可規則の一般的な例外として、アプリケーション・プログラムやインストール・プログラムに固有の、プロセス・ローカル値は変更できます。例えば、SYSTEM^%ZSTART エントリ・ポイントで、システム全体の既定値を設定します。同様に、アプリケーションのテストのために日付を設定し、月末処理の妥当性を検証することもできます。
-
^%ZSTOP には、リモート・データベース内のグローバルへの参照を含めることができません。^%ZSTOP の呼び出し時に、これらの参照の一部がアクセスできなくなります。
-
これらのルーチンが CACHESYS と異なるデータベースにマップされている場合、Caché は、CACHESYS ではなく、そのデータベースから実行しようとします。当然ながら、Caché は、呼び出しルーチンにそのデータベースへの適切なアクセス権限があることを事前に確認します。そのネームスペースで必要なあらゆるアプリケーション・グローバルとマッピングに対して、ルーチンに適切なアクセス権限があることを保証するのは、管理者の責任となります。
-
SYSTEM^%ZSTART と SYSTEM^%ZSTOP は、$USERNAME が $system に設定され、$ROLES が %All に設定された状態で実行されます。コードを別のユーザ名で実行するには、$SYSTEM.Security.Login() を使用して目的の名前を設定してから、カスタム・コードで続行します。JOB を使用して追加プロセスを起動すると、それらのプロセスは、開始プロセスと同じユーザ名 (およびロール) を継承します。
Caution:^%ZSTART と ^%ZSTOP にあるすべてのエントリ・ポイントは、システム処理の重要ポイントで呼び出され、システム処理、さらにはデータ処理に対しても、広範な影響が及ぶ場合があります。これらのルーチンの指定目的により、この高いレベルの特権が必要になります。このため、これらのエントリ・ポイントで呼び出すことができるコードのすべてが、くまなくテストされていることを確認する必要があります。さらに、ユーザ指定コードが XECUTE を介して、つまり間接的に実行されることを許可しないでください。
-
終了 (つまり停止) するプロセスでは、ECP データ・サーバからの回答を必要とする任意の参照で <FUNCTION> エラーが発生する場合があります。
アップグレードでは、Caché は、CACHESYS データベースにマップされた %Z* ルーチンのみを保持します。また、.INT コードまたは .MAC コードが存在する場合は、これらをリコンパイルします。他のデータベース内でのルーチンの保存は、サイト管理者が判断します。
%ZSTART および %ZSTOP を有効にする
ルーチンの設計、開発、コンパイルが完了し、テストできるようになっていれば、管理ポータルから個々のエントリ・ポイントを有効にすることができます。システム, 構成, 開始設定 ページに移動して、適切な個々の設定を編集します。
-
SysStart、SysHalt
-
ProcessStart、ProcessHalt
-
JobStart、JobHalt
-
CallinStart、CallinHalt
1 つ以上のエントリ・ポイントを無効にするには、同じプロシージャを使用しますが、値を [偽] に変更します。
^%ZSTART および ^%ZSTOP のデバッグ
最終的な環境において ^%ZSTART と ^%ZSTOP をデバッグする機会は制限されています。エラーが発生すると、エラーはオペレータのコンソール・ログに書き込まれます。これは、それらのルーチンが実行している時点での現在のデバイスです。これは管理者用ディレクトリにある cconsole.log ファイルです。
メッセージは、失敗の原因とエラーが検出された位置を示します。この位置は、プログラム・ロジックまたはフローで実際にエラーが発生した場所とは異なる場合があります。開発者は、提供された情報からエラーの特性と場所を推定し、ルーチンを修正してください。これにより、今後のテストでは、発生するエラーの特性についてより多くの情報を得ることができるようになります。
^%ZSTART および ^%ZSTOP の削除
ルーチンを削除する前に、管理ポータル経由でエントリ・ポイントの各種オプションを必ず無効にすることを強くお勧めします。この変更を有効にするために、ポータルが Caché を再起動するように警告した場合は、次に進む前に再起動も実行してください。これにより、削除される間に実行されるエントリ・ポイントがなくなります。
^%ZSTART および ^%ZSTOP (および、サポートする任意のルーチン) が永続的に格納されることを覚えておいてください。これらのトレースをすべて削除するには、管理ポータルから削除してください。
例
以下は、システムの動作状況を追跡する単純なログの実例です。^%ZSTART と ^%ZSTOP の例を示します。どちらでも、3 番目の例のルーチン (^%ZSSUtil) のサブルーチンが便宜上使用されます。
^%ZSSUtil の例
このルーチンには、2 つのパブリック・エントリ・ポイントがあります。1 つは、オペレータのコンソール・ログ・ファイルに単一行を書き込みます。もう 1 つは、ローカル・ログ・ファイルに名前と値の組み合わせのリストを書き込みます。どちらのファイルも管理者用ディレクトリにあり、そのディレクトリは、%Library.FileOpens in a new tab クラスの ManagerDirectory() メソッドにより返されます。
%ZSSUtil ;
; this routine packages a set of subroutines
; used by the %ZSTART and %ZSTOP entry points
;
; does not do anything if invoked directly
quit
#Define Empty ""
#Define OprLog 1
WriteConsole(LineText) PUBLIC ;
; write the line to the console log
; by default the file cconsole.log in the MGR directory
new SaveIO
; save the current device and open the operator console
; set up error handling to cope with errors
; there is little to do if an error happens
set SaveIO = $IO
set $ZTRAP = "WriteConsoleExit"
open $$$OprLog
use $$$OprLog
; we do not need an "!" for line termination
; because each WRITE statement becomes its
; own console record (implicit end of line)
write LineText
; restore the previous io device
close $$$OprLog
; pick up here in case of an error
WriteConsoleExit ;
set $ZTRAP = ""
use SaveIO
quit
WriteLog(rtnname, entryname, items) PUBLIC ;
; write entries into the log file
; the log is presumed to be open as
; the default output device
;
; rtnname: distinguishes between ZSTART & ZSTOP
; entryname: the name of the entry point we came from
; items: a $LIST of name-value pairs
new ThisIO, ThisLog
new i, DataString
; preserve the existing $IO device reference
; set up error handling to cope with errors
; there is little to do if an error happens
set ThisIO = $IO
set $ZTRAP = "WriteLogExit"
; construct the name of the file
; use the month and day as part of the name so that
; it will create a separate log file each day
set ThisLog = "ZSS"
_ "-"
_ $EXTRACT($ZDATE($HOROLOG, 3), 6, 10)
_".log"
; and change $IO to point to our file
open ThisLog:"AWS":0
use ThisLog
; now loop over the items writing one line per item pair
for i = 1 : 2 : $LISTLENGTH(items)
{
set DataString = $LISTGET(items, i, "*MISSING*")
if ($LISTGET(items, (i + 1), $$$Empty) '= $$$Empty)
{
set DataString = DataString
_ ": "
_ $LISTGET(items, (i + 1))
}
write $ZDATETIME($HOROLOG, 3, 1),
?21, rtnname,
?28, entryname,
?35, DataString, !
}
; stop using the log file and switch $IO back
; to the value saved on entry
close $IO
; pick up here in case of an error
WriteLogExit ;
set $ZTRAP = ""
use ThisIO
quit
各ラベルの説明を以下に示します。
このルーチンは、他のルーチンと同様に、以下のコマンドから呼び出されると良好な結果が得られるように、まず QUIT コマンドを実行します。
do ^%ZSSUtil
#DEFINE 配列は、外観をそろえるために、プログラムの本文に指定された制約を提供します。このインスタンスでは、空文字列とオペレータのコンソール・ログのデバイス番号を指定します。
このエントリ・ポイントは非常に単純です。容量の少ない出力用に、またデバッグの出力に使用するための最小限のルーチンとして、設計されたものです。
引数として 1 つの文字列を取り、これをオペレータのコンソール・ログに出力します。ただし、呼び出し全体にわたり、現在の $IO の接続状態を保持し、リストアするための注意が必要です。
各項目がデバイスに送信された結果、コンソール・ログには別々のレコードが出力されます。したがって、以下のように、4 つのレコードを出力します。
WRITE 1, 2, 3, !
最初の 3 つは 1 桁の数字から成り、4 つ目は空白行から成ります。1 行に複数の項目を記述したい場合は、呼び出し元がこれらを文字列に連結させなければなりません。
このサブルーチンは、^%ZSTART または ^%ZSTOP 内の任意のエントリ・ポイントから呼び出すことができます。最初の 2 つの引数により、サブルーチンがどのように起動されたかを報告するために必要な情報を渡します。3 番目の引数は、ログに出力される名前と値の組み合わせの $LIST です。
このエントリ・ポイントは最初に、そこで使用するファイルの名前を作成します。ログ管理を簡素化するため、この名前にはルーチンが入力された月日が含まれます。したがって、このサブルーチンを呼び出すと、現地時間が深夜 12 時を経過するたびにファイルが新規作成されます。そのファイル名は呼び出し時にのみ決定されるため、引数として渡される名前と値の組み合わせはすべて、同じファイルに表示されます。
いったん名前が作成されると、$IO の現在値を後で使用できるように保存し、出力デバイスを指定されたログ・ファイルに切り替えます。OPEN コマンドに使用するパラメータによって、そのファイルがなければ作成するように指定されています。timeout がゼロの場合、Caché がファイルを 1 回だけ開こうとします。もし開くことができなければ失敗します。
そのファイルがいったん開かれると、コードは名前と値の組み合わせをループします。それぞれの組み合わせに対して、呼び出し元ルーチン名およびエントリ・ポイント名が書き込まれ、その行に名前と値の組み合わせが続きます (値の部分が空の文字列の場合は、名前のみが書き込まれます)。組み合わせは 1 行に 1 個ずつ出力されます。読みやすいように、各行の最初の 3 つの値は一列に並ぶようになっています。
すべての組み合わせが出力された後、ログ・ファイルを終了し、先ほど保存した $IO の値がリストアされ、コントロールは呼び出し元に返されます。
^%ZSTART
このルーチンは、実際に Caché に呼び出されるエントリ・ポイントを含みます。上記の ^%ZSSUtil の機能も使用します。すべてのエントリ・ポイントはほとんど同じように動作し、情報をログに配置します。SYSTEM エントリ・ポイントは、他に比べるとやや複雑で、オペレータ・コンソール・ログにも情報を配置します。
%ZSTART ; User startup routine.
#Define ME "ZSTART"
#Define BgnSet "Start"
#Define Empty ""
; cannot be invoked directly
quit
SYSTEM ;
; Cache starting
new EntryPoint, Items
set EntryPoint = "SYSTEM"
; record the fact we got started in the console log
do WriteConsole^%ZSSUtil((EntryPoint
_ "^%"
_ $$$ME
_ " called @ "
_ $ZDATETIME($HOROLOG, 3)))
; log the data accumulate results
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3),
"Job", $JOB,
"Computer", $ZUTIL(110),
"Version", $ZVERSION,
"StdIO", $PRINCIPAL,
"Namespace", $ZUTIL(5),
"CurDirPath", $ZUTIL(12),
"CurNSPath", $ZUTIL(12, ""),
"CurDevName", $ZUTIL(67, 7, $JOB),
"JobType", $ZUTIL(67, 10, $JOB),
"JobStatus", $ZHEX($ZJOB),
"StackFrames", $STACK,
"AvailStorage", $STORAGE,
"UserName", $ZUTIL(67, 11, $JOB))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
LOGIN ;
; a user logs into Cache
new EntryPoint, Items
set EntryPoint = "LOGIN"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
JOB ;
; JOB'd process begins
new EntryPoint, Items
set EntryPoint = "JOB"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
CALLIN ;
; a process enters via CALLIN interface
new EntryPoint, Items
set EntryPoint = "CALLIN"
set Items = $LISTBUILD($$$BgnSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil($$$ME, EntryPoint, Items)
quit
各ラベルの説明を以下に示します。
このルーチンはまず QUIT コマンドを実行します。エントリ・ポイントの 1 つから実行を開始するのではなく、ルーチンとして呼び出す方が良好な結果を得ることができるためです。
このルーチンも、そのルーチン自体の名前、開始文字列および空文字列に対し、指定された定数 (マクロとして) を定義します。
このサブルーチンは、呼び出しルーチン名、エントリ・ポイント、呼び出された日付時刻で構成された文字列を作成します。その後、WriteConsole^%ZSSUtil を呼び出してオペレータのコンソール・ログに配置します。
その後、表示する名前と値の組み合わせのリストを作成します。これを WriteLog^%ZSSUtil に渡し、ローカル・ログ・ファイルに配置します。その後、呼び出し元に戻ります。
これらのサブルーチンは、オペレータのコンソール・ログに情報を置きません。その代わり、呼び出す際に識別するための簡単な項目のリストを作成し、WriteLog^%ZSSUtil を使用して記録します。
^%ZSTOP
このルーチンは、実際に Caché に呼び出されるエントリ・ポイントを含み、^%ZSSUtil のサブルーチンを使用します。この例は、^%ZSTART の例と似ています。詳細は、前のセクションを参照してください。
%ZSTOP ; User shutdown routine.
#Define ME "ZSTOP"
#Define EndSet "End"
#Define Empty ""
; cannot be invoked directly
quit
SYSTEM ; Cache stopping
new EntryPoint
set EntryPoint = "SYSTEM"
; record termination in the console log
do WriteConsole^%ZSSUtil((EntryPoint
_ "^%"
_ $$$ME
_ " called @ "
_ $ZDATETIME($HOROLOG, 3)))
; write the standard log information
do Logit(EntryPoint, $$$ME)
quit
LOGIN ; a user logs out of Cache
new EntryPoint
set EntryPoint = "LOGIN"
do Logit(EntryPoint, $$$ME)
quit
JOB ; JOB'd process exits.
new EntryPoint
set EntryPoint = "JOB"
do Logit(EntryPoint, $$$ME)
quit
CALLIN ; process exits via CALLIN interface.
new EntryPoint
set EntryPoint = "CALLIN"
do Logit(EntryPoint, $$$ME)
quit
Logit(entrypoint, caller) PRIVATE ;
; common logging for exits
new items
set items = $LISTBUILD($$$EndSet, $ZDATETIME($HOROLOG, 3))
do WriteLog^%ZSSUtil(caller, entrypoint, items)
quit
^%ZLANG ルーチンによる言語の拡張
(正確ではありませんが) 慣習的に、ルーチンは名前の一部にキャレットがあるかのように参照されます。このドキュメントではこの慣習に従いますが、ルーチンの実際の名前を直接参照する場合は例外とします。
^%ZLANG 機能を使用すると、ObjectScript などの言語に、カスタムのコマンド、関数、および特殊変数を追加することができます。拡張機能は標準機能と同じ方法で呼び出され、構文、演算子の優先順位などに関する同一の規則に従います。通常、^%ZLANG 機能の実行速度は標準の Caché 機能ほど速くありません。パフォーマンスが重要なルーチンをコーディングする際には、この点を考慮してください。
このような拡張機能を追加する手順は以下のとおりです。
-
必要に応じて、以下の名前でルーチンを定義します。
FeedbackOpens in a new tab%ZLANGCnn %ZLANGFnn %ZLANGVnn
%ZLANGCnn という名前のルーチンではコマンドを定義します。%ZLANGFnn という名前のルーチンでは関数を定義します。%ZLANGVnn という名前のルーチンでは特殊変数を定義します。ルーチン名の nn 部分は、これらの項目が利用できる言語を示します。nn は、以下のいずれかになります。
-
00 — ObjectScript
-
01 — DSM-11
-
02 — DTM
-
06 — DSM-J
-
07 — DTM-J
-
08 — MSM
-
09 — Caché Basic
-
11 — MV Basic
これらのルーチンでは、パブリックのサブルーチンを以下のように定義します。
-
サブルーチンのラベルでは、定義するコマンド、関数、または特殊変数の名前を使用します。
名前は英文字の Z で始まる必要があり、英字のみを含めることができます。ロケールでアルファベットとして定義すれば、Unicode 文字が使用できます。英文字はすべて大文字にする必要がありますが、実行の際は大文字と小文字の区別はありません。名前の最大長は 31 文字です。
名前は既存のコマンド、関数、または特殊変数と同じにすることはできません (同一の場合、Caché では無視されます)。SQL 予約語は使用しないことを強くお勧めします。
関数を定義する場合、関数に引数がない場合でも、ラベルには括弧を含める必要があります。
特殊変数のラベルには括弧を含めることができます。
-
オプションで別のラベルを含めて、略称を定義します。その略称が Caché で使用されていないように注意してください。
-
サブルーチンを適切に定義します。詳細は、“メモ” セクションを参照してください。
親ルーチンにある最初のコマンドでは QUIT を使用することをお勧めします。こうしておけば、ユーザがルーチンを直接呼び出しても、何も起こりません。
また、ルーチンでコメントを上部に記載して、ルーチン自体の名前を示すようにすると便利です。
Caution:他のサブルーチンの場合、パブリックのサブルーチンで呼び出されるものを含めて、それらのラベルは必ず、小文字のみか大文字と小文字を混在して記述します (先頭は Z にしない)。または、プライベートのサブルーチンとして実装します。
つまり、^%ZLANG ルーチンによって言語が拡張されるので、目的のサブルーチンのみが外部で利用できるようにすることが重要です。
メモ
コマンドは、ルーチンやプロシージャの DO のように処理されます。引数は、呼び出しのパラメータとして渡されます。
コードの結果にする場合を除いて、$TEST や $ZREFERENCE など、システム状態の値をコードで保持する必要があります (ただし、関数や特殊変数の場合、システムでは $TEST が自動的に保持されます)。
特殊変数の値を設定できます。変数のエントリ・ポイントは 1 つのみです。値を設定するか値を取得するかを判断するには、引数が指定されているかどうかをコードで調べる必要があります。以下はその例です。
ZVAR(NewValue) public { if $DATA(NewValue) Set ^||MyVar=NewValue Quit Quit $GET(^||MyVar) }
そうすると、以下に示すように、ユーザはこの変数を設定するか、または変数を取得できます。
SAMPLES>w $ZVAR SAMPLES>s $ZVAR="xyz" SAMPLES>w $ZVAR xyz
コマンドまたは関数からエラー・コードを返すには、$SYSTEM.Process.ThrowError() を使用します。
例
例えば、カスタムの特殊変数を定義して ObjectScript で使用するには、以下のようにして、^%ZLANGV00 ルーチンを定義します。
; implementation of ^%ZLANGV00 ; custom special variables for ObjectScript QUIT ZVERNUM ; tag becomes name of a special variable ZVE QUIT $PIECE($ZVERSION,"(Build")
次に、デモンストレーションとして、新規の変数はターミナルで以下のように使用できます。
SAMPLES>w $zvernum Cache for Windows (x86-32) 2011.1 SAMPLES>w $zve Cache for Windows (x86-32) 2011.1
別の例として、以下のように ^%ZLANGF00 ルーチンを定義するとします。
; implementation of ^%ZLANGF00 ; custom functions for ObjectScript QUIT ZCUBE(input) public { Quit input*input*input }
次に、デモンストレーションとして、新規の関数はターミナルで以下のように使用できます。
SAMPLES>w $zcube(2) 8
以下の例では ^%ZLANGC00 を示します。この例では、システム・ステータス・ユーティリティ ^%SS を実行するコマンドが作成されます。
; %ZLANGC00 ; custom commands for ObjectScript QUIT ZSS ; tag name of a command to check system status DO ^%SS QUIT
-