Roadmap modello per l'integrazione del modello logico

Di seguito viene riportato un elenco delle attività che i fornitori di modelli possono eseguire per beneficiare del supporto per il modello logico Team:

  1. Adattare gli elementi modello nelle viste dei modelli a ResourceMapping per consentire alle risorse basate su operazioni di comparire nei propri elementi di modello.
  2. Registrare un ModelProvider per assicurare che il proprio modello venga consultato quando si eseguono operazioni sulle risorse correlate al proprio modello.
  3. Utilizzare ResourceChangeValidator quando si eseguono operazioni su risorse allo scopo di assicurare che qualsiasi potenziale effetto collaterale sugli elementi di modelli correlati a queste risorse siano resi noti all'utente.
  4. Implementare un IResourceMappingMerger per partecipare in unioni headless in cui sono coinvolte risorse correlate al proprio modello.
  5. Registrare un teamContentProvider allo scopo di prendere parte in visualizzatori Team come l'anteprima unioni.
  6. Fornire un IHistoryPageSource per mostrare la cronologia del modello logico nella vista della cronologia Team.
  7. Utilizzare l'API Eclipse File System per accedere allo stato remoto di progetti del modello.
  8. Utilizzare l'API SynchronizationStateTester per assicurare decoro appropriato degli elementi del modello che non dispongono di un'associazione uno a uno alle risorse.

Le seguenti sezioni descrivono ciascun punto in maniera più dettagliata. Il plig-in org.eclipse.team.examples.filesystem contiene un esempio che illustra diversi di questi punti. È possibile estrarre il progetto dal repository CVS e utilizzarlo come riferimento durante la lettura di questa esercitazione. Dichiarazione di responsabilità: il codice di origine nel plug-ins di esempio può cambiare con il tempo. Per ottenere una copia che corrisponda a codice utilizzato in questo esempio, è possibile estrarre il progetto utilizzando il tag della versione 3.2 (quasi sicuramente R3_2) oppure un tag con data 28 giugno 2006.

Associazioni alle risorse

API per l'associazione di risorse di base

L'API per l'associazione di risorse è semplice e non presenta le manipolazioni del modello logico. Un client non può utilizzare questa interfaccia per visualizzare modelli logici oppure per ottenere ulteriori informazioni su di essi. La sua funzione è semplicemente quella di associare uno o più elementi modello alle risorse dello spazio di lavoro.

Questa API è formata dalle seguenti classi:

Esistono due tipi di plugin che dovrebbero essere interessati nell'associazione di risorse. Quelli che forniscono un modello composto di risorse nello spazio di lavoro e quelli che desiderano eseguire operazioni su risorse. I primi verranno trattati nella sezione successiva, mentre gli altri verranno descritti nella sezione Readmap del repository per l'integrazione dei modello logici.

Adattamento di un modlelo a un ResourceMapping

I plugin che hanno adattato i propri oggetti modello a IResource allo scopo di ottenere azioni specifiche delle risorse visualizzate nel menu di scelta rapida possono ora adattarsi a ResourceMapping se una descrizione più ricca del modo in cui l'oggetto si adatta alle risorse è utile. Tuttavia, se non c'è alcun vantaggio, non è necessario che effettuino questa operazione. Ad esempio, un'unità di compilazione Java (ossia un file *.java mostrato in una vista JDT) che ora si adatta a IFile non deve necessariamente adattarsi a ResourceMapping, poiché non si ottiene nulla. Ad ogni modo, un pacchetto Java dovrebbe adattarsi a ResourceMapping per indicare che il pacchetto è formato solo da file della cartella corrispondente e non delle sottocartelle.

Il modo preferito per adattare elementi di modello a un'associazione di risorse consiste nell'utilizzare un factory per adattatore. La seguente è la markup XML per fornire un factory per adattatore in un manifest di plugin.

   <extension
point="org.eclipse.core.runtime.adapters">
<factory
class="org.eclipse.example.library.logical.AdapterFactory"
adaptableType="org.eclipse.example.library.Book">
<adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/>
</factory>
<factory
class="org.eclipse.example.library.logical.AdapterFactory"
adaptableType="org.eclipse.example.library.Library">
<adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/>
</factory>
...
</extension>

L'implementazione del factory per adattatore dovrebbe essere simile a quanto riportato di seguito:

public class AdapterFactory implements IAdapterFactory {
public Object getAdapter(Object adaptableObject, Class adapterType) {
if((adaptableObject instanceof EObject) && adapterType == ResourceMapping.class) {
return new EObjectResourceMapping((EObject)adaptableObject);
}
return null;
}

public Class[] getAdapterList() {
return new Class[] {ResourceMapping.class};
}
}

Gli oggetti modello possono implementare l'interfaccia IAdaptable. In questi casi, devono assicurare che il gestore adattatori della piattaforma venga consultato. Questa operazione può essere eseguita creando delle sottoclassi di PlatformObject oppure utilizzando la seguente riga di codice:

Platform.getAdapterManager().getAdapter(Object, Class)

È questo l'approccio preferibile. Tuttavia, l'oggetto modello può implementare l'interfaccia IAdaptable e fornire un'implementazione di getAdapter(Class) crea un'istanza di ResourceMapping esplicitamente quando ne viene richiesta una. Questo è l'approccio più semplice ma il meno opportuno, in quanto il modello deve avere la conoscenza esplicita dell'adattamento alle risorse.

In alcuni casi, è possibile che il fornitore di un modello logico non desideri che il proprio modello si adatti a IResource in ogni contesto oppure che l'oggetto si adatti in maniera diversa ai contributi di oggetti rispetto ad altri contesti. A tale scopo, l'interfaccia utente del workbench fornisce una speciale API di adattatore intermedio IContributorResourceAdapter. Quando gli oggetti si adattano a IResource nel contesto dei contributi di oggetti, come prima cosa il workbench cerca di adattare la risorsa a IContributorResourceAdapter prima di provare ad adattarla direttamente a IResource. Una nuova sottointerfaccia di questa interfaccia, IContributorResourceAdapter2, è stata aggiunta e che fornisce la stessa funzionalità per ResourceMapping. L'unica differenza è che il fornitore di modelli dovrebbe registrare un factory per IContributorResourceAdapter in quanto il Workbench esegue un controllo instanceof per vedere se l'adattatore fornito è anche un'istanza di IContributorResourceAdapter2.

L'implementazione della sottoclasse ResourceMapping per un pacchetto Java dovrebbe essere simile a quella riportata di seguito.

public class JavaPackageResourceMapping extends ResourceMapping {
IPackageFragment package;
...
public getModelObject() {
return package;
}
public ResourceTraversals[] getTraversals(
ResourceMappingContext context,
IProgressMonitor monitor) {
return new ResourceTraversal[] {
new ResourceTraversal(
new IResource[] { package.getCorrespondingResource() },
IResource.DEPTH_ONE, IResource.NONE)
}
}
}

Questa è un'associazione piuttosto semplice, quindi l'implementazione non è complessa. La complessita dell'implementazione dell'associazione di risorse varierà, naturalmente, da modello a modello.

Contesto dell'associazione di risorse

Uno dei vantaggi dell'API di associazione di risorse è che consente ai plugin di implementare qualsiasi operazioni desiderino in termini di associazione di risorse (ad esempio, aggiornamento CVS, commit CVS, tag CVS, decorazioni non finali e così via). However, the API that has been introduced so far deals only with the local state of the model. When working with a model that may be shared between developers, you end up in a situation where the remote state of the model (i.e. the state of the model that another user has checked-in to the repository) may differ from the state in the workspace. If you performed a CVS update, you would want the local state of the model to match the remote state even if it meant that additional files needed to be included or some files needed to be removed.

This is not an issue for some logical models. Ad esempio, un pacchetto java è un contenitore visitato in profondità uno, indipendentemente dallo stato remoto del modello. Dato questo, un provider di repository può facilmente stabilire che le eliminazioni in uscita devono essere incluse durante il commit o che le aggiunte in arrivo debbano essere incluse durante l'aggiornamento. Tuttavia, le risorse che costituiscono alcuni modelli logici possono cambiare nel tempo. Ad esempio, le risorse che costituiscono un elemento di modello dipendono dal contenuto di un file manifest (o qualche altro meccanismo simile). Affinché l'associazione di risorsa ritorni al passaggio corretto, deve accedere al contenuto remoto del file manifest (differisce dal contenuto locale) per vedere se ci sono risorse aggiuntive che devono essere incluse. Queste risorse aggiuntive possono non esistere nello spazio di lavoro, ma il fornitore di repository sa come accertarsi che erano presenti quando l'azione selezionata è stata eseguita.

Per supportare questi modelli più complessi, un RemoteResourceMappingContext può passare a un metodo ResourceMapping#getTraversals. Quando viene fornito un contesto, la mappatura può utililizzarlo per garantire che tutte le risorse necessarie siano incluse nel passaggio. Se un contesto non viene fornito, la mappatura può presumere che solo lo stato locale è di interesse.

When does a ResourceMapping need to worry about the RemoteResourceMappingContext?

A ResourceMapping need only worry about a context supplied to the getTraversals method in cases were the resources that make up a model change over time and the relationship between the model and resources cannot be described by a simple traversal that is guaranteed to encompass those resources (and only those resources) that constitute the model. For example, although the resources of a Java package may change over time, the package can be described as a folder of depth of one so a resource mapping for java packages would not ned to make use of the resource mapping context.

As a more complicated example, consider an HTML file that contains several images. Let's make the assumption that any images references from an HTML file are part of the model of that file. When updating the local contents of the HTML file from a repository, the user would expect that any new images would be included. The getTraversals method for a ResourceMapping for the HTML file model would look something like this:

public class HTMLResourceMapping extends ResourceMapping {
private HTMLFile htmlFile;
public ResourceTraversal[] getTraversals(ResourceMappingContext context,
IProgressMonitor monitor)
IResource[] resources = htmlFile.getResources();
if (context instanceof RemoteResourceMappingContext) {
// Look for any additional resources on the server
RemoteResourceMappingContext remoteContext = (RemoteResourceMappingContext)context;
IFile file = htmlFile.getFile();
if (remoteContext.hasRemoteChange(file, monitor)) {
IStorage storage = remoteContext.fetchRemoteContents(file, monitor);
IResource[] additionalResources = getReferences(storage.getContents());
resources = combine(resources, additionalResources);
}
if (remoteContext.isThreeWay() && remoteContext.hasLocalChange(file, monitor)) {
IStorage storage = remoteContext.fetchBaseContents(file, monitor);
IResource[] additionalResources = getReferences(storage.getContents());
resources = combine(resources, additionalResources);
}
}
return new ResourceTraversal[] {
new ResourceTraversal(resources, IResource.DEPTH_ZERO, IResource.NONE)};
}
}

Notice that there are two sets of resources included in the model: those derived from the local contents of the HTML file in the workspace and those obtained from the contents of the remote file and base file. In either of these two sets, there may be resources that do not exist in the workspace. For instance, the local HTML file may contain a relative link to an image that does not exist in the workspace. This resource should be included so that it will be fetched if it exists remotely. As for the remote file, it may contain a new copy that references additional images that should be fetched when the new remote contents are downloaded.

Model Providers

Model providers are a means to group related resource mappings together. Here is a link to the ModelProvider class. This class serves three main purposes:

  1. From a model provider, clients can then obtain additional API pieces for performing operations on a set of resource mappings using the adaptable mechanism. For example, the IResourceMappingMerger for the model is obtained by adapting the model provider.
  2. Given a set of file-system resources, clients can query whether a model provider has model elements persisted in those resources and, if it does, obtain the set of resource mappings that describe the relationship.
  3. For operations on a set of resources, the resource change validator will query the model providers in order to determine if there are any potential side effects of an operation that users should be made aware of. This is covered in a separate section on change validation.

The following is an example of a modelProvider extension definition.

   <extension
id="modelProvider"
name="Library Example"
point="org.eclipse.core.resources.modelProviders">
<modelProvider
class="org.eclipse.team.examples.library.adapt.LibraryModelProvider"
name="Library Example"/>
<extends-model id="org.eclipse.core.resources.modelProvider"/>
<enablement> <test property="org.eclipse.core.resources.projectNature" value="org.eclipse.team.examples.library.view.nature" />
</enablement>
</extension>

The LibraryModelProvider is a subclass of ModelProvider. The enablement rule is used to match resources that the Library model persists its model in. In the above example, the model provider will match any resource in a project that has the library nature.

Once the model provider is defined, the ResourceMapping#getModelProviderId() method should be overridden to return the id of the model provider.

   public String getModelProviderId() {
return "org.eclipse.team.examples.library.adapt.modelProvider";
}

To get the proper inverse mapping of resources to resource mapping for those resources that match your provider's enablement rule, you should also override one or both of the getMapping methods. The method that you need to override depends on whether your model has elements that contain multiple resources or not. If your model elements map to a single resource, you can override the method that accepts a singleIResource argument. Otherwise, you will need to override the method that accepts an array of resources. Here's an example using the single resource case.

The following example method wraps a library model file in an appropriate resource mapping. It also wraps folders that contain files that are of interest to the model provider.

public class LibraryModelProvider extends ModelProvider {
public ResourceMapping[] getMappings(IResource resource,
ResourceMappingContext context, IProgressMonitor monitor) {
if (isModelFile(resource)) {
// Return a resource mapping on the file
return new LibraryResourceMapping(resource);
} if (containsModelFiles(resource)) {
// Create a deep resource mapping on the container
return new LibraryContainerResourceMapping(resource);
}
// The resource is not of interest to this model provider
return null;
}
}

Clients can then access the model provider to determine whether the model providers cares about the resources that are about to be operated on. The next section describes API that will be provided to team operations that use the model provider API to determine the complete set of resource mappings to be operated on when a team operation is performed on a set of selected resources or model elements.

Resource Change Validation

Operations performed on resources should be validated first to ensure that the user is aware of any potential side effects. Here are the steps required to validate a resource change.

  1. Build up a description of the change using the IResourceChangeDescriptionFactory. The factory produces an IResourceDelta that mirrors what the resulting resource delta will look like once the operation is performed.
  2. Validate the change using the ResourceChangeValidator. The validator consults all the model providers that have registered an interest in the affected resources. The result is one or more status that contain the id of the originating model and a description of the potential side effect of the operation on the model.
  3. Make the user aware of any potential side effects from models that are unknown to the originator of the operation. For instance, if a Java refactoring has received a side effect from the Java model, it can be ignored since the refactoring understands the semantics of the Java model. However, if a side effect from the Library model is returned, it should be made known to the user since Java has no knowledge of the Library model.

Unione basata sul modello

Quando un fornitore Team tenta un'unione senza intestazione:

  1. Otterrà le associazioni di risorse dagli elementi selezionati
  2. Determinerà i fornitori di modelli coinvolti utilizzando il metodo ResourceMapping#getModelProvider().
  3. Espanderà l'ambito dell'operazione per includere tutte le associazioni di risorse necessarie.
  4. Creerà una descrizione dello stato di sincronizzazione tra locale e remoto. Questa descrizione viene fornita ai modelli attraverso l'API IMergeContext.
  5. Adatterà i fornitori di modelli a IResourceMappingMerger.
  6. Richiamerà il metodo validateMerge su ciascun merger, passando nella descrizione di sincronizzazione, per assicurarsi che non ci sono condizioni che impediscano un tentativo di unione.
  7. Delegherà l'unione ai merger dei modelli per eseguire l'unione.
  8. I fornitori di modelli possono delegare di nuovo l'unione dei singoli file al fornitore Team se desiderano solo controllare l'ordine dell'unione o poter eseguire essi stessi l'unione e segnalare al fornitore team ciò che hanno fatto.

Contenuto del modello nelle viste Operazione team

La visualizzazione degli elemento di modello nel contesto di una operazione team è possibile grazie al framework Selezione comune.

La procedura riportata sopra consentirà ai modelli di essere visualizzati in finestre utilizzate dalle operazioni del team. Ci sono ulteriori passaggi da completare per l'integrazione nell'anteprima di un'unione.

Vista Cronologia

I seguenti miglioramenti sono stati apportati all'area della cronologia dei file e alla cronologia degli elementi del modello:

Navigazione in remoto

Per supportare la navigazione in remoto:

Decorazione degli elementi dei modelli con lo stato team

I fornitori team pososno decorare gli elemento del modello convertendo i propri decoratori per utilizzare le associazioni per risorse cosi come i contributi oggetto vengono convertiti per utilizzare associzioni per le risorse. Tuttavia, c'è un aspetto della decorazione degli elemento del modello logico che è problematico. Se un modello non presenta un'associazione uno-a-uno con una risorsa, l'elemento di modello potrebbe non ricevere l'aggiornamento dell'etichetta quando le risorse sottostanti cambiano.

Per risolvere questo problema, è stato introdotto un ITeamStateProvider per consentire al fornitore di modleli di accedere alla modifiche allo stato che possono influire sulle decorazioni team. Inoltre, le viste del modello possono utilizzare un SynchronizationStateTester per determinare quando occorre aggiornare le etichette degli elementi dei modello logici. Questa API utilizza l'interfaccia ITeamStateProvider per determinare quando lo stato team della risorsa è cambiato ed è possibile passare a un decoratore team come parte di IDecorationContext.