« 2004年2月 | トップページ | 2004年4月 »

2004.03.29

デバッグ用コード切り替えスイッチ

プログラムの内部動作というのは,なかなか表向きからはわからない。原因不明のバグも1,2件はめずらしくない。開発途中であれば,開発者が内部の動作状況をトレースするためのコードを埋め込んで検証したりすることは容易だが,一旦リリースしてしまった後となると検証を行うことは非常に難しくなる。リリース時にデバッグ用コードをプログラムから削除してしまうことが多いからだ。

J2SEの1.4から追加されたアサーション機構は,VMの起動時にそれを有効にするかどうかを切り替えられる。それと同じように,プラグインを試験するためのRun-time Workbenchでは,トレース機能などのデバッグ用コードを有効にするかどうかを切り替えるための機構が備わっている

Eclipseを構成するプラグインをいろいろ覗いていくと,.optionsという名前のファイルを持っているのがわかる。この.optionsファイルには,各機能を有効にするかどうかのスイッチとなる名前と,有効にするかどうかの真偽値(trueかfalse)を記述する。スイッチの名前は,プラグインのIDに機能名を加えたものである。

  # Turn on debugging for the yoichiro.myPlugin
  yoichiro.myPlugin/debug=true
  # Displays file name.
  yoichiro.myPlugin/display/filename=false

先頭が「#」の行はコメントとみなされる。上記を見てわかるように,プラグインIDのあとは「/」で機能名を区切って記述する。機能名は任意に決めてよい。プロパティファイルと同じように,trueかfalseかの値を「=」のあとに記述する。

プログラムから上記のファイルにアクセスするために,PlatformクラスのgetDebugOptionメソッドを使用する。一般的には,クラスのstaticイニシャライザで値を読み込んで,実際のコード中で使用する。

  public class Hoge {
    private static boolean debug = false;
    static {
      String value = Platform.getDebugOption(
        "yoichiro.myPlugin/debug");
      if (value != null && value.equalsIgnoreCase("true")) {
        Hoge.debug= = true;
      }
    }
  }

PlatfromクラスのgetDebugOptionメソッドに.optionsファイルに記載した「プラグインID+機能名」の文字列を渡すことで,その値を文字列として取得することができる。そして,文字列がnull(指定された項目が見つからない)ではなく,しかも値がtrueだった場合は,debugフィールドの値を真としている。あとはdebugフィールドの値を使って,

  if (Hoge.debug)
    System.out.println("Debug: ...");

というようにしてデバッグコードを記載すればよい。

次に.optionsファイルに記載された項目のON/OFFを切り替える方法だが,もちろん.optionsファイルを直接書き換えてtrue/falseを切り替えてもよい。しかし,Eclipseではプラグインを試験するためのRun-time Workbench実行では,この.optionsファイルに記載された値をGUIを用いて変更することができる

[Run]-[Run...]メニューで表示される設定画面のRun-time Workbench項目の中には,Tracingという項目がある。Enable tracingチェックボックスにチェックを入れ,プラグインを選択すると,そのプラグインの.optionsファイルに記載された項目が表示される。そのValue列をクリックして,true/falseを切り替えることができ,Run-time Workbenchの実行時にtrueにした項目が有効になる。

さて,上記の機構は基本的にはRun-time Workbenchによる実行時の話であるが,一旦プラグインがリリースされればRun-time Workbenchによる実行ではなく,通常のEclipseの起動によってプラグインが組み込まれる。通常の起動では,.optionsファイルが読み込まれることはなく,一般的にデバッグ機能は有効にならない。しかし,以下のようにEclipseの起動時にオプションを記述すれば.optionsファイルを読み込ませることができ,.optionsファイル中のtrueの項目に関して,デバッグ機能を有効にすることができる。

  eclipse.exe -debug C:\eclipse\plugins\yoichiro.myPlugin\.options

プラグインのリリース後に問題が発生した場合は,上記のコマンドでユーザに起動してもらい,デバッグ情報を採取すると良いだろう。

| | コメント (1) | トラックバック (0)

2004.03.27

エラーログの出力

Eclipseにおいて,発生したエラーの状況がIStatusオブジェクトによって表現され,CoreException例外にIStatusオブジェクトが保持されて伝達される(「CoreExceptionとIStatus」参照)。そして,IStatusオブジェクトの内容をエラーダイアログに簡単に表示することができた(「エラーダイアログの表示」参照)。

しかし,エラーダイアログが表示できない(表示することがふさわしくない)状況も当然存在する。そんなときに頼るのがロギング。ちゃんと起きたことは残しときましょう,という機構。エラーダイアログは閉じてしまえばそれまでだが,同時にログに記録しておけば,ログをクリアしない限りいつでも見ることができるようになる。

IStatusオブジェクトがエラーダイアログに統合され簡単に表示できたように,IStatusオブジェクトの内容をログに記録することも簡単にできるようになっている

  try {
    ...
  } catch(CoreException e) {
    Plugin plugin = ...;
    ILog logger = plugin.getLog();
    IStatus status = e.getStatus();
    logger.log(status);
  }

ログの出力は,ILogインタフェースにより規定されているILogオブジェクトの取得は,Pluginオブジェクトが必要である。Pluginクラス(のサブクラス)を持たないプラグインでは,基本的にロギングはできないので,ちゃんとプラグインクラスを準備する必要がある(他のプラグインを強引に取得して出力もできるが,これはマナー違反だろう)。「Pluginクラスの作成」で取り上げたような感じでPluginクラスを作成し,MyPlugin.getInstance() という呼び出しを使って,簡単に任意の場所でPluginオブジェクトを取得できるようにしておくと良いだろう。

話を元に戻そう。Pluginオブジェクトを取得できたら,getLogメソッドを呼び出してILogオブジェクトを取得する。そして,ロギングの対象であるIStatusオブジェクトをCoreExceptionオブジェクトのgetStatusメソッドで取り出す。あとはILogオブジェクトのlogメソッドにIStatusオブジェクトを渡すことによって,IStatusオブジェクトの内容がログに書き出される

さて,一体どこに書き出されたのかと言うと,ちゃんとファイルに出力されている。ログのファイルのパスは,
  $ECLIPSE_HOME/workspace/.metadata/.log
である。Run-time workbenchを使った場合は,runtime-workspaceディレクトリになる。

そして,上記のログファイルの内容は,PDE Runtime/Error Logビューを使って閲覧できる。さらに,各ログ項目をダブルクリックすることにより,ログの詳細を閲覧できる。

error-log.gif

error-log-detail.gif

詳細の表示画面では,IStatusオブジェクトが持つ例外のスタックトレースなどが表示されていることがわかるだろう。

| | コメント (0) | トラックバック (0)

2004.03.26

エラーダイアログの表示

CoreExceptionとIStatus」で紹介したように,Eclipseでは例外のほとんどがCoreExceptionとIStatusの組み合わせで成り立っている。一般的に,例外が発生したときには,ユーザにそのことを通知するためのダイアログを表示する。CoreException例外の発生時には,それが持つIStatusオブジェクトの内容をエラーダイアログに表示することになるが,EclipseではIStatusオブジェクトの内容をエラーダイアログとして簡単に表示するためのErrorDialogクラスが用意されている

ErrorDialogを使ってIStatusオブジェクトの内容をエラーダイアログとして表示する手順は,非常に簡単である。

  try {
    ...
  } catch(CoreException e) {
    Shell parentShell = ...;
    IStatus status = e.getStatus();
    ErrorDialog.openError(
      parentShell, "エラー", "エラーメッセージ", status);
  }

まず,ダイアログの表示に必要な,ダイアログの親となるShellオブジェクトを取得する。そして,キャッチしたCoreException例外オブジェクトのgetStatusメソッドを使って,IStatusオブジェクトを得る。あとは,ErrorDialogクラスのクラスメソッドであるopenErrorメソッドに「Shellオブジェクト」「タイトルに表示する文字列」「エラーの概要を示すメッセージ」「IStatusオブジェクト」を渡して呼び出せば,以下のようなエラーダイアログが表示される。「Reason」のところに表示されているメッセージは,IStatusオブジェクトのgetMessageメソッドの戻り値である。

error-dialog.gif

もし,openErrorメソッドに渡しているIStatusオブジェクトのエラーレベル(正確にはステータスレベル)がIStatus.ERROR以外だった場合は,ちゃんとそれ相応のダイアログが表示される(ダイアログのアイコンが変わる)。

[IStatus.WARNINGだった場合]
warning-dialog.gif

[IStatus.INFOだった場合]
information-dialog.gif

うまくできていることに,IStatus.OKだった場合は,openErrorメソッドを呼び出してもダイアログは表示されない。「OKなんだからエラーダイアログを表示しなくてもいいじゃん」っていう考えだと思われる。

ちなみに,ダイアログの親となるShellオブジェクトを渡すところでnullを渡しても,一応「親のいないダイアログ」が表示されるが,あまりお勧めできることではない。これについては「Shellオブジェクトの取得」を参照されたし。

| | コメント (0) | トラックバック (0)

2004.03.25

CoreExceptionとIStatus

EclipseプラットフォームのAPIを見ていると,いたるところで出てくるのがCoreException例外クラス。プラグインを開発していると,頻繁に,try { ... } catch(CoreException e) { ... } している自分がそこにいる。

一般的に,Javaでは例外クラスの考え方は大きく2つに分けられる。

一つは,正常系の処理から外れた状況の数だけ例外クラスをどんどん作る考え方。その状況がある程度グループ化されるのであれば,そのグループとなる親クラスを作って,具体的なエラー状況ごとにサブクラスを作成する。例えば,入出力関係の例外というグループ的な意味でIOException例外クラスがあり,その具体的な状況(ファイルが見つからない,など)の表現として,IOException例外クラスを継承したFileNotFoundException例外クラスがある,という感じである。どんなエラーなのかは,キャッチする例外の種別で判断する。

もう一つは,SQLException例外クラスのようなものもある。データベースに関するエラーとしてSQLException例外クラス一つしかなく,具体的なエラーの状況はgetErrorCodeメソッドの戻り値で判断する,という感じのものである。

Eclipseプラットフォームでは,後者の考え方が取り入れられている。つまり,Eclipse内での例外は,ほとんどの場合CoreException例外クラス一つでまかなわれている。では,どんな事態になってCoreException例外がおきたのかということを判断するにはどうしたらいいかというと,CoreException例外クラスが持つIStatusオブジェクトの中身を見てどんなエラーなのかどうかを判断するようになっている。CoreException例外クラスは,IStatusオブジェクトを例外発生元から例外処理側に運ぶためのコンテナと考えることができる。その証拠に,CoreException例外オブジェクトの生成には,必ずIStatusオブジェクトが必要である。

IStatusインタフェースでは,いくつかの情報を提供する処理が規定されている。CoreException例外に格納する目的で使用される場合,IStatusオブジェクトが持つ情報は以下のものになる。

  ・エラーが発生したプラグインのID (必須)
  ・エラーコード (必須)
  ・対人間用のメッセージ (必須)
  ・エラーレベル (必須)
  ・エラーの原因となった例外オブジェクト (任意)

エラーレベルは,IStatusインタフェースに定義されている以下の定数から選択する。

  ・IStatus.INFO - 情報
  ・IStatus.WARNING - 警告
  ・IStatus.ERROR - エラー

自分でCoreException例外をスローするときは,IStatusインタフェースの実装クラスを自作する必要は通常なく,プラットフォームが提供してくれているStatusクラスを使えばよい。以下に,CoreException例外を自分でスローする場合の例を示す。

  int ERROR_CODE_FILE_NOT_FOUND = 35;

  public void lodaFile(String fileName) throws CoreException {
    try {
      ...
    } catch(FileNotFoundException exception) {
      Plugin plugin = ...;
      String pluginId = plugin.getDescriptor().getUniqueIdentifier();
      IStatus status = new Status(
        IStatus.ERROR,
        pluginId,
        ERROR_CODE_FILE_NOT_FOUND,
        "File not found. " + fileName,
        exception);
      throw new CoreException(status);
    }
  }

上記では,ファイルを読み込む処理がFileNotFoundException例外の発生によって失敗したときにCoreException例外をスローする,という例である。PluginクラスのgetDescriptorメソッドでIPluginDescriptorオブジェクトを取得し,そのgetUniqueIdentifierメソッドを呼び出すことによって,プラグインのIDを取得できる。あとは,Statusクラスのコンストラクタに各種情報を渡してIStatusオブジェクトを生成し,CoreException例外クラスのコンストラクタにIStatusオブジェクトを渡して,できあがったCoreExceptionオブジェクトをスローしている。

ところで,IStatusインタフェースでは,IStatus.OKという定数も定義されているのだが,上記の説明ではIStatus.OKの存在に違和感を感じてしまうかもしれない。CoreException例外に格納されるという前提でステータスコードを考えてしまうとIStatus.OKは浮いてしまうのだが,IStatusオブジェクトは「ある状況」を汎用的に表現することを目的としたものであり,それはエラーという状況のみに限定されない,ということである。

さて,CoreException例外をキャッチする側はどうするのかというと,普通はエラーダイアログを表示したり,ログにエラーを書き出すことになる。

| | コメント (0) | トラックバック (0)

2004.03.24

プロパティ・ページの作成方法

Package ExplorerビューやNavigatorビューのコンテキストメニューの一番下に必ず表示されている「Properties」メニュー項目。Eclipseでは,IResourceオブジェクトであれば何でもプロパティを持つことができる。そのプロパティ値の編集を行う画面の呼び出しに使うのが「Properties」メニューである。ここでは,「Properties」メニューにより表示されるプロパティ・ページの作成方法を紹介する。

新規にプロパティ・ページを定義するには,org.eclipse.ui.propertyPages拡張ポイントを使用してプラグイン・マニフェストに記述を行う

  <extension point="org.eclipse.ui.propertyPages">
    <page
      id="yoichiro.myPlugin.myPropertyPage"
      name="My property page"
      objectClass="org.eclipse.jdt.core.IJavaProject"
      class="yoichiro.MyPropertyPage">
    </page>
  </extension>

org.eclipse.ui.propertyPages拡張ポイントでは,page要素を使って新規に作成したいプロパティ・ページの定義を行うid属性にプロパティ・ページを識別するためのID文字列を,name属性にプロパティ・ページの名前を記述する。プロパティ・ページの名前は,画面上のプロパティ・ページのタイトル部に表示される

objectClass属性は,このプロパティ・ページを適用するオブジェクトの種類を指定する。上記ではIJavaProjectクラス,つまりJavaプロジェクトに対してのみ適用されるプロパティ・ページということになる。そしてclass属性にプロパティ・ページの実装クラスを指定する。

プロパティ・ページのクラスは,IWorkbenchPropertyPageインタフェースを実装して作成するのだが,通常はプラットフォームが用意してくれているPropertyPageクラスを継承して作成する

  public class MyPropertyPage extends PropertyPage {
    public MyProperty() {
      super();
    }
    protected Control createContents(Composite parent) {
      // プロパティ・ページのGUI作成処理
    }
  }

基本的にはこれだけ。createContentsメソッドでプロパティ・ページの画面をSWTを使ってせっせと構築する。実は,「return null;」だけでも,下図のような空の画面が表示されるようになる。

property-page.gif

プロパティ・ページでは,自動的に[OK]ボタンと[Cancel]ボタンが提供される。これらのボタンが押されたときには,下記のメソッドがプラットフォームから呼び出されるので,その中にそれぞれの処理を記述する。

  ・[OK]ボタンが押されたとき - public boolean performOk() { ... }
  ・[Cancel]ボタンが押されたとき - public boolean performCancel() { ... }

上記のメソッドの結果として false を返却したときは,プロパティを確定しちゃいけない状況とプラットフォームは判断し,プロパティ・ページのダイアログは閉じずにそのままとなる。

プロパティ・ページでは,プロパティ・ページのダイアログを閉じずに「プロパティ値を初期値に戻す」「プロパティ値を確定させる」動作を行うことができる。それぞれ[Restore Defaults]ボタン,[Apply]ボタンがそれである。これらが押されたときには,下記のメソッドが呼び出されるので,その中で所定の処理を記述する。

  ・[Restore Defaults]ボタンが押されたとき - protected void performDefaults() { ... }
  ・[Apply]ボタンが押されたとき - protected void performApply() { ... }

なお,noDefaultAndApplyButtonメソッドをcreateContentsメソッド内で呼び出してあげると,[Restore Defaults]ボタンと[Apply]ボタンが表示されなくなるので,必要ないときは消してしまうことが可能である。

| | コメント (0) | トラックバック (0)

2004.03.21

ビルダー登録の担当者は?

新規ビルダーの定義」で紹介したビルダー。このビルダーのプロジェクトへの登録方法は「ビルダーの登録方法」で取り上げたが,一体どこでビルダーを登録する処理を書けばよいのだろうか?

実は「プロジェクト・ネーチャーの定義方法」で答えを書いているのだが,プロジェクトにビルダーを登録する処理は,プロジェクト・ネーチャーの仕事である。もちろんそうでない場合もあるが,99%はプロジェクト・ネーチャーの中でプロジェクトにビルダーを登録する処理を記述する。

つまり,こんな感じになる。

  public class MyNature implements IProjectNature {
    private IProject project;
    ...
    public void configure() throws CoreException {
      // projectに対してビルダーを登録する
    }
    public void deconfigure() throws CoreException {
      // projectからビルダーを解除する
    }
  }

プロジェクトにビルダーを登録すること,それこそが「プロジェクトに特徴付けを行う」ことである。プロジェクトに登録付けを行うものといえば,プロジェクト・ネーチャーである。つまり,
  (1) 新規プロジェクト作成ウィザードで,プロジェクトにプロジェクト・ネーチャーが登録される。
  (2) プロジェクト・ネーチャーにより,ビルダーが登録される。
  (3) ビルダーがリソースを変換する。
という流れになる。

何らかのビルダーを自作したら「プロジェクト・ネーチャーも作らなくっちゃ」ということになると思って,間違いない。逆に「プロジェクト・ネーチャーはプロジェクトにビルダーを登録するものである」と思っていても,間違いない。

| | コメント (0) | トラックバック (0)

ビルダーの解除方法

ビルダーの登録方法」を紹介したら,解除方法を紹介しない理由はないだろう。ここでは,プロジェクトに登録されているビルダーを登録解除する手順を紹介する。

ビルダーはIProjectDescriptionオブジェクトにICommandオブジェクトの配列として登録されている。「プロジェクト・ネーチャーの解除方法」のときと同じように,解除したいビルダーのICommandオブジェクトを配列から削除することで,ビルダーを解除できる

  String removeBuilderId = "yoichiro.myPlugin.myBuilder";

  IProject project = ...;
  IProjectDescription description = project.getDescription();
  ICommand[] commands = description.getBuildSpec();
  for (int i = 0; i < commands.length; i++) {
    if (commands[i].getBuilderName().equals(removeBuildName)) {
      ICommand[] newCommands = new ICommand[commands.length - 1];
      System.arraycopy(commands, 0, newCommands, 0, i);
      System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
      description.setBuildSpec(newCommands);
      project.setDescription(description, null);
      return; // or break;
    }
  }

IProjectDescriptionオブジェクトからgetBuildSpecメソッドで,登録されているビルダーのICommandオブジェクトの配列を取得する。その後,配列の要素ごとにgetBuilderNameメソッドでビルダーのIDを取得して,解除したいビルダーを見つける。もし見つかったら,あとはそのICommandオブジェクトを除外したICommandオブジェクトの配列を生成し,IProjectDescriptionオブジェクトのsetBuildSpecメソッドに新しい配列をセットする。そして最後にIProjectDescriptionオブジェクトをIProjectオブジェクトのsetDescriptionメソッドに渡して,完了。

上記の処理を行うことにより,ビルダーは解除され,.projectファイルからも該当ビルダーの記述は削除される。

| | コメント (0) | トラックバック (0)

ビルダーの登録方法

新規ビルダーの定義」で紹介したビルダーは,プロジェクトに登録されることにより機能する。ここでは,ビルダーをプロジェクトに登録する方法を紹介する。

プロジェクト・ネーチャーの適用方法」では,プロジェクト・ネーチャーはIProjectDescriptionオブジェクトを使用して登録を行った。プロジェクト・ネーチャーの登録方法と同じように,ビルダーの登録はIProjectDescriptionインタフェースを使って行う

  String newBuilderId = "yoichiro.myPlugin.myBuilder";

  IProject project = ...;
  IProjectDescription description = project.getDescription();
  ICommand[] commands = description.getBuildSpec();
  for (int i = 0; i < commands.length; i++) {
    if (commands[i].getBuilderName().equals(newBuilderId)) {
      return; // ビルダー登録のキャンセル
    }
  }
  ICommand command = description.newCommand();
  command.setBuildName(newBuilderId);
  ICommand[] newCommands = new ICommand[commands.length + 1];
  System.arraycopy(commands, 0, newCommands, 0, commands.length);
  newCommands[commands.length] = command;
  description.setBuildSpec(newCommands);
  project.setDescription(description, null);

プロジェクト・ネーチャーの場合は,プロジェクトに登録されているプロジェクト・ネーチャーのIDの配列がIProjectDescriptionオブジェクトに格納されていたが(「プロジェクト・ネーチャーの適用方法」参照),ビルダーの場合は,各ビルダーがICommandオブジェクトで表現され,ICommandオブジェクトの配列がIProjectDescriptionオブジェクトに格納される。つまり,新規にビルダーを登録したいときは,そのビルダーを表すICommandオブジェクトをIProjectDescriptionオブジェクトが持つ配列に追加すればよい

最初に,既に登録したいビルダーがプロジェクトに適用されているかどうかをチェックする。登録されているビルダーのICommandオブジェクトの配列は,IProjectDescriptionオブジェクトのgetBuildSpecメソッドにより取得できる。そして,得た配列の要素の中で,登録したいビルダーのIDを持つICommandオブジェクトがあるかどうかを見る。ビルダーのIDはICommandオブジェクトのgetBuilderNameメソッドで取得することができる。もし見つかれば,登録処理は行わない。

登録されていなければ,新規にビルダーを登録する。新規ビルダー用に,新しくICommandオブジェクトを生成する。ICommandオブジェクトの生成は,IProjectDescriptionオブジェクトのnewCommandメソッドを使用する。そして,生成したICommandオブジェクトのsetBuilderNameメソッドに登録したいビルダーのID文字列をセットする。あとは「プロジェクト・ネーチャーの適用方法」のときと同じように,新規にICommandオブジェクトの配列を生成し,元々登録されていたICommandオブジェクトをコピーし,新規に生成したICommandオブジェクトを最後の要素にセットする。さらにIProjectDescriptionオブジェクトのsetBuildSpecメソッドに,新規ビルダーを追加したICommandオブジェクトの配列をセットして,最後にIProjectオブジェクトにIProjectDescriptionオブジェクトをsetDescriptionメソッドでセットして,新規ビルダーの登録が完了となる。

上記の処理により,プロジェクトにビルダーが追加される。もちろん,登録されたビルダーは.projectファイルに追記される

  <?xml version="1.0" encoding="UTF-8"?>
  <projectDescription>
    <name>TestProject</name>
    <comment></comment>
    <buildSpec>
      ...
      <buildCommand>
        <name>yoichiro.myPlugin.myBuilder</name>
        <arguments>
        </arguments>
      </buildCommand>
    </buildSpec>
    ...
  </projectDescription>

このように,ビルダーの登録方法はプロジェクト・ネーチャーの適用方法とほとんど同じである。

・・・長文になってしまった。反省。

| | コメント (0) | トラックバック (0)

新規ビルダーの定義

Eclipseを使っている人のほとんどが,Javaプログラムの開発のためにEclipseを使用している。Eclipse JDTでは,コンパイルという作業は完全にEclipseの裏方作業であり,Eclipse使用者が明示的にコンパイル作業を行う必要がない。ソースコードを変更するとすぐに裏でコンパイルが実装され,エラーがあれば自動的にエディタ上にマーカーが表示される。

ソースコードをコンパイルしてクラスファイルを生成するように,あるリソースを元にして新規のリソースを作成する機能のことをビルダーと呼ぶ。ビルダーはプラグインにより定義され,プロジェクトに対して割り当てられる。例えば,JavaプロジェクトはJDTプラグインに定義されているJavaビルダーが割り当てられている。Javaビルダーは,変更されたソースファイルに対してコンパイルを行い,その結果をエディタに対してマーカーにより反映する処理を行っている。

では,新規にビルダーを定義する方法を紹介する。ビルダーはorg.eclipse.core.resources.builders拡張ポイントを使用してプラグイン・マニフェストに定義される

  <extension
      point="org.eclipse.core.resources.builders"
      id="myBuilder"
      name="My builder">
    <builder>
      <run class="yoichiro.MyBuilder"/>
    </builder/>
  </extension>

id属性はビルダーに付与するIDを,name属性はビルダーの名前を記述する。上記のプラグイン・マニフェストがyoichiro.myPluginというIDのプラグインに定義されていれば,このビルダーはyoichiro.myPlugin.myBuilderというIDになる。ビルダーの実体は,builder要素の子要素としてrun要素を定義し,そのclass属性の値として実装クラスを指定する。

ビルダーの処理を記述するクラスは,IncrementalProjectBuilder抽象クラスの実装クラスとして作成する

  public class MyBuilder extends IncrementalProjectBuilder {
    public MyBuilder() {
      super();
    }
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
        throws CoreException {
      IProject target = getProject();
      // targetに対するビルド処理
    }
  }

ビルド作業は,buildメソッドに記述する。ビルダーのオブジェクトはJavaのリフレクションによりインスタンスが生成されるので,引数なしのコンストラクタが必要となる(やることがなければ上記のように再定義する必要はない)。

このビルダーはEclipseプラットフォームにより自動的に利用される。この際,Eclipseプラットフォームはビルド対象のプロジェクトをビルダーにセットする。buildメソッドの中では,getProjectメソッドによりビルド対象のプロジェクトオブジェクトを取得し,プロジェクトが持つリソースに対してビルド処理を行えばよい。

| | コメント (0) | トラックバック (0)

2004.03.17

プロジェクト・ネーチャーの解除方法

プロジェクト・ネーチャーの適用方法」を紹介したが,その解除方法も気になるところだろう。ここではプロジェクト・ネーチャーの解除方法を紹介する。

プロジェクト・ネーチャーの適用は,適用したいプロジェクト・ネーチャーのIDをIProjectDescriptionオブジェクトが持っている配列に追加することで実現できた。ということは,適用されているプロジェクト・ネーチャーの解除は,解除したいプロジェクト・ネーチャーのIDをIProjectDescriptionオブジェクトが持つ配列から除外することで実現できる。理屈は至極単純である。

  String removeNatureId = "yoichiro.myPlugin.myNature";

  IProject project = ...;
  IProjectDescription description = project.getDescription();
  String[] ids = description.getNatureIds();
  for (int i = 0; i < ids.length; i++) {
    if (ids[i].equals(removeNatureId)) {
      String[] newIds = new String[ids.length - 1];
      System.arraycopy(ids, 0, newIds, 0, i);
      System.arraycopy(ids. i + 1, newIds, i, ids.length - i - 1);
      description.setNatureIds(newIds);
      project.setDescription(description);
      return; // or break;
    }
  }

最初にIProjectオブジェクトのgetDescriptionメソッドでIProjectDescriptionオブジェクトを取得し,そのgetNatureIdsメソッドを呼び出して適用されているプロジェクト・ネーチャーのIDの配列を取得している。そして,配列の中に解除したいプロジェクト・ネーチャーのIDがあるかどうかをチェックする。もし見つかったら,発見場所のインデックスを境に,その前後の要素を新しい配列にコピーして,解除したいプロジェクト・ネーチャーのIDを除外した配列を作成する。

あとは「プロジェクト・ネーチャーの適用方法」のときと同じように,IProjectDescriptionオブジェクトのsetNatureIdsメソッドで新しいIDの配列をセットし,IProjectDescriptionオブジェクトをIProjectオブジェクトのsetDescriptionメソッドに渡して,プロジェクト・ネーチャーのIDの配列の更新を反映させる。

上記の処理が完了すれば,プロジェクトからプロジェクト・ネーチャーが解除され,.projectファイルからも解除したプロジェクト・ネーチャーに関する記載が削除される。

プロジェクト・ネーチャーを解除しなければならないときって,そう多くはないはず。というか,ほぼないと思って間違いないだろう。プロジェクトの特徴が途中で変更になることってないだろうし。上記の処理は知っていて損はないが,きっと使わないもの,だと思う。

| | コメント (0) | トラックバック (0)

2004.03.16

プロジェクト・ネーチャーの適用方法

プロジェクト・ネーチャーの定義方法」において解説したプロジェクト・ネーチャーだが,プロジェクトへの適用方法はまだ紹介していなかった。ここでは,プロジェクト・ネーチャーのプロジェクトへの適用方法について解説する。

プロジェクトを表すクラスはIProjectクラスだが,そのプロジェクトの名前や説明文など,プロジェクト自身を説明するための情報(メタデータ)はIProjectクラスから直接取得することができない。その代わりに,プロジェクトの説明情報を扱うためのAPIが,IProjectDescriptionインタフェースに規定されている。IProjectDescriptionオブジェクトは,IProjectオブジェクトのgetDescriptionメソッドを使用することによって取得することができる

プロジェクトにどんなプロジェクト・ネーチャーが適用されているか,という情報についても,IProjectDescrptionオブジェクトが保持している。もう少し細かく言うと,プロジェクト・ネーチャーはそれを特定するためのIDが定義されるが,IProjectDescriptionオブジェクトは,プロジェクトに適用されているプロジェクト・ネーチャーのID文字列の配列を保持している

つまり,新規にプロジェクト・ネーチャーを適用したい場合は,適用したいプロジェクト・ネーチャーのIDを,IProjectDescriptionオブジェクトが保持しているIDの配列に追加してあげればよい。逆に,あるプロジェクト・ネーチャーの適用を解除したければ,IProjectDescriptionオブジェクトが保持しているIDの配列から,解除したいプロジェクト・ネーチャーのIDを削除すればよい。

例として,「プロジェクト・ネーチャーの定義方法」で新規に定義したyoichiro.myPlugin.myNatureというIDのプロジェクト・ネーチャーを,あるプロジェクトに適用するための手順を以下に示す。

  String newNatureId = "yoichiro.myPlugin.myNature";

  IProject project = ...;
  IProjectDescription description = project.getDescription();
  if (!description.hasNature(newNatureId)) {
    String[] ids = description.getNatureIds();
    String[] newIds = new String[ids.length + 1];
    System.arraycopy(ids, 0, newIds, 0, ids.length);
    newIds[ids.length] = newNatureId;
    description.setNatureIds(newIds);
    project.setDescription(description);
  }

重複登録を避けるため,最初にhasNatureメソッドを用いて既に登録されているかどうかをチェックしている。その後,getNatureIdsメソッドにより,適用されているプロジェクト・ネーチャーのIDの配列を取得し,その個数+1個の文字列配列を新規に作成,続いて配列の内容をコピーしている。そして,新規に生成した配列の最後の要素に,適用したいプロジェクト・ネーチャーのIDをセットしている

新規に適用するプロジェクト・ネーチャーのIDが追加された配列をsetNatureIdsメソッドに渡してIProjectDescriptionオブジェクトにセットし,IProjectDescriptionオブジェクトをIProjectオブジェクトのsetDescriptionメソッドに渡してセットして完了となる。setDescriptionして初めてプロジェクト・ネーチャーが適用される点に注意。

一度上記の処理を行ってプロジェクト・ネーチャーを適用すれば,そのプロジェクトを閉じようが,Eclipseを終了しようが,プロジェクトを開いたときにプロジェクト・ネーチャーの適用は継続される。なぜかというと,setDescriptionメソッドでIProjectDescriptionオブジェクトをセットしたと同時に,それが持つ情報が.projectファイルに書き込まれるからである。

  <?xml version="1.0" encoding="UTF-8"?>
  <projectDescription>
    <name>TestProject</name>
    <comment></comment>
    ...
    <natures>
      <nature>org.eclipse.jdt.core.javanature</nature>
      <nature>yoichiro.myPlugin.myNature</nature>
    </natures>
  </projectDescription>

さて,まだ疑問に思うことがあるだろう。プロジェクト・ネーチャーを適用するのは,一体いつなのか?これの明確な正解はなく,もちろん任意のタイミングで適用させることができる。しかし,一般的にはプロジェクトが作成されるタイミング,すなわち新規プロジェクト作成ウィザードの処理の中で行うことがほとんどである。

| | コメント (1) | トラックバック (0)

2004.03.15

プロジェクト・ネーチャーの定義方法

Eclipse(Platform,JDTおよびPDEが導入されている環境)では,以下のプロジェクトを作成できる。
  ・Java (Java Project)
  ・Plug-in Development (Plug-in Projectなど)
  ・Simple (Project)
Platformが標準で提供している単純なプロジェクト(Simple)に比べて,Java ProjectやPlug-in Projectはそれぞれ固有の機能拡張がなされている。Java ProjectであればJavaソースファイルを自動的にコンパイルしてくれるし,Plug-in ProjectはJava Projectに加えてプラグイン・マニフェストを自動的にチェックしてくれる(両方とも「ビルダー」と呼ぶ)。

あるプロジェクトに対して機能追加などの「特徴付け」を行うには,プロジェクト・ネーチャー(Project Nature)という機構を使用する。先ほどの機能拡張はプロジェクト・ネーチャーが行っているものであり,例えばJava NatureがJava Builder(自動コンパイル機能など)をプロジェクトに付加することで,プロジェクトをJavaプロジェクトとしている。

新規にプロジェクト・ネーチャーを定義するには,org.eclipse.core.resources.natures拡張ポイントを使用する

  <extension
      point="org.eclipse.core.resources.natures"
      id="myNature"
      name="My nature">
    <runtime>
      <run class="yoichiro.MyNature"/>
    </runtime>
  </extension>

id属性に新規プロジェクト・ネーチャーを識別するための文字列を,name属性に新規プロジェクト・ネーチャーの名前を記述する。このプロジェクト・ネーチャーのIDは,例のごとく「プラグインID+ネーチャーID」であり,上記のプラグイン・マニフェストがyoichiro.myPluginというIDのプラグインであれば,プロジェクト・ネーチャーのIDは「yoichiro.myPlugin.myNature」となる。プロジェクト・ネーチャーの実体は,runtime要素のrun子要素のclass属性を使って指定する

プロジェクト・ネーチャーの実体は,IProjectNatureインタフェースの実装クラスである。この実装クラス内で,プロジェクトに特徴付けを行う(ビルダーの登録処理がほとんど)。

  public class MyNature implements IProjectNature {
    private IProject project;
    public void setProject(IProject project) {
      this.project = project;
    }
    public IProject getProject() {
      return project;
    }
    public void configure() throws CoreException {
      // projectへ特徴付けを行う(ビルダーの登録など)
    }
    public void deconfigure() throws CoreException {
      // projectから特徴付けを解除する(ビルダーの削除など)
    }
  }

あるプロジェクトにこのプロジェクト・ネーチャーが登録されると,まずsetProjectメソッドに対象のプロジェクト(IProjectオブジェクト)が渡される(通常はフィールドに保持しておく)。そしてその後configureメソッドが呼び出される。このconfigureメソッド内で,保持しておいたprojectオブジェクトに対して,ビルダーの登録などの特徴付けの処理を行う

プロジェクトからプロジェクト・ネーチャーが解除されたときは,deconfigureメソッドが呼び出される。deconfigureメソッドでは,configureメソッド内で対象プロジェクトに登録したビルダーの登録解除など,特徴付けを解除する処理を行う

このように,プロジェクト・ネーチャーは,あるプロジェクトに対するライフサイクル(プロジェクト活性化の際はconfigureメソッドが呼ばれ,非活性化の際はdeconfigureメソッドが呼ばれる)を担当するということができる。

ここで,大きな疑問が浮かび上がる。「プロジェクト・ネーチャーをプロジェクトに関連付けるにはどうするの?いつやるの?」とお思いのことだろう。この話は後のお楽しみ,として今回はこれまで。

| | コメント (0) | トラックバック (0)

2004.03.11

型の仲間達(サブクラス,スーパークラス)の取得方法

型の発見方法で取得したITypeオブジェクト。それが表す型を起点として,そのスーパークラスやサブクラス(の列挙)を取得したいことが「ごくたまに」ある。例えば,junit.framework.TestCaseクラスのサブクラスの列挙を取得することによって,Javaプロジェクト内に存在するテストクラスの一覧を得る,などである。

ある型のスーパークラスやサブクラスを取得する,という処理は,対象となる型に強く依存した機能のはずなので,当然ITypeインタフェースに規定されていると思ってしまうが,ITypeインタフェースに直接関連のあるクラスを取得できるような機能は規定されていない。その代わりに,型の継承関係を表す階層構造への操作を規定したITypeHierarchyインタフェースが存在する

ある型を含む型の階層構造にアクセスしたいときは,ITypeインタフェースのnewTypeHierarchyメソッドを利用する。newTypeHierarchyメソッドにはいくつか種類があって,必要に応じて使い分ける。

  (1) newTypeHierarchy(IJavaProject project, IProgressMonitor monitor)
      - あるプロジェクトのクラスパス内に限定して,型の階層構造を構築する。
  (2) newTypeHierarchy(IProgressMonitor monitor)
      - ワークスペース内の全部の型を対象として,型の階層構造を構築する。

もうひとつnewTypeHierarchy(IWorkingCopy[] workingCopies, IProgressMonitor monitor)というメソッドもあるのだが,IWorkingCopyインタフェースがよくわからないので,用途不明。IWorkingCopyインタフェースのサブインタフェースとしてICompilationUnitインタフェース(Javaソースファイルを表す)があるので,特定のソース群を対象としてその中から型の階層構造を作り出すのかな,と勝手に想像するが,真相はわかりません。

newTypeHierarchyメソッドの結果のITypeHierarchyオブジェクトを得られれば,あとはgetAllSubtypesメソッドやgetAllSuperclassesメソッドを使って,サブクラスやスーパークラスのITypeオブジェクトの配列を得ることができる

  IJavaProject project = ...;
  IType baseType = ...; // 型の発見方法参照
  ITypeHierarchy hierarchy = baseType.newTypeHierarchy(project, null);
  // スーパークラス群の取得
  IType[] superclasses = hierarchy.getAllSuperclasses(baseType);
  // プロジェクト内を対象にしたサブクラス群の取得
  IType[] subclasses = hierarchy.getAllSubclasses(baseType);

メソッドの引数にIProgressMonitorオブジェクトがあることでもわかるように,newTypeHierarchyメソッドの実行には多くのコストが必要になる。型の継承関係を得るためには,特にサブクラスを見つけるためには,全ての型の情報を参照していかなければならないことは容易に想像つく。しかも,型の階層構造を求めなければならないことは,滅多にない。ITypeインタフェースから型の階層構造に関する処理をITypeHierarchyインタフェースに抜き出した理由は,利用頻度の少ない情報は極力ITypeインタフェースの実装に持たせないで軽くしておこうという配慮からだと思われる。

ちなみに,ある型のスーパークラス群だけを得たい(サブクラスはいらない)ときは,newSupertypeHierarchyメソッドを利用すべきである。このメソッドはnewTypeHierarchyメソッドよりも劇的にパフォーマンスはいいはず。なぜなら,スーパークラス群を求める処理は,対象となるJavaプロジェクト内に限定されるし,各型にはスーパークラスの指定が記載されているはずなので単純にその指定を追っていくだけで済むから,である。

| | コメント (0) | トラックバック (0)

2004.03.10

その型は誰の持ち物?

型の発見方法」で解説したITypeオブジェクトの取得方法だが,IJavaProject#findType()ではJavaプロジェクトのクラスパス全体から型を検索してしまう。つまり,ライブラリ(JARファイル)や,依存関係にある他プロジェクトのクラスがfindTypeメソッドの結果としてヒットしてしまうのである。あくまで対象のJavaプロジェクトで新規に作成された型のITypeオブジェクトを取得したいときにはどうしたら良いのだろうか?IJavaProjectオブジェクトからパッケージをたどってクラスを自分で見つけることもできなくもないが,せっかくあるIJavaProject#findType()を使って何とかしたい。

ITypeインタフェース(の親のIJavaElementインタフェース)に,getUnderlyingResourceメソッドがある。これは対象となるITypeオブジェクトを含んでいる最も根本的な(?)リソース(IResourceオブジェクト)を返してくれるメソッドである。このメソッドの結果を使うことによって,ITypeオブジェクトがどこに所属しているかを判断することができる。

例を見てみよう。

  [Javaプロジェクト]
    ・プロジェクトA - projectA
    ・プロジェクトB - projectB
      - 依存プロジェクト projectA
      - 依存ライブラリ hoge.jar

というプロジェクト構成で,この中のいずれかにyoichiro.Testクラスが存在するとする。そして,

  IJavaProject projectB = ...;
  IType type = projectB.findType("yoichiro.Test");
  IResource resource = type.getUnderlyingResource();

としてfindTypeメソッドの結果のITypeオブジェクトからgetUnderlyingResourceメソッドでIResourceオブジェクトを取得する。このresourceオブジェクトを以下のように判断することによって,存在場所を特定できる。

  (1) resourceオブジェクトがnullだった場合
      - 依存ライブラリに存在(例では依存ライブラリは1つなのでhoge.jar内)
  (2) resource.getProject().equals(projectB.getProject()) == true の場合
      - プロジェクトBに存在
  (3) resource.getProject().equals(projectB.getProject()) == false の場合
      - 依存プロジェクトに存在(例では依存プロジェクトは1つなのでプロジェクトA内)

IResourceクラスのgetProjectメソッドを使ってJavaプロジェクトの作成元のIProjectオブジェクトを取得し,ITypeオブジェクトから得たIProjectオブジェクトとIJavaProjectオブジェクトから得たIProjectオブジェクトが同一かどうかを判断している。上記の(1)と(2)を使えば,対象のプロジェクトで作成された型かどうかを判断することができる。

| | コメント (0) | トラックバック (0)

型の発見方法

Eclipse JDTでは,Javaの型(クラスやインタフェースなどのCompilation Unit)はITypeインタフェースで表現される。ITypeインタフェースはバイナリ(Javaクラスファイル)またはテキスト(Javaソースファイル)を指している。開発するプラグインの中で,型の情報を知りたいな,と思ったときは,その型のITypeオブジェクトを取得することが第一歩となる。

型は,Javaプロジェクト内またはプロジェクトに登録されているライブラリ(JARファイルなど)のどこかに存在する。つまり,ITypeオブジェクトを得るためには,Javaプロジェクトのオブジェクト,すなわちIJavaProjectインタフェースのオブジェクトをまずは得る必要がある(ビューアクションなどのElementオブジェクトから取得,などなど)。

IJavaProjectオブジェクトを得られれば,あとはfindTypeメソッドに得たい型のFQN(Fully Qualified Name:パッケージ名もちゃんとついた完全な名前)を渡せば,ITypeオブジェクトを得ることができる

  IJavaProject project = ...;
  IType type = project.findType("junit.framework.TestCase");

上記の例では,Javaプロジェクト内からJUnitのTestCaseクラスのITypeオブジェクトを探している。Javaプロジェクトのクラスパスにjunit.jarファイルが登録されていれば,TestCaseクラスのITypeオブジェクトを得ることができる。もちろんjunit.jarが登録されていなければTestCaseクラスを見つけることができないので,findTypeメソッドの結果はnullになる。

| | コメント (0) | トラックバック (0)

2004.03.05

APIリファレンスの抜き出し方法

EclipseのプラットフォームやJDTのAPIリファレンスは,HelpメニューのHelp Contentsから閲覧することができる。しかし,やっぱりAPIリファレンスの類は,普通にWebブラウザでいつでも閲覧できる状態にしておきたいものである。

Eclipseはプラグインの集合体であるので,Help Contentsで閲覧できるドキュメントはプラグインが提供してくれているものだと想像できる。実際そのとおりで,オンラインヘルプ用の拡張ポイントがあり,各種プラグインはその拡張ポイントを利用してオンラインドキュメントを提供している(オンラインヘルプの作成方法については,わかり次第紹介する予定)。オンラインドキュメントのファイル群は,docsディレクトリに配置するか,docsディレクトリを圧縮したdocs.zipというファイルに配置するかのいずれかである。

プラグインを開発するにあたり,最も頻繁に閲覧するものは,プラットフォームのAPIリファレンスと,JDTのAPIリファレンスだろう。これらのAPIリファレンスは,それぞれ以下のファイルに格納されている。

 ・プラットフォームのAPIリファレンス
    $ECLIPSE_HOME/plugins/org.eclipse.platform.doc.isv_2.1.0/docs.zip
    ZIPファイル内: /reference/api

 ・JDTのAPIリファレンス
    $ECLIPSE_HOME/plugins/org.eclipse.jdt.doc.isv_2.1.0/docs.zip
    ZIPファイル内: /reference/api

各ZIPファイルから,/reference/api以下を任意のディレクトリに展開すれば,その中にあるindex.htmlを閲覧することによって,各APIリファレンスをHelp Contentsを使わずに,Webで閲覧することができるようになる。

| | コメント (0) | トラックバック (0)

2004.03.04

マーカーレゾリューション

Eclipseの便利な機能に,Quick Fixがある。例えば,例外をcatchしなくちゃいけないのにそれをしなかった場合,コンパイルエラーの箇所で Ctrl + 1 キーを押すと「Add throws declaration」「Surround with try/catch」というポップアップメニューが表示され,「Surround with try/catch」を選べば自動的にtry-catchで括ってくれる,というのがQuick Fix機能である。

あるマーカー(特に何らかの問題を示すマーカー)に対して,マーカーを解決する(マーカーの原因となったコンパイルエラー箇所を自動修正してマーカーを削除できる状態にする,など)ための処理を自作し,何らかのマーカーに関連付けることができる。この,マーカーを解決するためのオブジェクトのことをマーカーレゾリューションと呼ぶ。マーカーレゾリューションは,Quick Fixの実装ということができる。

マーカーレゾリューションは,org.eclipse.ui.markerResolution拡張ポイントを利用して定義する

  <extension point="org.eclipse.ui.markerResolution">
    <markerResolutionGenerator
      markerType="yoichiro.myplugin.mymarker"
      class="yoichiro.MyMarkerResolutionGenerator">
      <attribute name="myattribute" value="myvalue"/>
    </markerResolution>
  </extension>

org.eclipse.ui.markerResolution拡張ポイントでは,マーカーレゾリューションの定義をmarkerResolutionGenerator要素を使って記述する。「なんでmarkerResolution要素じゃないの?」という疑問が沸くが,それは後述。

markerType属性で,対象マーカーのIDを記述する。そしてclass属性でマーカーレゾリューションの生成処理を持つクラスを指定する。

マーカーはそれぞれ属性(IMarker.LOCATIONなど)を持っているが,attribute要素を使うことによって,マーカーレゾリューションが有効になるための属性値を定義することができる。上記では,myattribute属性の値としてmyvalueを持つマーカーのみ有効,という指定を行っている。

さて,マーカーの解決方法は1つとは限らない。冒頭で紹介した「例外処理どうすんだエラー」では,「Add throws declaration」「Surround with try/catch」という2つの解決方法が存在した。つまり,なんでmarkerResolutionGeneratorという名前なのかというと,上記のclass属性で指定しているクラスは,あるマーカーに対する複数の解決処理を生成して返す処理を担当しているから,である。

class属性で指定するクラスは,IMarkerResolutionGeneratorインタフェースの実装クラスとして作成する。

  public class MyMarkerResolutionGenerator
      implements IMarkerResolutionGenerator {
    public IMarkerResolution[] getResolutions(IMarker marker) {
      IMarkerResolution resolution = new IMarkerResolution() {
        public String getLabel() {
          return "My resolution";
        }
        public void run(IMarker marker) {
          // 解決処理
        }
      };
      return new IMarkerResolution[]{resolution};
    }
  }

IMarkerResolutionGeneratorインタフェースの実装クラスでは,getResolutionsメソッドを実装しなければならない。getResolutionsメソッドの結果として,IMarkerResolutionインタフェースのオブジェクトの配列を返す。マーカーの解決処理は,IMarkerResolutionインタフェースの実装クラスとして作成する。つまり,マーカーの解決方法が複数ある場合は,それぞれIMarkerResolutionインタフェースの実装クラスとして作成し,それらのインスタンスの配列をgetResolutionsメソッドの戻りとして返す。上記ではIMarkerResolutionインタフェースの実装クラスを匿名クラスとして作成し,それを返却している。

IMarkerResolutionインタフェースの実装クラスでは,2つのメソッドを実装する必要がある。getLabelメソッドでは,マーカーレゾリューションを説明するための短い文字列(「Surround with try/catch」など)を返す。これが Ctrl + 1 キー押下時やQuick Fixメニュー選択時に表示されるポップアップメニューの選択肢の文字列になる。そして,その選択肢を選択した際に呼び出されるメソッドがrunメソッドである。runメソッド内に,マーカーを解決するための処理を記述する(解決しなくてもいいけど)。

このマーカーレゾリューションという機構は,マーカーが持つ属性の値をうまく利用することが求められるだろう。

| | コメント (0) | トラックバック (0)

2004.03.02

マーカーのアイコン

新規マーカーの作成」で定義したマーカーに,アイコンを登録することができる。

アイコンはもちろん自分で用意する必要あり。16x16のJPEGかGIFでイメージファイルを用意する。イメージファイルの配置場所は,
  $PLUGIN_HOME/icons
ディレクトリとするのが慣習らしい。

マーカーのアイコンは,「新規マーカーの作成」で紹介したorg.eclipse.core.resources.markers拡張ポイントの要素や属性で定義できるかと思いきや,そんなものはない。ではどうするのかというと,マーカーのアイコンはorg.eclipse.ui.markerImageProvider拡張ポイントを利用して定義する

  <extension point="org.eclipse.ui.markerImageProvider">
    <imageProvider
      markertype="yoichiro.plugin.mymarker"
      icon="icons/mymarker.gif"
      id="yoichiro.plugin.mymarker.icon"/>
  </extension>

org.eclipse.ui.markerImageProvider拡張ポイントでは,imageProvider要素を使ってアイコンの定義を行う。markertype属性には,対象とするマーカーのIDを,icon属性にはアイコンのイメージファイルをプラグインディレクトリからの相対パスで,id属性にはこのアイコンを特定するためのIDを記述する。これだけで,「リソースへのマーキング」を行って対象マーカーを作成した際に,エディタの左縦ルーラーにアイコンが表示されるようになる。

ちなみに,マーカーのアイコンを定義しただけで,「リソースへのマーキング」で問題にした「マーキングしても,エディタの左縦ルーラーにマーカーがすぐに描画されない」という問題は解決する。新規にマーカーを定義したときには,いっしょにアイコンも登録しておきましょう,ということです。

| | コメント (0) | トラックバック (0)

2004.03.01

新規マーカーの作成

自分なりの新しいマーカーを定義するには,プラグイン・マニフェストに新規マーカー拡張ポイントを利用して定義すればよい。記述内容は今までの中でもかなり単純な部類に入る。

  <extension point="org.eclipse.core.resources.markers"
      id="mymarker"
      name="My Marker">
    <super type="org.eclipse.core.resources.problemmarker"/>
    <super type="org.eclipse.core.resources.textmarker"/>
    <persistent value="true"/>
    <attribute name="myattribute"/>
  </extension>

新規マーカーは,org.eclipse.core.resources.markers拡張ポイントを利用する。id属性はマーカーを特定するためのIDを任意に指定するのだが,プレフィックスとしてプラグインのIDが付加される。例えば,上記の定義がyoichiro.mypluginというIDのプラグインのプラグイン・マニフェストに定義されれば,
  ・yoichiro.myplugin.mymarker
というIDになる。name属性には,このマーカーの名前を値として記述する。

新規マーカーは,他のマーカーを継承して定義する。多重継承が許されているので,上記のようにsuper要素を複数記述することができる。普通はIMarkerインタフェースに定義されている標準マーカーを継承すればよい。ほかには,JDTで定義されているマーカーを継承することもあるだろう。super要素のtype属性に,継承元のマーカーのIDを記述する

定義するマーカーが,一時的なものか,永続化(Eclipseを再起動してもマーカーが消えない)されるものかを,persistent要素で決定する。value属性にtrueを指定すれば永続化falseを指定すれば一時的なマーカーになる。

最後に,attribute要素を使って,マーカーが持つことができる属性を定義する。上記では,myattribute属性をmymarkerマーカーが持つことができると定義している。

これで新規マーカーの定義は完成。マーカーは特別何かクラスを作成する必要はないので,上記だけでOK。
  IResource resource = ...;
  resource.createMarker("yoichiro.myplugin.mymarker");
で,上記の新規マーカーのオブジェクトを作成することができる。

| | コメント (0) | トラックバック (0)

« 2004年2月 | トップページ | 2004年4月 »