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
The following figure shows an overview of the generic plug-in builder framework:
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:
BuildPropertiesChecker checks that all files that implement a model are included in the build.properties file for packaging. This checker is reusable as is for any model, with some configuration capability to compute dependent resources that also should be included in the buildExtensionsChecker checks that all extensions required for run-time registration etc. of the model are present and correct in the plugin.xml file. More on this checker, belowModelDependenciesChecker checks that all bundle dependencies implied by the model are listed in the MANIFEST.MF. This is mostly automatic, using cross-document references in the models that it checks to find bundles in which dependencies are deployed. Some configuration is available, though, to specify additional requirements that are not computable in this way from the modelModelValidationChecker runs the EMF Diagnostician on the model resource and relays the diagnostics that it reports. The scope of this checker is limited to EMF model validation, and so it is generally reusable as is for any modelCustomModelChecker is like the previous except that it is configured with custom constraints that are not implemented (and often not implementable) in the model because they are specific to the tooling aspect. For example, checking that foreign model constructs referenced by name from a model are deployed in some bundle in either the workspace or the target platform. Custom checks are injected as EValidators; a convenient superclass for which is the CustomModelChecker::SwitchValidator that delegates dynamically to validation methods implemented in the subclass, matched by signature to the model elements being validatedThe 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.
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:
MarkerType to encapsulate the marker type to generate from the diagnostic, andMarkerAttribute to encapsulate a name-value pair of the attribute types supported by the IMarker protocolWhen 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.
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:
IProject being validatedIFile in the project that is to be checked, if applicableResource loaded from that IFile, if applicableThe "if applicable" qualifiers above are significant: the builder uses the same factories to create three kinds of checkers:
nullThe 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
ResourceSets in which to load those files, andResources loaded from those filesAs 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.
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, 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.
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.
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:
SimpleMissingAttributeMarkerResolution: this fixes a problem that reports a required attribute missing from an extension in the plugin.xml, where a suitable value can be suggested automatically. It is configured with the attribute name and a value to set (or a function that computes the value from the marker's attributes). The marker already has the information about which element of which extension to updateSimpleMissingExtensionMarkerResolution: similar to the previous, this fixes a problem that reports a required or recommended extension missing on a model-specific extension point. It is configured with the values, or computations of, the extension point, element name, and one or more attributes to setSimpleModelEditMarkerResolution: a flexible marker resolution to fix problems in EMF-based model resources. It is configured with a function that computes an EMF command to update the object in the model that the marker targets. The resolution takes care of finding an editing domain in which context to create the command, using an existing open editor if available, otherwise editing the model "off-line", as it were