Den bedste måde at forstå synkroniserings-API'erne på er at oprette et enkelt eksempel, der faktisk virker. I dette eksempel opretter vi en side i oversigten Synkronisér, der viser den seneste lokale historiktilstand for alle filer i arbejdsområdet. Synkroniseringen af den lokale historik opdateres automatisk, når der foretages ændringer af arbejdsområdet, og der kan åbnes en sammenligningseditor, så brugeren kan gennemse og flette ændringerne. Vi tilføjer også en tilpasset dekoratør for at vise det seneste tidsstempel for det lokale historikelement og en funktion, der bringer arbejdsområdefilerne tilbage til den senest gemte lokale historiktilstand. Det er et fremragende eksempel, fordi vi allerede har et lager af ressourcevarianter til rådighed, og vi ikke behøver at administrere det.
I resten af denne gennemgang bruger vi et aktivt eksempel. Det meste af kildekoden findes på denne side, men der mangler en lille smule. Hele kildekilden findes i pakken med lokal historik i plugin'en org.eclipse.team.examples.filesystem. Du kan tjekke projektet ud fra CVS-opbevaringsstedet og bruge det som reference, mens du læser denne øvelse. Ansvarsfraskrivelse: Kildekoden i eksempelplugins kan ændres over tid. Hvis du vil hente en kopi, der matcher den, som bruges i dette eksempel, kan du tjekke projektet ud ved brug af 3.0-versionskoden (sandsynligvis R3_0) eller datokoden 28. juni 2004.
Dette skærmbillede viser synkronisering af lokal historik i oversigten Synkronisér. Du kan bruge det til at gennemse forskelle mellem den lokale ressource og den seneste tilstand i historikken. Der findes en tilpasset dekoratør til fremvisning af det tidsstempel, som er knyttet til den lokale historikværdi, og en tilpasset funktion til at bringe filen tilbage til indholdet i den lokale historik. Bemærk også, at standardpræsentationen i oversigten Synkronisér bruges til at levere problemannotationer, komprimeret folderlayout og navigationsknapper.
Det første trin er at definere en variant, som skal repræsentere elementerne fra den lokale historik. Herved kan synkroniserings-API'erne få adgang til indholdet fra den lokale historik, så det kan sammenlignes med det aktuelle indhold og vises for brugeren.
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;
}
}
Da grænsefladen IFileState allerede giver adgang til indholdet af filen fra lokal historik (altså implementerer grænsefladen IStorage), var dette nemt.
Når du opretter en variant, skal du normalt levere en måde at få adgang til indholdet på, en indholds-id, der vises for brugeren, så varianten kan identificeres, og et navn. Metoden asBytes() kræves kun, hvis varianten skal opbevares mellem sessioner.
Lad os derefter oprette en variantsammenligner, der giver SyncInfo-beregningen mulighed for at sammenligne lokale ressourcer med deres varianter. Igen er dette nemt, fordi eksistensen af en lokal historik tilstand indebærer, at indholdet af den lokale historiktilstand adskiller sig fra indholdet i filen. Dette skyldes, at specifikationen for lokal historik siger, at der ikke oprettes en lokal historiktilstand, hvis filen ikke er ændret.
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;
}
}
Da vi ved, at eksistensen af den lokale historik indebærer, at den er forskellig fra den lokale, kan vi simpelthen returnere false, når vi sammenligner filen med dens lokale historiktilstand. Desuden er synkroniseringen med den lokale historik kun tovejs, fordi vi ikke har adgang til en basisressource, så metoden til sammenligning af to ressourcevarianter bruges ikke.
Bemærk at synkroniseringsberegningen ikke kalder sammenlignerens sammenligningsmetode, hvis varianten ikke eksisterer (dvs. er NULL). Den kaldes kun, hvis begge elementer eksisterer. I vores eksempel sker dette både for filer, der ikke har en lokal historik, og for alle foldere (som aldrig har en lokal historik). For at håndtere dette skal vi definere vores egen underklasse af SyncInfo for at ændre den beregnede synkroniseringstilstand for disse tilfælde.
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 sat konstruktøren ud af funktion, så den altid leverer en basis, der er NULL (eftersom vi kun bruger envejssammenligning), og vi har ændret beregningen af synkroniseringstype til at returnere IN_SYNC, hvis der ikke er nogen ekstern (da vi kun interesserer os for tilfælde, hvor der er en lokal fil og en filtilstand i den lokale historik).
Nu skal vi oprette en abonnent, der leverer adgang til ressourcevarianterne i den lokale historik. Da lokal historik kan gemmes for alle filer i arbejdsområdet, vil den lokale historikabonnent supervisere alle ressourcer, og rodsættet vil være alle projekter i arbejdsområdet. Desuden er der ikke behov for at levere mulighed for at opfriske abonnenten, da den lokale historik kun ændres, når indholdet af en lokal fil ændres. Derfor kan vi opdatere vores tilstand, hver gang der finder et ressourcedelta sted. Tilbage bliver der kun to interessante metoder i den lokale historikabonnent: opnåelse af en SyncInfo og gennemløb af arbejdsområdet.
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) {
// kun sidste tilstand
variant = new LocalHistoryVariant(states[0]);
}
}
SyncInfo info = new LocalHistorySyncInfo(resource, variant, comparator);
info.init();
return info;
} catch (CoreException e) {
throw TeamException.asTeamException(e);
}
}
Abonnenten returnerer en ny SyncInfo-forekomst, der indeholder filens seneste tilstand i den lokale historik. SyncInfo oprettes med en lokal historikvariant for det eksterne element. For projekter, foldere og filer uden lokal historik leveres der ikke nogen ekstern ressourcevariant, hvilket vil bevirke, at ressourcen betragtes som synkroniseret på grund af metoden calculateKind i vores LocalHistorySyncInfo.
Den resterende kode i den lokale historikabonnent er implementeringen af metoden 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);
}
}
Den interessante detalje ved denne metode er, at den returnerer ikke-eksisterende underordnede, hvis en slettet ressource har lokal historik. Dette vil give vores abonnent mulighed for at returnere SyncInfo for elementer, der kun findes i lokal historik og ikke længere findes i arbejdsområdet.
Indtil nu har vi oprettet de klasser, der giver adgang til SyncInfo for elementer i lokal historik. Nu vil vi oprette de UI-elementer, der giver os mulighed for at få en side i oversigten Synkronisér til at vise den seneste historiktilstand for alle elementer i lokal historik. Da vi har en abonnent, er det nemt at tilføje dette til oversigten Synkronisér. Lad os starte med at tilføje et udvidelsespunkt til synkroniseringsdeltageren:
<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>
Dernæst skal vi implementere LocalHistoryParticipant. Den vil oprette en underklasse af SubscriberParticipant, som leverer hele den nødvendige funktionalitet til indsamling af SyncInfo fra abonnenten og opdatering af synkroniseringstilstande, når der finder ændringer sted af arbejdsområdet. Desuden tilføjer vi en funktion, som bringer arbejdsområdet tilbage til den seneste tilstand i lokal historik.
Først ser vi på, hvordan en tilpasset funktion tilføjes til deltageren.
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);
}
});
}
}
Her tilføjer vi en specifik SynchronizeMoidelAction og funktion. Den funktionsmåde, vi får gratis med her, er muligheden for at køre i baggrunden og vise optaget-status for de noder, der arbejdes på. Funktionen bringer alle ressourcer i arbejdsområdet tilbage til deres seneste tilstand i lokal historik. Du tilføjer funktionen ved at tilføje et funktionsbidrag til deltagerkonfigurationen. Konfigurationen bruges til at beskrive de egenskaber, der bruges til at bygge den deltagerside, der vises i det faktiske synkroniserings-UI.
Deltageren initialiserer konfigurationen på følgende måde for at tilføje funktionsgruppen for den lokale historik til kontekstmenuen:
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());
}
Lad os nu se, hvordan vi kan levere en tilpasset dekoration. Sidste linje i ovenstående metode registrerer følgende dekoratør i sidens konfiguration.
offentlig 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;
}
}
Dekoratøren udtrækker ressourcen fra det modelelement, der vises i oversigten Synkronisér, og tilføjer indholds-id'en for den lokale historiks ressourcevariant til den tekstetiket, der vises i oversigten.
Det sidste, der skal gøres, er et levere en guide, som opretter den lokale historikdeltager. Perspektivet Teamsynkronisering definerer en global synkroniseringsfunktion, der giver brugerne mulighed for at oprette en synkronisering. Desuden er muligheden for at oprette synkroniseringer tilgængelig fra værktøjslinjen i oversigten Synkronisér. Start med at oprette et synchronizeWizards-udvidelsespunkt:
<extension
point="org.eclipse.team.ui.synchronizeWizards">
<wizard
class="org.eclipse.team.synchronize.example.LocalHistorySynchronizeWizard"
icon="synced.png"
description="Opretter en synkronisering mod den seneste lokale historik og alle ressourcer i arbejdsområdet"
name="Seneste fra lokal historiksynkronisering"
id="ExampleSynchronizeSupport.wizard1"/>
</extension>
Herved tilføjes vores guide til listen, og i guidemetoden finish() opretter vi simpelthen vores deltager og tilføjer den til synkroniseringsstyringen.
LocalHistoryPartipant participant = new LocalHistoryPartipant();
ISynchronizeManager manager = TeamUI.getSynchronizeManager();
manager.addSynchronizeParticipants(new ISynchronizeParticipant[] {participant});
ISynchronizeView view = manager.showSynchronizeViewInActivePage();
view.display(participant);
Dette er et enkelt eksempel på brug af synkroniserings-API'erne, og vi har forenklet nogle af detaljerne for at gøre eksemplet nemmere at forstå.
Det er ikke trivielt at skrive lydhør og nøjagtig understøttelse af synkronisering. Det sværeste er styringen af synkroniseringsoplysningerne og beskederne om ændringer af synkroniseringstilstanden. Hvis den brugergrænseflade, der følger med SubscriberParticipants, er tilstrækkelig, er brugergrænsefladen den nemme del, så snart implementeringen af abonnenten er udført. Du kan finde flere eksempler i plugin'en org.eclipse.team.example.filesystem og gennemse underklasserne i arbejdsområdet for Subscriber og ISynchronizeParticipant.
I næste afsnit beskrives nogle af de klasser og grænseflader, der kan hjælpe dig med at skrive en abonnent fra bunden, herunder hvordan du cacher synkroniseringstilstande mellem arbejdsbænkssessioner.