Eksempel på lokal historikk

Den beste måten å lære om programmeringsgrensesnitt for synkronisering på, er gjennom å opprette et enkelt eksempel som fungerer. I dette eksempelet skal vi opprette en side i synkroniseringsvisningen som viser den siste tilstanden for lokal historikk for alle filer i arbeidsområdet. Synkroniseringen av lokal historikk oppdateres automatisk når det utføres endringer i arbeidsområdet, og det kan åpnes et redigeringsprogram for sammenlikninger for å bla gjennom, slå sammen og deretter foreta endringer. Vi skal også legge til en egendefinert dekoratør for å vise den siste tidsangivelsen for det lokale historikkelementet, og en handling som gjenoppretter arbeidsområdefilene til siste lagrede tilstand for lokal historikk. Dette er et utmerket eksempel fordi vi allerede har et lager med tilgjengelige ressursvarianter som vi ikke trenger å håndtere.

Her skal vi se et eksempel på hvordan du bruker et kjøringseksempel. Mye av kildekoden er med på denne siden, men ikke all. Du finner den fullstendige kildekoden i pakken med lokal historikk, i plugin-modulen for org.eclipse.team.examples.filesystem. Du kan hente ut prosjektet fra CVS-datalageret og bruke det som en referanse mens du leser denne opplæringen. Obs: Kildekoden i eksempel-plugin-modulen kan bli endret. Hvis du vil ha en kopi som samsvarer det som brukes i dette eksempelet, kan du sjekke ut prosjektet med 3.0-versjonskoden (sannsynligvis R3_0) eller med datoen 28. juni 2004.

Oversikt over lokal historikk

I dette skjermbildet ser du synkroniseringen av lokal historikk i synkroniseringsvisningen. Du kan bruke den til å bla gjennom endringene mellom den lokale ressursen og den siste tilstanden i historikken. Den har en egen dekoratør som viser tidsangivelsen som er tilknyttet oppføringen for den lokale historikken, og en egen handling som gjenoppretter filen til innholdet i den lokale historikken. Merk også at presentasjonen av standard synkroniseringsvisning brukes for å oppgi problemannotasjoner, komprimert mappelayout og navigeringsknapper.

Definere varianter for lokal historikk

Det første trinnet går ut på å definere en variant som representerer elementene fra lokal historikk. Dette gjør det mulig for programmeringsgrensesnittene for synkronisering å få tilgang til innholdet i lokal historikk slik at det kan sammenliknes med det gjeldende innholdet og vises for brukeren.

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

Dette er enkelt siden IFileState-grensesnittet allerede gir tilgang til innholdet i filen fra den lokale historikken (det vil si implementerer IStorage-grensesnittet). Når du oppretter en variant, må du vanligvis oppgi en måte å få tilgang til innholdet på, en innholds-ID som vises for brukeren for å identifisere varianten, og et navn. asBytes()-metoden kreves bare om varianten skal opprettholdes mellom sesjonene.

La oss opprette en variantsammenlikningsfunksjon slik at SyncInfo-beregningen kan sammenlikne lokale ressurser med variantene. Selv dette er enkelt ettersom forekomsten av den lokale historikktilstanden antyder at innholdet i den lokale historikktilstanden skiller seg fra det gjeldende innholdet i filen. Årsaken til dette er at spesifikasjonen for lokal historikk angir at det ikke vil bli opprettet en lokal historikktilstand, hvis filen ikke er endret.

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

Ettersom vi vet at forekomsten av den lokale historikktilstanden angir at den skiller seg fra den lokale, kan vi helt enkelt returnere false når filen sammenliknes med den lokale historikktilstanden. Synkronisering med den lokale historikken er også bare toveis fordi vi ikke har tilgang til en baseressurs, derfor brukes ikke metoden for sammenlikning av de to ressursvariantene.

Merk at synkroniseringsberegningen ikke kaller sammenlikningsmetoden for sammenlikningsfunksjonen hvis varianten ikke finnes (det vil si er "null"). Den kalles bare hvis begge elementene finnes. I vårt eksempel skal dette skje både for filer som ikke har lokal historikk, og for alle mapper (som ikke har lokal historikk). Vi håndterer dette ved å definere vår egen subklasse av SyncInfo og endrer den beregnede synkroniseringstilstanden for disse tilfellene.

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 overstyrt konstruktøren slik at vi alltid har en base som er null (ettersom vi bare bruker toveis sammenlikning), og vi har endret synkroniseringstypen for beregningen slik at den returnerer IN_SYNC hvis det ikke finnes eksternt (siden vi bare vil ha tilfeller der det er en lokal fil og en filtilstand i den lokale historikken.)

Opprette abonnenten

Vi skal nå opprette en abonnent som gir tilgang til ressursvariantene i den lokale historikken. Siden det er mulig å lagre den lokale historikken for alle filer i arbeidsområdet, overvåker den lokale historikkabonnenten alle ressurser og settet med røtter er alle prosjekter i arbeidsområdet. Det er heller ikke behov for å gjøre det mulig å oppdatere abonnenten siden den lokale historikken bare endres når innholdet i en lokal fil endres. Derfor kan vi oppdatere tilstanden når det oppstår en ressursdelta. Vi har dermed bare to aktuelle metoder i den lokale historikkabonnenten: hente en SyncInfo og traversere arbeidsområ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) {
        // 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);
  }
}

Abonnenten returnerer en ny forekomst av SyncInfo som inneholder den siste tilstanden for filen i den lokale historikken. SyncInfo opprettes med en lokal historikkvariant for det eksterne elementet. Det oppgis ikke en ekstern ressursvariant for prosjekter, mapper og filer uten lokal historikk. Dette betyr at ressursen betraktes som synkronisert på grunn av calculateKind-metoden i LocalHistorySyncInfo.

Den øvrige koden i den lokale historikkabonnenten er implementeringen av members-metoden:

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 som er interessant med denne metoden, er at den returnerer ikke-eksisterende underordnede objekter hvis en ressurs som er slettet, har lokal historikk. Dette gjør det mulig for abonnenten å returnere SyncInfo for elementer som bare finnes i lokal historikk, og ikke lenger i arbeidsområdet.

Legge til en synkroniseringsdeltaker for lokal historikk

Frem til nå har vi opprettet de klassene som gir tilgang til SyncInfo for elementer i lokal historikk. Vi skal nå opprette brukergrensesnittelementer som gjennom en side i synkroniseringsvisningen gjør det mulig å vise den siste historikktilstanden for alle elementene i lokal historikk. Siden vi har en abonnent er det enkelt å legge til synkroniseringsvisningen. Vi begynner med å legge til et utvidelsespunkt for en synkroniseringsdeltaker:

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

Vi må nå implementere LocalHistoryParticipant. Det blir brukt en subklasse for SubscriberParticipant som gir all standardfunksjonalitet for innsamling av SyncInfo fra abonnenten, og oppdaterer synkroniseringstilstander når det oppstår endringer i arbeidsområdet. I tillegg skal vi legge til en handling som gjenoppretter arbeidsområderessursene til seneste tilstand i lokal historikk.

Først skal vi imidlertid se hvordan en egen handling legges til i deltakeren.

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 legger vi til en bestemt SynchronizeMoidelAction og operasjon. Den funksjonaliteten vi får gratis her, er muligheten til å kjøre i bakgrunnen og vise opptattstatus for de nodene som vi jobber med. Handlingen gjenoppretter ressursene i arbeidsområdet til den siste tilstanden i lokal historikk. Handlingen legges til ved å legge til et handlingsbidrag i deltakerkonfigurasjonen. Konfigurasjonen brukes til å beskrive egenskapene som brukes ved bygging av deltakersiden, som viser det faktiske synkroniseringsgrensesnittet.

Deltakeren initialiserer følgende konfigurasjon for å legge til en handlingsgruppe for lokal historikk på hurtigmenyen:

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

Vi skal nå se nærmere på hvordan vi oppgir en tilpasset dekorasjon. Den siste linjen i metoden ovenfor registrerer følgende dekoratør med konfigurasjonen av siden.

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

Dekoratøren trekker ut ressursen fra modellelementet som vises i synkroniseringsvisningen, og tilføyer innholds-IDen i ressursvarianten for den lokale historikken i tekstetiketten som vises i visningen.

Det siste og endelige delen er å oppgi en veiviser som oppretter den lokale historikkdeltakeren. Perspektivet Gruppesynkronisering definerer en global synkroniseringshandling som lar brukere raskt opprette en synkronisering. Dessuten er det mulig å opprette synkroniseringer via verktøylinjen i synkroniseringsvisningen. Begynn med å oppette utvidelsespunkt for 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>

Dette legger til veiviseren i listen. I veiviserens finish()-metode oppretter vi ganske enkelt deltakeren og legger den til i synkroniseringsstyreren.

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

Avslutningsvis

Dette er et enkelt eksempel på hvordan programmeringsgrensesnittet for synkronisering kan brukes. Vi har hoppet over detaljene for å gjøre eksemplene så enkle som mulig. Det å skrive operativ og nøyaktig synkroniseringsstøtte er ikke enkelt, spesielt gjelder dette styring av synkroniseringsinformasjon og varsling om endringer i synkroniseringstilstanden. Brukergrensesnittet, hvis det som er tilknyttet SubscriberParticipants er tilstrekkelig, er enkelt siden abonnentimplementeringen er komplett. Hvis du vil se flere eksempler, kan du se i plugin-modulen for org.eclipse.team.example.filesystem og gå gjennom subklassene i arbeidsområdet i abonnenten og ISynchronizeParticipant.

Vi skal nå gå videre og se på noen klasser og grensesnitt som kan være nyttige når du skriver en abonnent fra grunnen av, blant annet hvordan du bufrer synkroniseringstilstander mellom arbeidsbenksesjoner.