Papyrus Banner

Plug-in Builder Framework

As part of the Element Types plug-in validation builder work under bug 569357, a reusable Plug-in Builder framework based on injection of configurable project checkers is introduced. The two main goals of this framework are

  1. To make generic plug-in checkers that do 80% of the work for their specific aspect of the plug-in validation builder, with the model-specific logic injected for the other 20%. This should accelerate the development of plug-in builders for other models and ensure some kind of consistency in their behaviour.
  2. To change the builder approach from one in which the checkers each create problem markers to one in which they all report problems to a common sink in which they are sifted, redundant problems elided, and from which then the resource markers are created in the workspace.

The following figure shows an overview of the generic plug-in builder framework:

API Overview

API Details

Package oep.toolsmiths.validation.common.checkers

In previous releases, this package defined an interface IPluginChecker, to implementations of which the PluginValidationService used by the plug-in validation menu actions delegates the work of validating the project and creating problem markers. The new framework adds an alternative IPluginChecker2 that does the same, except that it reports problems as Diagnostics to a DiagnosticChain instead of creating resource markers. This is critical to externalizing the analysis of problems and optimization of the marker creation at the end of the validation process.

A new class AbstractPluginChecker unifies the old and new checker protocols, implementing the IPluginChecker::check(IProgressMonitor) method by invocation of the new IPluginChecker::check(DiagnosticChain, IProgressMonitor) API and creating markers from the collected diagnostics. This provides compatibility for the existing project validation menu actions and the legacy approach of marker creation. But the AbstractPluginChecker leaves the new check operation for subclasses to implement each in their own way.

Five concrete checker classes are defined to implement the IPluginChecker2 protocol:

ExtensionsChecker

The ExtensionsChecker is based on a PluginErrorReporter that extends ManifestErrorReporter from the PDE internals. Like other checkers, this one is configured by injection of computations, but in this case what is injected is a model-specific configuration of the PluginErrorReporter. So, there is another level of indirection in this checker for the most common cases of checking for the existence of and validating the content of extension elements that register the model.

The PluginErrorReporter is configured with strategies for finding extension elements on various extension points and (optionally) for checking those elements in the case that they exist (absence of the element doesn't require a checker to report that problem). The injected element checkers are given a call-back interface for reporting problems that knows how to map elements and attributes to lines in the source plugin.xml file. This problem reporting API, as per the PDE framework, create VirtualMarkers to encapsulate the information that later will fill resource markers. This is managed by an IncrementalErrorReporter which in this case is replaced by an implementation (DiagnosticErrorReporter) that doesn't create actual resource markers but which generates Diagnostics and appends them to the collector chain, in accordance with the general protocol of the IPluginChecker2s.

Most cases of validating the existence and form of extensions that register the tooling models can be handled by this configurable PluginErrorReporter class as is. More complex cases may require a more custom solution, in which case any kind of XMLErrorReporter that implements the IPluginChecker2 protocol can be substituted.

Finally, the PluginErrorReporter is aware of the registration of tooling models via Architecture Domain models. For these cases, the client can indicate which extension points support this mechanism as an alternative. For those extension points, if the PluginErrorReporter does not find a matching extension element in the plugin.xml, it will search the available architecture domains for a cross-reference to the tooling model via a new index that caches these cross-references. If no such reference is found, then a warning is issued instead of an error on the assumption that a suitable architecture model just isn't available. For tooling models that cannot be associated with architecture contexts, a missing extension in the plugin.xml is reported as an error.

Diagnostics

Markers have some details that are not specifically coded in Diagnostics, of which the most commonly used are marker type, line number, and character offset range. To encode these in a recognizable form in the Diagnostics that they produce, implementors of IPluginChecker2 can use static APIs of that interface to create simple data wrapper objects of two kinds:

When creating markers from diagnostics, the framework scans the Diagnostic::data list for these wrappers to determine the marker type to create (instead of the builder's default marker type) and attributes such as line number to set into the marker.

Package oep.toolsmiths.plugin.builder

As in earlier releases, the Activator of this bundle configures the PapyrusPluginBuilder by adding a suite of AbstractPapyrusBuilders to it. The difference in the new framework is that there is now a generic PluginCheckerBuilder (a subclass of AbstractPapyrusBuilder) that delegates to IPluginChecker2s to do all of the work, apart from cleaning markers; for that, it just deletes all the markers of its model marker type from the project.

Any number of IPluginChecker2s can be added to a PluginCheckerBuilder. It will run all of them, collate their diagnostics, and then create markers from those diagnostics. Because the builder itself is long-lived and the checkers are not assumed to be usable for more than one invocation, the builder is actually configured with factories to create the checkers as it needs them. The IPluginChecker2.Factory interface provides for creation of checkers from three inputs:

The "if applicable" qualifiers above are significant: the builder uses the same factories to create three kinds of checkers:

The PluginCheckerBuilder needs to know what are the model resources in the project for which it needs to create and run these checkers. That is the configured by injection of a function that computes the a multi mapping of IFile to EObject for the specific model objects in the various applicable EMF resources to validate. A convenient mechanism for computing this mapping is the ModelResourceMapper class, which itself is configurable with strategies for

Package oep.toolsmiths.validation.elementtypes.internal.checkers

As a prototypical example of the implementation of this generic plug-in checker builder framework for a particular Papyrus model, the Element Types plug-in validation provides configuration of all of the different checker types that are then installed into the PapyrusPluginBuilder.

The ElementTypesPluginChecker class that is the central component of the older plug-in validation menu action is retained as a factory for the checker configurations. The ElementTypesPluginXMLValidator class is a helper that provides the strategies configured in the PluginErrorReporter as discussed above, with validation logic for the extension points in the plugin.xml that pertain to Element Types models.

A Word on Marker Types

As mentioned above, the PluginCheckerBuilder is configured with a default marker type that it will use to create IMarkers from Diagnostics that do not specify an explicit marker type in their data lists. This marker type is also used in cleaning the project to delete existing markers. Thus, it works best when this marker type is a supertype of all marker types that individual checkers and diagnostics will create.

Note that marker types can specify multiple supertypes, so the plug-in builder for a model can specify an abstract marker type for its domain and have checkers/diagnostics specify various specific subtypes of it that are also subtypes of other marker types from Eclipse Platform as needed, e.g., Java Problem, PDE Problem, EMF Problem, etc. as applicable.

In the 2020-12 release, Eclipse PDE added support for any sub-type of the Java Problem marker type to trigger the dialog confirming to launch a run-time instance projects contributing to it have errors. Accordingly, it is recommended that any model-specific plug-in validation problems that should trigger this launch gate should use a marker type that is a sub-type both of Java Problem and the model's abstract problem type that all of its markers conform to. This latter marker type is recommended to make it easy for the plug-in builder to clean all relevant markers in a project.

Quick Fixes

Quick fixes, known in the Eclipse APIs as marker resolutions, provide the user with convenient automation of changes to fix problems identified in markers. Wherever it is practical, it is helpful to provide quick fixes for every change that would commonly be recommended for any given problem.

To assist in the development of simple quick fixes, the common plug-in builder bundle provides a small framework.

API Details

Package oep.toolsmiths.validation.common.checkers

The IPluginChecker2 interface provides a convenient method problem(int) : MarkerAttribute that creates a marker attribute encoding an unique problem identifier for inclusion in the data of a Diagnostic. This identifier correlates to a quick-fix marker resolution.

The CommonProblemConstants class has two constants PROBLEM_ID_BASE and MAX_PROBLEM_ID that define a range of problem IDs reserved for problems reported by the common layer, itself, of the plug-in builder framework. Problem IDs used by model-specific builders must use IDs greater than the MAX_PROBLEM_ID of the common layer. They can use the same IDs as problems from other models because they are tagged with different marker types; the problems reported by the common framework do use each model's marker type, which is thy the ID numbers need to be kept separate.

Package oep.toolsmiths.validation.common.quickfix

Builder bundles that provide quick fixes for their problems should extend the CommonMarkerResolutionGenerator class. This provides fixes for all of the problems reported by the common framework and is easily extended to add fixes for your model's specific problems. A variety of marker resolutions are available to implement fixes for the most common kinds of problems: