連載
» 2004年08月14日 00時00分 UPDATE

Eclipse徹底活用(10):辞書検索プラグインを作る

[的場聡弘, 岡本隆史,NTTデータ]

 第8回「Eclipseプラグインを作る(1)」第9回「Eclipseプラグインを作る(2)」の記事で、プラグインの作り方のおおよそをご理解いただけたことと思います。今回は、プラットフォームAPIなども使いながら、Eclipse開発環境と関連しながら動作するプラグインを作っていく様子をより詳細にご紹介します。

 今回作成するプラグインは、辞書を検索するプラグインです。Javaアプリケーションを開発するに当たっては、国際化プロパティファイルを作成するときなどのような、しばしば辞書を検索したい場面がありますが、このプラグインを使えば、文字列を選択して、そのまま辞書を引くといったような、シームレスな作業が可能になることが期待できます。

 なお、本稿の解説は前回と同様にEclipseのヘルプを参照しながら話を進めていきます。Eclipseには、ヘルプ・サーバという内部サーバ機能が用意されており、Webブラウザからヘルプページを参照することができます。ヘルプ・サーバを利用するためには[ウィンドウ]→[設定]の[ヘルプ]→[ヘルプ・サーバ]設定画面で、[listenするサーバのポート]に“12080”を設定してOKをクリックした後、[ヘルプ]メニューから[ヘルプ目次]を実行して、ヘルプサーバを起動しておいてください。記事中のリンクをクリックするとヘルプページを参照できるようになります(ヘルプ・サーバで参照できる記事中のリンクは< >で囲われています)。

郵便番号を引くプラグインを作成する

 まずは、第6回「Eclipseをプロファイラとして活用する」で利用した、郵便番号を引くZipCode.javaというプログラムを実行するプラグインを作成することから始めましょう。

 入力領域と結果領域を持つビューを作成し、入力領域に何か入力されたら、辞書を検索して結果を表示するプラグインを作成します。前回の記事を参考に、“org.ocharake.matobaa.dictionary”というIDのプラグインプロジェクトを作成し、ビューを拡張して、createPartControl()を実装してみました。

 実際にZipCode#searchByZip()を呼び出すロジックをどのクラスに置くべきか一瞬悩みますが、取りあえずViewPart1に実装してしまいます。不似合いだったら後でリファクタリングすることにしましょう。

郵便番号辞書を検索している様子 郵便番号辞書を検索している様子

 入力領域にSWTのModifyListenerを登録し、ModifyTextイベントを拾って検索を行い、結果を表示するようにします。実装は以下のようになります。

package org.ocharake.matobaa.dictionary;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.part.ViewPart;
import org.ocharake.matobaa.postal.ZipCode;
/**
 * 辞書検索プラグインのためのビューを提供します。
 * @see ViewPart
 */
public class ViewPart1 extends ViewPart implements ModifyListener {
    private Text queryWord;
    private Text result;
    /**
     * このプラグイン用のビューを作成します。
     * 1行のテキスト領域と、スクロールバーを伴う複数行のテキスト領域を縦に配置します。
     * 1行のテキスト領域には、このインスタンス自身を ModifyListenerとして登録します。
     * @see ViewPart#createPartControl
     */
    public void createPartControl(Composite parent) {
        GridLayout layout = new GridLayout(1, true);
        parent.setLayout(layout);
        queryWord = new Text(parent, SWT.SINGLE | SWT.BORDER);
        queryWord.setText("input word to query");
        queryWord.selectAll();
        queryWord.addModifyListener(this);
        GridData gridData = new GridData();
        gridData.horizontalAlignment = GridData.FILL;
        queryWord.setLayoutData(gridData);
        result =
            new Text(parent, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP);
        result.setText("Result will be shown here");
        gridData = new GridData();
        gridData.horizontalAlignment = GridData.FILL;
        gridData.verticalAlignment = GridData.FILL;
        gridData.grabExcessHorizontalSpace = true;
        gridData.grabExcessVerticalSpace = true;
        result.setLayoutData(gridData);
        parent.layout();
    }
    /**
     * テキストが変更される都度、辞書を検索します。
     * @param e テキスト変更イベント
     * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
     */
    public void modifyText(ModifyEvent e) {
        result.setText(""); // 結果をクリアしておく
        if (queryWord.getText().length() != 7) {
            return; // 入力が7けたでなければ検索しない。
        }
        try {
            ZipCode zip = ZipCode.searchByZip(queryWord.getText());
            result.setText(zip.都道府県名 + zip.市区町村名 + zip.町域名);
        } catch (java.io.IOException ex) {
            result.setText(ex.getMessage());
        }
    }
    public void setFocus() {
    }
}

拡張ポイントを定義する

 プラットフォームが拡張ポイントを提供しているのと同じように、プラグインも、ほかのプラグインによって拡張することのできる拡張ポイントを定義することができます。この仕組みを用いて、検索したい辞書と、その辞書フォーマットを解釈できる検索エンジンをプラグインできるような拡張ポイントを定義し、別の辞書を利用できるようにしましょう。

拡張ポイントを持つプラグイン 拡張ポイントを持つプラグイン

 そのためにはまず、共通的なインターフェイスを作成することから始めます。辞書を検索するために、以下のようなインターフェイスを作成してみました。

package org.ocharake.matobaa.dictionary;
 
import java.io.IOException;
 
public interface IDictionary {
    /** 対応している辞書形式を返します。 */
    String getSupportType();
    /** 辞書ファイルの所在を指定します。 */
    void setDictionary(String dictionaryPath) throws IOException;
    /** 辞書ファイルの後始末を行います。 */
    void closeDictionary()
    /** 単語を検索し、意味を返します。*/
    String query(String queryWord);
    /** 検索した語の次の意味を返します。*/
    String next();
    /** 検索した語自体を返します。*/
    String getHeading();
}

 次に、拡張ポイントを定義します。この拡張ポイントでは、上記インターフェイスを実装したクラスを指定するように定義しましょう。これには、拡張ポイントを定義し、そのスキーマを作成する、という手順を踏みます。では、実際に拡張ポイントを定義しましょう。plugin.xmlを編集するマニフェストエディターで、[拡張ポイント]タブを開き[追加]ボタンを押します。「拡張ポイントID」に、「externalDictionary」と入力して[終了]をクリックすると拡張ポイント定義が作成され、同時にスキーマファイルが作成されて[スキーマ・エディター]が開きます。

 次に、スキーマを作成します。[新規エレメント]をクリックし、作成された[new_element1]を選択してプロパティビューでNameを「dictionary」のように修正します。

 そして、dictionaryエレメントを選択した状態で、[新規属性]をクリックすると、「new_attribute1」が追加されるので、このプロパティを、以下の画面のように修正します。

 最後に、そのツリーの一番頭にあるextensionエレメントを選択し、右側にある「エレメント規則」のSequenceを右クリックし、「新規」「参照」「dictionary」と追加して、この拡張ポイントのスキーマが完成です。

classのプロパティと、extensionのエレメント規則 classのプロパティと、extensionのエレメント規則

作成した拡張ポイントを拡張する

 それでは、前回と同じ要領で、この拡張ポイントを利用する拡張を作成していきましょう。マニフェストエディターの[拡張]タブで[追加]し、[スキーマ・ベースの拡張]を選択すると、拡張ポイント一覧に、いま定義した「org.ocharake.matobaa.dictionary.externalDictionary」がありますので、これを選択し、追加した拡張を右クリックして[新規][dictionary]のように追加し、class属性の右端のボタンを押して「新規Javaクラスの作成」を作成します。作成されたスケルトンを基に、ZipCode#zipCodeString()を呼び出すよう実装しましょう。ZipCodeDictionary.javaに実装例を置いておきます。

プラグインレジストリを検索する

 さて、拡張ポイントを提供している側は、どのようなプラグインがインストールされているかを知り、それを呼び出す必要があります。これには、プラグインレジストリを検索する、という実装を行います。<ヘルプ>にそのやり方が書いてあるので、同じように実装してみましょう。

 protected IDictionary[] searchExtentions() {
    ArrayList dictionaries = new ArrayList();
    IPluginRegistry registry = Platform.getPluginRegistry();
    IExtensionPoint point =
        registry.getExtensionPoint(
            "org.ocharake.matobaa.dictionary.externalDictionary");
    IExtension[] extensions = point.getExtensions();
    for (int i = 0; i < extensions.length; i++) {
        IConfigurationElement[] elements =
            extensions[i].getConfigurationElements();
        for (int j = 0; j < elements.length; j++) {
            if ("dictionary".equals(elements[j].getName())) {
                IDictionary dictionary =
                    (IDictionary) elements[j]
                            .createExecutableExtension("class");
                dictionaries.add(dictionary);
            }
        }
    }
    IDictionary[] result = new IDictionary[dictionaries.size()];
    dictionaries.toArray(result);
    return result;
}

 後は、ViewPart1#modifyText()などから、このメソッドを利用して検索したプラグインのquery(String)を呼び出してやればよいですね。

 PDIC形式、PDICテキスト形式、電子ブック形式を検索できるプラグインをそれぞれ登録し、英辞郎、gene95、三省堂ビジネスマン英和辞典を検索してみました。

英辞郎、Gene95、三省堂ビジネスマン英和辞典を引いてみた 英辞郎、Gene95、三省堂ビジネスマン英和辞典を引いてみた

プラットフォームとの連携 − 選択文字列を検索する

 辞書ビューにフォーカスが移ったときに、直前にアクティブだったエディタの選択文字列を拾って自動的に検索できるようにしてみましょう。これには、アクティブなエディタを取得して、そのエディタで選択されている文字列を取得する、という実装を行う必要があります。

 こういった、パッケージやファイル、エディタやビューの情報を取得するには、EclipseプラットフォームAPIを使って実現します。<用語>と、APIドキュメントを探検してみましょう。

プラットフォーム・コア・ランタイム 開発環境の核となる部分。プラグインを管理したり、ログを書き出したりする場合に、<org.eclipse.core.runtime>パッケージにあるクラスのAPIを利用する
<ワークベンチ> プラットフォームのUI部分であり、開発環境の顔であるウィンドウ。<org.eclipse.ui.IWorkbenchWindow><org.eclipse.ui.IWorkbenchPage>クラスのAPIを用いて、エディターやビュー、選択文字列などを手に入れることができる
<ワークスペース> プロジェクト、フォルダー、ファイルなど、ナビゲータービューで表示される各要素(リソース)を管理する。<org.eclipse.core.resources.IWorkspace>クラスなどのAPIを用いて、プロジェクトを開いたりファイルを作成したりすることができる

 これらのEclipse APIを使って、以下のように選択文字列を取り出せるよう、以下のように実装してみました。

 public void setFocus() {
    // アクティブなエディターを手に入れる
    IWorkbench workbench = PlatformUI.getWorkbench();
    IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
    IWorkbenchPage page = window.getActivePage();
    IEditorPart editorPart = page.getActiveEditor();
    // エディターの選択文字列を手に入れる
    if (!(editorPart instanceof AbstractTextEditor)) {
      return;
    }
    AbstractTextEditor textEditor = (AbstractTextEditor) editorPart;
    ISelection selection = textEditor.getSelectionProvider().getSelection();
    if (!(selection instanceof TextSelection)) {
      return;
    }
    TextSelection textSelection = (TextSelection) selection;
    // それを検索テキストボックスに設定する
    queryWord.setText(textSelection.getText());
    // (あとはModifyListenerに任せる)
}

 実行すると、Javaのソースエディターやプロパティエディタで文字列を選択した状態で、辞書ビューをクリックすることで、自動的にその文字列を取り込んで辞書検索するように動作することが確認できます。

 この記事で作成しているプラグインはまだまだ中途半端な状態ですが、SourceForge.jpでこの後も継続して開発を進めていく予定です。

 Eclipseに同梱されているヘルプは、プラグイン作成に当たって非常に参考になります。サンプルプラグインや、ウィザードによって自動生成されたソースを眺めるのもよいでしょう。

 参考になるWebサイトを以下にご紹介しておきます。日本語のリソースも徐々に増えてきました。これからが非常に楽しみです。

まとめ − プラグインの開発とは?

 ちょっとした工夫を付け加えるだけで、自分の手になじんだ開発環境が、もっと便利なものになります。「一流の職人は、道具は自分で作る」。この快感を味わうことができるのも、Eclipseプラグイン作成のだいご味ではないでしょうか。

 Eclipseという素晴らしい環境をIBMが私たちに与えてくれたように、便利なプラグインを作ったら、オープンソースライセンスで公開し、コミュニティに還元するのがよいでしょう。

 本連載「連載:Eclipse徹底活用」は今回で最終回となります。「連載:Eclipseを使おう!」も含めると、Eclipseの導入から始まり、プラグインを利用したいろいろなパターンの開発を行い、最後には自分でプラグインを開発するところまでご紹介してきました。Eclipseの爆発的な普及に伴い、本連載もたくさんの方に読んでいただくことができました。

 連載当初は、「Eclipseなんて便利なIDEがあるんだ。へぇ〜」という読者が多かったのですが、いまではJava標準の開発プラットフォームになりつつあります。本連載の筆者一同、あらためて時代の移り変わりの速さに驚いています。

 Eclipseを公開してくれたIBMをはじめ、有用なプラグインをリリースされているプラグインデベロッパの方々、Eclipse-MLやWikiサイト「エクリプス」などのコミュニティの皆さんには、筆者自身も大変お世話になりました。併せて、本連載をお読みいただいた読者の皆さまに、感謝します。

 機会がありましたら、またお目にかかりたいと思います。また、同じ技術者同士として、コミュニティで一緒に活動する仲間としても、お会いできることを楽しみにしています。

筆者プロフィール

的場 聡弘(まとば あきひろ;matobaa)

現在、株式会社NTTデータ ビジネス開発事業本部に所属。社内技術支援業務に携わり、主にJ2SEおよびJ2EEを用いたシステム構築に係わる方式設計や障害対応を担当している。おちゃらけプログラマ七号機(http://www.ocharake.org/)や、ぱ〜む脱力ゲーム協会(http://palmgames.tripod.co.jp/)の公認脱力作家という顔も持つ。

岡本 隆史(おかもと たかし)

NTTデータ 技術開発本部 所属。Debian GNU/Linuxの優れたメンテナンス性と他のディストリビューションを圧倒するパッケージ数に引かれDebianを使い始めたのをきっかけに、Debian プロジェクトの開発者となりJavaサポートの強化を行う。『Jakartaプロジェクト徹底攻略』(技術評論社)、『WEB+DB PRESS』(技術評論社)、『Java World』(IDGジャパン)、『JAVA Developer』(ソフトバンクパブリッシング)などで執筆活動を行っている。


Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。