


みなさんこんにちは。CalcArkプラグイン担当のTです。このコーナーでは、CalcArk編 第1回以来、久々の登場となります。
今回は、表計算には必須のソート機能において、データの並べ替えの方法を追加するプラグインを作成してみました。
ちょっと手を加えるだけで、自分用にカスタマイズできるサンプルプラグインです。
今回のサンプルプラグインは、ソースも併せて公開していますので、まずは こちらからプラグインソースの圧縮ファイルをダウンロードしてください。
準備はいいですか。それでは、始めましょう。
今回取り上げる「サンプルソート方法プラグイン」とはどんなプラグインでしょうか。最初にその説明をしておきましょう。
「ソート方法プラグイン」とは、データを並べ替えるソート機能において、並べ替えの方法を追加するものです。
CalcArk製品には、本体組み込みで「UNICODE順」のソート方法が、標準プラグインで「日本語辞書順」のソート方法が提供されています。
これらに加えて、このサンプルプラグインでは「曜日名順」のソート方法を追加することが出来ます。
◆仕様のまとめ
- このプラグインを動作させると、[データ-ソート]で開くダイアログに、「曜日名順」というソート方法が追加されます。
 【図1】「曜日名順」が追加されたソートダイアログ
- ソート対象範囲の文字列を、日本語の曜日名順で並べ替えます。
- 曜日名の順序は、昇順の場合
- 日曜日
- 月曜日
- 火曜日
- 水曜日
- 木曜日
- 金曜日
- 土曜日
であり、降順の場合はこの逆となります。
- ソート対象範囲内に曜日名以外の文字列が含まれる場合、それらの文字列は曜日名の後ろにUNICODE順で並べられます。
- ソート対象範囲内に文字列以外の値(数値、エラー値、論理値)が含まれる場合、それらのデータは昇順の場合
- 数値
- 文字列(文字列同士は並べ替えられる)
- 論理値
- エラー値
という順序で並べられます。降順の場合はこの逆となります。
◆実行してみよう
では、このプラグインを動作中にして、さっそく「曜日名順」ソートを実行してみましょう。
動作確認用に以下のようなデータを用意してみました。
 【図2】サンプルデータ:実行前
B3:C13の範囲を選択して、[データ-ソート]を選択し、B列をキーに、昇順/降順でそれぞれ実行してみた結果は以下の通りです。
 |
 |
| 【図3】実行後:昇順 |
【図4】実行後:降順 |
曜日文字列のキーは曜日名順でソートされており、それ以外のキーも上記仕様どおり並べ替えられています。
ソートが動作しているのが確認できたところで、次項ではプラグインの中身を見ていきましょう。
この項では、ダウンロードしたプラグインソースの圧縮ファイルの中身をざっと確認しておきます。
さっそく、圧縮ファイルを解凍してみてください。
圧縮ファイルには以下のようなファイルが含まれます。各ファイルの内容は次の通りです。
| ファイル名 |
内容 |
| SampleSortPlugin.java |
プラグイン本体クラス |
| DayOfTheWeekSortKey.java |
曜日名順ソート方法のキークラス |
| DayOfTheWeekSortTextKey.java |
曜日名順ソート方法における文字列キークラス |
| manifest.mf |
マニフェストファイル |
| compile.bat |
コンパイルツール |
| makejar.bat |
プラグインモジュール作成ツール |
| resconv.bat |
日本語リソースUnicode変換ツール |
| MainRes.properties |
メインリソース(プラグイン動作中にUIで使用するリソース) |
| MainRes_ja.properties |
日本語メインリソース(Unicode) |
| MainRes.properties.sjis |
日本語メインリソース(シフトJIS) |
| Resource.properties |
ベースリソース(プラグインの動作状態に関係なく使用するリソース) |
| Resource_ja.properties |
日本語ベースリソース(Unicode) |
| Resource.properties.sjis |
日本語ベースリソース(シフトJIS) |
【図5】サンプルソート方法プラグインモジュールの中身
ソースファイルは上から3つのみで、あとはリソース用ファイルや各種ツール類です。
それでは、いよいよ実装の方を見ていきましょう。次項からは、各ソースファイルの実装内容について解説します。
◆その1:プラグイン本体
まずは、プラグイン本体であるSampleSortPlugin.javaを見てみましょう。
ソート方法プラグインとして意味のある部分だけ抜粋してみました。
(注。抜粋しているソースの行番号は、必ずしもダウンロードされたソースファイルの行番号と一致しているわけではありません。)
|
リスト1 :SampleSortPlugin.java の抜粋
|
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
|
import jp.co.justsystem.choco.model.Value;
import jp.co.justsystem.choco.model.sort.Sortable;
import jp.co.justsystem.choco.plugin.AbstractSortMethodPlugin;
public class SampleSortPlugin extends AbstractSortMethodPlugin {
/**
* デフォルトコンストラクタ
*/
public SampleSortPlugin {
super;
}
/**
* 名前を取得します。
*
* @return 名前
*/
public String getName {
return "Day of the Week Sort";
}
/**
* 値を比較する場合に、その値の比較と同等の結果を得るキーを返します
*
* @param val 値
* @return キーとなる比較方法
*/
public Sortable getKey( Value val ) {
return new DayOfTheWeekSortKey( val );
}
(その他の実装については、略)
}
|
スーパークラス:AbstractSortMethodPlugin
このクラスは、5行目にあるようにAbstractSortMethodPluginの派生クラスとして実装しています。
この抽象クラスは、第1回で触れたように、ソート方法プラグインを実装するために便利な抽象クラスであり、ソート方法のためのインターフェイスSortMethodを実装しています。
プラグイン本体をこのクラスの派生にしておけば、ソート方法プラグインとして必要な処理のほとんどは個別に記述する必要はありません。
19行目のgetName、29行目のgetKeyが、個別にオーバーライドする必要があるメソッドです。
メソッド:getName
このソート方法を内部で一意に識別するための名称を文字列で返すメソッドです。この名称はXHTML1.0形式でファイルを保存する際にも使用されます。
このプラグインがサポートするのは曜日名順ソートなので、名称は"Day of the Week Sort"としてみました。
メソッド:getKey
このメソッドこそが、ソート方法プラグインの肝です。
実際に値を並べ替える方法を実装したソートキークラスを返します。
返値はSortableというインターフェイスを実装している必要があり、このプラグインではDayOfTheWeekSortKeyというクラスで実装しています。
◆その2:ソートキー
では、続いてソートキーを実装したDayOfTheWeekSortKeyクラスを見てみましょう。
|
リスト2 :DayOfTheWeekSortKey.java の抜粋
|
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
|
import jp.co.justsystem.choco.model.Value;
import jp.co.justsystem.choco.model.sort.AbstractSortKey;
public class DayOfTheWeekSortKey extends AbstractSortKey {
/**
* コンストラクタ
*
* @param val 値
*/
public DayOfTheWeekSortKey( Value val ) {
super( val );
}
/**
* 文字列での比較用のキーを返します。
*
* @param s 比較対象の文字列
* @return キー
*/
public Comparable getTextKey( String s ) {
return new DayOfTheWeekSortTextKey( s );
}
}
|
スーパークラス:AbstractSortKey
このクラスは、4行目にあるようにAbstractSortKeyの派生クラスとして実装しています。
この抽象クラスは、ソートキーのためのインターフェイスSortableを実装しており、ソートにおける文字列以外の値の標準的な処理を提供しています。
この抽象クラスを派生しておけば、ソート対象範囲に含まれる文字列以外の値の処理を気にすることなく、文字列の比較方法のみを実装するだけで済みます。
メソッド:getTextKey
このメソッドは、文字列の並べ替え実行時に呼ばれ、実際に文字列の並べ替えをするために必要な文字列ソートキークラスを返します。
返値はjava.lang.Comparableインターフェイスを実装している必要があり、このプラグインではDayOfTheWeekTextSortKeyというクラスで実装しています。
◆その3:文字列ソートキー
いよいよ、実際に文字列を並べ替える実装を含む、DayOfTheWeekSortTextKeyクラスを見てみましょう。
|
リスト3 :DayOfTheWeekSortTextKey.java の抜粋
|
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
|
import java.util.Hashtable;
public class DayOfTheWeekSortTextKey implements Comparable {
private static int STATUS_SUNDAY = 1;
private static int STATUS_MONDAY = 2;
private static int STATUS_TUEDAY = 3;
private static int STATUS_WEDENSDAY = 4;
private static int STATUS_THURSDAY = 5;
private static int STATUS_FRIDAY = 6;
private static int STATUS_SATURDAY = 7;
/**
* 文字列
*/
private String str_;
/**
* 文字列のステータス(曜日名でない場合はnull)
*/
private Integer status_;
/**
* 曜日文字列とステータスの対応テーブル
*/
private static final Hashtable days_ = new Hashtable(7);
static {
days_.put("日曜日", new Integer(STATUS_SUNDAY));
days_.put("月曜日", new Integer(STATUS_MONDAY));
days_.put("火曜日", new Integer(STATUS_TUEDAY));
days_.put("水曜日", new Integer(STATUS_WEDENSDAY));
days_.put("木曜日", new Integer(STATUS_THURSDAY));
days_.put("金曜日", new Integer(STATUS_FRIDAY));
days_.put("土曜日", new Integer(STATUS_SATURDAY));
}
/**
* コンストラクタ
*/
public DayOfTheWeekSortTextKey( String s ) {
initStatus( s );
}
/**
* このオブジェクトと指定されたオブジェクトの順序を比較します。
* このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、
* 等しい場合はゼロ、大きい場合は正の整数を返します。
*
* @param o 比較対象のキー
*/
public int compareTo( Object o ) {
return compareTo( (DayOfTheWeekSortTextKey)o );
}
private int compareTo( DayOfTheWeekSortTextKey key ) {
Integer status = key.getStatus;
if( status == null ){
if( status_ == null ){
// このキーの文字列は曜日文字列ではない
// 引数キーの文字列も曜日文字列ではない
// 文字列自身を比較して返す
return str_.compareTo( key.getString );
} else {
// このキーの文字列は曜日文字列ではない
// 引数キーの文字列は曜日文字列である
// 負の整数を返す
return -1;
}
} else {
if( status_ == null ){
// このキーの文字列は曜日文字列である
// 引数キーの文字列は曜日文字列ではない
// 正の整数を返す
return 1;
} else {
// このキーの文字列は曜日文字列である
// 引数キーの文字列も曜日文字列である
// 文字列ステータス(Integer)を比較して返す
return status_.compareTo( key.getStatus );
}
}
}
/**
* 与えられた文字列からステータスを初期化します。
*
* @param s 文字列
*/
private void initStatus( String s ){
Object status = days_.get( s );
if( status != null ){
// いずれかの曜日文字列だった
status_ = (Integer)status;
} else {
// 曜日文字列ではなかった
status_ = null;
}
str_ = s;
}
(その他の実装については、略)
}
|
このクラスでやるべきこと
このクラスでは、Comparableインターフェイスを実装して文字列の比較を行いますが、少なくとも
- コンストラクタに、文字列が与えられる
- ComparableインターフェイスのメソッドcompareToの引数に別の文字列キーオブジェクトが渡されるので、この文字列キー自身との比較を行う
ということを行えばよいだけで、文字列の比較方法については特に定められていません。
そこで、このクラスでは
- コンストラクタで文字列が与えられると、それが曜日文字列のいずれかであるか、曜日文字列でないかを判別する
- compareToメソッドでその結果をもとに比較を行う
ということをしてみます。
文字列の判別
コンストラクタで与えられた文字列を判別し、結果を数値のステータスで保持しておくことを考えてみましょう。
最も単純な方法としては、与えられた文字列を各曜日文字列と順次比較していくというものがあります。
しかし、これを例えばString#equalsメソッドなど、Javaの世界では敬遠すべきとされている重い処理で行うのは得策ではありません。
そこで、曜日文字列とステータスを格納するHashtableを用意し、Hashtableの検索を利用することでマッチングを行うようにしてみました。
5行目から11行目に文字列ステータスの定数を定義し、16行目に文字列を、20行目にステータスを保持するフィールドを設けました。
コンストラクタから呼ばれるinitStatusメソッド(88行目)で判別を行います。
文字列の比較
では、54行目からのcompareToメソッドで比較を行います。
引数で与えられた文字列キークラスからステータスを取得して比較しますが、比較には4つのパターンがあります。
- このキー自身の文字列は曜日文字列でない 引数のキーの文字列は曜日文字列でない
- このキー自身の文字列は曜日文字列でない 引数のキーの文字列は曜日文字列
- このキー自身の文字列は曜日文字列 引数のキーの文字列は曜日文字列でない
- このキー自身の文字列は曜日文字列 引数のキーの文字列は曜日文字列
各パターンの対処方法は以下の通りです。
- いずれも曜日文字列でない場合は、文字列自身を比較した結果を返します。
StringクラスはComparableインターフェイスを実装しているので、compareToメソッドの返値をそのまま返します。
- このキー自身が曜日文字列でなく、引数のキーが曜日文字列の場合は、このキーの方が小さいと判断し、負の整数を返します。
- このキー自身が曜日文字列で、引数のキーが曜日文字列でない場合は、このキーの方が大きいと判断し、正の整数を返します。
- いずれも曜日文字列である場合は、文字列ステータスを比較した結果を返します。
ここでステータスのフィールドをIntegerオブジェクトにしていたことが威力を発揮します。IntegerクラスはComparableインターフェイスを実装しているので、値の大小を比較しなくてもcompareToメソッドを使用できます。
これで、文字列の比較を行うことが出来ました。
それでは最後に、これまで見てきたソート方法プラグインの実装方法についてまとめておきます。
◆プラグイン本体クラス
- プラグイン本体は、AbstractSortMethodPluginの派生クラスとして実装します。
- getNameメソッドで、ソート方法の名称を返します。
- getKeyメソッドで、ソートキークラスを返します。
◆ソートキークラス
- ソートキーは、Sortableインターフェイスを実装する必要があり、AbstractSortKeyの派生クラスとして実装すれば便利です。
- getTextKeyメソッドで、文字列ソートキークラスを返します。
◆文字列ソートキークラス
- 文字列ソートキーは、Comparableインターフェイスを実装します。
- compareToメソッドで、文字列の比較方法を自由に実装します。
- サンプルでは、曜日文字列かどうかの判別結果をステータス(Integer)に保持し、ステータスをもとに比較を行うという方法で実装しました。
以上、今回はソート方法プラグインの実装方法について解説してきました。いかがでしたか。
サンプルでは、できるだけ単純に実装するために、ソートに使用する文字列キー(曜日文字列)をハードコードしていましたが、この文字列を書き換えるだけで、自分専用の使えるプラグインにすることも出来ます。(例えば"東京本社","大阪支社","福岡支社"など)
あるいはもう少し拡張して、文字列キーを別ファイルに切り出して差し替え可能にしたり、文字列キーを任意の個数使用できるようにしたりと、応用すればより汎用的なプラグインも作成できます。
この記事を参考に、是非自作プラグインにチャレンジしてみてください。
圧縮ファイル
各ソースファイル
jp.co.justsystem.choco.model.sort パッケージ
jp.co.justsystem.choco.plugin パッケージ
|