Det bästa sättet att förstå synkroniserings-API:er är att skapa ett enkelt exempel som verkligen fungerar. I det här exemplet skapar vi en sida på synkroniseringsvyn som visar det senaste lokala historikläget för alla filer på arbetsytan. Synkroniseringen av den lokala historiken uppdateras automatiskt när ändringar görs av arbetsytan och en jämförelseredigerare kan öppna för att bläddra, sammanfoga och sedan ändra. Vi lägger också till en egen dekorering som visar den senaste tidsstämpeln för det lokala historikelementet och en åtgärd för att återställa arbetsytefilerna till senast sparade lokala historikläge. Detta är ett utmärkt exempel eftersom vi redan har ett lager av tillgängliga resursvarianter som inte behöver hanteras.
I resten av det här exemplet använder vi ett körningsexempel. Mycket, men inte all, källkod finns med på den här sidan. Den fullständiga källkoden hittar du i paketet med lokal historik i insticksprogrammet org.eclipse.team.examples.filesystem. Du kan checka ut projektet från CVS-lagret och använda som referens medan du går igenom den här självstudiekursen. Obs! Källkoden i exempelinsticksprogrammen kan förändras. Om du vill ha en kopia som matchar det som används i exemplet kan du checka ut projektet med hjälp av 3.0 versionsmärkordet (troligen R3_0) eller datummärkningen 28 juni 2004."
På den här skärmbilden visas synkroniseringen av den lokala historiken på synkroniseringsvyn. Du kan använda den för att bläddra genom ändringarna mellan lokal resurs och senaste läget i historiken. Den har en egen dekorering för att visa den tidsstämpel som associeras till den lokala historikposten och en anpassad åtgärd för att återställa filen till innehållet i den lokala historiken. Notera också att standardpresentationen av synkroniseringsvyn används, vilket ger problemanteckningar, komprimerad mapplayout och navigeringsknappar.
Det första steget är att definiera en variant som representerar elementen från lokal historik. Med synkroniserings-API:erna kan du accessa innehållet från den lokala historiken så att den kan jämföras med aktuellt innehåll och visas för användaren.
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;
}
}
Detta var lätt eftersom IFileState-gränssnittet redan tillhandahåller access till innehållet i filen från den lokala historiken (dvs. implementerar IStorage-gränssnittet).
I allmänhet, måste du tillhandahålla ett sätt att accessa innehållet när du skapar en variant, ett innehålls-ID som visas för användaren för att identifiera varianten, och ett namn. asBytes()-metoden behövs bara om varianten ska permanentas mellan sessionerna.
Därefter skapar vi en variantjämförare som tillåter SyncInfo-beräkningen för att jämföra lokala resurser med sina varianter. Även detta är lätt eftersom förekomsten av ett lokalt historikläge antyder att innehållet i det lokala historikläget skiljer sig från filens aktuella innehåll. Detta beror på att specifikationen för lokal historik säger att inget lokalt historikläge skapas om filen inte har ändrats.
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;
}
}
Eftersom vi vet att förekomsten av lokalt historikläge betyder att det skiljer sig från det lokala kan vi helt enkelt returnera false när vi jämför filen med sitt lokala historikläge. Synkronisering med lokal historik är också bara tvåvägs eftersom vi inte har access till någon basresurs så metoden för jämförelse av de två resursvarianterna används inte.
Observera att synkroniseringsberäkningen inte anropar jämförarens jämförelsemetod om varianten inte finns (dvs. är null). Den anropas bara om båda elementen finns. I vårt exempel skulle detta inträffa både för filerna som inte har någon lokal historik och för alla mappar (som aldrig har någon lokal historik). Vi hanterar detta genom att definiera vår egen underordnade klass av SyncInfo för att ändra det beräknade synkroniseringsläget för dessa fall.
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();
}
}
Vi har åsidosatt konstruktorn för att alltid ge en bas som är null (eftersom vi bara använder tvåvägsjämförelse) och vi har ändrat synkroniseringstypen av beräkningen till att returnera IN_SYNC om det inte finns någon fjärr (eftersom vi bara bryr oss om de fall där det finns en lokal fil och ett filläge i den lokala historiken).
Nu skapar vi en prenumerant som ger access till resursvarianterna i den lokala historiken. Eftersom lokal historik kan sparas för alla filer på arbetsytan övervakar den lokala historikprenumeranten alla resurser och den rotuppsättningen är alla projekt på arbetsytan. Det finns inte heller något behov av att ge möjlighet att uppdatera prenumeranten eftersom den lokala historiken bara ändras när innehållet i en lokal fil ändras. Vi kan därför uppdatera vårt läge närhelst resursdelta inträffar. Det ger bara två intressanta metoder i vår lokala historikprenumerant: att få ett SyncInfo och att traversera arbetsytan.
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) {
// bara sista läget
variant = new LocalHistoryVariant(states[0]);
}
}
SyncInfo info = new LocalHistorySyncInfo(resource, variant, comparator);
info.init();
return info;
} catch (CoreException e) {
throw TeamException.asTeamException(e);
}
}
Prenumeranten returnerar en ny SyncInfo-förekomst med filens senaste läge i den lokala historiken. SyncInfo skapas med en lokal historikvariant för fjärrelementet. För projekt, mappar och filer som inte har någon lokal historik tillhandahålls ingen fjärresursvariant, vilket resulterar i att resursen anses vara synkroniserad på grund av calculateKind-metoden i vår LocalHistorySyncInfo.
Resten av koden i den lokala historikprenumeranten är implementationen av medlemsmetoden:
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);
}
}
Det intressanta med den här metoden är att den returnerar icke-befintliga underordnade objekt om en borttagen resurs har lokal historik. Med detta kan prenumerant returnera SyncInfo för element som bara finns i lokal historik och inte längre på arbetsytan.
Fram till nu har vi skapat de klasser som ger access till SyncInfo för element i lokal historik. Härefter skapar vi de användargränssnittselement som gör att vi kan ha en sida på synkroniseringsvyn med det senaste historikläget för varje element i lokal historik. Eftersom vi har en prenumerant är det lätt att lägga till detta på synkroniseringsvyn. Låt oss börja med att lägga till en utökningspunkt för synkroniseringsdeltagare:
<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>
Därefter måste vi implementera LocalHistoryParticipant. Det är en underordnad klass till SubscriberParticipant som ger allt standardbeteende för att samla in SyncInfo från prenumeranten och uppdatera synkroniseringslägen när arbetsytan ändras. Dessutom lägger vi till en åtgärd för att återställa arbetsyteresurserna till det senaste i lokala historiken.
Först tittar vi på hur en anpassad åtgärd läggs till i deltagaren.
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);
}
});
}
}
Här lägger vi till en specifik SynchronizeMoidelAction och åtgärd. Det beteende vi får gratis här är möjligheten att köra i bakgrunden och visa upptagetstatus för de noder där arbete görs. Med åtgärden återställs alla resurser på arbetsytan till senaste läget i lokal historik. Åtgärden läggs till med hjälp av ett åtgärdsbidrag till deltagarkonfigurationen. Konfigurationen används för att beskriva de egenskaper som används för att skapa deltagarsidan som visar det verkliga användargränssnittet för synkronisering.
Deltagaren initierar konfigurationen enligt följande för att lägga till en åtgärdsgrupp för lokal historik på kontextmenyn:
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());
}
Låt oss nu titta på hur vi kan ge en anpassad dekorering. Med den sista raden i ovanstående metod registreras följande dekorering med sidans konfiguration.
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;
}
}
Dekoreringen extraherar resurser från modellelementet som visas på synkroniseringsvyn och lägger till innehålls-ID:t för den lokala historikens resursvariant på den textetikett som visas på vyn.
Den sista och slutliga delen är att tillhandahålla en guide som skapar lokal historikdeltagare . Med gruppsynkroniseringsperspektivet definieras en global synkroniseringsåtgärd så att användare snabbt kan skapa en synkronisering. Dessutom kan synkroniseringar skapas från synkroniseringsvyns verktygsfält. Börja med att skapa en utökningspunkt för synchronizeWizards:
<extension
point="org.eclipse.team.ui.synchronizeWizards">
<wizard
class="org.eclipse.team.synchronize.example.LocalHistorySynchronizeWizard"
icon="synced.png"
description="Skapar en synkronisering mot senaste lokala historikläget för alla resurser på arbetsytan"
name="Senaste från synkronisering av lokal historik"
id="ExampleSynchronizeSupport.wizard1"/>
</extension>
Med detta läggs guiden till i listan och guidernas finish()-metod skapar vi helt enkelt vår deltagare och lägger till den i synkroniseringshanteraren.
LocalHistoryPartipant participant = new LocalHistoryPartipant();
ISynchronizeManager manager = TeamUI.getSynchronizeManager();
manager.addSynchronizeParticipants(new ISynchronizeParticipant[] {participant});
ISynchronizeView view = manager.showSynchronizeViewInActivePage();
view.display(participant);
Det här är ett enkelt exempel på hur du kan använda synkroniserings-API:er. Vi har hoppat över vissa av detaljerna i syfte att göra exemplet mer lättförståeligt.
Att skriva mottaglig och korrekt synkroniseringssupport är inte lätt, den svåraste delen är hanteringen av synkroniseringsinformationen och aviseringar om förändringar av synkroniseringsläge. Användargränssnittet, om det som associeras till SubscriberParticipants är tillräckligt, är den enkla delen eftersom prenumerantimplementation är komplett. Det finns fler exempel i insticksprogrammet org.eclipse.team.example.filesystem där du kan bläddra genom de underordnade klasserna på arbetsytan för prenumeranten och ISynchronizeParticipant.
I nästa avsnitt beskrivs viss klasser och gränssnitt som du kan använda för att skriva en prenumerant från början, inklusive hur du cachelagrar synkroniseringslägen mellan arbetsytesessioner.