Calculator Plugins

Contents

Introduction

Plugins are generally used to add dynamically loaded modules to an application based on user defined configuration.

Our chemical calculator plugin structure is added to Marvin for the following reasons:

  1. There are a large number of chemical calculations and the set of necessary calculations may vary by user
  2. Users may need special calculations that are available on the internet or even more specific ones for which they have their own implementation
  3. A general mechanism is needed to enable both graphical java applications and command line tools to set chemical calculation parameters, perform the required calculation and display the results
There are some built-in calculations such as charge, pKa, logP and logD computations that can be evaluated in demo mode or purchased from ChemAxon Ltd.1. If a user needs another calculation, then he/she can integrate that calculation into Marvin as a custom plugin.

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 MarvinSketch and MarvinView.

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.

Calculator plugins have a common base class: chemaxon.marvin.plugin.CalculatorPlugin. This base class implements the license handling, provides some helper functions for number formatting and declares the abstract methods to be implemented by the specific plugin classes for input molecule and calculation parameter setting, performing the calculation, and getting the results.

Apart from this main plugin class, our graphical applications and applets MarvinSketch and MarvinView require a plugin loader class that must be a subclass of chemaxon.marvin.plugin.CalculatorPluginLoader. This loader class is responsible for the instantiation of the plugin and handles the plugin parameter setting through the graphical user interface. It provides the parameter panel as a java.awt.Component object, verifies and fetches the plugin parameters from this panel and returns them in a java.util.Properties object.

The calc command line tool uses chemaxon.marvin.plugin.CalculatorOutput to generate the plugin results in table form. Unlike the chemaxon.marvin.plugin.CalculatorPlugin and chemaxon.marvin.plugin.CalculatorPluginLoader classes, this class is not abstract: it 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.

To access a plugin by the java API you only need to use the public methods in the chemaxon.marvin.plugin.CalculatorPlugin class as shown in the examples section of this manual.

The graphical plugin handling algorithm:

The central plugin handler class is chemaxon.marvin.plugin.CalculatorPluginManager: this reads the configuration, and adds the list of available plugins to the Tools menu and the Tools/Options submenu. The Tools menu items activate the plugins, while the Tools/Options menu items invoke the plugin loaders and display their parameter panel on a general options pane. When a plugin is activated, the corresponding plugin loader object is asked for the parameters as a java.util.Properties object and sets them in the plugin object. Then the input molecule (the current molecule in the sketcher/viewer) is set and the plugin calculation is run. Finally, the calculation result is queried from the plugin, converted to string in the plugin and displayed on the sketcher/viewer GUI or in a dialog box.

Configuration

Both the cxcalc command line tool and the graphical applications and applets require one or more configuration files that specify the plugins available. For the configuration of the command line tool see the Configuration File section in the Calculator user manual. The plugin configuration for MarvinSketch and MarvinView is specified in one or more java properties file given in the toolfiles application or applet parameter. If more than one file is specified, then the file names should be separated by a ',' character (without spaces) and their contents will be merged. The file names should be given relative to the CLASSPATH. Marvin applets load configuration files from the server computer. If no configuration file is given then the default configuration file is read: chemaxon/marvin/calculations/plugins.properties. The configuration file syntax is best shown by an example:

#<loader class name>$<menu/mnemonics>$<title>$<CHART>$<*>
# only the class name mandatory, "CHART" means line-chart data, an ending '*' means preload

plugin_1=chemaxon.marvin.calculations.ChargePluginLoader$Charge/Ch$Charge
plugin_2=chemaxon.marvin.calculations.pKaPluginLoader$pKa/K$pKa

The property keys should be unique within one configuration file. If the key ends with a number preceeded by a '_' character then that ending number is used to determine the position of the plugin in the Tools menu: the plugins are sorted according to ascending ending number order, plugins with no ending number are put at the end.

Each property value defines a plugin configuration by giving the following fields (fields are separated by '$' characters):

  1. the full package name of the plugin loader class
  2. the submenu label in the Tools menu and the possible mnemonics characters (these two are separated by a '/' character): the first possible mnemonic character is set for menu mnemonic that is not already used by another plugin - if no such character exists then no mnemonic is set
  3. the display title that appears on the parameter panel and result window title bars
  4. an optional "CHART" string meaning that the plugin result may be displayed graphically in a line-chart
  5. an optional '*' character meaning preload (only useful for applets): in this case the plugin is loaded together with the applet, otherwise dynamically loaded when first used

Your Plugin

You can write a custom plugin for a chemical calculation that is not currently available as a built-in plugin. This section describes how to do this. Your plugin can be integrated into our system and will be accessed by the same mechanism as the built-in plugins. This means that by adding 2-3 java classes (the Plugin, the PluginLoader and optionally the PluginOutput) that provide the uniform interface for plugin handling, and setting your plugin configuration in the 2 configuration files (one for the command line tool and the another one for the graphical sketcher/viewer) your plugin will be run by the cxcalc command line tool, inserted into the plugin set under the Tools menu in MarvinSketch and MarvinView, and enabled for access it through CalculatorPlugin API.

The following table shows the items needed for the different plugin uses:

CalculatorPlugin subclassCalculatorPluginLoader subclassCalculatorOutput class or subclassplugins.propertiescalc.properties
sketcher/viewer
cxcalc tool
java API

Implementation step-by-step guide:

  1. The CalculatorPlugin subclass

    The first step: go to the internet and download some code or write your own. The calculation code is assumed to work on one input molecule at a time, perform the calculation and then return various results of the calculation. The plugin class will first set the input molecule, then 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 the calculation 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:

    1. protected String getLicenseKey()
      Implement this only if you want to sell your plugin and protect it with a license key. The default implementation returns null which means that the plugin is free: no license key is required. A license key is an 8-character string that is hard-coded in your plugin and sold to your clients in the license key file chemaxon/licenses.txt located in the CLASSPATH or in the .chemaxon/licenses.txt (Unix) or chemaxon/licenses.txt (Windows) file under the user's home directory. In the license key file, the plugin name with full package name is paired with the license key:
      chemaxon.marvin.calculations.YourPlugin=56TYAD12
      The license key returned by this method will be checked against the license key loaded from the licenses.txt file. If they are equal then the plugin can be run without restriction, otherwise it will run in demo mode: only one calculation can be performed without restarting the application (e.g. cxcalc will be run only for the first molecule of the input SDF file).

      The fact that this method has protected accessibility level may result in a security problem: someone might add a java class to the same package, instantiate your plugin and try to fetch the license key:

      CalculatorPlugin plugin = new YourPlugin();
      System.out.println(plugin.getLicenseKey());
      
      There is a workaround for this: instead of simply returning your license key, implement this method in the following way:
      protected final String getLicenseKey() {
          if (check()) {
      	return "56TYAD12"; // return your license key here
          }
          else {
      	return ""; // return the empty string which is the invalid key
          }
      }	
      
      First note that the method is declared final to prevent the intruder from subclassing your plugin and replacing your license key by overwriting this method. Next note that you return your license key only if the check() method implemented in CalculatorPlugin returns true. Otherwise you return the empty string which is the invalid key. Do not return null which would mean that the plugin has no license key - if you return null then your plugin will be freely used without any license key.

      The mysterious check() method checks whether your plugin has been validated. Validation is a simple process that checks whether the caller knows the license key. This is done by a call to the validate(String license) method. Hence with the above implementation the intruder would simply print out the empty string while a programmer who has bought your license key and would like to use your plugin can do that by proving that he/she knows your license key:

      CalculatorPlugin plugin = new YourPlugin();
      System.out.println(plugin.getLicenseKey()); // prints the empty string
      plugin.validate("56TYAD12"); // supposing your license key is"56TYAD12"
      System.out.println(plugin.getLicenseKey()); // now prints the real license key
      

    2. 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.

    3. 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. The default implementation accepts all molecules (simply returns).

    4. public void setMolecule(Molecule mol) throws PluginException
      This sets the input molecule. Again, throw a PluginException on any error.

    5. public void 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. Throw a PluginException on any error.

    6. public Molecule getMolecule()
      This simply returns the input molecule.

    7. public Object[] getResultTypes()
      This method returns the queried result types. For example, the charge calculation may have three result types: sigma, pi and total, the logp calculation may have two result types: increments and molecule. The built-in plugins charge, logp and pka have 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.

    8. 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 key is "increments" and returns MOLECULE if key is "molecule".

    9. 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.

    10. 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 into java.lang.Double). PluginException can be thrown on any error.

    11. 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 to double -> 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.

    12. 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 API 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.

    13. protected 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 the nitro group conversion [O-]-[N+]=O >> O=N=O. 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.

      Remark: This local standardization will be replaced by a general standardizer module.

    14. public String getOutputClassName()
      This method is called by chemaxon.marvin.Calculator to get the class name of the table form output provider class corresponding to your plugin. The default implementation returns "chemaxon.marvin.plugin.CalculatorOutput" which is the class name of the default table form output provider (outputs the molecule ID and the result strings for each required result type). Implement this method by returning to your specific output provider class name only if it is different from chemaxon.marvin.plugin.CalculatorOutput.

  2. The CalculatorPluginLoader subclass

    Extend the abstract base plugin loader class chemaxon.marvin.plugin.CalculatorPluginLoader. Here is the list of methods that have to be implemented:

    1. public Component getParameterPanel()
      This method returns the parameter panel as a java.awt.Component object. This component will be placed on the general options pane when the user invokes the plugin parameter setting through the corresponding Tools/Options submenu item.

    2. public Properties getParameters()
      This method returns the parameters that has been set on the parameter panel (or the default values if the parameter setting has not been invoked). This object will be set in the plugin when the calculation is run by the user. The parameter keys have to match the parameter long names configured in the cxcalc configuration because the chemaxon.marvin.Calculator class sets the parameter long names as property keys.

    3. public boolean verifyParameters()
      This method verifies the parameter settings on the parameter panel, called when the user presses the "OK" button on the option pane: the option pane is hidden and parameters are accepted only if this method returns true, otherwise the option pane remains shown to let the user correct the parameter settings. It is recommended to show an error message dialog before returning false to let the user know the parameter setting error.

    4. public void updateParameters()
      This method is called when the user presses the "OK" button on the option pane and the parameter settings are accepted by verifyParameters(). The Properties object or any inner structure storing the user settings can be filled with the parameter panel data. In theory even the parameter panel component object itself can store this data, but an auxiliary structure may be more efficient: the Properties object is queried each time the plugin is run and it is better to perform data conversion from the parameter panel only once per parameter setting. Another reason for storing parameter data outside of the panel is that the user could change setting then press the "CANCEL" button in which case we would want know the previous parameter settings!

    5. public void reloadParameters()
      This method is called when the user presses the "CANCEL" button on the option pane. In this case, the panel should be reset to the previous settings.. This can be done from the Properties object or any other inner structure used to store the parameter settings. This is the reason why you should not use the pure GUI component to store the parameters, so that previous settings can be recalled.

    6. protected String getPluginClassName()
      This method is called by the dynamic plugin loading mechanism to get the plugin class name. The default implementation simply strips off the last 6 characters (the "Loader" ending) from the loader class name. All built-in plugins have both their plugin class and their loader class in the chemaxon.marvin.calculations package and follow a simple naming convention: plugin class names have a base name ending with "Plugin", loader class names have the same base name ending with "PluginLoader" (e.g. chemaxon.marvin.calculations.ChargePlugin and chemaxon.marvin.calculations.ChargePluginLoader). If your plugin and loader class naming follow this convention then you need not implement this method, otherwise return the full plugin class name here.

  3. The CalculatorPluginOutput subclass

    This is optional: the default table output is implemented in chemaxon.marvin.plugin.CalculatorOutput. This implementation outputs the molecule ID and the results for all required result types in a table row. To provide a different output table form you should subclass chemaxon.marvin.plugin.CalculatorOutput and return the full class name of your output class in public String getOutputClassName() implemented in your plugin class. You should implement the following methods in your output class:

    1. public String getHeader()
      Returns the table header string.

    2. public String getResult(Molecule target)
      Returns the result table row for the given target molecule.

    You can use the protected member variables of chemaxon.marvin.plugin.CalculatorOutput:

  4. The Calculator.properties configuration file

    See the Configuration File section of the Calculator user manual.

  5. The plugins.properties configuration file

    See the Configuration section of this manual.

API Examples

Here are some examples that illustrate the usage of the plugin API. Note the validate(<license key>) is called right after the plugin instantiation: this is important because otherwise you can only use the plugin without license in demo mode.

Notes

  1. Dr. Ferenc Csizmadia
    CEO
    ChemAxon Ltd.
    Maramaros koz 3/A, Budapest, 1037 Hungary
    http://www.chemaxon.com
    Cell: +3620 9570988
    Tel: +361 4532660 Fax: +361 4532659 
    e-mail: sales@chemaxon.com