Modellübersicht für die Integration logischer Modelle

Die folgende Liste erläutert, wie Modellprovider die Teamunterstützung für logische Modelle nutzen können:

  1. Mit der Übernahme eines Objekts ResourceMapping durch Modellelemente in Modellsichten können ressourcenbasierte Operationen für die Modellelemente angezeigt werden.
  2. Wenn Sie ein Objekt ModelProvider registrieren, können Sie sicherstellen, dass Ihr Modell bei der Ausführung von Operationen für die Ressourcen, denen das Modell zugeordnet ist, berücksichtigt wird.
  3. Mit dem Prüfprogramm ResourceChangeValidator können Sie bei der Ausführung von Operationen für Ressourcen sicherstellen, dass der Benutzer Kenntnis über mögliche Nebeneffekte bei den Modellelementen enthält, denen diese Ressourcen zugeordnet sind.
  4. Durch die Implementierung einer Schnittstelle IResourceMappingMerger ist die Teilnahme an automatischen Mischvorgängen möglich, in die die Ressourcen, die zu Ihrem Modell gehören, einbezogen werden.
  5. Durch die Registrierung von teamContentProvider ist eine Teilnahme an Anzeigefunktionen für Teams wie an den Vorschauen für Mischvorgänge möglich.
  6. Durch die Bereitstellung von IHistoryPageSource kann das Protokoll des logischen Modells in der Sicht mit dem Teamprotokoll angezeigt werden.
  7. Durch die Verwendung der Eclipse-Dateisystem-API ist der Zugriff auf den fernen Status von Modellprojekten möglich.
  8. Durch die Verwendung der API SynchronizationStateTester kann die korrekte Dekoration von Modellelementen sichergestellt werden, die eine Eins-zu-eins-Zuordnung zu Ressourcen aufweisen.

Die folgenden Abschnitte beschreiben die obigen Aspekte ausführlicher. Das Plug-in org.eclipse.team.examples.filesystem enthält ein Beispiel, das einige dieser Punkte veranschaulicht. Sie können das Projekt aus dem CVS-Repository auschecken und während dieses Lernprogramms als Referenz verwenden. Hinweis: Der Quellcode der Beispiel-Plug-ins kann sich im Laufe der Zeit ändern. Um eine Kopie zu erhalten, die diesem Beispiel entspricht, können Sie das Projekt mit der Kennung der Version 3.2 (wahrscheinlich R3_2) oder einer Datumskennung vom 28. Juni 2006 auschecken.

Ressourcenzuordnungen

Basis-API für Ressourcenzuordnung

Die API für die Ressourcenzuordnung ist absichtlich einfach und ermöglicht keine Bearbeitung von logischen Modellen. Ein Client kann diese Schnittstelle nicht dazu verwenden, logische Modelle anzuzeigen oder zusätzliche Informationen über sie zu erhalten. Sie ist lediglich dazu gedacht, einem oder mehreren Modellelementen Arbeitsbereichsressourcen zuzuordnen.

Die API besteht aus den folgenden Klassen:

Es gibt zwei Typen von Plug-ins, die für Ressourcenzuordnungen wichtig sind. Zum einen handelt es sich hierbei um Plug-ins, von denen ein Modell bereitgestellt wird, das aus Ressourcen im Arbeitsbereich besteht bzw. in diesen permanent enthalten ist, sowie um Plug-ins, die Operationen für Ressourcen ausführen sollen. Der erste Typ wird im nächsten Abschnitt behandelt. Angaben zum zweiten Typ finden Sie im Thema Repository-Übersicht für die Integration logischer Modelle.

Übernahme einer Klasse "ResourceMapping" durch ein Modell

Plug-ins, deren Modellobjekte die Schnittstelle IResource übernehmen, um ressourcenspezifische Aktionen für das Kontextmenü abzurufen, können nun die Klasse ResourceMapping übernehmen, wenn eine umfangreichere Beschreibung für die Übernahme von Ressourcen durch das Objekt gewünscht wird. Wenn dies nicht der Fall ist, ist eine Übernahme der Klasse jedoch nicht erforderlich. Beispiel: Eine Java-Kompiliereinheit (d. h. eine Datei "*.java", die in einer JDT-Sicht angezeigt wird), die gegenwärtige IFile übernimmt, muss die Klasse ResourceMapping nicht unbedingt übernehmen, weil dies keinen nennenswerten Nutzen mit sich bringt. Ein Java-Paket hingegen sollte die Klasse ResourceMapping übernehmen, um anzugeben, dass die im Paket enthaltenen Dateien ausschließlich aus dem entsprechenden Ordner und nicht aus den Unterordnern stammen.

Es hat sich bewährt, zur Übernahme von Ressourcenzuordnungen durch Modellelemente eine Adapterfactory zu verwenden. Die folgende XML-Markup-Formatierung ergänzt eine Adapterfactory in einem Plug-in-Manifest:

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

Die Implementierung der Adapterfactory könnte in etwa so aussehen:

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

Modellobjekte können die Schnittstelle IAdaptable implementieren. In diesem Fall müssen sie sicherstellen, dass der Adaptermanager der Plattform einbezogen wird. Hierzu kann entweder eine Unterklasse von PlatformObject gebildet oder die folgende Codezeile verwendet werden:

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

Die Verwendung der Codezeile ist die bessere Methode. Das Modellobjekt kann jedoch die Schnittstelle "IAdaptable" implementieren und eine Implementierung von getAdapter(Class) bereitstellen, die ein Exemplar von ResourceMapping erstellt und zurückgibt, wenn dies ausdrücklich angefordert wird. Dieser Ansatz ist zwar stringenter, aber weniger wünschenswert, weil das Modell explizites Konstruktionswissen über die Übernahme von Ressourcen besitzen muss.

In manchen Fällen gibt der Provider eines logischen Modells vor, dass das Modell die Schnittstelle IResource nicht in jedem Kontext übernehmen soll. Außerdem kann es sein, dass das Objekt bei Objektergänzungen andere Übernahmen vollziehen soll, als bei anderen Kontexten. Die Benutzerschnittstelle der Workbench stellt zu diesem Zweck eine spezielle temporäre Adapter-API namens IContributorResourceAdapter bereit. Wenn Objekte im Kontext von Objektergänzungen die Schnittstelle IResource übernehmen, versucht die Workbench zunächst, die Schnittstelle IContributorResourceAdapter durch die Ressource übernehmen zu lassen, bevor eine direkte Übernahme der Schnittstelle IResource vorgenommen wird. Es wurde die neue untergeordnete Schnittstelle IContributorResourceAdapter2 dieser Schnittstelle hinzugefügt, die dieselbe Funktionalität für die Klasse ResourceMapping bereitstellt. Der einzige Unterschied besteht darin, dass der Modellprovider eine Factory für IContributorResourceAdapter registrieren sollte, da die Workbench mit einer Prüfung des Typs instanceof feststellt, ob der ergänzte Adapter ebenfalls ein Exemplar von IContributorResourceAdapter2 ist.

Die Implementierung der Unterklasse ResourceMapping für ein Java-Paket könnte etwa folgendermaßen aussehen:

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

Diese Zuordnung ist ziemlich stringent, weshalb die Implementierung nicht komplex ist. Die Komplexität der Implementierung für die Ressourcenzuordnung variiert jedoch natürlich je nach Modell.

Ressourcenzuordnungskontext

Einer der Vorteile einer Ressourcenzuordnungs-API ist die Tatsache, dass Plug-ins jede gewünschte Operation im Hinblick auf Ressourcenzuordnungen implementieren können (z. B. CVS-Aktualisierung, CVS-Festschreibung, CVS-Tags, Änderungsdekorationen usw.). Die bislang eingeführte API kann jedoch lediglich zur Bearbeitung des lokalen Statuswerts des Modells verwendet werden. Bei der Arbeit an einem Modell, das von Entwicklern gemeinsam genutzt werden kann, ergibt sich irgendwann die Situation, dass der ferne Status des Modells (also der Status des Modells, den ein anderer Benutzer im Repository eingecheckt hat) möglicherweise vom Status im Arbeitsbereich abweicht. Im Anschluss an eine CVS-Aktualisierung sollte der lokale Status des Modells mit dem fernen Status übereinstimmen, selbst wenn zu diesem Zweck zusätzliche Dateien aufgenommen oder einige Dateien entfernt werden müssten.

Bei einigen logischen Modellen stellt dies kein Problem dar. Ein Java-Paket ist beispielsweise ein Container, der - unabhängig vom fernen Status des Modells - nur eine Tiefenebene aufweist, die verwendet werden kann. Infolgedessen kann ein Repositoryprovider ohne großen Aufwand feststellen, dass beim Festschreiben abgehende Löschvorgänge oder bei der Aktualisierung eingehende Ergänzungen aufgenommen werden sollten. Es kann jedoch sein, dass sich die Ressourcen, aus denen sich einige logische Modelle zusammensetzen, mit der Zeit ändern. So sind die Ressourcen, die ein Modellelement bilden, möglicherweise vom Inhalt einer Manifestdatei (oder einem anderen ähnlichen Mechanismus) abhängig. Damit die Ressourcenzuordnung die richtige Traversierung zurückgibt, muss sie auf den fernen Inhalt der Manifestdatei zugreifen (falls dieser sich vom lokalen Inhalt unterscheidet), um feststellen zu können, ob zusätzliche Ressourcen aufgenommen werden müssen. Diese zusätzlichen Ressourcen sind zwar unter Umständen nicht im Arbeitsbereich vorhanden, aber der Repository-Provider ist in diesem Fall in der Lage sicherzustellen, dass sie bei einer Ausführung der ausgewählten Aktion zur Verfügung stehen.

Um diese komplexeren Modelle zu unterstützen, kann ein Objekt RemoteResourceMappingContext an die Methode ResourceMapping#getTraversals übergeben werden. Wenn ein Kontext zur Verfügung steht, kann die Zuordnung mithilfe dieses Objekts sicherstellen, dass alle erforderlichen Ressourcen in der Traversierung enthalten sind. Wird kein Kontext bereitgestellt, kann die Zuordnung davon ausgehen, dass nur der lokale Status von Bedeutung ist.

Situationen, in denen der Wert von "RemoteResourceMappingContext" für die Klasse "ResourceMapping" wichtig ist

Eine Klasse ResourceMapping muss nur in solchen Fällen den Kontext berücksichtigen, der für die Methode getTraversals bereitgestellt wird, in denen sich die Ressourcen, aus denen ein Modell besteht, mit der Zeit ändern und die Beziehung zwischen dem Modell und den Ressourcen nicht durch eine einfache Traversierung beschrieben werden kann, die diese Ressourcen (und nur diese Ressourcen) als Bestandteile des Modells garantiert berücksichtigt. Beispiel: Obwohl sich die Ressourcen eines Java-Pakets mit der Zeit ändern können, kann das Paket als Ordner mit einer Tiefe von einer Ebene beschrieben werden. Daher muss eine Ressourcenzuordnung für Java-Pakete den Ressourcenzuordnungskontext nicht verwenden.

Nehmen wir zur Veranschaulichung eines etwas komplizierteren Falls an, dass eine HTML-Datei mehrere Images enthält. Alle Imageverweise aus einer HTML-Datei sind Bestandteil des Modells für diese Datei. Bei einer Aktualisierung des lokalen Inhalts der HTML-Datei aus einem Repository würde der Benutzer erwarten, dass alle neuen Images aufgenommen werden. Die Methode getTraversals für ein Objekt ResourceMapping für das Modell der HTML-Datei könnte etwa folgendermaßen aussehen:

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

Sie können feststellen, dass das Modell zwei Gruppen von Ressourcen enthält, nämlich Ressourcen, die aus dem lokalen Inhalt der HTML-Datei im Arbeitsbereich abgeleitet sind, sowie Ressourcen, die aus dem Inhalt der fernen Datei und der Basisdatei abgerufen werden. Beiden Gruppen können Ressourcen enthalten, die im Arbeitsbereich nicht vorhanden sind. Beispielsweise kann die lokale HTML-Datei einen relativen Link zu einem Image enthalten, das im Arbeitsbereich nicht vorhanden ist. Diese Ressource sollte aufgenommen werden, damit sie abgerufen wird, wenn sie in der fernen Position vorhanden ist. Die ferne Datei könnte eine neue Kopie enthalten, in der auf zusätzliche Images verwiesen wird, die beim Download des neuen fernen Inhalts abgerufen werden müssen.

Modellprovider

Mit Modellprovidern können zusammengehörige Ressourcenzuordnungen gruppiert werden. Die Klasse ModelProvider (Link) dient drei Hauptzwecken:

  1. Aus einem Modellprovider können Clients zusätzliche API-Elemente abrufen, um Operationen für eine Gruppe von Ressourcenzuordnungen mit dem übernahmefähigen Mechanismus auszuführen. Die Schnittstelle IResourceMappingMerger für das Modell wird beispielsweise durch die Übernahme des Modellproviders abgerufen.
  2. Für eine gegebene Gruppe von Dateisystemressourcen können Clients abfragen, ob die Modellelemente eines Modellproviders in diesen Ressourcen permanent gespeichert sind, und in einem solchen Fall die Gruppe der Ressourcenzuordnungen abrufen, mit denen die Beziehung beschrieben wird.
  3. Bei Operationen für eine Gruppe von Ressourcen fragt das Prüfprogramm für Ressourcenänderungen die Modellprovider ab, um festzustellen, ob eine Operation mögliche Nebeneffekte mit sich bringt, über die die Benutzer informiert werden müssen. Dieser Vorgang ist in einem eigenen Abschnitt über die Änderungsprüfung erläutert.

Das folgende Beispiel zeigt eine Erweiterungsdefinition für "modelProvider":

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

Die Klasse LibraryModelProvider ist eine Unterklasse von ModelProvider. Mit der Aktivierungsregel werden Ressourcen abgeglichen, in denen das Modell des Bibliotheksmodells permanent gespeichert ist. Im obigen Beispiel gleicht der Modellprovider alle Ressourcen in einem Projekt ab, für das die Gattung "Bibliothek" definiert ist.

Sobald der Modellprovider definiert wurde, sollte die Methode ResourceMapping#getModelProviderId() so überschrieben werden, dass sie die ID des Modellproviders zurückgibt.

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

Um für solche Ressourcen, die mit der Aktivierungsregel des Providers übereinstimmen, die richtige umgekehrte Zuordnung von Ressourcen zur Ressourcenzuordnung abzurufen, sollen Sie außerdem eine oder beide der Methoden getMapping überschreiben. Welche Methode Sie überschreiben müssen, richtet sich danach, ob Ihr Modell Elemente verwendet, die mehrere Ressourcen enthalten. Falls die Elemente des Modells einer einzelnen Ressource zugeordnet sind, können Sie die Methode überschreiben, die ein einziges Argument für IResource akzeptiert. Andernfalls müssen Sie die Methode überschreiben, die einen Bereich von Ressourcen akzeptiert. Das folgende Beispiel verwendet den Fall einer einzelnen Ressource.

In diesem Beispiel wird eine Bibliotheksmodelldatei in eine entsprechende Ressourcenzuordnung eingeschlossen. Außerdem sind Ordner eingeschlossen, in denen Dateien enthalten sind, die für den Modellprovider von Bedeutung sind.

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 können anschließend auf den Modellprovider zugreifen, um festzustellen, ob die Ressourcen, für die Operationen ausgeführt werden sollen, für den Modellprovider von Bedeutung sind. Der nächste Abschnitt beschreibt die API, die für Teamoperationen bereitgestellt wird, bei denen mit der Modellprovider-API die vollständige Gruppe der Ressourcenzuordnungen ermittelt wird, für die eine Operation erforderlich ist, wenn eine Teamoperation für eine Gruppe von ausgewählten Ressourcen oder Modellelementen ausgeführt wird.

Ressourcenänderungsprüfung

Operationen, die für Ressourcen ausgeführt werden, sollten zunächst ausgewertet werden, um sicherzustellen, dass der Benutzer über mögliche Nebeneffekte unterrichtet wird. Die folgenden Schritte sind erforderlich, um eine Ressourcenänderung auszuwerten.

  1. Erstellen Sie mit IResourceChangeDescriptionFactory eine Beschreibung der Änderung. Die Factory erzeugt ein Objekt IResourceDelta, in dem das Aussehen des Ressourcendeltas angegeben ist, das aus der Ausführung der Operation resultieren würde.
  2. Werten Sie die Änderung mit dem Prüfprogramm ResourceChangeValidator aus. Das Prüfprogramm fragt alle Modellprovider ab, für die registriert ist, dass die betroffenen Ressourcen von Bedeutung sind. Das Ergebnis besteht aus einer oder mehreren Statusangaben, die die ID des ursprünglichen Modells sowie eine Beschreibung der möglichen Nebeneffekte der Operation auf das Modell enthalten.
  3. Benachrichtigen Sie den Benutzer von möglichen Nebeneffekten auf Modelle, die dem Veranlasser der Operation nicht bekannt sind. Falls eine Java-Refactoringoperation beispielsweise einen Nebeneffekt vom Java-Modell mitgeteilt bekommt, kann dieser ignoriert werden, weil die Refactoringoperation die Semantik des Java-Modells kennt. Wird jedoch ein Nebeneffekt vom Bibliotheksmodell zurückgegeben, muss dieser dem Benutzer mitgeteilt werden, weil Java keine Kenntnis vom Bibliotheksmodell hat.

Modellbasierte Mischvorgänge

Wenn ein Team-Provider versucht, einen automatischen Mischvorgang vorzunehmen, führt er hierzu die folgenden Aktionen durch:

  1. Die Ressourcenzuordnungen werden aus den ausgewählten Elementen abgerufen.
  2. Die involvierten Modellprovider werden mit der Methode "ResourceMapping#getModelProvider()" ermittelt.
  3. Der Bereich der Operation wird so erweitert, dass alle erforderlichen Ressourcenzuordnungen eingeschlossen sind.
  4. Es wird eine Beschreibung des Synchronisationsstatus' zwischen dem lokalen und dem fernen Standort erstellt. Diese Beschreibung wird den Modellen über die API IMergeContext zur Verfügung gestellt.
  5. Die Modellprovider übernehmen IResourceMappingMerger.
  6. Die Methode "validateMerge" wird für jede Mischfunktion aufgerufen (als Eingabe wird die Beschreibung der Synchronisierung verwendet), um sicherzustellen, dass keine Bedingung vorliegt, die einen Mischversuch verhindert.
  7. Der Mischvorgang wird an die Modellmischfunktionen delegiert, die den Mischvorgang ausführen.
  8. Modellprovider können den Mischvorgang für einzelne Dateien wieder an den Team-Provider delegieren, wenn sie lediglich die Reihenfolge des Mischvorgangs steuern wollen, oder aber den Mischvorgang selbst ausführen und seinen Abschluss dem Team-Provider melden.

Modellinhalt in Sichten für Teamoperationen

Die Anzeige von Modellelementen im Kontext einer Teamoperation ist im allgemeinen Navigatorgerüst möglich.

Die obigen Schritte ermöglichen das Anzeigen von Modellen in Dialogen, die von Teamoperationen verwendet werden. Die Integration in eine Vorschau für einen Mischvorgang erfordert zusätzliche Schritte.

Sicht "Protokoll"

Die Bereiche des Dateiprotokolls und des Modellelementprotokolls wurden folgendermaßen erweitert:

Ferne Positionen anzeigen

Die folgende Unterstützung wurde für das Anzeigen ferner Positionen hinzugefügt:

Modellelemente mit Teamstatusdekoratoren versehen

Team-Provider können Modellelemente mit Dekoratoren versehen, indem sie ihre einfachen Dekoratoren so konvertieren, dass sie zusammen mit Ressourcenzuordnungen verwendet werden können. Die Konvertierung erfolgt genauso wie bei Objektergänzungen, die für die Zusammenarbeit mit Ressourcenzuordnungen konvertiert werden. Es gibt jedoch einen problematischen Aspekt bei der Dekoration von logischen Modellelementen. Falls ein Modellelement keine Eins-zu-eins-Zuordnung zu einer Ressource aufweist, empfängt das Modellelement möglicherweise keine Bezeichnungsaktualisierung, wenn sich die zu Grunde liegende Ressource ändert.

Um dieses Problemstellung zu lösen, wurde ITeamStateProvider eingeführt, damit Modellprovider auf Statusänderungen zugreifen können, die sich möglicherweise auf Teamdekorationen auswirken. Außerdem können Modellsichten mit der Testfunktion SynchronizationStateTester ermitteln, ob die Bezeichnungen von logischen Modellelementen aktualisiert werden müssen. Diese API nutzt die Schnittstelle ITeamStateProvider, um zu ermitteln, ob sich der Teamstatus einer Ressource geändert hat und als Teil von IDecorationContext an einen Teamdekorator übergeben werden kann.