AbstractPluginBase.java

package com.github.celldynamics.quimp.plugin;

import com.github.celldynamics.quimp.QuimpException;
import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
import com.github.celldynamics.quimp.registration.Registration;

import ij.IJ;
import ij.plugin.PlugIn;

/**
 * Base template of QuimP (legacy Fiji compatible) plugins.
 * 
 * <p>This template provides template of {@link #run(String)} method specified in
 * {@link PlugIn#run(String)} interface that detects context of caller by testing if there is valid
 * parameter string specified and then sets proper {@link AbstractOptionsParser#errorSink} and
 * {@link AbstractOptionsParser#apiCall}. Thus depending on context plugin will report errors in
 * correct place. Using {@link #run(String)} errors can be directed to {@link MessageSinkTypes#GUI}
 * or {@link MessageSinkTypes#IJERROR}. For having them in {@link MessageSinkTypes#CONSOLE} override
 * {@link #executer()} or use other method for executing plugin.
 * 
 * @author p.baniukiewicz
 * @see QuimpException#handleException(java.awt.Frame, String)
 */
public abstract class AbstractPluginBase extends AbstractOptionsParser implements IQuimpPlugin {

  /**
   * Name of the plugin that will be displayed in Macro Recorder.
   */
  private String pluginName = "";

  /**
   * This default constructor must be overridden in concrete class. It is called by IJ when plugin
   * instance is created. A concrete instance of {@link AbstractPluginOptions} class should be
   * created there and then passed to
   * {@link AbstractOptionsParser#AbstractOptionsParser(AbstractPluginOptions)}. One needs to call
   * {@link #setPluginName(String)} here as well.
   */
  public AbstractPluginBase() {
    super();
  }

  /**
   * Default constructor.
   * 
   * <p>Set api call to false and assign provided options to object.
   * 
   * @param options Reference to plugin configuration container.
   * @param pluginName name of the plugin that will be displayed in Macro Recorder
   */
  public AbstractPluginBase(AbstractPluginOptions options, String pluginName) {
    super(options);
    setPluginName(pluginName);
  }

  /**
   * Constructor that allows to provide own parameter string.
   * 
   * <p>Intended to run from API. Set {@link #apiCall} to true and {@link #errorSink} to
   * {@link MessageSinkTypes#CONSOLE}.
   * {@link AbstractPluginOptions} is initialised from specified string and assigned to this
   * instance.
   * 
   * @param argString parameters string like that passed in macro. If it is empty string or null
   *        constructor exits before deserialisation.
   * @param options Reference to plugin configuration container.
   * @param pluginName name of the plugin that will be displayed in Macro Recorder
   * @throws QuimpPluginException on any error in plugin execution.
   */
  public AbstractPluginBase(String argString, AbstractPluginOptions options, String pluginName)
          throws QuimpPluginException {
    super(argString, options);
    setPluginName(pluginName);
  }

  /**
   * Called on plugin run by ImageJ or from API.
   * 
   * <p>Overrides {@link PlugIn#run(String)}. If input string is null or empty it sets
   * {@link AbstractOptionsParser#errorSink} to
   * {@link MessageSinkTypes#GUI}. Note that {@link AbstractOptionsParser#apiCall} is set by
   * choosing proper constructor. Then it tries to parse specified parameter string, if it succeeds,
   * {@link AbstractOptionsParser#options} is set and deserialised and {@link #executer()} method is
   * executed. If parsing fails, {@link #showUi(boolean)} is called with option true. If there is
   * parsable string {@link AbstractOptionsParser#errorSink} is set to
   * {@link MessageSinkTypes#IJERROR}
   * 
   * <p>Finally, macro string is published to ImageJ that represents current state of
   * {@link AbstractOptionsParser#options}.
   * 
   * <p>All exceptions thrown by plugin logic (from {@link #executer()}) are handled here depending
   * on {@link AbstractOptionsParser#errorSink} value.
   * 
   * @see AbstractOptionsParser#parseArgumentString(String)
   */
  @Override
  public void run(String arg) {
    if (arg == null || arg.isEmpty()) {
      errorSink = MessageSinkTypes.GUI; // no parameters - assume menu call
    } else {
      errorSink = MessageSinkTypes.IJERROR; // parameters available - macro call
    }
    // validate registered user
    new Registration(IJ.getInstance(), "QuimP Registration");
    try {
      if (parseArgumentString(arg)) { // process options passed to this method
        IJ.showStatus(pluginName);
        executer();
        IJ.log(pluginName + " complete");
        IJ.showStatus("Finished");
      } else {
        showUi(true);
      }

    } catch (QuimpException qe) {
      qe.setMessageSinkType(errorSink);
      qe.handleException(IJ.getInstance(), this.getClass().getSimpleName());
    } catch (Exception e) { // catch all exceptions here
      logger.debug(e.getMessage(), e);
      logger.error("Problem with running plugin: " + e.getMessage() + " (" + e.toString() + ")");
    } finally {
      publishMacroString(pluginName);
    }
  }

  /**
   * Open plugin UI.
   * 
   * <p>Executed if {@link #run(String)} could not parse parameters.
   * 
   * @param val true to show UI
   * @throws Exception on any error. Handled by {@link #run(String)}
   */
  public abstract void showUi(boolean val) throws Exception;

  /**
   * Executed if {@link #run(String)} got parsable parameter string.
   * 
   * @throws QuimpException on any error. Exception is handled depending on
   *         {@link AbstractOptionsParser#errorSink} set by {@link #run(String)}
   */
  protected abstract void executer() throws QuimpException;

  /**
   * Set plugin name, should be that recognisable by IJ, usually plugins.config.
   * 
   * @param pluginName the pluginName to set
   */
  public void setPluginName(String pluginName) {
    this.pluginName = pluginName;
  }

}