参照先: 説明
このライブラリは、日立ソリューションズの製造・販売するJP1/AJS2の定義情報をパースし、
JavaオブジェクトとしてアクセスするためのAPIを提供するものである。
APIを構成するモジュールやそれらのモジュールが提供する機能を使用するサンプルコードは
src/test/java
ディレクトリ配下のorg.unclazz.jp1ajs2.sample
パッケージに含まれている。
まずリリース一覧
からjarファイルを取得してプロジェクトのビルドパスに含める。
もしあなたのプロジェクトがMavenを使用しているのであれば、アーティファクトはGithub上の
Mavenリポジトリから取得できる。
そのための設定はpom.xml
に以下のコード断片を追加するだけである:
<repositories> ... <repository> <id>unclazz-mvn-repo</id> <url>https://raw.github.com/unclazz/mvn-repo/master/</url> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </snapshots> </repository> </repositories> <dependencies> ... <dependency> <groupId>org.unclazz.jp1ajs2</groupId> <artifactId>unclazz-jp1ajs2-unitdef</artifactId> <version>X.X.X-RELEASE</version> </dependency> <dependencies>
そしてビジネスロジックをコーディングする:
import static org.unclazz.jp1ajs2.unitdef.query.Queries.*; final String sampleDef = "" + "unit=XXXX0000,,,;\r\n" + "{\r\n" + " ty=n;\r\n" + " el=XXXX0001,g,+80 +48;\r\n" + " el=XXXX0002,g,+240 +144;\r\n" + " ar=(f=XXXX0001,t=XXXX0002);\r\n" + " cm=\"これはコメントです。\";\r\n" + " fd=30;\r\n" + " unit=XXXX0001,,,;\r\n" + " {\r\n" + " ty=pj;\r\n" + " sc=\"hello.exe\";\r\n" + " }\r\n" + " unit=XXXX0002,,,;\r\n" + " {\r\n" + " ty=pj;\r\n" + " sc=\"bonjour.exe\";\r\n" + " }\r\n" + "}\r\n"; // ユニット定義を文字列から読み取る // Units.from...系メソッドは文字列・ストリーム・ファイルに対応している final Unit u0 = Units.fromCharSequence(sampleDef).get(0); // Unitオブジェクトはユニット定義情報にアクセスするローレベルのAPIを提供する u0.getName(); // => "XXXX0000" u0.getType(); // => "JOBNET" u0.getSubUnits().size(); // => 2 // Query<T>とそのユーティリティを使ってユニットから各種の情報を取得できる // arパラメータ(下位ユニット間の実行順序関係を示す)を遅延評価Iterableのかたちで取得 final Iterable<AnteroposteriorRelationship> ars = u0.query(ar()); // elパラメータ(下位ユニットの種別とマップ上の配置を示す)を正格評価Listのかたちで取得 final Iterable<Element> els = u0.query(el().list()); // fdパラメータ(実行所要時間を示す)を1つ取得 final FixedDuration fd0 = u0.query(fd().one()); // scパラメータ(PCジョブの実行ファイル名を示す)が"hello"で始まるユニットを遅延評価Iterableのかたちで取得 final Iterable<Unit> us = u0.query(descendants() .hasParameter("sc").startsWith("hello"));
Unit
はJP1/AJS2のユニット定義をあらわすインターフェースである。
クライアント・コードはこのインターフェースを通じてユニット定義に含まれる情報 ──ユニット名やその他のユニット属性パラメータや、
sz・cm・fdといったユニット定義パラメータ、 そして下位ユニットの情報──にアクセスすることができる。
final Unit u = ...; u.getName(); // => ユニット名 u.getFullQualifiedName(); // => ユニット完全名 u.getAttributes(); // => ユニット属性パラメータ u.getSubUnits(); // => 下位ユニットのリスト
Units
は
Unit
オブジェクトのファクトリ/ユーティリティ・クラスである。
Units.fromStream(InputStream)とその同系統のメソッドは、各種の入力ソースから ユニット定義情報を読み取って
Unit
オブジェクトを返す。
final List<Unit> us = Units.fromCharSequence("unit=FOO,,,;{" + "ty=g;cm=\"This is a jobnet-group unit named #\"FOO#\".\";}"); final Unit u = us.get(0); u.getName(); // => "FOO"
一方serialize()
や
Units#writeToStream(Unit,OutputStream)
メソッドは
その名前の通りUnit
オブジェクトの文字列化を行う。
JP1/AJS2のユニット定義は、ユニットをノードとする木構造と そのノードに紐づく1つ以上のユニット定義パラメータ (名前の重複が許容されるものとそうでないものがある)、 そしてそのパラメータに紐づく1つ以上の値という部品を中心として成り立っている。
このうちユニット、つまりUnit
についてはすでに紹介した。
そのgetParameters()
メソッドを呼び出すと
ユニット定義パラメータをあらわすParameter
インターフェースのリストが得られる。
そしてgetValues()
メソッドを呼び出すと
パラメータ値をあらわすParameterValue
インターフェースが得られる。
ParameterValue
には3つの値のかたち
──文字シーケンス、 二重引用符で囲われた文字シーケンス、そしてタプル──のいずれかをとる。
ParameterValue
が
提供するメソッドを通じてこれらのデータにアクセスすることができる。
Unit
・Parameter
・ParameterValue
という3つのインターフェースを通じた
木構造のトラバースは もっとも基本的でローレベルの操作となる。
これに対してQuery
インターフェースを中核とした より生産性の高いAPIが用意されている。
例えばユニット定義パラメータszの情報を読み取るには次のようにする:
import static org.unclazz.jp1ajs2.unitdef.Queries.*; ... final Unit u = ...; final Iterable<MapSize> szs = u.query(sz()); // sz() = Queries.sz()
何かしらの条件を満たすサブユニット(子ユニット)や子孫ユニットを取得するには次のようにする:
import static org.unclazz.jp1ajs2.unitdef.Queries.*; ... final Unit u = ...; // サブユニット(子ユニット)を問合せる final Iterable<Unit> cs0 = u.query(children()); // サブユニットを持つサブユニットを問合せる final Iterable<Unit> cs1 = u.query(children.hasChildren()); // サブユニットの完全名を問合せる final Iterable<FullQualifiedName> cs2 = u.query(children().theirFqn()); // 子孫ユニットを問合せる final Iterable<Unit> ds0 = u.query(descendants()); // ユニット種別がPCジョブである子孫ユニットを問合せる final Iterable<Unit> ds1 = u.query(descendants().typeIs(UnitType.PC_JOB)); // ユニット種別がPCジョブでユニット名が"..."で始まる子孫ユニットを問合せる final Iterable<Unit> ds2 = u.query(descendants().typeIs(UnitType.PC_JOB).nameStartsWith("..."));
定義済みのクエリはQueries
というユーティリティ・クラスを通じて得られる。
サンプルコードにもあるように、定義済みクエリを利用する場合はこれらのクラスの静的メンバを適宜インポートしておこう。
ユーティリティは以下のような定義済みクエリを提供している(このうちいくつかはサンプルコードにも登場している):
children()
descendants()
itSelfAndDescendants()
parameters()
parameter()
ar()
、
fd()
、
sz()
など定義済みクエリの実装はイミュータブルでありステートレスであるので、複雑な条件を設定したインスタンスの参照を保持しておくことで、 複数の異なるユニットに対して繰り返し問合せを行うことができる。
Query<Unit,Iterable<Unit>> q0 = children().hasChildren(); Query<Unit,Iterable<Unit>> q1 = q0.typeIs(UnitType.PC_JOB); Query<Unit,Iterable<Unit>> q2 = q1.hasParameter("cm").anyValue(); Query<Unit,Iterable<Unit>> q3 = q2.hasParameter("sc").endsWith(".exe"); Iterable<Unit> us0 = u.query(q1); // cmパラメータを持つこと という条件は付かない Iterable<Unit> us1 = u.query(q2); // scパラメータ値が".exe"で終わることという条件は付かない
定義済みクエリのうち問合せ結果としてIterable
を返すものは処理方式として遅延評価を採用している。
つまりこれらのクエリのqueryFrom(Object)
メソッドから返えされるIterable
はそれが生成された時点ではまだ問合せ結果を内包していない。
問合せ処理の起動は可能な限り遅らせられるので、仮に要素を1つ取得するだけでIterable
インスタンスを破棄したとしても、
そのために消費されるCPUとメモリのコストは当該の1ユニットを問合せるのに必要な分だけである。
なおこのように問合せ結果のうち最初の1つだけを取得したい場合は、
one()
メソッドもしくはそのオーバーロードを呼び出して
OneQuery
のインスタンスを得ると便利である。
また問合せ結果として遅延評価Iterable
の代わりに正格評価List
を取得したい場合は、
list()
メソッドを呼び出してクエリListQuery
のインスタンスを得るとよい。
Unit u2 = u.query(children().one()); List<Unit> ul = u.query(children().list());
JP1/AJS2のユニット定義ではスケジュールルールに関する設定のパラメータのほか多くの箇所でデフォルト値の省略が行われている。
このため定義情報のファイルサイズは節約されるかもしれないが、その読み取りロジックはしばしば煩雑なものとなってしまう。
このような状況をさけ、ロジックのコーディングを可能な限りアプリケーションの主な関心のためのものとするため、
ParameterConditionalModifier
インターフェースを利用することができる。
このインターフェースを実装したクエリはchildren().theirParameters()
や
descendants().theirParameters()
、parameters()
そしてparameter()
などから得られる。
いずれもQueries
ユーティリティ・クラスが直接・間接に提供しているものだ。
これらのクエリは例えば以下のようにして使う:
final Unit u = ...; // ユニット定義パラメータsdを問合せる // ただし各パラメータについてパラメータ値の数が1つであれば先頭に省略された"1"を補う final Iterable<Parameter> sds = u.query(parameters("sd") .whenValueCount(1).thenPrepend("1")); // ユニット定義パラメータctfdを問合せる // ただし各パラメータについてその1番目の値が"no"・"be"・"af"のいずれかなら先頭に省略された"1"を補う final Iterable<Parameter> ctfds = u.query(parameters("ctfd") .whenValueAt(0).equalsAnyOf("no", "be", "af") .thenPrepend("1"));
JP1/AJS2製造・販売元に対する本プロジェクト開発者の立場は単なる「ユーザー」である。 したがって、本プロジェクトで開発・配布するコードは製造・販売元とは一切関わりがない。
本プロジェクトで開発・配布するコードは、製造・販売元で公開している 定義情報リファレンスに基づき、 これを参考にして、定義ファイルのパースや各種パラメータのアクセサのロジックを実装しているものの、 同リファレンスの誤読もしくはリファレンスとJP1/AJS2間の実装齟齬などにより、実際の動作に差異が存在する可能性がある。
Copyright © 2016. All rights reserved.