1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package gov.nasa.pds.vtool;
17
18
19 import gov.nasa.pds.tools.dict.Dictionary;
20 import gov.nasa.pds.tools.dict.parser.DictionaryParser;
21 import gov.nasa.pds.tools.file.FileList;
22 import gov.nasa.pds.tools.file.FileListGenerator;
23 import gov.nasa.pds.tools.handler.ToolsFileHandler;
24 import gov.nasa.pds.tools.handler.ToolsStreamHandler;
25 import gov.nasa.pds.tools.label.Label;
26 import gov.nasa.pds.tools.label.parser.LabelParser;
27 import gov.nasa.pds.tools.label.parser.LabelParserFactory;
28 import gov.nasa.pds.tools.label.validate.Status;
29 import gov.nasa.pds.tools.license.ToolsLicense;
30 import gov.nasa.pds.tools.logging.ToolsLevel;
31 import gov.nasa.pds.tools.logging.ToolsLogFormatter;
32 import gov.nasa.pds.tools.logging.ToolsLogRecord;
33 import gov.nasa.pds.tools.options.ToolsOption;
34 import gov.nasa.pds.tools.report.StyleSheet;
35 import gov.nasa.pds.tools.time.ToolsTime;
36 import gov.nasa.pds.tools.util.Utility;
37 import gov.nasa.pds.vtool.config.VToolConfigKeys;
38 import gov.nasa.pds.vtool.flags.VToolFlags;
39 import gov.nasa.pds.vtool.handler.HandlerFactory;
40 import gov.nasa.pds.vtool.handler.UnknownHandlerConfigurationException;
41 import gov.nasa.pds.vtool.status.ExitStatus;
42 import gov.nasa.pds.vtool.status.ExitStatusType;
43 import gov.nasa.pds.vtool.validate.UnknownLabelStatusException;
44 import gov.nasa.pds.vtool.validate.ValidationRecord;
45 import gov.nasa.pds.vtool.validate.Validator;
46 import gov.nasa.pds.vtool.validate.ValidatorException;
47 import gov.nasa.pds.vtool.validate.ValidatorFactory;
48 import java.io.File;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.net.MalformedURLException;
52 import java.net.URL;
53 import java.text.SimpleDateFormat;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Iterator;
57 import java.util.List;
58 import java.util.Properties;
59 import java.util.logging.Handler;
60 import java.util.logging.Level;
61 import java.util.logging.Logger;
62
63 import org.apache.commons.cli.CommandLine;
64 import org.apache.commons.cli.CommandLineParser;
65 import org.apache.commons.cli.GnuParser;
66 import org.apache.commons.cli.HelpFormatter;
67 import org.apache.commons.cli.Options;
68 import org.apache.commons.cli.ParseException;
69 import org.apache.commons.configuration.AbstractConfiguration;
70 import org.apache.commons.configuration.Configuration;
71 import org.apache.commons.configuration.ConfigurationException;
72 import org.apache.commons.configuration.ConversionException;
73 import org.apache.commons.configuration.PropertiesConfiguration;
74
75 /***
76 * Class to perform automated validation to determine if a given data product
77 * is PDS compliant.
78 * <p>
79 * This replaces LVTool functionality.
80 *
81 * @author mcayanan
82 * @version $Revision: 3392 $
83 *
84 *
85 */
86 public class VTool implements VToolConfigKeys, VToolFlags, Status,
87 ExitStatusType, ToolsLicense, StyleSheet {
88 private final String FILE_REP = "*";
89 private static Logger log = Logger.getLogger(VTool.class.getName());
90 private String currDir;
91 private Options options;
92
93 private boolean alias;
94 private URL config;
95
96 private List dictionaries;
97 private List userSpecifiedDictionaries;
98 private List noFiles;
99 private List noDirs;
100 private boolean followPtrs;
101 private List includePaths;
102 private boolean force;
103 private boolean progress;
104 private List regexp;
105 private boolean recursive;
106 private List targets;
107 private File logFile;
108 private boolean showLog;
109 private File rptFile;
110 private String rptStyle;
111 private short verbose;
112 private String severity;
113 private boolean dictionaryPassed;
114
115 private final String PROPERTYFILE = "vtool.properties";
116 private final String PROPERTYTOOLNAME = "vtool.name";
117 private final String PROPERTYVERSION = "vtool.version";
118 private final String PROPERTYDATE = "vtool.date";
119 private final String PROPERTYCOPYRIGHT = "vtool.copyright";
120
121 /***
122 * Default constructor.
123 */
124 public VTool() {
125 alias = false;
126 config = null;
127 dictionaries = new ArrayList();
128 userSpecifiedDictionaries = null;
129
130 noFiles = null;
131 noDirs = null;
132 followPtrs = true;
133 targets = new ArrayList();
134 includePaths = null;
135 force = false;
136 progress = false;
137 regexp = null;
138 recursive = true;
139 rptFile = null;
140 rptStyle = "full";
141 severity = "Warning";
142 logFile = null;
143 showLog = false;
144 verbose = 2;
145 currDir = null;
146 buildOpts();
147 dictionaryPassed = true;
148 }
149
150 /***
151 * Show the version and disclaimer notice for VTool.
152 * @throws IOException
153 *
154 */
155 public void showVersion() throws IOException {
156 URL propertyFile = this.getClass().getResource(PROPERTYFILE);
157 Properties p = new Properties();
158 InputStream in = null;
159 try {
160 in = propertyFile.openStream();
161 p.load(in);
162 } finally {
163 in.close();
164 }
165 System.err.println("\n" + p.get(PROPERTYTOOLNAME));
166 System.err.println(p.get(PROPERTYVERSION));
167 System.err.println("Release Date: " + p.get(PROPERTYDATE));
168 System.err.println(p.get(PROPERTYCOPYRIGHT) + "\n");
169 }
170
171 /***
172 * Display VTool usage and help information
173 *
174 */
175 public void showHelp() {
176 HelpFormatter formatter = new HelpFormatter();
177 formatter.printHelp(37, "VTool", null, options, null);
178 }
179
180 /***
181 * Builds the set of configurable parameters for VTool.
182 */
183 private void buildOpts() {
184 options = new Options();
185 ToolsOption opt = null;
186
187 options.addOption(new ToolsOption(ALIAS[SHORT], ALIAS[LONG], WHATIS_ALIAS));
188 options.addOption(new ToolsOption(FOLLOW[SHORT], FOLLOW[LONG],WHATIS_FOLLOW));
189 options.addOption(new ToolsOption(HELP[SHORT], HELP[LONG], WHATIS_HELP));
190 options.addOption(new ToolsOption(PARTIAL[SHORT], PARTIAL[LONG], WHATIS_PARTIAL));
191 options.addOption(new ToolsOption(LOCAL[SHORT], LOCAL[LONG], WHATIS_LOCAL));
192 options.addOption(new ToolsOption(PROGRESS[SHORT], PROGRESS[LONG], WHATIS_PROGRESS));
193 options.addOption(new ToolsOption(VERSION[SHORT], VERSION[LONG], WHATIS_VERSION));
194
195
196
197
198 opt = new ToolsOption(CONFIG[SHORT], CONFIG[LONG], WHATIS_CONFIG);
199 opt.hasArg(CONFIG[ARGNAME], String.class);
200 options.addOption(opt);
201
202
203 opt = new ToolsOption(DICT[SHORT], DICT[LONG], WHATIS_DICT);
204 opt.hasArgs(DICT[ARGNAME], String.class);
205 options.addOption(opt);
206
207
208 opt = new ToolsOption(IGNOREFILE[SHORT], IGNOREFILE[LONG],WHATIS_IGNOREFILE);
209 opt.hasArgs(IGNOREFILE[ARGNAME], String.class);
210 options.addOption(opt);
211
212
213 opt = new ToolsOption(TARGET[SHORT], TARGET[LONG], WHATIS_TARGET);
214 opt.hasArgs(TARGET[ARGNAME], String.class);
215 options.addOption(opt);
216
217
218 opt = new ToolsOption(REGEXP[SHORT], REGEXP[LONG], WHATIS_REGEXP);
219 opt.hasArgs(REGEXP[ARGNAME], String.class);
220 options.addOption(opt);
221
222
223 opt = new ToolsOption(INCLUDES[SHORT], INCLUDES[LONG], WHATIS_INCLUDES);
224 opt.hasArgs(INCLUDES[ARGNAME], String.class);
225 options.addOption(opt);
226
227
228 opt = new ToolsOption(IGNOREDIR[SHORT], IGNOREDIR[LONG], WHATIS_IGNOREDIR);
229 opt.hasArgs(IGNOREDIR[ARGNAME], String.class);
230 options.addOption(opt);
231
232
233 opt = new ToolsOption(LOG[SHORT], LOG[LONG], WHATIS_LOG);
234 opt.hasArg(LOG[ARGNAME], String.class, true);
235 options.addOption(opt);
236
237
238 opt = new ToolsOption(REPORT[SHORT], REPORT[LONG], WHATIS_REPORT);
239 opt.hasArg(REPORT[ARGNAME], String.class);
240 options.addOption(opt);
241
242
243 opt = new ToolsOption(RPTSTYLE[SHORT], RPTSTYLE[LONG], WHATIS_RPTSTYLE);
244 opt.hasArg(RPTSTYLE[ARGNAME], String.class);
245 options.addOption(opt);
246
247
248 opt = new ToolsOption(VERBOSE[SHORT], VERBOSE[LONG], WHATIS_VERBOSE);
249 opt.hasArg(VERBOSE[ARGNAME], short.class);
250 options.addOption(opt);
251 }
252
253 /***
254 * Parses the VTool command-line.
255 * @param argv arguments given on the command-line
256 * @throws ApplicationException
257 */
258 public CommandLine parseLine(String[] argv) throws ApplicationException {
259 CommandLineParser parser = new GnuParser();
260 try {
261 return parser.parse(options, argv);
262 }
263 catch( ParseException exp ) {
264 throw new ApplicationException("Command line parser failed.\n\nReason: " + exp.getMessage() );
265 }
266 }
267
268 /***
269 * Queries the VTool command-line.
270 * @throws ApplicationException
271 * @throws MalformedURLException
272 * @throws SystemException
273 *
274 */
275 public void queryCmdLine(CommandLine cmd) throws ApplicationException,
276 MalformedURLException, SystemException {
277
278
279 if (cmd.hasOption(HELP[SHORT])) {
280 showHelp();
281 System.exit(SUCCESS);
282 } else if(cmd.hasOption(VERSION[SHORT])) {
283
284
285 try {
286 showVersion();
287 } catch (IOException i) {
288 throw new SystemException(i.getMessage());
289 }
290 System.exit(SUCCESS);
291 }
292 else {
293
294 if (cmd.hasOption(CONFIG[SHORT])) {
295 URL file = Utility.toURL(cmd.getOptionValue(CONFIG[SHORT]));
296 readConfigFile(file);
297 }
298
299
300 if (cmd.hasOption(LOG[SHORT])) {
301 if (cmd.getOptionValue(LOG[SHORT]) != null)
302 setLogFile(new File(cmd.getOptionValue(LOG[SHORT])));
303 else
304 setShowLog(true);
305 }
306
307 if (cmd.hasOption(VERBOSE[SHORT])) {
308 try {
309 setVerbose(Short.parseShort(cmd.getOptionValue(VERBOSE[SHORT])));
310 }
311 catch(NumberFormatException ne) {
312 throw new ApplicationException("Problems parsing value for the 'v' flag: "
313 + cmd.getOptionValue(VERBOSE[SHORT]));
314 }
315 catch(IllegalArgumentException ae) {
316 throw new ApplicationException(ae.getMessage());
317 }
318 }
319
320 List targetList = new ArrayList();
321
322 for(Iterator i=cmd.getArgList().iterator(); i.hasNext();) {
323 String []values = i.next().toString().split(",");
324 for(int index=0; index<values.length; index++)
325 targetList.add(values[index].trim());
326 }
327
328 if (cmd.hasOption(TARGET[SHORT])) {
329 targetList.addAll(
330 Arrays.asList(cmd.getOptionValues(TARGET[SHORT])));
331 }
332 if(!targetList.isEmpty()) {
333 setTargets(targetList);
334 }
335
336
337 if (cmd.hasOption(ALIAS[SHORT]))
338 setAlias(true);
339
340 if (cmd.hasOption(FOLLOW[SHORT]))
341 setFollowPtrs(false);
342
343
344 if (cmd.hasOption(INCLUDES[SHORT]))
345 setIncludePaths(
346 Arrays.asList(cmd.getOptionValues(INCLUDES[SHORT])));
347
348
349
350
351 if (cmd.hasOption(PROGRESS[SHORT]))
352 setProgress(true);
353
354 if (cmd.hasOption(LOCAL[SHORT]))
355 setRecursive(false);
356
357 if (cmd.hasOption(REGEXP[SHORT]))
358 setRegexp(Arrays.asList(cmd.getOptionValues(REGEXP[SHORT])));
359
360 if (cmd.hasOption(IGNOREFILE[SHORT])) {
361 setNoFiles(Arrays.asList(
362 cmd.getOptionValues(IGNOREFILE[SHORT])));
363 }
364
365 if (cmd.hasOption(IGNOREDIR[SHORT])) {
366 setNoDirs(Arrays.asList(cmd.getOptionValues(IGNOREDIR[SHORT])));
367 }
368
369 if (cmd.hasOption(DICT[SHORT]))
370 setDictionaries(Arrays.asList(cmd.getOptionValues(DICT[SHORT])));
371
372 if (cmd.hasOption(RPTSTYLE[SHORT]))
373 setRptStyle(cmd.getOptionValue(RPTSTYLE[SHORT]));
374
375 if (cmd.hasOption(REPORT[SHORT]))
376 setRptFile(new File(cmd.getOptionValue(REPORT[SHORT])));
377
378 if (cmd.hasOption(PARTIAL[SHORT]))
379 setForcePartial(true);
380 }
381 }
382
383 /***
384 * Set aliasing flag.
385 * @param a 'false' if aliasing should be turned off,
386 * 'true' otherwise
387 */
388 public void setAlias(boolean a) {
389 this.alias = a;
390 }
391
392
393
394
395
396
397
398 /***
399 * Set the dictionary file names passed into VTool .
400 * @param d a List object of dictionary files
401 * @throws ApplicationException
402 */
403 public void setDictionaries(List d) throws ApplicationException {
404 URL url = null;
405 this.dictionaries.clear();
406 List list = new ArrayList(d);
407
408 while(list.remove("") == true);
409 for(Iterator i=list.iterator(); i.hasNext();) {
410 try {
411 url = Utility.toURL(i.next().toString());
412 url.openStream().close();
413 this.dictionaries.add(url);
414 } catch(IOException eX) {
415 throw new ApplicationException(eX.getMessage());
416 }
417 }
418
419 userSpecifiedDictionaries = list;
420 }
421
422 /***
423 * Set the flag that determines whether to follow pointers found in
424 * a label.
425 * @param f 'true' to follow, 'false' otherwise
426 */
427 public void setFollowPtrs(boolean f) {
428 this.followPtrs = f;
429 }
430
431 /***
432 * Set the flag that determines whether to validate standalone label.
433 * fragments
434 * @param f 'true' to enable, 'false' otherwise
435 */
436 public void setForcePartial(boolean f) {
437 this.force = f;
438 }
439
440 /***
441 * Set the paths to search for files referenced by pointers.
442 * <p>
443 * Default is to always look first in the same directory
444 * as the label, then search specified directories.
445 * @param i List of paths
446 */
447 public void setIncludePaths(List i) {
448 this.includePaths = new ArrayList(i);
449 while(this.includePaths.remove(""));
450 }
451
452 /***
453 * Set the flag to ignore specified directories.
454 * @param d a text file containing a list of directories and/or directory
455 * patterns to ignore during validation. The names must be listed one
456 * name per line.
457 */
458 public void setNoDirs(List d) {
459 this.noDirs = new ArrayList(d);
460 while(this.noDirs.remove(""));
461 }
462
463 /***
464 * Set the flag to ignore specified files.
465 * @param f a list of files/file patterns to ignore during validation.
466 */
467 public void setNoFiles(List f) {
468 this.noFiles = new ArrayList(f);
469 while(this.noFiles.remove(""));
470 }
471
472 /***
473 * Set the file name for the machine-readable log.
474 * @param f file name of the log
475 */
476 public void setLogFile(File f) {
477 this.logFile = f;
478 }
479
480 /***
481 * Set the flag to write the log to standard out.
482 * @param l
483 */
484 public void setShowLog(boolean l) {
485 this.showLog = l;
486 }
487
488 /***
489 * Set the file for the human-readable report.
490 * @param f file name
491 */
492 public void setRptFile(File f) {
493 this.rptFile = f;
494 }
495
496 /***
497 * Set the output style for the report.
498 * @param style 'sum' for a summary report, 'min' for a minimal report,
499 * and 'full' for a full report
500 * @throws ApplicationException
501 */
502 public void setRptStyle(String style) throws ApplicationException {
503 if ( (style.equalsIgnoreCase("sum") == false) &&
504 (style.equalsIgnoreCase("min") == false) &&
505 (style.equalsIgnoreCase("full") == false) ) {
506 throw new ApplicationException(
507 "Invalid value entered for 's' flag. Value can only "
508 + "be either 'full', 'sum', or 'min'");
509 }
510 this.rptStyle = style;
511 }
512
513 /***
514 * Set the progress reporting flag.
515 * @param p
516 */
517 public void setProgress(boolean p) {
518 this.progress = p;
519 }
520
521 /***
522 * Set the patterns flag.
523 * @param e a List of patterns to be matched when searching for files to
524 * validate in a directory
525 */
526 public void setRegexp(List e) {
527 this.regexp = new ArrayList(e);
528 while(this.regexp.remove(""));
529 }
530
531 /***
532 * Set the recursive flag.
533 * @param r 'true' to recursively traverse down a directory and all its
534 * sub-directories, 'false' otherwise
535 */
536 public void setRecursive(boolean r) {
537 this.recursive = r;
538 }
539
540 /***
541 * Set the targets flag.
542 * @param t a List of files, URLs, and/or directories to be validated
543 */
544 public void setTargets(List t) {
545 this.targets = new ArrayList(t);
546 while(this.targets.remove(""));
547 }
548
549 /***
550 * Set the verbosity level and above to include in the reporting.
551 * @param v '1' for info, '2' for warnings, and '3' for errors
552 * @throws ApplicationException
553 */
554 public void setVerbose(short v) throws ApplicationException {
555 if (v < 1 || v > 3) {
556 throw new ApplicationException(
557 "Invalid value entered for 'v' flag. "
558 + "Valid values can only be 1, 2, or 3");
559 }
560 verbose = v;
561
562 if (verbose == 1)
563 severity = new String("Info");
564 else if (verbose == 2)
565 severity = new String("Warning");
566 else if (verbose == 3)
567 severity = new String("Error");
568 }
569
570 /***
571 * Reads a configuration file to set the default behaviors for VTool.
572 * <p>
573 * Flags set on the command-line will override flags set in the
574 * configuration file
575 *
576 * @param file a file containing keyword/value statements
577 * @throws ApplicationException
578 */
579 public void readConfigFile(URL file) throws ApplicationException {
580 Configuration config = null;
581 AbstractConfiguration.setDelimiter(',');
582
583 try {
584 config = new PropertiesConfiguration(file);
585 } catch(ConfigurationException eX) {
586 throw new ApplicationException(eX.getMessage());
587 }
588
589 try {
590 if (config.isEmpty())
591 throw new ApplicationException("Configuration file is empty: "
592 + file.toString());
593 if (config.containsKey(ALIASKEY))
594 setAlias(config.getBoolean(ALIASKEY));
595
596
597
598 if (config.containsKey(DICTKEY))
599 setDictionaries(config.getList(DICTKEY));
600 if (config.containsKey(FORCEKEY))
601 setForcePartial(config.getBoolean(FORCEKEY));
602 if (config.containsKey(FOLLOWKEY))
603 setFollowPtrs(config.getBoolean(FOLLOWKEY));
604 if (config.containsKey(IGNOREDIRKEY)) {
605 setNoDirs(config.getList(IGNOREDIRKEY));
606
607 for(int i=0; i < this.noDirs.size(); i++) {
608 this.noDirs.set(i,
609 this.noDirs.get(i).toString().replace('"', ' ').trim());
610 }
611 }
612 if (config.containsKey(IGNOREFILEKEY)) {
613 setNoFiles(config.getList(IGNOREFILEKEY));
614
615 for(int i=0; i < noFiles.size(); i++) {
616 this.noFiles.set(i,
617 this.noFiles.get(i).toString().replace('"', ' ').trim());
618 }
619 }
620 if (config.containsKey(INCLUDESKEY))
621 setIncludePaths(config.getList(INCLUDESKEY));
622 if (config.containsKey(LOGKEY)) {
623 String logValue = new String(config.getProperty(LOGKEY).toString());
624
625 if(logValue.equalsIgnoreCase("true")
626 || logValue.equalsIgnoreCase("false")) {
627 setShowLog(Boolean.valueOf(logValue).booleanValue());
628 }
629 else
630 setLogFile(new File(logValue));
631 }
632 if (config.containsKey(PROGRESSKEY))
633 setProgress(config.getBoolean(PROGRESSKEY));
634 if (config.containsKey(RECURSEKEY))
635 setRecursive(config.getBoolean(RECURSEKEY));
636 if (config.containsKey(REGEXPKEY)) {
637 setRegexp(config.getList(REGEXPKEY));
638
639 for(int i=0; i < this.regexp.size(); i++) {
640 this.regexp.set(i,
641 this.regexp.get(i).toString().replace('"', ' ').trim());
642 }
643 }
644 if (config.containsKey(REPORTKEY))
645 setRptFile(new File(config.getString(REPORTKEY)));
646 if (config.containsKey(STYLEKEY))
647 setRptStyle(config.getString(STYLEKEY));
648 if (config.containsKey(TARGETKEY))
649 setTargets(config.getList(TARGETKEY));
650 if (config.containsKey(VERBOSEKEY))
651 setVerbose(config.getShort(VERBOSEKEY));
652 } catch(ConversionException eX) {
653 throw new ApplicationException(eX.getMessage());
654 }
655 }
656
657 /***
658 * Routine to store a message into the logger.
659 * @param level The severity level of the message.
660 * @param msg The message to log.
661 */
662 public void logMessage(Level level, String msg) {
663 logMessage(level, msg, null);
664 }
665
666 /***
667 * Routine to store a message into the logger.
668 * @param level The severity level of the message.
669 * @param msg The message to log.
670 * @param file The file name associated with the message being logged.
671 */
672 public void logMessage(Level level, String msg, String file) {
673 log.log(new ToolsLogRecord(level, msg, file));
674 }
675
676 /***
677 * Logs report header information such as version of the tool,
678 * execution time, and flag settings.
679 * @throws SystemException
680 * @throws IOException
681 *
682 */
683 public void logRptHeader() throws SystemException, IOException {
684 ToolsTime time = new ToolsTime();
685
686
687 URL propertyFile = this.getClass().getResource(PROPERTYFILE);
688 Properties p = new Properties();
689 InputStream in = null;
690 try {
691 in = propertyFile.openStream();
692 p.load(in);
693 } finally {
694 in.close();
695 }
696 String version = p.getProperty(PROPERTYVERSION).replaceFirst("Version", "").trim();
697 logMessage(ToolsLevel.CONFIGURATION, "Version " + version);
698 try {
699 logMessage(ToolsLevel.CONFIGURATION,
700 "Date "
701 + time.getTime(new SimpleDateFormat("EEE, MMM dd yyyy 'at' hh:mm:ss a")));
702 } catch(IllegalArgumentException eX) {
703 throw new SystemException(eX.getMessage());
704 }
705
706 String validationLevel = null;
707 if(dictionaries.isEmpty())
708 validationLevel = "Syntactic only";
709 else
710 validationLevel = "Syntactic and Semantic";
711 logMessage(ToolsLevel.CONFIGURATION, "Level of Validation " + validationLevel);
712
713 if(targets != null) {
714 logMessage(ToolsLevel.PARAMETER,
715 "Target(s) " + targets);
716 }
717 if (userSpecifiedDictionaries != null) {
718 logMessage(ToolsLevel.PARAMETER,
719 "Dictionary File(s) " + userSpecifiedDictionaries);
720 }
721 logMessage(ToolsLevel.PARAMETER,
722 "Aliasing " + alias);
723 logMessage(ToolsLevel.PARAMETER,
724 "Directory Recursion " + recursive);
725 logMessage(ToolsLevel.PARAMETER,
726 "Follow Pointers " + followPtrs);
727 logMessage(ToolsLevel.PARAMETER,
728 "Validate Standalone Fragments " + force);
729
730 if (logFile != null) {
731 logMessage(ToolsLevel.PARAMETER,
732 "Log File " + logFile);
733 }
734 if (rptFile != null) {
735 logMessage(ToolsLevel.PARAMETER,
736 "Report File " + rptFile);
737 }
738 if (rptStyle != null) {
739 logMessage(ToolsLevel.PARAMETER,
740 "Report Style " + rptStyle);
741 }
742 if (severity != null) {
743 logMessage(ToolsLevel.PARAMETER,
744 "Severity Level " + severity);
745 }
746 if (includePaths != null) {
747 logMessage(ToolsLevel.PARAMETER,
748 "Include Path(s) " + includePaths);
749 }
750 if (config != null) {
751 logMessage(ToolsLevel.PARAMETER,
752 "Configuration File " + config);
753 }
754 if (regexp != null) {
755 logMessage(ToolsLevel.PARAMETER,
756 "Files Patterns " + regexp);
757 }
758 if (noDirs != null) {
759 logMessage(ToolsLevel.PARAMETER,
760 "Excluded Directories " + noDirs);
761 }
762 if (noFiles != null) {
763 logMessage(ToolsLevel.PARAMETER,
764 "Excluded Files " + noFiles);
765 }
766 logMessage(ToolsLevel.PARAMETER,
767 "Progress Reporting " + progress);
768 }
769
770 /***
771 * Configures the logger appropriately.
772 * <p>
773 * If a log file was specified on the command-line, the log
774 * will be written to that file. If the log flag was
775 * specified with no file spec, then the log will be written
776 * to standard out. Otherwise, the log will be written to
777 * memory (ByteArrayOutputStream).
778 * @throws SystemException
779 * @throws ApplicationException
780 *
781 */
782 public void setupLogger()
783 throws ApplicationException, SystemException {
784
785 Logger logger = Logger.getLogger("");
786 logger.setLevel(Level.ALL);
787
788 Handler []handler = logger.getHandlers();
789 for(int i = 0; i < logger.getHandlers().length; i++)
790 logger.removeHandler(handler[i]);
791
792 HandlerFactory hf = HandlerFactory.getInstance();
793 Handler reportHandler = null;
794 try {
795
796 if(logFile != null && !showLog) {
797 logger.addHandler(new ToolsFileHandler(logFile.toString(), new ToolsLogFormatter()));
798 }
799 else if(showLog && logFile == null) {
800 logger.addHandler(new ToolsStreamHandler(System.out, new ToolsLogFormatter()));
801 }
802 reportHandler = hf.newInstance(rptFile, rptStyle, severity);
803 logger.addHandler(reportHandler);
804 } catch (SecurityException s) {
805 throw new SystemException(s.getMessage());
806 } catch (IOException io) {
807 throw new ApplicationException(io.getMessage());
808 } catch (UnknownHandlerConfigurationException uh) {
809 throw new ApplicationException(uh.getMessage());
810 }
811 }
812
813 /***
814 * Parse the dictionary files.
815 *
816 * @param dictionary a list of dictionary URLs
817 * @return a Dictionary object that includes all the dictionary
818 * information from all the dictionary files passed in.
819 * @throws ApplicationException
820 * @throws gov.nasa.pds.tools.label.parser.ParseException
821 * @throws UnknownLabelStatusException
822 */
823 public Dictionary readDictionaries(List dictionary) throws
824 ApplicationException {
825 Dictionary dict = null;
826 URL url = null;
827 Iterator i = dictionary.iterator();
828
829 try {
830 url = (URL) i.next();
831 dict = DictionaryParser.parse(url, alias);
832 logMessage(ToolsLevel.CONFIGURATION, "Dictionary version \n"
833 + dict.getInformation(), url.toString());
834
835 if(dict.getStatus().equals(FAIL)) {
836 throw new gov.nasa.pds.tools.label.parser.ParseException(
837 "Dictionary parsing failed: " + url.toString());
838 }
839
840 while (i.hasNext()) {
841 url = (URL) i.next();
842 Dictionary mergeDict = DictionaryParser.parse(url, alias);
843 dict.merge(mergeDict, true);
844 logMessage(ToolsLevel.CONFIGURATION, "Dictionary version \n"
845 + mergeDict.getInformation(), url.toString());
846
847 if(mergeDict.getStatus().equals(FAIL)) {
848 throw new gov.nasa.pds.tools.label.parser.ParseException(
849 "Dictionary parsing failed: " + url.toString());
850 }
851 }
852 } catch (IOException iEx) {
853 throw new ApplicationException(iEx.getMessage());
854 } catch (gov.nasa.pds.tools.label.parser.ParseException pe) {
855 logMessage(ToolsLevel.NOTIFICATION, "FAIL", url.toString());
856 dictionaryPassed = false;
857 }
858 return dict;
859 }
860
861 /***
862 * Sets up the include paths and flag to follow pointers in
863 * the parser.
864 * @param parser The label parser to set properties for
865 * @throws ApplicationException
866 */
867 private void setParserProps(LabelParser parser)
868 throws ApplicationException {
869 URL path = null;
870 if (includePaths != null) {
871 for( Iterator i = includePaths.iterator(); i.hasNext(); ) {
872 try {
873 path = Utility.toURL(i.next().toString());
874 path.openStream().close();
875 } catch (Exception eX) {
876 throw new ApplicationException(eX.getMessage());
877 }
878 parser.addIncludePath(path);
879 }
880 }
881
882 if (followPtrs == false)
883 parser.getProperties().setProperty("parser.pointers", "false");
884 }
885
886 /***
887 * Processes a target.
888 *
889 * @param target The file or URL to process
890 * @param getSubDirs 'True' to look for sub-directories, 'false' otherwise
891 * @return A FileList object containing the sub-directories and files
892 * found in the target, if any
893 * @throws ApplicationException
894 * @throws SystemException
895 */
896 public FileList processTarget(String target, boolean getSubDirs)
897 throws ApplicationException, SystemException {
898 FileListGenerator fileGen = new FileListGenerator();
899 FileList list = new FileList();
900 fileGen.setFilters(regexp, noFiles, noDirs);
901
902 try {
903 list = fileGen.visitTarget(target, getSubDirs);
904 }catch(IOException io) {
905 throw new ApplicationException(io.getMessage());
906 }catch (Exception eX) {
907 throw new SystemException(eX.getMessage());
908 }
909 return list;
910 }
911
912 /***
913 * Validate labels, performing only syntactic validation.
914 *
915 * @param targets
916 * @return A ValidationRecord containing the results of the validation
917 * run.
918 * @throws MalformedURLException
919 * @throws ApplicationException
920 * @throws SystemException
921 * @throws UnknownLabelStatusException
922 */
923 public ValidationRecord validateLabels(List targets)
924 throws MalformedURLException, ApplicationException,
925 SystemException, UnknownLabelStatusException {
926 return validateLabels(targets, null);
927 }
928
929 /***
930 * Validate labels, performing both syntactic and semantic validation.
931 * If the target is a directory, this method will validate all labels
932 * found within the directory and its sub-directories. If recursion
933 * is turned OFF, then sub-directories will not be looked into.
934 *
935 * @param targets a list of files, directories, and/or URLs.
936 * @param dict the dictionary file.
937 * @return A ValidationRecord containing the results of the validation.
938 * @throws SystemException
939 * @throws ApplicationException
940 * @throws MalformedURLException
941 * @throws UnknownLabelStatusException
942 */
943 public ValidationRecord validateLabels(List targets, Dictionary dict)
944 throws ApplicationException, SystemException,
945 MalformedURLException, UnknownLabelStatusException {
946
947 ValidationRecord record = new ValidationRecord();
948
949 for (Iterator i1 = targets.iterator(); i1.hasNext();) {
950 FileList fileList = new FileList();
951 fileList = processTarget(i1.next().toString(), recursive);
952 for (Iterator i2 = fileList.getFiles().iterator(); i2.hasNext();) {
953 URL target = Utility.toURL(i2.next().toString());
954 record.add(validateLabel(target, dict));
955 }
956 if (!fileList.getDirs().isEmpty())
957 record.add(validateLabels(fileList.getDirs(), dict));
958 }
959
960 return record;
961 }
962
963 /***
964 * Validate a label file.
965 *
966 * @param url The URL of the file to be validated
967 * @param dict a Dictionary object needed for semantic validation. If null,
968 * only syntactic validation will be performed.
969 * @return A ValidationRecord object containing the results of the validation.
970 * @throws ApplicationException
971 * @throws SystemException
972 * @throws UnknownLabelStatusException
973 */
974 public ValidationRecord validateLabel(URL url, Dictionary dict)
975 throws ApplicationException, SystemException,
976 UnknownLabelStatusException {
977 LabelParserFactory factory = LabelParserFactory.getInstance();
978 LabelParser parser = factory.newLabelParser();
979 Label label = null;
980 setParserProps(parser);
981
982 if (progress)
983 showProgress(url);
984
985 ValidatorFactory vf = ValidatorFactory.getInstance();
986 Validator validator = vf.newInstance(parser, url, force);
987 try {
988 if(dict == null)
989 label = validator.validate(url);
990 else
991 label = validator.validate(url, dict);
992 } catch (gov.nasa.pds.tools.label.parser.ParseException pEx) {
993 label = new Label();
994 label.setStatus(Label.SKIP);
995 label.incrementWarnings();
996 } catch (ValidatorException ve) {
997 label = new Label();
998 label.setStatus(Label.SKIP);
999 label.incrementWarnings();
1000 }
1001 return new ValidationRecord(label.getStatus(), label.getNumWarnings(), label.getNumErrors());
1002 }
1003
1004 /***
1005 * Prints out the current directory being validated and
1006 * represents each file being validated by an asterisk.
1007 * This is used when progress reporting is enabled.
1008 *
1009 * @param file The URL being validated
1010 * @throws SystemException
1011 */
1012 public void showProgress(URL file) throws SystemException {
1013 URL dir = null;
1014 try {
1015 dir = new URL(file.toString().
1016 substring(0, file.toString().lastIndexOf("/")));
1017 } catch (MalformedURLException eX) {
1018 throw new SystemException(eX.getMessage());
1019 }
1020
1021 if (dir.toString().equals(currDir)) {
1022 System.err.print(FILE_REP);
1023 }
1024 else {
1025 currDir = new String(dir.toString());
1026 System.err.println("\nValidating file(s) in: " + currDir);
1027 System.err.print(FILE_REP);
1028 }
1029 }
1030
1031 /***
1032 * Closes the handlers that were set for the logger.
1033 *
1034 */
1035 public void closehandle() {
1036 Logger logger = Logger.getLogger("");
1037 Handler handlers[] = logger.getHandlers();
1038 for(int i=0; i<handlers.length; i++) {
1039 handlers[i].close();
1040 }
1041 }
1042
1043 /***
1044 * Implementation to perform automated PDS validation.
1045 *
1046 * <p>
1047 * The main calls the following methods (in this order):
1048 * <p>
1049 * To setup the flags and parse the command-line options:
1050 * <ul>
1051 * <li>parseLine</li>
1052 * <li>queryCmdLine</li>
1053 * </ul>
1054 *
1055 * <p>
1056 * To setup the logger and log the report header information:
1057 * <ul>
1058 * <li>setupLogger</li>
1059 * <li>logRptHeader</li>
1060 * </ul>
1061 *
1062 * <p>
1063 * To perform validation:
1064 * <ul>
1065 * <li>readDictionaries (if the PSDD was passed in)</li>
1066 * <li>validateLabels</li>
1067 * </ul>
1068 *
1069 * <p>
1070 * To create the final report:
1071 * <ul>
1072 * <li>closehandle (to flush the buffers)</li>
1073 * <li>doReporting</li>
1074 * </ul>
1075 *
1076 * <p>
1077 * Reporting is not generated if the log flag was specified with
1078 * no file spec and the report file flag was not specified
1079 *
1080 * <p>
1081 * VTool returns an appropriate exit status based on validation results.
1082 *
1083 * @param argv Arguments passed on the command-line
1084 */
1085 public static void main(String[] argv) {
1086 VTool vtool = new VTool();
1087 ExitStatus status = new ExitStatus();
1088
1089 if (argv.length == 0) {
1090 System.out.println("\nType 'VTool -h' for usage");
1091 System.exit(SUCCESS);
1092 }
1093
1094 try {
1095
1096 CommandLine commandLine = vtool.parseLine(argv);
1097
1098 vtool.queryCmdLine(commandLine);
1099
1100 vtool.setupLogger();
1101
1102 vtool.logRptHeader();
1103
1104 ValidationRecord record = new ValidationRecord();
1105 if ( vtool.dictionaries.isEmpty() ) {
1106 record = vtool.validateLabels(vtool.targets);
1107 status.setStatus(record);
1108 }
1109 else {
1110
1111
1112 Dictionary dictionary = vtool.readDictionaries(vtool.dictionaries);
1113 if(vtool.dictionaryPassed) {
1114 record = vtool.validateLabels(vtool.targets, dictionary);
1115 status.setStatus(record);
1116 }
1117 else
1118 status.setStatus(APPLICATION_ERROR);
1119 }
1120 vtool.closehandle();
1121
1122
1123 if(vtool.progress == true)
1124 System.err.println();
1125
1126 } catch(ApplicationException app) {
1127 System.err.println(app.getMessage());
1128 status.setStatus(APPLICATION_ERROR);
1129 } catch(Exception e) {
1130 System.err.println(e.getMessage());
1131 status.setStatus(SYSTEM_ERROR);
1132 } finally {
1133 System.exit(status.getStatus(vtool.severity));
1134 }
1135 }
1136 }