Merge pull request #246 from Claudenw/create_archive_processor

RAT-372: Create archive processor
diff --git a/apache-rat-core/pom.xml b/apache-rat-core/pom.xml
index a4ad439..6c48473 100644
--- a/apache-rat-core/pom.xml
+++ b/apache-rat-core/pom.xml
@@ -97,6 +97,10 @@
       </exclusions>
     </dependency>
     <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-text</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
     </dependency>
diff --git a/apache-rat-core/src/main/java/org/apache/rat/Defaults.java b/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
index be0456b..05c4ed8 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
@@ -44,7 +44,6 @@
 
 /**
  * A class that provides the standard system defaults for the ReportConfiguration.
- *
  * Properties in this class may be overridden or added to by configuration options in the various UIs.
  * See the specific UI for details.
  */
@@ -63,14 +62,21 @@
      */
     public static final String UNAPPROVED_LICENSES_STYLESHEET = "org/apache/rat/unapproved-licenses.xsl";
 
+     public static final FilenameFilter FILES_TO_IGNORE = FalseFileFilter.FALSE;
+
+    public static final IOFileFilter DIRECTORIES_TO_IGNORE = NameBasedHiddenFileFilter.HIDDEN;
+
+    public static final ReportConfiguration.Processing ARCHIVE_PROCESSING = ReportConfiguration.Processing.NOTIFICATION;
+
+    public static final LicenseFilter LIST_FAMILIES = LicenseFilter.NONE;
+
+    public static final LicenseFilter LIST_LICENSES = LicenseFilter.NONE;
+
     private final LicenseSetFactory setFactory;
 
-    private static final FilenameFilter FILES_TO_IGNORE = FalseFileFilter.FALSE;
-
-    private static final IOFileFilter DIRECTORIES_TO_IGNORE = NameBasedHiddenFileFilter.HIDDEN;
 
     /**
-     * Initialize the system configuration reader..
+     * Initialize the system configuration reader.
      */
     public static void init() {
         Format fmt = Format.fromURL(DEFAULT_CONFIG_URL);
@@ -166,14 +172,6 @@
     public SortedSet<String> getLicenseIds(final LicenseFilter filter) {
         return setFactory.getLicenseFamilyIds(filter);
     }
-
-    public static FilenameFilter getFilesToIgnore() {
-        return FILES_TO_IGNORE;
-    }
-
-    public static IOFileFilter getDirectoriesToIgnore() {
-        return DIRECTORIES_TO_IGNORE;
-    }
     
     /**
      * The Defaults builder.
diff --git a/apache-rat-core/src/main/java/org/apache/rat/Report.java b/apache-rat-core/src/main/java/org/apache/rat/Report.java
index 6a8ea88..fc0e187 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/Report.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/Report.java
@@ -22,7 +22,7 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.io.Serializable;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
@@ -31,11 +31,15 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 import java.util.regex.PatternSyntaxException;
 import java.util.stream.Collectors;
 
 import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Converter;
 import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Option;
@@ -43,6 +47,7 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FalseFileFilter;
 import org.apache.commons.io.filefilter.NameFileFilter;
 import org.apache.commons.io.filefilter.NotFileFilter;
 import org.apache.commons.io.filefilter.OrFileFilter;
@@ -50,7 +55,10 @@
 import org.apache.commons.io.filefilter.WildcardFileFilter;
 import org.apache.commons.io.function.IOSupplier;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.WordUtils;
+import org.apache.rat.api.Document;
 import org.apache.rat.config.AddLicenseHeaders;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
 import org.apache.rat.report.IReportable;
 import org.apache.rat.utils.DefaultLog;
@@ -59,74 +67,198 @@
 import org.apache.rat.walker.ArchiveWalker;
 import org.apache.rat.walker.DirectoryWalker;
 
+import static java.lang.String.format;
+
 /**
  * The CLI based configuration object for report generation.
  */
 public class Report {
+
+    private static final String[] NOTES = {
+            "Rat highlights possible issues.",
+            "Rat reports require interpretation.",
+            "Rat often requires some tuning before it runs well against a project.",
+            "Rat relies on heuristics: it may miss issues"
+    };
+
+    private enum StyleSheets { PLAIN("plain-rat", "The default style"),
+        MISSING_HEADERS("missing-headers", "Produces a report of files that are missing headers"),
+        UNAPPROVED_LICENSES("unapproved-licenses", "Produces a report of the files with unapproved licenses");
+        private final String arg;
+        private final String desc;
+        StyleSheets(String arg, String description) {
+            this.arg = arg;
+            this.desc = description;
+        }
+
+        public String arg() {
+            return arg;
+        }
+
+        public String desc() {
+            return desc;
+        }
+    }
+
+    private static final Map<String, Supplier<String>> ARGUMENT_TYPES;
+
+    static {
+        ARGUMENT_TYPES = new TreeMap<>();
+        ARGUMENT_TYPES.put("FileOrURI", () -> "A file name or URI");
+        ARGUMENT_TYPES.put("DirOrArchive", () -> "A directory or archive file to scan");
+        ARGUMENT_TYPES.put("Expression", () -> "A wildcard file matching pattern. example: *-test-*.txt");
+        ARGUMENT_TYPES.put("LicenseFilter", () -> format("A defined filter for the licenses to include.  Valid values: %s.",
+                asString(LicenseFilter.values())));
+        ARGUMENT_TYPES.put("LogLevel", () -> format("The log level to use.  Valid values %s.", asString(Log.Level.values())));
+        ARGUMENT_TYPES.put("ProcessingType", () -> format("Specifies how to process file types.  Valid values are: %s",
+                Arrays.stream(ReportConfiguration.Processing.values())
+                        .map(v -> format("\t%s: %s", v.name(), v.desc()))
+                        .collect(Collectors.joining(""))));
+        ARGUMENT_TYPES.put("StyleSheet", () -> format("Either an external xsl file or maybe one of the internal named sheets.  Internal sheets are: %s.",
+                Arrays.stream(StyleSheets.values())
+                        .map(v -> format("\t%s: %s", v.arg(), v.desc()))
+                        .collect(Collectors.joining(""))));
+    }
+
+    // RAT-85/RAT-203: Deprecated! added only for convenience and for backwards
+    // compatibility
     /**
      * Adds license headers to files missing headers.
      */
-    private static final String ADD = "A";
-    private static final String ADD_OLD = "a";
+    private static final OptionGroup ADD = new OptionGroup()
+            .addOption(new Option("a", false,
+                    "(deprecated) Add the default license header to any file with an unknown license.  Use '-A' or ---addLicense instead."))
+            .addOption(new Option("A", "addLicense", false,
+                    "Add the default license header to any file with an unknown license that is not in the exclusion list. "
+                            + "By default new files will be created with the license header, "
+                            + "to force the modification of existing files use the --force option.")
+            );
+
+    /**
+     * Defines the output for the file.
+     * @since 0.16.0
+     */
+    static final Option OUT = Option.builder().option("o").longOpt("out").hasArg()
+            .desc("Define the output file where to write a report to (default is System.out).")
+            .converter(Converter.FILE).build();
+
+    static final Option DIR = Option.builder().option("d").longOpt("dir").hasArg()
+            .desc("(deprecated, use '--') Used to indicate source when using --exclude.").argName("DirOrArchive").build();
+
     /**
      * Forces changes to be written to new files.
      */
-    private static final String FORCE = "f";
+    static final Option FORCE = new Option("f", "force", false,
+            "Forces any changes in files to be written directly to the source files (i.e. new files are not created).");
     /**
      * Defines the copyright header to add to the file.
      */
-    private static final String COPYRIGHT = "c";
+    static final Option COPYRIGHT = Option.builder().option("c").longOpt("copyright").hasArg()
+            .desc("The copyright message to use in the license headers, usually in the form of \"Copyright 2008 Foo\"")
+            .build();
     /**
      * Name of File to exclude from report consideration.
      */
-    private static final String EXCLUDE_CLI = "e";
+    static final Option EXCLUDE_CLI = Option.builder("e").longOpt("exclude").hasArgs().argName("Expression")
+            .desc("Excludes files matching wildcard <expression>. May be followed by multiple arguments. "
+                    + "Note that '--' or a following option is required when using this parameter.")
+            .build();
     /**
      * Name of file that contains a list of files to exclude from report
      * consideration.
      */
-    private static final String EXCLUDE_FILE_CLI = "E";
+    static final Option EXCLUDE_FILE_CLI = Option.builder("E").longOpt("exclude-file")
+            .argName("FileOrURI")
+            .hasArg().desc("Excludes files matching regular expression in the input file.")
+            .build();
+
     /**
      * The stylesheet to use to style the XML output.
      */
-    private static final String STYLESHEET_CLI = "s";
+    static final Option STYLESHEET_CLI = Option.builder("s").longOpt("stylesheet").hasArg().argName("StyleSheet")
+            .desc("XSLT stylesheet to use when creating the report.  Not compatible with -x.  " +
+                    "Either an external xsl file may be specified or one of the internal named sheets: plain-rat (default), missing-headers, or unapproved-licenses")
+            .build();
     /**
      * Produce help
      */
-    private static final String HELP = "h";
+    static final Option HELP = new Option("h", "help", false, "Print help for the RAT command line interface and exit.");
     /**
      * Flag to identify a file with license definitions.
+     * @since 0.16.0
      */
-    private static final String LICENSES = "licenses";
+    static final Option LICENSES = Option.builder().longOpt("licenses").hasArgs().argName("FileOrURI")
+            .desc("File names or URLs for license definitions")
+            .build();
     /**
      * Do not use the default files.
+     * @since 0.16.0
      */
-    private static final String NO_DEFAULTS = "no-default-licenses";
+    static final Option NO_DEFAULTS = new Option(null, "no-default-licenses", false, "Ignore default configuration. By default all approved default licenses are used");
+
     /**
      * Scan hidden directories.
      */
-    private static final String SCAN_HIDDEN_DIRECTORIES = "scan-hidden-directories";
+    static final Option SCAN_HIDDEN_DIRECTORIES = new Option(null, "scan-hidden-directories", false, "Scan hidden directories");
+
     /**
      * List the licenses that were used for the run.
+     * @since 0.16.0
      */
-    private static final String LIST_LICENSES = "list-licenses";
+    static final Option LIST_LICENSES = Option.builder().longOpt("list-licenses").hasArg().argName("LicenseFilter")
+            .desc("List the defined licenses (default is NONE). Valid options are: " + asString(LicenseFilter.values()))
+            .converter(s -> LicenseFilter.valueOf(s.toUpperCase()))
+            .build();
 
     /**
      * List the all families for the run.
+     * @since 0.16.0
      */
-    private static final String LIST_FAMILIES = "list-families";
+    static final Option LIST_FAMILIES = Option.builder().longOpt("list-families").hasArg().argName("LicenseFilter")
+            .desc("List the defined license families (default is NONE). Valid options are: " + asString(LicenseFilter.values()))
+            .converter(s -> LicenseFilter.valueOf(s.toUpperCase()))
+            .build();
 
-    private static final String LOG_LEVEL = "log-level";
-    private static final String DRY_RUN = "dry-run";
+    /**
+     * Specify the log level for output
+     * @since 0.16.0
+     */
+    static final Option LOG_LEVEL = Option.builder().longOpt("log-level")
+            .hasArgs().argName("LogLevel")
+            .desc("sets the log level.")
+            .converter(s -> Log.Level.valueOf(s.toUpperCase()))
+            .build();
+
+    /**
+     * Do not update files.
+     * @since 0.16.0
+     */
+    static final Option DRY_RUN = Option.builder().longOpt("dry-run")
+            .desc("If set do not update the files but generate the reports.")
+            .build();
     /**
      * Set unstyled XML output
      */
-    private static final String XML = "x";
+    static final Option XML = new Option("x", "xml", false, "Output the report in raw XML format.  Not compatible with -s");
 
+    /**
+     * Specify the processing of ARCHIVE files.
+     * @since 0.17.0
+     */
+    static final Option ARCHIVE = Option.builder().longOpt("archive").hasArg().argName("ProcessingType")
+            .desc(format("Specifies the level of detail in ARCHIVE reporting. (default is %s)",
+                    ReportConfiguration.Processing.NOTIFICATION))
+            .converter(s -> ReportConfiguration.Processing.valueOf(s.toUpperCase()))
+            .build();
+
+    private static String asString(Object[] args) {
+        return Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", "));
+    }
 
     /**
      * Processes the command line and builds a configuration and executes the
      * report.
-     * 
+     *
      * @param args the arguments.
      * @throws Exception on error.
      */
@@ -140,7 +272,8 @@
 
     /**
      * Parses the standard options to create a ReportConfiguraton.
-     * @param args the arguments to parse
+     *
+     * @param args    the arguments to parse
      * @param helpCmd the help command to run when necessary.
      * @return a ReportConfiguration or null if Help was printed.
      * @throws IOException on error.
@@ -148,12 +281,13 @@
     public static ReportConfiguration parseCommands(String[] args, Consumer<Options> helpCmd) throws IOException {
         return parseCommands(args, helpCmd, false);
     }
-    
+
     /**
      * Parses the standard options to create a ReportConfiguraton.
-     * @param args the arguments to parse
+     *
+     * @param args    the arguments to parse
      * @param helpCmd the help command to run when necessary.
-     * @param noArgs If true then the commands do not need extra arguments
+     * @param noArgs  If true then the commands do not need extra arguments
      * @return a ReportConfiguration or null if Help was printed.
      * @throws IOException on error.
      */
@@ -167,16 +301,14 @@
             DefaultLog.INSTANCE.error("Please use the \"--help\" option to see a list of valid commands and options");
             System.exit(1);
             return null; // dummy return (won't be reached) to avoid Eclipse complaint about possible NPE
-                    // for "cl"
+            // for "cl"
         }
 
         if (cl.hasOption(LOG_LEVEL)) {
             try {
-                Log.Level level = Log.Level.valueOf(cl.getOptionValue(LOG_LEVEL).toUpperCase());
-                DefaultLog.INSTANCE.setLevel(level);
-            } catch (IllegalArgumentException e) {
-                DefaultLog.INSTANCE.warn(String.format("Invalid Log Level (%s) specified.", cl.getOptionValue(LOG_LEVEL)));
-                DefaultLog.INSTANCE.warn(String.format("Log level set at: %s", DefaultLog.INSTANCE.getLevel()));
+                DefaultLog.INSTANCE.setLevel(cl.getParsedOptionValue(LOG_LEVEL));
+            } catch (ParseException e) {
+                logParseException(e, LOG_LEVEL, cl, DefaultLog.INSTANCE.getLevel());
             }
         }
         if (cl.hasOption(HELP)) {
@@ -191,34 +323,14 @@
                 return null;
             }
         } else {
-            args = new String[] {null};
+            args = new String[]{null};
         }
-/*            ReportConfiguration configuration = createConfiguration(args[0], cl);
-            configuration.validate(DefaultLog.INSTANCE::error);
+        return createConfiguration(args[0], cl);
+    }
 
-            boolean dryRun = false;
-            
-            if (cl.hasOption(LIST_FAMILIES)) {
-                LicenseFilter f = LicenseFilter.fromText(cl.getOptionValue(LIST_FAMILIES));
-                if (f != LicenseFilter.none) {                        
-                    dryRun = true;
-                    Reporter.listLicenseFamilies(configuration, f);
-                }
-            }
-            if (cl.hasOption(LIST_LICENSES)) {
-                LicenseFilter f = LicenseFilter.fromText(cl.getOptionValue(LIST_LICENSES));
-                if (f != LicenseFilter.none) {                        
-                    dryRun = true;
-                    Reporter.listLicenses(configuration, f);
-                }
-            }
-            
-            if (!dryRun) {
-                new Reporter(configuration).output();
-            }
-        }
-*/        ReportConfiguration configuration = createConfiguration(args[0], cl);
-        return configuration;
+    private static void logParseException(ParseException e, Option opt, CommandLine cl, Object dflt) {
+        DefaultLog.INSTANCE.warn(format("Invalid %s specified: %s ", opt.getOpt(), cl.getOptionValue(opt)));
+        DefaultLog.INSTANCE.warn(format("%s set to: %s", opt.getOpt(), dflt));
     }
 
     static ReportConfiguration createConfiguration(String baseDirectory, CommandLine cl) throws IOException {
@@ -226,24 +338,44 @@
 
         configuration.setDryRun(cl.hasOption(DRY_RUN));
         if (cl.hasOption(LIST_FAMILIES)) {
-           configuration.listFamilies( LicenseFilter.valueOf(cl.getOptionValue(LIST_FAMILIES).toLowerCase()));
+            try {
+                configuration.listFamilies(cl.getParsedOptionValue(LIST_FAMILIES));
+            } catch (ParseException e) {
+                logParseException(e, LIST_FAMILIES, cl, Defaults.LIST_FAMILIES);
+            }
         }
-        
+
         if (cl.hasOption(LIST_LICENSES)) {
-            configuration.listFamilies( LicenseFilter.valueOf(cl.getOptionValue(LIST_LICENSES).toLowerCase()));
+            try {
+                configuration.listFamilies(cl.getParsedOptionValue(LIST_LICENSES));
+            } catch (ParseException e) {
+                logParseException(e, LIST_LICENSES, cl, Defaults.LIST_LICENSES);
+            }
         }
-        
-        if (cl.hasOption('o')) {
-            configuration.setOut(new File(cl.getOptionValue('o')));
+
+        if (cl.hasOption(ARCHIVE)) {
+            try {
+                configuration.setArchiveProcessing(cl.getParsedOptionValue(ARCHIVE));
+            } catch (ParseException e) {
+                logParseException(e, ARCHIVE, cl, Defaults.ARCHIVE_PROCESSING);
+            }
+        }
+
+        if (cl.hasOption(OUT)) {
+            try {
+                configuration.setOut((File) cl.getParsedOptionValue(OUT));
+            } catch (ParseException e) {
+                logParseException(e, OUT, cl, "System.out");
+            }
         }
 
         if (cl.hasOption(SCAN_HIDDEN_DIRECTORIES)) {
-            configuration.setDirectoriesToIgnore(null);
+            configuration.setDirectoriesToIgnore(FalseFileFilter.FALSE);
         }
 
-        if (cl.hasOption('a') || cl.hasOption('A')) {
-            configuration.setAddLicenseHeaders(cl.hasOption('f') ? AddLicenseHeaders.FORCED : AddLicenseHeaders.TRUE);
-            configuration.setCopyrightMessage(cl.getOptionValue("c"));
+        if (ADD.getSelected() != null) {
+            configuration.setAddLicenseHeaders(cl.hasOption(FORCE) ? AddLicenseHeaders.FORCED : AddLicenseHeaders.TRUE);
+            configuration.setCopyrightMessage(cl.getOptionValue(COPYRIGHT));
         }
 
         if (cl.hasOption(EXCLUDE_CLI)) {
@@ -271,14 +403,12 @@
                     DefaultLog.INSTANCE.error("Please specify a single stylesheet");
                     System.exit(1);
                 }
-                IOSupplier<InputStream> ioSupplier = null;
 
                 URL url = Report.class.getClassLoader().getResource(String.format("org/apache/rat/%s.xsl", style[0]));
-                if (url == null) {
-                    ioSupplier = () -> Files.newInputStream(Paths.get(style[0]));
-                } else {
-                    ioSupplier = url::openStream;
-                }
+
+                IOSupplier<InputStream> ioSupplier = (url == null) ?
+                        () -> Files.newInputStream(Paths.get(style[0])) :
+                        url::openStream;
                 configuration.setStyleSheet(ioSupplier);
             }
         }
@@ -299,10 +429,10 @@
         }
         return configuration;
     }
-    
+
     /**
      * Creates a filename filter from patterns to exclude.
-     * 
+     *
      * @param excludes the list of patterns to exclude.
      * @return the FilenameFilter tht excludes the patterns
      */
@@ -332,101 +462,63 @@
     }
 
     static Options buildOptions() {
-        String licFilterValues = Arrays.stream(LicenseFilter.values()).map(LicenseFilter::name).collect(Collectors.joining(", "));
-
-        Options opts = new Options()
-        .addOption(Option.builder().longOpt(DRY_RUN)
-                .desc("If set do not update the files but generate the reports.")
-                .build())
-        .addOption(
-                Option.builder().hasArg(true).longOpt(LIST_FAMILIES)
-                .desc("List the defined license families (default is none). Valid options are: "+licFilterValues+".")
-                .build())
-        .addOption(
-                Option.builder().hasArg(true).longOpt(LIST_LICENSES)
-                .desc("List the defined licenses (default is none). Valid options are: "+licFilterValues+".")
-                .build())
-
-        .addOption(new Option(HELP, "help", false, "Print help for the RAT command line interface and exit."));
-
-        Option out = new Option("o", "out", true,
-                "Define the output file where to write a report to (default is System.out).");
-        opts.addOption(out);
-
-        String defaultHandlingText = " By default all approved default licenses are used";
-        Option noDefaults = new Option(null, NO_DEFAULTS, false, "Ignore default configuration." + defaultHandlingText);
-        opts.addOption(noDefaults);
-
-        opts.addOption(null, LICENSES, true, "File names or URLs for license definitions");
-        opts.addOption(null, SCAN_HIDDEN_DIRECTORIES, false, "Scan hidden directories");
-
-        OptionGroup addLicenseGroup = new OptionGroup();
-        // RAT-85/RAT-203: Deprecated! added only for convenience and for backwards
-        // compatibility
-        Option addLicence = new Option(ADD_OLD, false, "(deprecated) Add the default license header to any file with an unknown license.  Use '-A' or ---addLicense instead.");
-        addLicenseGroup.addOption(addLicence);
-        Option addLicense = new Option(ADD, "addLicense", false, "Add the default license header to any file with an unknown license that is not in the exclusion list. "
-                + "By default new files will be created with the license header, "
-                + "to force the modification of existing files use the --force option.");
-        addLicenseGroup.addOption(addLicense);
-        opts.addOptionGroup(addLicenseGroup);
-
-        Option write = new Option(FORCE, "force", false,
-                "Forces any changes in files to be written directly to the source files (i.e. new files are not created).");
-        opts.addOption(write);
-
-        Option copyright = new Option(COPYRIGHT, "copyright", true,
-                "The copyright message to use in the license headers, usually in the form of \"Copyright 2008 Foo\"");
-        opts.addOption(copyright);
-
-        final Option exclude = Option.builder(EXCLUDE_CLI).argName("expression").longOpt("exclude").hasArgs()
-                .desc("Excludes files matching wildcard <expression>. "
-                        + "Note that --dir is required when using this parameter. " + "Allows multiple arguments.")
-                .build();
-        opts.addOption(exclude);
-
-        final Option excludeFile = Option.builder(EXCLUDE_FILE_CLI).argName("fileName").longOpt("exclude-file")
-                .hasArgs().desc("Excludes files matching regular expression in <file> "
-                        + "Note that --dir is required when using this parameter. ")
-                .build();
-        opts.addOption(excludeFile);
-
-        Option dir = new Option("d", "dir", false, "Used to indicate source when using --exclude");
-        opts.addOption(dir);
-
-        opts.addOption( Option.builder().argName("level").longOpt(LOG_LEVEL)
-                .hasArgs().desc("sets the log level.  Valid options are: DEBUG, INFO, WARN, ERROR, OFF")
-                .build() );
-        
-        OptionGroup outputType = new OptionGroup();
-
-        Option xml = new Option(XML, "xml", false, "Output the report in raw XML format.  Not compatible with -s");
-        outputType.addOption(xml);
-
-        Option xslt = new Option(STYLESHEET_CLI, "stylesheet", true,
-                "XSLT stylesheet to use when creating the report.  Not compatible with -x.  Either an external xsl file may be specified or one of the internal named sheets: plain-rat (default), missing-headers, or unapproved-licenses");
-        outputType.addOption(xslt);
-        opts.addOptionGroup(outputType);
-        
-        
-
-        return opts;
+        return new Options()
+                .addOption(ARCHIVE)
+                .addOption(DRY_RUN)
+                .addOption(LIST_FAMILIES)
+                .addOption(LIST_LICENSES)
+                .addOption(HELP)
+                .addOption(OUT)
+                .addOption(NO_DEFAULTS)
+                .addOption(LICENSES)
+                .addOption(SCAN_HIDDEN_DIRECTORIES)
+                .addOptionGroup(ADD)
+                .addOption(FORCE)
+                .addOption(COPYRIGHT)
+                .addOption(EXCLUDE_CLI)
+                .addOption(EXCLUDE_FILE_CLI)
+                .addOption(DIR)
+                .addOption(LOG_LEVEL)
+                .addOptionGroup(new OptionGroup()
+                        .addOption(XML).addOption(STYLESHEET_CLI));
     }
 
     private static void printUsage(Options opts) {
-        HelpFormatter f = new HelpFormatter();
-        f.setOptionComparator(new OptionComparator());
-        String header = System.lineSeparator()+"Available options";
+        printUsage(new PrintWriter(System.out), opts);
+    }
 
-        String footer = String.format("%nNOTE:%nRat is really little more than a grep ATM%n"
-                + "Rat is also rather memory hungry ATM%n" + "Rat is very basic ATM%n"
-                + "Rat highlights possible issues%n" + "Rat reports require interpretation%n"
-                + "Rat often requires some tuning before it runs well against a project%n"
-                + "Rat relies on heuristics: it may miss issues%n");
+    protected static String createPadding(int len) {
+        char[] padding = new char[len];
+        Arrays.fill(padding, ' ');
+        return new String(padding);
+    }
 
-        f.printHelp("java -jar apache-rat/target/apache-rat-CURRENT-VERSION.jar [options] [DIR|TARBALL]", header, opts,
-                footer, false);
-        System.exit(0);
+   static String header(String txt) {
+        return String.format("%n====== %s ======%n", WordUtils.capitalizeFully(txt));
+   }
+    static void printUsage(PrintWriter writer, Options opts) {
+
+        HelpFormatter helpFormatter = new HelpFormatter();
+        helpFormatter.setWidth(130);
+        helpFormatter.setOptionComparator(new OptionComparator());
+        String syntax = "java -jar apache-rat/target/apache-rat-CURRENT-VERSION.jar [options] [DIR|TARBALL]";
+        helpFormatter.printHelp(writer, helpFormatter.getWidth(), syntax, header("Available options"), opts,
+                helpFormatter.getLeftPadding(), helpFormatter.getDescPadding(),
+                header("Argument Types"), false);
+
+        String argumentPadding = createPadding(helpFormatter.getLeftPadding() + 5);
+        for (Map.Entry<String, Supplier<String>> argInfo : ARGUMENT_TYPES.entrySet()) {
+            writer.format("\n<%s>\n", argInfo.getKey());
+            helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + 10,
+                    argumentPadding + argInfo.getValue().get());
+        }
+        writer.println(header("Notes"));
+
+        int idx = 1;
+        for (String note : NOTES) {
+            writer.format("%d. %s%n", idx++, note);
+        }
+        writer.flush();
     }
 
     private Report() {
@@ -436,40 +528,34 @@
     /**
      * Creates an IReportable object from the directory name and ReportConfiguration
      * object.
-     * 
+     *
      * @param baseDirectory the directory that contains the files to report on.
-     * @param config the ReportConfiguration.
+     * @param config        the ReportConfiguration.
      * @return the IReportable instance containing the files.
      */
     private static IReportable getDirectory(String baseDirectory, ReportConfiguration config) {
-        try (PrintStream out = new PrintStream(config.getOutput().get())) {
-            File base = new File(baseDirectory);
-            
-            if (!base.exists()) {
-                config.getLog().log(Level.ERROR, "Directory '"+baseDirectory+"' does not exist");
-                return null;
-            }
+        File base = new File(baseDirectory);
 
-            if (base.isDirectory()) {
-                return new DirectoryWalker(base, config.getFilesToIgnore(), config.getDirectoriesToIgnore());
-            }
-
-            try {
-                return new ArchiveWalker(base, config.getFilesToIgnore());
-            } catch (IOException ex) {
-                config.getLog().log(Level.ERROR, "file '"+baseDirectory+"' is not valid gzip data.");
-                return null;
-            }
-        } catch (IOException e) {
-            throw new ConfigurationException("Error opening output", e);
+        if (!base.exists()) {
+            config.getLog().log(Level.ERROR, "Directory '" + baseDirectory + "' does not exist");
+            return null;
         }
+
+        Document doc = new FileDocument(base);
+        if (base.isDirectory()) {
+            return new DirectoryWalker(config, doc);
+        }
+
+        return new ArchiveWalker(config, doc);
     }
 
     /**
      * This class implements the {@code Comparator} interface for comparing Options.
      */
     public static class OptionComparator implements Comparator<Option>, Serializable {
-        /** The serial version UID. */
+        /**
+         * The serial version UID.
+         */
         private static final long serialVersionUID = 5305467873966684014L;
 
         private String getKey(Option opt) {
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java b/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java
index dee2915..4b7c1b1 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java
@@ -57,6 +57,17 @@
  * configuration and invoke the {@link Reporter}.
  */
 public class ReportConfiguration {
+
+    public enum Processing {NOTIFICATION("List file as present"), PRESENCE("List any licenses found"), ABSENCE("List licenses found and any unknown licences");
+
+    private final String description;
+    Processing(String description) {
+        this.description = description;
+    }
+    public String desc() { return description; }
+    }
+
+
     private final ReportingSet<ILicenseFamily> families;
     private final ReportingSet<ILicense> licenses;
     private final SortedSet<String> approvedLicenseCategories;
@@ -74,7 +85,7 @@
     private LicenseFilter listFamilies;
     private LicenseFilter listLicenses;
     private boolean dryRun;
-
+    private Processing archiveProcessing;
    
     /**
      * Constructor
@@ -89,11 +100,27 @@
         approvedLicenseCategories = new TreeSet<>();
         removedLicenseCategories = new TreeSet<>();
         styleReport = true;
-        listFamilies = LicenseFilter.NONE;
-        listLicenses = LicenseFilter.NONE;
+        listFamilies = Defaults.LIST_FAMILIES;
+        listLicenses = Defaults.LIST_LICENSES;
         dryRun = false;
     }
-    
+
+    /**
+     * Retrieves the archive processing type.
+     * @return The archive processing type.
+     */
+    public Processing getArchiveProcessing() {
+        return archiveProcessing == null ? Defaults.ARCHIVE_PROCESSING : archiveProcessing;
+    }
+
+    /**
+     * Sets the archive processing type.  If not set will default to NOTIFICATION.
+     * @param archiveProcessing the type of processing archives should have.
+     */
+    public void setArchiveProcessing(Processing archiveProcessing) {
+        this.archiveProcessing = archiveProcessing;
+    }
+
     /**
      * Retrieves the Log that was provided in the constructor.
      * @return the Log for the system.
@@ -168,40 +195,55 @@
     
     /**
      * Returns the state of the dry run flag.
-     * @return the stae of the dry run flag.
+     * @return the state of the dry run flag.
      */
     public boolean isDryRun() {
         return dryRun;
     }
     
     /**
-     * @return The filename filter for the potential input files.
+     * Gets the file name filter for files to ignore.
+     * @return The filename filter that identifies files to ignore.
      */
     public FilenameFilter getFilesToIgnore() {
-        return filesToIgnore;
+        return filesToIgnore == null ? Defaults.FILES_TO_IGNORE : filesToIgnore;
     }
 
     /**
+     * Sets the files name filter for files to ignore.
+     * If not set or set to {@code null} uses the Default.FILES_TO_IGNORE value.
      * @param filesToIgnore the filename filter to filter the input files.
+     * @see Defaults#FILES_TO_IGNORE
      */
     public void setFilesToIgnore(FilenameFilter filesToIgnore) {
         this.filesToIgnore = filesToIgnore;
     }
 
+    /**
+     * Gets the directories filter.
+     * @return the directories filter.
+     */
     public IOFileFilter getDirectoriesToIgnore() {
-        return directoriesToIgnore;
+        return directoriesToIgnore == null ? Defaults.DIRECTORIES_TO_IGNORE : directoriesToIgnore;
     }
 
+    /**
+     * Sets the directories to ignore.
+     * If {@code directoriesToIgnore} is null removes all directories to ignore.
+     * If not set {@code Defaults.DIRECTORIES_TO_IGNORE} is used.
+     * @param directoriesToIgnore the filter that defines the directories to ignore.
+     * @see Defaults#DIRECTORIES_TO_IGNORE
+     */
     public void setDirectoriesToIgnore(IOFileFilter directoriesToIgnore) {
-        if (directoriesToIgnore == null) {
-            this.directoriesToIgnore = FalseFileFilter.FALSE;
-        } else {
-            this.directoriesToIgnore = directoriesToIgnore;
-        }
+        this.directoriesToIgnore = directoriesToIgnore == null ? FalseFileFilter.FALSE : directoriesToIgnore;
     }
 
+    /**
+     * Adds a directory filter to the directories to ignore.
+     * @param directoryToIgnore the directory filter to add.
+     */
     public void addDirectoryToIgnore(IOFileFilter directoryToIgnore) {
-        this.directoriesToIgnore = this.directoriesToIgnore.and(directoryToIgnore);
+        this.directoriesToIgnore = this.directoriesToIgnore.or(directoryToIgnore);
     }
 
     /**
@@ -245,8 +287,6 @@
      * @param defaults The defaults to set.
      */
     public void setFrom(Defaults defaults) {
-        setFilesToIgnore(Defaults.getFilesToIgnore());
-        setDirectoriesToIgnore(Defaults.getDirectoriesToIgnore());
         addLicensesIfNotPresent(defaults.getLicenses(LicenseFilter.ALL));
         addApprovedLicenseCategories(defaults.getLicenseIds(LicenseFilter.APPROVED));
         if (isStyleReport() && getStyleSheet() == null) {
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/DefaultAnalyserFactory.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/DefaultAnalyserFactory.java
index 141de2a..416bfd4 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/DefaultAnalyserFactory.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/DefaultAnalyserFactory.java
@@ -19,13 +19,18 @@
 package org.apache.rat.analysis;
 
 import java.util.Collection;
+import java.util.Set;
+import java.util.function.Predicate;
 
 import org.apache.rat.ConfigurationException;
+import org.apache.rat.ReportConfiguration;
 import org.apache.rat.api.Document;
+import org.apache.rat.api.RatException;
 import org.apache.rat.document.IDocumentAnalyser;
 import org.apache.rat.document.RatDocumentAnalysisException;
 import org.apache.rat.license.ILicense;
-import org.apache.rat.utils.Log;
+import org.apache.rat.license.LicenseSetFactory;
+import org.apache.rat.walker.ArchiveWalker;
 
 /**
  * Creates default analysers.
@@ -35,58 +40,71 @@
     /**
      * Creates a DocumentAnalyser from a collection of ILicenses.
      * 
-     * @param log The Log to use for logging.
-     * @param licenses The licenses to use in the Analyser.
+     * @param configuration the ReportConfiguration
      * @return A document analyser that uses the provides licenses.
      */
-    public static IDocumentAnalyser createDefaultAnalyser(Log log, Collection<ILicense> licenses) {
+    public static IDocumentAnalyser createDefaultAnalyser(final ReportConfiguration configuration) {
+        Set<ILicense> licenses = configuration.getLicenses(LicenseSetFactory.LicenseFilter.ALL);
         if (licenses.isEmpty()) {
             throw new ConfigurationException("At least one license must be defined");
         }
-        log.debug("Licenses in Test");
-        licenses.forEach(log::debug);
-        return new DefaultAnalyser(log, licenses);
+        configuration.getLog().debug("Licenses in Test");
+        licenses.forEach(configuration.getLog()::debug);
+        return new DefaultAnalyser(configuration, licenses);
     }
 
     /**
-     * A DocumentAnalyser for the license
+     * A DocumentAnalyser a collection of licenses
      */
     private final static class DefaultAnalyser implements IDocumentAnalyser {
 
         /** The licenses to analyze */
         private final Collection<ILicense> licenses;
-        /** The log to use */
-        private final Log log;
+
+        /** the Report Configuration */
+        private final ReportConfiguration configuration;
 
         /**
          * Constructs a DocumentAnalyser for the specified license.
-         * @param log the Log to use
+         * @param config the ReportConfiguration
          * @param licenses The licenses to analyse
          */
-        public DefaultAnalyser(final Log log, final Collection<ILicense> licenses) {
+        public DefaultAnalyser(ReportConfiguration config, final Collection<ILicense> licenses) {
             this.licenses = licenses;
-            this.log = log;
+            this.configuration = config;
         }
 
         @Override
         public void analyse(Document document) throws RatDocumentAnalysisException {
 
-            TikaProcessor.process(log, document);
+            TikaProcessor.process(configuration.getLog(), document);
 
             switch (document.getMetaData().getDocumentType()) {
             case STANDARD:
-                DocumentHeaderAnalyser analyser = new DocumentHeaderAnalyser(log, licenses);
-                analyser.analyse(document);
-            case NOTICE:
+                new DocumentHeaderAnalyser(configuration.getLog(), licenses).analyse(document);
+                break;
             case ARCHIVE:
+                if (configuration.getArchiveProcessing() != ReportConfiguration.Processing.NOTIFICATION) {
+                    ArchiveWalker archiveWalker = new ArchiveWalker(configuration, document);
+                    Predicate<ILicense> filter = configuration.getArchiveProcessing() == ReportConfiguration.Processing.ABSENCE ?
+                            l -> Boolean.TRUE : lic -> !lic.getLicenseFamily().equals(UnknownLicense.INSTANCE.getLicenseFamily());
+                    try {
+                        Collection<Document> docs = archiveWalker.getDocuments(configuration.getLog());
+                        for (Document doc : docs) {
+                            analyse(doc);
+                            doc.getMetaData().licenses().filter(filter).forEach(lic -> document.getMetaData().reportOnLicense(lic));
+                        }
+                    } catch (RatException e) {
+                        throw new RatDocumentAnalysisException(e);
+                    }
+                }
+                break;
+            case NOTICE:
             case BINARY:
             case UNKNOWN:
             default:
                 break;
             }
-
-
-
         }
     }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/DocumentHeaderAnalyser.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/DocumentHeaderAnalyser.java
index 7ff1f26..31edfa0 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/DocumentHeaderAnalyser.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/DocumentHeaderAnalyser.java
@@ -42,7 +42,8 @@
     /**
      * Constructs the HeaderAnalyser for the specific license.
      *
-     * @param license The license to analyse
+     * @param log The log to write message to.
+     * @param licenses The licenses to analyse
      */
     public DocumentHeaderAnalyser(final Log log, final Collection<ILicense> licenses) {
         super();
diff --git a/apache-rat-core/src/main/java/org/apache/rat/api/Document.java b/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
index 5d4467d..c2d861a 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
@@ -21,17 +21,21 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.SortedSet;
 
 import org.apache.rat.document.CompositeDocumentException;
 
 /**
  * The representation of a document being scanned.
  */
-public interface Document {
+public abstract class Document implements Comparable<Document> {
+
     /**
      * An enumeraton of document types.
      */
-    enum Type {
+    public enum Type {
         /** A generated document. */
         GENERATED,
         /** An unknown document type. */
@@ -46,10 +50,50 @@
         STANDARD;;
     }
 
+    private final MetaData metaData;
+    private final String name;
+
+    /**
+     * Creates an instance.
+     * @param name the name of the resource.
+     */
+    protected Document(String name) {
+        this.name = name;
+        this.metaData = new MetaData();
+    }
+
     /**
      * @return the name of the current document.
      */
-    String getName();
+    public final String getName() {
+        return name;
+    }
+
+    @Override
+    public int compareTo(Document doc) {
+        return getPath().compareTo(doc.getPath());
+    }
+
+    @Override
+    public int hashCode() {
+        return getPath().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || ! (obj instanceof Document)) {
+            return false;
+        }
+        return getPath().equals(((Document)obj).getPath());
+    }
+
+    /**
+     * Get the path that identifies the document.
+     * @return
+     */
+    public Path getPath() {
+        return Paths.get(getName());
+    }
 
     /**
      * Reads the contents of this document.
@@ -59,7 +103,7 @@
      * @throws CompositeDocumentException if this document can only be read as a
      * composite archive
      */
-    Reader reader() throws IOException;
+    public abstract Reader reader() throws IOException;
 
     /**
      * Streams the document's contents.
@@ -67,19 +111,38 @@
      * @return a non null input stream of the document.
      * @throws IOException when stream could not be opened
      */
-    InputStream inputStream() throws IOException;
+    public abstract InputStream inputStream() throws IOException;
 
     /**
      * Gets data describing this resource.
      * 
      * @return a non null MetaData object.
      */
-    MetaData getMetaData();
+    public final MetaData getMetaData() {
+        return metaData;
+    }
 
     /**
-     * Tests if this a composite document.
-     * 
-     * @return true if composite, false otherwise
+     * Representations suitable for logging.
+     * @return a <code>String</code> representation
+     * of this object.
      */
-    boolean isComposite();
+    @Override
+    public String toString()
+    {
+        return String.format("%s( name = %s metaData = %s )", this.getClass().getSimpleName(), getName(), getMetaData());
+    }
+
+    /**
+     * Determines if this Document is a directory type.
+     * @return {@code true} if this is a directory.
+     */
+    public abstract boolean isDirectory();
+
+    /**
+     * Gets a sorted set of Documents that are children of this document.
+     * @return A sorted set of child Documents.  May  be empty
+     */
+    public abstract SortedSet<Document> listChildren();
+
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/impl/AbstractMonolithicDocument.java b/apache-rat-core/src/main/java/org/apache/rat/document/impl/AbstractMonolithicDocument.java
deleted file mode 100644
index 302e372..0000000
--- a/apache-rat-core/src/main/java/org/apache/rat/document/impl/AbstractMonolithicDocument.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */ 
-package org.apache.rat.document.impl;
-
-import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
-
-
-/**
- * Abstract base class for monolithic documents.
- */
-public abstract class AbstractMonolithicDocument implements Document {
-    private final String name;
-    private final MetaData metaData;
-
-    public AbstractMonolithicDocument(String pName) {
-        name = pName;
-        this.metaData = new MetaData();
-    }
-
-    public boolean isComposite() {
-        return false;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public MetaData getMetaData() {
-        return metaData;
-    }
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/impl/ArchiveEntryDocument.java b/apache-rat-core/src/main/java/org/apache/rat/document/impl/ArchiveEntryDocument.java
index f9b7f17..7450d1f 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/document/impl/ArchiveEntryDocument.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/document/impl/ArchiveEntryDocument.java
@@ -20,62 +20,56 @@
 package org.apache.rat.document.impl;
 
 import java.io.ByteArrayInputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.SortedSet;
 
 import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
-import org.apache.rat.api.RatException;
 
-public class ArchiveEntryDocument implements Document {
+/**
+ * A Document that wraps an Archive entry.
+ */
+public class ArchiveEntryDocument extends Document {
 
+    /** The contents of the entry */
     private final byte[] contents;
-    private final String name;
 
-    private final MetaData metaData = new MetaData();
+    /** The path for the entry */
+    private final Path path;
 
-    public ArchiveEntryDocument(File file, byte[] contents) throws RatException {
-        super();
-        name = DocumentImplUtils.toName(file);
+    /**
+     * Creates an Archive entry.
+     * @param path The path for the entry.
+     * @param contents the contents of the entry.
+     */
+    public ArchiveEntryDocument(Path path, byte[] contents) {
+        super(FileDocument.normalizeFileName(path.toFile()));
+        this.path = path;
         this.contents = contents;
     }
 
-    public MetaData getMetaData() {
-        return metaData;
-    }
-
-    public String getName() {
-        return name;
-    }
-
+    @Override
     public InputStream inputStream() throws IOException {
         return new ByteArrayInputStream(contents);
     }
 
-    public boolean isComposite() {
-        return DocumentImplUtils.isZipStream(new ByteArrayInputStream(contents));
+    @Override
+    public boolean isDirectory() {
+        return false;
     }
 
+    @Override
+    public SortedSet<Document> listChildren() {
+        return Collections.emptySortedSet();
+    }
+
+    @Override
     public Reader reader() throws IOException {
         return new InputStreamReader(new ByteArrayInputStream(contents), StandardCharsets.UTF_8);
     }
-
-
-    /**
-     * Representations suitable for logging.
-     * @return a <code>String</code> representation 
-     * of this object.
-     */
-    @Override
-    public String toString()
-    {
-        return "TarEntryDocument ( "
-            + "name = " + this.name + " "
-            + "metaData = " + this.metaData + " "
-            + " )";
-    }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/impl/DocumentImplUtils.java b/apache-rat-core/src/main/java/org/apache/rat/document/impl/DocumentImplUtils.java
deleted file mode 100644
index 5522e67..0000000
--- a/apache-rat-core/src/main/java/org/apache/rat/document/impl/DocumentImplUtils.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */ 
-package org.apache.rat.document.impl;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.ZipInputStream;
-
-import org.apache.commons.io.IOUtils;
-
-public class DocumentImplUtils {
-
-    public final static String toName(File file) {
-        String path = file.getPath();
-        return path.replace('\\', '/');
-    }
-    
-    public static final boolean isZipStream(InputStream stream) {
-        ZipInputStream zip = new ZipInputStream(stream);
-        try {
-            zip.getNextEntry();
-            return true;
-        } catch (IOException e) {
-            return false;
-        } finally {
-            IOUtils.closeQuietly(zip);
-        }
-    }
-
-    public static final boolean isZip(File file) {
-        try {
-            return isZipStream(new FileInputStream(file));
-        } catch (IOException e) {
-            return false;
-        }
-    }
-
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/impl/FileDocument.java b/apache-rat-core/src/main/java/org/apache/rat/document/impl/FileDocument.java
index d6509bc..16d35b9 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/document/impl/FileDocument.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/document/impl/FileDocument.java
@@ -19,67 +19,71 @@
 package org.apache.rat.document.impl;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
 
 /**
- * Document wrapping a file of undetermined composition.
- *
+ * Document wrapping a File object
  */
-public class FileDocument implements Document {
+public class FileDocument extends Document {
 
+    /** the wrapped file */
     private final File file;
-    private final String name;
-    private final MetaData metaData = new MetaData();
-    
+
+    /**
+     * Creates a File document.
+     * @param file the file to wrap.
+     */
     public FileDocument(final File file) {
-        super();
+        super(normalizeFileName(file));
         this.file = file;
-        name = DocumentImplUtils.toName(file);
     }
 
-    public boolean isComposite() {
-        return DocumentImplUtils.isZip(file);
+    /**
+     * Normalizes a file name.  Accounts for Windows to Unix conversion.
+     * @param file
+     * @return
+     */
+    public final static String normalizeFileName(File file) {
+        String path = file.getPath();
+        return path.replace('\\', '/');
+    }
+
+    @Override
+    public Path getPath() {
+        return file.toPath();
+    }
+
+    @Override
+    public boolean isDirectory() {
+        return file.isDirectory();
+    }
+
+    @Override
+    public SortedSet<Document> listChildren() {
+        if (isDirectory()) {
+            SortedSet<Document> result = new TreeSet<>();
+            Arrays.stream(file.listFiles()).map(f -> new FileDocument(f)).forEach(result::add);
+            return result;
+        }
+        return Collections.emptySortedSet();
     }
 
     public Reader reader() throws IOException {
         return new FileReader(file);
     }
 
-    public String getName() {
-        return name;
-    }
-
-    public MetaData getMetaData() {
-        return metaData;
-    }    
-    
     public InputStream inputStream() throws IOException {
         return Files.newInputStream(file.toPath());
     }
-
-    /**
-     * Representations suitable for logging.
-     * @return a <code>String</code> representation 
-     * of this object.
-     */
-    @Override
-    public String toString()
-    {
-        return "FileDocument ( "
-            + "file = " + this.file + " "
-            + "name = " + this.name + " "
-            + "metaData = " + this.metaData + " "
-            + " )";
-    }
-    
-    
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/impl/MonolithicFileDocument.java b/apache-rat-core/src/main/java/org/apache/rat/document/impl/MonolithicFileDocument.java
deleted file mode 100644
index e19c509..0000000
--- a/apache-rat-core/src/main/java/org/apache/rat/document/impl/MonolithicFileDocument.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.document.impl;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-
-import org.apache.rat.api.Document;
-
-
-public class MonolithicFileDocument extends AbstractMonolithicDocument {
-    private static final String FILE_URL_PREFIX = "file";
-
-    private final File file;
-
-    /**
-     * @return Creates and returns a new instance.
-     *
-     * @param url The document is read from the given URL.
-     */
-    public static Document newInstance(final URL url) {
-        if (FILE_URL_PREFIX.equals(url.getProtocol())) {
-            final File f = new File(url.getFile());
-            return new MonolithicFileDocument(f);
-        }
-        return new AbstractMonolithicDocument(url.toExternalForm()){
-            public Reader reader() throws IOException {
-                return new InputStreamReader(inputStream(), StandardCharsets.UTF_8);
-           }
-
-            public InputStream inputStream() throws IOException {
-                return url.openStream();
-            }
-        };
-    }
-
-     public MonolithicFileDocument(final File file) {
-        super(DocumentImplUtils.toName(file));
-        this.file = file;
-    }
-
-     public Reader reader() throws IOException {
-        return new FileReader(file);
-    }
-
-     public InputStream inputStream() throws IOException {
-        return Files.newInputStream(file.toPath());
-    }
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
index 1e5c17d..7f10204 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
@@ -67,8 +67,7 @@
         
         reporters.add(new SimpleXmlClaimReporter(writer));
 
-        final IDocumentAnalyser analyser =
-            DefaultAnalyserFactory.createDefaultAnalyser(configuration.getLog(), configuration.getLicenses(LicenseFilter.ALL));
+        final IDocumentAnalyser analyser = DefaultAnalyserFactory.createDefaultAnalyser(configuration);
         final DefaultPolicy policy = new DefaultPolicy(configuration.getLicenseFamilies(LicenseFilter.APPROVED));
 
         final IDocumentAnalyser[] analysers = {analyser, policy};
diff --git a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/impl/base/XmlWriter.java b/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/impl/base/XmlWriter.java
index 2495947..debd748 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/impl/base/XmlWriter.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/impl/base/XmlWriter.java
@@ -552,9 +552,20 @@
         StringBuilder sb = new StringBuilder(content);
         int found;
         while (-1 != (found = sb.indexOf("]]>"))) {
-            sb.replace(found, found + 3, "<!-- Rat removed CDATA closure here -->");
+            sb.replace(found, found + 3, "{rat:CDATA close}");
         }
-        writer.write(String.format("<![CDATA[ %s ]]>", sb.toString()));
+
+        writer.write("<![CDATA[ " );
+        for (int i=0;i<sb.length();i++) {
+            char c = sb.charAt(i);
+            if (isOutOfRange(c)) {
+                writer.write(String.format("\\u%X", (int)c));
+            } else {
+                writer.write(c);
+            }
+        }
+        writer.write(" ]]>");
+
         inElement = false;
         return this;
     }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/ArchiveWalker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/ArchiveWalker.java
index d61f735..a2a25c3 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/walker/ArchiveWalker.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/walker/ArchiveWalker.java
@@ -19,36 +19,45 @@
 
 package org.apache.rat.walker;
 
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FilenameFilter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
-import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
-import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.io.IOUtils;
+import org.apache.rat.ReportConfiguration;
 import org.apache.rat.api.Document;
 import org.apache.rat.api.RatException;
 import org.apache.rat.document.impl.ArchiveEntryDocument;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.report.RatReport;
+import org.apache.rat.utils.Log;
 
 /**
  * Walks various kinds of archives files
  */
 public class ArchiveWalker extends Walker {
+    private final Log log;
 
     /**
      * Constructs a walker.
-     * @param file not null
-     * @param filter filters input files (optional) null when no filtering should be performed
-     * @throws FileNotFoundException in case of I/O errors.
+     * @param config the report configuration for this run.
+     * @param document the document to process.
      */
-    public ArchiveWalker(final File file, final FilenameFilter filter) throws FileNotFoundException {
-        super(file, filter);
+    public ArchiveWalker(final ReportConfiguration config, Document document) {
+        super(document, config.getFilesToIgnore());
+        this.log = config.getLog();
     }
 
     /**
@@ -59,57 +68,43 @@
      *
      */
     public void run(final RatReport report) throws RatException {
-
-        try {
-            ArchiveInputStream<? extends ArchiveEntry> input;
-
-            /* I am really sad that classes aren't first-class objects in
-               Java :'( */
-            try {
-                input = new TarArchiveInputStream(new GzipCompressorInputStream(Files.newInputStream(getBaseFile().toPath())));
-            } catch (IOException e) {
-                try {
-                    input = new TarArchiveInputStream(new BZip2CompressorInputStream(Files.newInputStream(getBaseFile().toPath())));
-                } catch (IOException e2) {
-                    input = new ZipArchiveInputStream(Files.newInputStream(getBaseFile().toPath()));
-                }
-            }
-
-            ArchiveEntry entry = input.getNextEntry();
-            while (entry != null) {
-                File f = new File(entry.getName());
-                byte[] contents = new byte[(int) entry.getSize()];
-                int offset = 0;
-                int length = contents.length;
-
-                while (offset < entry.getSize()) {
-                    int actualRead = input.read(contents, offset, length);
-                    length -= actualRead;
-                    offset += actualRead;
-                }
-
-                if (!entry.isDirectory() && isNotIgnored(f)) {
-                    report(report, contents, f);
-                }
-
-                entry = input.getNextEntry();
-            }
-
-            input.close();
-        } catch (IOException e) {
-            throw new RatException(e);
+        for (Document document : getDocuments(log)) {
+            report.report(document);
         }
     }
 
     /**
-     * Report on the given file.
-     *
-     * @param report the report to process the file with
-     * @param file the file to be reported on
-     * @throws RatException
+     * Creates an input stream from the Directory being walked.
+     * @return A buffered input stream reading the archive data.
+     * @throws IOException on error
      */
-    private void report(final RatReport report, final byte[] contents, final File file) throws RatException {
-        Document document = new ArchiveEntryDocument(file, contents);
-        report.report(document);
+    private InputStream createInputStream() throws IOException {
+        return new BufferedInputStream(getDocument().inputStream());
+    }
+    /**
+     * Retrieves the documents from the archive.
+     * @param log The log to write messages to.
+     * @return A collection of documents that pass the file filter.
+     * @throws RatException on error.
+     */
+    public Collection<Document> getDocuments(Log log) throws RatException {
+        List<Document> result = new ArrayList<>();
+        try (ArchiveInputStream<? extends ArchiveEntry> input = new ArchiveStreamFactory().createArchiveInputStream(createInputStream())) {
+            ArchiveEntry entry = null;
+            while ((entry = input.getNextEntry()) != null) {
+                Path path = this.getDocument().getPath().resolve("#"+entry.getName());
+                if (!entry.isDirectory() && this.isNotIgnored(path) && input.canReadEntryData(entry)) {
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    IOUtils.copy(input,baos);
+                    result.add(new ArchiveEntryDocument(path, baos.toByteArray()));
+                }
+            }
+        } catch (ArchiveException e) {
+            log.warn(String.format("Unable to process %s: %s", getDocument().getName(), e.getMessage()));
+        }
+        catch (IOException e) {
+            throw RatException.asRatException(e);
+        }
+        return result;
     }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/DirectoryWalker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/DirectoryWalker.java
index e3cf3a7..f62d0c0 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/walker/DirectoryWalker.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/walker/DirectoryWalker.java
@@ -21,10 +21,15 @@
 
 import java.io.File;
 import java.io.FilenameFilter;
+import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.SortedSet;
 
 import org.apache.commons.io.filefilter.FalseFileFilter;
 import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.rat.ReportConfiguration;
+import org.apache.rat.api.Document;
 import org.apache.rat.api.RatException;
 import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.report.RatReport;
@@ -34,33 +39,30 @@
  */
 public class DirectoryWalker extends Walker {
 
-    private static final FileNameComparator COMPARATOR = new FileNameComparator();
-
+    /** The directories to ignore */
     private final IOFileFilter directoriesToIgnore;
 
     /**
-     * Constructs a walker.
+     * Constructs a directory walker.
      *
-     * @param file the directory to walk (not null).
-     * @param filesToIgnore filters input files (optional),
-     *               or null when no filtering should be performed
-     * @param directoriesToIgnore filters directories (optional), or null when no filtering should be performed.
+     * @param config the report configuration for this run.
+     * @param document the document to process.
      */
-    public DirectoryWalker(final File file, final FilenameFilter filesToIgnore, final IOFileFilter directoriesToIgnore) {
-        super(file, filesToIgnore);
-        this.directoriesToIgnore = directoriesToIgnore == null ? FalseFileFilter.FALSE : directoriesToIgnore;
+    public DirectoryWalker(final ReportConfiguration config, Document document) {
+        super(document, config.getFilesToIgnore());
+        this.directoriesToIgnore = config.getDirectoriesToIgnore();
     }
 
     /**
      * Process a directory, restricted directories will be ignored.
      *
      * @param report The report to process the directory with
-     * @param file   the directory to process
+     * @param document the document to process.
      * @throws RatException on error.
      */
-    private void processDirectory(final RatReport report, final File file) throws RatException {
-        if (!directoriesToIgnore.accept(file)) {
-            process(report, file);
+    private void processDirectory(final RatReport report, Document document) throws RatException {
+        if (isNotIgnoredDirectory(document.getPath())) {
+            process(report, document);
         }
     }
 
@@ -72,41 +74,41 @@
      * @throws RatException on error
      */
     public void run(final RatReport report) throws RatException {
-        process(report, getBaseFile());
+        process(report, getDocument());
     }
 
     /**
      * Process a directory, ignoring any files/directories set to be ignored.
      *
      * @param report the report to use in processing
-     * @param file   the run the report against
+     * @param document the document to run the report against
      * @throws RatException on error
      */
-    protected void process(final RatReport report, final File file) throws RatException {
-        final File[] files = file.listFiles();
-        if (files != null) {
-            Arrays.sort(files, COMPARATOR);
+    protected void process(final RatReport report, Document document) throws RatException {
+        final SortedSet<Document> documents = document.listChildren();
+        if (documents != null) {
             // breadth first traversal
-            processNonDirectories(report, files);
-            processDirectories(report, files);
+            processNonDirectories(report, documents);
+            processDirectories(report, documents);
         }
     }
 
-    private boolean isNotIgnoredDirectory(final File file) {
-        return !directoriesToIgnore.accept(file.getParentFile(), file.getName());
+    /** test for directory that is not ignored */
+    private boolean isNotIgnoredDirectory(Path path) {
+        return !directoriesToIgnore.accept(path.getParent().toFile(), path.toString());
     }
 
     /**
      * Process all directories in a set of file objects, ignoring any directories set to be ignored.
      *
      * @param report the report to use in processing
-     * @param files  the files to process (only directories will be processed)
+     * @param documents the documents to process (only directories will be processed)
      * @throws RatException on error
      */
-    private void processDirectories(final RatReport report, final File[] files) throws RatException {
-        for (final File file : files) {
-            if (file.isDirectory() && isNotIgnoredDirectory(file)) {
-                processDirectory(report, file);
+    private void processDirectories(final RatReport report, final SortedSet<Document> documents) throws RatException {
+        for (final Document doc : documents) {
+            if (doc.isDirectory() && isNotIgnoredDirectory(doc.getPath())) {
+                processDirectory(report, doc);
             }
         }
     }
@@ -115,13 +117,13 @@
      * Process all files in a set of file objects, ignoring any files set to be ignored.
      *
      * @param report the report to use in processing
-     * @param files  the files to process (only files will be processed)
+     * @param documents the documents to process (only files will be processed)
      * @throws RatException on error
      */
-    private void processNonDirectories(final RatReport report, final File[] files) throws RatException {
-        for (final File file : files) {
-            if (!file.isDirectory() && isNotIgnored(file)) {
-                report.report(new FileDocument(file));
+    private void processNonDirectories(final RatReport report, final SortedSet<Document> documents) throws RatException {
+        for (final Document doc : documents) {
+            if (!doc.isDirectory() && isNotIgnored(doc.getPath())) {
+                report.report(doc);
             }
         }
     }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/FileNameComparator.java b/apache-rat-core/src/main/java/org/apache/rat/walker/FileNameComparator.java
deleted file mode 100644
index c3ed803..0000000
--- a/apache-rat-core/src/main/java/org/apache/rat/walker/FileNameComparator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */ 
-package org.apache.rat.walker;
-
-import java.io.File;
-import java.util.Comparator;
-
-class FileNameComparator implements Comparator<File> {
-
-    public int compare(File firstFile, File secondFile) {
-        int result = 0;
-        if (firstFile == null) {
-            if (secondFile != null) {
-                result = 1;
-            }
-        } else {
-            if (secondFile == null) {
-                result = -1;
-            } else {
-                final String firstName = firstFile.getName();
-                final String secondName = secondFile.getName();
-                result = firstName.compareTo(secondName);
-            }
-        }
-        return result;
-    }
-
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java
index abb8cae..b5f112e 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java
@@ -20,41 +20,47 @@
 package org.apache.rat.walker;
 
 import org.apache.commons.io.filefilter.FalseFileFilter;
+import org.apache.rat.api.Document;
 import org.apache.rat.report.IReportable;
 
 import java.io.File;
 import java.io.FilenameFilter;
+import java.nio.file.Path;
 
 /**
  * Abstract walker.
  */
 public abstract class Walker implements IReportable {
 
-    /** The file that this walker started at */
-    private final File baseFile;
-
     /** The file name filter that the walker is applying */
     private final FilenameFilter filesToIgnore;
+    /** The document this walker is walking */
+    private  final Document document;
 
-    public Walker(final File file, final FilenameFilter filesToIgnore) {
-        this.baseFile = file;
+    /**
+     * Creates  the walker
+     * @param document The document the walker is walking.
+     * @param filesToIgnore the Files to ignore.  If {@code null} no files are ignored.
+     */
+    protected Walker(final Document document, final FilenameFilter filesToIgnore) {
+        this.document = document;
         this.filesToIgnore = filesToIgnore == null ? FalseFileFilter.FALSE : filesToIgnore;
     }
 
     /**
-     * Retrieve the file from the constructor.
-     * @return the file from the constructor.
+     * Retrieves the document from the constructor.
+     * @return the document from the constructor.
      */
-    protected File getBaseFile() {
-        return baseFile;
+    protected Document getDocument() {
+        return document;
     }
 
     /**
-     * Test if the specified file should be ignored.
-     * @param file the file to test.
-     * @return {@code true} if the file should be ignored.
+     * Tests if the specified path should be ignored.
+     * @param path the Path to test
+     * @return {@code true} if the file should not be ignored.
      */
-    protected final boolean isNotIgnored(final File file) {
-        return !filesToIgnore.accept(file.getParentFile(), file.getName());
+    protected final boolean isNotIgnored(Path path) {
+        return !filesToIgnore.accept(path.getParent().toFile(), path.toString());
     }
 }
diff --git a/apache-rat-core/src/main/resources/org/apache/rat/plain-rat.xsl b/apache-rat-core/src/main/resources/org/apache/rat/plain-rat.xsl
index da01cbb..b9cb417 100644
--- a/apache-rat-core/src/main/resources/org/apache/rat/plain-rat.xsl
+++ b/apache-rat-core/src/main/resources/org/apache/rat/plain-rat.xsl
@@ -54,9 +54,10 @@
 <xsl:if test="descendant::resource[@type='ARCHIVE']">
 Archives:
 <xsl:for-each select='descendant::resource[@type="ARCHIVE"]'>
- + <xsl:value-of select='@name'/>
- <xsl:text>
- </xsl:text>
+  <xsl:text>  </xsl:text>
+  <xsl:value-of select='@name'/>
+  <xsl:text>
+</xsl:text>
  </xsl:for-each>
 </xsl:if>
 <xsl:text>
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java
index 48e962a..27b3aea 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java
@@ -44,9 +44,9 @@
 import java.util.SortedSet;
 import java.util.function.Function;
 
-import org.apache.commons.io.filefilter.AndFileFilter;
 import org.apache.commons.io.filefilter.DirectoryFileFilter;
 import org.apache.commons.io.filefilter.FalseFileFilter;
+import org.apache.commons.io.filefilter.OrFileFilter;
 import org.apache.commons.io.function.IOSupplier;
 import org.apache.rat.ReportConfiguration.NoCloseOutputStream;
 import org.apache.rat.analysis.IHeaderMatcher;
@@ -189,10 +189,9 @@
     @Test
     public void filesToIgnoreTest() {
 
-        assertThat(underTest.getFilesToIgnore()).isNull();
+        assertThat(underTest.getFilesToIgnore()).isExactlyInstanceOf(FalseFileFilter.class);
 
         underTest.setFrom(Defaults.builder().build(DefaultLog.INSTANCE));
-        assertThat(underTest.getFilesToIgnore()).isNotNull();
         assertThat(underTest.getFilesToIgnore()).isExactlyInstanceOf(FalseFileFilter.class);
 
         FilenameFilter filter = mock(FilenameFilter.class);
@@ -202,21 +201,37 @@
 
     @Test
     public void directoriesToIgnoreTest() {
-        assertThat(underTest.getDirectoriesToIgnore()).isNull();
+        assertThat(underTest.getDirectoriesToIgnore()).isExactlyInstanceOf(NameBasedHiddenFileFilter.class);
 
         underTest.setFrom(Defaults.builder().build(DefaultLog.INSTANCE));
-        assertThat(underTest.getDirectoriesToIgnore()).isNotNull();
         assertThat(underTest.getDirectoriesToIgnore()).isExactlyInstanceOf(NameBasedHiddenFileFilter.class);
 
         underTest.setDirectoriesToIgnore(DirectoryFileFilter.DIRECTORY);
         underTest.addDirectoryToIgnore(NameBasedHiddenFileFilter.HIDDEN);
-        assertThat(underTest.getDirectoriesToIgnore()).isExactlyInstanceOf(AndFileFilter.class);
+        assertThat(underTest.getDirectoriesToIgnore()).isExactlyInstanceOf(OrFileFilter.class);
 
         underTest.setDirectoriesToIgnore(null);
         assertThat(underTest.getDirectoriesToIgnore()).isExactlyInstanceOf(FalseFileFilter.class);
     }
 
     @Test
+    public void archiveProcessingTest() {
+        assertThat(underTest.getArchiveProcessing()).isEqualTo(ReportConfiguration.Processing.NOTIFICATION);
+
+        underTest.setFrom(Defaults.builder().build(DefaultLog.INSTANCE));
+        assertThat(underTest.getArchiveProcessing()).isEqualTo(ReportConfiguration.Processing.NOTIFICATION);
+
+        underTest.setArchiveProcessing(ReportConfiguration.Processing.ABSENCE);
+        assertThat(underTest.getArchiveProcessing()).isEqualTo(ReportConfiguration.Processing.ABSENCE);
+
+        underTest.setArchiveProcessing(ReportConfiguration.Processing.PRESENCE);
+        assertThat(underTest.getArchiveProcessing()).isEqualTo(ReportConfiguration.Processing.PRESENCE);
+
+        underTest.setArchiveProcessing(null);
+        assertThat(underTest.getArchiveProcessing()).isEqualTo(ReportConfiguration.Processing.NOTIFICATION);
+    }
+
+    @Test
     public void licenseFamiliesTest() {
         assertThat(underTest.getLicenseFamilies(LicenseFilter.ALL)).isEmpty();
         assertThat(underTest.getLicenseFamilies(LicenseFilter.APPROVED)).isEmpty();
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReportTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReportTest.java
index 185848c..448f824 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ReportTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ReportTest.java
@@ -18,36 +18,37 @@
  */
 package org.apache.rat;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathFactory;
-
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.io.FileUtils;
 import org.apache.rat.testhelpers.TextUtils;
-import org.apache.rat.testhelpers.XmlUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
-import org.w3c.dom.Document;
-import org.w3c.dom.NodeList;
 
 public class ReportTest {
     @TempDir
     File tempDirectory;
-    
+
+    private ReportConfiguration createConfig(String... args) throws IOException, ParseException {
+        CommandLine cl = new DefaultParser().parse(Report.buildOptions(), args);
+        return Report.createConfiguration("target/test-classes/elements", cl);
+    }
+
     @Test
     public void parseExclusionsForCLIUsage() {
         final FilenameFilter filter = Report
@@ -66,7 +67,7 @@
     @Test
     public void testOutputOption() throws Exception {
         File output = new File(tempDirectory, "test");
-        CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] { "-o", output.getCanonicalPath()});
+        CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[]{"-o", output.getCanonicalPath()});
         ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
         new Reporter(config).output();
         assertTrue(output.exists());
@@ -77,104 +78,52 @@
     }
 
     @Test
-    public void testDefaultOutput() throws Exception {
-        File output = new File(tempDirectory,"sysout");
-        output.delete();
-        PrintStream origin = System.out;
-        try (PrintStream out = new PrintStream(output)){
-            System.setOut(out);
-            CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] {});
-            ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
-            new Reporter(config).output();
-        } finally {
-            System.setOut(origin);
-        }
-        assertTrue(output.exists());
-        String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
-        TextUtils.assertPatternInOutput("Notes: 2$", content);
-        TextUtils.assertPatternInOutput("Binaries: 2$", content);
-        TextUtils.assertPatternInOutput("Archives: 1$", content);
-        TextUtils.assertPatternInOutput("Standards: 8$", content);
-        TextUtils.assertPatternInOutput("Apache Licensed: 5$", content);
-        TextUtils.assertPatternInOutput("Generated Documents: 1$", content);
-        TextUtils.assertPatternInOutput("^2 Unknown Licenses", content);
-        assertTrue(content.contains(" S target/test-classes/elements/ILoggerFactory.java"));
-        assertTrue(content.contains(" B target/test-classes/elements/Image.png"));
-        assertTrue(content.contains(" N target/test-classes/elements/LICENSE"));
-        assertTrue(content.contains(" N target/test-classes/elements/NOTICE"));
-        assertTrue(content.contains("!S target/test-classes/elements/Source.java"));
-        assertTrue(content.contains(" S target/test-classes/elements/Text.txt"));
-        assertTrue(content.contains(" S target/test-classes/elements/TextHttps.txt"));
-        assertTrue(content.contains(" S target/test-classes/elements/Xml.xml"));
-        assertTrue(content.contains(" S target/test-classes/elements/buildr.rb"));
-        assertTrue(content.contains(" A target/test-classes/elements/dummy.jar"));
-        assertTrue(content.contains("!S target/test-classes/elements/sub/Empty.txt"));
-        assertTrue(content.contains(" S target/test-classes/elements/tri.txt"));
-        assertTrue(content.contains(" G target/test-classes/elements/generated.txt"));
+    public void helpTest() {
+        Options opts = Report.buildOptions();
+        StringWriter out = new StringWriter();
+        Report.printUsage(new PrintWriter(out), opts);
+
+        String result = out.toString();
+        System.out.println(result);
+
+        TextUtils.assertContains("-a ", result);
+        TextUtils.assertContains("-A,--addLicense ", result);
+        TextUtils.assertContains("--archive <ProcessingType> ", result);
+        TextUtils.assertContains("-c,--copyright <arg> ", result);
+        TextUtils.assertContains("-d,--dir <DirOrArchive> ", result);
+        TextUtils.assertContains("--dry-run ", result);
+        TextUtils.assertContains("-e,--exclude <Expression> ", result);
+        TextUtils.assertContains("-E,--exclude-file <FileOrURI> ", result);
+        TextUtils.assertContains("-f,--force ", result);
+        TextUtils.assertContains("-h,--help ", result);
+        TextUtils.assertContains("--licenses <FileOrURI> ", result);
+        TextUtils.assertContains("--list-families <LicenseFilter> ", result);
+        TextUtils.assertContains("--list-licenses <LicenseFilter> ", result);
+        TextUtils.assertContains("--log-level <LogLevel> ", result);
+        TextUtils.assertContains("--no-default-licenses ", result);
+        TextUtils.assertContains("-o,--out <arg> ", result);
+        TextUtils.assertContains("-s,--stylesheet <StyleSheet> ", result);
+        TextUtils.assertContains("--scan-hidden-directories ", result);
+        TextUtils.assertContains("-x,--xml ", result);
+    }
+
+    private static String shortOpt(Option opt) {
+        return "-" + opt.getOpt();
+    }
+
+    private static String longOpt(Option opt) {
+        return "--" + opt.getLongOpt();
     }
 
     @Test
     public void testXMLOutput() throws Exception {
-        File output = new File(tempDirectory,"sysout");
-        output.delete();
-        PrintStream origin = System.out;
-        try (PrintStream out = new PrintStream(output)){
-            System.setOut(out);
-            CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] { "-x" });
-            ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
-            new Reporter(config).output();
-        } finally {
-            System.setOut(origin);
-        }
-        assertTrue(output.exists());
-        Document doc = XmlUtils.toDom(new FileInputStream(output));
-        XPath xPath = XPathFactory.newInstance().newXPath();
+        ReportConfiguration config = createConfig();
+        assertTrue(config.isStyleReport());
 
-        NodeList nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@approval='false']");
-        assertEquals(2, nodeList.getLength());
+        config = createConfig(shortOpt(Report.XML));
+        assertFalse(config.isStyleReport());
 
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='AL']");
-        assertEquals(5, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='MIT']");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='BSD-3']");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='TMF']");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='?????']");
-        assertEquals(2, nodeList.getLength());
-
-        // GENERATED, UNKNOWN, ARCHIVE, NOTICE, BINARY, STANDARD
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='STANDARD']");
-        assertEquals(8, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='ARCHIVE']");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='BINARY']");
-        assertEquals(2, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='GENERATED']");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='UNKNOWN']");
-        assertEquals(0, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='NOTICE']");
-        assertEquals(2, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/sample");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='GENERATED']/license/notes");
-        assertEquals(1, nodeList.getLength());
-
-        nodeList = XmlUtils.getNodeList(doc, xPath,
-                "/rat-report/resource[@name='target/test-classes/elements/Source.java']/sample");
-        assertEquals(1, nodeList.getLength());
+        config = createConfig(longOpt(Report.XML));
+        assertFalse(config.isStyleReport());
     }
 }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
index 7ca9b7d..160519c 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
@@ -24,13 +24,20 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathFactory;
 
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.filefilter.HiddenFileFilter;
 import org.apache.rat.api.Document.Type;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.test.utils.Resources;
 import org.apache.rat.testhelpers.TextUtils;
@@ -38,6 +45,7 @@
 import org.apache.rat.utils.DefaultLog;
 import org.apache.rat.walker.DirectoryWalker;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 import org.w3c.dom.Document;
 import org.w3c.dom.NodeList;
 
@@ -45,7 +53,123 @@
  * Tests the output of the Reporter.
  */
 public class ReporterTest {
+    @TempDir
+    File tempDirectory;
 
+    @Test
+    public void testOutputOption() throws Exception {
+        File output = new File(tempDirectory, "test");
+        CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] { "-o", output.getCanonicalPath()});
+        ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
+        new Reporter(config).output();
+        assertTrue(output.exists());
+        String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
+        assertTrue(content.contains("2 Unknown Licenses"));
+        assertTrue(content.contains("target/test-classes/elements/Source.java"));
+        assertTrue(content.contains("target/test-classes/elements/sub/Empty.txt"));
+    }
+
+    @Test
+    public void testDefaultOutput() throws Exception {
+        File output = new File(tempDirectory,"sysout");
+        output.delete();
+        PrintStream origin = System.out;
+        try (PrintStream out = new PrintStream(output)){
+            System.setOut(out);
+            CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] {});
+            ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
+            new Reporter(config).output();
+        } finally {
+            System.setOut(origin);
+        }
+        assertTrue(output.exists());
+        String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
+        TextUtils.assertPatternInTarget("Notes: 2$", content);
+        TextUtils.assertPatternInTarget("Binaries: 2$", content);
+        TextUtils.assertPatternInTarget("Archives: 1$", content);
+        TextUtils.assertPatternInTarget("Standards: 8$", content);
+        TextUtils.assertPatternInTarget("Apache Licensed: 5$", content);
+        TextUtils.assertPatternInTarget("Generated Documents: 1$", content);
+        TextUtils.assertPatternInTarget("^2 Unknown Licenses", content);
+        assertTrue(content.contains(" S target/test-classes/elements/ILoggerFactory.java"));
+        assertTrue(content.contains(" B target/test-classes/elements/Image.png"));
+        assertTrue(content.contains(" N target/test-classes/elements/LICENSE"));
+        assertTrue(content.contains(" N target/test-classes/elements/NOTICE"));
+        assertTrue(content.contains("!S target/test-classes/elements/Source.java"));
+        assertTrue(content.contains(" S target/test-classes/elements/Text.txt"));
+        assertTrue(content.contains(" S target/test-classes/elements/TextHttps.txt"));
+        assertTrue(content.contains(" S target/test-classes/elements/Xml.xml"));
+        assertTrue(content.contains(" S target/test-classes/elements/buildr.rb"));
+        assertTrue(content.contains(" A target/test-classes/elements/dummy.jar"));
+        assertTrue(content.contains("!S target/test-classes/elements/sub/Empty.txt"));
+        assertTrue(content.contains(" S target/test-classes/elements/tri.txt"));
+        assertTrue(content.contains(" G target/test-classes/elements/generated.txt"));
+    }
+
+    @Test
+    public void testXMLOutput() throws Exception {
+        File output = new File(tempDirectory,"sysout");
+        output.delete();
+        PrintStream origin = System.out;
+        try (PrintStream out = new PrintStream(output)){
+            System.setOut(out);
+            CommandLine cl = new DefaultParser().parse(Report.buildOptions(), new String[] { "-x" });
+            ReportConfiguration config = Report.createConfiguration("target/test-classes/elements", cl);
+            new Reporter(config).output();
+        } finally {
+            System.setOut(origin);
+        }
+        assertTrue(output.exists());
+        Document doc = XmlUtils.toDom(new FileInputStream(output));
+        XPath xPath = XPathFactory.newInstance().newXPath();
+
+        NodeList nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@approval='false']");
+        assertEquals(2, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='AL']");
+        assertEquals(5, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='MIT']");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='BSD-3']");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='TMF']");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='?????']");
+        assertEquals(2, nodeList.getLength());
+
+        // GENERATED, UNKNOWN, ARCHIVE, NOTICE, BINARY, STANDARD
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='STANDARD']");
+        assertEquals(8, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='ARCHIVE']");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='BINARY']");
+        assertEquals(2, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='GENERATED']");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='UNKNOWN']");
+        assertEquals(0, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='NOTICE']");
+        assertEquals(2, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/sample");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='GENERATED']/license/notes");
+        assertEquals(1, nodeList.getLength());
+
+        nodeList = XmlUtils.getNodeList(doc, xPath,
+                "/rat-report/resource[@name='target/test-classes/elements/Source.java']/sample");
+        assertEquals(1, nodeList.getLength());
+    }
     /**
      * Finds a node via xpath on the document. And then checks family, approval and
      * type of elements of the node.
@@ -89,7 +213,8 @@
         final ReportConfiguration configuration = new ReportConfiguration(DefaultLog.INSTANCE);
         configuration.setStyleReport(false);
         configuration.setFrom(defaults);
-        configuration.setReportable(new DirectoryWalker(new File(elementsPath), configuration.getFilesToIgnore(), HiddenFileFilter.HIDDEN));
+        configuration.setDirectoriesToIgnore(HiddenFileFilter.HIDDEN);
+        configuration.setReportable(new DirectoryWalker(configuration, new FileDocument(new File(elementsPath))));
         configuration.setOut(() -> out);
         new Reporter(configuration).output();
         Document doc = XmlUtils.toDom(new ByteArrayInputStream(out.toByteArray()));
@@ -151,7 +276,8 @@
         final String elementsPath = Resources.getResourceDirectory("elements/Source.java");
         final ReportConfiguration configuration = new ReportConfiguration(DefaultLog.INSTANCE);
         configuration.setFrom(defaults);
-        configuration.setReportable(new DirectoryWalker(new File(elementsPath), configuration.getFilesToIgnore(), HiddenFileFilter.HIDDEN));
+        configuration.setDirectoriesToIgnore(HiddenFileFilter.HIDDEN);
+        configuration.setReportable(new DirectoryWalker(configuration, new FileDocument(new File(elementsPath))));
         configuration.setOut(() -> out);
         new Reporter(configuration).output();
 
@@ -160,43 +286,43 @@
 
         assertTrue(document.startsWith(HEADER), "'Generated at' is not present in " + document);
 
-        TextUtils.assertPatternInOutput("^Notes: 2$", document);
-        TextUtils.assertPatternInOutput("^Binaries: 2$", document);
-        TextUtils.assertPatternInOutput("^Archives: 1$", document);
-        TextUtils.assertPatternInOutput("^Standards: 8$", document);
-        TextUtils.assertPatternInOutput("^Apache Licensed: 5$", document);
-        TextUtils.assertPatternInOutput("^Generated Documents: 1$", document);
-        TextUtils.assertPatternInOutput("^2 Unknown Licenses$", document);
-        TextUtils.assertPatternInOutput(
+        TextUtils.assertPatternInTarget("^Notes: 2$", document);
+        TextUtils.assertPatternInTarget("^Binaries: 2$", document);
+        TextUtils.assertPatternInTarget("^Archives: 1$", document);
+        TextUtils.assertPatternInTarget("^Standards: 8$", document);
+        TextUtils.assertPatternInTarget("^Apache Licensed: 5$", document);
+        TextUtils.assertPatternInTarget("^Generated Documents: 1$", document);
+        TextUtils.assertPatternInTarget("^2 Unknown Licenses$", document);
+        TextUtils.assertPatternInTarget(
                 "^Files with unapproved licenses:\\s+" //
                         + "\\Qsrc/test/resources/elements/Source.java\\E\\s+" //
                         + "\\Qsrc/test/resources/elements/sub/Empty.txt\\E\\s",
                 document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.ARCHIVE, "src/test/resources/elements/dummy.jar"),
+        TextUtils.assertPatternInTarget(documentOut(true, Type.ARCHIVE, "src/test/resources/elements/dummy.jar"),
                 document);
-        TextUtils.assertPatternInOutput(
+        TextUtils.assertPatternInTarget(
                 documentOut(true, Type.STANDARD, "src/test/resources/elements/ILoggerFactory.java")
                         + licenseOut("MIT", "The MIT License"),
                 document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.BINARY, "src/test/resources/elements/Image.png"),
+        TextUtils.assertPatternInTarget(documentOut(true, Type.BINARY, "src/test/resources/elements/Image.png"),
                 document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.NOTICE, "src/test/resources/elements/LICENSE"),
+        TextUtils.assertPatternInTarget(documentOut(true, Type.NOTICE, "src/test/resources/elements/LICENSE"),
                 document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.NOTICE, "src/test/resources/elements/NOTICE"), document);
-        TextUtils.assertPatternInOutput(documentOut(false, Type.STANDARD, "src/test/resources/elements/Source.java")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.NOTICE, "src/test/resources/elements/NOTICE"), document);
+        TextUtils.assertPatternInTarget(documentOut(false, Type.STANDARD, "src/test/resources/elements/Source.java")
                 + licenseOut("?????", "Unknown license (Unapproved)"), document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.STANDARD, "src/test/resources/elements/Text.txt")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.STANDARD, "src/test/resources/elements/Text.txt")
                 + licenseOut("AL", "Apache License Version 2.0"), document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.STANDARD, "src/test/resources/elements/Xml.xml")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.STANDARD, "src/test/resources/elements/Xml.xml")
                 + licenseOut("AL", "Apache License Version 2.0"), document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.STANDARD, "src/test/resources/elements/buildr.rb")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.STANDARD, "src/test/resources/elements/buildr.rb")
                 + licenseOut("AL", "Apache License Version 2.0"), document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.STANDARD, "src/test/resources/elements/TextHttps.txt")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.STANDARD, "src/test/resources/elements/TextHttps.txt")
                 + licenseOut("AL", "Apache License Version 2.0"), document);
-        TextUtils.assertPatternInOutput(documentOut(true, Type.STANDARD, "src/test/resources/elements/tri.txt")
+        TextUtils.assertPatternInTarget(documentOut(true, Type.STANDARD, "src/test/resources/elements/tri.txt")
                 + licenseOut("AL", "Apache License Version 2.0") + licenseOut("BSD-3", "BSD 3 clause")
                 + licenseOut("BSD-3", "TMF", "The Telemanagement Forum License"), document);
-        TextUtils.assertPatternInOutput(documentOut(false, Type.STANDARD, "src/test/resources/elements/sub/Empty.txt")
+        TextUtils.assertPatternInTarget(documentOut(false, Type.STANDARD, "src/test/resources/elements/sub/Empty.txt")
                 + licenseOut("?????", "Unknown license (Unapproved)"), document);
     }
 
@@ -208,7 +334,8 @@
         final String elementsPath = Resources.getResourceDirectory("elements/Source.java");
         final ReportConfiguration configuration = new ReportConfiguration(DefaultLog.INSTANCE);
         configuration.setFrom(defaults);
-        configuration.setReportable(new DirectoryWalker(new File(elementsPath), configuration.getFilesToIgnore(), HiddenFileFilter.HIDDEN));
+        configuration.setDirectoriesToIgnore(HiddenFileFilter.HIDDEN);
+        configuration.setReportable(new DirectoryWalker(configuration, new FileDocument(new File(elementsPath))));
         configuration.setOut(() -> out);
         configuration.setStyleSheet(this.getClass().getResource("/org/apache/rat/unapproved-licenses.xsl"));
         new Reporter(configuration).output();
@@ -218,8 +345,8 @@
 
         assertTrue(document.startsWith("Generated at: "), "'Generated at' is not present in " + document);
 
-        TextUtils.assertPatternInOutput("\\Qsrc/test/resources/elements/Source.java\\E$", document);
-        TextUtils.assertPatternInOutput("\\Qsrc/test/resources/elements/sub/Empty.txt\\E", document);
+        TextUtils.assertPatternInTarget("\\Qsrc/test/resources/elements/Source.java\\E$", document);
+        TextUtils.assertPatternInTarget("\\Qsrc/test/resources/elements/sub/Empty.txt\\E", document);
     }
 
     private static class LicenseInfo {
diff --git a/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java b/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
deleted file mode 100644
index 9484b6f..0000000
--- a/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.analysis;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.StringWriter;
-import java.util.Arrays;
-
-import org.apache.rat.document.IDocumentAnalyser;
-import org.apache.rat.document.impl.MonolithicFileDocument;
-import org.apache.rat.license.ILicense;
-import org.apache.rat.report.claim.impl.xml.SimpleXmlClaimReporter;
-import org.apache.rat.report.xml.writer.impl.base.XmlWriter;
-import org.apache.rat.test.utils.Resources;
-import org.apache.rat.testhelpers.TextUtils;
-import org.apache.rat.utils.DefaultLog;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-public class AnalyserFactoryTest {
-
-    private static ILicense MATCHES_NOTHING_MATCHER = UnknownLicense.INSTANCE;
-
-    private StringWriter out;
-    private SimpleXmlClaimReporter reporter;
-    private IDocumentAnalyser analyser;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        out = new StringWriter();
-        reporter = new SimpleXmlClaimReporter(new XmlWriter(out));
-        analyser = DefaultAnalyserFactory.createDefaultAnalyser(DefaultLog.INSTANCE,
-                Arrays.asList(MATCHES_NOTHING_MATCHER));
-    }
-
-    @Test
-    public void standardTypeAnalyser() throws Exception {
-        String[] expected = {
-                "<resource name='src/test/resources/elements/Text.txt' type='STANDARD'>"
-                        + "<license id='?????' name='Unknown license' approval='false' family='?????'/>"
-                        + "<sample><![CDATA[ /*", //
-                " * Licensed to the Apache Software Foundation (ASF) under one", //
-                " * or more contributor license agreements.  See the NOTICE file", //
-                " * distributed with this work for additional information", //
-                " * regarding copyright ownership.  The ASF licenses this file", //
-                " * to you under the Apache License, Version 2.0 (the \"License\");", //
-                " * you may not use this file except in compliance with the License.", //
-                " * You may obtain a copy of the License at", //
-                " *    http://www.apache.org/licenses/LICENSE-2.0", //
-                " * Unless required by applicable law or agreed to in writing,", //
-                " * software distributed under the License is distributed on an", //
-                " * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY", //
-                " * KIND, either express or implied.  See the License for the", //
-                " * specific language governing permissions and limitations", //
-                " * under the License.", //
-                " ]]></sample></resource>" };
-
-        final MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/elements/Text.txt"));
-        analyser.analyse(document);
-        reporter.report(document);
-        String result = out.toString();
-        for (String exp : expected) {
-            assertTrue(result.contains(exp), () -> exp);
-        }
-    }
-
-    @Test
-    public void noteTypeAnalyser() throws Exception {
-        final MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/elements/LICENSE"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals("<resource name='src/test/resources/elements/LICENSE' type='NOTICE'/>", out.toString(),
-                "Open note element");
-    }
-
-    @Test
-    public void binaryTypeAnalyser() throws Exception {
-        final MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/elements/Image.png"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals("<resource name='src/test/resources/elements/Image.png' type='BINARY'/>", out.toString(),
-                "Open binary element");
-    }
-
-    @Test
-    public void archiveTypeAnalyser() throws Exception {
-        final MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/elements/dummy.jar"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'/>", out.toString(),
-                "Open archive element");
-    }
-
-    @Test
-    public void archiveTypeAnalyserIntelliJ() throws Exception {
-        final MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/elements/dummy.jar"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'/>", out.toString(),
-                "Open archive element");
-    }
-
-    @Test
-    public void RAT211_bmp_Test() throws Exception {
-        MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/jira/RAT211/side_left.bmp"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals("<resource name='src/test/resources/jira/RAT211/side_left.bmp' type='BINARY'/>", out.toString(),
-                "Open archive element");
-    }
-
-    @Test
-    public void RAT211_dia_Test() throws Exception {
-        MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/jira/RAT211/leader-election-message-arrives.dia"));
-        analyser.analyse(document);
-        reporter.report(document);
-        assertEquals(
-                "<resource name='src/test/resources/jira/RAT211/leader-election-message-arrives.dia' type='ARCHIVE'/>",
-                out.toString(), "Open archive element");
-    }
-
-    @Test
-    public void RAT147_unix_Test() throws Exception {
-        MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/jira/RAT147/unix-newlines.txt.bin"));
-        analyser.analyse(document);
-        reporter.report(document);
-        String result = out.toString();
-        TextUtils.assertPatternInOutput(
-                "<resource name='src/test/resources/jira/RAT147/unix-newlines.txt.bin' type='STANDARD'",
-                result);
-        TextUtils.assertPatternInOutput("sentence 1.$", result);
-        TextUtils.assertPatternInOutput("^sentence 2.$", result);
-        TextUtils.assertPatternInOutput("^sentence 3.$", result);
-        TextUtils.assertPatternInOutput("^sentence 4.$", result);
-    }
-
-    @Test
-    public void RAT147_windows_Test() throws Exception {
-        MonolithicFileDocument document = new MonolithicFileDocument(
-                Resources.getResourceFile("/jira/RAT147/windows-newlines.txt.bin"));
-        analyser.analyse(document);
-        reporter.report(document);
-        String result = out.toString();
-        TextUtils.assertPatternInOutput(
-                "<resource name='src/test/resources/jira/RAT147/windows-newlines.txt.bin' type='STANDARD'",
-                result);
-        TextUtils.assertPatternInOutput("sentence 1.$", result);
-        TextUtils.assertPatternInOutput("^sentence 2.$", result);
-        TextUtils.assertPatternInOutput("^sentence 3.$", result);
-        TextUtils.assertPatternInOutput("^sentence 4.$", result);
-    }
-}
diff --git a/apache-rat-core/src/test/java/org/apache/rat/analysis/DefaultAnalyserFactoryTest.java b/apache-rat-core/src/test/java/org/apache/rat/analysis/DefaultAnalyserFactoryTest.java
new file mode 100644
index 0000000..3070b31
--- /dev/null
+++ b/apache-rat-core/src/test/java/org/apache/rat/analysis/DefaultAnalyserFactoryTest.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ */
+package org.apache.rat.analysis;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.StringWriter;
+
+import org.apache.commons.io.filefilter.FalseFileFilter;
+import org.apache.rat.Defaults;
+import org.apache.rat.ReportConfiguration;
+import org.apache.rat.api.Document;
+import org.apache.rat.document.IDocumentAnalyser;
+import org.apache.rat.document.impl.FileDocument;
+import org.apache.rat.report.claim.impl.xml.SimpleXmlClaimReporter;
+import org.apache.rat.report.xml.writer.impl.base.XmlWriter;
+import org.apache.rat.test.utils.Resources;
+import org.apache.rat.testhelpers.TextUtils;
+import org.apache.rat.utils.DefaultLog;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DefaultAnalyserFactoryTest {
+
+    private StringWriter out;
+    private SimpleXmlClaimReporter reporter;
+    private IDocumentAnalyser analyser;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        out = new StringWriter();
+        reporter = new SimpleXmlClaimReporter(new XmlWriter(out));
+        ReportConfiguration config = new ReportConfiguration(DefaultLog.INSTANCE);
+        config.addLicense(UnknownLicense.INSTANCE);
+        analyser = DefaultAnalyserFactory.createDefaultAnalyser(config);
+    }
+
+    @Test
+    public void standardTypeAnalyser() throws Exception {
+        String[] expected = {
+                "<resource name='src/test/resources/elements/Text.txt' type='STANDARD'>"
+                        + "<license id='?????' name='Unknown license' approval='false' family='?????'/>"
+                        + "<sample><![CDATA[ /*", //
+                " * Licensed to the Apache Software Foundation (ASF) under one", //
+                " * or more contributor license agreements.  See the NOTICE file", //
+                " * distributed with this work for additional information", //
+                " * regarding copyright ownership.  The ASF licenses this file", //
+                " * to you under the Apache License, Version 2.0 (the \"License\");", //
+                " * you may not use this file except in compliance with the License.", //
+                " * You may obtain a copy of the License at", //
+                " *    http://www.apache.org/licenses/LICENSE-2.0", //
+                " * Unless required by applicable law or agreed to in writing,", //
+                " * software distributed under the License is distributed on an", //
+                " * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY", //
+                " * KIND, either express or implied.  See the License for the", //
+                " * specific language governing permissions and limitations", //
+                " * under the License.", //
+                " ]]></sample></resource>" };
+
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/Text.txt"));
+        analyser.analyse(document);
+        reporter.report(document);
+        String result = out.toString();
+        for (String exp : expected) {
+            assertTrue(result.contains(exp), () -> exp);
+        }
+    }
+
+    @Test
+    public void noteTypeAnalyser() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/LICENSE"));
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals("<resource name='src/test/resources/elements/LICENSE' type='NOTICE'/>", out.toString());
+    }
+
+    @Test
+    public void binaryTypeAnalyser() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/Image.png"));
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals("<resource name='src/test/resources/elements/Image.png' type='BINARY'/>", out.toString());
+    }
+
+    @Test
+    public void archiveTypeAnalyserTest() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/dummy.jar"));
+        Defaults defaults = Defaults.builder().build(DefaultLog.INSTANCE);
+        ReportConfiguration config = new ReportConfiguration(DefaultLog.INSTANCE);
+        config.setFrom(defaults);
+        config.setFilesToIgnore(FalseFileFilter.FALSE);
+        analyser = DefaultAnalyserFactory.createDefaultAnalyser(config);
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'/>", out.toString());
+    }
+
+    @Test
+    public void archivesAbsenceTest() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/dummy.jar"));
+        Defaults defaults = Defaults.builder().build(DefaultLog.INSTANCE);
+        ReportConfiguration config = new ReportConfiguration(DefaultLog.INSTANCE);
+        config.setFrom(defaults);
+        config.setFilesToIgnore(FalseFileFilter.FALSE);
+        config.setArchiveProcessing(ReportConfiguration.Processing.ABSENCE);
+        analyser = DefaultAnalyserFactory.createDefaultAnalyser(config);
+        analyser.analyse(document);
+        reporter.report(document);
+        String result = out.toString();
+        TextUtils.assertContains("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'>", out.toString());
+        TextUtils.assertContains("<license id='?????' name='Unknown license' approval='false' family='?????'/>", out.toString());
+        TextUtils.assertContains("<license id='ASL' name='Applied Apache License Version 2.0' approval='false' family='AL   '/>", out.toString());
+    }
+
+    @Test
+    public void archivesPresenceTest() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/dummy.jar"));
+        Defaults defaults = Defaults.builder().build(DefaultLog.INSTANCE);
+        ReportConfiguration config = new ReportConfiguration(DefaultLog.INSTANCE);
+        config.setFrom(defaults);
+        config.setFilesToIgnore(FalseFileFilter.FALSE);
+        config.setArchiveProcessing(ReportConfiguration.Processing.PRESENCE);
+        analyser = DefaultAnalyserFactory.createDefaultAnalyser(config);
+        analyser.analyse(document);
+        reporter.report(document);
+        String result = out.toString();
+        TextUtils.assertContains("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'>", out.toString());
+        TextUtils.assertNotContains("<license id='?????' name='Unknown license' approval='false' family='?????'/>", out.toString());
+        TextUtils.assertContains("<license id='ASL' name='Applied Apache License Version 2.0' approval='false' family='AL   '/>", out.toString());
+    }
+
+    @Test
+    public void archiveTypeAnalyserIntelliJ() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/elements/dummy.jar"));
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals("<resource name='src/test/resources/elements/dummy.jar' type='ARCHIVE'/>", out.toString());
+    }
+
+    @Test
+    public void RAT211_bmp_Test() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/jira/RAT211/side_left.bmp"));
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals("<resource name='src/test/resources/jira/RAT211/side_left.bmp' type='BINARY'/>", out.toString());
+    }
+
+    @Test
+    public void RAT211_dia_Test() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/jira/RAT211/leader-election-message-arrives.dia"));
+        analyser.analyse(document);
+        reporter.report(document);
+        assertEquals(
+                "<resource name='src/test/resources/jira/RAT211/leader-election-message-arrives.dia' type='ARCHIVE'/>",
+                out.toString());
+    }
+
+    @Test
+    public void RAT147_unix_Test() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/jira/RAT147/unix-newlines.txt.bin"));
+        analyser.analyse(document);
+        reporter.report(document);
+        String result = out.toString();
+        TextUtils.assertPatternInTarget(
+                "<resource name='src/test/resources/jira/RAT147/unix-newlines.txt.bin' type='STANDARD'",
+                result);
+        TextUtils.assertPatternInTarget("sentence 1.$", result);
+        TextUtils.assertPatternInTarget("^sentence 2.$", result);
+        TextUtils.assertPatternInTarget("^sentence 3.$", result);
+        TextUtils.assertPatternInTarget("^sentence 4.$", result);
+    }
+
+    @Test
+    public void RAT147_windows_Test() throws Exception {
+        final Document document = new FileDocument(
+                Resources.getResourceFile("/jira/RAT147/windows-newlines.txt.bin"));
+        analyser.analyse(document);
+        reporter.report(document);
+        String result = out.toString();
+        TextUtils.assertPatternInTarget(
+                "<resource name='src/test/resources/jira/RAT147/windows-newlines.txt.bin' type='STANDARD'",
+                result);
+        TextUtils.assertPatternInTarget("sentence 1.$", result);
+        TextUtils.assertPatternInTarget("^sentence 2.$", result);
+        TextUtils.assertPatternInTarget("^sentence 3.$", result);
+        TextUtils.assertPatternInTarget("^sentence 4.$", result);
+    }
+}
diff --git a/apache-rat-core/src/test/java/org/apache/rat/analysis/HeaderCheckWorkerTest.java b/apache-rat-core/src/test/java/org/apache/rat/analysis/HeaderCheckWorkerTest.java
index 8dcfdf3..36e5cdc 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/analysis/HeaderCheckWorkerTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/analysis/HeaderCheckWorkerTest.java
@@ -23,9 +23,9 @@
 import java.util.Arrays;
 
 import org.apache.rat.api.Document;
+import org.apache.rat.testhelpers.TestingDocument;
 import org.apache.rat.license.ILicense;
 import org.apache.rat.testhelpers.TestingLicense;
-import org.apache.rat.testhelpers.TestingLocation;
 import org.junit.jupiter.api.Test;
 
 
@@ -33,7 +33,7 @@
 
     @Test
     public void isFinished() throws Exception {
-        final Document subject = new TestingLocation("subject");
+        final Document subject = new TestingDocument("subject");
         ILicense matcher = new TestingLicense();
         HeaderCheckWorker worker = new HeaderCheckWorker(new StringReader(""), Arrays.asList(matcher), subject);
         worker.read();
diff --git a/apache-rat-core/src/test/java/org/apache/rat/analysis/TikaProcessorTest.java b/apache-rat-core/src/test/java/org/apache/rat/analysis/TikaProcessorTest.java
index aef464d..ad76070 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/analysis/TikaProcessorTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/analysis/TikaProcessorTest.java
@@ -34,9 +34,7 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.MalformedInputException;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -135,13 +133,8 @@
      * @return
      */
     private static Document getDocument(final InputStream stream) {
-        MetaData metaData = new MetaData();
 
-        Document doc = new Document() {
-            @Override
-            public String getName() {
-                return "Testing Document";
-            }
+        Document doc = new Document("Testing Document") {
 
             @Override
             public Reader reader() throws IOException {
@@ -154,13 +147,13 @@
             }
 
             @Override
-            public MetaData getMetaData() {
-                return metaData;
+            public boolean isDirectory() {
+                return false;
             }
 
             @Override
-            public boolean isComposite() {
-                return false;
+            public SortedSet<Document> listChildren() {
+                return Collections.emptySortedSet();
             }
         };
         return doc;
diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/impl/SingularFileDocumentTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/impl/FileDocumentTest.java
similarity index 92%
rename from apache-rat-core/src/test/java/org/apache/rat/document/impl/SingularFileDocumentTest.java
rename to apache-rat-core/src/test/java/org/apache/rat/document/impl/FileDocumentTest.java
index b074e01..bfeba21 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/document/impl/SingularFileDocumentTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/document/impl/FileDocumentTest.java
@@ -30,14 +30,14 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-public class SingularFileDocumentTest {
+public class FileDocumentTest {
     private Document document;
     private File file;
     
     @BeforeEach
     public void setUp() throws Exception {
         file = Resources.getResourceFile("elements/Source.java");
-        document = new MonolithicFileDocument(file);
+        document = new FileDocument(file);
     }
 
     @Test
@@ -52,6 +52,6 @@
     public void getName() {
         final String name = document.getName();
         assertNotNull("Name is set", name);
-        assertEquals(DocumentImplUtils.toName(file), name, "Name is filename");
+        assertEquals(FileDocument.normalizeFileName(file), name, "Name is filename");
     }
 }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/impl/guesser/NoteGuesserTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/impl/guesser/NoteGuesserTest.java
index 82ca98c..2987adf 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/document/impl/guesser/NoteGuesserTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/document/impl/guesser/NoteGuesserTest.java
@@ -18,7 +18,7 @@
  */ 
 package org.apache.rat.document.impl.guesser;
 
-import org.apache.rat.document.MockDocument;
+import org.apache.rat.testhelpers.TestingDocument;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -27,13 +27,13 @@
 
     @Test
     public void testMatches() {
-        assertTrue(NoteGuesser.isNote(new MockDocument("DEPENDENCIES")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("LICENSE")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("LICENSE.txt")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("NOTICE")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("NOTICE.txt")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("README")));
-        assertTrue(NoteGuesser.isNote(new MockDocument("README.txt")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("DEPENDENCIES")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("LICENSE")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("LICENSE.txt")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("NOTICE")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("NOTICE.txt")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("README")));
+        assertTrue(NoteGuesser.isNote(new TestingDocument("README.txt")));
     }
 
     @Test
diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/impl/util/DocumentAnalyserMultiplexerTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/impl/util/DocumentAnalyserMultiplexerTest.java
index 1d4f9c6..743b439 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/document/impl/util/DocumentAnalyserMultiplexerTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/document/impl/util/DocumentAnalyserMultiplexerTest.java
@@ -19,8 +19,8 @@
 package org.apache.rat.document.impl.util;
 
 import org.apache.rat.document.IDocumentAnalyser;
-import org.apache.rat.document.MockDocument;
-import org.apache.rat.document.MockDocumentAnalyser;
+import org.apache.rat.testhelpers.TestingDocument;
+import org.apache.rat.testhelpers.TestingDocumentAnalyser;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -30,30 +30,30 @@
 
     private DocumentAnalyserMultiplexer multiplexer;
     private IDocumentAnalyser[] analysers;
-    private MockDocument document;
+    private TestingDocument document;
     
     @BeforeEach
     public void setUp() {
         IDocumentAnalyser[] analysers = {
-                new MockDocumentAnalyser(), 
-                new MockDocumentAnalyser(),
-                new MockDocumentAnalyser()
+                new TestingDocumentAnalyser(),
+                new TestingDocumentAnalyser(),
+                new TestingDocumentAnalyser()
         };
         this.analysers = analysers;
-        document = new MockDocument();
+        document = new TestingDocument();
         multiplexer = new DocumentAnalyserMultiplexer(analysers);
     }
 
     @Test
     public void testAnalyse() throws Exception {
         multiplexer.analyse(document);
-        MockDocumentAnalyser analyser =  (MockDocumentAnalyser) (analysers[0]);
+        TestingDocumentAnalyser analyser =  (TestingDocumentAnalyser) (analysers[0]);
         assertEquals(1, analyser.matches.size(),"Call made to analyser");
         assertEquals( document, analyser.matches.get(0), "Call made to analyser");
-        analyser =  (MockDocumentAnalyser) (analysers[1]);
+        analyser =  (TestingDocumentAnalyser) (analysers[1]);
         assertEquals(1, analyser.matches.size(), "Call made to analyser");
         assertEquals(document, analyser.matches.get(0), "Call made to analyser");
-        analyser =  (MockDocumentAnalyser) (analysers[2]);
+        analyser =  (TestingDocumentAnalyser) (analysers[2]);
         assertEquals( 1, analyser.matches.size());
         assertEquals( document, analyser.matches.get(0),"Call made to analyser");
     }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/policy/DefaultPolicyTest.java b/apache-rat-core/src/test/java/org/apache/rat/policy/DefaultPolicyTest.java
index cde06b7..250bf51 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/policy/DefaultPolicyTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/policy/DefaultPolicyTest.java
@@ -30,7 +30,7 @@
 import org.apache.rat.license.LicenseFamilySetFactory;
 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
 import org.apache.rat.testhelpers.TestingLicense;
-import org.apache.rat.testhelpers.TestingLocation;
+import org.apache.rat.testhelpers.TestingDocument;
 import org.apache.rat.utils.DefaultLog;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -62,7 +62,7 @@
     public void setUp() throws Exception {
         defaults = Defaults.builder().build(DefaultLog.INSTANCE);
         policy = new DefaultPolicy(defaults.getLicenseFamilies(LicenseFilter.APPROVED));
-        document = new TestingLocation("subject");
+        document = new TestingDocument("subject");
     }
 
     private void assertApproval(boolean pApproved) {
@@ -153,7 +153,7 @@
         Document.Type[] nonStandardDocuments = { Document.Type.NOTICE, Document.Type.ARCHIVE, Document.Type.BINARY };
 
         for (Document.Type d : nonStandardDocuments) {
-            document = new TestingLocation("subject");
+            document = new TestingDocument("subject");
             document.getMetaData().setDocumentType(d);
             policy.analyse(document);
             assertEquals(0, document.getMetaData().licenses().count(), "failed on " + d);
diff --git a/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java b/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
index e386f93..2860499 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
@@ -34,6 +34,7 @@
 import org.apache.rat.Defaults;
 import org.apache.rat.ReportConfiguration;
 import org.apache.rat.api.Document;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.license.ILicense;
 import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.report.RatReport;
@@ -74,8 +75,8 @@
         final ReportConfiguration configuration = new ReportConfiguration(DefaultLog.INSTANCE);
         final TestingLicense testingLicense = new TestingLicense(new TestingMatcher(true), family);
         configuration.setFrom(Defaults.builder().build(DefaultLog.INSTANCE));
-
-        DirectoryWalker directory = new DirectoryWalker(new File(elementsPath), configuration.getFilesToIgnore(), HiddenFileFilter.HIDDEN);
+        configuration.setDirectoriesToIgnore(HiddenFileFilter.HIDDEN);
+        DirectoryWalker directory = new DirectoryWalker(configuration, new FileDocument(new File(elementsPath)));
         final ClaimStatistic statistic = new ClaimStatistic();
 
         configuration.addLicense(testingLicense);
diff --git a/apache-rat-core/src/test/java/org/apache/rat/test/utils/Resources.java b/apache-rat-core/src/test/java/org/apache/rat/test/utils/Resources.java
index 8ebcca1..6229393 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/test/utils/Resources.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/test/utils/Resources.java
@@ -29,7 +29,7 @@
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 
-import org.apache.rat.document.impl.DocumentImplUtils;
+import org.apache.rat.document.impl.FileDocument;
 
 /**
  * Utility class, which provides static methods for creating test cases.
@@ -127,6 +127,6 @@
     public static String getResourceDirectory(String pResource) throws IOException {
         final File resource = getResourceFile(pResource);
         final File dir = resource.getParentFile();
-        return DocumentImplUtils.toName(dir);
+        return FileDocument.normalizeFileName(dir);
     }
 }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/MockDocument.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java
similarity index 74%
rename from apache-rat-core/src/test/java/org/apache/rat/document/MockDocument.java
rename to apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java
index 743aa25..3b77f3e 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/document/MockDocument.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java
@@ -16,33 +16,32 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  */
-package org.apache.rat.document;
+package org.apache.rat.testhelpers;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.Collections;
+import java.util.SortedSet;
 
 import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
 
-public class MockDocument implements Document {
+public class TestingDocument extends Document {
 
     private final Reader reader;
-    private final String name;
-    private final MetaData metaData = new MetaData();
 
-    public MockDocument() {
+    public TestingDocument() {
         this(null, "name");
     }
 
-    public MockDocument(String name) {
+    public TestingDocument(String name) {
         this(null, name);
     }
 
-    public MockDocument(Reader reader, String name) {
-        super();
+    public TestingDocument(Reader reader, String name) {
+        super(name);
         this.reader = reader;
-        this.name = name;
+
     }
 
     @Override
@@ -51,20 +50,16 @@
     }
 
     @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public boolean isComposite() {
+    public boolean isDirectory() {
         return false;
     }
 
     @Override
-    public MetaData getMetaData() {
-        return metaData;
+    public SortedSet<Document> listChildren() {
+        return Collections.emptySortedSet();
     }
 
+
     @Override
     public InputStream inputStream() throws IOException {
         throw new UnsupportedOperationException();
diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/MockDocumentAnalyser.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocumentAnalyser.java
similarity index 86%
rename from apache-rat-core/src/test/java/org/apache/rat/document/MockDocumentAnalyser.java
rename to apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocumentAnalyser.java
index c5b513e..f8c78d4 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/document/MockDocumentAnalyser.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocumentAnalyser.java
@@ -16,19 +16,20 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  */
-package org.apache.rat.document;
+package org.apache.rat.testhelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.rat.api.Document;
+import org.apache.rat.document.IDocumentAnalyser;
 
-public class MockDocumentAnalyser implements IDocumentAnalyser {
+public class TestingDocumentAnalyser implements IDocumentAnalyser {
 
     public final List<Document> matches = new ArrayList<>();
 
     @Override
-    public void analyse(Document document) throws RatDocumentAnalysisException {
+    public void analyse(Document document)  {
         matches.add(document);
     }
 
diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLocation.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLocation.java
deleted file mode 100644
index 0425da8..0000000
--- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLocation.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.testhelpers;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-
-import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
-
-public class TestingLocation implements Document {
-
-    public final String name;
-    public final String url;
-    private final MetaData metaData = new MetaData();
-
-    public TestingLocation() {
-        this("name", "url");
-    }
-
-    public TestingLocation(String name) {
-        this(name, "url");
-    }
-
-    public TestingLocation(String name, String url) {
-        super();
-        this.name = name;
-        this.url = url;
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    public String getURL() {
-        return url;
-    }
-
-    @Override
-    public boolean isComposite() {
-        return false;
-    }
-
-    @Override
-    public Reader reader() throws IOException {
-        throw new UnsupportedOperationException("Opening Reader in TestingLocation");
-    }
-
-    @Override
-    public MetaData getMetaData() {
-        return metaData;
-    }
-
-    @Override
-    public InputStream inputStream() throws IOException {
-        throw new UnsupportedOperationException("Opening inputStream in TestingLocation");
-    }
-}
diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TextUtils.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TextUtils.java
index 41bb196..a88f70f 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TextUtils.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TextUtils.java
@@ -26,23 +26,56 @@
 
 public class TextUtils {
     public static final String[] EMPTY = {};
-    
-    public static void assertPatternInOutput(String pattern, String out) {
+
+    /**
+     * Asserts a regular expression pattern is in a string
+     *
+     * @param pattern the pattern to match.
+     * @param target  the string to match.
+     */
+    public static void assertPatternInTarget(String pattern, String target) {
         assertTrue(
-                isMatching(pattern, out), ()->"Output does not match string: " + pattern+"\n"+out);
-    }
-    
-    public static void assertPatternNotInOutput(String pattern, String out) {
-        assertFalse(
-                isMatching(pattern, out), ()->"Output matches the pattern: " + pattern+"\n"+out);
+                isMatching(pattern, target), () -> "Target does not match string: " + pattern + "\n" + target);
     }
 
-   public static boolean isMatching(final String pattern, final String value) {
-        return Pattern.compile(pattern, Pattern.MULTILINE).matcher(value).find();
+    /**
+     * Asserts a regular expression pattern is not in a string
+     *
+     * @param pattern the pattern to match.
+     * @param target  the string to match.
+     */
+    public static void assertPatternNotInTarget(String pattern, String target) {
+        assertFalse(
+                isMatching(pattern, target), () -> "Target matches the pattern: " + pattern + "\n" + target);
     }
-   
-   public static void find(String pattern, String document) {
-       assertTrue(
-               Pattern.compile(pattern, Pattern.MULTILINE).matcher(document).find(), () ->String.format("Could not find '%s'", pattern));
-   }
+
+    /**
+     * Returns {@code true} if a regular expression pattern is in a string
+     *
+     * @param pattern the pattern to match.
+     * @param target  the string to match.
+     */
+    public static boolean isMatching(final String pattern, final String target) {
+        return Pattern.compile(pattern, Pattern.MULTILINE).matcher(target).find();
+    }
+
+    /**
+     * Asserts that a string is contained within another string.
+     * @param find The string to find.
+     * @param target The string to search.
+     */
+    public static void assertContains(final String find, final String target) {
+        assertTrue(
+                target.contains(find), () -> "Target does not contain the text: " + find + "\n" + target);
+    }
+
+    /**
+     * Asserts that a string is not contained within another string.
+     * @param find The string to find.
+     * @param target The string to search.
+     */
+    public static void assertNotContains(final String find, final String target) {
+        assertFalse(
+                target.contains(find), () -> "Target does contain the text: " + find + "\n" + target);
+    }
 }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/walker/DirectoryWalkerTest.java b/apache-rat-core/src/test/java/org/apache/rat/walker/DirectoryWalkerTest.java
index 0fe276a..5c946e7 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/walker/DirectoryWalkerTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/walker/DirectoryWalkerTest.java
@@ -27,17 +27,23 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.io.filefilter.FalseFileFilter;
+import org.apache.rat.ReportConfiguration;
 import org.apache.rat.api.Document;
 import org.apache.rat.api.RatException;
-import org.apache.rat.document.impl.DocumentImplUtils;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.report.RatReport;
+import org.apache.rat.utils.DefaultLog;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
 public class DirectoryWalkerTest {
 
-	private static File toWalk;
+	private static Document toWalk;
+    private ReportConfiguration reportConfiguration ;
+
 
     private static void fileWriter(File dir, String name, String contents) throws IOException {
         try (FileWriter writer = new FileWriter(new File(dir, name))) {
@@ -45,9 +51,15 @@
             writer.flush();
         }
     }
+
+    @BeforeEach
+    public void beforeEach() {
+        reportConfiguration = new ReportConfiguration(DefaultLog.INSTANCE);
+    }
+
     @BeforeAll
     public static void setUp(@TempDir File dir) throws Exception {
-        toWalk = dir;
+        toWalk = new FileDocument(dir);
         /*
         Create a directory structure like this:
 
@@ -58,26 +70,28 @@
                 regularFile
                 .hiddenFile
          */
-        File regular = new File(toWalk, "regular");
+        File regular = new File(dir, "regular");
         regular.mkdir();
         fileWriter(regular, "regularFile", "regular file");
         fileWriter(regular, ".hiddenFile", "hidden file");
 
-        File hidden = new File(toWalk, ".hidden");
+        File hidden = new File(dir, ".hidden");
         hidden.mkdir();
         fileWriter(hidden, "regularFile", "regular file");
         fileWriter(hidden, ".hiddenFile", "hidden file");
     }
 
     private String expectedName(String name) {
-        return DocumentImplUtils.toName(toWalk)+name;
+        return toWalk.getName()+name;
     }
 
 
     
     @Test
     public void noFiltersTest() throws IOException, RatException {
-        DirectoryWalker walker = new DirectoryWalker(toWalk, null,null);
+        reportConfiguration.setFilesToIgnore(FalseFileFilter.FALSE);
+        reportConfiguration.setDirectoriesToIgnore(FalseFileFilter.FALSE);
+        DirectoryWalker walker = new DirectoryWalker(reportConfiguration, toWalk);
         List<String> scanned = new ArrayList<>();
         walker.run(new TestRatReport(scanned));
         String[] expected = {"/regular/regularFile", "/regular/.hiddenFile", "/.hidden/regularFile", "/.hidden/.hiddenFile"};
@@ -89,7 +103,9 @@
 
     @Test
     public void noHiddenFileFiltersTest() throws IOException, RatException {
-        DirectoryWalker walker = new DirectoryWalker(toWalk, NameBasedHiddenFileFilter.HIDDEN,null);
+        reportConfiguration.setFilesToIgnore(NameBasedHiddenFileFilter.HIDDEN);
+        reportConfiguration.setDirectoriesToIgnore(FalseFileFilter.FALSE);
+        DirectoryWalker walker = new DirectoryWalker(reportConfiguration, toWalk);
         List<String> scanned = new ArrayList<>();
         walker.run(new TestRatReport(scanned));
         String[] expected = {"/regular/regularFile", "/.hidden/regularFile"};
@@ -101,7 +117,9 @@
 
     @Test
     public void noHiddenDirectoryFiltersTest() throws IOException, RatException {
-        DirectoryWalker walker = new DirectoryWalker(toWalk, null, NameBasedHiddenFileFilter.HIDDEN);
+        reportConfiguration.setFilesToIgnore(FalseFileFilter.FALSE);
+        reportConfiguration.setDirectoriesToIgnore(NameBasedHiddenFileFilter.HIDDEN);
+        DirectoryWalker walker = new DirectoryWalker(reportConfiguration, toWalk);
         List<String> scanned = new ArrayList<>();
         walker.run(new TestRatReport(scanned));
         String[] expected = {"/regular/regularFile", "/regular/.hiddenFile"};
@@ -113,7 +131,9 @@
 
     @Test
     public void noHiddenDirectoryAndNoHiddenFileFiltersTest() throws IOException, RatException {
-        DirectoryWalker walker = new DirectoryWalker(toWalk, NameBasedHiddenFileFilter.HIDDEN, NameBasedHiddenFileFilter.HIDDEN);
+        reportConfiguration.setDirectoriesToIgnore(NameBasedHiddenFileFilter.HIDDEN);
+        reportConfiguration.setFilesToIgnore(NameBasedHiddenFileFilter.HIDDEN);
+        DirectoryWalker walker = new DirectoryWalker(reportConfiguration, toWalk);
         List<String> scanned = new ArrayList<>();
         walker.run(new TestRatReport(scanned));
         String[] expected = {"/regular/regularFile"};
diff --git a/apache-rat-core/src/test/java/org/apache/rat/walker/FileNameComparatorTest.java b/apache-rat-core/src/test/java/org/apache/rat/walker/FileNameComparatorTest.java
deleted file mode 100644
index dd1e934..0000000
--- a/apache-rat-core/src/test/java/org/apache/rat/walker/FileNameComparatorTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */ 
-package org.apache.rat.walker;
-
-import org.apache.rat.test.utils.Resources;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class FileNameComparatorTest {
-
-    @Test
-    public void compare() throws IOException {
-        FileNameComparator comparator = new FileNameComparator();
-        assertNotNull(comparator);
-        final int compare = comparator.compare(Resources.getResourceFile("elements/LICENSE"), Resources.getResourceFile("elements/NOTICE"));
-        assertTrue(compare < 0, "LICENSE is before NOTICE");
-    }
-}
diff --git a/apache-rat-plugin/src/main/java/org/apache/rat/mp/AbstractRatMojo.java b/apache-rat-plugin/src/main/java/org/apache/rat/mp/AbstractRatMojo.java
index 2386ed3..bf48681 100644
--- a/apache-rat-plugin/src/main/java/org/apache/rat/mp/AbstractRatMojo.java
+++ b/apache-rat-plugin/src/main/java/org/apache/rat/mp/AbstractRatMojo.java
@@ -346,8 +346,6 @@
             config.setFrom(defaults);
         } else {
             config.setStyleSheet(Defaults.getPlainStyleSheet());
-            config.setDirectoriesToIgnore(Defaults.getDirectoriesToIgnore());
-            config.setFilesToIgnore(Defaults.getFilesToIgnore());
         }
 
         if (additionalLicenseFiles != null) {
diff --git a/apache-rat-plugin/src/test/java/org/apache/rat/mp/RatTestHelpers.java b/apache-rat-plugin/src/test/java/org/apache/rat/mp/RatTestHelpers.java
index f005474..bb4dfc2 100644
--- a/apache-rat-plugin/src/test/java/org/apache/rat/mp/RatTestHelpers.java
+++ b/apache-rat-plugin/src/test/java/org/apache/rat/mp/RatTestHelpers.java
@@ -206,11 +206,11 @@
         List<String> lines = IOUtils.readLines(new FileInputStream(pRatTxtFile), Charsets.UTF_8);
         String document = String.join("\n", lines);
         for (String pattern : in) {
-            TextUtils.assertPatternInOutput(pattern, document);
+            TextUtils.assertPatternInTarget(pattern, document);
         }
 
         for (String pattern : notIn) {
-            TextUtils.assertPatternInOutput(pattern, document);
+            TextUtils.assertPatternInTarget(pattern, document);
         }
     }
     
diff --git a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/ResourceCollectionContainer.java b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/ResourceCollectionContainer.java
index ca474b9..2b2ddcd 100644
--- a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/ResourceCollectionContainer.java
+++ b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/ResourceCollectionContainer.java
@@ -24,11 +24,12 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.SortedSet;
 
 import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
 import org.apache.rat.api.RatException;
-import org.apache.rat.document.impl.DocumentImplUtils;
+import org.apache.rat.document.impl.FileDocument;
 import org.apache.rat.report.IReportable;
 import org.apache.rat.report.RatReport;
 import org.apache.tools.ant.types.Resource;
@@ -57,14 +58,20 @@
         }
     }
 
-    private static class ResourceDocument implements Document {
+    private static class ResourceDocument extends Document {
 
         private final Resource resource;
-        private final MetaData metaData;
+
+        private static String asName(Resource resource) {
+            return resource instanceof FileResource ?
+                    FileDocument.normalizeFileName(((FileResource) resource).getFile())
+                    : resource.getName();
+        }
+
 
         private ResourceDocument(Resource resource) {
+            super(asName(resource));
             this.resource = resource;
-            this.metaData = new MetaData();
         }
 
         @Override
@@ -74,32 +81,22 @@
         }
 
         @Override
-        public String getName() {
-            // TODO: reconsider names
-            String result = null;
+        public boolean isDirectory() {
             if (resource instanceof FileResource) {
                 final FileResource fileResource = (FileResource) resource;
                 final File file = fileResource.getFile();
-                result = DocumentImplUtils.toName(file);
-            } else {
-                result = resource.getName();
-            }
-            return result;
-        }
-
-        @Override
-        public boolean isComposite() {
-            if (resource instanceof FileResource) {
-                final FileResource fileResource = (FileResource) resource;
-                final File file = fileResource.getFile();
-                return DocumentImplUtils.isZip(file);
+                return file.isDirectory();
             }
             return false;
         }
 
         @Override
-        public MetaData getMetaData() {
-            return metaData;
+        public SortedSet<Document> listChildren() {
+            if (resource instanceof FileResource) {
+                final FileResource fileResource = (FileResource) resource;
+                return new FileDocument(fileResource.getFile()).listChildren();
+            }
+            return Collections.emptySortedSet();
         }
 
         @Override
diff --git a/apache-rat/src/site/apt/index.apt.vm b/apache-rat/src/site/apt/index.apt.vm
index 06baa08..93c2414 100644
--- a/apache-rat/src/site/apt/index.apt.vm
+++ b/apache-rat/src/site/apt/index.apt.vm
@@ -78,56 +78,72 @@
 usage: java -jar apache-rat/target/apache-rat-${project.version}.jar
             [options] [DIR|TARBALL]
 
-Available options
- -a                             (deprecated) Add the default license
-                                header to any file with an unknown
-                                license.  Use '-A' or ---addLicense
-                                instead.
- -A,--addLicense                Add the default license header to any file
-                                with an unknown license that is not in the
-                                exclusion list. By default new files will
-                                be created with the license header, to
-                                force the modification of existing files
-                                use the --force option.
- -c,--copyright <arg>           The copyright message to use in the
-                                license headers, usually in the form of
-                                "Copyright 2008 Foo"
- -d,--dir                       Used to indicate source when using
-                                --exclude
- -e,--exclude <expression>      Excludes files matching wildcard
-                                <expression>. Note that --dir is required
-                                when using this parameter. Allows multiple
-                                arguments.
- -E,--exclude-file <fileName>   Excludes files matching regular expression
-                                in <file> Note that --dir is required when
-                                using this parameter.
- -f,--force                     Forces any changes in files to be written
-                                directly to the source files (i.e. new
-                                files are not created).
- -h,--help                      Print help for the RAT command line
-                                interface and exit.
-    --licenses <arg>            File names or URLs for license definitions
-    --list-families <arg>       List the defined license families (default
-                                is none). Valid options are: all,
-                                approved, none.
-    --list-licenses <arg>       List the defined licenses (default is
-                                none). Valid options are: all, approved,
-                                none.
-    --log-level <level>         sets the log level.  Valid options are:
-                                DEBUG, INFO, WARN, ERROR, OFF
-    --no-default-licenses       Ignore default configuration. By default
-                                all approved default licenses are used
- -o,--out <arg>                 Define the output file where to write a
-                                report to (default is System.out).
- -s,--stylesheet <arg>          XSLT stylesheet to use when creating the
-                                report.  Not compatible with -x.  Either
-                                an external xsl file may be specified or
-                                one of the internal named sheets:
-                                plain-rat (default), missing-headers, or
-                                unapproved-licenses
-    --scan-hidden-directories   Scan hidden directories
- -x,--xml                       Output the report in raw XML format.  Not
-                                compatible with -s
+
+====== Available Options ======
+ -a                                   (deprecated) Add the default license header to any file with an unknown license.  Use '-A'
+                                      or ---addLicense instead.
+ -A,--addLicense                      Add the default license header to any file with an unknown license that is not in the
+                                      exclusion list. By default new files will be created with the license header, to force the
+                                      modification of existing files use the --force option.
+    --archive <ProcessingType>        Specifies the level of detail in ARCHIVE reporting. (default is NOTIFICATION)
+ -c,--copyright <arg>                 The copyright message to use in the license headers, usually in the form of "Copyright 2008
+                                      Foo"
+ -d,--dir <DirOrArchive>              (deprecated, use '--') Used to indicate source when using --exclude.
+    --dry-run                         If set do not update the files but generate the reports.
+ -e,--exclude <Expression>            Excludes files matching wildcard <expression>. May be followed by multiple arguments. Note
+                                      that '--' or a following option is required when using this parameter.
+ -E,--exclude-file <FileOrURI>        Excludes files matching regular expression in the input file.
+ -f,--force                           Forces any changes in files to be written directly to the source files (i.e. new files are
+                                      not created).
+ -h,--help                            Print help for the RAT command line interface and exit.
+    --licenses <FileOrURI>            File names or URLs for license definitions
+    --list-families <LicenseFilter>   List the defined license families (default is NONE). Valid options are: ALL, APPROVED, NONE
+    --list-licenses <LicenseFilter>   List the defined licenses (default is NONE). Valid options are: ALL, APPROVED, NONE
+    --log-level <LogLevel>            sets the log level.
+    --no-default-licenses             Ignore default configuration. By default all approved default licenses are used
+ -o,--out <arg>                       Define the output file where to write a report to (default is System.out).
+ -s,--stylesheet <StyleSheet>         XSLT stylesheet to use when creating the report.  Not compatible with -x.  Either an
+                                      external xsl file may be specified or one of the internal named sheets: plain-rat (default),
+                                      missing-headers, or unapproved-licenses
+    --scan-hidden-directories         Scan hidden directories
+ -x,--xml                             Output the report in raw XML format.  Not compatible with -s
+
+====== Argument Types ======
+
+<DirOrArchive>
+      A directory or archive file to scan
+
+<Expression>
+      A wildcard file matching pattern. example: *-test-*.txt
+
+<FileOrURI>
+      A file name or URI
+
+<LicenseFilter>
+      A defined filter for the licenses to include.  Valid values: ALL, APPROVED, NONE.
+
+<LogLevel>
+      The log level to use.  Valid values DEBUG, INFO, WARN, ERROR, OFF.
+
+<ProcessingType>
+      Specifies how to process file types.  Valid values are:
+           NOTIFICATION: List file as present
+           PRESENCE: List any licenses found
+           ABSENCE: List licenses found and any unknown licences
+
+<StyleSheet>
+      Either an external xsl file or maybe one of the internal named sheets.  Internal sheets are:
+           plain-rat: The default style
+           missing-headers: Produces a report of files that are missing headers
+           unapproved-licenses: Produces a report of the files with unapproved licenses.
+
+====== Notes ======
+
+1. Rat highlights possible issues.
+2. Rat reports require interpretation.
+3. Rat often requires some tuning before it runs well against a project.
+4. Rat relies on heuristics: it may miss issues
+
 +------------------------------------------+
 
 ** Styling output
diff --git a/pom.xml b/pom.xml
index e8669cc..f96930b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,6 +110,11 @@
         <version>1.26.1</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-text</artifactId>
+        <version>1.12.0</version>
+      </dependency>
+      <dependency>
         <groupId>org.junit</groupId>
         <artifactId>junit-bom</artifactId>
         <version>5.10.2</version>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7449d6d..f9c6734 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -72,6 +72,16 @@
     </release>
     -->
     <release version="0.17-SNAPSHOT" date="xxxx-yy-zz" description="Current SNAPSHOT - release to be done">
+      <action issue="RAT-372" type="add" dev="claudenw">
+        Added ability to process archive files within a project to look for license files.  This necessitated an addition
+        of a command line option "archive" to limit specify the level of detail in the archive report.  See command line
+        help for more details.  By default, there is no change in the reporting and only the presence of archives are reported.
+
+        This change also marked as deprecated the "-a", "--dir" and command line options.
+
+        This change also marks an architecture change from processing Files to processing Documents in order to facilitate
+        processing nested files in archives.
+      </action>
       <action issue="RAT-314" type="add" dev="pottlinger">
         Add integration test for new default exclude .mvn, that was introduced with v0.16.
       </action>
@@ -90,14 +100,16 @@
       <action issue="RAT-147" type="fix" dev="claudenw">
         Change to detect non UTF-8 text files as text not binary.
       </action>
-      <action issue="RAT-150" type="add" dev="claudenw">
-        Switch to Apache Tika to detect file types.
+
+      <action issue="RAT-150" type="fix" dev="claudenw">
+        Switch to Tika to detect file types.  This will result in more file types being properly categorized and may
+        result in some failures where the scans previously did not fail because we now properly check all text files.
       </action>
       <action issue="RAT-211" type="fix" dev="claudenw">
         Generated rat-output.xml is now well-formed, even if BinaryGuesser fails or there is XML content
         in the sample element.
       </action>
-      <action issue="RAT-368" type="update" dev="claudenw">
+      <action issue="RAT-368" type="remove" dev="claudenw">
         Removed ReportFailedRuntimeException, ReportTransformer, RatReportAnalysisResultException, MimeTyper, ToNameTransformer,
         UnsuitableDocumentException, ReportTransformerTest, and ToNameTransformerTest as they are no longer used in the codebase.
         Note: FullTextMatchingeLicense and SimplePatternBasedLicense will be removed in 0.18.0
@@ -110,12 +122,15 @@
       </action>
       <action issue="RAT-366" type="update" dev="claudenw">
         Switch to processing header matches in one call rather than line by line.
+
+        This change also resulted in the possibility of multiple licenses being detected and reported.  forcing a change in the
+        XML ouptut.  XML schema was developed for the output.
       </action>
       <action issue="RAT-333" type="fix" dev="claudenw">
         Fix if --force option is used executable bit is not set properly on newly created/license-augmented file.
       </action>
       <action issue="RAT-345" type="update" dev="pottlinger" due-to="Niels Basjes">
-        Update gitignore-reader from 1.4.0 to 1.5.1 to fetch changes resulting from fixes of RAT-362.
+        Update gitignore-reader from 1.4.0 to 1.5.1 to fetch changes resulting from fixes of RAT-362.
       </action>
       <action issue="RAT-362" type="fix" dev="pottlinger" due-to="Niels Basjes, Arnout Engelen">
         Gitignore parsing fails when excluded element is part of the current base directory.
@@ -186,28 +201,28 @@
         Fix double output by deleting any existing RAT report before writing a fresh file during plugin runs.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update mavenPluginPluginVersion from 3.10.2 to 3.11.0 and introduce goalPrefix in plugin configuration.
+        Update mavenPluginPluginVersion from 3.10.2 to 3.11.0 and introduce goalPrefix in plugin configuration.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update junit-platform-runner from 1.8.1 to 1.10.1.
+        Update junit-platform-runner from 1.8.1 to 1.10.1.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update junit from 5.10.0 to 5.10.1.
+        Update junit from 5.10.0 to 5.10.1.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update actions/cache from 3.3.2 to 4.0.0.
+        Update actions/cache from 3.3.2 to 4.0.0.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-surefire-plugin from 3.2.3 to 3.2.5.
+        Update maven-surefire-plugin from 3.2.3 to 3.2.5.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-jxr-plugin from 3.3.1 to 3.3.2.
+        Update maven-jxr-plugin from 3.3.1 to 3.3.2.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update slf4j-simple from 2.0.9 to 2.0.11.
+        Update slf4j-simple from 2.0.9 to 2.0.11.
       </action>
       <action issue="RAT-339" type="update" dev="pottlinger" due-to="dependabot">
-        Update assertj-core from 3.24.2 to 3.25.1.
+        Update assertj-core from 3.24.2 to 3.25.1.
       </action>
     </release>
     <release version="0.16" date="2023-12-28" description=
@@ -225,79 +240,79 @@
         Update actions/cache from 3.0.11 to 3.3.2
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update actions/checkout from 3 to 4.
+        Update actions/checkout from 3 to 4.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update mockito-core from 4.7.0 to 4.11.0, newer versions 5.x cannot be applied due to our JDK8-compatibility restriction.
+        Update mockito-core from 4.7.0 to 4.11.0, newer versions 5.x cannot be applied due to our JDK8-compatibility restriction.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update plexus-utils from 3.4.2 to 3.5.1, versions 4.x are for upcoming Maven4 and must not be applied here.
+        Update plexus-utils from 3.4.2 to 3.5.1, versions 4.x are for upcoming Maven4 and must not be applied here.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-plugin-version from 3.6.4 to 3.8.2.
+        Update maven-plugin-version from 3.6.4 to 3.8.2.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update wagon-ssh from 3.5.2 to 3.5.3.
+        Update wagon-ssh from 3.5.2 to 3.5.3.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update Ant from 1.10.12 to 1.10.14.
+        Update Ant from 1.10.12 to 1.10.14.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
         Update ASF parent pom from 27 to 31 and update multiple maven plugin versions implicitly (surefire, release, project-info, enforcer, jxr).
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update doxiaVersion from 1.11.1 to 1.12.0.
+        Update doxiaVersion from 1.11.1 to 1.12.0.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-shared-utils from 3.3.4 to 3.4.2.
+        Update maven-shared-utils from 3.3.4 to 3.4.2.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update org.slf4j:slf4j-simple from 1.7.36 to 2.0.9.
+        Update org.slf4j:slf4j-simple from 1.7.36 to 2.0.9.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update commons-lang3 from 3.5 to 3.14.0.
+        Update commons-lang3 from 3.5 to 3.14.0.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
         Update commons-compress from 1.21 to 1.25.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update commons-io from 2.11.0 to 2.15.1.
+        Update commons-io from 2.11.0 to 2.15.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update commons-cli from 1.5.0 to 1.6.0.
+        Update commons-cli from 1.5.0 to 1.6.0.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-pmd-plugin from 3.18.0 to 3.21.2.
+        Update maven-pmd-plugin from 3.18.0 to 3.21.2.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
         Update maven-dependency-plugin from 3.3.0 to 3.6.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-compiler-plugin from 3.10.1 to 3.12.1.
+        Update maven-compiler-plugin from 3.10.1 to 3.12.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-javadoc-plugin from 3.4.1 to 3.6.3.
+        Update maven-javadoc-plugin from 3.4.1 to 3.6.3.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-         Update maven-release-plugin from 2.5.3 to 3.0.1.
+         Update maven-release-plugin from 2.5.3 to 3.0.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-enforcer-plugin from 3.1.0 to 3.4.1.
+        Update maven-enforcer-plugin from 3.1.0 to 3.4.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update extra-enforcer-rules from 1.6.1 to 1.7.0
+        Update extra-enforcer-rules from 1.6.1 to 1.7.0
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-release-plugin from 2.5.3 to 3.0.1.
+        Update maven-release-plugin from 2.5.3 to 3.0.1.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update animal-sniffer-maven-plugin from 1.22 to 1.23.
+        Update animal-sniffer-maven-plugin from 1.22 to 1.23.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-project-info-reports-plugin from 3.4.1 to 3.5.0.
+        Update maven-project-info-reports-plugin from 3.4.1 to 3.5.0.
       </action>
       <action issue="RAT-311" type="update" dev="pottlinger" due-to="dependabot">
-        Update maven-surefire-plugin from 3.2.2 to 3.2.3.
+        Update maven-surefire-plugin from 3.2.2 to 3.2.3.
       </action>
       <action issue="RAT-326" type="fix" dev="pottlinger">
         Fix existing javadoc build errors and add javadoc generation to existing GithubActions to not introduce build errors via merge requests.
diff --git a/src/site/apt/index.apt.vm b/src/site/apt/index.apt.vm
index 236cab0..a0b40c0 100644
--- a/src/site/apt/index.apt.vm
+++ b/src/site/apt/index.apt.vm
@@ -72,7 +72,9 @@
    
    * Add a new {{{./matcher_def.html}Matcher definition}}. Requires Java expertise.
    
-   * Add a new definition format.  Requires Java expertice as well as expertise with the format.
+   * Add a new definition format.  Requires Java expertise as well as expertise with the format.
+
+   * Write a new {{{./xslt_def.html}output report}}.  Requires XSLT knowledge.
 
 ** Who Develops Rat?
 
@@ -136,6 +138,8 @@
 
  These stylesheets can be specified using options in the command line, Maven or Ant clients.
 
+ In addition, new {{{/xslt_def.html}stylesheets may be developed}} and applied to create new reports.
+
 * Checking Out Rat
 
   Quick start
diff --git a/src/site/apt/xslt_def.apt.vm b/src/site/apt/xslt_def.apt.vm
new file mode 100644
index 0000000..79de4d4
--- /dev/null
+++ b/src/site/apt/xslt_def.apt.vm
@@ -0,0 +1,86 @@
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~   Licensed to the Apache Software Foundation (ASF) under one or more
+~~   contributor license agreements.  See the NOTICE file distributed with
+~~   this work for additional information regarding copyright ownership.
+~~   The ASF licenses this file to You under the Apache License, Version 2.0
+~~   (the "License"); you may not use this file except in compliance with
+~~   the License.  You may obtain a copy of the License at
+~~
+~~       http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~   Unless required by applicable law or agreed to in writing, software
+~~   distributed under the License is distributed on an "AS IS" BASIS,
+~~   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~~   See the License for the specific language governing permissions and
+~~   limitations under the License.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+            --------------------------
+            How to define new Rat reports
+            --------------------------
+
+How to define new Rat reports
+
+ All Rat reports are {{{https://www.w3.org/TR/xslt-30/}XSLT stylesheets}} that transform the XML output.  The {{{https://www.w3.org/TR/xmlschema11-1/}schema}} for the XML output can be found in the
+ {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat-core/src/main/resources/org/apache/rat/rat-report.xsd}
+core resources rat-report.xsd file.}}
+
+ There are three internal reports in the same directory that can also be used as examples:
+
+ * {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat-core/src/main/resources/org/apache/rat/plain-rat.xsl}
+plain-rat.xsl}}: The default report.
+
+ * {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat-core/src/main/resources/org/apache/rat/missing-headers.xsl}
+missing-headers.xsl}}: A report that lists files that do not have headers.
+
+ * {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat-core/src/main/resources/org/apache/rat/unapproved-licenses.xsl}
+unapproved-licenses.xsl}}: A report that lists files with unapproved licenses.
+
+* The Report XML
+
+ The apache-rat sub-module has an example of
+ {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat/src/site/examples/rat-report.txt}the Rat report XML file.}}
+
+** <<<rat-report>>> Element
+
+ The XML document starts with a <<<rat-report>>> element that has a <<<timestamp>>> attribute that contains the date and time the Rat report was
+ created.  The <<<rat-report>>> only contains <<<resource>>> elements.
+
+** <<<resource>>> Element
+
+ There is one <<<resource>>> element for every file that was processed.  The <<<resource>>> may contain <<<license>>> and <<<sample>>> child elements.
+ It has two attributes:
+
+ * <<<name>>>: The name of the file.
+
+ * <<<type>>>: The document type.  This is one of the <<<Document.Type>>> enum values.  The value is always in lower case.  For a complete
+ list see the {{{https://gitbox.apache.org/repos/asf/creadur-rat/blob/master/apache-rat/src/main/java/org/apache/rat/api/Document.java}Document.java}}
+ file for a complete list of document types.
+
+** <<<license>>> Element
+
+ The <<<license>>> element is only found as a child of a <<<resource>>> element and may have a <<<notes>>> child element.  The <<<license>>> element has four elements:
+
+ * <<<approval>>>: Specifies whether the license is approved.  Values are restricted to 'true' and 'false'.
+
+ * <<<family>>>: The license family code.  This is always 5 characters long and may have spaces at the end.
+
+ * <<<id>>>: The license id.  This is often the same as the license family code with trailing spaces removed, but may be other values.
+ It is guaranteed to be unique within licenses.
+
+ * <<<name>>>: The name of the license.  This is the human readable name that was provided when the license was defined.
+
+** <<<sample>>> Element
+
+ The  <<<sample>>> element contains a <<<CDATA>>> block comprising the text that was processed when the <<<resource>>> was processed.
+
+** <<<notes>>> Element
+
+ The <<<notes>>> element contains a <<<CDATA>>> block comprising the text of any notes about the license that were provided when the license was defined.
+
+* How to use the report
+
+ Once an XSLT stylesheet has been developed it can be tested by running Rat and providing the full path to the XSLT stylesheet as the stylesheet
+ argument for the client being used.  See Running Rat from the {{{./apache-rat/index.html}Command line}},
+ {{{./apache-rat-plugin/index.html}Maven}}, or
+ {{{./apache-rat-tasks/index.html}Ant}} for details.