ローカル・ヒストリーの例

同期化 API を理解する一番良い方法は、実際に実行可能な単純な例を作成することです。 この例では、ワークスペース内のすべてのファイルに関する最新のローカル・ヒストリーの状態を表示する「同期化」ビューにページを作成します。 ローカル・ヒストリー同期は、ワークスペースに変更が加えられると、自動的に更新され、 表示、マージ、および変更を行う比較エディターを開きます。 カスタム・デコレーターを追加して、ワークスペース・ファイルを最後に保管されたローカル・ヒストリーの状態に復帰させるための、 ローカル・ヒストリー・エレメントおよびアクションの最後のタイム・スタンプを表示することもできます。 使用可能なリソース・バリアントのストアが既にあり、それを管理する必要がないため、この例は非常に優れています。

この例の残りの部分では、実行例を使用します。 このページには、ソース・コードの多くが (すべてではありません) 含まれています。 全ソース・コードは、org.eclipse.team.examples.filesystem プラグインのローカル・ヒストリー・パッケージにあります。 CVS リポジトリーのプロジェクトを確認して、本チュートリアルを読む際の参考にすることができます。 特記事項: 例にあるプラグインのソース・コードは、変更される場合があります。 この例で使用されているコードに一致するコピーを入手するには、 3.0 バージョン・タグ (おそらく R3_0)、または 2004 年 6 月 28 日の日付タグを使用してプロジェクトを調べてください。

ローカル・ヒストリーの概要

このスクリーン・ショットは、「同期化」ビューのローカル・ヒストリー同期を示しています。 このビューを使用して、ローカル・リソースとヒストリー内の最新の状態との変更点をブラウズできます。 このビューには、ローカル・ヒストリー・エントリーに関連付けられたタイム・スタンプを表示するためのカスタム・デコレーターと、 ファイルをローカル・ヒストリーの内容に復帰させるカスタム・アクションがあります。 問題の注釈、圧縮フォルダー・レイアウト、 およびナビゲーション・ボタンを提供する標準の「同期化」ビュー表示が使用されていることにも注意してください。

ローカル・ヒストリーのバリアントの定義

最初のステップは、ローカル・ヒストリーからのエレメントを表すためのバリアントを定義することです。 これによって、同期化 API はローカル・ヒストリーの内容にアクセスし、現在の内容と比較して、ユーザーに表示することができます。

public class LocalHistoryVariant implements IResourceVariant {
private final IFileState state;

public LocalHistoryVariant(IFileState state) {
this.state = state;
}

public String getName() {
return state.getName();
}

public boolean isContainer() {
return false;
}

public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
return state;
}

public String getContentIdentifier() {
return Long.toString(state.getModificationTime());
}

public byte[] asBytes() {
return null;
}
}

IFileState インターフェースによって、ローカル・ヒストリーのファイルの内容に既にアクセスできるようになっている (すなわち、IStorage インターフェースが実装されている) ため、これは簡単でした。 一般に、バリアントの作成時に、 ユーザーは、内容、このバリアントを識別するためにユーザーに表示される内容識別子、 および名前にアクセスする方法を提供する必要があります。 asBytes() メソッドは、セッション間のバリアントを保持する場合のみ必要です。

次に、SyncInfo 計算によって、ローカル・リソースとそのバリアントを比較するバリアント・コンパレーターを作成します。 繰り返しになりますが、ローカル・ヒストリー状態の存在が、 ローカル・ヒストリー状態の内容が現在のファイルの内容とは異なっていることを暗黙的に示しているため、これは簡単です。 これは、ローカル・ヒストリーの仕様で、ファイルが変更されていない場合にはローカル・ヒストリー状態を作成しないということが指定されているためです。

public class LocalHistoryVariantComparator implements IResourceVariantComparator {
public boolean compare(IResource local, IResourceVariant remote) {
return false;
}

public boolean compare(IResourceVariant base, IResourceVariant remote) {
return false;
}

public boolean isThreeWay() {
return false;
}
}

ローカル・ヒストリー状態の存在が、ローカルとは異なっていることを暗黙的に示していることがわかっているため、 ファイルをそのローカル・ヒストリー状態と比較する際には、単純に false を戻すことができます。 ローカル・ヒストリーとの同期は両方向のみです。 これは、ベース・リソースへアクセスできないためで、2 つのリソース・バリアントを比較するメソッドは使用されません。

同期の計算では、バリアントが存在していない場合 (すなわちヌルの場合) には、 コンパレーターの比較メソッドは呼び出されないことに注意してください。 この比較メソッドは、両方のエレメントが存在している場合のみ呼び出されます。 本書の例では、ローカル・ヒストリーを持たないファイルの場合、およびすべてのフォルダー (ローカル・ヒストリーを持ったことがない) の場合の両方で呼び出されます。 これを処理するため、SyncInfo のユーザー独自のサブクラスを定義して、 これらの場合に計算された同期の状態を変更する必要があります。

public class LocalHistorySyncInfo extends SyncInfo {
  public LocalHistorySyncInfo(IResource local, IResourceVariant remote, IResourceVariantComparator comparator) {
    super(local, null, remote, comparator);
  }

  protected int calculateKind() throws TeamException {
    if (getRemote() == null)
      return IN_SYNC;
    else
      return super.calculateKind();
  }
}

ヌル であるベースを常に提供するようにコンストラクターがオーバーライドされ (両方向比較のみを使用するため)、 リモートが存在しない場合に IN_SYNC を戻すように同期の種類計算が変更されています (これは、ローカル・ファイルおよびローカル・ヒストリー内のファイル状態が存在している場合のみを考慮しているためです)。

サブスクライバーの作成

ここで、ローカル・ヒストリーのリソース・バリアントへのアクセスを提供するサブスクライバーを作成します。 ローカル・ヒストリーは、ワークスペースの任意のファイルに保管できるため、 ローカル・ヒストリーのサブスクライバーは、すべてのリソースを監視し、 ルートのセットは、ワークスペース内のすべてのプロジェクトになります。 ローカル・ヒストリーは、ローカル・ファイルの内容が変更された場合のみ変更されるため、 サブスクライバーを更新する機能を用意する必要もありません。 したがって、リソース・デルタが発生すると必ず状態は更新できます。 これによって、ローカル・ヒストリー・サブスクライバー上に、SyncInfo の取得およびワークスペースの全探索という 2 つの興味深いメソッドのみが残ります。

public SyncInfo getSyncInfo(IResource resource) throws TeamException {
  try {
    IResourceVariant variant = null;
    if(resource.getType() == IResource.FILE) {
      IFile file = (IFile)resource;
      IFileState[] states = file.getHistory(null);
      if(states.length > 0) {
        // last state only
        variant = new LocalHistoryVariant(states[0]);
      } 
    }
    SyncInfo info = new LocalHistorySyncInfo(resource, variant, comparator);
    info.init();
    return info;
  } catch (CoreException e) {
    throw TeamException.asTeamException(e);
  }
}

サブスクライバーは、ローカル・ヒストリーに最新のファイルの状態を含んだ新規 SyncInfo インスタンスを戻します。 SyncInfo は、リモート・エレメントのローカル・ヒストリー・バリアントを使用して作成されます。 ローカル・ヒストリーを持たないプロジェクト、フォルダー、およびファイルの場合には、 リモート・リソース・バリアントは提供されません。この場合には、LocalHistorySyncInfo の calculateKind メソッドによって、 リソースは同期が取れていると考えられます。

ローカル・ヒストリー・サブスクライバーの残りのコードは、次の members メソッドの実装です。

public IResource[] members(IResource resource) throws TeamException {
  try {
    if(resource.getType() == IResource.FILE)
      return new IResource[0];
    IContainer container = (IContainer)resource;
    List existingChildren = new ArrayList(Arrays.asList(container.members()));
    existingChildren.addAll(
      Arrays.asList(container.findDeletedMembersWithHistory(IResource.DEPTH_INFINITE, null)));
    return (IResource[]) existingChildren.toArray(new IResource[existingChildren.size()]);
  } catch (CoreException e) {
    throw TeamException.asTeamException(e);
  }
}

このメソッドで興味深い部分は、削除済みリソースにローカル・ヒストリーがある場合に、このメソッドが存在しない子を戻すということです。 これによって、サブスクライバーが、ローカル・ヒストリーにのみ存在し、ワークスペースには存在しないエレメントに対して SyncInfo を戻せるようになります。

ローカル・ヒストリーの同期 Participant の追加

この時点で、ローカル・ヒストリー内のエレメントに対して SyncInfo へのアクセスを提供するクラスが作成されています。 次に、UI エレメントを作成します。このエレメントによって、「同期化」ビューにページを持つことができ、 ローカル・ヒストリーのすべてのエレメントに対する最新のヒストリー状態を表示できます。 サブスクライバーが存在するため、このエレメントを「同期化」ビューに簡単に追加できます。 以下のように、同期 Participant 拡張ポイントの追加から開始します。

<extension
       point="org.eclipse.team.ui.synchronizeParticipants">
<participant
persistent="false"
icon="synced.png"
class="org.eclipse.team.synchronize.example.LocalHistoryParticipant"
name="Latest From Local History"
id="org.eclipse.team.synchronize.example"/>
</extension>

次に、LocalHistoryParticipant を実装する必要があります。 これは、SubscriberParticipant をサブクラス化し、これによってサブスクライバーから SyncInfo を収集し、 ワークスペースの変更が発生したときに同期状態を更新するすべてのデフォルトの振る舞いを提供します。 また、ワークスペース・リソースをローカル・ヒストリーの最新の状態に復帰させるアクションを追加します。

最初に、カスタム・アクションが参加プログラムに追加される方法について説明します。

public static final String CONTEXT_MENU_CONTRIBUTION_GROUP = "context_group_1"; //$NON-NLS-1$
  
private class LocalHistoryActionContribution extends SynchronizePageActionGroup {
  public void initialize(ISynchronizePageConfiguration configuration) {
    super.initialize(configuration);
    appendToGroup(
      ISynchronizePageConfiguration.P_CONTEXT_MENU, CONTEXT_MENU_CONTRIBUTION_GROUP,
      new SynchronizeModelAction("Revert to latest in local history", configuration) { //$NON-NLS-1$
        protected SynchronizeModelOperation getSubscriberOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
          return new RevertAllOperation(configuration, elements);
        }
      });
  }
}

ここで、特定の SynchronizeMoidelAction および操作を追加します。 ここで無償で入手する振る舞いは、バックグラウンドで実行する機能、 および稼働中のノードのビジー状態を表示する機能です。 アクションは、ワークスペース内のすべてのリソースをローカル・ヒストリーの最新の状態に復帰させます。 アクションの追加は、アクション・コントリビューションを参加プログラム構成に追加することによって行います。 構成は、実際の同期 UI を表示する参加プログラム・ページの作成で使用するプロパティーを記述するために使用します。

参加プログラムは、ローカル・ヒストリー・アクション・グループをコンテキスト・メニューに追加するために、 以下のように構成を初期設定します。

protected void initializeConfiguration(ISynchronizePageConfiguration configuration) {
super.initializeConfiguration(configuration);
configuration.addMenuGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU,
CONTEXT_MENU_CONTRIBUTION_GROUP);
configuration.addActionContribution(new LocalHistoryActionContribution()); configuration.addLabelDecorator(new LocalHistoryDecorator());
}

ここでは、カスタム・デコレーションを使用する方法について説明します。 上記メソッドの最後の行は、ページの構成に以下のデコレーターを登録しています。

public class LocalHistoryDecorator extends LabelProvider implements ILabelDecorator {
public String decorateText(String text, Object element) {
if(element instanceof ISynchronizeModelElement) {
ISynchronizeModelElement node = (ISynchronizeModelElement)element;
if(node instanceof IAdaptable) {
SyncInfo info = (SyncInfo)((IAdaptable)node).getAdapter(SyncInfo.class);
if(info != null) {
LocalHistoryVariant state = (LocalHistoryVariant)info.getRemote();
return text+ " ("+ state.getContentIdentifier() + ")";
}
}
}
return text;
}

public Image decorateImage(Image image, Object element) {
return null;
}
}

デコレーターは、同期ビューに表示されたモデル・エレメントからリソースを抽出し、 ローカル・ヒストリー・リソース・バリアントの内容識別子をビューに表示されたテキスト・ラベルに付加します。

最後の断片は、ローカル・ヒストリー参加プログラムを作成するウィザードを提供します。 Team Synchronizing パースペクティブは、ユーザーが同期を迅速に作成できるようにするグローバル同期アクションを定義します。 また、「同期化」ビュー・ツールバーから同期を作成する機能を使用することもできます。 開始するには、以下のように synchronizeWizards 拡張ポイントを作成します。

<extension
point="org.eclipse.team.ui.synchronizeWizards">
<wizard
class="org.eclipse.team.synchronize.example.LocalHistorySynchronizeWizard"
icon="synced.png"
description="Creates a synchronization against the latest local history state of all resources in the workspace"
name="Latest From Local History Synchronize"
id="ExampleSynchronizeSupport.wizard1"/>
</extension>

これによって、ウィザードがリストに追加され、そのウィザードの finish() メソッドで参加プログラムを簡単に作成し、 同期マネージャーに追加します。

LocalHistoryPartipant participant = new LocalHistoryPartipant();
ISynchronizeManager manager = TeamUI.getSynchronizeManager();
manager.addSynchronizeParticipants(new ISynchronizeParticipant[] {participant});
ISynchronizeView view = manager.showSynchronizeViewInActivePage();
view.display(participant);

結論

これは、同期化 API を使用した簡単な例です。この例を簡単に理解するために、いくつかの点を詳細に説明してきました。 即応性がある正確な同期サポートは簡単に作成できません。難しい点は、同期情報の管理と同期状態変更の通知です。SubscriberParticipants に関連付けられているユーザー・インターフェースが適切な場合には、サブスクライバーの実装が一度完了すれば、 ユーザー・インターフェース部分は簡単です。 その他の例については、org.eclipse.team.example.filesystem プラグインを参照し、 サブスクライバーおよび ISynchronizeParticipant のワークスペースのサブクラスをブラウズしてください。

次の節では、ワークベンチ・セッション間で同期状態をキャッシュする方法などの、 サブスクライバーを最初から作成する際に役立つクラスおよびインターフェースについて説明します。