目次


実用的な XML

プロジェクトを作成する

Eclipse に新規ウィザードを追加する

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: 実用的な XML

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:実用的な XML

このシリーズの続きに乞うご期待。

このプロジェクトに関する記事を最初から読んできた読者の方は、私が Eclipse に熱を上げていることはご存知のはずです。Emacs のファンが言うように、優れた開発環境の条件は、拡張可能かつ構成可能であることです。Eclipse はその両方を満たしていますが、評価の高い Emacs の流れを汲む Eclipse には Emacs にはない今どきのユーザー・インターフェースも用意されています。

SWT の移植性

Eclipse の素晴らしい点の 1 つは、Java テクノロジーで最ももどかしい欠陥の 1 つと思われる、ユーザー・インターフェース・ライブラリーが修正されていることです。以前の記事で述べたとおり、Eclipse には SWT (Standard Widget Toolkit) が使われています。SWT は、AWT (Abstract Windowing Toolkit) が持つ最高の機能と Swing の豊富なウィジェット・セットとを組み合わせ、ネイティブ・コントロールを使用するようにしたものです。これにより、ついにネイティブ・インターフェースのように動作する、洗練されたユーザー・インターフェースを設計できるようになったのです。

SWT に対する批判の大半は移植性に向けられてきました。SWT は JDK (Java Development Kit) に組み込まれていないため、プラットフォームによっては SWT を利用できない場合があります。

幸いなことに Eclipse の開発チームは、SWT の移植を必要としているできるだけ多くのプラットフォームに SWT を移植できるように取り組んでいるようなので、実際には移植性は問題にならないはずです。

私は SWT の移植性をテストしたいと思っていたので、SWT が公開された時に Macintosh 版のビルドをダウンロードしてみました。この Mac へ移植したバージョンのビルドは、まだ開発中であるにもかかわらず、かなり安定しています。ただし、私が最も感心したのは、SWT でネイティブのAqua コントロールを描画できることです。

ドキュメントについて

新しいプロジェクトを使用し、それらのプロジェクトについてレポートするのは楽しいものですが、新しいプロジェクトの場合、ドキュメントが不十分なことが多いという困った点もあります。Eclipse も例外ではありません。この記事を書くに当たって (いやむしろ、この記事のためにコーディングするに当たって、と言うべきかもしれません) 私は多くのことを調べなければなりませんでした。Eclipse のドキュメントとしては、基本的なことに関する説明が数件と、API についてのドキュメント、そしてソース・コードしかありません (ただしソース・コードがあるのは幸いです)。

開発作業は、ほとんど試行錯誤の連続で、何か意味のある結果が得られるまで、繰り返しコードをいじるしかありませんでした。残念なことに、何かを理解したつもりで新しい作業に取り組んでみると、実は私の理解が誤っていたことに気付かされるということが度々ありました。最終的に、自分の理解に確信を持てるようになりましたが、遭遇した個々の問題はどれも重大なものではないにもかかわらず、問題が絡み合うと、解決は簡単ではありませんでした。今後 Eclipse に関するもっと多くの記事が書かれるようになるにつれ、ドキュメントが不足しているという問題は次第に解消されるはずです。

XM プラグインの進化

前回の記事「XMとEclipseの統合」の終わりまでで、XM と Eclipse との大まかな統合が実現されました。そこではコンテキスト・メニューに項目を追加し、ユーザーがその項目を選択すると XM が実行されるようにしました。それがまだ統合の最初の段階でしかないことはわかっていましたが、私 (および Pineapplesoft 社の人達) が実際に使ってみると、もっと改善する必要があることに気付きました。具体的には、以下の点に不満を感じました。

  • 新規プロジェクトを簡単に開始できないこと。「New Project (新規プロジェクト)」ウィザードを実行してから、ファイルをいくつか手動で作成しなければなりませんでした。ウィザードですべての処理が完了するようにしなければなりません。
  • .xmp ファイルが混乱を招きやすいこと。.xmp ファイルはクリックする対象として必要なだけなので、ほとんどの場合は空です。当初、XM には構成ファイルが必要なく、それが XM の魅力の 1 つでした。
  • 右クリックは Windows/KDE/Gnome で定義される操作であること。Mac の場合、右クリックに相当する操作には、複雑で使用頻度の低い操作を割り当てるのが最善の手段です。右クリックではなく、もっと移植性の高いインターフェースを考えるべきでした。1 つの方法として、Eclipse にビルダー (本質的にはコンパイラーです) を登録する方法があります。これにより、Eclipse はプロジェクトが変更されると、適切なビルダーを自動的に呼び出すようになります。

こうした問題があることを考えると、なるべく早いうちに、「New Project (新規プロジェクト)」ウィザードと、プロジェクトのビルド時に XM を呼び出せる機能とを XM プラグインに組み込む必要があると考えました。そこで、拡張ポイントを使用して、この 2 つの機能を実装します (拡張ポイントの詳細については、「参考文献」に挙げた記事、「Eclipseを使用してXMのユーザー・インターフェースを構築する」を参照してください)。新規ウィザードのための拡張ポイントは org.eclipse.ui.newWizards であり、この拡張ポイントはインターフェースとして org.eclipse.ui.INewPlugin を使用します。ビルダーのための拡張ポイントは org.eclipse.core.resources.builders であり、インターフェースとして org.eclipse.core.resources.IncrementalProjectBuilder を使います。

新規プロジェクト・ウィザード

新規プロジェクト・ウィザードとビルダーは関連しています。例えば、Eclipse は新しいプロジェクトを作成する際に自動的にビルダーを呼び出します。オンライン・リポジトリーからコードをダウンロードしてみると、新規プロジェクト・ウィザードとビルダーの両方が実装されていることがわかるはずです。この記事では新規プロジェクト・ウィザードについて説明し、次回の記事ではビルダーについて説明します。

ファイルおよびプロジェクト・ディレクトリー

まず、いくつかの定義から始めましょう。Eclipse の場合、プロジェクトとは、プロジェクト・ディレクトリー内にあるファイル (ソース・ファイル、構成ファイル、コンパイル済みファイルなど) のセットのことです。Eclipse の用語では、ファイルおよびディレクトリーのことをリソースと呼びます。Eclipse のリソース (ファイル) を Java のリソース (ローカライズに使用されるストリング) と混同しないようにしてください。

ワークスペースは、ユーザーが使用することのできるプロジェクトのリストによって構成されます。デフォルトで、プロジェクトは eclipse/workspace ディレクトリーの配下に格納されますが、ユーザーは任意の場所にプロジェクトを作成することができます。

ユーザーは、図 1 のようなナビゲーター・パネルを使ってプロジェクトを操作します。この場合も、プラグインによって特別なナビゲーターにすることが可能ですが、図 1 に示しているのはプロジェクト内のリソース (ファイル) を一覧表示する汎用のナビゲーターです。

図 1. Eclipse のナビゲーター
Eclipse のナビゲーター

新規プロジェクトを作成するためには、Eclipse が管理する新規ディレクトリーといくつかの構成ファイルが必要です。ワークスペースに新規ディレクトリーを作成するだけでは不十分で、それだけでは Eclipse にプロジェクトとしては認識されないので注意してください。

プロジェクト・ネイチャー

プロジェクトのメインの構成ファイルには .project という名前が付けられます (UNIX ファイルシステムでは、ドットで始まるファイルは隠しファイルであることを表しています)。この構成ファイルには、プロジェクトのビルド方法とプロジェクト・ネイチャーに関する情報が含まれています。

ネイチャーというのは、プロジェクト・タイプを表す ID です。例えば、Java プロジェクトには Java ネイチャーがあります。Eclipse はプロジェクト・ネイチャーを使用することで、ユーザーとの対話を制御します。ネイチャーによってメニュー項目を有効にしたり無効にしたりすることができます。つまり、Java ネイチャーを持つプロジェクトのメニューには Java 特有の項目があります。

1 つのプロジェクトが複数のネイチャーを持つ場合があります。例えば、プラグイン・プロジェクトは Java プロジェクトである (プラグインは Java 言語で作成されます) と同時にプラグイン・プロジェクトでもあります。実際に、プラグイン・プロジェクトのメニューには Java プロジェクトとプラグイン・プロジェクトの両方の項目があります。

Eclipse での他の ID と同様、ネイチャー ID もドメイン名を逆順にしたもので始まり、この点はパッケージ名と変わりません。例えば、Java ネイチャーの ID は org.eclipse.jdt.core.javanature です。この ID をクラス名やパッケージ名と混同しないように注意してください。ID の詳細については、「参考文献」に挙げた記事「Eclipseを使用してXMのユーザー・インターフェースを構築する」を参照してください。

以上のことを学んだ私は、プロジェクト・ネイチャーによって、(前回の記事で導入した) ダミーの .xmp ファイルが冗長になることに気が付きました。XM プロジェクトに対して特別なネイチャーを定義すれば、「XMとEclipseの統合」で導入した「Run XM (XM を実行)」というメニュー項目と XM プロジェクトとを関連付けることができます。

私は XM のプロジェクト・ネイチャーとして、org.ananas.xm.eclipse.xmnature を定義しました。リスト 1 は、これを反映してどのようにマニフェスト・ファイル (plugin.xml) を変更したのかを示しています。

リスト 1. 新しい拡張ポイント
<extension point="org.eclipse.ui.popupMenus">
   <objectContribution adaptable="true"
         objectClass="org.eclipse.core.resources.IResource"
         id="org.ananas.xm.eclipse.popupMenu">
<filter name="projectNature"
            value="org.ananas.xm.eclipse.xmnature">
      </filter>
      <action label="Run XM"
              tooltip="Call XM to publish the site."
              class="org.ananas.xm.eclipse.XMRunner"
              menubarPath="additions"
enablesFor="+"
              id="org.ananas.xm.eclipse.popMenu.action">
      </action>
   </objectContribution>
</extension>

リスト 1 を前に公開したコードと比較すると、以下の点が変更されていることに気付くはずです。

  • nameFilter 属性が filter タグに置き換えられています。nameFilter ではファイル名で突き合わせを行いましたが、filter ではプロジェクト・ネイチャーで突き合わせを行います。
  • enablesFor 属性が + に変更されています。これはつまり、プロジェクトの中の複数のファイルを選択できるということです。この変更はユーザーにとって使いやすくするための変更にすぎません。
  • XMRunner クラスは、ほとんど変更されていません。ただしパッケージ名を短くし、入力が少なくてすむようにしています。

新規 XM プロジェクト・ウィザード

リスト 1 のマニフェストが機能するのは、プロジェクトが XM ネイチャーとして宣言されている場合のみであることは明らかです。汎用ウィザードは、ネイチャーを持たないプロジェクトを作成します。従ってリスト 1 のコードをテストするためには、まず始めに XM プロジェクト・ウィザードを用意する必要があります。

Eclipse のウィザード

Eclipse は極めて拡張性が高く、その拡張性はウィザードの扱い方にも反映されています。Eclipse では、1 つのウィザードに複数のプラグインを適用することができます。プロジェクト・ウィザードはこの機能を利用しています。

ユーザーがメニューで「New Project (新規プロジェクト)」を選択すると、図 2 のウィザードが表示されます。ユーザーは、これから作成するプロジェクトのタイプを選択します。XM はリストの中に表示されていますが、XM プラグインはまだロードされていません。このリストはマニフェスト (plugin.xml) から取り込まれたものです。

図 2. 「New Project (新規プロジェクト)」ウィザード
「New Project (新規プロジェクト)」ウィザード
「New Project (新規プロジェクト)」ウィザード

しかし図 3 に示す次の画面では、XM プラグインがロードされており、XM プラグインによって画面を制御できるようになっています。この遷移はユーザーから見て非常に円滑に行われ、ウィザードがちらついたりウィザードが変化したりすることはありません。それどころか、ユーザーは自分でプラグインを作成したのでない限り、新しいプラグインがロードされたことに気が付かない可能性があります。

図 3. プラグインがロードされた状態
プラグインがロードされた状態
プラグインがロードされた状態

このようにユーザーにはわからないように統合を行うために、Eclipse はウィザードを以下の 3 つのオブジェクト・セットに分けています。

  • コンテナーは、ボタンを含むダイアログ・ボックスです。
  • ウィザードは、ユーザー・アクションに応答します。コンテナーは 1 つまたは複数のウィザードとのやり取りを通じて、ユーザー・アクションに応答します。
  • ページは、コンテナー内のコントロールを描画します。ユーザーが「Next (次へ)」ボタンをクリックするたびに、新規ページがロードされます。図 3 に示したページは、プロジェクト名のフィールドとディレクトリーのフィールドを扱っています。

XM の実装

XM は、INewWizard インターフェースを実装する NewXMProjectWizard クラスでウィザードを実装していますが、INewWizard によって定義されるメソッドのほとんどが、Wizard クラスをデフォルト実装としているため、実際には Wizard から派生させるのがウィザードを実装する最も簡単な方法になります。

NewXMProjectWizard は、通常のウィザードと同じように、マニフェスト内に拡張ポイントとして登録されています。リスト 2 はマニフェストの該当部分を抜粋したものです。この部分でウィザードのカテゴリーが宣言されていることに注意してください。このカテゴリーによって、いくつかの XM ウィザードがグループ化されます。

リスト 2. Wizard 拡張ポイント
<extension point="org.eclipse.ui.newWizards">
   <category name="ananas.org"
             id="org.ananas.xm.eclipse.newWizards">
   </category>
   <wizard name="XM Project"
           icon="icons/newproject16.gif"
           category="org.ananas.xm.eclipse.newWizards"
           class="org.ananas.xm.eclipse.NewXMProjectWizard"
project="true"
           id="org.ananas.xm.eclipse.newproject">
      <description>Create an XM project.</description>
   </wizard>
</extension>

wizard タグはウィザードにとっては新しいタグですが、このタグは前回の記事で紹介した view と非常によく似ているため、理解しやすいはずです。唯一、目新しいものは project 属性です。project 属性を true に設定すると、そのウィザードが新規プロジェクトのリストに追加されます。この属性がないウィザードは、「Other (その他)」カテゴリーのリストに追加されます。

プラグインがロードされると、コンテナーは init() を呼び出します。NewXMProjectWizard はほとんどの場合に初期化する必要がないため、init() では最初に setNeedsProgressMonitor() を呼び出して、プログレス・バーが表示されるように要求しています。これにより、プロジェクトを作成している間、プログレス・バーが徐々に伸びていきます。リスト 3 に init() メソッドを示します。

リスト 3. init() メソッド
public void init(IWorkbench workbench,
                 IStructuredSelection selection)
{
   setNeedsProgressMonitor(true);
}

次に、コンテナーはリスト 4 に示す addPages() を呼び出します。このメソッドによってウィザードのページが登録されます。NewXMProjectWizard には、プロジェクトの名前と場所の入力を促す 1 つのページしか必要ありません。便利なことに、Eclipse はそうしたページを WizardNewProjectCreationPage クラスによって作成してくれます。

リスト 4. addPages() メソッド
public void addPages()
{
 super.addPages();
 namePage = new WizardNewProjectCreationPage("NewXMProjectWizard");
 namePage.setTitle(Resources.getString("eclipse.newprojectname"));
 namePage.setDescription(Resources.getString("eclipse.newprojectdescription"));
 namePage.setImageDescriptor(ImageDescriptor.createFromFile(getClass(),
       "/org/ananas/xm/eclipse/resources/newproject58.gif"));
addPage(namePage);
}

ウィザードは、ウィザードに登録するページごとに addPage() を呼び出します。この場合には、ウィザードに登録するページは 1 つしかありません。

ユーザーが「Finish (終了)」をクリックした場合の処理

ユーザーが 「Finish (終了)」 をクリックすると、performFinish() メソッドが実行されます。リスト 5 に、このメソッドの内容を示してありますが、このメソッドではあまりたいしたことをしていません。プロジェクトを作成する作業の大半は、performFinish() を実行すると間接的に呼び出される createProject() の中で行われます。

リスト 5. performFinish() メソッド
public boolean performFinish()
{
   try
   {
      WorkspaceModifyOperation op =
         new WorkspaceModifyOperation()
      {
         protected void execute(IProgressMonitor monitor)
         {
            createProject(monitor != null ?
                          monitor : new NullProgressMonitor());
         }
      });
      getContainer().run(false,true,op);
   }
   catch(InvocationTargetException x)
   {
      reportError(x);
      return false;
   }
   catch(InterruptedException x)
   {
      reportError(x);
      return false;
   }
   return true; 
}

プロジェクトを作成すると、ディレクトリーとファイルが追加されるため、ワークスペースに変更が加えられます。すると、ウィザードはワークスペースとの同期をとります。そのための最も簡単な方法は、WorkspaceModifyOperation の子孫の中で createProject() を実行することです。

ワークスペースに変更を加えるコードは、execute() メソッドから呼び出す必要があります。リスト 5execute() メソッドは、IProgressMonitor を引数に取って createProject() を呼び出しているだけです。プロジェクトの作成に時間がかかる場合には、ウィザードはIProgressMonitor 引数を使って進行状況を知らせます。コンテナーはプログレス・バーを更新するために、この引数を使用します。

IProgressMonitor のメソッドで最も便利なのは、以下のメソッドです。

  • beginTask(): 処理を開始する前に、このメソッドを呼び出す必要があります。このメソッドは引数として、処理の説明と、処理の継続時間を取ります。
  • worked(): このメソッドは進行状況をレポートします。通常はプログレス・バーの更新に使われます。
  • done(): プロジェクトの作成が完了したときに、このメソッドを呼び出す必要があります。
  • subTask(): このメソッドによって処理の説明を変更することができます。

プロジェクトを作成するのに要する時間とその進行状況は、作業を単位としてレポートされます。そして、1 つの単位が何を表すのかは、ユーザーが自由に定義することができます。例えばファイルを処理する場合であれば、1 つの単位がファイルを表すようにすることができます。

スレッドを実行するためには、コンテナーの run() メソッドを呼び出します。すると、コンテナーはプログレス・バーを IProgressMonitor と同期させます。

プロジェクトの作成

実際に、プロジェクトの作成が行われるのは createProject() の中になります (リスト 6 を参照)。このメソッドでは、順序が非常に重要です。このメソッドのコードが適切な順序に従っていないと、ウィザードは失敗します (そして多くの場合、よく理解できないエラー・メッセージが表示されます)。

リスト 6. createProject() メソッド
protected void createProject(IProgressMonitor monitor)
{
   monitor.beginTask(Resources.getString("eclipse.creatingproject"),50);
   try
   {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
      monitor.subTask(Resources.getString("eclipse.creatingdirectories"));
IProject project = root.getProject(namePage.getProjectName());
      IProjectDescription description = ResourcesPlugin.getWorkspace().newProjectDescription(project.getName());
      if(!Platform.getLocation().equals(namePage.getLocationPath()))
         description.setLocation(namePage.getLocationPath());
      description.setNatureIds(new String[] { PluginConstants.NATURE_ID });
      ICommand command = description.newCommand();
      command.setBuilderName(PluginConstants.BUILDER_ID);
      description.setBuildSpec(new ICommand[] { command });
      project.create(description,monitor);
      monitor.worked(10);
project.open(monitor);
      project.setPersistentProperty(PluginConstants.SOURCE_PROPERTY_NAME,"src");
      project.setPersistentProperty(PluginConstants.RULES_PROPERTY_NAME,"rules");
      project.setPersistentProperty(PluginConstants.PUBLISH_PROPERTY_NAME,"publish");
      project.setPersistentProperty(PluginConstants.BUILD_PROPERTY_NAME,"false");
      monitor.worked(10);
      IPath projectPath = project.getFullPath(),
            srcPath = projectPath.append("src"),
            rulesPath = projectPath.append("rules"),
            publishPath = projectPath.append("publish");
      IFolder srcFolder = root.getFolder(srcPath),
              rulesFolder = root.getFolder(rulesPath),
              publishFolder = root.getFolder(publishPath);
      createFolderHelper(srcFolder,monitor);
      createFolderHelper(rulesFolder,monitor);
      createFolderHelper(publishFolder,monitor);
      monitor.worked(10);
      monitor.subTask(Resources.getString("eclipse.creatingfiles"));
      IPath indexPath = srcPath.append("index.xml"),
            defaultPath = rulesPath.append("default.xsl");
      IFile indexFile = root.getFile(indexPath),
            defaultFile = root.getFile(defaultPath);
      Class clasz = getClass();
      InputStream indexIS = clasz.getResourceAsStream("/org/ananas/xm/eclipse/resources/index.xml"),
          defaultIS = clasz.getResourceAsStream("/org/ananas/xm/eclipse/resources/default.xsl");
      indexFile.create(indexIS,false,new SubProgressMonitor(monitor,10));
      defaultFile.create(defaultIS,false,new SubProgressMonitor(monitor,10));
   }
   catch(CoreException x)
   {
      reportError(x);
   }
   finally
   {
      monitor.done();
   }
}

新しいプロジェクトを作成するためには、以下のステップに従います。

  1. ワークスペースのルートを IWorkspaceRoot のインスタンスとして取得します。Eclipse には便利なプラグイン (ResourcesPlugin) が用意されており、このプラグインを使用することで、ワークスペースなどのリソースにアクセスすることができます。この場合のリソースも、ファイルやディレクトリーといった Eclipse のリソースです。
  2. IWorkspaceRootgetProject() を呼び出します (私の個人的な意見としては、このメソッドは新しいプロジェクトを作成するメソッドなので、newProject() という名前にすべきだったと思います)。
  3. newProjectDescription() メソッドを使用して、プロジェクトに関する空の説明を作成します。プロジェクトの説明には、そのプロジェクトがファイルシステム上のどこにあるのかなど、そのプロジェクトに関する特定の情報が含まれることになります。
  4. デフォルトの場所 (ワークスペース・ディレクトリーの下) を選択した場合には、次のステップに進みます。それ以外の場合には、プロジェクトの説明をするオブジェクトに、プロジェクトがある場所を設定します。理由はわかりませんが、プロジェクトの場所を明示的にデフォルトの場所に設定すると、プロジェクトの作成は失敗します。
  5. プロジェクト・ネイチャーがある場合には、そのプロジェクト・ネイチャーを登録します。ネイチャーはストリングで表現された ID の配列です。
  6. プロジェクト・ビルダー (つまりコンパイラー) を登録します。これについては次回の記事で説明しますが、この段階で XM はビルダーとして使用できるようになっています。
  7. create() メソッドを使用してプロジェクトを作成します。
  8. 最後に、新たに作成したプロジェクトを開きます。

プロジェクトを作成して開くと、そのプロジェクトのプロパティーを設定することができます。これらのプロパティーは Eclipse のプロパティーと共に保存されます。リスト 6 を見るとわかるように、ウィザードは XM ビルダーに対してプロパティーをいくつか設定しています。これらのプロパティーは「XMとEclipseの統合」で紹介した .xmp ファイルと同じなので、容易に理解できるはずです。

最後に忘れてはならないのは、このウィザードは新たに作成したプロジェクトに対し、デフォルトのディレクトリー (おなじみの srcrulespublish)、サンプルの XML 文書、単純なスタイルシートを追加します。これは、ユーザーの使いやすさを考慮してのことです。

進行中の作業

オンライン・リポジトリー (「参考文献」を参照) からソース・コードをダウンロードすると、XM プラグインにビルダーも用意されていることがわかります。このビルダーについては、次回の記事で取り上げます。

今回の記事では、Eclipse では自由自在にユーザー・エクスペリエンスを制御できるプラグインを開発できることを説明しました。プラグインを使用することで、ユーザー・インターフェースのほとんどすべての側面を拡張することができます。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=242722
ArticleTitle=実用的な XML: プロジェクトを作成する
publish-date=10012010