Příklad lokální historie

Nejlepším způsobem jak pochopit rozhraní API pro synchronizaci je vytvořit si jednoduchý a opravdu funkční příklad. V tomto příkladu si vytvoříme stránku v pohledu Synchronizace, která bude zobrazovat stav lokální historie pro všechny soubory v pracovním prostoru. Synchronizace lokální historie se bude automaticky aktualizovat se změnami v pracovním prostoru, přičemž lze otevřít editor porovnání za účelem procházení a sloučení změn. Rovněž přidáme vlastní dekorátor pro zobrazení posledního časového razítka prvku lokální historie a akci pro vrácení souborů pracovního prostoru do naposledy uloženého stavu lokální historie. Je to výborný příklad, protože již máme k dispozici zásobu variant prostředků a nemusíme je spravovat.

Po zbytek tohoto příkladu využijeme již běžící příklad. Na této stránce bude většina zdrojového kódu, nikoli však kompletní kód. Kompletní zdrojový kód najdete v balíčku lokální historie modulu plug-in org.eclipse.team.examples.filesystem. Při čtení tohoto návodu můžete přezkoušet projekt z úložiště CVS a použít jej jako odkaz. Bez záruky: Zdrojový kód v ukázkových modulech plug-in se může časem měnit. Chcete-li získat kód reflektující vše, co je použito v tomto příkladu, můžete si zapůjčit projekt za použití značky verze 3.0 (nejpravděpodobněji R3_0) nebo značky data 28. června 2004.

přehled lokální historie

Tento snímek obrazovky ukazuje synchronizaci lokální historie v pohledu Synchronizace. Můžete zde procházet změny mezi lokálním prostředkem a posledním stavem v historii. Obsahuje vlastní dekorátor pro zobrazení časového razítka spojeného s položkou lokální historie a vlastní akci pro vrácení verze vašeho souboru uložené v lokální historii. Všimněte si také, že se používá standardní prezentace pohledu Synchronizace poskytující anotace problémů, komprimované rozvržení složky a tlačítka navigace.

Definování variant pro lokální historii

Prvním krokem je nadefinování varianty představující prvky z lokální historie. To umožní rozhraním API synchronizace přistupovat k obsahu z lokální historie tak, aby mohl být porovnán s aktuálním obsahem a zobrazen uživateli.

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;
}
}

Rozhraní IFileState již poskytuje přístup k obsahu souboru z lokální historie (tj. implementuje rozhraní IStorage), takže toto bylo jednoduché. Při vytváření varianty obecně musíte poskytnout způsob přístupu k obsahu, identifikátor obsahu, jenž bude zobrazen uživateli pro určení této varianty, a dále název. Metoda asBytes() je potřeba pouze při trvalém uchovávání varianty mezi relacemi.

Dále můžeme vytvořit komparátor variant, který umožňuje výpočet SyncInfo pro porovnání lokálních prostředků a jejich variant. To je opět jednoduché, protože z existence stavu lokální historie plyne, že obsah stavu lokální historie se liší od aktuálního obsahu souboru. Je tomu tak proto, že specifikace lokální historie říká, že stav lokální historie se nevytvoří, dokud se soubor nezmění.

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;
}
}

Víme, že z existence stavu lokální historie plyne, že se liší od lokální, a proto můžeme při porovnání souboru s příslušným stavem lokální historie jednoduše vrátit hodnotu false. Rovněž platí, že synchronizace s lokální historií je pouze oboustranná, protože nemáme přístup k základnímu prostředku, proto se metoda pro porovnání dvou variant prostředku se nepoužívá.

Vezměte na vědomí, že pokud varianta neexistuje (tj. je null), výpočet synchronizace nebude volat metodu porovnání komparátoru. Ta se volá pouze, pokud oba prvky existují. V našem příkladu by k tomuto došlo u souborů, které nemají lokální historii, i u všech složek (které nikdy lokální historii nemají). To musíme vyřešit tak, že nadefinujeme vlastní podtřídu třídy SyncInfo, aby se v těchto případech upravil vypočtený stav synchronizace.

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();
  }
}

Potlačili jsme konstruktor, abychom vždy poskytovali základní třídu rovnou null (protože používáme pouze oboustranné porovnání) a upravili jsme výpočet druhu synchronizace, aby se vracela hodnota IN_SYNC v případě, že není žádný vzdálený prostředek (protože nás zajímají pouze případy, kdy je v lokální historii lokální soubor a stav souboru).

Vytváření odběratele

Nyní vytvoříme odběratele, který bude poskytovat přístup k variantám prostředku v lokální historii. Lokální historii lze uložit pro libovolný soubor v pracovním prostoru, proto odběratel lokální historie bude dohlížet nad všemi prostředky a sada kořenů bude představována všemi projekty v pracovním prostoru. Rovněž není nutné umožňovat aktualizaci odběratele, protože lokální historie se mění pouze tehdy, pokud se mění obsah lokálního souboru. Náš stav můžeme tedy aktualizovat v případě, že se objeví rozdílová data prostředku. Tímto na našem odběrateli historie zůstávají pouze dvě zajímavé metody: získání SyncInfo a procházení pracovním prostorem.

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) {
        // pouze poslední stav
        variant = new LocalHistoryVariant(states[0]);
      } 
    }
    SyncInfo info = new LocalHistorySyncInfo(resource, variant, comparator);
    info.init();
    return info;
  } catch (CoreException e) {
    throw TeamException.asTeamException(e);
  }
}

Odběratel vrátí novou instanci třídy SyncInfo, která bude obsahovat poslední stav souboru v lokální historii. SyncInfo se vytváří s variantou lokální historie pro vzdálený prvek. Pro projekty, složky a soubory bez lokální historie není poskytnuta žádná varianta vzdáleného prostředku. To se projeví tím, že prostředek bude považován za synchronizovaný (in-sync) z důvodu metody calculateKind v naší LocalHistorySyncInfo.

Zbývající kód v odběrateli lokální historie je implementací metody 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);
  }
}

Zajímavým detailem této metody je skutečnost, že vrací neexistující podřízený prvek v případě, že odstraněný prostředek má lokální historii. To umožní našemu odběrateli vracet SyncInfo pro prvky, které existují pouze v lokální historii a již nejsou v pracovním prostoru.

Přidávání účastníka synchronizace lokální historie

Doposud jsme vytvářeli třídy poskytující přístup k SyncInfo pro prvky v lokální historii. V dalším budeme vytvářet prvky uživatelského rozhraní, které nám umožní mít stránku v pohledu Synchronizace za účelem zobrazení posledního stavu historie pro všechny prvky v lokální historii. Máme odběratele, proto je již snadné doplnit pohled Synchronizace. Začněme přidáním bodu rozšíření účastníka synchronizace:

<extension
       point="org.eclipse.team.ui.synchronizeParticipants">
<participant
persistent="false"
icon="synced.png"
class="org.eclipse.team.synchronize.example.LocalHistoryParticipant"
name="Poslední z lokální historie"
id="org.eclipse.team.synchronize.example"/>
</extension>

Dále musíme implementovat prvek LocalHistoryParticipant. Bude podtřídou třídy SubscriberParticipant, která poskytne veškeré výchozí chování pro shromáždění SyncInfo z odběratele a aktualizaci stavů synchronizace v případě změn v pracovním prostoru. Kromě toho přidáme akci pro vrácení prostředků pracovního prostoru zpět do posledního stavu v lokální historii.

Nejprve se podíváme na to, jak se přidává vlastní akce do účastníka.

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("Vrátit zpět k nejnovější lokální historii", configuration) { //$NON-NLS-1$
        protected SynchronizeModelOperation getSubscriberOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
          return new RevertAllOperation(configuration, elements);
        }
      });
  }
}

Zde přidáváme konkrétní akci SynchronizeMoidelAction a operaci. Získáváme zde zdarma určité chování, a to schopnost běžet na pozadí a zobrazovat stav zaneprázdnění u uzlů, na kterých se pracuje. Akce vrací všechny prostředky v pracovním prostoru do posledního stavu v lokální historii. Akce se přidává tím, že se přidá příspěvek akce do konfigurace účastníka. Konfigurace se používá k popisu vlastností použitých při výstavbě stránky účastníka, která bude zobrazovat vlastní uživatelské rozhraní synchronizace.

Účastník bude následujícím způsobem inicializovat konfiguraci, aby se přidala skupina akcí lokální historie do kontextové nabídky:

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());
}

Nyní se podívejme, jak můžeme poskytnout vlastní zdobení. Na posledním řádku výše uvedené metody se registruje následující dekorátor na konfiguraci stránky.

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;
}
}

Dekorátor extrahuje prostředek z modelového prvku, který se zobrazuje v pohledu Synchronizace a připojuje identifikátor obsahu varianty prostředku lokální historie k textovému štítku, který se zobrazuje v pohledu.

Posledním a konečným krokem je poskytnutí průvodce, který vytvoří účastníka lokální historie. Perspektiva týmové synchronizace definuje globální akci synchronizace, která uživatelům umožňuje rychlé vytvoření synchronizace. Kromě toho je schopnost vytvářet synchronizace k dispozici na panelu nástrojů pohledu Synchronizace. Pro začátek vytvořte bod rozšíření průvodce synchronizeWizards:

<extension
point="org.eclipse.team.ui.synchronizeWizards">
<wizard
class="org.eclipse.team.synchronize.example.LocalHistorySynchronizeWizard"
icon="synced.png"
description="Vytváří synchronizaci proti poslednímu stavu lokální historie všech prostředků v pracovním prostoru"
name="Poslední ze synchronizace lokální historie"
id="ExampleSynchronizeSupport.wizard1"/>
</extension>

Tímto se náš průvodce přidá do seznamu a v metodě finish() průvodce jednoduše vytvoříme naše účastníka a přidáme jej do správce synchronizace.

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

Závěr

Uvedli jsme jednoduchý příklad použití rozhraní API pro synchronizaci a hlouběji jsme probrali některé detaily, aby byl náš příklad snazší na pochopení. Napsat odpovídající a přesnou podporu synchronizace není jednoduché, přičemž nejtěžší je správa synchronizačních informací a upozorňování na změny stavu synchronizace. Je-li hotová implementace odběratele, uživatelské rozhraní je již jednoduché - pokud vyhovuje rozhraní asociované se SubscriberParticipants. Další příklady najdete v modulu plug-in org.eclipse.team.example.filesystem. Projděte si podtřídy v pracovním prostoru odběratele (Subscriber) a ISynchronizeParticipant.

V dalším oddílu jsou popsány některé třídy a rozhraní, které vám mohou napomoci napsat odběratele zcela od začátku, včetně ukládání do mezipaměti stavů synchronizace mezi jednotlivými relacemi pracovní plochy.