Resource markers

We know that plug-ins can define specialized file extensions and contribute editors that provide specialized editing features for these file types.  During the course of editing (or building) a resource, a plug-in may need to tag resources to communicate problems or other information to the user. The resource marker mechanism is used to manage this kind of information.

A marker is like a yellow sticky note stuck to a resource. On the marker you can record information about a problem (e.g., location, severity) or a task to be done.  Or you can simply record a location for a marker as a bookmark. 

Users can quickly jump to the marked location within a resource. The workbench UI supports presentation of bookmarks, breakpoints, tasks, and problems along the side of the editor.  These markers can also be shown as items in views, such as the tasks or bookmarks view.

The platform resources API defines methods for creating markers, setting marker values, and extending the platform with new marker types. While the platform manages markers, it is the plug-ins that control their creation, removal and attribute values.

Markers are intended to be small, lightweight objects. There could be hundreds, even thousands of markers in a single project.  For example, the Java compiler uses a marker to flag each problem it finds in source code.

The platform will throw away markers attached to resources that are deleted, but plug-ins are responsible for removing their stale markers when they no longer apply to a resource that still exists.

Marker operations

Manipulating a marker is similar to manipulating a resource.  Markers are handle objects.  You can obtain a marker handle from a resource, but you don't know if it actually exists until you use exists() protocol or otherwise try to manipulate it.  Once you've established that a marker exists, you can query named attributes that may have been assigned to it.

Markers are owned and managed by the platform, which takes care of making markers persistent and notifying listeners as markers are added, deleted, or changed.  Plug-ins are responsible for creating any necessary markers, changing their attributes, and removing them when they are no longer needed.

Marker creation

Markers are not directly created using a constructor. They are created using a factory method (IResource.createMarker()) on the associated resource.

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

To create a marker that has global scope (not associated with any specific resource), you can use the workspace root (IWorkspace.getRoot()) as the resource.

Marker deletion

The code for deleting a marker is straightforward.

   try {
      marker.delete();
   } catch (CoreException e) {
      // Something went wrong
   }

When a marker is deleted, its marker object (handle) becomes "stale." Plug-ins should use the IMarker.exists() protocol to make sure a marker object is still valid.

Markers can be deleted in batch by asking a resource to delete its markers. This method is useful when removing many markers at once or if individual marker references or ids are not available.

   int depth = IResource.DEPTH_INFINITE;
   try {
      resource.deleteMarkers(IMarker.PROBLEM, true, depth);
   } catch (CoreException e) {
      // something went wrong
   }

When deleting a group of markers, you specify a marker type to delete, such as IMarker.PROBLEM, or null to delete all markers. The second argument indicates whether you want to delete subtype markers.  (We'll look a subtypes in a moment when we define new marker types.)  The depth argument controls the depth of deletion. 

You can also delete markers using IWorkspace.deleteMarkers(IMarker []).

Marker attributes

Given a marker, you can ask for its associated resource, its id (unique relative to that resource), and its type. You can also access additional information via generic attributes.

Each type of marker has a specific set of attributes that are defined by the creator of the marker type using naming conventions.  The IMarker interface defines a set of constants containing the standard attribute names (and some of the expected values) for the platform marker types. The following method manipulates attributes using the platform constants.

   IMarker marker = file.createMarker(IMarker.TASK);
   if (marker.exists()) {
      try {
         marker.setAttribute(IMarker.MESSAGE, "A sample marker message");
         marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
      } catch (CoreException e) {
         // You need to handle the case where the marker no longer exists      }
   }

Attributes are maintained generically as name/value pairs, where the names are strings and a value can be any one of the supported value types (boolean, integer, string). The limitation on value types allows the platform to persist the markers quickly and simply.

Querying markers

Resources can be queried for their markers and the markers of their children. For example, querying the workspace root with infinite depth considers all of the markers in the workspace.

   IMarker[] problems = null;
   int depth = IResource.DEPTH_INFINITE;
   try {
      problems = resource.findMarkers(IMarker.PROBLEM, true, depth);
   } catch (CoreException e) {
      // something went wrong
   }

The result returned by findMarkers depends on the arguments passed.  In the snippet above, we are looking for all markers of type PROBLEM that appear on the resource and all of its direct and indirect descendants. 

If you pass null as the marker type, you will get all the marker types associated with the resource. The second argument specifies whether you want to look at the resource's children.  The depth argument controls the depth of the search when you are looking at the resource's children. The depth can be DEPTH_ZERO (just the given resource), DEPTH_ONE (the resource and all of its direct children) or DEPTH_INFINITE (the resource and all of its direct and indirect descendants).

Marker persistence

The platform standard markers (task, problem, and bookmark) are persistent. This means that their state will be saved across workbench shutdown and startup. However, markers of a persistent type may be selectively made transient by setting the reserved attribute transient to true.

New marker types declared by plug-ins are not persistent unless they are declared as such.

Extending the platform with new marker types

Plug-ins can declare their own marker types using the org.eclipse.core.resources.markers extension point. The standard marker types for problems, tasks and bookmarks are declared by the platform in the resources plug-in's markup.

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

New marker types are derived from existing ones using multiple inheritance. New marker types inherit all of the attributes from their super types and add any new attributes defined as part of the declaration. They also transitively inherit attributes from the super types of their super types. The following markup defines a new kind of marker in a hypothetical com.example.markers plug-in.

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

Note that the type org.eclipse.core.resources.problemmarker is actually one of the pre-defined types (aka IMarker.PROBLEM). 

The only aspect of a marker super type that is not inherited is its persistence flag.  The default value for persistence is false, so any marker type that should be persistent must specify <persistent value="true"/>.

After declaring the new marker type in your plug-in manifest file, you can create instances of com.example.markers.myproblem marker type and freely set or get the myAttribute attribute.

Declaring new attributes allows you to associate data with markers that you plan to use elsewhere (in your views and editors). Markers of a particular type do not have to have values for all of the declared attributes. The attribute declarations are more for solving naming convention problems (so everyone uses "message" to talk about a marker's description) than for constraining content.

   public IMarker createMyMarker(IResource resource) {
      try {
         IMarker marker = resource.createMarker("com.example.markers.myproblem");
         marker.setAttribute("myAttribute", "MYVALUE");
         return marker;
      } catch (CoreException e) {
         // You need to handle the cases where attribute value is rejected
      }
   }

You can query your own marker types in the same way you query the platform marker types.  The method below finds all mymarkers associated with the given target resource and all of its descendents. Note that this will also find all myproblems since true is passed for the includeSubtypes argument.

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