Introduction to developers
This page gives you a walk-through on how the Calculator Plugins work:
Introduction
Plugins are generally used to add dynamically loaded modules to an application based on user defined configuration. Our chemical calculator plugin structure was added to Marvin for the following reasons:
-
There are a large number of chemical calculations and the set of necessary calculations may vary.
-
Users may need special calculations that are available via the internet or even more specific ones for which they have their own implementation.
-
A general mechanism is needed to enable both graphical Java applications and command line tools to set calculation parameters, perform the required calculation and display the results.
We developed a mechanism to handle these calculations in a uniform way. This common interface is utilized as a common Java API for developers, as a command line tool and also in our graphical applications and applets.
Plugin mechanism
The implementation of the general plugin handling mechanism can be found in the chemaxon.marvin.plugin package. Our specific plugin implementations are in the chemaxon.marvin.calculations package. The calculator plugins have a common base class: chemaxon.marvin.plugin.CalculatorPlugin. This base class declares methods to be implemented by the specific plugin classes for input molecule setting, parameter setting, performing the calculation, getting the results, license handling, and provides some helper functions (e.g. for number formatting).
Apart from this main plugin class, our graphical applications and applets MarvinSketch and MarvinView require a chemaxon.marvin.plugin.CalculatorPlugin class to provide result GUI components. For most plugins, the default implementation is sufficient as long as the CalculatorPlugin.getResultMolecule() method is implemented such that molecular results are written to molecule properties by Molecule.setProperty(String key, String value) and atomic properties are written to extra labels by MolAtom.setExtraLabel(String label). It is also possible to set the atom label display color by MolAtom.setExtraLabelColor(long color). The CalculatorPluginDisplay base class provides a MarvinView display for all of these in CalculatorPluginDisplay.getResultComponent(). However, your plugin may require a different display, in which case you should write a custom display class extending chemaxon.marvin.plugin.CalculatorPluginDisplay that overrides CalculatorPluginDisplay.getResultComponent(). You can also rewrite CalculatorPluginDisplay.store() to store results - this is mainly useful when more molecules are drawn as a multi-fragment molecule in the sketcher and your plugin handles these one-by-one (CalculatorPlugin.handlesMultiFragmentMolecules() returns false).
In Marvin applications and applets plugin parameters can be set in a plugin specific parameter panel which can be configured in a corresponding XML code. If the parameter setting panel is more complicated then it is also possible to return it in CalculatorPluginDisplay.getParameterPanel().
There is an example plugin implementation with a test application to demonstrate the implementation mechanism.
The cxcalc command line tool uses chemaxon.marvin.plugin.CalculatorPluginOutput to generate the plugin results in table form. This class implements the default table output with one result row for each input molecule, the molecule ID in the first column followed by the plugin results in the subsequent columns. A specific output table format can be defined by subclassing this class.
Plugin loading mechanism
The central plugin loader class is the chemaxon.marvin.plugin.PluginFactory: this class reads the configuration from ajava.util.Properties object or from a configuration file. In the latter case the Tools menu is also constructed based on this configuration. The parameter panels are dynamically constructed from the plugin specific parameter panel descriptor XMLs or fetched from the display class. In the former case the parameter panel can be constructed and displayed before the plugin class is loaded.
The plugin loading mechanism is the following: first the program tries to load the plugin class by the default class loader from the CLASSPATH; if the plugin class is not found then the JAR is loaded and the system tries to load the plugin class from there.
If the plugin name is omitted then the plugin is loaded directly from the JAR where the Plugin-Class manifest attribute specifies the plugin class.
If the JAR name is omitted then the plugin is loaded from the CLASSPATH.
For example, here is the above plugin configuration with omitted fields:
#$<plugin class name>$<plugin JAR URL>$<menu>$<mnemonics>$<group>$<groupmnemonics>$<NOPARAMS>
#first char is separator, you can choose any ASCII character that is not contained in the fields
plugin_01=$chemaxon.marvin.calculations.ElementalAnalyserPlugin$$Elemental Analysis$EA$$
plugin_11=$$pKaPlugin.jar$pKa$pK$Protonation$P
Plugin configuration
The cxcalc command line tool, the graphical applications and applets require separate configuration files that specify the available calculations/calculator plugins. For the configuration of the command line tool see its user manual. The plugin configuration for MarvinSketch and MarvinView describes the calculator plugins accessible from the Tools menu. It is read from plugins/plugins.properties file (relative to the Marvin root directory), or from xjars/plugins.properties file (located inside MarvinBeans.jar) if the previous one does not exist. It is also possible to specify the configuration in Java properties file given in the toolfiles application or applet parameter (the file name should be given relative to the CLASSPATH). Marvin applets load configuration files from the server computer.
The configuration file syntax is best shown by an example:
#$<plugin class name>$<plugin JAR URL>$<menu>$<mnemonics>$<group>$<groupmnemonics>$<NOPARAMS>
#first char is separator, you can choose any ASCII character that is not contained in the fields
plugin_01=$chemaxon.marvin.calculations.ElementalAnalyserPlugin$ElementalAnalyserPlugin.jar$Elemental Analysis$EA$$
plugin_11=$chemaxon.marvin.calculations.pKaPlugin$pKaPlugin.jar$pKa$pK$Protonation$P
The property keys should be unique within one configuration file. The Tools menu is constructed with submenus specified in group strings with menuitems defined in menu strings. Mnemonics can be set for groups using groupmnemonics and for plugins defining mnemonics strings. If group string is left empty then the plugin is listed directly in the Tools menu.
Each property value defines a plugin configuration by giving the following fields:
-
the full package name of the plugin class
-
the plugin JAR (with path relative to the plugins directory)
-
the label in the Tools menu
-
the menu label mnemonics character (only first character is considered if a string is given)
-
the menu group label in the Tools menu
-
the menu group label mnemonics character (only first character is considered if a string is given)
-
an optional "NOPARAMS" string indicating that the plugin has no parameters
At least one of the plugin classes and plugin JARs is mandatory. If the plugin class is not specified, then it is read from the Plugin-Class attribute of the JAR manifest. If the JAR is not specified, then the plugin is loaded from the CLASSPATH. The ending "NOPARAMS" option can simply be omitted, while if an intermediate field is omitted, then the corresponding separator character should be added.
#$<plugin class name>$<plugin JAR URL>$<menu>$<mnemonics>$<group>$<groupmnemonics>$<NOPARAMS>
#first char is separator, you can choose any ASCII character that is not contained in the fields
plugin_01=$chemaxon.marvin.calculations.ElementalAnalyserPlugin$ElementalAnalyserPlugin.jar$Elemental Analysis$EA$$
plugin_11=$chemaxon.marvin.calculations.pKaPlugin$pKaPlugin.jar$pKa$pK$Protonation$P
Plugin customization
The following table shows the items needed for the different plugin uses.
|
Calculator Plugin subclass |
CalculatorPluginDisplay class or subclass |
CalculatorPluginOutput class or subclass |
plugins.properties |
calc.properties |
MarvinSketch/MarvinView |
|
|
|
|
|
cxcalc command line tool |
|
|
|
|
|
Java API |
|
|
|
|
|
Naming convention for plugin classes
The plugin handling mechanism currently requires the following naming convention:
-
Plugin class: any class name will do, but built-in plugins are named ...Plugin (e.g. chemaxon.marvin.calculations.HBDAPlugin)
-
Display class: <plugin class name>Display (e.g. chemaxon.marvin.calculations.HBDAPluginDisplay)
-
Output class: <plugin class name>Output (e.g. chemaxon.marvin.calculations.HBDAPluginOutput)
Here are some guidelines to follow when designing your own plugin as a CalculatorPlugin subclass:
First search and download some code or write your own. The calculation code is assumed to work on one input molecule at a time; it should perform the calculation and then return various results of the calculation. The plugin class should first set the input molecule, run the calculation and finally query the results, so it is a good idea to follow roughly the same implementation style in the calculation module. The more your code follows this model, the easier your work will be when you write the plugin wrapper.
Then extend the abstract base plugin class chemaxon.marvin.plugin.CalculatorPlugin. Here is the list of methods that have to be implemented:
abstract protected void setInputMolecule(Molecule mol) throws PluginException
This method sets the input molecule. Throw a PluginException on any error.
public void setParameters(Properties params) throws PluginException
This method sets the plugin specific parameters: the params argument contains the plugin parameters as long parameter name -> parameter value pairs. (The long parameter name here is without the "--" prefix: e.g. if you have --type as a command line parameter then it will be present with key type in this property table.) Your task is to convert the parameter values from string to the required format and set the parameter in the calculation module or store it in the plugin for later use. Throw a PluginException on any error (unexpected format, unexpected value). All possible plugin parameters have a default value so a missing parameter should not cause any error: use its default value instead.
public void checkMolecule(Molecule mol) throws PluginException
Checks the input molecule. Throws a PluginException if the plugin calculation result is not defined for the given molecule (e.g. molecule is a reaction molecule or a molecule with R-groups). The exception message will be formed to an error message to the user and the molecule will not be processed if a PluginException is thrown. Do nothing if the molecule is accepted.
abstract public boolean run() throws PluginException
This method performs the calculation and stores the results. Include those tasks that must be run once for each molecule and produce the calculation results in the end. Returns true if the calculation was successful, false on calculation problems. Again, throw a PluginException on any error.
public Object[] getResultTypes()
This method returns the queried result types. For example, the charge calculation may have three result types: sigma, pi andtotal, the logp calculation may have two result types: increments and molecule. The built-in plugins charge, logp and pkahave the --type command line parameter that specifies the required result types: this method returns those that are specified in this parameter. However, it is possible to return all available result types and not provide this choice.
public int getResultDomain(Object type)
This returns the domain that the calculation result for the specified result type refers to: currently it can be ATOM or MOLECULE. For example, the logPPlugin returns ATOM if type is "increments" and returns MOLECULE if type is "molecule".
public int getResultCount(Object type)
This returns the number of result items for the specified result type. For ATOM result domain this is usually the number of atoms in the molecule, for MOLECULE domain this is usually 1.
public Object getResult(Object type, int index) throws PluginException
This returns the result for the specified result type and the specified result index: this index must be at least 0 and less than the result count returned by getResultCount(Object type) for this result type. In our case the result is a number: it must be wrapped into the derived class of java.lang.Number corresponding to its primitive type (e.g. double must be wrapped intojava.lang.Double). PluginException can be thrown on any error.
public String getResultAsString(Object type, int index, Object result) throws PluginException
This returns a string representation of the result. The result type and index are also given: in some cases the string representation may include these or depend on these as well. The protected String format(double x) can be used todouble -> String conversion with a given number of fractional digits. If you intend to use this formatting then protected void setDoublePrecision(int precision) has to be called once beforehand to set the maximum number of decimal digits allowed in the fractional portion of a number. PluginException can be thrown on
any error.public String getResultAsRGB(Object type, int index, Object result) throws PluginException
Returns the color to be used when displaying the result. For example, this method is used when acidic pKa values are displayed in red while basic pKa values are displayed in blue. The color is returned as a single int (see the java.awt.Color.(int) for a definition of encoding a color into a single int). The default implementation returns 0 which means that the result will be displayed using the current foreground color. PluginException can be thrown on any error.
-
public void standardize(Molecule mol)
This method is used to bring the molecule to a standardized form. Some calculations require a certain standardization preprocess, such as aromatization or a common form of certain functional groups (one prescribed tautomer or mezomer form). The current implementation performs only aromatization and nitro group and sulphynil group conversions (transforms to the neutral forms). If any other transformation is needed or no such transformation is necessary, then you must implement this method. Be careful with transformations that change the atom set of the molecule since these change the atom indices as well: if the result domain is ATOM, then after querying the results with getResult(Object key, int index) and getResultAsString(Object key, int index, Object result) the program will output the returned result for the specified atom index in the original molecule and not in the transformed one. If the standardization procedure changes the atom indices then the index given in these result query methods must be transformed to the corresponding atom index in the transformed molecule and the result for that atom index must be returned.
public boolean isLicensed()
Returns true if the plugin is licensed. Implement this only if you want to sell your plugin and protect it with a license key. The default implementation always returns true which means that the plugin is free: no license key is required. This method returns only information about the licensing, license checking has to be implemented separately.