View Javadoc
1   package com.github.celldynamics.quimp.plugin;
2   
3   import org.slf4j.Logger;
4   import org.slf4j.LoggerFactory;
5   
6   import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
7   import com.github.celldynamics.quimp.utils.QuimpToolsCollection;
8   
9   import ij.IJ;
10  import ij.Macro;
11  import ij.plugin.frame.Recorder;
12  
13  /**
14   * This is template allows for serialize/deserialize plugin options to/from macro string.
15   *
16   * @author p.baniukiewicz
17   * @see AbstractPluginOptions
18   */
19  public abstract class AbstractOptionsParser {
20  
21    /**
22     * If true plugin is run from parametrised constructor what usually means API.
23     */
24    public boolean apiCall;
25    /**
26     * The Constant logger.
27     */
28    protected final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
29  
30    protected AbstractPluginOptions options;
31  
32    /**
33     * Indicate that plugin is run as macro from script. Blocks all UIs.
34     * 
35     * <p>Use this variable in child class together with {@link #apiCall} to decide whether to show
36     * message in UI or console. the following rules applies:
37     * <ol>
38     * <li>errorSink = MessageSinkTypes.GUI - plugin called from IJ menu
39     * <li>errorSink = MessageSinkTypes.IJERROR - plugin called from macro
40     * <li>errorSink = MessageSinkTypes.CONSOLE - plugin called from API (but exceptions are
41     * re-thrown to be handled in caller code)
42     * </ol>
43     */
44    protected MessageSinkTypes errorSink = MessageSinkTypes.GUI;
45  
46    /**
47     * This default constructor must be overridden in concrete class. It is called by IJ when plugin
48     * instance is created. A concrete instance of {@link AbstractPluginOptions} class should be
49     * created there and then passed to {@link #AbstractOptionsParser(AbstractPluginOptions)}.
50     */
51    protected AbstractOptionsParser() {
52      throw new NoSuchMethodError();
53    }
54  
55    /**
56     * Default constructor.
57     * 
58     * <p>Set {@link #apiCall} to false and assign provided options to object.
59     * 
60     * @param options Reference to plugin configuration container.
61     */
62    protected AbstractOptionsParser(AbstractPluginOptions options) {
63      apiCall = false;
64      this.options = options;
65    }
66  
67    /**
68     * Constructor that allows to provide own parameter string.
69     * 
70     * <p>Intended to run from API. Set {@link #apiCall} to true and {@link #errorSink} to
71     * {@link MessageSinkTypes#CONSOLE}.
72     * {@link AbstractPluginOptions} is initialised from specified string and assigned to this
73     * instance.
74     * 
75     * @param argString parameters string like that passed in macro. If it is empty string or null
76     *        constructor exits before deserialisation.
77     * @param options Reference to plugin configuration container.
78     * @throws QuimpPluginException on any error in plugin execution.
79     */
80    protected AbstractOptionsParser(String argString, AbstractPluginOptions options)
81            throws QuimpPluginException {
82      apiCall = true;
83      this.options = options;
84      errorSink = MessageSinkTypes.CONSOLE;
85      if (argString == null || argString.isEmpty()) {
86        return;
87      }
88      this.options = AbstractPluginOptions.deserialize2Macro(argString, options);
89    }
90  
91    /**
92     * Analyse and parse parameter string passed from IJ, macro or API.
93     * 
94     * <p>If arg is empty or null, {@link #parseArgumentString(String)} tries to get it from Macro, if
95     * succeeds it parses it and returns true. Otherwise returns false. If arg is non-empty it assumes
96     * Macro call, sets {@link #errorSink} to {@link MessageSinkTypes#IJERROR} and parses arg
97     * returning true. {@link #apiCall} is set to false.
98     * 
99     * <p>If parser succeeded, internal {@link AbstractPluginOptions} object is properly initalised
100    * and deserialised.
101    * 
102    * @param arg parameter string
103    * @return return true if something has been parsed, false if input was empty or null
104    * @throws QuimpPluginException when parsing failed
105    * @see AbstractPluginOptions
106    * @see #getOptions()
107    */
108   protected boolean parseArgumentString(String arg) throws QuimpPluginException {
109     String argString;
110     IJ.log(new QuimpToolsCollection().getQuimPversion());
111     // decode possible params passed in macro or from constructor
112     if (arg == null || arg.isEmpty()) { // no options passed directly to method
113       argString = Macro.getOptions(); // check if there are any in macro
114     } else {
115       argString = arg; // options passed here - they must be in the same format as in macro
116     }
117     if (argString != null && !argString.isEmpty()) { // something passed
118       errorSink = MessageSinkTypes.IJERROR; // set errors to ij, we are in macro mode
119       options = AbstractPluginOptions.deserialize2Macro(argString, options);
120       return true;
121     } else {
122       return false;
123     }
124   }
125 
126   /**
127    * Return {@link #options}.
128    * 
129    * @return the options
130    */
131   public AbstractPluginOptions getOptions() {
132     return options;
133   }
134 
135   /**
136    * Helper, show macro parameters string if recorder is active.
137    * 
138    * <p>Perform serialisation of {@link AbstractPluginOptions} object composed with this
139    * class.
140    * 
141    * @param commandName name of the command to be displayed in IJ. Usually it is string taken from
142    *        plugins.conf
143    */
144   protected void publishMacroString(String commandName) {
145     // check whether config file name is provided or ask user for it
146     if (options != null) { // for some mockito tests
147       logger.debug("Internal options " + options.serialize2Macro());
148     }
149     if (Recorder.record) {
150       Recorder.setCommand(commandName);
151       Recorder.recordOption(AbstractPluginOptions.KEY, options.serialize2Macro());
152       Recorder.saveCommand();
153     }
154   }
155 
156 }