Marqueurs des ressources

Nous savons que les plug-ins peuvent définir des extensions de fichiers spéciales et ajouter des éditeurs qui fournissent des fonctions d'édition spécialisées pour ces types de fichiers. Au cours de l'édition (ou de la génération) d'une ressource, un plug-in peut devoir marquer les ressources pour communiquer des problèmes ou d'autres informations à l'utilisateur. Le mécanisme du marqueur de ressources est utilisé pour gérer ce type d'informations.

Un marqueur est semblable à un post-it collé sur une ressource. Sur le marqueur, vous pouvez consigner des informations sur un problème (emplacement, gravité) ou sur une tâche à effectuer, ou tout simplement enregistrer l'emplacement d'un marqueur comme signet.

Les utilisateurs peuvent passer rapidement à l'emplacement marqué dans une ressource. L'interface utilisateur du plan de travail supporte la présentation des signets, des points d'arrêt, des tâches et des problèmes au côté de l'éditeur. Ces marqueurs peuvent également être indiqués en tant qu'éléments dans des vues, telles que la vue des tâches ou des signets.

L'API des ressources de la plate-forme définit des méthodes pour la création des marqueurs, la définition des valeurs de marqueur et l'extension de la plate-forme avec de nouveaux types de marqueur. Alors que la plate-forme gère les marqueurs, les plug-ins contrôlent leurs valeurs de création, de suppression et d'attribut.

Les marqueurs sont destinés à être de petits objets légers. Il peut y avoir des centaines, voire même des milliers de marqueurs dans un seul projet. Par exemple, le compilateur Java utilise un marqueur pour indiquer chaque problème qu'il trouve dans le code source.

La plate-forme élimine les marqueurs liés à des ressources supprimées, mais les plug-ins sont responsables de la suppression des marqueurs périmés lorsque ceux-ci ne s'appliquent plus à une ressource qui existe toujours.

Opérations de marquage

La manipulation d'un marqueur est similaire à celle d'une ressource. Les marqueurs sont des objets descripteur. Vous pouvez en obtenir à partir d'une ressource, mais sans savoir s'il existe tant que vous n'avez pas utilisé le protocole exists() ou essayé de l'employer. Une fois établi que le marqueur existe, vous pouvez interroger des attributs désignés pouvoir lui avoir été assignés.

Les marqueurs sont détenus et gérés par la plate-forme qui prend soin de rendre les marqueurs persistants et de notifier les modules d'écoute chaque fois qu'un marqueur est ajouté, supprimé ou modifié. Les plug-ins sont responsables de la création de tous les marqueurs nécessaires, de la modification de leurs attributs et le leur suppression lorsqu'ils ne sont plus requis.

Création de marqueurs

Les marqueurs ne sont pas directement créés à l'aide d'un constructeur. Ils sont créés à l'aide d'une méthode de la fabrique (IResource.createMarker()) sur la ressource associée.

   IMarker marker = file.createMarker(IMarker.TASK);

Pour créer un marqueur ayant une portée globale (non associé à une ressource spécifique), vous pouvez utiliser la racine de l'espace de travail (IWorkspace.getRoot()) comme ressource.

Suppression de marqueurs

Le code de suppression d'un marqueur est simple.

   try {
      marker.delete();
   } catch (CoreException e) {
      // Un incident a eu lieu.
   }

Lorsqu'un marqueur est supprimé, son objet descripteur devient "périmé". Les plug-ins doivent utiliser le protocole IMarker.exists() pour s'assurer qu'un objet descripteur est encore valide.

Les marqueurs peuvent être supprimés par lot en demandant à une ressource de supprimer ses marqueurs. Cette méthode est utile lors de la suppression simultanée d'un grand nombre de marqueurs ou si des références ou des ID de marqueur individuels ne sont pas disponibles.

   int depth = IResource.DEPTH_INFINITE;
   try {
      resource.deleteMarkers(IMarker.PROBLEM, true, depth);
   } catch (CoreException e) {
      // Un incident a eu lieu.
   }

Lors de la suppression d'un groupe de marqueurs, vous spécifiez un type de marqueur à supprimer, tel que IMarker.PROBLEM ou null pour supprimer tous les marqueurs. Le second argument indique si vous souhaitez supprimer un sous-type de marqueurs (nous les aborderons plus loin lors de la définition de nouveaux types de marqueurs).L'argument depth contrôle la profondeur de la suppression.

Vous pouvez également supprimer des marqueurs avec IWorkspace.deleteMarkers(IMarker []).

Attributs de marqueur

Pour un marqueur donné, vous pouvez demander sa ressource associée, son ID (unique, relatif à cette ressource) et son type. Vous pouvez également accéder à des informations supplémentaires via des attributs génériques.

Chaque type de marqueur est doté d'un ensemble spécifique d'attributs, définis par le créateur du type de marqueur à l'aide des conventions de dénomination. L'interface IMarker définit un ensemble de constantes contenant les noms d'attribut standard (et certaines des valeurs attendues) pour les types de marqueurs de la plate-forme. La méthode ci-dessous manipule des attributs à l'aide des constantes de la plate-forme.

   IMarker marker = file.createMarker(IMarker.TASK);
   if (marker.exists()) {
      try {
         marker.setAttribute(IMarker.MESSAGE, "Message exemple
de marqueur");
         marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
      } catch (CoreException e) {
         // Inutile de vous soucier si le marqueur n'existe plus.
   }

Les attributs sont gérés de manière générique en tant que paires nom-valeur, où les noms sont des chaînes et une valeur peut être l'un des types de valeur supportés (booléen, entier, chaîne). Les limites concernant les types de valeur permettent à la plate-forme de conserver les marqueurs rapidement et simplement.

Requête de marqueurs

Les ressources peuvent être sollicitées pour leurs marqueurs et les marqueurs de leurs enfants. Par exemple, l'interrogation de la racine de l'espace de travail dans une infinie profondeur prend en compte la totalité des marqueurs de l'espace de travail.

   IMarker[] problems = null;
   int depth = IResource.DEPTH_INFINITE;
   try {
      problems = resource.findMarkers(IMarker.PROBLEM, true, depth);
   } catch (CoreException e) {
      // Un incident a eu lieu.
   }

Le résultat renvoyé par findMarkers dépend des arguments transmis. Dans le fragment de code ci-dessus, nous recherchons tous les marqueurs de type PROBLEM qui s'affichent dans la ressource et tous les ses descendants directs et indirects. 

Si vous transmettez la valeur null comme type de marqueur, vous obtiendrez tous les types de marqueur associés à la ressource. Le second argument spécifie si vous souhaitez considérer les enfants de la ressource. L'argument depth contrôle la profondeur de la recherche lorsque vous considérez les enfants de la ressource. La profondeur peut être DEPTH_ZERO (juste la ressource donnée), DEPTH_ONE (la ressource et tous ses enfants directs) ou DEPTH_INFINITE (la ressource et tous ses enfants directs et indirects).

Persistance des marqueurs

Les marqueurs standard de plate-forme (tâche, incident et signet) sont persistants. Cela signifie que leur état sera sauvegardé par delà les arrêts et les démarrages du plan de travail. Toutefois, des marqueurs d'un type persistant peuvent devenir transitoires en accordant à l'attribut réservé transient la valeur true.

De nouveaux types de marqueurs déclarés par des plug-ins ne sont pas persistants à moins qu'ils ne soient déclarés tels.

Extension de la plate-forme avec de nouveaux types de marqueurs

Les plug-ins peuvent déclarer leurs propres types de marqueurs à l'aide du point d'extension org.eclipse.core.resources.markers. Les types de marqueurs standard pour les incidents, les tâches et les signets sont déclarés par la plate-forme dans les marques du plug-in des ressources.

   <extension
      id="problemmarker" 
      point="org.eclipse.core.resources.markers" 
      name="%problemName">
      <super type="org.eclipse.core.resources.marker"/>
      <persistent value="true"/>
      <attribute name="severity"/>
      <attribute name="message"/>
      <attribute name="location"/>
   </extension>
   <extension
      id="taskmarker" 
      point="org.eclipse.core.resources.markers" 
      name="%taskName">
      <super type="org.eclipse.core.resources.marker"/>
      <persistent value="true"/>
      <attribute name="priority"/>
      <attribute name="message"/>
      <attribute name="done"/>
      <attribute name="userEditable"/>      
   </extension>
   <extension
      id="bookmark" 
      point="org.eclipse.core.resources.markers" 
      name="%bookmarkName">
      <super type="org.eclipse.core.resources.marker"/>
      <persistent value="true"/>
      <attribute name="message"/>
      <attribute name="location"/>
   </extension>

De nouveaux types de marqueurs sont dérivés des existants via un héritage multiple. Ils héritent de la totalité des attributs de leurs types supérieurs et ajoutent de nouveaux attributs définis dans la déclaration. Ils héritent également de manière transitive des types supérieurs de leurs types supérieurs. Les marques ci-dessous définissent un nouveau type de marqueur dans un hypothétique plug-in com.example.markers.

   <extension
      id="mymarker"
      point="org.eclipse.core.resources.markers" />
   <extension
      id="myproblem"
      point="org.eclipse.core.resources.markers">
      <super type="org.eclipse.core.resources.problemmarker"/>
      <super type="com.example.markers.mymarker"/>
      <attribute name="myAttribute" />
      <persistent value="true" />
   </extension>

Notez que le type org.eclipse.core.resources.problemmarker est en fait l'un des types prédéfinis (IMarker.PROBLEM). 

Le seul aspect dont ils n'héritent pas d'un super type de marqueur est son indicateur de persistance. La valeur par défaut de la persistance est "false", ainsi tout type de marqueur devant être persistant doit être spécifié comme suit : <persistent value="true"/>.

Une fois le nouveau type de marqueur déclaré dans le fichier manifeste du plug-in, vous pouvez créer des instances de type de marqueur com.example.markers.myproblem et définir librement ou obtenir l'attribut myAttribute.

La déclaration de nouveaux attributs vous permet d'associer des données aux marqueurs que vous avez l'intention d'utiliser ailleurs (dans des vues ou des éditeurs). Les marqueurs d'un type particulier n'ont pas besoin d'avoir de valeur pour tous les attributs déclarés. Les déclarations d'attribut sont plutôt destinées à la résolution des problèmes de conventions de dénomination (ainsi tout le monde utilise le terme "message" pour parler de description d'un marqueur) qu'à la limitation du contenu.

   public IMarker createMyMarker(IResource resource) {
      try {
         IMarker marker = resource.createMarker("com.example.markers.myproblem");
         marker.setAttribute("myAttribute", "MYVALUE");
         return marker;
      } catch (CoreException e) {
         // Vous devez traiter les cas où la valeur d'attribut est rejetée.
      }
   }

Vous pouvez interroger vos propres types de marqueur de la même manière que vous le faites pour les types de marqueur de la plate-forme. La méthode ci-après recherche tous les marqueurs mymarkers associés à la ressource cible donnée et à tous ses descendants. Notez que tous les marqueurs myproblems seront également recherchés du fait que la valeur "true" est transmise pour l'argument includeSubtypes.

   public IMarker[] findMyMarkers(IResource target) {
      String type = "com.example.markers.mymarker";
      IMarker[] markers = target.findMarkers(type, true, IResource.DEPTH_INFINITE);
   }