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 Diagnostic
s 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 EValidator
s; 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 VirtualMarker
s 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 Diagnostic
s and appends them to the collector chain, in accordance with the general protocol of the IPluginChecker2
s.
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 Diagnostic
s, of which the most commonly used are marker type,
line number, and character offset range. To encode these in a recognizable form in the Diagnostic
s 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
AbstractPapyrusBuilder
s to it. The difference in the new framework is that there is now a generic
PluginCheckerBuilder
(a subclass of AbstractPapyrusBuilder
) that delegates to IPluginChecker2
s
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 IPluginChecker2
s 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:
null
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
ResourceSet
s in which to load those files, andResource
s 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
IMarker
s from Diagnostic
s 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