This is an automated email from the ASF dual-hosted git repository.
claude pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git
The following commit(s) were added to refs/heads/master by this push:
new e7397ffe RAT-98: Add matcher set (#430)
e7397ffe is described below
commit e7397ffecdb8704a7139886c87217aaf8dc60bb2
Author: Claude Warren <[email protected]>
AuthorDate: Tue Feb 4 15:29:56 2025 +0100
RAT-98: Add matcher set (#430)
* added working directory
* Added FSInfo to handle file system differences
* fixed pattern match
* added more descriptive failure messages
* added file converter + test
* fixed file list walker
* implemented matcher set in ExclusionProcessor
---------
Co-authored-by: P. Ottlinger <[email protected]>
---
.../rat/config/exclusion/ExclusionProcessor.java | 217 ++++++++++++---------
.../rat/config/exclusion/ExclusionUtils.java | 66 +++++--
.../apache/rat/config/exclusion/MatcherSet.java | 178 +++++++++++++++++
.../rat/config/exclusion/StandardCollection.java | 38 ++--
4 files changed, 384 insertions(+), 115 deletions(-)
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java
index c1de611f..d5b3ebe4 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java
@@ -24,8 +24,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
+import java.util.stream.Collectors;
-import org.apache.rat.config.exclusion.plexus.MatchPatterns;
import org.apache.rat.document.DocumentName;
import org.apache.rat.document.DocumentNameMatcher;
import org.apache.rat.utils.DefaultLog;
@@ -85,10 +85,8 @@ public class ExclusionProcessor {
* @return this
*/
public ExclusionProcessor addIncludedPatterns(final Iterable<String>
patterns) {
- List<String> lst = new ArrayList<>();
- patterns.forEach(lst::add);
- DefaultLog.getInstance().info(format("Including patterns: %s",
String.join(", ", lst)));
- includedPatterns.addAll(lst);
+ DefaultLog.getInstance().info(format("Including patterns: %s",
String.join(", ", patterns)));
+ patterns.forEach(includedPatterns::add);
resetLastMatcher();
return this;
}
@@ -140,10 +138,8 @@ public class ExclusionProcessor {
* @return this
*/
public ExclusionProcessor addExcludedPatterns(final Iterable<String>
patterns) {
- List<String> lst = new ArrayList<>();
- patterns.forEach(lst::add);
- DefaultLog.getInstance().info(format("Excluding patterns: %s",
String.join(", ", lst)));
- excludedPatterns.addAll(lst);
+ DefaultLog.getInstance().info(format("Excluding patterns: %s",
String.join(", ", patterns)));
+ patterns.forEach(excludedPatterns::add);
resetLastMatcher();
return this;
}
@@ -175,22 +171,6 @@ public class ExclusionProcessor {
return this;
}
- /**
- * Adds to lists of qualified file patterns. Non-matching patterns start
with a {@code !}.
- * @param matching the list to put matching file patterns into.
- * @param notMatching the list to put non-matching files patterns into.
- * @param patterns the patterns to match.
- */
- private void segregateList(final Set<String> matching, final Set<String>
notMatching,
- final Iterable<String> patterns) {
- if (patterns.iterator().hasNext()) {
-
ExtendedIterator.create(patterns.iterator()).filter(ExclusionUtils.MATCH_FILTER).forEachRemaining(matching::add);
-
ExtendedIterator.create(patterns.iterator()).filter(ExclusionUtils.NOT_MATCH_FILTER)
- .map(s -> s.substring(1))
- .forEachRemaining(notMatching::add);
- }
- }
-
/**
* Creates a Document name matcher that will return {@code false} on any
* document that is excluded.
@@ -204,84 +184,137 @@ public class ExclusionProcessor {
if (lastMatcher == null || !basedir.equals(lastMatcherBaseDir)) {
lastMatcherBaseDir = basedir;
- final Set<String> incl = new TreeSet<>();
- final Set<String> excl = new TreeSet<>();
- final List<DocumentNameMatcher> inclMatchers = new ArrayList<>();
-
// add the file processors
- for (StandardCollection sc : fileProcessors) {
- ExtendedIterator<FileProcessor> iter = sc.fileProcessor();
- if (iter.hasNext()) {
- iter.forEachRemaining(fp -> {
- segregateList(excl, incl, fp.apply(basedir));
-
fp.customDocumentNameMatchers().forEach(inclMatchers::add);
- });
- } else {
- DefaultLog.getInstance().info(String.format("%s does not
have a fileProcessor.", sc));
- }
- }
+ final List<MatcherSet> matchers = extractFileProcessors(basedir);
+ final MatcherSet.Builder fromCommandLine = new
MatcherSet.Builder();
+ DocumentName.Builder nameBuilder =
DocumentName.builder(basedir).setBaseName(basedir);
+ extractPatterns(nameBuilder, fromCommandLine);
+ extractCollectionPatterns(nameBuilder, fromCommandLine);
+ extractCollectionMatchers(fromCommandLine);
+ extractPaths(fromCommandLine);
+ matchers.add(fromCommandLine.build());
- // add the standard patterns
- segregateList(incl, excl, new
FileProcessor(includedPatterns).apply(basedir));
- segregateList(excl, incl, new
FileProcessor(excludedPatterns).apply(basedir));
+ lastMatcher = MatcherSet.merge(matchers).createMatcher();
+ DefaultLog.getInstance().debug(format("Created matcher set for
%s%n%s", basedir.getName(),
+ lastMatcher));
+ }
+ return lastMatcher;
+ }
- // add the collection patterns
- for (StandardCollection sc : includedCollections) {
- Set<String> patterns = sc.patterns();
- if (patterns.isEmpty()) {
- DefaultLog.getInstance().info(String.format("%s does not
have a defined collection for inclusion.", sc));
- } else {
- segregateList(incl, excl, new
FileProcessor(sc.patterns()).apply(basedir));
- }
- }
- for (StandardCollection sc : excludedCollections) {
- Set<String> patterns = sc.patterns();
- if (patterns.isEmpty()) {
- DefaultLog.getInstance().info(String.format("%s does not
have a defined collection for exclusion.", sc));
- } else {
- segregateList(excl, incl, new
FileProcessor(sc.patterns()).apply(basedir));
- }
- }
+ /**
+ * Extracts the file processors from {@link #fileProcessors}.
+ * @param basedir The directory to base the file processors on.
+ * @return a list of MatcherSets that are created for each {@link
#fileProcessors} entry.
+ */
+ private List<MatcherSet> extractFileProcessors(final DocumentName basedir)
{
+ final List<MatcherSet> fileProcessorList = new ArrayList<>();
+ for (StandardCollection sc : fileProcessors) {
+ final Set<String> names = new HashSet<>();
+ sc.fileProcessor().map(fp -> fp.apply(basedir)).forEachRemaining(n
-> n.forEach(names::add));
+ MatcherSet.Builder builder = new MatcherSet.Builder();
+ Set<String> matching = new HashSet<>();
+ Set<String> notMatching = new HashSet<>();
+ MatcherSet.Builder.segregateList(matching, notMatching, names);
+ builder.addIncluded(basedir.resolve(sc.name()), notMatching);
+ builder.addExcluded(basedir.resolve(sc.name()), matching);
+ fileProcessorList.add(builder.build());
+ }
+ return fileProcessorList;
+ }
- // add the matchers
- ExtendedIterator.create(includedCollections.iterator())
- .map(StandardCollection::staticDocumentNameMatcher)
- .filter(Objects::nonNull)
- .forEachRemaining(inclMatchers::add);
+ /**
+ * Converts the pattern to use the directory separator specified by the
document name and localises it for
+ * exclusion processing.
+ * @param documentName The document name to adjust the pattern against.
+ * @param pattern the pattern.
+ * @return the prepared pattern.
+ */
+ private String preparePattern(final DocumentName documentName, final
String pattern) {
+ return ExclusionUtils.qualifyPattern(documentName,
+ ExclusionUtils.convertSeparator(pattern, "/",
documentName.getDirectorySeparator()));
+ }
- List<DocumentNameMatcher> exclMatchers =
ExtendedIterator.create(excludedCollections.iterator())
- .map(StandardCollection::staticDocumentNameMatcher)
- .filter(Objects::nonNull)
- .addTo(new ArrayList<>());
+ /**
+ * Extracts {@link #includedPatterns} and {@link #excludedPatterns} into
the specified matcherBuilder.
+ * @param nameBuilder The name builder for the pattern. File names are
resolved against the generated name.
+ * @param matcherBuilder the MatcherSet.Builder to add the patterns to.
+ */
+ private void extractPatterns(final DocumentName.Builder nameBuilder, final
MatcherSet.Builder matcherBuilder) {
+ DocumentName name = nameBuilder.setName("Patterns").build();
+ if (!excludedPatterns.isEmpty()) {
+ matcherBuilder.addExcluded(name, excludedPatterns.stream()
+ .map(s -> preparePattern(name, s))
+ .collect(Collectors.toSet()));
+ }
+ if (!includedPatterns.isEmpty()) {
+ matcherBuilder.addIncluded(name, includedPatterns.stream()
+ .map(s -> preparePattern(name,
s)).collect(Collectors.toSet()));
+ }
+ }
- if (!incl.isEmpty()) {
- inclMatchers.add(new DocumentNameMatcher("included patterns",
MatchPatterns.from(basedir.getDirectorySeparator(), incl), basedir));
+ /**
+ * Extracts {@link #includedCollections} and {@link #excludedCollections}
patterns into the specified matcherBuilder.
+ * @param nameBuilder the name builder for the pattern names.
+ * @param matcherBuilder the MatcherSet.Builder to add the collections to.
+ */
+ private void extractCollectionPatterns(final DocumentName.Builder
nameBuilder, final MatcherSet.Builder matcherBuilder) {
+ final Set<String> incl = new TreeSet<>();
+ final Set<String> excl = new TreeSet<>();
+ for (StandardCollection sc : includedCollections) {
+ Set<String> patterns = sc.patterns();
+ if (patterns.isEmpty()) {
+ DefaultLog.getInstance().debug(String.format("%s does not have
a defined collection for inclusion.", sc));
+ } else {
+ MatcherSet.Builder.segregateList(incl, excl, sc.patterns());
}
- if (!excl.isEmpty()) {
- exclMatchers.add(new DocumentNameMatcher("excluded patterns",
MatchPatterns.from(basedir.getDirectorySeparator(), excl), basedir));
+ }
+ for (StandardCollection sc : excludedCollections) {
+ Set<String> patterns = sc.patterns();
+ if (patterns.isEmpty()) {
+ DefaultLog.getInstance().debug(String.format("%s does not have
a defined collection for exclusion.", sc));
+ } else {
+ MatcherSet.Builder.segregateList(excl, incl, sc.patterns());
}
+ }
+ DocumentName name = nameBuilder.setName("Collections").build();
+ matcherBuilder
+ .addExcluded(name, excl.stream().map(s ->
preparePattern(name.getBaseDocumentName(), s)).collect(Collectors.toSet()))
+ .addIncluded(name, incl.stream().map(s ->
preparePattern(name.getBaseDocumentName(), s)).collect(Collectors.toSet()));
+ }
- if (!includedPaths.isEmpty()) {
- for (DocumentNameMatcher matcher : includedPaths) {
- DefaultLog.getInstance().info(format("Including path
matcher %s", matcher));
- inclMatchers.add(matcher);
- }
- }
- if (!excludedPaths.isEmpty()) {
- for (DocumentNameMatcher matcher : excludedPaths) {
- DefaultLog.getInstance().info(format("Excluding path
matcher %s", matcher));
- exclMatchers.add(matcher);
- }
- }
+ /**
+ * Extracts {@link #includedCollections} and {@link #excludedCollections}
matchers into the specified matcherBuilder.
+ * @param matcherBuilder the MatcherSet.Builder to add the collections to.
+ */
+ private void extractCollectionMatchers(final MatcherSet.Builder
matcherBuilder) {
+ ExtendedIterator.create(includedCollections.iterator())
+ .map(StandardCollection::staticDocumentNameMatcher)
+ .filter(Objects::nonNull)
+ .forEachRemaining(matcherBuilder::addIncluded);
- lastMatcher = DocumentNameMatcher.MATCHES_ALL;
- if (!exclMatchers.isEmpty()) {
- lastMatcher =
DocumentNameMatcher.not(DocumentNameMatcher.or(exclMatchers));
- if (!inclMatchers.isEmpty()) {
- lastMatcher =
DocumentNameMatcher.or(DocumentNameMatcher.or(inclMatchers), lastMatcher);
- }
+ ExtendedIterator.create(excludedCollections.iterator())
+ .map(StandardCollection::staticDocumentNameMatcher)
+ .filter(Objects::nonNull)
+ .forEachRemaining(matcherBuilder::addExcluded);
+ }
+
+ /**
+ * Extracts {@link #includedPaths} and {@link #excludedPaths} patterns
into the specified matcherBuilder.
+ * @param matcherBuilder the MatcherSet.Builder to add the collections to.
+ */
+ private void extractPaths(final MatcherSet.Builder matcherBuilder) {
+ if (!includedPaths.isEmpty()) {
+ for (DocumentNameMatcher matcher : includedPaths) {
+ DefaultLog.getInstance().info(format("Including path matcher
%s", matcher));
+ matcherBuilder.addIncluded(matcher);
+ }
+ }
+ if (!excludedPaths.isEmpty()) {
+ for (DocumentNameMatcher matcher : excludedPaths) {
+ DefaultLog.getInstance().info(format("Excluding path matcher
%s", matcher));
+ matcherBuilder.addExcluded(matcher);
}
}
- return lastMatcher;
}
+
}
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionUtils.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionUtils.java
index 7932db4f..ef2bab1b 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionUtils.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionUtils.java
@@ -34,9 +34,13 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang3.StringUtils;
import org.apache.rat.ConfigurationException;
+import org.apache.rat.config.exclusion.plexus.MatchPattern;
+import org.apache.rat.config.exclusion.plexus.SelectorUtils;
import org.apache.rat.document.DocumentName;
import org.apache.rat.document.DocumentNameMatcher;
+import org.apache.rat.utils.DefaultLog;
import org.apache.rat.utils.ExtendedIterator;
+import org.apache.rat.utils.Log;
import static java.lang.String.format;
@@ -48,11 +52,14 @@ public final class ExclusionUtils {
/** The list of comment prefixes that are used to filter comment lines. */
public static final List<String> COMMENT_PREFIXES = Arrays.asList("#",
"##", "//", "/**", "/*");
- /** A predicate that filters out lines that do NOT start with "!" */
- public static final Predicate<String> NOT_MATCH_FILTER = s ->
s.startsWith("!");
+ /** Prefix used to negate a given pattern. */
+ public static final String NEGATION_PREFIX = "!";
- /** A predicate that filters out lines that start with "!" */
- public static final Predicate<String> MATCH_FILTER = s ->
!s.startsWith("!");
+ /** A predicate that filters out lines that do NOT start with {@link
#NEGATION_PREFIX}. */
+ public static final Predicate<String> NOT_MATCH_FILTER = s ->
s.startsWith(NEGATION_PREFIX);
+
+ /** A predicate that filters out lines that start with {@link
#NEGATION_PREFIX}. */
+ public static final Predicate<String> MATCH_FILTER =
NOT_MATCH_FILTER.negate();
private ExclusionUtils() {
// do not instantiate
@@ -112,7 +119,20 @@ public final class ExclusionUtils {
* @return a FileFilter.
*/
public static FileFilter asFileFilter(final DocumentName parent, final
DocumentNameMatcher nameMatcher) {
- return file ->
nameMatcher.matches(DocumentName.builder(file).setBaseName(parent.getBaseName()).build());
+ return file -> {
+ DocumentName candidate =
DocumentName.builder(file).setBaseName(parent.getBaseName()).build();
+ boolean result = nameMatcher.matches(candidate);
+ Log log = DefaultLog.getInstance();
+ if (log.isEnabled(Log.Level.DEBUG)) {
+ log.debug(format("FILTER TEST for %s -> %s", file, result));
+ if (!result) {
+ List< DocumentNameMatcher.DecomposeData> data =
nameMatcher.decompose(candidate);
+ log.debug("Decomposition for " + candidate);
+ data.forEach(log::debug);
+ }
+ }
+ return result;
+ };
}
/**
@@ -171,13 +191,12 @@ public final class ExclusionUtils {
}
/**
- * Returns {@code true} if the file name represents a hidden file.
- * @param f the file to check.
- * @return {@code true} if it is the name of a hidden file.
+ * Returns {@code true} if the filename represents a hidden file
+ * @param fileName the file to check.
+ * @return true if it is the name of a hidden file.
*/
- public static boolean isHidden(final File f) {
- String s = f.getName();
- return s.startsWith(".") && !(s.equals(".") || s.equals(".."));
+ public static boolean isHidden(final String fileName) {
+ return fileName.startsWith(".") && !(fileName.equals(".") ||
fileName.equals(".."));
}
private static void verifyFile(final File file) {
@@ -186,6 +205,31 @@ public final class ExclusionUtils {
}
}
+ /**
+ * Modifies the {@link MatchPattern} formatted {@code pattern} argument by
expanding the pattern and
+ * by adjusting the pattern to include the basename from the {@code
documentName} argument.
+ * @param documentName the name of the file being read.
+ * @param pattern the pattern to format.
+ * @return the completely formatted pattern
+ */
+ public static String qualifyPattern(final DocumentName documentName, final
String pattern) {
+ boolean prefix = pattern.startsWith(NEGATION_PREFIX);
+ String workingPattern = prefix ? pattern.substring(1) : pattern;
+ String normalizedPattern =
SelectorUtils.extractPattern(workingPattern,
documentName.getDirectorySeparator());
+
+ StringBuilder sb = new StringBuilder(prefix ? NEGATION_PREFIX : "");
+ if (SelectorUtils.isRegexPrefixedPattern(workingPattern)) {
+ sb.append(SelectorUtils.REGEX_HANDLER_PREFIX)
+ .append("\\Q").append(documentName.getBaseName())
+ .append(documentName.getDirectorySeparator())
+ .append("\\E").append(normalizedPattern)
+ .append(SelectorUtils.PATTERN_HANDLER_SUFFIX);
+ } else {
+
sb.append(documentName.getBaseDocumentName().resolve(normalizedPattern).getName());
+ }
+ return sb.toString();
+ }
+
/**
* Tokenizes the string based on the directory separator.
* @param source the source to tokenize.
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/MatcherSet.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/MatcherSet.java
new file mode 100644
index 00000000..5224fa6f
--- /dev/null
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/MatcherSet.java
@@ -0,0 +1,178 @@
+/*
+ * 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.config.exclusion;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import org.apache.rat.config.exclusion.plexus.MatchPattern;
+import org.apache.rat.config.exclusion.plexus.MatchPatterns;
+import org.apache.rat.config.exclusion.plexus.SelectorUtils;
+import org.apache.rat.document.DocumentName;
+import org.apache.rat.document.DocumentNameMatcher;
+
+import static org.apache.rat.document.DocumentNameMatcher.MATCHES_NONE;
+
+/**
+ * The file processor reads the file specified in the DocumentName.
+ * It must return a list of fully qualified strings for the {@link
MatchPattern} to process.
+ * It may return either Ant or Regex style strings, or a mixture of both.
+ * See {@link SelectorUtils} for a description of the formats.
+ * It may also generate custom DocumentNameMatchers which are added to the
customMatchers instance variable.
+ */
+public interface MatcherSet {
+ Optional<DocumentNameMatcher> includes();
+ Optional<DocumentNameMatcher> excludes();
+
+ default String getDescription() {
+ return String.format("MatcherSet: include [%s] exclude [%s]",
includes().orElse(MATCHES_NONE), excludes().orElse(MATCHES_NONE));
+ }
+ /**
+ * Creates a DocumentNameMatcher from an iterable of matcher sets.
+ * @return A DocumentNameMatcher that processes the matcher sets.
+ */
+ default DocumentNameMatcher createMatcher() {
+ return DocumentNameMatcher.matcherSet(includes().orElse(MATCHES_NONE),
excludes().orElse(MATCHES_NONE));
+ }
+
+ static MatcherSet merge(List<MatcherSet> matcherSets) {
+ Builder builder = new Builder();
+ for (MatcherSet matcherSet : matcherSets) {
+ matcherSet.includes().ifPresent(builder::addIncluded);
+ matcherSet.excludes().ifPresent(builder::addExcluded);
+ }
+ return builder.build();
+ }
+
+ /**
+ * A MatcherSet that assumes the files contain the already formatted
strings and just need to be
+ * localised for the fileName. When {@link #build()} is called the builder
is reset to the initial state.
+ */
+ class Builder {
+
+ /**
+ * Adds to lists of qualified file patterns. Non-matching patterns
start with a {@link ExclusionUtils#NEGATION_PREFIX}.
+ * @param matching the list to put matching file patterns into.
+ * @param notMatching the list to put non-matching file patterns into.
+ * @param patterns the patterns to match.
+ */
+ public static void segregateList(final Set<String> matching, final
Set<String> notMatching,
+ final Iterable<String> patterns) {
+ patterns.forEach(s -> {
+ if (ExclusionUtils.MATCH_FILTER.test(s)) {
+ matching.add(s);
+ } else {
+ notMatching.add(s.substring(1));
+ }
+ });
+ }
+
+ /** The DocumentNameMatcher that specifies included files */
+ protected DocumentNameMatcher included;
+ /** The DocumentNameMatcher that specifies excluded files */
+ protected DocumentNameMatcher excluded;
+
+ public Builder() {
+ }
+
+ /**
+ * Converts a collection names into DocumentNameMatchers that use the
{@code fromDocument} directory separator.
+ * @param dest the consumer to accept the DocumentNameMatcher.
+ * @param nameFormat the format for the matcher names. Requires '%s'
for the {@code fromDocument} localised name.
+ * @param fromDocument the document that the patterns are associated
with.
+ * @param names the list of patterns. If empty no action is taken.
+ */
+ private void processNames(final Consumer<DocumentNameMatcher> dest,
final String nameFormat, final DocumentName fromDocument, final Set<String>
names) {
+ if (!names.isEmpty()) {
+ String name = String.format(nameFormat,
fromDocument.localized("/").substring(1));
+ dest.accept(new DocumentNameMatcher(name,
MatchPatterns.from(fromDocument.getDirectorySeparator(), names),
fromDocument.getBaseDocumentName()));
+ }
+ }
+
+ /**
+ * Adds included file names from the specified document. File names
are resolved relative to the directory
+ * of the {@code fromDocument}.
+ * @param fromDocument the document the names were read from.
+ * @param names the names that were read from the document. Must use
the separator specified by {@code fromDocument}.
+ * @return this
+ */
+ public Builder addIncluded(final DocumentName fromDocument, final
Set<String> names) {
+ processNames(this::addIncluded, "'included %s'", fromDocument,
names);
+ return this;
+ }
+
+ /**
+ * Adds excluded file names from the specified document. File names
are resolved relative to the directory
+ * of the {@code fromDocument}.
+ * @param fromDocument the document the names were read from.
+ * @param names the names that were read from the document. Must use
the separator specified by {@code fromDocument}.
+ * @return this
+ */
+ public Builder addExcluded(final DocumentName fromDocument, final
Set<String> names) {
+ processNames(this::addExcluded, "'excluded %s'", fromDocument,
names);
+ return this;
+ }
+
+ /**
+ * Adds specified DocumentNameMatcher to the included matchers.
+ * @param matcher A document name matcher to add to the included set.
+ * @return this
+ */
+ public Builder addIncluded(final DocumentNameMatcher matcher) {
+ this.included = this.included == null ? matcher :
DocumentNameMatcher.or(this.included, matcher);
+ return this;
+ }
+
+ /**
+ * Adds specified DocumentNameMatcher to the excluded matchers.
+ * @param matcher A document name matcher to add to the excluded set.
+ * @return this
+ */
+ public Builder addExcluded(final DocumentNameMatcher matcher) {
+ this.excluded = this.excluded == null ? matcher :
DocumentNameMatcher.or(this.excluded, matcher);
+ return this;
+ }
+
+ /**
+ * Builds a MatcherSet. When {@code build()} is called the builder is
reset to the initial state.
+ * @return the MatcherSet based upon the included and excluded
matchers.
+ */
+ public MatcherSet build() {
+ MatcherSet result = new MatcherSet() {
+ private final DocumentNameMatcher myIncluded = included;
+ private final DocumentNameMatcher myExcluded = excluded;
+
+ @Override
+ public Optional<DocumentNameMatcher> includes() {
+ return Optional.ofNullable(myIncluded);
+ }
+
+ @Override
+ public Optional<DocumentNameMatcher> excludes() {
+ return Optional.ofNullable(myExcluded);
+ }
+ };
+ included = null;
+ excluded = null;
+ return result;
+ }
+ }
+}
diff --git
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
index 1bd805f4..d2c59707 100644
---
a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
+++
b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/StandardCollection.java
@@ -96,24 +96,38 @@ public enum StandardCollection {
new GitFileProcessor()
),
/**
- * The hidden directories. Directories with names that start with '.'
+ * The hidden directories. Directories with names that start with {@code .}
*/
HIDDEN_DIR("The hidden directories. Directories with names that start with
'.'",
null,
- new DocumentNameMatcher("HIDDEN_DIR", (Predicate<DocumentName>)
documentName -> {
- File f = new File(documentName.getName());
- return f.isDirectory() && ExclusionUtils.isHidden(f);
- }), null
+ new DocumentNameMatcher("HIDDEN_DIR", new
Predicate<DocumentName>() {
+ @Override
+ public boolean test(final DocumentName documentName) {
+ File file = documentName.asFile();
+ return file.isDirectory() &&
ExclusionUtils.isHidden(documentName.getShortName());
+ }
+ @Override
+ public String toString() {
+ return "HIDDEN_DIR";
+ }
+ }), null
),
/**
- * The hidden files. Directories with names that start with '.'
+ * The hidden files. Directories with names that start with {@code .}
*/
HIDDEN_FILE("The hidden files. Directories with names that start with '.'",
null,
- new DocumentNameMatcher("HIDDEN_FILE", (Predicate<DocumentName>)
documentName -> {
- File f = new File(documentName.getName());
- return f.isFile() && ExclusionUtils.isHidden(f);
- }), null
+ new DocumentNameMatcher("HIDDEN_FILE", new
Predicate<DocumentName>() {
+ @Override
+ public boolean test(final DocumentName documentName) {
+ File file = documentName.asFile();
+ return file.isFile() &&
ExclusionUtils.isHidden(documentName.getShortName());
+ }
+ @Override
+ public String toString() {
+ return "HIDDEN_FILE";
+ }
+ }), null
),
/**
* The files and directories created by an IDEA IDE based tool.
@@ -121,9 +135,9 @@ public enum StandardCollection {
IDEA("The files and directories created by an IDEA IDE based tool.",
Arrays.asList("**/*.iml", "**/*.ipr", "**/*.iws", "**/.idea/**"),
null, null),
/**
- * The .DS_Store files MAC computer.
+ * The .DS_Store files on Mac computers.
*/
- MAC("The .DS_Store files MAC computer.",
+ MAC("The .DS_Store files Mac computers.",
Collections.singletonList("**/.DS_Store"), null, null),
/**
* The files and directories created by Maven build system based project.