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 }