From 4c3862654a8888a85636acc38b0a2635babc6f35 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 12 Jan 2025 16:01:43 +0000 Subject: [PATCH] Added FSInfo to handle file system differences --- apache-rat-core/pom.xml | 5 + .../it/java/org/apache/rat/ReportTest.java | 2 +- .../rat/config/exclusion/ExclusionUtils.java | 14 + .../apache/rat/document/ArchiveEntryName.java | 74 +++ .../org/apache/rat/document/DocumentName.java | 553 ++++++++++++------ .../org/apache/rat/walker/ArchiveWalker.java | 15 +- .../org/apache/rat/OptionCollectionTest.java | 12 +- .../analysis/DefaultAnalyserFactoryTest.java | 4 +- .../config/exclusion/FileProcessorTest.java | 3 +- .../apache/rat/document/DocumentNameTest.java | 325 +++++++--- .../org/apache/rat/document/FSInfoTest.java | 60 ++ .../rat/document/guesser/NoteGuesserTest.java | 7 +- .../rat/testhelpers/TestingDocument.java | 4 +- pom.xml | 10 +- 14 files changed, 798 insertions(+), 290 deletions(-) create mode 100644 apache-rat-core/src/main/java/org/apache/rat/document/ArchiveEntryName.java create mode 100644 apache-rat-core/src/test/java/org/apache/rat/document/FSInfoTest.java diff --git a/apache-rat-core/pom.xml b/apache-rat-core/pom.xml index 8ac00d1e3..844a7efc5 100644 --- a/apache-rat-core/pom.xml +++ b/apache-rat-core/pom.xml @@ -281,5 +281,10 @@ groovy-all test + + com.google.jimfs + jimfs + test + diff --git a/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java b/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java index b58c6732b..16bc908bc 100644 --- a/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java +++ b/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java @@ -163,7 +163,7 @@ public static Stream args() throws RatException { @Override public void report(Document document) { if (!document.isIgnored()) { - String[] tokens = document.getName().tokenize(document.getName().localized()); + String[] tokens = DocumentName.FSInfo.getDefault().tokenize(document.getName().localized()); results.add(Arguments.of(tokens[1], document)); } } 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 339ce0978..3f082336a 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 @@ -185,4 +185,18 @@ private static void verifyFile(final File file) { throw new ConfigurationException(format("%s is not a valid file.", file)); } } + + /** + * Tokenizes the string based on the directory separator. + * @param source the source to tokenize + * @param from the directory separator for the source. + * @param to the directory separator for the result. + * @return the source string with the separators converted. + */ + public static String convertSeparator(final String source, final String from, final String to) { + if (StringUtils.isEmpty(source) || from.equals(to)) { + return source; + } + return String.join(to, source.split("\\Q" + from + "\\E")); + } } diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/ArchiveEntryName.java b/apache-rat-core/src/main/java/org/apache/rat/document/ArchiveEntryName.java new file mode 100644 index 000000000..889a7b165 --- /dev/null +++ b/apache-rat-core/src/main/java/org/apache/rat/document/ArchiveEntryName.java @@ -0,0 +1,74 @@ +/* + * 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; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; + +public class ArchiveEntryName extends DocumentName { + /** Then name of the document that contains this entry */ + private final DocumentName archiveFileName; + + private static DocumentName.Builder prepareBuilder(final DocumentName archiveFileName, final String archiveEntryName) { + String root = archiveFileName.getName() + "#"; + FSInfo fsInfo = new FSInfo("archiveEntry", "/", true, Collections.singletonList(root)); + return DocumentName.builder(fsInfo) + .setRoot(root) + .setBaseName(root + "/") + .setName(archiveEntryName); + } + public ArchiveEntryName(final DocumentName archiveFileName, final String archiveEntryName) { + super(prepareBuilder(archiveFileName, archiveEntryName)); + this.archiveFileName = archiveFileName; + } + + @Override + public File asFile() { + return archiveFileName.asFile(); + } + + @Override + public Path asPath() { + return Paths.get(archiveFileName.asPath().toString(), "#", super.asPath().toString()); + } + + @Override + public DocumentName resolve(final String child) { + return new ArchiveEntryName(this.archiveFileName, super.resolve(child).localized()); + } + + @Override + public String getBaseName() { + return archiveFileName.getName() + "#"; + } + + @Override + boolean startsWithRootOrSeparator(final String candidate, final String root, final String separator) { + return super.startsWithRootOrSeparator(candidate, root, separator); + } + + @Override + public String localized(final String dirSeparator) { + String superLocal = super.localized(dirSeparator); + superLocal = superLocal.substring(superLocal.lastIndexOf("#") + 1); + return archiveFileName.localized(dirSeparator) + "#" + superLocal; + } +} diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/DocumentName.java b/apache-rat-core/src/main/java/org/apache/rat/document/DocumentName.java index e71bed85b..9b0c6a0d9 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/document/DocumentName.java +++ b/apache-rat-core/src/main/java/org/apache/rat/document/DocumentName.java @@ -20,19 +20,24 @@ import java.io.File; import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Set; +import java.util.Optional; +import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.CompareToBuilder; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -import org.apache.rat.utils.DefaultLog; /** * The name for a document. The {@code DocumentName} is an immutable structure that handles all the intricacies of file @@ -51,66 +56,85 @@ * within an archive. When representing a file in an archive the baseName is the name of the enclosing archive document. *

*/ -public final class DocumentName implements Comparable { - /** The list of all roots on the file system. */ - static final Set ROOTS = new HashSet<>(); - /** {@code True} if the file system on which we are operating is case-sensitive. */ - public static final boolean FS_IS_CASE_SENSITIVE; +public class DocumentName implements Comparable { /** The full name for the document. */ private final String name; /** The name of the base directory for the document. */ - private final String baseName; - /** The directory separator for this document. */ - private final String dirSeparator; - /** The case-sensitive flag */ - private final boolean isCaseSensitive; + //private final String baseName; + private final DocumentName baseName; + /** The file system info for this document. */ + private final FSInfo fsInfo; /** The root for the DocumentName. May be empty but not null. */ private final String root; - // determine the case sensitivity of the file system we are operating on. - static { - boolean fsSensitive = true; - File f = null; + /** + * Determines if the file system is case-sensitive. + * @param fileSystem the file system to check + * @return {@code true} if the file system is case-sensitive. + */ + private static boolean isCaseSensitive(final FileSystem fileSystem) { + boolean isCaseSensitive = false; + Path nameSet = null; + Path filea = null; + Path fileA = null; try { - Path p = Files.createTempDirectory("NameSet"); - f = p.toFile(); - fsSensitive = !new File(f, "a").equals(new File(f, "A")); - } catch (IOException e) { - fsSensitive = true; - } finally { - if (f != null) { - try { - FileUtils.deleteDirectory(f); - } catch (IOException e) { - DefaultLog.getInstance().warn("Unable to delete temporary directory: " + f, e); + try { + Path root = fileSystem.getPath(""); + nameSet = Files.createTempDirectory(root, "NameSet"); + filea = nameSet.resolve("a"); + fileA = nameSet.resolve("A"); + Files.createFile(filea); + Files.createFile(fileA); + isCaseSensitive = true; + } catch (IOException e) { + // do nothing + } finally { + if (filea != null) { + Files.deleteIfExists(filea); + } + if (fileA != null) { + Files.deleteIfExists(fileA); + } + if (nameSet != null) { + Files.deleteIfExists(nameSet); } } + } catch (IOException e) { + // do nothing. } - FS_IS_CASE_SENSITIVE = fsSensitive; - - // determine all the roots on the file system(s). - File[] roots = File.listRoots(); - if (roots != null) { - for (File root : roots) { - String name = root.getPath(); - ROOTS.add(name); - } - } - + return isCaseSensitive; } /** - * Creates a Builder with directory separator and case sensitivity based on the local file system. + * Creates a Builder with the default File system info. * @return the Builder. + * @see FSInfo */ public static Builder builder() { - return new Builder(); + return new Builder(FSInfo.getDefault()); + } + + /** + * Creates a builder with the specified FSInfo instance. + * @param fsInfo the FSInfo to use for the builder. + * @return a new builder. + */ + public static Builder builder(final FSInfo fsInfo) { + return new Builder(fsInfo); + } + + /** + * Creates a builder for the specified file system. + * @param fileSystem the file system to create ethe builder on. + * @return a new builder. + */ + public static Builder builder(final FileSystem fileSystem) { + return new Builder(fileSystem); } /** * Creates a builder from a File. The {@link #baseName} is set to the file name if it is a directory otherwise - * it is set to the directory containing the file. The {@link #dirSeparator} is set from the file and - * case sensitivity based on the local file system. + * it is set to the directory containing the file. * @param file The file to set defaults from. * @return the Builder. */ @@ -131,26 +155,49 @@ public static Builder builder(final DocumentName documentName) { * Builds the DocumentName from the builder. * @param builder the builder to provide the values. */ - private DocumentName(final Builder builder) { + DocumentName(final Builder builder) { this.name = builder.name; - this.baseName = builder.baseName; - this.dirSeparator = builder.dirSeparator; - this.isCaseSensitive = builder.isCaseSensitive; + this.fsInfo = builder.fsInfo; this.root = builder.root; + this.baseName = builder.sameNameFlag ? this : builder.baseName; + } + + /** + * Creates a file from the document name. + * @return a new File object. + */ + public File asFile() { + return new File(getName()); + } + + /** + * Creates a path from the document name. + * @return an new Path object. + */ + public Path asPath() { + return Paths.get(name); } /** - * Creates a new DocumentName by adding the child to the current name. + * Creates a new DocumentName by adding the child to the current name. Resulting documentName will + * have the same base name. * @param child the child to add (must use directory separator from this document name). - * @return the new document name with the same {@link #baseName}, {@link #dirSeparator} and case sensitivity as + * @return the new document name with the same {@link #baseName}, directory sensitivity and case sensitivity as * this one. */ public DocumentName resolve(final String child) { - List parts = new ArrayList<>(); - parts.addAll(Arrays.asList(tokenize(name))); - parts.addAll(Arrays.asList(tokenize(child))); - String newName = String.join(dirSeparator, parts); - return new Builder(this).setName(newName).build(); + if (StringUtils.isBlank(child)) { + return this; + } + String separator = fsInfo.dirSeparator(); + String pattern = separator.equals("/") ? child.replace('\\', '/') : + child.replace('/', '\\'); + + if (!pattern.startsWith(separator)) { + pattern = name + separator + pattern; + } + + return new Builder(this).setName(pattern).build(); } /** @@ -158,7 +205,7 @@ public DocumentName resolve(final String child) { * @return the fully qualified name of the document. */ public String getName() { - return root + dirSeparator + name; + return root + fsInfo.dirSeparator() + name; } /** @@ -166,7 +213,7 @@ public String getName() { * @return the fully qualified basename of the document. */ public String getBaseName() { - return root + dirSeparator + baseName; + return baseName.getName(); } /** @@ -182,7 +229,7 @@ public String getRoot() { * @return the DocumentName for the basename of this document name. */ public DocumentName getBaseDocumentName() { - return name.equals(baseName) ? this : builder(this).setName(baseName).build(); + return baseName; } /** @@ -190,7 +237,25 @@ public DocumentName getBaseDocumentName() { * @return the directory separator. */ public String getDirectorySeparator() { - return dirSeparator; + return fsInfo.dirSeparator(); + } + + /** + * Determines if the candidate starts with the root or separator strings. + * @param candidate the candidate ot check. If blank method will return false. + * @param root the root to check. If blank the root check is skipped. + * @param separator the separator to check. If blank the check is skipped. + * @return true if either the root or separator check returned true. + */ + boolean startsWithRootOrSeparator(final String candidate, final String root, final String separator) { + if (StringUtils.isBlank(candidate)) { + return false; + } + boolean result = !StringUtils.isBlank(root) && candidate.startsWith(root); + if (!result) { + result = !StringUtils.isBlank(separator) && candidate.startsWith(separator); + } + return result; } /** @@ -199,12 +264,13 @@ public String getDirectorySeparator() { * @return the portion of the name that is not part of the base name. */ public String localized() { - String result = name; - if (result.startsWith(baseName)) { - result = result.substring(baseName.length()); + String result = getName(); + String baseNameStr = baseName.getName(); + if (result.startsWith(baseNameStr)) { + result = result.substring(baseNameStr.length()); } - if (!result.startsWith(dirSeparator)) { - result = dirSeparator + result; + if (!startsWithRootOrSeparator(result, getRoot(), fsInfo.dirSeparator())) { + result = fsInfo.dirSeparator() + result; } return result; } @@ -216,24 +282,26 @@ public String localized() { * @return the portion of the name that is not part of the base name. */ public String localized(final String dirSeparator) { - return String.join(dirSeparator, tokenize(localized())); - } + String[] tokens = fsInfo.tokenize(localized()); + if (tokens.length == 0) { + return dirSeparator; + } + if (tokens.length == 1) { + return dirSeparator + tokens[0]; + } - /** - * Tokenizes the string based on the {@link #dirSeparator} of this DocumentName. - * @param source the source to tokenize - * @return the array of tokenized strings. - */ - public String[] tokenize(final String source) { - return source.split("\\Q" + dirSeparator + "\\E"); + String modifiedRoot = dirSeparator.equals("/") ? root.replace('\\', '/') : + root.replace('/', '\\'); + String result = String.join(dirSeparator, tokens); + return startsWithRootOrSeparator(result, modifiedRoot, dirSeparator) ? result : dirSeparator + result; } /** - * Gets the last segment of the name. This is the part after the last {@link #dirSeparator}.. + * Gets the last segment of the name. This is the part after the last directory separator. * @return the last segment of the name. */ public String getShortName() { - int pos = name.lastIndexOf(dirSeparator); + int pos = name.lastIndexOf(fsInfo.dirSeparator()); return pos == -1 ? name : name.substring(pos + 1); } @@ -242,7 +310,7 @@ public String getShortName() { * @return {@code true} if the name is case-sensitive. */ public boolean isCaseSensitive() { - return isCaseSensitive; + return fsInfo.isCaseSensitive(); } /** @@ -255,28 +323,158 @@ public String toString() { } @Override - public int compareTo(final DocumentName o) { - return FS_IS_CASE_SENSITIVE ? name.compareTo(o.name) : name.compareToIgnoreCase(o.name); + public int compareTo(final DocumentName other) { + return CompareToBuilder.reflectionCompare(this, other); } @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DocumentName that = (DocumentName) o; - if (isCaseSensitive() == that.isCaseSensitive() && Objects.equals(dirSeparator, that.dirSeparator)) { - return isCaseSensitive ? name.equalsIgnoreCase(that.name) : name.equals(that.name); - } - return false; + public boolean equals(final Object other) { + return EqualsBuilder.reflectionEquals(this, other); } @Override public int hashCode() { - return Objects.hash(name, dirSeparator, isCaseSensitive()); + return HashCodeBuilder.reflectionHashCode(this); + } + + /** + * The File system information needed to process document names. + */ + public static class FSInfo implements Comparable { + /** The common name for the file system this Info represents */ + private final String name; + /** The separator between directory names */ + private final String separator; + /** The case-sensitivity flag. */ + private final boolean isCaseSensitive; + /** The list of roots for the file system */ + private final List roots; + + public static FSInfo getDefault() { + FSInfo result = (FSInfo) System.getProperties().get("FSInfo"); + return result == null ? + new FSInfo("default", FileSystems.getDefault()) + : result; + } + /** + * Constructor. Extracts the necessary data from the file system. + * @param fileSystem the file system to extract data from. + */ + public FSInfo(final FileSystem fileSystem) { + this("anon", fileSystem); + } + + /** + * Constructor. Extracts the necessary data from the file system. + * @param fileSystem the file system to extract data from. + */ + public FSInfo(final String name, final FileSystem fileSystem) { + this.name = name; + this.separator = fileSystem.getSeparator(); + this.isCaseSensitive = DocumentName.isCaseSensitive(fileSystem); + roots = new ArrayList<>(); + fileSystem.getRootDirectories().forEach(r -> roots.add(r.toString())); + } + + /** + * Gets the common name for the underlying file system. + * @return the common file system name. + */ + @Override + public String toString() { + return name; + } + /** + * Constructor for virtual/abstract file systems for example the entry names within an an archive. + * @param separator the separator string to use. + * @param isCaseSensitive the case-sensitivity flag. + * @param roots the roots for the file system. + */ + FSInfo(final String name, final String separator, final boolean isCaseSensitive, final List roots) { + this.name = name; + this.separator = separator; + this.isCaseSensitive = isCaseSensitive; + this.roots = new ArrayList<>(roots); + } + + /** + * Gets the directory separator. + * @return The directory separator. + */ + public String dirSeparator() { + return separator; + } + + /** + * Gets the case-sensitivity flag. + * @return the case-sensitivity flag. + */ + public boolean isCaseSensitive() { + return isCaseSensitive; + } + + /** + * Retrieves the root extracted from the name. + * @param name the name to extract the root from + * @return an optional containing the root or empty. + */ + public Optional rootFor(final String name) { + for (String sysRoot : roots) { + if (name.startsWith(sysRoot)) { + return Optional.of(sysRoot); + } + } + return Optional.empty(); + } + + /** + * Tokenizes the string based on the directory separator of this DocumentName. + * @param source the source to tokenize + * @return the array of tokenized strings. + */ + public String[] tokenize(final String source) { + return source.split("\\Q" + dirSeparator() + "\\E"); + } + + /** + * Removes "." and ".." from filenames names. + * @param pattern the file name pattern + * @return the normalized file name. + */ + public String normalize(final String pattern) { + if (StringUtils.isBlank(pattern)) { + return ""; + } + List parts = new ArrayList<>(Arrays.asList(tokenize(pattern))); + for (int i = 0; i < parts.size(); i++) { + String part = parts.get(i); + if (part.equals("..")) { + if (i == 0) { + throw new IllegalStateException("can not creat path before root"); + } + parts.set(i - 1, null); + parts.set(i, null); + } else if (part.equals(".")) { + parts.set(i, null); + } + } + return parts.stream().filter(Objects::nonNull).collect(Collectors.joining(dirSeparator())); + } + + @Override + public int compareTo(final FSInfo other) { + return CompareToBuilder.reflectionCompare(this, other); + } + + @Override + public boolean equals(final Object other) { + return EqualsBuilder.reflectionEquals(this, other); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } } /** @@ -286,44 +484,65 @@ public static final class Builder { /** The name for the document */ private String name; /** The base name for the document */ - private String baseName; - /** The directory separator */ - private String dirSeparator; - /** The case sensitivity flag */ - private boolean isCaseSensitive; + private DocumentName baseName; + /** The file system info */ + private final FSInfo fsInfo; /** The file system root */ private String root; + /** A flag for baseName same as this */ + private boolean sameNameFlag; /** * Create with default settings. */ - private Builder() { - isCaseSensitive = FS_IS_CASE_SENSITIVE; - dirSeparator = File.separator; + private Builder(final FSInfo fsInfo) { + this.fsInfo = fsInfo; root = ""; } + /** + * Create with default settings. + */ + private Builder(final FileSystem fileSystem) { + this(new FSInfo(fileSystem)); + } + /** * Create based on the file provided. * @param file the file to base the builder on. */ private Builder(final File file) { - this(); + this(FSInfo.getDefault()); + setName(file); + } + + /** + * Used in testing + * @param fsInfo the FSInfo for the file. + * @param file the file to process + */ + Builder(final FSInfo fsInfo, final File file) { + this(fsInfo); setName(file); - isCaseSensitive = FS_IS_CASE_SENSITIVE; - dirSeparator = File.separator; } /** * Create a Builder that clones the specified DocumentName. * @param documentName the DocumentName to clone. */ - private Builder(final DocumentName documentName) { + Builder(final DocumentName documentName) { this.root = documentName.root; this.name = documentName.name; this.baseName = documentName.baseName; - this.isCaseSensitive = documentName.isCaseSensitive; - this.dirSeparator = documentName.dirSeparator; + this.fsInfo = documentName.fsInfo; + } + + /** + * Get the directory separator for this builder. + * @return the directory separator fo this builder. + */ + public String directorySeparator() { + return fsInfo.dirSeparator(); } /** @@ -331,12 +550,11 @@ private Builder(final DocumentName documentName) { */ private void verify() { Objects.requireNonNull(name, "Name cannot be null"); - Objects.requireNonNull(baseName, "Basename cannot be null"); - if (name.startsWith(dirSeparator)) { - name = name.substring(dirSeparator.length()); + if (name.startsWith(fsInfo.dirSeparator())) { + name = name.substring(fsInfo.dirSeparator().length()); } - if (baseName.startsWith(dirSeparator)) { - baseName = baseName.substring(dirSeparator.length()); + if (!sameNameFlag) { + Objects.requireNonNull(baseName, "Basename cannot be null"); } } @@ -346,58 +564,54 @@ private void verify() { * @return this. */ public Builder setRoot(final String root) { - this.root = root; + this.root = StringUtils.defaultIfBlank(root, ""); return this; } /** - * Sets the name for this DocumentName. Will reset the root to the empty string. + * Sets the name for this DocumentName relative to the baseName. If the {@code name} is null + * an empty string is used. *

- * To correctly parse the string it must either be the directory separator specified by - * {@link File#separator} or must have been explicitly set by calling {@link #setDirSeparator(String)} - * before making this call. + * To correctly parse the string it must use the directory separator specified by + * this Document. *

- * @param name the name for this Document name. + * @param name the name for this Document name. Will be made relative to the baseName * @return this */ public Builder setName(final String name) { - Pair pair = splitRoot(name, dirSeparator); + Pair pair = splitRoot(StringUtils.defaultIfBlank(name, "")); if (this.root.isEmpty()) { this.root = pair.getLeft(); } - this.name = pair.getRight(); + this.name = fsInfo.normalize(pair.getRight()); + if (this.baseName != null && !baseName.name.isEmpty()) { + if (!this.name.startsWith(baseName.name)) { + this.name = this.name.isEmpty() ? baseName.name : + baseName.name + fsInfo.dirSeparator() + this.name; + } + } return this; } - /** - * Extracts the root/name pair from a file. - * @param file the file to extract the root/name pair from. - * @return the root/name pair. - */ - static Pair splitRoot(final File file) { - return splitRoot(file.getAbsolutePath(), File.separator); - } - /** * Extracts the root/name pair from a name string. *

* Package private for testing. *

* @param name the name to extract the root/name pair from. - * @param dirSeparator the directory separator. * @return the root/name pair. */ - static Pair splitRoot(final String name, final String dirSeparator) { + Pair splitRoot(final String name) { String workingName = name; - String root = ""; - for (String sysRoot : ROOTS) { - if (workingName.startsWith(sysRoot)) { - workingName = workingName.substring(sysRoot.length()); - if (!workingName.startsWith(dirSeparator)) { - if (sysRoot.endsWith(dirSeparator)) { - root = sysRoot.substring(0, sysRoot.length() - dirSeparator.length()); + Optional maybeRoot = fsInfo.rootFor(name); + String root = maybeRoot.orElse(""); + if (!root.isEmpty()) { + if (workingName.startsWith(root)) { + workingName = workingName.substring(root.length()); + if (!workingName.startsWith(fsInfo.dirSeparator())) { + if (root.endsWith(fsInfo.dirSeparator())) { + root = root.substring(0, root.length() - fsInfo.dirSeparator().length()); } - return ImmutablePair.of(root, workingName); } } } @@ -415,21 +629,24 @@ private void setEmptyRoot(final String root) { } /** - * Sets the properties from the file. This method sets the {@link #root} if it is empty, and resets {@link #name}, - * {@link #dirSeparator} and {@link #baseName}. + * Sets the properties from the file. Will reset the baseName appropraitly. * @param file the file to set the properties from. * @return this. */ public Builder setName(final File file) { - Pair pair = splitRoot(file); + Pair pair = splitRoot(file.getAbsolutePath()); setEmptyRoot(pair.getLeft()); - this.name = pair.getRight(); - this.dirSeparator = File.separator; - this.baseName = name; - if (!file.isDirectory()) { + this.name = fsInfo.normalize(pair.getRight()); + if (file.isDirectory()) { + sameNameFlag = true; + } else { File p = file.getParentFile(); if (p != null) { setBaseName(p); + } else { + Builder baseBuilder = new Builder(this.fsInfo).setName(this.directorySeparator()); + baseBuilder.sameNameFlag = true; + setBaseName(baseBuilder.build()); } } return this; @@ -439,17 +656,15 @@ public Builder setName(final File file) { * Sets the baseName. * Will set the root if it is not set. *

- * To correctly parse the string it must either be the directory separator specified by - * {@link File#separator} or must have been explicitly set by calling {@link #setDirSeparator(String)} - * before making this call. + * To correctly parse the string it must use the directory separator specified by this builder. *

* @param baseName the basename to use. * @return this. */ public Builder setBaseName(final String baseName) { - Pair pair = splitRoot(baseName, dirSeparator); - setEmptyRoot(pair.getLeft()); - this.baseName = pair.getRight(); + DocumentName.Builder builder = DocumentName.builder(fsInfo).setName(baseName); + builder.sameNameFlag = true; + setBaseName(builder); return this; } @@ -460,7 +675,7 @@ public Builder setBaseName(final String baseName) { * @return this. */ public Builder setBaseName(final DocumentName baseName) { - this.baseName = baseName.getName(); + this.baseName = baseName; if (!baseName.getRoot().isEmpty()) { this.root = baseName.getRoot(); } @@ -468,36 +683,24 @@ public Builder setBaseName(final DocumentName baseName) { } /** - * Sets the basename from a File. Sets {@link #root} and the {@link #baseName} - * Will set the root. - * @param file the file to set the base name from. - * @return this. + * Executes the builder, sets the base name and clears the sameName flag. + * @param builder the builder for the base name. */ - public Builder setBaseName(final File file) { - Pair pair = splitRoot(file); - this.root = pair.getLeft(); - this.baseName = pair.getRight(); - return this; + private void setBaseName(final DocumentName.Builder builder) { + this.baseName = builder.build(); + this.sameNameFlag = false; } /** - * Sets the directory separator. - * @param dirSeparator the directory separator to use. - * @return this. - */ - public Builder setDirSeparator(final String dirSeparator) { - Objects.requireNonNull(dirSeparator, "Directory separator cannot be null"); - this.dirSeparator = dirSeparator; - return this; - } - - /** - * Sets the {@link #isCaseSensitive} flag. - * @param isCaseSensitive the expected state of the flag. + * Sets the basename from a File. Sets {@link #root} and the {@link #baseName} + * Will set the root. + * @param file the file to set the base name from. * @return this. */ - public Builder setCaseSensitive(final boolean isCaseSensitive) { - this.isCaseSensitive = isCaseSensitive; + public Builder setBaseName(final File file) { + DocumentName.Builder builder = DocumentName.builder(fsInfo).setName(file); + builder.sameNameFlag = true; + setBaseName(builder); 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 8d2939c68..d1e9a3dad 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 @@ -35,6 +35,7 @@ import org.apache.rat.api.Document; import org.apache.rat.api.RatException; import org.apache.rat.document.ArchiveEntryDocument; +import org.apache.rat.document.ArchiveEntryName; import org.apache.rat.document.DocumentName; import org.apache.rat.report.RatReport; import org.apache.rat.utils.DefaultLog; @@ -82,20 +83,18 @@ private InputStream createInputStream() throws IOException { */ public Collection getDocuments() throws RatException { List result = new ArrayList<>(); + //DocumentName.FSInfo archiveInfo = new DocumentName.FSInfo(true, Arrays.asList("/"), "/"); try (ArchiveInputStream input = new ArchiveStreamFactory().createArchiveInputStream(createInputStream())) { - ArchiveEntry entry = null; + ArchiveEntry entry; while ((entry = input.getNextEntry()) != null) { if (!entry.isDirectory() && input.canReadEntryData(entry)) { - DocumentName innerName = DocumentName.builder().setDirSeparator("/").setName(entry.getName()) - .setBaseName(".").setCaseSensitive(true).build(); + DocumentName innerName = DocumentName.builder().setName(entry.getName()) + .setBaseName(".").build(); if (this.getDocument().getNameMatcher().matches(innerName)) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(input, baos); - DocumentName archiveName = getDocument().getName(); - String outerNameStr = format("%s#%s", archiveName.getName(), entry.getName()); - DocumentName outerName = DocumentName.builder(archiveName).setName(outerNameStr) - .setCaseSensitive(true).build(); - result.add(new ArchiveEntryDocument(outerName, baos.toByteArray(), getDocument().getNameMatcher())); + ArchiveEntryName entryName = new ArchiveEntryName(getDocument().getName(), entry.getName()); + result.add(new ArchiveEntryDocument(entryName, baos.toByteArray(), getDocument().getNameMatcher())); } } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java index ab1ce763a..38712c9bf 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java @@ -18,11 +18,13 @@ */ package org.apache.rat; +import java.nio.file.Path; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.tuple.Pair; +import org.apache.rat.document.DocumentName; import org.apache.rat.license.LicenseSetFactory; import org.apache.rat.report.IReportable; import org.apache.rat.test.AbstractOptionsProvider; @@ -46,6 +48,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static java.lang.String.format; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -148,11 +151,12 @@ public void testDefaultConfiguration() throws ParseException { @ParameterizedTest @ValueSource(strings = { ".", "./", "target", "./target" }) public void getReportableTest(String fName) throws IOException { - File expected = new File(fName); + File base = new File(fName); + String expected = DocumentName.FSInfo.getDefault().normalize(base.getAbsolutePath()); ReportConfiguration config = OptionCollection.parseCommands(new String[]{fName}, o -> fail("Help called"), false); - IReportable reportable = OptionCollection.getReportable(expected, config); - assertNotNull(reportable, () -> format("'%s' returned null", fName)); - assertThat(reportable.getName().getName()).isEqualTo(expected.getAbsolutePath()); + IReportable reportable = OptionCollection.getReportable(base, config); + assertThat(reportable).as(() -> format("'%s' returned null", fName)).isNotNull(); + assertThat(reportable.getName().getName()).isEqualTo(expected); } /** 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 index bb2e515cc..4e29d6c1a 100644 --- 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 @@ -114,8 +114,8 @@ public void archiveTypeAnalyserTest() throws Exception { private static Stream archiveProcessingTestData() { List lst = new ArrayList<>(); lst.add(Arguments.of(ReportConfiguration.Processing.NOTIFICATION, 0)); - lst.add(Arguments.of(ReportConfiguration.Processing.PRESENCE, 2)); - lst.add(Arguments.of(ReportConfiguration.Processing.ABSENCE, 3)); + lst.add(Arguments.of(ReportConfiguration.Processing.PRESENCE, 1)); + lst.add(Arguments.of(ReportConfiguration.Processing.ABSENCE, 2)); return lst.stream(); } diff --git a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/FileProcessorTest.java b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/FileProcessorTest.java index 10a2ca457..806ba042a 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/FileProcessorTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/FileProcessorTest.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.stream.Stream; import org.apache.rat.document.DocumentName; +import org.apache.rat.document.FSInfoTest; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -41,7 +42,7 @@ void localizePatternTest(DocumentName baseName, String pattern, String expectedS public static Stream localizePatternData() { List lst = new ArrayList<>(); - DocumentName baseName = DocumentName.builder().setName("fileBeingRead").setBaseName("baseDir").setDirSeparator("/").build(); + DocumentName baseName = DocumentName.builder(FSInfoTest.UNIX).setName("fileBeingRead").setBaseName("baseDir").build(); lst.add(Arguments.of(baseName, "file", "/baseDir/file")); lst.add(Arguments.of(baseName, "!file", "!/baseDir/file")); diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/DocumentNameTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/DocumentNameTest.java index def6ec447..31693e030 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/document/DocumentNameTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/document/DocumentNameTest.java @@ -18,43 +18,217 @@ */ package org.apache.rat.document; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import java.io.File; +import java.io.FileFilter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; + +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.tuple.Pair; +import org.apache.rat.config.exclusion.ExclusionUtils; +import org.apache.rat.document.DocumentName.FSInfo; + import org.assertj.core.util.Files; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; -import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.apache.rat.document.FSInfoTest.OSX; +import static org.apache.rat.document.FSInfoTest.UNIX; +import static org.apache.rat.document.FSInfoTest.WINDOWS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.any; public class DocumentNameTest { + public static DocumentName mkName(Path tempDir, FSInfo fsInfo) throws IOException { + File docFile = mkFile(tempDir.toFile(), fsInfo); + DocumentName result = DocumentName.builder(fsInfo).setName(docFile).build(); + DocumentName mocked = Mockito.spy(result); + + String fn = result.localized(FileSystems.getDefault().getSeparator()); + File file = tempDir.resolve(fn.substring(1)).toFile(); + File mockedFile = mkFile(file, fsInfo); + when(mocked.asFile()).thenReturn(mockedFile); + + assertThat(mocked.asFile()).isEqualTo(mockedFile); + return mocked; + } + + private static File[] listFiles(File file, FSInfo fsInfo) { + File[] fileList = file.listFiles(); + if (fileList == null) { + return fileList; + } + return Arrays.stream(fileList).map(f -> mkFile(f, fsInfo)).toArray(File[]::new); + } + + private static File[] listFiles(File file, FSInfo fsInfo, FileFilter filter) { + File[] fileList = file.listFiles(); + if (fileList == null) { + return fileList; + } + return Arrays.stream(fileList).map(f -> mkFile(f, fsInfo)).filter(filter::accept).toArray(File[]::new); + } + + private static File[] listFiles(File file, FSInfo fsInfo, FilenameFilter filter) { + File[] fileList = file.listFiles(); + if (fileList == null) { + return fileList; + } + return Arrays.stream(fileList).map(f -> mkFile(f, fsInfo)).filter(x -> filter.accept(x, x.getName())).toArray(File[]::new); + } + + public static File mkFile(final File file, final FSInfo fsInfo) { + File mockedFile = mock(File.class); + when(mockedFile.listFiles()).thenAnswer( env -> listFiles(file, fsInfo)); + when(mockedFile.listFiles(any(FilenameFilter.class))).thenAnswer( env -> listFiles(file, fsInfo, env.getArgument(0, FilenameFilter.class))); + when(mockedFile.listFiles(any(FileFilter.class))).thenAnswer(env -> listFiles(file, fsInfo, env.getArgument(0, FileFilter.class))); + when(mockedFile.getName()).thenReturn(ExclusionUtils.convertSeparator(file.getName(), FSInfoTest.DEFAULT.dirSeparator(), fsInfo.dirSeparator())); + when(mockedFile.getAbsolutePath()).thenReturn(ExclusionUtils.convertSeparator(file.getAbsolutePath(), FSInfoTest.DEFAULT.dirSeparator(), fsInfo.dirSeparator())); + when(mockedFile.isFile()).thenAnswer(env -> file.isFile()); + when(mockedFile.exists()).thenAnswer(emv -> file.exists()); + when(mockedFile.isDirectory()).thenAnswer(env -> file.isDirectory()); + + return mockedFile; + } + + public static DocumentName mkName(Path tempDir, DocumentName baseDir, String pth) throws IOException { + DocumentName result = baseDir.resolve(ExclusionUtils.convertSeparator(pth, "/", baseDir.getDirectorySeparator())); + DocumentName mocked = Mockito.spy(result); + + String fn = result.localized(FileSystems.getDefault().getSeparator()); + File file = tempDir.resolve(fn.substring(1)).toFile(); + File parent = file.getParentFile(); + if (parent.exists() && !parent.isDirectory()) { + parent.delete(); + } + parent.mkdirs(); + if (file.exists()) { + if (file.isDirectory()) { + FileUtils.deleteDirectory(file); + } else { + FileUtils.delete(file); + } + } + file.createNewFile(); + when(mocked.asFile()).thenReturn(file); + return mocked; + } + + + @ParameterizedTest(name = "{index} {0} {2}") + @MethodSource("resolveTestData") + void resolveTest(String testName, DocumentName base, String toResolve, DocumentName expected) { + DocumentName actual = base.resolve(toResolve); + assertThat(actual).isEqualTo(expected); + } + + private static Stream resolveTestData() { + List lst = new ArrayList<>(); + + DocumentName base = DocumentName.builder(UNIX).setName("/dir/unix").setBaseName("/").build(); + + DocumentName expected = DocumentName.builder(UNIX).setName("/dir/unix/relative").setBaseName("/").build(); + lst.add(Arguments.of("unix", base, "relative", expected)); + + expected = DocumentName.builder(UNIX).setName("/from/root").setBaseName("/").build(); + lst.add(Arguments.of("unix", base, "/from/root", expected)); + + expected = DocumentName.builder(UNIX).setName("dir/up/and/down").setBaseName("/").build(); + lst.add(Arguments.of("unix", base, "../up/and/down", expected)); + + expected = DocumentName.builder(UNIX).setName("/from/root").setBaseName("/").build(); + lst.add(Arguments.of("unix", base, "\\from\\root", expected)); + + expected = DocumentName.builder(UNIX).setName("dir/up/and/down").setBaseName("/").build(); + lst.add(Arguments.of("unix", base, "..\\up\\and\\down", expected)); + + // WINDOWS + base = DocumentName.builder(WINDOWS).setName("\\dir\\windows").setBaseName("C:\\").build(); + + expected = DocumentName.builder(WINDOWS).setName("\\dir\\windows\\relative").setBaseName("C:\\").build(); + lst.add(Arguments.of("windows", base, "relative", expected)); + + expected = DocumentName.builder(WINDOWS).setName("\\from\\root").setBaseName("C:\\").build(); + lst.add(Arguments.of("windows", base, "/from/root", expected)); + + expected = DocumentName.builder(WINDOWS).setName("dir\\up\\and\\down").setBaseName("C:\\").build(); + lst.add(Arguments.of("windows", base, "../up/and/down", expected)); + + expected = DocumentName.builder(WINDOWS).setName("\\from\\root").setBaseName("C:\\").build(); + lst.add(Arguments.of("windows", base, "\\from\\root", expected)); + + expected = DocumentName.builder(WINDOWS).setName("dir\\up\\and\\down").setBaseName("C:\\").build(); + lst.add(Arguments.of("windows", base, "..\\up\\and\\down", expected)); + + // OSX + base = DocumentName.builder(OSX).setName("/dir/osx").setBaseName("/").build(); + + expected = DocumentName.builder(OSX).setName("/dir/osx/relative").setBaseName("/").build(); + lst.add(Arguments.of("osx", base, "relative", expected)); + + expected = DocumentName.builder(OSX).setName("/from/root").setBaseName("/").build(); + lst.add(Arguments.of("osx", base, "/from/root", expected)); + + expected = DocumentName.builder(OSX).setName("dir/up/and/down").setBaseName("/").build(); + lst.add(Arguments.of("osx", base, "../up/and/down", expected)); + + expected = DocumentName.builder(OSX).setName("/from/root").setBaseName("/").build(); + lst.add(Arguments.of("osx", base, "\\from\\root", expected)); + + expected = DocumentName.builder(OSX).setName("dir/up/and/down").setBaseName("/").build(); + lst.add(Arguments.of("osx", base, "..\\up\\and\\down", expected)); + + return lst.stream(); + + } + @Test - public void localizeTest() { - DocumentName documentName = DocumentName.builder().setName("/a/b/c") - .setBaseName("/a").setDirSeparator("/").setCaseSensitive(false).build(); + void localizeTest() { + DocumentName documentName = DocumentName.builder(UNIX).setName("/a/b/c") + .setBaseName("/a").build(); + assertThat(documentName.localized()).isEqualTo("/b/c"); + assertThat(documentName.localized("-")).isEqualTo("-b-c"); + + documentName = DocumentName.builder(WINDOWS).setName("\\a\\b\\c") + .setBaseName("\\a").build(); + assertThat(documentName.localized()).isEqualTo("\\b\\c"); + assertThat(documentName.localized("-")).isEqualTo("-b-c"); + + documentName = DocumentName.builder(OSX).setName("/a/b/c") + .setBaseName("/a").build(); assertThat(documentName.localized()).isEqualTo("/b/c"); assertThat(documentName.localized("-")).isEqualTo("-b-c"); + } - @ParameterizedTest(name ="{index} {0}") + @ParameterizedTest(name = "{index} {0}") @MethodSource("validBuilderData") void validBuilderTest(String testName, DocumentName.Builder builder, String root, String name, String baseName, String dirSeparator) { DocumentName underTest = builder.build(); assertThat(underTest.getRoot()).as(testName).isEqualTo(root); assertThat(underTest.getDirectorySeparator()).as(testName).isEqualTo(dirSeparator); - assertThat(underTest.getName()).as(testName).isEqualTo(root+dirSeparator+name); - assertThat(underTest.getBaseName()).as(testName).isEqualTo(root+dirSeparator+baseName); + assertThat(underTest.getName()).as(testName).isEqualTo(root + dirSeparator + name); + assertThat(underTest.getBaseName()).as(testName).isEqualTo(root + dirSeparator + baseName); } - private static Stream validBuilderData() { + private static Stream validBuilderData() throws IOException { List lst = new ArrayList<>(); File f = Files.newTemporaryFile(); @@ -94,98 +268,67 @@ private static Stream validBuilderData() { lst.add(Arguments.of("setName(root)", DocumentName.builder().setName(r), root, "", "", File.separator)); lst.add(Arguments.of("Builder(root)", DocumentName.builder(r), root, "", "", File.separator)); - lst.add(Arguments.of("foo/bar foo", DocumentName.builder().setDirSeparator("/") + + lst.add(Arguments.of("foo/bar foo", DocumentName.builder(UNIX) .setName("/foo/bar").setBaseName("foo"), "", "foo/bar", "foo", "/")); - DocumentName.Builder builder = DocumentName.builder().setDirSeparator("\\").setName("\\foo\\bar").setBaseName("foo") + + DocumentName.Builder builder = DocumentName.builder(WINDOWS).setName("\\foo\\bar").setBaseName("C:\\foo") .setRoot("C:"); - lst.add(Arguments.of("C:\\foo\\bar foo", builder, "C:", "foo\\bar", "foo", "\\")); + lst.add(Arguments.of("\\foo\\bar foo", builder, "C:", "foo\\bar", "foo", "\\")); + + lst.add(Arguments.of("foo/bar foo", DocumentName.builder(OSX) + .setName("/foo/bar").setBaseName("foo"), "", "foo/bar", "foo", "/")); return lst.stream(); } @Test - public void splitRootsTest() { - Set preserve = new HashSet<>(DocumentName.ROOTS); - try { - DocumentName.ROOTS.clear(); - DocumentName.ROOTS.add("C:\\"); - Pair result = DocumentName.Builder.splitRoot("C:\\My\\path\\to\\a\\file.txt", "\\"); - assertThat(result.getLeft()).isEqualTo("C:"); - assertThat(result.getRight()).isEqualTo("My\\path\\to\\a\\file.txt"); - - - DocumentName.ROOTS.clear(); - DocumentName.ROOTS.add("/"); - result = DocumentName.Builder.splitRoot("/My/path/to/a/file.txt", "/"); - assertThat(result.getLeft()).isEqualTo(""); - assertThat(result.getRight()).isEqualTo("My/path/to/a/file.txt"); - - } finally { - DocumentName.ROOTS.clear(); - DocumentName.ROOTS.addAll(preserve); - } + void splitRootsTest() throws IOException { + Pair result = DocumentName.builder(WINDOWS).splitRoot("C:\\My\\path\\to\\a\\file.txt"); + assertThat(result.getLeft()).isEqualTo("C:"); + assertThat(result.getRight()).isEqualTo("My\\path\\to\\a\\file.txt"); + + result = DocumentName.builder(UNIX).splitRoot("/My/path/to/a/file.txt"); + assertThat(result.getLeft()).isEqualTo(""); + assertThat(result.getRight()).isEqualTo("My/path/to/a/file.txt"); + + result = DocumentName.builder(OSX).splitRoot("/My/path/to/a/file.txt"); + assertThat(result.getLeft()).isEqualTo(""); + assertThat(result.getRight()).isEqualTo("My/path/to/a/file.txt"); + } @Test - public void archiveEntryNameTest() { - Set preserve = new HashSet<>(DocumentName.ROOTS); - try { - DocumentName.ROOTS.clear(); - DocumentName.ROOTS.add("C:\\"); - - DocumentName archiveName = DocumentName.builder().setDirSeparator("\\") - .setName("C:\\archives\\anArchive.zip").setBaseName("archives").build(); - assertThat(archiveName.getRoot()).isEqualTo("C:"); - assertThat(archiveName.getDirectorySeparator()).isEqualTo("\\"); - assertThat(archiveName.getBaseName()).isEqualTo("C:\\archives"); - assertThat(archiveName.getName()).isEqualTo("C:\\archives\\anArchive.zip"); - assertThat(archiveName.localized()).isEqualTo("\\anArchive.zip"); - - String entryName = "./anArchiveEntry.txt"; - DocumentName innerName = DocumentName.builder() - .setDirSeparator("/").setName(entryName) - .setBaseName(".").setCaseSensitive(true).build(); - assertThat(innerName.getRoot()).isEqualTo(""); - assertThat(innerName.getDirectorySeparator()).isEqualTo("/"); - assertThat(innerName.getBaseName()).isEqualTo("/."); - assertThat(innerName.getName()).isEqualTo("/" + entryName); - assertThat(innerName.localized()).isEqualTo("/anArchiveEntry.txt"); - - String outerNameStr = format("%s#%s", archiveName.getName(), entryName); - DocumentName outerName = DocumentName.builder(archiveName).setName(outerNameStr) - .setCaseSensitive(innerName.isCaseSensitive()).build(); - - assertThat(outerName.getRoot()).isEqualTo("C:"); - assertThat(outerName.getDirectorySeparator()).isEqualTo("\\"); - assertThat(outerName.getBaseName()).isEqualTo("C:\\archives"); - assertThat(outerName.getName()).isEqualTo("C:\\archives\\anArchive.zip#./anArchiveEntry.txt"); - assertThat(outerName.localized()).isEqualTo("\\anArchive.zip#./anArchiveEntry.txt"); - assertThat(outerName.localized("/")).isEqualTo("/anArchive.zip#./anArchiveEntry.txt"); - - // test with directory - entryName = "./someDir/anArchiveEntry.txt"; - innerName = DocumentName.builder() - .setDirSeparator("/").setName(entryName) - .setBaseName(".").setCaseSensitive(true).build(); - assertThat(innerName.getRoot()).isEqualTo(""); - assertThat(innerName.getDirectorySeparator()).isEqualTo("/"); - assertThat(innerName.getBaseName()).isEqualTo("/."); - assertThat(innerName.getName()).isEqualTo("/" + entryName); - assertThat(innerName.localized()).isEqualTo("/someDir/anArchiveEntry.txt"); - - outerNameStr = format("%s#%s", archiveName.getName(), entryName); - outerName = DocumentName.builder(archiveName).setName(outerNameStr) - .setCaseSensitive(innerName.isCaseSensitive()).build(); - - assertThat(outerName.getRoot()).isEqualTo("C:"); - assertThat(outerName.getDirectorySeparator()).isEqualTo("\\"); - assertThat(outerName.getBaseName()).isEqualTo("C:\\archives"); - assertThat(outerName.getName()).isEqualTo("C:\\archives\\anArchive.zip#./someDir/anArchiveEntry.txt"); - assertThat(outerName.localized()).isEqualTo("\\anArchive.zip#./someDir/anArchiveEntry.txt"); - assertThat(outerName.localized("/")).isEqualTo("/anArchive.zip#./someDir/anArchiveEntry.txt"); - } finally { - DocumentName.ROOTS.clear(); - DocumentName.ROOTS.addAll(preserve); - } + void archiveEntryNameTest() throws IOException { + String entryName = "./anArchiveEntry.txt"; + DocumentName archiveName = DocumentName.builder(WINDOWS) + .setName("C:\\archives\\anArchive.zip").setBaseName("C:\\archives").build(); + + assertThat(archiveName.getRoot()).isEqualTo("C:"); + assertThat(archiveName.getDirectorySeparator()).isEqualTo("\\"); + assertThat(archiveName.getBaseName()).isEqualTo("C:\\archives"); + assertThat(archiveName.getName()).isEqualTo("C:\\archives\\anArchive.zip"); + assertThat(archiveName.localized()).isEqualTo("\\anArchive.zip"); + + + ArchiveEntryName archiveEntryName = new ArchiveEntryName(archiveName, entryName); + + assertThat(archiveEntryName.getRoot()).isEqualTo(archiveName.getName()+"#"); + assertThat(archiveEntryName.getDirectorySeparator()).isEqualTo("/"); + assertThat(archiveEntryName.getBaseName()).isEqualTo("C:\\archives\\anArchive.zip#"); + assertThat(archiveEntryName.getName()).isEqualTo("C:\\archives\\anArchive.zip#/anArchiveEntry.txt"); + assertThat(archiveEntryName.localized()).isEqualTo("/anArchiveEntry.txt"); + assertThat(archiveEntryName.localized("/")).isEqualTo("/anArchive.zip#/anArchiveEntry.txt"); + + // test with directory + entryName = "./someDir/anArchiveEntry.txt"; + archiveEntryName = new ArchiveEntryName(archiveName, entryName); + + assertThat(archiveEntryName.getRoot()).isEqualTo(archiveName.getName()+"#"); + assertThat(archiveEntryName.getDirectorySeparator()).isEqualTo("/"); + assertThat(archiveEntryName.getBaseName()).isEqualTo("C:\\archives\\anArchive.zip#"); + assertThat(archiveEntryName.getName()).isEqualTo("C:\\archives\\anArchive.zip#/someDir/anArchiveEntry.txt"); + assertThat(archiveEntryName.localized()).isEqualTo("/someDir/anArchiveEntry.txt"); + assertThat(archiveEntryName.localized("/")).isEqualTo("/anArchive.zip#/someDir/anArchiveEntry.txt"); } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/FSInfoTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/FSInfoTest.java new file mode 100644 index 000000000..db2f91ea2 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/document/FSInfoTest.java @@ -0,0 +1,60 @@ +/* + * 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; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +public class FSInfoTest { + public static final DocumentName.FSInfo DEFAULT; + public static final DocumentName.FSInfo OSX; + public static final DocumentName.FSInfo UNIX; + public static final DocumentName.FSInfo WINDOWS; + + static { + try (FileSystem osx = Jimfs.newFileSystem(Configuration.osX()); + FileSystem unix = Jimfs.newFileSystem(Configuration.unix()); + FileSystem windows = Jimfs.newFileSystem(Configuration.windows())) { + OSX = new DocumentName.FSInfo("osx", osx); + UNIX = new DocumentName.FSInfo("unix", unix); + WINDOWS = new DocumentName.FSInfo("windows", windows); + DEFAULT = new DocumentName.FSInfo("default", FileSystems.getDefault()); + } catch (IOException e) { + throw new RuntimeException("Unable to creat FSInfo objects: " + e.getMessage(), e); + } + } + + public static final DocumentName.FSInfo[] TEST_SUITE = {UNIX, WINDOWS, OSX}; + + /** + * Provided arguments for parameterized tests that only require the fsInfo. + * @return a stream of TEST_SUITE based Arguments. + */ + public static Stream fsInfoArgs() { + return Arrays.stream(TEST_SUITE).map(Arguments::of); + } +} diff --git a/apache-rat-core/src/test/java/org/apache/rat/document/guesser/NoteGuesserTest.java b/apache-rat-core/src/test/java/org/apache/rat/document/guesser/NoteGuesserTest.java index 2abf19340..87d157a0e 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/document/guesser/NoteGuesserTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/document/guesser/NoteGuesserTest.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.stream.Stream; import org.apache.rat.document.DocumentName; +import org.apache.rat.document.FSInfoTest; import org.apache.rat.testhelpers.TestingDocument; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -41,10 +42,8 @@ public void testMatches(DocumentName testingName, boolean expected) { private static Stream nameData() { List lst = new ArrayList<>(); - final DocumentName linuxBaseName = DocumentName.builder().setName("/").setBaseName("/").setDirSeparator("/") - .setCaseSensitive(true).build(); - final DocumentName windowsBaseName = DocumentName.builder().setName("\\").setBaseName("\\") - .setDirSeparator("\\").setCaseSensitive(false).build(); + final DocumentName linuxBaseName = DocumentName.builder(FSInfoTest.UNIX).setName("/").setBaseName("/").build(); + final DocumentName windowsBaseName = DocumentName.builder(FSInfoTest.WINDOWS).setName("\\").setBaseName("\\").build(); lst.add(Arguments.of(linuxBaseName.resolve("DEPENDENCIES"), true)); lst.add(Arguments.of(linuxBaseName.resolve("LICENSE"), true)); diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java index 7ab034a3e..26ba04c38 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java +++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingDocument.java @@ -45,12 +45,12 @@ public TestingDocument(DocumentName documentName) { } public TestingDocument(String name, DocumentNameMatcher matcher) { - super(DocumentName.builder().setName(name).setBaseName("").setDirSeparator("/").setCaseSensitive(true).build(), matcher); + super(DocumentName.builder().setName(name).setBaseName("").build(), matcher); this.reader = null; } public TestingDocument(Reader reader, String name) { - super(DocumentName.builder().setName(name).setBaseName("").setDirSeparator("/").setCaseSensitive(true).build(), DocumentNameMatcher.MATCHES_ALL); + super(DocumentName.builder().setName(name).setBaseName("").build(), DocumentNameMatcher.MATCHES_ALL); this.reader = reader; } diff --git a/pom.xml b/pom.xml index a6c08c299..bf2aaabf5 100644 --- a/pom.xml +++ b/pom.xml @@ -198,7 +198,7 @@ agnostic home for software distribution comprehension and audit tools. org.assertj assertj-core - 3.27.2 + 3.27.1 test @@ -217,6 +217,12 @@ agnostic home for software distribution comprehension and audit tools. 2.4.21 test + + com.google.jimfs + jimfs + 1.3.0 + test + @@ -489,7 +495,7 @@ agnostic home for software distribution comprehension and audit tools. org.apache.maven.plugins maven-remote-resources-plugin - 3.3.0 + 3.2.0 org.apache.maven.plugins