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

2004.01.31

PDE JUnit Pluginのインストール

単体試験用のフレームワークとして爆発的に普及したJUnit。EclipseはJUnitが統合され,かなり高いレベルでテストファーストを実践することができるが,プラグインを開発する際にもJUnitを利用できたら嬉しいことこの上ない。

プラグイン開発でJUnitを使用することができるように,PDE JUnit Plug-inが提供されている。これは,Featureとして公開されているので,Update Managerを使ってインストールできる。PDE JUnit Plug-inは,Update Managerで以下のURLを指定することでインストールできる。

http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/jdt-ui-home/plugins/org.eclipse.jdt.junit/PDE-JUnit-Site

ちなみに,まだインストールしただけで,使い方はわかりません。。。

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

2004.01.30

拡張ポイント呼び出し時の例外対処

あるプラグインが自前で用意している拡張ポイント。拡張ポイントの自作(1)および拡張ポイントの自作(2)で解説した拡張ポイントの自作方法において,各プラグインにより提供されたクラスのメソッド呼び出しに対して,例外処理は何も行っていなかった。

例えば,あるIObserverインタフェースの実装クラスが以下のようなものだったとき,

  public BadObserver implements IObserver {
    public void update() {
      throw new NullPointerException();
    }
  }

実行時例外の発生により,一連の拡張ポイントへのメソッド呼び出しが途中で停止してしまう。拡張ポイントの定義側では,拡張ポイントの利用側がどんなコードを記述するのか想像できない(想像してはいけない)ので,(Errorはほっとくにしても)実行時例外を含む例外の発生について安全性を確保しておく必要がある。

  public void notify() {
    Iterator i = observers.iterator();
    while(i.hasNext()) {
      IObserver observer = (IObserver)i.next();
      observer.update(); ← (実行時)例外への対処が必要!
    }
  }

そこで,Eclipseでは上記のような例外処理を補佐するためのISafeRunnableインタフェースが提供されている。行いたい処理とそれについての例外処理をカプセル化することを目的としている。ISafeRunnableインタフェースの実装オブジェクトは,Platformクラスのrunメソッドにより実行することができる。このインタフェースを用いると,上記のコードは以下のように書き換えることができる。

  public void nofity() {
    final Iterator i = observers.iterator();
    while(i.hasNext()) {
      final IObserver observer = (IObserver)i.next();
      ISafeRunnable runnable = new ISafeRunnable() {
        public void run() throws Exception {
          observer.update();
        }
        public void handleException(Throwable exception) {
          exception.printStackTrace();
          observers.remove();
        }
      }
      Platform.run(runnable);
    }
  }

実行したい処理をrunメソッド内に記述する。このrunメソッドで何らかの例外が発生したときには,自動的にhandleExceptionメソッドが呼び出され,発生した例外が渡される。そして,handleExceptionメソッド内で,適切な例外処理を行うのである。これにより,IObserverオブジェクト1つに対して正常処理と例外処理がカプセル化され,他のIObserverオブジェクトの実行に影響が及ぶことはなくなる。ちなみに上記のコードでは,例外が発生したIObserverオブジェクトをコレクションから除外している。

ISafeRunnbaleインタフェースのデフォルト実装として,handleExceptionメソッドにエラーダイアログの表示処理が記述されたSafeRunnableクラスも用意されている。しかし,これは固定的なメッセージしか表示できないので,あまりお勧めできないかな。それにしても,IBMってほんと「handleException」好きだな。。。

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

2004.01.29

なぜcreateExecutableExtensionを用いるべきか?

拡張ポイントの自作(2)において,class属性で指定されたクラスのインスタンスを生成するにはIConfigurationElement#createExecutableExtension()を使用すべきだ,と述べた。それはなぜか?理由はとりあえず以下の2つがあげられる。

(1) Eclipseプラットフォームにより,クラスを提供したプラグインが活性化される。

(2) 引数で指定された属性の値のクラスがIExecutaleExtensionインタフェースを実装していた場合,setInitializationDataメソッドが自動的に呼び出され,初期化用の情報が渡される。

独自にclass属性値を取得し,forName().newInstance()してしまうと,上記の動作は行われなくなってしまう。もちろん上記の動作が必要なければ自前でインスタンスを生成してしまってもよいのだが,特に(2)による拡張性,柔軟性を切り捨ててしまうことになる。よって,createExecutableExtensionメソッドを使用しておいて損はないだろうし,将来のEclipseプラットフォームのバージョンアップに影響されることもなくなるはずである。

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

拡張ポイントの自作(2)

さて,拡張ポイントの自作(1)まででとりあえずの準備は完了。観測者,つまり拡張ポイントを使用して機能を追加する側のプラグインでは,以下のような記述が可能になる。

  <plugin id="yoichiro.observer" ...>
    <requires>
      <import plugin="yoichiro.subject"/>
    </requires>
    <extension point="yoichiro.subject.observers">
      <observer
        class="yoichiro.observer.ConcreteObserver"
      </observer>
    </extension>
  </plugin>

class属性で指定しているConcreteObserverクラスは,先ほどのIObserverインタフェースの実装クラスである。

さて被観測者は,上記のように各プラグインが拡張ポイントに登録してきた情報を取得し,class属性で指定されたクラスのインスタンスを生成して,IObserverインタフェースで規定したupdateメソッドを呼び出さなければならない。それは以下のコードで実現できる。

  private static final String observerId = "yoichiro.subject.observers";
  public void notify() {
    IPluginRegistry registry = Platform.getPluginRegistry();
    IExtensionPoint extensionPoint =
      registry.getExtensionPoint(observerId);
    IExtension[] extensions = extensionPoint.getExtensions();
    for (int i = 0; i < extensions.length; i++) {
      IConfigurationElement[] elements =
        extensions[i].getConfigurationElements();
      for (int j = 0; j < elements.length; j++) {
        if (elements[j].getName().equals("observer")) {
          try {
            Object observer =
              elements[j].createExecutableExtension("class");
            if (observer instanceof IObserver) {
              ((IObserver)observer).update();
            }
          } catch (CoreException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

PlatformクラスからgetPluginRegistryメソッドを使ってIPluginRegistryオブジェクトを取得する。そしてgetExtensionPointメソッドに拡張ポイントIDを渡すことにより,拡張ポイント自体の情報を持つIExtensionPointオブジェクトを取得する。これは,<extension-point>定義の情報を取得しているというイメージである。そして更にgetExtensionsメソッドを呼び出して,各種プラグインが拡張ポイントに提供してきた情報をIExtensionオブジェクトの配列として取得する。IExtensionオブジェクトは,extension要素全体の情報を持つとイメージすればよい。

個々のIExtensionオブジェクトに対して,getConfigurationElementsメソッドを呼び出し,extension要素の子要素群をIConfigurationElementオブジェクトの配列として取得する。今回は子要素としてobserver要素しか相手にしないので,getNameメソッドを使って要素名を取得し,observer要素かどうかを検査している。

さて,observer要素の情報を取得できたので,次はclass属性で指定されたクラスのインスタンスを生成し,updateメソッドを呼び出すのだが,安易に属性値を取得してClass.forName().newInstance()などを行うべきではない。ちゃんとEclipseが用意してくれている。それがcreateExecutableExtensionメソッド。これに属性名を渡すことで,属性値で指定されたクラスのインスタンスを生成し返却してくれる。あとはそれをIObserverインタフェース型にキャストし,updateメソッドを呼び出してめでたしめでたしとなる。

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

拡張ポイントの自作(1)

PropertyListenerやActionListenerに代表されるリスナーという仕組みはJavaの世界の中で数多く使われている。これはデザインパターン的にはObserverパターンであり,ある対象について,それぞれ異なる複数の観測者を動的に登録する仕組みである。ある対象は,予め定められたプロトコル(インタフェース)に則った観測者に対して通知を行うことができ,逆に言うと,ある対象は個々の観測者を全く知らなくてよい(プロトコルしか相手にしないから)。

拡張性という点ではObserverパターンも非常に強力な仕組みだが,Eclipseプラットフォームではプラグインという拡張機構を獲得するために,より強力な拡張機構を備えている。それが拡張ポイント(Extension-Point)だ。Observerパターンでは,被観測者に対して観測者を登録する作業はプログラム上で記述されるが,EclipseではXMLファイル(プラグイン・マニフェスト)により記述される。

具体的に見ていこう。まず,被観測者,つまり拡張ポイントの定義は,あるプラグイン・マニフェスト内で以下のように定義される。

  <plugin id="yoichiro.subject" ...>
    <extension-point
      id="observers"
      name="Test Observers"/>
  </plugin>

extension-point要素を使うことにより,拡張ポイントを自作することができる。通常拡張ポイントを指定するときには,「org.eclipse.ui.views」などのような感じになるが,extension-point要素のid属性では「observers」と短い名前になっている。実は拡張ポイントのIDは「プラグインID+拡張ポイントID」であり,上記では「yoichiro.subject.observers」という拡張ポイントになるのだ。name属性は,適当に名前をつけよう。

さらにschema属性を使って,extension要素の子要素の構造を規定することができるが,それは必須ではない。ここでは,observer要素をextension要素の子要素として持ち,それが持つclass属性により観測者の具象クラスが指定される,と仮定する。

次に,観測者が実装しなければならないプロトコル,つまりインタフェースを規定する。

  package yoichiro.subject;
  public interface IObserver {
    public void update();
  }

((2)に続く。。。)

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

3日間記事が作成できなかったのは・・・

一人寂しく猪苗代に2泊3日でスキーに行っていたからです。

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

2004.01.25

アクションの対象オブジェクト(Element)の取得方法

コンテキストメニューへのアクションの追加で,ビュー上で対象オブジェクトに対するアクションの追加方法を述べた。このアクションが実行されたときの対象オブジェクト(Elementというらしい)をどのように取得するのか?これは,IStructuredSelectionインタフェースがキーとなる。

アクションの実装となるクラスは,コンテキストメニューの場合はIObjectActionDelegateインタフェースの実装クラスとなる。で,対象オブジェクトが選択されたとき(遅延ロードのため,2.1.2の場合1回目はアクションが実行されたとき)に,対象オブジェクトがselectionChangedメソッドに渡されてくる

さて,第二引数のISelectionインタフェースの引数に渡されてくるのだが,対象オブジェクトをどのように取り出せばよいのだろうか?Elementを伴う選択オブジェクトは,IStructuredSelectionインタフェースの実装オブジェクトとして渡されてくる。つまり,引数をIStructuredSelectionインタフェースにキャストする。

  public void selectionChanged(IAction action, ISelection selection) {
    IStructuredSelection structured = (IStucturedSelection)selection;
  }

選択された対象オブジェクトは,一つとは限らない。これはプラグイン・マニフェストで記述されたenablesFor属性値とユーザの選択操作に依存する。IStructuredSelectionインタフェースとは,選択された複数の対象オブジェクトを取り出す方法が規定されたインタフェースなのである。

もしenablesFor属性値が1であれば,
  Object targetElement = structured.getFirstElement();
で取り出せばよい。また,enablesFor属性値によって複数個選択が許されているのであれば,
  IStructuredSelection#size() → 個数の取得
  IStructuredSelection#iterator() → 走査用のイテレータの取得
  IStructuredSelection#toArray() → オブジェクトの配列として取得
  IStructuredSelection#toList() → Listコレクションとして取得
を利用して対象オブジェクト群を取り出すことができる。

アクション実行時に呼び出されるrunメソッドに対象オブジェクトは渡されてこないので,runメソッド内で対象オブジェクトを扱わなければならないときは,予めselectionChangedメソッドが呼び出されたときにselection引数をインスタンスフィールドに保持しておく,という工夫が必要になる。

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

2004.01.24

コンテキストメニューへのアクションの追加

Package ExplorerやOutlineビューなどのコンテキストメニュー(右クリックで表示されるポップアップメニュー)に独自のアクションを追加する方法です。これは,拡張ポイントの違いと,実装に必要なインタフェースの違いを除けば,ツールバーへのアクションの追加とほぼ同じ手順。では早速プラグイン・マニフェストから見ていく。

  <extension point="org.eclipse.ui.popupMenus">
    <objectContribution
        id="yoichiro.hello"
        objectClass="org.eclipse.jdt.core.IType">
      <action
        id="yoichiro.hello.action"
        label="Hello World"
        enablesFor="1"
        class="yoichiro.hello.HelloAction">
      </action>
    </objectContribution>
  </extension>

拡張ポイントはorg.eclipse.ui.popupMenusを使用する。この拡張ポイントで提供されるアクションは,対象となるオブジェクトごとに定義することができる。つまり「オブジェクトへの貢献」って意味で,objectContribution要素を使う。その要素のobjectClass属性で,対象とするオブジェクトの型を決定する。上記で指定している「~.IType」は,Javaのクラスおよびインタフェースを表す要素(class-icon.gifアイコン)が対象となる。

そしてアクションを定義するaction要素を記述するが,これはほとんどツールバーへのアクションの追加と同じ。ただし,toolbarPath属性はもちろん必要なく,代わりにenablesFor属性が記述されている。これは,対象のオブジェクトがいくつ選択されているときにこのアクションが有効になるかどうかを定義する属性。ここでは対象のオブジェクトが一つ選択されているときのみアクションを有効としている。

さて,class属性で指定されているアクションの実装のクラスだが,これはIObjectActionDelegateインタフェースの実装クラスである。

  public class HelloAction
      implements IObjectActionDelegate {
    public HelloAction() {
      super();
    }
    public void setActivePart(
        IAction action, IWorkbenchPart targetPart) {}
    public void run(IAction action) {}
    public void selectionChanged(ISelection selection) {}
  }

runメソッドにアクションの実際の処理を記述する。ここで注目なのはsetActivePartメソッド。これには,アクションが発生したパート(一般的にビュー)が渡されてくる。例えば,Package Explorer上でアクションが実行されれば,
  targetPart.getTitle() → "Package Explorer"
となる。

ツールバーへのアクションの追加よりは,このコンテキストメニューへのアクションの追加の方が利用頻度は多いと思われます。objectClass属性で指定する型はビュー内の要素の型だけど,これはビューの作成の仕方がわからないとちょっとピンとこない。ビューとその中で表示する要素の自作方法を早く知りたいところだ。。。

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

2004.01.23

ツールバーへのトグルボタンの追加

ツールバーへのアクションの追加でツールバーへのボタンの追加方法を説明したが,このボタンをトグルボタン(凹んだままになるボタン)に簡単に変更することができる。これはプラグイン・マニフェスト内のaction要素に,「style="toggle"」を追加するだけ。

  <action
    label="Hello"
    id="yoichiro.hello.action"
    toolbarPath="helloGroup"
    class="yoichiro.hello.HelloAction"
    style="toggle">
  </action>

これだけでトグルボタンになる。とても簡単。で,ボタンが押し込まれたのか,元にもどされたのかは,runメソッドの引数にわたってくるIActionオブジェクトのisCheckedメソッドで取得することができる。

  public class HelloAction
      implements IWorkbenchWindowActionDelegate {
    ...
    public void run(IAction action) {
      if (action.isChecked()) {
        // 押し込まれたときの処理
      } else {
        // 元にもどされたときの処理
      }
    }
    ...
  }

○○モードON!みたいな,機能のON/OFFとかに使うと思われるボタンです。

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

ツールバーへのアクションの追加

基本に立ち戻り,「Contributing to eclipse」に沿って学習しなおすことにしたEclipseプラグイン開発。最初のお題は,「Helloアクション」。ツールバーにボタンを追加するという単純なプラグイン。

アクションはアクションセットの中で定義されるので,まずプラグインのマニフェスト(plugin.xml)の中にactionSet要素を準備する。そしてactionSet要素の中に,action要素を記述してアクションを定義する。以下のような感じ。

  <extension point="org.eclipse.ui.actionSets">
    <actionSet
        label="Hello Action Set"
        id="yoichiro.hello.actionSet">
      <action
        label="Hello"
        id="yoichiro.hello.action"
        toolbarPath="helloGroup"
        class="yoichiro.hello.HelloAction">
      </action>
    </actionSet>
  </extension>

上記をプラグインのマニフェストのplugin要素内に記述するだけで,一応プラグインとして認識され,ちゃんとツールバーにボタンが出現する(アイコンを指定していないので,デフォルトの赤ポッチのちっちゃなボタンになる)。この際,「Window>Customize Perspective」メニューを使って,Other内にあるHello Action Set にチェックを入れないと,ツールバーにボタンが表示されない(チェックを入れることを活性化,チェックを外すことを非活性化とここでは呼ぶ)。toolbarPath属性を使って,ツールバー上に新たにhelloGroupグループを作成している。これにより,ツールバー内にアクションがボタンとして表示されるようになる。

アクションの定義は上記でOK。次はアクションの実装となるクラスであるが,これはIWorkbenchWindowActionDelegateインタフェースの実装クラスでなければならない。クラスは以下のような感じ。

  public class HelloAction
      implements IWorkbenchWindowActionDelegate {
    public HelloAction() {}
    public void init(IWorkbenchWindow window) {}
    public void run(IAction action) {}
    public void dispose() {}
    public void selectionChanged(IAction action, ISelection selection) {}
  }

アクションセットが活性化されてツールバーにボタンが表示されただけでは,上記のアクションのインスタンスは生成されない。ボタンを押して初めてインスタンスが生成される。・・・と本には書いてあるんだけど,それは最初の一回目だけで,アクションセットを非活性化し,再度活性化すると,その直後にインスタンスが作られている。まぁ,この動作の違いはプラグインを自作するときにはそんなに気にならないだろう。

ボタンが押されたときに実行したい処理は,runメソッド内に記述すればよい。

ちなみに,パースペクティブを切り替えたりするとアクションはdisposeメソッドが呼ばれて破棄され,元のパースペクティブに戻ってくるとインスタンスが再度作り直される,といった動きをするので,もちろんアクションクラスにインスタンス変数を作って状態を覚えるといったことはご法度です。

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

Eclipseを覗き込むSpiderプラグイン

「Contributing to eclipse」に,面白いプラグインが紹介されていた。その名は「Spider」。Featureとして提供されているので,
  www.javaspider.org
を指定すれば「Help>Software Updates>Update Manager」からインストールできる。

これを使えば,
spider.gif
という感じでEclipse内のオブジェクトを覗き込むことができます。

プラグインを作るときは何かとEclipse内のオブジェクトの構成を気にするもの。他のプラグイン内で利用されているオブジェクト構成を覗き込むこともできるので,Spiderはプラグイン開発にもってこいのプラグインです。

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

2004.01.21

基本が大事

テキストエディタを中心に独学で習得しようと今までやってきたけど,最近どうも行き詰まりを感じてならない。理解できることと,ほとんど理解できていないもの,これらがテキストエディタに関しては見えてきたからだ。ただし,理解していることと言っても,コード上こうすれば実現できるっていうのを把握しているだけで,各インタフェースの役割や,各クラスが行っていることなどを説明できるわけではない。つまり,人に説明できることはほとんど無いに等しい。
ここで,テキストエディタを離れて,昨日届いた「Contributing to eclipse」を頑張って読んで,しっかりとEclipseの仕組みや考え方を把握していこうと思う。ソースが全てだ!という人もいるけど,やはりセオリーがないと,プラグインを作ったとしても不安が残るし,Eclipseがバージョンアップしたら動かなくなっちゃった!ではしゃれにならない。
しばらくTipsみたいな記事はここには書けないかもしれないけど,本から得た知識は順次掲載していこうと思う。何事も,基本が大事のはずだから。。。

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

2004.01.20

洋書到着!Part2

紀伊国屋から,注文していた洋書の2冊目も到着。「Contributing to eclipse」です。ぱっと見たけど,こっちの本の方が,かなり親切に説明されてるみたい。ただ,eclipseの仕組みを解説した話がほとんどなので,どっちの本から読めばいいのか悩んでしまう。。。「Contributing to eclipse」の方が厚さが薄いので,電車の中で読むならこっちかな。

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

2004.01.19

洋書到着!

注文していた洋書の1冊目がやっと到着!最初に来たのは,「Java Develpoer's Guide to Eclipse」の方。結構分厚い本で,3分の2はプラグイン関連の話となっているので,なかなか良さそう。
しばらくは英語との戦いです。。。

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

2004.01.18

テキストエディタのワードラップをONにするには?

Eclipseを使っていて,気に食わない点が一つある。それは「テキストエディタにワードラップ機能がない」という点だ。今までEmacsやPeggyPro for Oracle,Danaや秀丸,JBuilderやVisual Age for Java,Visual Cafe(懐かしい!)など,数多くのエディタでJavaのソースコードを書いてきたが,どれもワードラップ(1行の文字数がエディタの幅を超えた場合に自動改行する)機能が搭載されていて,必ずワードラップを効かせていた。僕は横スクロールが嫌いなのだ。

でも,いくらメニューや設定ダイアログを探しても,Eclipse内でワードラップに関する設定が見つからない(知ってる人がいたら,ぜひ教えてください)。しかし,テキストエディタで使われているであろうSWTのコンポーネントに,まさかワードラップ機能が搭載されていないなんてわけはないよね!?と勝手に思い込み,せめて自作のエディタではワードラップができるようにしたいと思って,試してみました。

Eclipseのテキストエディタは内部にSourceViewerオブジェクトを抱えていて,そのSourceViewerオブジェクトは実際のテキストエディタコンポーネントとしてSWTのStyledTextオブジェクトが使用されています。このStyledTextクラスに,しっかりとワードラップの機能が搭載されていました。つまり,TextEditorオブジェクトからどんどん辿ってStyledTextオブジェクトが取得できれば,それに対してワードラップを有効にすることで,ちゃんと自動改行されるようになります。これは以下のコードで実現できます。

  class MyTextEditor extends TextEditor {
    protected ISourceViewer createSourceViewer(
          Composite parent, IVerticalRuler ruler, int styles) {
      ISourceViewer sourceViewer =
        super.createSourceViewer(parent, ruler, styles);
      StyledText styledText = sourceViewer.getTextWidget();
      styledText.setWordWrap(true);
      return sourceViewer;
    }
  }

テキストエディタでは,SourceViewerオブジェクトを自由に入れ替えられるように,createSourceViewerメソッドが用意されている。このメソッドをオーバーライドし,TextEditorクラスのデフォルト実装で生成されたSourceViewerオブジェクトからgetTextWidgetメソッドでStyledTextオブジェクトを取り出し,それに対してsetWordWrapメソッドにtureを渡して呼び出すことでワードラップ機能をONにできます。

SourceViewerオブジェクトは,AbstractTextEditor#getSourceViewer()でも取得することができる。しかし,コンストラクタ,initメソッド,initializeEditorメソッド,setSourceViewerConfigurationメソッドで試してみたが,どれもgetSourceViewerメソッドはnullを返してきた。AWTのpeerの確定がオブジェクトの生成時ではないように,SourceViewerオブジェクトの生成もエディタがワークベンチにaddPageされるときに行われるらしく,createSourceViewerメソッドで行うのが正しいかな,と思われる。

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

2004.01.17

テキストエディタからのIEditorInput,IDocumentオブジェクトの取得

テキストエディタにより編集される対象はIEditorInputインタフェースにより表され,それはドキュメントプロバイダによってIDocumentインタフェースにより表されるドキュメントに変換される。通常編集対象はテキストファイルなので,FileEditorInputオブジェクトが利用される。

あるテキストエディタにより編集されているドキュメントのオブジェクトは,以下のようにして取得できる。

  TextEditor editor = ...;
  IEditorInput editorInput = editor.getEditorInput();
  IDocument doc = editor.getDocumentProvider().getDocument(editorInput);

上記の処理を持つIDocumentPartitioningListenerオブジェクトをドキュメントにセットして動作を確認したところ,テキストエディタから得られるIEditorInputオブジェクト,およびそれから得られるIDocumentオブジェクトは,テキストが編集されたとしても変化はない(得られるオブジェクトは一緒)。

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

カウンター設置!

普段「Header-Reader」っていうRSSリーダーでココログの最新記事をチェックしているが,さっき「埼玉住人」さんのココログでアクセスカウンターの設置方法が解説されているのを発見!早速ここにもつけてみました。
同じ埼玉県民として誇らしいです!埼玉住人さん,ありがとう!

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

2004.01.16

紀伊国屋からの連絡Part2

家に帰ってきたら,紀伊国屋からまたまたメールが届いていた。「Contributing to Eclipse: Principles, Patterns, and Plug-Ins」が2,3日以内に紀伊国屋に入荷するとのこと。この本こそ,多くの謎を解き明かしてくれる超期待の一冊なので,とにかく早く手元に届いて欲しいです。。。

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

2004.01.15

Plugin Developer Guideだけの日本語化

お正月に必死に読んだドキュメント。もちろん英語なので翻訳ソフトまで買って読んでいたのに,Eclipseの国際化パックを入れると,いとも簡単に「Platform Plugin Developer Guide」が日本語で読めちゃう。なんだったんだ,あの苦労は。。。

ただ,国際化パックをまるごと入れちゃうと,何もかも日本語になってしまうので都合が悪い。画面で使われている英単語から,プラグイン開発に必要なクラスを探し出すこともあるからだ。何とかして,「Platform Plugin Developer Guide」だけ日本語にしたい。

これはとても簡単で,国際化パックのzipファイルの中から,
  plugins/org.eclipse.platform.doc.isv.nl1_2.1.2
だけを,Eclipseインストールディレクトリにあるpluginsディレクトリにコピれば良い。望みどおり,「Platform Plugin Developer Guide」だけが日本語化されます。

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

アウトラインページの表示更新タイミング

EclipseでJavaのソースコードを記述しているとき,そのソースコードの構造がアウトラインページに自動的に表示される。エディタで次々とソースコードを入力すれば,保存することなくアウトラインページ内の情報は更新されていく。

でも,サンプルプログラムのインストール方法で手に入るプラグインの例のどれを見ても,リアルタイムに同期が取られるわけではなく,ファイルを保存したときにアウトラインページの内容が更新されるようになっている。

ReadmeEditorプラグインやExampleJavaEditorプラグインでは,アウトラインページのオブジェクトに対してTextEditorオブジェクトからgetEditorInputメソッドで取り出したIEditorInputオブジェクトを渡している。そしてDocumentProviderオブジェクトを使ってIEditorInputオブジェクトからIDocumentオブジェクトを取得している。

アウトラインページの表示内容の更新は,ReadmeEditorプラグインもExampleJavaEditorプラグインも,TextEditorクラスのサブクラス内でオーバーライドしたdoSaveメソッドで,

  IContentOutlinePage page;
  public void doSave(IProgressMonitor monitor) {
    super.doSave(monitor);
    if (page != null)
      page.update();
    }
  }

っていようにアウトラインページに更新をかけにいっている。ただ,これだとあんまりよくない。だって,Ctrl+Sを頻繁に押さないと更新されないんじゃ。。。ね。

そこで,JDTに搭載されているJavaEditorはどうやってるんだろうと思い,pluginsディレクトリ内のjdt.uiの中にあるjdt.jarを展開し,せっせと逆コンパイル♪見てみると,IEditorInputオブジェクトをアウトラインページクラスにセットしているのは,Exampleプラグインと変わらないみたい。ただし,DocumentProviderのなかでファイル⇔エディタ⇔アウトライン間の同期を取る処理をやっているみたいなんだけど,ぱっと見じゃさっぱりわからない。。。

特にアウトラインページがIEditorInputオブジェクトの利用を規定しているわけでもないし,アウトラインページの更新タイミングは自前でガツガツ作りこんでもいい気がする。ただ,IEditorInputオブジェクトはちゃんと使えるようになっていないと後々まずそう。。。この辺はもうちょいで到着するであろう洋書に期待するとしよう。

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

紀伊国屋からの連絡

今日紀伊国屋からメールがきた。注文していた洋書「Java Developer's Guide to Eclipse」が2,3日後に入荷するらしい,という報告だった。早ければ,来週の火曜日には手にすることができるかな?

楽しみ♪です。

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

空のアウトラインページの作成

今までテキストエディタ,特にドキュメント関連について調べてきたけど,行き詰まりを感じつつあるのでちょっと浮気することにした。エディタの次に手を出したのは,アウトラインページ。テキストエディタ内のドキュメントの構造を表示するためのページですね。

アウトラインページはIContentOutlinePageインタフェースを実装したクラスで表される。一般的にドキュメントの構造は木構造となることがほとんどなので,EclipseはjfaceのコンポーネントのTreeViewerコンポーネントを標準で搭載したContentOutlinePageクラスを用意してくれている。

早速アウトラインページのクラスを作成する。

  class MyOutlinePage extends ContentOutlinePage {
  }

これだけ。特になにするわけでもないので,ここではメソッドは何も用意しません。

さて,これをエディタと関連付けてアウトラインページを作り出さなければならない。ここで登場するのが,IAdaptableとは?(後編)で登場したIAdaptableインタフェース。TextEditorクラスのサブクラスでgetAdapterメソッドをオーバーライドし,その中で以下のようにしてアウトラインページを作成して返してあげる。

  class MyTextEditor extends TextEditor {
    private MyOutlinePage outlinePage;
    public Object getAdapter(Class adapter) {
      if (IContentOutlinePage.class.equals(adapter)) {
        if (outlinePage == null) {
          outlinePage = new MyOutlinePage();
        }
        return outlinePage;
      }
      return super.getAdapter(adapter);
    }
  }

このMyTextEditorが活性化されるとき,Eclipseプラットフォームは「おまえんとこのエディタなんだけど,アウトラインページはどうすんだい?」っていう感じで,IContentOutlinePageクラスオブジェクトをgetAdapterメソッドに渡して呼び出してきます。そこで,アウトラインページのオブジェクトを生成して,結果として返してあげます。すると,Eclipseプラットフォームは受け取ったアウトラインページをアウトラインビューに貼り付けます。

上記のコードによって,アウトラインビューに今までグレーの背景で「An outline is not available.」と表示されていたのが,白紙の状態になります。何もメソッドを持たないMyOutlinePageオブジェクトが貼りついた証拠です。

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

2004.01.14

ドキュメントを区画分けするスキャナの作成

そういえばドキュメントを区画(パーティション)分けするためのスキャナクラスの作成方法を書き残していなかった気がするので,ここに記しておこう。

ドキュメントを区画分けするためのクラスは,IDocumentPartitionerインタフェースの実装クラスである。これは,多くの場合はDefaultPartitionerクラスで事足りるだろう。このDefaultPartitionerクラスでは,実際にどのようにドキュメントを区画分けするかはIDocumentTokenScannerインタフェースの実装クラスに委ねられる

IDocumentTokenScannerインタフェースの場合も例に漏れず,しっかりと便利な実装クラスをEclipseが用意してくれている。それがRuleBasedPartitionScannerクラスだ。基本的な使い方は簡単で,区画を決めるルールオブジェクトの配列をsetPredicateRulesメソッドにセットするだけ

  class MyPartitionScanner extends RuleBasedPartitionScanner {
    static final String JAVA_DOC = "__my_javadoc";
    static final String JAVA_COMMENT = "__my_comment";
    MyPartitionScanner() {
      IToken javaDoc = new Token(JAVA_DOC);
      IToken javaComment = new Token(JAVA_COMMENT);
      IPredicateRule[] rules = new IPredicateRule[2];
      rules[0] = new MultiLineRule("/**", "*/", javaDoc, (char)0, true);
      rules[1] = new MultiLineRule("/*", "*/", javaComment, (char)0, true);
      setPredicateRules(rules);
    }
  }

上記のコードでは,Javaのソースコードを,
  (1) Javadocコメントの部分(JAVA_DOCコンテントタイプ)
  (2) 複数行コメントの部分(JAVA_COMMENTコンテントタイプ)
  (3) それ以外(IDocument.DEFAULT_CONTENT_TYPEコンテントタイプ)
というように区画分けしています。(char)0っていう引数は,エスケープしなくちゃいけない文字は特にないので,とりあえずnullの代わりって感じのものです。

このクラスは,RuleBasedPartitionScannerはIRuleじゃだめ!でも登場したクラスで,そのときに書いた内容は,ここでセットできるルールオブジェクトはIRuleインタフェースの実装じゃダメで,IPredicateRuleインタフェースの実装が必要なんだよってことを書いています。

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

サンプルプラグインのインストール方法

Eclipse.orgが提供してくれているプラグインの例がいくつか存在する。それらのインストール方法は,下記のサイトに記載されています。

Examples - Installation

Featureとしてサイトから自動インストールするも良し,ダウンロードして手動でインストールするも良し。

もちろんソースコードもついてきます。あ,Javaの場合はソースコードの有無はさほど重要じゃないですね。。。

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

RuleBasedPartitionScannerはIRuleじゃだめ!

テキストエディタのドキュメントを区画分けするためのIDocumentPartitionerインタフェース。そのデフォルト実装がDefaultPartitionerクラスなのだが,実際にドキュメントを区画分けするためのルールを処理するためにはIPartitionTokenScannerインタフェースを実装したクラスが必要になる。

ただし,IPartitionTokenScannerインタフェースの実装クラスを自作するんじゃなく,通常はRuleBasedPartitionScannerクラスを使用することが多いでしょう。このRuleBasedPartitionScannerクラスはRuleBasedScannerクラスを継承しているので,区画を分けるためのIRuleインタフェースの実装クラスを作って,setRules(IRule[] rules)メソッドに渡してあげれば,ドキュメントの区画分けは完璧だぜ!と思って実行してみたら・・・Eclipseが「おまえのエディタを作るのに失敗しちゃったよ,だめね」って拒否されちゃう。。。

実はRuleBasedPartitionScannerクラスの中で,setRulesメソッドはオーバーライドされています。しかも,その処理は,
  throw new UnsupportedOperationException();
ときたもんだ!つまり,RuleBasedParitionScannerクラスはIRuleインタフェースを受け付けてくれません

その代わり,これなんだろうな~と思いながら触れないようにしていた,IPredicateRuleインタフェースの実装クラスが必要となります。つまり,setRulesメソッドの代わりにsetPredicateRules(IPredicateRule[] rules)メソッドを使用しなければなりません。

Predicate・・・述語?うーん,よくわかりません。PatternRuleクラスがIPredicateRuleインタフェースを実装しているので,MultiLineRuleクラスとかEndOfLineRuleクラスとかを使用している分には気にしなくてもよさそう。でも,自作のルールクラスを作らないといけないような場面では,IPredicateRuleインタフェースの役割をちゃんと知ってないとダメそう。

また一つ,IPredicateRuleっていう壁がきてしまいました。。。

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

2004.01.12

ドキュメント区画の取得

ドキュメントパーティショナーによって区画分けされたドキュメントから,そのドキュメントの区画を取り出す方法です。

  IDocument document = ...;
  int length = document.getLength();
  ITypedRegion[] regisons = document.computePartitioning(0, length);

最初にドキュメント全体の長さを取得して,computePartitioningメソッドを使って型付き区画の配列を得ています。その際,オフセット(開始位置)を0とし,長さをlengthつまりドキュメント全体としています。

あとは,取得した配列をぐるぐる回して,以下の内容を利用します。

  regions[n].getType() - 区画のコンテンツタイプ
  regions[n].getOffset() - 区画の開始位置
  regions[n].getLength() - 区画の長さ

この際,もし区画に改行文字が含まれる場合は,Windowsの場合はCR+LFで2文字が長さに含まれます。
さらに,区画の実際の文字列を得たければ,

  String text = document.get(regions[n].getOffset(), regions[n].getLength());

とすべし。

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

IDocumentPartitionerに文書を接続するのを忘れずに・・・

IDocumentPartitioningListenerインタフェースの動作を確認するために作っていたプログラムがなぜか動かない。テキストエディタに文字を入力すると,

  Unhandled exception caught in event loop.
  Reason:
  java.lang.NullPointerException

と表示され,文字が入力できない。。。しかも,あろうことか「ぬるぽ」である。

ちゃんとドキュメントにPartitionScannerをセットしてるし,Nullになりそうなものも見当たらない。あ!区画に対応したDamagerやRepairerが登録されていないからかな?と思い,SourceViewerConfigurationのサブクラスを作って,区画に対応したDamagerとRepairerを登録する。でも症状は変わらない。

実は,ドキュメントパーティショナーを作ってドキュメントにセットしただけでは足りないのだ。その後に,ドキュメントパーティショナーに対して文書を接続する必要がある

  IDocument document = ...;
  IDocumentPartitioner partitioner = ...;
  document.setDocumentPartitioner(partitioner);
  partitioner.connect(document);

なぜ相互参照してしまうようなことをしないといけないのかは不明。まぁ,おまじない程度で今は考えておくことにしよう。

自前のドキュメントパーティショナーをセットしてMy区画を作ったにも関わらず,それに対する処理を持つSourceViewerConfigurationオブジェクトをセットしなかった場合,文字入力ができなくなってしまいました。。。

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

ドキュメント区画の変更通知を受け取るには

テキストエディタ内のドキュメントが変更されたことを知るためには,IDocumentListenerインタフェースを用いればよい。このIDocumentListenerインタフェースの場合,ドキュメントが1文字でも変更されれば通知が飛んでくる。もちろんドキュメントの変更に伴う処理を行うには,このリスナーだけで事足りそうである。

ドキュメントはContentTypeごとに区画(パーティション)分けすることができる。例えばパーティションがアウトラインに対応して 分割されていれば,アウトラインビューの表示の更新はパーティションの構成が変更されたときに行えばよいことになる。

ドキュメントのパーティション分割が変更されたことを知るには,IDocumentPartitioningListenerインタフェースを使用すればよい。例えばこんな感じ。

  IDocument document = ...;
  document.addDocumentPartitioningListener(new IDocumentPartitioningListener() {
    public void documentPartitioningChanged(IDocument document) {
      ...
    }
  });

もちろん,ドキュメントがパーティションスキャナにより区画分けされていないと,上記リスナーは役に立ちません。。。

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

IDocumentListenerインタフェース

テキストエディタで文字入力などによりドキュメントに変更が生じたとき,もちろんアウトラインの変更が生じる可能性がある。つまり,IDocumentオブジェクトの内容の変更の通知をアウトラインビューが受け取れるようにすれば,エディタとアウトラインビューの同期が取れるはず,である。

ドキュメントの変更通知を受け取るために,IDocumentListenerインタフェースを用いる。例えば,こんな感じ。

  IDocument document = ...;
  document.addDocumentListener(new IDocumentListener() {
    public void documentAboutToBeChanged(DocumentEvent event) {
      ...
    }
    public void documentChanged(DocumentEvent event) {
      ...
    }
  }

各メソッドにMessageDialogでダイアログ表示を埋め込んで処理をブロックしたときに見た動作では,documentAboutToBeChangedメソッドはドキュメントの変更が発生した直後,つまり画面まだ変更が反映される前に呼び出されている。それに比べてdocumentChangedメソッドはドキュメントの変更が画面に反映された後に呼び出されている。もしかしたら,documentAboutToBeChanegdメソッド内で,ドキュメントへの変更を取り消したりできるのかな?わからないけど。。。ちなみに,イベントの内容はどっちのメソッドでも同じでした。

ここで解せない点が一つ。エディタで2行分を選択し,削除を行ってみると,documentAboutToBeChangedメソッドの呼び出し時には変更が反映されていないかと思いきや,2行目だけが削除された状態になってしまう。いまいち2つのメソッドの違いがわからないけど,基本的にはdocumentChangedメソッドを使用することが多いと思う。

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

MessageDialogで簡単ダイアログ

アクティブなShellオブジェクトを取得できるようになったので,JOptionPaneちっくなダイアログをEclipseで実現するMessageDialogクラスを手軽に利用できるようになった。

  MessageDialog.openInformation(
    Display.getCurrent().getActiveShell(),
    "タイトル",
    "本文");

他にも,openConfirm,openError,openQuestion,openWarningっていうのがあって,各種ダイアログを簡単に表示できるみたい。MessageDialogクラスを拡張することもできるっぽいので,今後使うこともあるだろう。

ちなみに,親にnullを渡したときにどうなるか?は,やってみればわかるけど,アクティブなShellを渡したときとほぼ同じ動作をする。つまり,親としてShellを渡さなくても,ワークベンチのウィンドウに対するイベントはちゃんとブロックされるみたい。ただし,親子関係があるときには親のウィンドウをクリックしたときにダイアログのタイトルバーが点滅する(Windowsのときの動作)けど,nullを渡したときには親子関係がないためにダイアログのタイトルバーは点滅されなかった。

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

Shellオブジェクトの取得

ちょっとしたことをやるにも困ってしまうEclipseプラグイン開発。今日も「こんなこと俺はできないのか・・・」とちょっと悩んでしまった。

よくGUIを伴うプログラムをやっていて,頻繁に使うクラスがある。それはJOptionPaneクラス。便利なダイアログを手軽に表示してくれる可愛い奴。メソッドが呼び出されているかどうかを確認したり,イベントの内容を表示したりするときによく使用する。System.out.println(...)でもいいんだけどね。スタティックメソッドのshowMessageDialogメソッドを良く使うんだけど,そのときには親のコンポーネントを引数に渡さなければならない。ダイアログの表示にはFrameオブジェクトが必要で,JOptionPaneクラスのshowMessageDialogメソッドは渡された親のコンポーネントのコンポジット階層を辿って,自動的にFrameクラスを見つけてくれている。親のコンポーネントにnullを渡すことも可能。

さて,Eclipseでも同じようにダイアログを表示して処理の確認を行おうと考えた。JOptionPaneクラスの代わりとなるものが,MessageDialogクラスらしい。これはjfaceのコンポーネント。これにもスタティックなメソッドのopenInformationメソッドなどを使用することによって,簡単にダイアログを表示できる

さぁ,さくっと利用しようとしたら。。。予想通り親が必要みたい。それも,Shellオブジェクト。Shellオブジェクトって,確かアプリケーションのトップレベルのウィンドウのことだったかな。つまり,Workbenchのウィンドウのことになる。ということは,Workbench何たらってクラスかインタフェースにgetShellメソッドがあって,それから取得できるかな?と考えた。けど,getShellメソッドなんて見当たらない。。。ビュー関連のクラスからはIWorkbenchPartSiteオブジェクトからShellオブジェクトを取得できるが,それが得られない場所ではどうするのか?

結論としては,Displayクラスを利用する。現在アクティブなSellオブジェクトをDisplayクラスのgetActiveShellメソッドで取得できる。つまり,
  Display.getCurrent().getActiveShell()
ってすればよいだろう。

当然これはデバッグ用ダイアログの話なので,アプリケーションダイアログはちゃんとIWorkbenchPartSteオブジェクトなどから適切なShellオブジェクトを得る必要があるだろう。

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

2004.01.11

行の先頭ルールとunreadについて

今日は引き続きIRuleの自作方法について試行錯誤。やってみたことは2つ。
  (1) 行の先頭でないと適用としないルールの作成
  (2) ルールに適用されないと判断した場合に,スキャンした文字をunreadしたときの動きの確認

(1)については,scanner#getColumn()メソッドを使って,スキャンした文字が行の何文字目かにあるかどうかでルールに適用するかどうかを判断するように,コードを書き換えてみた。単純に,
  scanner.getColumn() == 1
っていう評価式をevaluateメソッド内の条件判断に追加してあげただけだけど,結果はNG。単に行の先頭からキーワードを入力してあげればちゃんと動作するんだけど,先頭に空白を入れてあげても色付けがされたまま。そのほかにも怪しい動きをしてしまう。動作を見てると,文字入力をした位置よりも後の既に入力されている文字列がルール評価の対象になっていない模様。う~ん,どういうことだろうか。。。

(2)については,これまた単純にunreadしただけでは期待通りの動きにならない。っていうか,そりゃそうだなって動きになる。どういうことかというと,例えば「hoge」っていう文字列を色付けしたいときに,
  hogehoge
って入力したとすると,下線を引いた部分が色づけされてしまう。ちゃんとunreadしているおかげで,
  hogehoge(Unmatch) → ogehoge(Unmatch) → gehoge(Unmatch) → ehoge(Unmatch) → hoge(Match)
っていう評価がされて後半のhogeだけが色付けされてしまう。

これをやってて思ったんだけど,各Ruleオブジェクトによる評価の結果,Undefinedと判断された文字については,色づけなどのスタイルの更新が行われない?のかもしれない。それだとしたら,SQLWordRuleクラスでは,ルールにマッチしなかった文字についてはそのルール独自のトークンを返して,unreadしなかったというのも納得ができる。

・・・難しいなぁ。

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

2004.01.09

IAdaptableとは?(後編)

今まで謎だったIAdaptableインタフェース。これについてPlatform Plug-in Developer GuideのelementFactoriesの解説の中でIAdaptableインタフェースの説明が記載されているけど,最初に読んだときは「何いってんだ?」とちんぷんかんぷんだった。しかし,わかってしまえば(わかったつもりになってしまえば)なんてことはない。

ここでIAdaptableインタフェースが定義している唯一つのメソッドを紹介しよう。クラスオブジェクトを渡して,何らかのオブジェクトを得るというメソッドである。
  Object getAdapter(Class adapter)
これだけみても「は?」って感じだが,Eclipseはこのメソッドが支えているといっても過言ではない。

Eclipseはプラグインの集合体で成り立っているわけで,プラグイン開発者がどんなプラグインを用意するかもわからない。しかし,プラットフォームはプラグインに対して,いろいろと要求を行わなければならない。例えば,テキストエディタのプラグインであれば,プラットフォームは「アウトラインビューはどうするの?」「検索や置換はどうするの?」などというように,プラットフォームはテキストエディタを補佐する機能をテキストエディタ自身から得なければならない。そこで活躍するのがIAdaptableインタフェースなのだ。

プラットフォームはテキストエディタに対して,「アウトラインビューとして何か提供できる?」って感じで,IContentOutlinePageクラスオブジェクトをgetAdapterメソッドに渡して呼び出す。テキストエディタの実装は,もしアウトラインビューを提供するのであればIContentOutlinePageの実装オブジェクトを生成して返せばいいし,提供しないのであればnullを返せばよい。もし実装オブジェクトを返したときは,プラットフォームがそれを検地してアウトラインビューを生成して表示してくれる。つまり,「これをくれ」→「じゃぁこれあげるよ」というコーディングになる。

もちろん別のやり方,例えばShowableOulineインタフェースを定義してそれをエディタが実装し,適切なアウトラインビューオブジェクトを返してもらって・・・って感じの仕組みでも問題はなさそうだけど,インタフェースの定義とはかなりきつい静的な制約になってしまうために,Eclipseの開発者はきっと避けたかったはず。それよりももっと柔軟性に富んだ仕組みにするためには,Windowsのメッセージ駆動みたいなやり方,つまりメッセージ(Classオブジェクト)を決まったオブジェクト(getAdapterメソッド)に送り込み処理を実行させて結果(getAdapterメソッドの戻り値)を得る,ということを実現したかったに違いない。これによって,パーツ間の祖結合が促進されるし,パーツ間の関係は実行時に行われるようになるのでバージョンアップ時にメッセージが変更されたとしてもプラグインが動作しなくなってしまうことはない(テキストエディタが結果として提供したオブジェクトがプラットフォームに無視されちゃうかもしれないけど)。プラットフォームとしては,エディタなどの対象物が最低限IAdaptableインタフェースで規定されたgetAdapterメソッドを備えていれば良いということになる。

試しにTextEditorのサブクラスを作って,getAdapterメソッドを以下のようにオーバーライドしてエディタをプラグインとして実行してみると,プラットフォームがテキストエディタに何を期待しているのかが見て取れる。

class HogeTextEditor extends TextEditor {
  ...
  public Object getAdapter(Class adapter) {
    System.out.println(adapter.getName());
    return super.getAdapter(adapter);
  }
}

オブジェクト間の関連について高いレベルで祖結合を実現した仕組みって感じかな。プラットフォームを支える基本的かつ柔軟な仕組みといえるけど,プラグイン開発者にとってはきっと「ひえぇ」って仕組みだろう。なぜなら,ドキュメントやサンプルプログラムなどに頼らないと,どんなクラスオブジェクトが飛んでくるかわからないからだ・・・。

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

IAdaptableとは?(前編)

最大の謎としていつかは戦わないといけないと覚悟していたIAdaptableインタフェース。これの用途が何なのかさっぱりわからず途方にくれようとしていたそのとき!「ほ~こういうことか~」って感じでなんとなくIAdaptableインタフェースの用途が見えてきました。

最初考えたIAdaptableインタフェースの存在意義は,「java.io.Serializableインタフェースみたいなもの」だった。なぜなら,Platform Plug-in Delevoper GuideのelementFactoriesの説明の中で「ワークベンチがシャットダウンされる際に表示されていたIAdaptableオブジェクトのそのときの状態を保存するに違いない」と記載されていたからだ。Eclipseは起動時に前の状態が復元される。もちろんプリファレンスやプロパティ値はどこかに保持されているはずで,保持される対象となるものに対してIAdaptableインタフェースを実装しておく,という風に考えた。これはこれで間違ってはいないかもしれないし,間違っているかもしれない。。。

で次は,「COMのIUnknownみたいなもの」って考えた。つまり,Eclipseプラットフォームが管理対象とするオブジェクトでプラグインが提供するような未知のクラスに対しては,IAdaptableインタフェースを実装しておこうってこと。Eclipseプラットフォームは,IAdaptable[] adapters みたいな持ち方をしているのかな~って。ただ,この考えは全く根拠がない勝手な想像。もちろん,当たってるかどうかも現時点では断言できない。

でも,上記2つが正しいとしても,インタフェースの名前と全くそぐわない(Adaptableじゃない)ので,きっと間違ってるんだろうな~って思ってました。使われ方的に上記が当たってる場面もあるかもしれないけど,真のIAdaptableインタフェースの存在意義はそんな安っちょろいもんじゃなかったんだ!

・・・後半に続く。。。

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

2004.01.08

キーワードRuleの作成方法

DbEditプラグインについてくるSQLエディタを逆コンパイルしたら・・・出てきました!参考になる代物が!

それはIRuleインタフェースの直実装として作成された,SQL文のキーワード(SELECTやCREATEなど)を検出するルールクラスです。

とりあえずソースはこれ。

class SQLWordRule implements IRule {
  private Map keywords;
  private IToken defaultToken;
  public IToken evaluate(ICharacterScanner scanner) {
    char c = (char)scanner.read();
    if (Character.isLetter(C)) {
      StringBuffer value = new StringBuffer();
      do {
        value.append(c);
        c = (char)scanner.read();
      } while(Character.isLetterOrDigit(c) || c == '_');
      scanner.unread();
      IToken token =
        (IToken)keywords.get(value.toString().toUpperCase());
      return (token != null) ? token : defaultToken;
    } else {
      scanner.unread();
      return Token.UNDEFINED;
    }
  }
}

keywordsフィールドは,キーワード文字列をキーとし,それに対応するITokenオブジェクトを値として持つコレクション。defaultTokenフィールドは,このルールに適用されないと判断された場合に返却するITokenオブジェクト。

そしてevaluateメソッドが,このルールクラスの心臓部。scannerオブジェクトのreadメソッドを呼んで,一文字ずつ文字列を取り出していき,文字ごとにチェックを行うという地道な作業。ここでは,SQL文のキーワードは「汎用文字で始まり,2文字目以降は汎用文字あるいは数字もしくはアンダーバーが許可される」というルールに基づいてチェックしている。そんでもって,キーワードのルール外の文字が来たらscannerから文字を取り出すのをやめて,最後に取り出した一文字をunreadメソッドでもどしておく(キーワードに含まれないため)。この作業でキーワードの候補の文字列が出来上がるので,keywordsコレクションにそのキーワードが存在するかどうかをチェックし,もしあればそのキーワードに対応するITokenオブジェクトを,もしなければdefaultTokenオブジェクトをevaluateメソッドの結果として返却しています。

一文字目から文字じゃなかった場合は,その文字をunreadメソッドで元にもどして,Token.UNDEFINEDを結果として返却している。これ,なぜdefaultTokenオブジェクトじゃないんだろう・・・?これだけ謎。

テキストエディタ内の任意の場所でのルールは,基本的に上記のコードがすべてを物語っていると思われる。これに,行中の左から何文字目からのルール,とか,行末まで,とかが加味されると,PlatformAPIについてくる各種Ruleクラスになるのね,って理解でいいかな。

このコードを見つけるまで,SingleLineやらWordRuleやらを見て四苦八苦してたけど,基本形がなんとなく見えてきたので何とかなりそうな気配。ちょっと進んだかな。

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

NonRuleBasedDamagerRepairerって

派手そうなエディタ機能を持ってるプラグインを探しては逆コンパイルしてソースコードを覗いている今日この頃。なかなか把握できずにちょっと苛立っているけど,頻繁に見かける奴がいる。。。

NonRuleBasedDamagerRepairerクラスだ。

このクラス,エディタプラグインをPDEのウィザードで作るときに勝手に作成されるXMLエディタにくっついてくるものだ。中身を見ると,エディタ内で文字入力などの変更が加えられた行において,修復すべき範囲とそれに適用するスタイルを決定しているように見受けられる(全く自信なし)。

面白いのは,このクラスがXMLエディタでもDbEditプラグインについてくるSQLエディタでも,コメント区画に対して適用されているということ。

これを理解するまでには,ちょっと時間がかかりそう。。。デバッガでステップ実行をしまくって,細かな動作を見なければならなそうだ。。。

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

洋書発注

そういえば,昨日Eclipseのプラグイン開発に必要かなと思われる洋書を2冊発注した(1冊はちょっと前に発注してるけど)。
  (1) Java Developer's Guide to Eclipse
  (2) Contributing to Eclipse : Principles, Patterns, and Plig-Ins
(1)は,パート1がEclipseの普通の使い方,パート2が「Extending Eclipse」というプラグイン開発の方法が記載されているらしい。しかもどうやら例題でSQLエディタの作り方などプラグイン開発のTipsがかなり書いてあるらしく,期待大。
(2)はEclipseの普通の使い方というようりは,Eclipseの構造,仕組みなどを観点とした本らしい。「Gamma&Beck(誰?Eclipseの開発メンバーかな?)が執筆しただけあって情報量は多い」などと紹介されているので,こちらも期待大。
本の到着は(1)のほうが早いと思うけど,どうやら(2)を読んだ後に(1)を読むといいらしい。
  http://dann.dyndns.info/diary/20030610.html#p03
洋書なのでさすがに米国から取り寄せになってしまうので,到着まで1-2週間かかるかな。それまでは自力で頑張るしかないです。。。

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

2004.01.07

「行の先頭から~」Rule

雑誌の原稿を書いているときに良く使う表現で,
  1.2 インストール方法
というように見出し番号をつけたりする。このような見出し行に対してEclipseのエディタで色付けさせようと考えた。

普段は原稿を書くときはM$-Wordなんぞ使わずに,テキストエディタでガシガシ書いているので,もちろん殆どの段落はインデントなんぞさせない。つまり,
  1. はじめに
    ...
    1.2 インストール方法
なんてしないで,
  1. はじめに
  ...
  1.2 インストール方法
と同列に書いてます。

当然色付けのルールについても,行の先頭から始まる「1.2」という表記について適用したい。でも,どのRuleクラスを見ても,コンストラクタには開始・終了文字列やエスケープの文字指定,EOFまで!などのフラグしかない。それじゃダメじゃん。

ルール自作はいやっ!とあがきながらJavadocを見ていたとき,ふと目に飛び込んできたメソッドがこれ。
  PatternRule#setColumnConstraint(int column)
なんだ,先頭からって指定できるじゃん!つまりcolumn引数に0を与えてあげれば,行の先頭からのチェックをしてくれる(行頭に空白やタブ文字などがあればルールに適用されなくなる)。

Javadocはちゃんと隅々見るもんだな~って,Java開発の基本ですね。。。

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

~LineRuleって怪しいぞ

文字列のパーティションを決定し,色づけなどのために使用するSingleLineRuleクラスを使ってみたけど,なんかおかしい。。。

以下のように自作のパーティションスキャナを作って,その中でSingleLineRuleオブジェクトにより,
  [......]
という文字列があったら色付けしようと思ってやってみた。

public class ManuscriptPartitionScanner extends RuleBasedPartitionScanner {
  ....
  public ManuscriptPartitionScanner() {
    super();
    IToken headerToken = new Token(MANUSCRIPT_HEADER);
    List ruleList = new ArrayList();
    ruleList.add(new SingleLineRule("[", "]", headerToken));
    IPredicateRule[] rules;
    rules = (IPredicateRule[])(ruleList.toArray(new IPredicateRule[0]));
    setPredicateRules(rules);
  }
}

実行結果は,ちゃんと [......] が色づけされてOK!なんだけど,一旦 [......] と入力した後に,
  [....]..]
というように文字列中に終了文字を挿入すると,なぜか ...] が相変わらず色づけされたまま。

う~ん。。。文字の追加や削除が発生したときに,その影響範囲を決定するための Damager や Repairer などを自作して,ちゃんと「パーティションが変わったぞ!」って知らせてあげないといけないのかな。。。でも XMLEditor のサンプルはちゃんとタグ中に強引に > を打つとそこまでで色付けが解除されるし。。。

なんでだろう。。。

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

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