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 }