001 /*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2013
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 *
010 * All Rights Reserved
011 *
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.
014 *
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 *
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023 * GNU Lesser Public License for more details.
024 *
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program. If not, see http://www.gnu.org/licenses.
027 */
028
029 package edu.wisc.ssec.mcidasv;
030
031 import java.rmi.RemoteException;
032 import java.util.ArrayList;
033 import java.util.Collections;
034 import java.util.List;
035
036 import edu.wisc.ssec.mcidasv.startupmanager.StartupManager;
037 import ucar.unidata.idv.ArgsManager;
038 import ucar.unidata.idv.IntegratedDataViewer;
039 import ucar.unidata.util.IOUtil;
040 import ucar.unidata.util.LogUtil;
041 import ucar.unidata.util.PatternFileFilter;
042 import visad.VisADException;
043
044 /**
045 * McIDAS-V needs to handle a few command line flags/options that the IDV does
046 * not. Only the ability to force the Aqua look and feel currently exists.
047 *
048 * @author McIDAS-V Developers
049 */
050 public class ArgumentManager extends ArgsManager {
051
052 /** McIDAS-V flag that signifies everything that follows is a Jython argument. */
053 public static final String ARG_JYTHONARGS = "-scriptargs";
054
055 /** usage message */
056 public static final String USAGE_MESSAGE =
057 "Usage: runMcV [OPTIONS] <bundle/script files, e.g., .mcv, .mcvz, .py>";
058
059 /** Jython arguments, if any. */
060 private List<String> jythonArguments;
061
062 /** Jython script to execute, or {@literal "<none>"} if one was not given. */
063 private String jythonScript;
064
065 /**
066 * Given by the "-user" argument. Alternative user path for bundles, resources, etc.
067 */
068 String defaultUserDirectory = StartupManager.getInstance().getPlatform().getUserDirectory();
069
070 /**
071 * Just bubblin' on up the inheritance hierarchy.
072 *
073 * @param idv The IDV instance.
074 * @param args The command line arguments that were given.
075 */
076 public ArgumentManager(IntegratedDataViewer idv, String[] args) {
077 super(idv, args);
078 jythonArguments = new ArrayList<String>();
079 jythonScript = "<none>";
080 }
081
082 private static List<String> extractJythonArgs(int index, String[] args) {
083 List<String> jythonArgs = new ArrayList<String>(args.length);
084 for (int i = index; i < args.length; i++) {
085 jythonArgs.add(args[i]);
086 }
087 return jythonArgs;
088 }
089
090 /**
091 * Currently we're only handling the {@code -forceaqua} flag so we can
092 * mitigate some overlay issues we've been seeing on OS X Leopard.
093 *
094 * @param arg The current argument we're examining.
095 * @param args The actual array of arguments.
096 * @param idx The index of {@code arg} within {@code args}.
097 *
098 * @return The idx of the last value in the args array we look at. i.e.,
099 * if the flag arg does not require any further values in the args array
100 * then don't increment idx. If arg requires one more value then
101 * increment idx by one. etc.
102 *
103 * @throws Exception Throw bad things off to something that can handle 'em!
104 */
105 protected int parseArg(String arg, String[] args, int idx)
106 throws Exception {
107
108 if ("-forceaqua".equals(arg)) {
109 // unfortunately we can't simply set the look and feel here. If I
110 // were to do so, the loadLookAndFeel in the IdvUIManager would
111 // eventually get loaded and then set the look and feel to whatever
112 // the preferences dictate.
113 // instead I use the boolean toggle to signal to McV's
114 // UIManager.loadLookAndFeel that it should simply ignore the user's
115 // preference is and load the Aqua L&F from there.
116 McIDASV.useAquaLookAndFeel = true;
117 } else if (ARG_HELP.equals(arg)) {
118 System.err.println(USAGE_MESSAGE);
119 System.err.println(getUsageMessage());
120 ((McIDASV)getIdv()).exit(1);
121 } else if (checkArg(arg, "-script", args, idx, 1) || checkArg(arg, "-pyfile", args, idx, 1)) {
122 String scriptArg = args[idx++];
123 jythonScript = scriptArg;
124 scriptingFiles.add(scriptArg);
125 if (!getIslInteractive()) {
126 setIsOffScreen(true);
127 }
128 } else if ("-console".equals(arg)) {
129 System.err.println("*** WARNING: console flag is likely to go away soon!");
130 } else if (ARG_JYTHONARGS.equals(arg)) {
131 if (scriptingFiles.isEmpty()) {
132 System.err.println("*** WARNING: Jython script arguments will be ignored unless you provide a Jython script to execute!");
133 } else {
134 jythonArguments.addAll(extractJythonArgs(idx, args));
135
136 // jump to end of args to halt further idv processing.
137 return args.length;
138 }
139 } else {
140 if (ARG_ISLINTERACTIVE.equals(arg) || ARG_B64ISL.equals(arg) || ARG_ISLFILE.equals(arg) || isIslFile(arg)) {
141 System.err.println("*** WARNING: ISL is being deprecated!");
142 }
143 return super.parseArg(arg, args, idx);
144 }
145 return idx;
146 }
147
148 /**
149 * Print out the command line usage message and exit
150 *
151 * @param err The usage message
152 */
153 @Override public void usage(String err) {
154 String msg = USAGE_MESSAGE;
155 msg = msg + '\n' + getUsageMessage();
156 LogUtil.userErrorMessage(err + '\n' + msg);
157 ((McIDASV)getIdv()).exit(1);
158 }
159
160 /**
161 * Append some McIDAS-V specific command line options to the default IDV
162 * usage message.
163 *
164 * @return Usage message.
165 */
166 protected String getUsageMessage() {
167 return msg(ARG_HELP, "(this message)")
168 + msg("-forceaqua", "Forces the Aqua look and feel on OS X")
169 + msg(ARG_PROPERTIES, "<property file>")
170 + msg("-Dpropertyname=value", "(Define the property value)")
171 + msg(ARG_INSTALLPLUGIN, "<plugin jar file or url to install>")
172 + msg(ARG_PLUGIN, "<plugin jar file, directory, url for this run>")
173 + msg(ARG_NOPLUGINS, "Don't load plugins")
174 + msg(ARG_CLEARDEFAULT, "(Clear the default bundle)")
175 + msg(ARG_NODEFAULT, "(Don't read in the default bundle file)")
176 + msg(ARG_DEFAULT, "<.mcv/.mcvz file>")
177 + msg(ARG_BUNDLE, "<bundle file or url>")
178 + msg(ARG_B64BUNDLE, "<base 64 encoded inline bundle>")
179 + msg(ARG_SETFILES, "<datasource pattern> <semi-colon delimited list of files> (Use the list of files for the bundled datasource)")
180 + msg(ARG_ONEINSTANCEPORT, "<port number> (Check if another version of McIDAS-V is running. If so pass command line arguments to it and shutdown)")
181 + msg(ARG_NOONEINSTANCE, "(Don't do the one instance port)")
182 + msg(ARG_NOPREF, "(Don't read in the user preferences)")
183 + msg(ARG_USERPATH, "<user directory to use>")
184 + msg(ARG_SITEPATH, "<url path to find site resources>")
185 + msg(ARG_NOGUI, "(Don't show the main window gui)")
186 + msg(ARG_DATA, "<data source> (Load the data source)")
187 + msg(ARG_DISPLAY, "<parameter> <display>")
188 // + msg("<scriptfile.isl>", "(Run the IDV script in batch mode)")
189 + msg("-script", "<jython script file to evaluate>")
190 + msg("-pyfile", "<jython script file to evaluate>")
191 + msg(ARG_JYTHONARGS, "All arguments after this flag will be considered Jython arguments.")
192 // + msg(ARG_B64ISL, "<base64 encoded inline isl> This will run the isl in interactive mode")
193 // + msg(ARG_ISLINTERACTIVE, "run any isl files in interactive mode")
194 + msg(ARG_IMAGE, "<image file name> (create a jpeg image and then exit)")
195 + msg(ARG_MOVIE, "<movie file name> (create a quicktime movie and then exit)")
196 + msg(ARG_IMAGESERVER, "<port number or .properties file> (run McIDAS-V in image generation server mode. Support http requests on the given port)")
197 + msg(ARG_CATALOG, "<url to a chooser catalog>")
198 + msg(ARG_CONNECT, "<collaboration hostname to connect to>")
199 + msg(ARG_SERVER, "(Should McIDAS-V run in collaboration server mode)")
200 + msg(ARG_PORT, "<Port number collaboration server should listen on>")
201 + msg(ARG_CHOOSER, "(show the data chooser on start up) ")
202 + msg(ARG_PRINTJNLP, "(Print out any embedded bundles from jnlp files)")
203 + msg(ARG_CURRENTTIME, "<dttm> (Override current time for background processing)")
204 // + msg(ARG_CURRENTTIME, "<dttm> (Override current time for ISL processing)")
205 + msg(ARG_LISTRESOURCES, "<list out the resource types")
206 + msg(ARG_DEBUG, "(Turn on debug print)")
207 + msg(ARG_MSG_DEBUG, "(Turn on language pack debug)")
208 + msg(ARG_MSG_RECORD, "<Language pack file to write missing entries to>")
209 + msg(ARG_TRACE, "(Print out trace messages)")
210 + msg(ARG_NOERRORSINGUI, "(Don't show errors in gui)")
211 + msg(ARG_TRACEONLY, "<trace pattern> (Print out trace messages that match the pattern)")
212 + msg("-console", "[ fix for getting the console functionality in install4j launcher ]");
213 }
214
215 /**
216 * Determine whether or not the user has provided any arguments for a
217 * Jython script.
218 *
219 * @return {@code true} if the user has provided Jython arguments,
220 * {@code false} otherwise.
221 */
222 public boolean hasJythonArguments() {
223 return !jythonArguments.isEmpty();
224 }
225
226 /**
227 * Returns Jython arguments. <b>Note:</b> this does not include the Jython
228 * script that will be executed.
229 *
230 * @return Either a {@link List} of {@link String Strings} containing the
231 * arguments or an empty {@code List} if there were no arguments given.
232 */
233 public List<String> getJythonArguments() {
234 return jythonArguments;
235 }
236
237 /**
238 * Returns the name of the Jython script the user has provided.
239 *
240 * @return Either the path to a Jython file or {@literal "<none>"} if the
241 * user did not provide a script.
242 */
243 public String getJythonScript() {
244 return jythonScript;
245 }
246
247 /**
248 * Gets called by the IDV to process the set of initial files, e.g.,
249 * default bundles, command line bundles, jnlp files, etc.
250 *
251 * <p>Overridden by McIDAS-V to remove bundle file paths that are zero
252 * characters long. This was happening because {@code runMcV.bat} was
253 * always passing {@literal '-bundle ""'} on the command line (for Windows).
254 *
255 * @throws VisADException When something untoward happens
256 * @throws RemoteException When something untoward happens
257 */
258 @Override protected void processInitialBundles()
259 throws VisADException, RemoteException
260 {
261 for (int i = 0; i < argXidvFiles.size(); i++) {
262 String path = (String)argXidvFiles.get(i);
263 if (path.isEmpty()) {
264 argXidvFiles.remove(i);
265 }
266 }
267 super.processInitialBundles();
268 }
269
270 /**
271 * @see ArgsManager#getBundleFileFilters()
272 */
273 @Override public List<PatternFileFilter> getBundleFileFilters() {
274 List<PatternFileFilter> filters = new ArrayList<PatternFileFilter>();
275 Collections.addAll(filters, getXidvFileFilter(), getZidvFileFilter(), FILTER_JNLP, FILTER_ISL, super.getXidvFileFilter(), super.getZidvFileFilter());
276 return filters;
277 }
278
279 /**
280 * Returns a list of {@link PatternFileFilter}s that can be used to determine
281 * if a file is a bundle.
282 *
283 * <p>If {@code fromOpen} is {@code true}, the
284 * returned list will contain {@code PatternFileFilter}s for bundles as
285 * well as JNLP and ISL files. If {@code false}, the returned list will
286 * only contain filters for XML and zipped bundles.
287 *
288 * @param fromOpen Whether or not this has been called from an
289 * {@literal "open file"} dialog.
290 *
291 * @return Filters for bundles.
292 */
293 public List<PatternFileFilter> getBundleFilters(final boolean fromOpen) {
294 List<PatternFileFilter> filters = new ArrayList<PatternFileFilter>();
295
296 if (fromOpen)
297 Collections.addAll(filters, getXidvZidvFileFilter(), FILTER_JNLP, FILTER_ISL, super.getXidvZidvFileFilter());
298 else
299 filters.addAll(getBundleFileFilters());
300
301 return filters;
302 }
303
304 /**
305 * @see ArgsManager#getXidvFileFilter()
306 */
307 @Override public PatternFileFilter getXidvFileFilter() {
308 return Constants.FILTER_MCV;
309 }
310
311 /**
312 * @see ArgsManager#getZidvFileFilter()
313 */
314 @Override public PatternFileFilter getZidvFileFilter() {
315 return Constants.FILTER_MCVZ;
316 }
317
318 /**
319 * @see ArgsManager#getXidvZidvFileFilter()
320 */
321 @Override public PatternFileFilter getXidvZidvFileFilter() {
322 return Constants.FILTER_MCVMCVZ;
323 }
324
325 /*
326 * There's some internal IDV file opening code that relies on this method.
327 * We've gotta override if we want to use .zidv bundles.
328 */
329 @Override public boolean isZidvFile(final String name) {
330 return isZippedBundle(name);
331 }
332
333 /* same story as isZidvFile! */
334 @Override public boolean isXidvFile(final String name) {
335 return isXmlBundle(name);
336 }
337
338 /**
339 * Tests to see if <code>name</code> has a known XML bundle extension.
340 *
341 * @param name Name of the bundle.
342 *
343 * @return Whether or not <code>name</code> has an XML bundle suffix.
344 */
345 public static boolean isXmlBundle(final String name) {
346 return IOUtil.hasSuffix(name, Constants.FILTER_MCV.getPreferredSuffix())
347 || IOUtil.hasSuffix(name, Constants.FILTER_XIDV.getPreferredSuffix());
348 }
349
350 /**
351 * Tests to see if <code>name</code> has a known zipped bundle extension.
352 *
353 * @param name Name of the bundle.
354 *
355 * @return Whether or not <code>name</code> has zipped bundle suffix.
356 */
357 public static boolean isZippedBundle(final String name) {
358 return IOUtil.hasSuffix(name, Constants.FILTER_MCVZ.getPreferredSuffix())
359 || IOUtil.hasSuffix(name, Constants.FILTER_ZIDV.getPreferredSuffix());
360 }
361
362 /**
363 * Tests <code>name</code> to see if it has a known bundle extension.
364 *
365 * @param name Name of the bundle.
366 *
367 * @return Whether or not <code>name</code> has a bundle suffix.
368 */
369 public static boolean isBundle(final String name) {
370 return (isXmlBundle(name) || isZippedBundle(name));
371 }
372 }