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