001 package org.LiveGraph.bootstrap; 002 003 import java.io.File; 004 import java.util.ArrayList; 005 import java.util.List; 006 007 import org.LiveGraph.settings.DataFileSettings; 008 import org.LiveGraph.settings.DataSeriesSettings; 009 import org.LiveGraph.settings.GraphSettings; 010 011 /** 012 * This class represents a parser for command line arguments for a LiveGraph application. 013 * 014 * <p> 015 * <strong>LiveGraph</strong> 016 * (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>). 017 * </p> 018 * <p>Copyright (c) 2007-2008 by G. Paperin.</p> 019 * <p>File: CommandLineProcessor.java</p> 020 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or 021 * without modification, are permitted provided that the following terms and conditions are met: 022 * </p> 023 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above 024 * acknowledgement of the LiveGraph project and its web-site, the above copyright notice, 025 * this list of conditions and the following disclaimer.<br /> 026 * 2. Redistributions in binary form must reproduce the above acknowledgement of the 027 * LiveGraph project and its web-site, the above copyright notice, this list of conditions 028 * and the following disclaimer in the documentation and/or other materials provided with 029 * the distribution.<br /> 030 * 3. All advertising materials mentioning features or use of this software or any derived 031 * software must display the following acknowledgement:<br /> 032 * <em>This product includes software developed by the LiveGraph project and its 033 * contributors.<br />(http://www.live-graph.org)</em><br /> 034 * 4. All advertising materials distributed in form of HTML pages or any other technology 035 * permitting active hyper-links that mention features or use of this software or any 036 * derived software must display the acknowledgment specified in condition 3 of this 037 * agreement, and in addition, include a visible and working hyper-link to the LiveGraph 038 * homepage (http://www.live-graph.org). 039 * </p> 040 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 041 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 042 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 043 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 044 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 045 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 046 * </p> 047 * 048 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>) 049 * @version {@value org.LiveGraph.LiveGraph#version} 050 * 051 */ 052 public class CommandLineProcessor { 053 054 /** 055 * System specific new-line string. 056 */ 057 public static final String newLine = String.format("%n"); 058 059 /** 060 * Message specifying the correct usage of command line argumnts. 061 */ 062 public static final String correctPromptMsg = 063 "Legal command line arguments are as follows: " + newLine 064 + " > java edu.monash.LiveGraph.LiveGraph [-dfs \"{data file settings file}\"] " + newLine 065 + " [-gs \"{graph settings file}\"] " + newLine 066 + " [-dss \"{data series settings file}\"] " + newLine 067 + " [-f \"{actual data file}\"] " + newLine 068 + " This means the program expects either 0, 2, 4, 6 or 8 command line arguments. " + newLine 069 + " Note: '-f' overrides any data file that may be specified within " + newLine 070 + " a data file settings file that may be set with '-dfs'."; 071 072 073 /** 074 * Holds any error messages occured during parsing. 075 */ 076 private List<String> errMessages = new ArrayList<String>(); 077 078 /** 079 * Flag set when any errors during argument parsing or validation occur. 080 */ 081 private boolean hasError = false; 082 083 084 /** 085 * Command line arguments to parse. 086 */ 087 private String[] args = null; 088 089 /** 090 * The data file settings file if such was parsed. 091 */ 092 private File file_DataFileSettings = null; 093 094 /** 095 * The graph settings file if such was parsed. 096 */ 097 private File file_GraphSettings = null; 098 099 /** 100 * The data series settings file if such was parsed. 101 */ 102 private File file_DataSeriesSettings = null; 103 104 /** 105 * The data file if such was parsed. 106 */ 107 private File file_Data = null; 108 109 110 /** 111 * Initialises the parser for an empty command line. 112 * 113 */ 114 public CommandLineProcessor() { 115 this.args = new String[0]; 116 processArgs(); 117 } 118 119 120 /** 121 * Initialises the parser for the specified command line arguments. 122 * 123 * @param args Command line arguments. 124 */ 125 public CommandLineProcessor(String[] args) { 126 if (null == args) { 127 this.args = new String[0]; 128 } else { 129 this.args = new String[args.length]; 130 for (int i = 0; i < args.length; i++) 131 this.args[i] = args[i]; 132 } 133 processArgs(); 134 } 135 136 137 /** 138 * Used internally to process the arguments. 139 * First, parses the command line. 140 * Then, tries to apply default settings for arguments not specified on the command line. 141 */ 142 private void processArgs() { 143 parseArgs(); 144 supplementWithDefaults(); 145 } 146 147 148 /** 149 * If data file, graph or data series settings are not set, checks whether the 150 * apropriate session.lgdfs, session.lggs and session.lgdss files are available 151 * and, if so, sets the argument values appropriately. 152 */ 153 private void supplementWithDefaults() { 154 155 if (null == getFile_DataFileSettings()) { 156 File f = new File("session" + DataFileSettings.preferredFileExtension); 157 try { 158 if (f.exists()) 159 setFile_DataFileSettings(f.getAbsoluteFile()); 160 } catch(SecurityException e) {} 161 } 162 163 if (null == getFile_GraphSettings()) { 164 try { 165 File f = new File("session" + GraphSettings.preferredFileExtension); 166 if (f.exists()) 167 setFile_GraphSettings(f.getAbsoluteFile()); 168 } catch(SecurityException e) {} 169 } 170 171 if (null == getFile_DataSeriesSettings()) { 172 try { 173 File f = new File("session" + DataSeriesSettings.preferredFileExtension); 174 if (f.exists()) 175 setFile_DataSeriesSettings(f.getAbsoluteFile()); 176 } catch(SecurityException e) {} 177 } 178 179 // No default for data file! 180 } 181 182 183 /** 184 * Parses the command line arguments for legal settings values. 185 */ 186 private void parseArgs() { 187 188 int parseIndex = 0; 189 while (parseIndex < args.length) { 190 191 String arg = args[parseIndex].trim(); 192 193 if ("-dfs".equalsIgnoreCase(arg)) { 194 File f = parseArgFile2(parseIndex, "file for data file settings", getFile_DataFileSettings()); 195 if (null != f) 196 setFile_DataFileSettings(f.getAbsoluteFile()); 197 parseIndex += 2; 198 continue; 199 } 200 201 if ("-gs".equalsIgnoreCase(arg)) { 202 File f = parseArgFile2(parseIndex, "file for graph settings", getFile_GraphSettings()); 203 if (null != f) 204 setFile_GraphSettings(f.getAbsoluteFile()); 205 parseIndex += 2; 206 continue; 207 } 208 209 if ("-dss".equalsIgnoreCase(arg)) { 210 File f = parseArgFile2(parseIndex, "file for data series settings", getFile_DataSeriesSettings()); 211 if (null != f) 212 setFile_DataSeriesSettings(f.getAbsoluteFile()); 213 parseIndex += 2; 214 continue; 215 } 216 217 if ("-f".equalsIgnoreCase(arg)) { 218 File f = parseArgFile2(parseIndex, "data file", getFile_Data()); 219 if (null != f) 220 setFile_Data(f.getAbsoluteFile()); 221 parseIndex += 2; 222 continue; 223 } 224 225 error(parseIndex, arg, "This is an illegal argument and cannot be processed. It will be ignored."); 226 parseIndex += 1; 227 } 228 } 229 230 231 /** 232 * Validates a pair of command line arguments, 233 * where the first is a flag and the second is a file path. 234 * 235 * @param parseIndex The argument index of the flag, i.e. the first argument of the pair. 236 * @param argDesc A textual description of the current flag (for megsages). 237 * @param currValue The current value for the setting corresponding to the flag. 238 * 239 * @return A validated File object that stands for the path described by the second argument. 240 */ 241 private File parseArgFile2(int parseIndex, String argDesc, File currValue) { 242 243 if (parseIndex + 1 >= args.length) { 244 error(parseIndex, args[parseIndex], 245 "This argument specifies '" + argDesc + "' and must be followed by a file path.", 246 "However, this argument is not followed by anything. It will be ignored."); 247 return null; 248 } 249 250 String fileName = args[parseIndex + 1].trim(); 251 252 if (null != currValue) { 253 error(parseIndex, args[parseIndex], 254 "This argument specifies '" + argDesc + "' and must be unique.", 255 "However, '" + argDesc + "' is already set to '" + currValue + "'.", 256 "The repeated argument will be ignored."); 257 return null; 258 } 259 260 File file = new File(fileName); 261 262 try { 263 if (!file.exists()) { 264 error(parseIndex, args[parseIndex], 265 "This argument specifies '" + argDesc + "'.", 266 "However, the specified path '" + fileName + "' does not exist.", 267 "This argument will be ignored."); 268 return null; 269 } 270 271 if (!file.canRead()) { 272 error(parseIndex, args[parseIndex], 273 "This argument specifies '" + argDesc + "'.", 274 "However, the specified path '" + fileName + "' cannot be read.", 275 "This argument will be ignored."); 276 return null; 277 } 278 279 if (file.isDirectory()) { 280 error(parseIndex, args[parseIndex], 281 "This argument specifies '" + argDesc + "'.", 282 "However, the specified path '" + fileName + "' is a directory.", 283 "This argument will be ignored."); 284 return null; 285 } 286 287 if (!file.isFile()) { 288 error(parseIndex, args[parseIndex], 289 "This argument specifies '" + argDesc + "'.", 290 "However, the specified path '" + fileName + "' is not a normal file.", 291 "This argument will be ignored."); 292 return null; 293 } 294 } catch(SecurityException e) { 295 error(parseIndex, args[parseIndex], 296 "This argument specifies '" + argDesc + "'.", 297 "However, the specified path '" + fileName + "' cannot be accessed by LiveGraph.", 298 "Info: " + e.getMessage() + ".", 299 "This argument will be ignored."); 300 return null; 301 } 302 303 return file; 304 } 305 306 307 /** 308 * Logs an error message. 309 * 310 * @param argIndex Index of the argument to which the error relates. 311 * @param argStr The argument to which the error relates. 312 * @param messages Error messages. 313 */ 314 private void error(int argIndex, String argStr, String... messages) { 315 316 StringBuffer s = new StringBuffer(); 317 318 s.append("Error in command line argument #"); 319 s.append(argIndex + 1); 320 s.append(" (\""); 321 s.append(argStr); 322 s.append("\")"); 323 s.append(newLine); 324 for (String msg : messages) { 325 s.append(" "); 326 s.append(msg); 327 s.append(newLine); 328 } 329 330 errMessages.add(s.toString()); 331 hasError = true; 332 } 333 334 335 /** 336 * Checks whether any errors have occured. 337 * 338 * @return {@code true} if at least one error occured during parsing or validation, 339 * {@code false} otherwise. 340 */ 341 public boolean hasErrors() { 342 return hasError; 343 } 344 345 346 /** 347 * Constructs a {@code String} from all error messages that occured during parsing or validation. 348 * 349 * @return A verbose message resarding all errors that occured during parsing or validation, 350 * or {@code null} if no errors have occured. 351 */ 352 public String getErrorMessages() { 353 354 if (!hasErrors()) 355 return null; 356 357 StringBuffer s = new StringBuffer(); 358 for (String msg : errMessages) { 359 s.append(msg.trim()); 360 s.append(newLine); 361 } 362 s.append(correctPromptMsg.trim()); 363 s.append(newLine); 364 365 return s.toString(); 366 } 367 368 369 /** 370 * File for data file settings - either parsed from the command line or the default session file. 371 * @return Validated {@code File} from which LiveGraph should load the data file settings 372 * or {@code null} if no file was set or if the file is not present or cannot be accessed. 373 */ 374 public File getFile_DataFileSettings() { 375 return file_DataFileSettings; 376 } 377 378 379 /** 380 * Sets the file for data file settings - either parsed from the command line or the default session file. 381 * @param f Validated {@code File} from which LiveGraph should load the data file settings. 382 */ 383 private void setFile_DataFileSettings(File f) { 384 file_DataFileSettings = f; 385 } 386 387 388 /** 389 * File for graph settings - either parsed from the command line or the default session file. 390 * @return Validated {@code File} from which LiveGraph should load the graph settings 391 * or {@code null} if no file was set or if the file is not present or cannot be accessed. 392 */ 393 public File getFile_GraphSettings() { 394 return file_GraphSettings; 395 } 396 397 398 /** 399 * Sets the file for graph settings - either parsed from the command line or the default session file. 400 * @param f Validated {@code File} from which LiveGraph should load the graph settings. 401 */ 402 private void setFile_GraphSettings(File f) { 403 file_GraphSettings = f; 404 } 405 406 407 /** 408 * File for data series settings - either parsed from the command line or the default session file. 409 * @return Validated {@code File} from which LiveGraph should load the data series settings 410 * or {@code null} if no file was set or if the file is not present or cannot be accessed. 411 */ 412 public File getFile_DataSeriesSettings() { 413 return file_DataSeriesSettings; 414 } 415 416 417 /** 418 * Sets the file for data series settings - either parsed from the command line or the default session file. 419 * @param f Validated {@code File} from which LiveGraph should load the data series settings. 420 */ 421 private void setFile_DataSeriesSettings(File f) { 422 file_DataSeriesSettings = f; 423 } 424 425 426 /** 427 * Data file - parsed from the command line. 428 * @return Validated {@code File} from which LiveGraph should load the data 429 * or {@code null} if no file was set or if the file is not present or cannot be accessed. 430 */ 431 public File getFile_Data() { 432 return file_Data; 433 } 434 435 436 /** 437 * Sets the data file -parsed from the command line. 438 * @param f Validated {@code File} from which LiveGraph should load the data. 439 */ 440 private void setFile_Data(File f) { 441 file_Data = f; 442 } 443 444 445 } // class CommandLineProcessor