-
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 theanalyzercollection, able to analyze entities of typeT. -
IS4.SFI.Formats.IBinaryFileFormat‒ for thedata-formatcollection, used by the data analyzer. -
IS4.SFI.Formats.IXmlDocumentFormat‒ for thexml-formatcollection, used by the XML analyzer. -
IS4.SFI.Services.IDataHashAlgorithm‒ for thedata-hashcollection, used by the data analyzer (also mirrored inpixel-hash). -
IS4.SFI.Services.IFileHashAlgorithm‒ for thefile-hashcollection, used by the file analyzer. -
IS4.SFI.Services.IContainerAnalyzerProvider‒ for thecontainer-formatcollection to analyze complex node structures. -
IS4.SFI.Services.IObjectHashAlgorithm<System.Drawing.Image>‒ for theimage-hashcollection, 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
setaccessor). - Since the command-line arguments are text only, its value must be convertible to and from
stringusing the property's converter:- All types are initially convertible to
stringby the virtue of having theToStringmethod. - Conversion from
stringcan 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 implementGetStandardValuesto 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.