AbstractPluginQconf.java

package com.github.celldynamics.quimp.plugin;

import java.io.File;

import com.github.celldynamics.quimp.QParams;
import com.github.celldynamics.quimp.QuimpException;
import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
import com.github.celldynamics.quimp.filesystem.FileExtensions;
import com.github.celldynamics.quimp.filesystem.QconfLoader;

/**
 * Template of plugin focused at processing QCONF/paQP files.
 * 
 * <p>This type of plugin opens QCONF/paQP file immediately after run, process it and returns
 * results. User interface is not directly supported here unless you override {@link #executer()}.
 * For UI plugins use {@link AbstractPluginTemplate}. If {@link AbstractPluginOptions#paramFile} is
 * not null and not empty, it will be used, otherwise template displays file dialog.
 * 
 * <p>Following workflow specified in {@link AbstractPluginBase}, this implementation calls
 * {@link #loadFile(String)} from {@link #executer()}. If default constructor
 * {@link #AbstractPluginQconf(String, AbstractPluginOptions, String)} is called from constructor in
 * implementing class and any additional configuration is needed before plugin executes (that
 * typically should go after calling super (this) constructor) that configuration can be added in
 * overridden {@link #loadFile(String)} method before calling super#loadFile.
 * 
 * @author p.baniukiewicz
 * @see AbstractPluginBase
 */
public abstract class AbstractPluginQconf extends AbstractPluginBase {

  /**
   * Loaded configuration file.
   */
  protected QconfLoader qconfLoader; // main object representing loaded configuration file

  /**
   * Extension of file plugin asks for after execution in IJ mode.
   */
  protected String fileExt = FileExtensions.newConfigFileExt;

  /**
   * 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 #AbstractPluginQconf(AbstractPluginOptions,String)}.
   */
  protected AbstractPluginQconf() {
    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
   */
  protected AbstractPluginQconf(AbstractPluginOptions options, String pluginName) {
    super(options, pluginName);
  }

  /**
   * Constructor that allows to provide own parameters.
   * 
   * <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.
   * 
   * <p>It loads QCONF.paQP file specified in {@link AbstractPluginOptions#paramFile}. If file is
   * not specified it shows load file window.
   * 
   * @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.
   * @see #loadFile(String)
   */
  protected AbstractPluginQconf(String argString, AbstractPluginOptions options, String pluginName)
          throws QuimpPluginException {
    super(argString, options, pluginName);
    try {
      loadFile(this.options.paramFile); // load configuration file and verify it
    } catch (QuimpPluginException qe) {
      throw qe;
    } catch (Exception qe) {
      throw new QuimpPluginException(qe);
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.github.celldynamics.quimp.plugin.AbstractPluginBase#executer()
   */
  @Override
  protected void executer() throws QuimpException {
    loadFile(options.paramFile);
    // FIXME It should call either runFromPaqp or validate();runFromQconf(); here
  }

  /**
   * Load specified configuration file and execute plugin depending on file type.
   * 
   * <p>This method executes only once, if {@link #getQconfLoader()} is null. Should be overridden
   * if there is any additional configuration needed before execution.
   * 
   * <p>If file is QCONF then {@link #runFromQconf()} is executed, if it is paQP then
   * {@link #runFromPaqp()}. Validate loaded QCONF file by {@link #validate()}.
   * 
   * @param paramFile path to the file. It can be null or empty string to allow user pick the file.
   * 
   * @throws QuimpException When configuration file could not be loaded or it does not meet
   *         requirements.
   * @see #validate()
   */
  protected void loadFile(String paramFile) throws QuimpException {
    File pf;
    if (paramFile == null || paramFile.isEmpty()) {
      pf = null;
    } else {
      pf = new File(paramFile);
    }
    if (qconfLoader == null || qconfLoader.getQp() == null) {
      // load new file
      qconfLoader = new QconfLoader(pf, fileExt);
      if (qconfLoader.getQp() == null) {
        // not loaded (cancelled)
        throw new QuimpPluginException("Cancelled", MessageSinkTypes.MESSAGE, true);
      }
      if (qconfLoader.isFileLoaded() == QParams.QUIMP_11) { // old path
        runFromPaqp();
      } else if (qconfLoader.isFileLoaded() == QParams.NEW_QUIMP) { // new path
        validate();
        runFromQconf();
      } else {
        qconfLoader = null; // failed load or checking
        throw new QuimpPluginException(
                "QconfLoader returned unsupported version of QuimP or error.");
      }
    }
  }

  /**
   * Override this method to pass your own validation of QCONF structure.
   * 
   * @throws QuimpException if file can not be validated.
   */
  protected void validate() throws QuimpException {
    qconfLoader.getBOA();
  }

  /**
   * Return {@link QconfLoader} object.
   * 
   * @return the qconfLoader
   */
  public QconfLoader getQconfLoader() {
    return qconfLoader;
  }

  /**
   * Remove loaded QCONF file and allows to load new one.
   * 
   * <p>Note that file can be processed once, therefore next call of {@link #run(String)} wil do
   * nothing unless {@link #unload()} is used.
   * 
   * @see AbstractPluginQconf#loadFile(String)
   */
  public final void unload() {
    qconfLoader = null;
  }

  /**
   * Called if loaded file is QCONF.
   * 
   * <p>This method expects that {@link #qconfLoader} is already set up ({@link #run(String)}. In
   * macro or IJ mode exceptions will be handled in place and displayed as IJERROR or GUI message.
   * For API call (only if initialised by
   * {@link #AbstractPluginQconf(String, AbstractPluginOptions, String)})
   * exceptions are re-thrown.
   * 
   * @throws QuimpException on error
   */
  protected abstract void runFromQconf() throws QuimpException;

  /**
   * Called if loaded file is paQP.
   * 
   * <p>This method expects that {@link #qconfLoader} is already set up ({@link #run(String)}. In
   * macro or IJ mode exceptions will be handled in place and displayed as IJERROR or GUI message.
   * For API call (only if initialised by
   * {@link #AbstractPluginQconf(String, AbstractPluginOptions, String)})
   * exceptions are re-thrown.
   * 
   * @throws QuimpException on error
   */
  protected abstract void runFromPaqp() throws QuimpException;

}