com.nexacro.java.xapi.data

X-APIのデータ構造を定義します。

クライアントとサーバー間で送受信するデータは単一データも存在すれば、DBのTableと類似している2次元データも存在ます。このようなデータを送受信したり操作するためのデータ構造を定義します。主なクラスはPlatformData、DataSet、Variableなどがあります。

はじめに

次はX-APIのデータを参照する簡単な例題です。

$r_title(データ参照)
PlatformData department = ...;

// VariableList参照
VariableList varList = department.getVariableList();

// VariableListから値を参照
String name = varList.getString("name");
String location = varList.getString("location");
int number = varList.getInt("number");

// ...

// DataSet参照
DataSet employees = department.getDataSet("employees");

// DataSetの行(row)の数分繰り返す
for (int i = 0; i < employees.getRowCount(); i++) {
  // DataSetのデータ参照
  int id = employees.getInt(i, "id");
  String firstName = employees.getString(i, "firstName");
  String lastName = employees.getString(i, "lastName");
  boolean manager = employees.getBoolean(i, "manager");

  // ...
}

次はX-APIのデータを生成する簡単な例題です。

$r_title(データ生成)
// PlatformData生成
PlatformData department = new PlatformData();

// VariableList参照
VariableList varList = department.getVariableList();

// VariableListに値を追加
varList.add("name", "R&D Center");
varList.add("location", "5chome, Soto-Kanda, Chiyoda-ku, Tokyo");
varList.add("number", 99);

// DataSet生成
DataSet employees = new DataSet("employees");

// 列(column)追加
employees.addColumn("id", DataTypes.INT);
employees.addColumn("firstName", DataTypes.STRING, 16);
employees.addColumn("lastName", DataTypes.STRING, 8);
employees.addColumn("manager", DataTypes.BOOLEAN);

// 行(row)追加
int row = employees.newRow();

// 追加された行(row)のデータ設定
employees.set(row, "id", 0);
employees.set(row, "firstName", "John");
employees.set(row, "lastName", "Jones");
employees.set(row, "manager", false);

// 行(row)追加
row = employees.newRow();

// 追加された行(row)のデータ設定
employees.set(row, "id", 1);
employees.set(row, "firstName", "Tom");
employees.set(row, "lastName", "Glover");
employees.set(row, "manager", true);

// DataSetをPlatformDataに追加
department.addDataSet(employees);

データ構造

データには大きく単一データと2次元データがあります。単一データはデータを区分できる識別子(name)と値(value)を持っていて、VariableListに保存されます。2次元データを保存するDataSetは列(column)と行(row)で構成されていて、DataSetListを通じて保存および参照されます。

VariableListとDataSetListを持っているPlatformDataはデータ構造の最上位であり、データ移動とデータ送受信の基本単位として使用されます。

データ型

X-APIでサポートするデータ型(type)はDataTypesに定義されています。

データ型はnexacroとX-API間で若干差があり、X-APIの方がより細分化されています。しかし、基本的にnexacroとX-API間の通信中のデータ損失は発生しません。

NRE

Javascript

X-API

Java

説明

STRING

String

DataTypes.STRING

String

文字列

INT

Int

DataTypes.INT

int

4 byteの整数

INT

Int

DataTypes.BOOLEAN

boolean

trueまたはfalse

(1または0)

BIGDECIMAL

BigDecimal

DataTypes.LONG

long

8 byteの整数

FLOAT

BigDecimal

DataTypes.FLOAT

float

4 byteの実数

FLOAT

BigDecimal

DataTypes.DOUBLE

double

8 byteの実数

BIGDECIMAL

BigDecimal

DataTypes.BIG_DECIMAL

java.math.BigDecimal

-

DATE

Date

DataTypes.DATE

java.util.Date

日付(yyyyMMdd)

TIME

Date

DataTypes.TIME

java.util.Date

時刻(HHmmssSSS)

DATETIME

Date

DataTypes.DATE_TIME

java.util.Date

日付と時刻

(yyyyMMddHHmmssSSS)

BLOB

非サポート

DataTypes.BLOB

byte[]

byte配列

Variableの単一データの操作

Variableはデータを保存する変数を意味し、識別子(name)と値(value)で構成されます。値(value)データ型(type)によって変換された後保存されます。

Variableの生成およびデータ設定は下記の3つの方法をサポートします。

VariableListのVariable追加は、Variableの生成後にadd(var)メソッドにより追加します。また、Variableは生成せずにadd(name, value)メソッドにより直接値を追加することができます。

$r_title(単一データ(Variable)追加)
// PlatformData生成
PlatformData department = new PlatformData();

// VariableList参照
VariableList varList = department.getVariableList();

// VariableListに値を直接追加
varList.add("name", "R&D Center");

// Variableを生成した後にVariableListに値を追加
Variable location = new Variable("location");
location.set("5chome, Soto-Kanda, Chiyoda-ku, Tokyo");
varList.add(location);

// VariableListにint型の値を直接追加
varList.add("number", 99);

Variableに保存されたデータに対して、getObject()やgetString()などの各データ型のメソッドにより値を参照することができます。

注意点としては、元のデータ型と異なるデータ型を返すメソッドではデータの不一致が発生することがあります。

VariableListに保存されたVariableはget(name)メソッドにより参照し、getObject(name)や getString(name)などのメソッドにより直接値を参照することもできます。

$r_title(単一データ(Variable)参照)
PlatformData department = ...;

// VariableList参照
VariableList varList = department.getVariableList();

// VariableListから値を直接参照
String name = varList.getString("name");

// Variableによる値の参照
Variable locationVar = department.getVariable("location");
String location = locationVar.getString();

// VariableListからint型の値を直接参照
int number = varList.getInt("number");

DataSetの2次元データの参照

DataSetは列(column)と行(row)で構成されていて、2次元データを格納します。構造はDBの Tableと類似していて、列(column)に対する情報はColumnHeaderによる保存され、データは内部クラスであるDataRowにより行(row)単位で保存されます。

DataSetに格納されているデータは行(row)の位置(index)と列(column)の名前(name)または位置(index)により参照し、Variableと同じ方式でgetObject(rowIndex, columnIndex)や getString(rowIndex, columnIndex)などの各データ型のメソッドにより値を参照することができます。

同じく注意点としては、元のデータ型と異なるデータ型を返すメソッドではデータの不一致が発生することがあります。

$r_title(識別子(name)によるDataSetのデータ参照)
PlatformData department = ...;

// DataSetを識別子(name)により参照
DataSet employees = department.getDataSet("employees");

// DataSetの行(row)の数分繰り返す
for (int i = 0; i < employees.getRowCount(); i++) {
	// DataSetのデータを識別子(name)により参照
	Object name = employees.getObject(i, "name");
	String jobTitle = employees.getString(i, "jobTitle");
	int number = employees.getInt(i, "number");
	boolean manager = employees.getBoolean(i, "manager");
	// ...
}
$r_title(位置(index)によるDataSetのデータ参照)
PlatformData department = ...;

// DataSetを位置(index)により参照
DataSet employees = department.getDataSet(0);

// DataSetの行(row)数分繰り返す
for (int i = 0; i < employees.getRowCount(); i++) {
  // DataSetのデータを列(column)の位置(index)により参照
  Object name = employees.getObject(i, 0);
  String jobTitle = employees.getString(i, 1);
  int number = employees.getInt(i, 2);
  boolean manager = employees.getBoolean(i, 3);

  // ...
}

DataSetの生成

DataSetの生成は下記のような手順で行います。

  1. DataSetオブジェクト生成

  2. 列(column)追加

  3. 行(row)追加

  4. データ設定

$r_title(DataSetオブジェクト生成およびデータ追加)
PlatformData department = new PlatformData();

// DataSetオブジェクト生成
DataSet employees = new DataSet("employees");

// 列(column)追加
employees.addColumn("name", DataTypes.STRING, 8);
employees.addColumn("jobTitle", DataTypes.STRING, 16);
employees.addColumn("number", DataTypes.INT);
employees.addColumn("manager", DataTypes.BOOLEAN);

// 行(row)追加
int row = employees.newRow();

// データ設定
employees.set(row, "name", "John Jones");
employees.set(row, "jobTitle", "developer");
employees.set(row, "number", 1234);
employees.set(row, "manager", false);

// 行(row)追加
row = employees.newRow();

// データ設定
employees.set(row, "name", "Tom Glover");
employees.set(row, "jobTitle", "manager");
employees.set(row, "number", 9876);
employees.set(row, "manager", true);

// DataSetオブジェクトをPlatformDataに追加
department.addDataSet(employees);

// ...

ColumnHeaderのプロパティ参照

DataSetの列(column)に関する情報はColumnHeaderにより保存され、列(column)の情報は下記の通りです。

属性

変数名

データ型

有効な値

識別子

name

String

nullと""を除外したDataSet内でユニークな文字列

列(column)のタイプ

type

int

通常の列(TYPE_NORMAL)と定数値を持つ列(TYPE_CONSTANT)

データ型

dataType

int

DataTypesに定義されている定数を参照

データサイズ

dataSize

int

定数値

value

Object

ConstantColumnHeader内でのみ有効

$r_title(ColumnHeaderのプロパティ参照)
PlatformData department = ...;

// DataSet参照
DataSet employees = department.getDataSet("employees");

// DataSetの列(column)数分繰り返す
for (int i = 0; i < employees.getColumnCount(); i++) {
  // DataSetからColumnHeader参照
  ColumnHeader columnHeader = employees.getColumn(i);

  // 列(column)の属性参照
  String name = columnHeader.getName();
  int type = columnHeader.getType();
  int dataType = columnHeader.getDataType();
  int dataSize = columnHeader.getDataSize();
  boolean isConstant = columnHeader.isConstant();

  // 定数値を持つColumnHeaderの場合
  Object value = null;
  if (isConstant) {
    value = ((ConstantColumnHeader) columnHeader).getValue();
  }

  // ...
}

ColumnHeaderによるDataSetの列(column)の追加

DataSetに列(column)を追加する方法は、addColumn(name, dataType, dataSize)メソッドによる方法と直接ColumnHeaderを生成して追加する方法があります。

$r_title(ColumnHeaderによるDataSetの列(column)追加)
// DataSetオブジェクト生成
DataSet employees = new DataSet("employees");

// 列(column)追加
employees.addColumn(new ColumnHeader("name", DataTypes.STRING, 8));
employees.addColumn(new ColumnHeader("jobTitle", DataTypes.STRING, 16));
employees.addColumn(new ColumnHeader("number", DataTypes.INT));
employees.addColumn(new ColumnHeader("manager", DataTypes.BOOLEAN));

// 行(row)追加
int row = employees.newRow();

// 追加した行(row)のデータ設定
employees.set(row, "name", "John Jones");
employees.set(row, "jobTitle", "developer");
employees.set(row, "number", 1234);
employees.set(row, "manager", false);

// 行(row)追加
row = employees.newRow();

// 追加した行(row)のデータ設定
employees.set(row, "name", "Tom Glover");
employees.set(row, "jobTitle", "manager");
employees.set(row, "number", 9876);
employees.set(row, "manager", true);

ColumnHeaderによるDataSetのデータ参照

たまにDataSetの列(column)の定義情報を参照したい場合があります。例えば、各列(column)の情報を知らなかったり、DataSetのデータを共通的に処理する必要がある場合などがあります。

DataSetのgetColumn(index)メソッドを呼び出して、列(column)の数分ColumnHeaderを参照し、ColumnHeaderから識別子(name)、データ型(dataType)、データサイズ(dataSize)などを参照することができます。

$r_title(ColumnHeaderによるDataSetのデータ参照)
PlatformData department = ...;

// DataSetを識別子(id)により参照
DataSet employees = department.getDataSet("employees");

// DataSetの行(row)の数分繰り返す
for (int i = 0; i < employees.getRowCount(); i++) {
  // DataSetの列(column)の数分繰り返す
  for (int j = 0; j < employees.getColumnCount(); j++) {
    // DataSetからColumnHeader参照
    ColumnHeader columnHeader = employees.getColumn(j);
    // 列(column)の識別子(name)参照
    String name = columnHeader.getName();

    // データ型(dataType)による区分
    switch (columnHeader.getDataType()) {
    case DataTypes.STRING:
      String str = employees.getString(i, name);

      // ...

      break;
    case DataTypes.INT:
      int n = employees.getInt(i, name);

      // ...

      break;
    case DataTypes.BOOLEAN:
      boolean bool = employees.getBoolean(i, name);

      // ...

      break;
    default:
      Object obj = employees.getObject(i, name);

      // ...

      break;
    }
  }
}

ConstantColumnHeaderが持つ列(column)の低数値

ConstantColumnHeaderは定数値を持つ列(column)を意味します。

DataSetのaddConstantColumn(name, value)メソッドを呼び出して列(column)を追加したり、ConstantColumnHeaderを生成して追加すると、該当の列(column)の値は行(row)の位置(index)と関係なく定数値を持ちます。

$r_title(DataSetオブジェクトに定数値を持つ列(column)追加)
// DataSetオブジェクト生成
DataSet employees = new DataSet("employees");

// 通常の列(column)追加
employees.addColumn("name", DataTypes.STRING, 8);
employees.addColumn("jobTitle", DataTypes.STRING, 16);
// 定数値を持つ列(column)追加
employees.addConstantColumn("city", "Tokyo");
employees.addColumn(new ConstantColumnHeader("company", "Nexaweb"));

DataSetの元データと変更後のデータ

DataSetでは、データが追加・変更・削除された場合に更新状態と変更前の元データを保存します。データ更新の場合には元データを別途保存し、現在のデータを更新します。データ削除の場合には現在のデータからは削除されますが、別途の削除データとして保存されます。変更された状態は行(row)単位で保存され、DataSetのgetRowType(index)メソッドにより現在の状態を確認することができます。

定数値

説明

DataSet.ROW_TYPE_NORMAL

通常の行(row)

DataSet.ROW_TYPE_INSERTED

追加された行(row)

DataSet.ROW_TYPE_UPDATED

変更された行(row)、元データあり

DataSet.ROW_TYPE_DELETED

削除された行(row)、他のデータとは別途保存される

保存有無はDataSetのstartStoreDataChanges()メソッドにより有効になり、stopStoreDataChanges()メソッドにより保存が中止され、startStoreDataChanges()を呼び出す時点のデータを基準データとして設定されます。

startStoreDataChanges()メソッドを呼び出すと、以前保存された元のデータや削除済データは削除されるため、データの維持が必要な場合にはstartStoreDataChanges(true)を呼び出す必要があります。逆に、stopStoreDataChanges()が呼び出されると、以前保存していた元のデータや削除済データが保存されるため、保存しない場合には stopStoreDataChanges(false)を呼び出してください。

また、各状態によるデータは下記のメソッドにより参照することができます。

DataSetのデフォルト設定としては、変更状態とデータの保存が有効になっています。つまり、 DataSetの生成と同時にstartStoreDataChanges()が自動的に呼び出されるため、ご注意ください。

ユーザがstartStoreDataChanges()を別途呼び出さない限り、DataSetに保存されるすべてのデータの更新状態は「ROW_TYPE_INSERTED」になります。例えば、DataSetを生成した後にデータを追加し、追加したデータを再度変更しても更新状態は「ROW_TYPE_INSERTED」です。その理由は、基準となるデータはDataSetの生成直後のデータになるため、その時点でデータが存在しない状態を基準として、データは追加された状態になります。それと同様に、データを追加した後に削除しても、更新状態は「ROW_TYPE_DELETED」ではなく、データが存在しなかった状態を基準として、何も変化がないことになります。

したがって、DataSetの追加・変更・削除済状態とデータが必要な場合に、適切な時点でstartStoreDataChanges()を呼び出す必要があります。DataSetを生成せずに通信で受け取り、その時点のデータを基準にする場合には該当のメソッドを呼び出す必要はありません。

$r_title(追加・変更・削除済のDataSetの行(row))
PlatformData department = ...;

// 識別子(id)によりDatasetを参照
DataSet employees = department.getDataSet("employees");

// 変更情報保存の開始
employees.startStoreDataChanges();

// DataSetのデータ追加・変更・削除
...

// 変更情報保存の中止
employees.stopStoreDataChanges();

// DataSetの行(row)数分繰り返す
for (int i = 0; i < employees.getRowCount(); i++) {
  // 行(row)の状態参照
  int rowType = employees.getRowType(i);

  if (rowType == DataSet.ROW_TYPE_NORMAL) {
    // 通常の行(row)の場合
    Object name = employees.getObject(i, "name");

    // ...
  } else if (rowType == DataSet.ROW_TYPE_INSERTED) {
    // 追加された行(row)の場合
    Object name = employees.getObject(i, "name");

    // ...
  } else if (rowType == DataSet.ROW_TYPE_UPDATED) {
    // 変更された行(row)の場合
    Object name = employees.getObject(i, "name");
    Object savedName = employees.getSavedData(i, "name");

    // ...
  } else {
    // 発生しない
  }
}

for (int i = 0; i < employees.getRemovedRowCount(); i++) {
  // 削除済の行(row)の場合
  Object removedName = employees.getRemovedData(i, "name");

  // ...
}

DataSetのイベント

DataSetでは構造やデータが変更された場合にDataSetEventが発生します。DataSetの変更に応じて任意の処理が必要な場合にはDataSetListenerを実装してDataSetに登録する必要があります。

イベントが呼び出される時点は下記の4つになります。

呼び出すメソッド

説明

DataSetListener.structureChanged

列(column)の追加など、構造が変更された場合

DataSetListener.rowInserted

行(row)が追加された場合

DataSetListener.dataUpdated

データが変更された場合

DataSetListener.rowRemoved

行(row)が削除された場合

$r_title(DataSetにDataSetListener登録)
DataSet employees = new DataSet("employees");

// DataSetにDataSetListener登録
DataSetListener listener = new DataSetEventHandler();
employees.addDataSetListener(listener);

// 列(column)追加、DataSetListenerのstructureChangedが呼び出される
employees.addColumn("name", DataTypes.STRING, 8);

// 行(row)追加、DataSetListenerのrowInsertedが呼び出される
int row = employees.newRow();

// データ設定、DataSetListenerのdataUpdatedが呼び出される
employees.set(row, "name", "John Jones");

// ...
$r_title(DataSetListenerの実装)
class DataSetEventHandler implements DataSetListener {

  public void structureChanged(DataSetEvent e) {
    // 列(column)の削除など、構造が変更された場合
    DataSet ds = (DataSet) e.getSource();

    // ...
  }

  public void dataUpdated(DataSetEvent e) {
    // データが変更された場合
    DataSet ds = (DataSet) e.getSource();
    int firstRow = e.getFirstRow();
    int lastRow = e.getLastRow();
    int column = e.getColumn();

    // ...
  }

  public void rowInserted(DataSetEvent e) {
    // 行(row)が追加された場合
    DataSet ds = (DataSet) e.getSource();
    int firstRow = e.getFirstRow();
    int lastRow = e.getLastRow();

    // ...
  }

  public void rowRemoved(DataSetEvent e) {
    // 行(row)が削除された場合
    DataSet ds = (DataSet) e.getSource();
    int firstRow = e.getFirstRow();
    int lastRow = e.getLastRow();

    // ...
  }
}

DataSetのオプション

DataSetには下記のようなオプションがあり、必要に応じて値を変更します。

オプション項目

デフォルト値

説明

isStoreDataChanges

true

データ更新情報の保存有無。詳細はDataSetの元データと変更後のデータをご参照ください。

isCheckingGetterDataIndex

false

データを返す際の行(row)または列(column)の位置(index)に対するチェック有無

isCheckingSetterDataIndex

true

データを設定する際の行(row)または列(column)の位置(index)に対するチェック有無

changeStructureWithData

false

データが存在する場合、構造変更有無

isConvertingToDataType

true

データを設定する際の列(column)のデータ型(type)への変換有無

データ設定やリターン時のデータ型(type)とデータ変換

VariableとDataSetはデータと共にデータ型(type)を持っています。データ型(type)の詳細については、データ型をご参照ください。設定されたデータ型(type)と異なる型(type)のデータを保存したり、保存されたデータと異なる型(type)のデータを返す場合にデータの変換が発生します。

例えば、Variableのデータ型(type)はStringですが、数値の123を保存する場合、数値123は文字列の"123"に変換して保存する必要があります。また、文字列の"123"を保存している状態でint型のデータが要求されたら文字列"123"を数値123に変換して返す必要があります。

ただし、DataSetのisConvertingToDataTypeが「false」である場合には、データ設定時に上記のような変換は行いません。

VariableとDataSetでは、上記のようなデータ変換をVariableDataConverterと DataSetDataConverterで行っています。Variableにデフォルトで設定されているVariableDataConverterはDefaultVariableDataConverterであり、DataSetにデフォルトで設定されているDataSetDataConverterはDefaultDataSetDataConverterです。

デフォルトで設定されているDataSetのDataSetDataConverterを利用せずに、別の方式に変換したい場合にはDataSetDataConverterを直接実装したり、DefaultDataSetDataConverterを継承して必要な部分だけ再定義したうえでDataSetのsetDataConverter(DataSetDataConverter)で設定してください。DataSetのメソッドとその内部で呼び出されるDataSetDataConverterのメソッドとの関係はDataSetDataConverterをご参照ください。VariableもDataSetと同じ方法で設定することができます。

$r_title(DataSetのユーザ定義のDataSetDataConverter登録)
DataSet ds = new DataSet("ds");
// "yyyy-MM-dd"形式の文字列もDate型に変換できるDataSetDataConverter設定
ds.setDataConverter(new UserDataConverter());

// 列(column)と行(row)追加
ds.addColumn("date", DataTypes.DATE_TIME, 256);
ds.newRow();

// "yyyy-MM-dd"形式のデータ設定
ds.set(0, "date", "2008-12-25");
$r_title(ユーザ定義のDataSetDataConverter)
class UserDataConverter extends DefaultDataSetDataConverter {

  public Object convert(DataSet ds, int row, int column, String value, int type) {
    return convert(ds, row, column, value, type, null);
  }

  public Object convert(DataSet ds, int row, int column, String value, int type, String charset) {
    // データ型(type)がDataTypes.DATE_TIMEである場合
    // 基本的にサポートしない"yyyy-MM-dd"形式の文字列もDateに変換
    if (type == DataTypes.DATE_TIME) {
      int len = (value == null) ? -1 : value.length();

      if (len == 10) {
        try {
          return new SimpleDateFormat("yyyy-MM-dd").parse(value);
        } catch (ParseException ex) {
          ;
        }
      }
    }

    return super.convert(ds, row, column, value, type, charset);
  }
}

データのデバッグ情報

開発時にデータの内容を把握するために出力したい場合にはDebuggerを利用します。

下記はDebuggerによりデータを出力する例題と出力結果です。

$r_title(出力結果の例)
PlatformData department = ...;

// Debugger生成
Debugger debugger = new Debugger();

// Debug情報出力
log(debugger.detail(department));
variable=[
index=0 (name, string, "R&D Center")
, index=1 (location, string, "222 Jamsil-Dong, Songpa-Ku, Seoul")
, index=2 (number, int, "99")
]
---------- index=000 ----------
name=employees, alias=employees, columnCount=4, rowCount=2, charset=null, isStoreDataChanges=true
, column=[
index=0 (id, int, 4)
, index=1 (firstName, string, 16)
, index=2 (lastName, string, 8)
, index=3 (manager, bool, 2)
]
, row=[
index=0 inserted ("0", "John", "Jones", "false")
, index=1 inserted ("1", "Tom", "Glover", "true")
]