-
Notifications
You must be signed in to change notification settings - Fork 0
Plugins
Plugins are a way to add custom components that provide support for new features or augment existing ones. They are loaded from the plugins
directory, or via the --plugin
command-line option.
A plugin is generally packed as a ZIP file, storing a DLL file with the same bare name as the archive (when loaded from the plugins
directory, it may also be unzipped and loaded as a directory inside plugins
). The DLL file should be a .NET assembly containing the component types that should be loaded and instantiated. Hence a plugin should reference the IS4.SFI
assembly, otherwise no component will be loaded.
All public types in the plugin assembly are browsed to see if they can be used as a component. In addition, types forwarded to other assemblies (as indicated by [assembly: TypeForwardedTo]
) are also taken into account.
Via the --plugin
option, a plugin may be loaded from a multitude of sources, including paths or URIs. It is also possible to load a plugin from NuGet using the nuget:id/version
syntax (version is optional), which will also pull all its dependencies if needed. To load official SFI components, it is possible to use sfi:
, for example sfi:formats.images
, which is the same as nuget:is4.sfi.formats.images
.
In order for a type to be loaded as a component, it should implement an interface based on one of the component collections. This type can be determined via the about
command. By default, the expected interfaces are:
-
IS4.SFI.Services.IEntityAnalyzer<T>
‒ for theanalyzer
collection, able to analyze entities of typeT
. -
IS4.SFI.Formats.IBinaryFileFormat
‒ for thedata-format
collection, used by the data analyzer. -
IS4.SFI.Formats.IXmlDocumentFormat
‒ for thexml-format
collection, used by the XML analyzer. -
IS4.SFI.Services.IDataHashAlgorithm
‒ for thedata-hash
collection, used by the data analyzer (also mirrored inpixel-hash
). -
IS4.SFI.Services.IFileHashAlgorithm
‒ for thefile-hash
collection, used by the file analyzer. -
IS4.SFI.Services.IContainerAnalyzerProvider
‒ for thecontainer-format
collection to analyze complex node structures. -
IS4.SFI.Services.IObjectHashAlgorithm<System.Drawing.Image>
‒ for theimage-hash
collection, used by the image analyzer.
A type can implement all of these interfaces, in which case it will be instantiated only once and added to all supported collections. In addition to that, a type may also implement IEnumerable<T>
or IAsyncEnumerable<T>
where T
matches one of the already recognized types, in which case it is instantiated and enumerated to obtain the final components.
In order to be selected, component types should not be internal
, abstract
, generic, nested, or marked [Browsable(false)]
, in which case they are skipped entirely even if they match one of the interfaces above.
When such a type is encountered, dependency injection is used to select the appropriate constructor. These services are provided to it, distinguished by their type:
-
Inspector
– the inspector instance that is loading the plugin, -
ILogger
– an instance used for logging messages, -
IDirectoryInfo
– the directory where the main assembly of the plugin is located.
Components can be configured from the application, allowing it to assign their properties to different values, or to specify other components that they can use. For this purpose, .NET's design-time services are used instead of reflection, with TypeDescriptor.GetProperties
being called to obtain the list of configurable properties (which defaults to reflection if no other provider is registered). Properties marked [Browsable(false)]
are removed from any consideration.
A property must satisfy a couple of criteria in order to be accessible through command-line configuration or XML:
- It must be
public
(other properties are not accessible byTypeDescriptor.GetProperties
). - It must not be read-only (usually have a
set
accessor). - Since the command-line arguments are text only, its value must be convertible to and from
string
using the property's converter:- All types are initially convertible to
string
by the virtue of having theToString
method. - Conversion from
string
can achieved by using primitive types, enums, or types using[TypeConverter]
to provide the conversion. This attribute can also be placed on the property itself. When using a custom converter, it is also recommended to implementGetStandardValues
to give hints as to what values are commonly used for the type.
- All types are initially convertible to
Additionally, in order for the conversion to be considered successful, it must return a non-null
value (unless the original string is empty).
A type may also implement ISupportInitialize
, which is used to enclose property assignment in a single batch by the application.