diff --git a/Gemfile.lock b/Gemfile.lock index f20255105a..b41e9645e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,14 +68,14 @@ GEM multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.13.5) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) - pmdtester (1.5.0) + pmdtester (1.5.1) differ (~> 0.1) liquid (~> 5.2) logger-colors (~> 1.0) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 020814c547..af044ad8d5 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.4.8) + activesupport (6.0.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -232,7 +232,7 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.15.0) multipart-post (2.1.1) - nokogiri (1.13.5) + nokogiri (1.13.6) mini_portile2 (~> 2.8.0) racc (~> 1.4) octokit (4.22.0) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index a1be51b66c..083d855a4b 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,7 +20,8 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy ### Fixed Issues - +* core + * [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425) * javascript * [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 50ede2c0da..a35d757176 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -61,10 +61,6 @@ - - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index b96f988a88..fadc17f77e 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -91,10 +91,6 @@ com.beust jcommander - - commons-io - commons-io - net.sf.saxon diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 522e5e9741..2d0a2a08c7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -21,7 +21,6 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.CDATASection; @@ -37,6 +36,7 @@ import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.util.IOUtil; /** * This class represents a way to serialize a RuleSet to an XML configuration @@ -62,7 +62,7 @@ public class RuleSetWriter { } public void close() { - IOUtils.closeQuietly(outputStream); + IOUtil.closeQuietly(outputStream); } public void write(RuleSet ruleSet) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java index 6f16e686fb..6156579241 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/Formatter.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; @@ -32,6 +31,7 @@ import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.renderers.RendererFactory; import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; @InternalApi @@ -190,8 +190,8 @@ public class Formatter { isOnError = false; } finally { if (isOnError) { - IOUtils.closeQuietly(output); - IOUtils.closeQuietly(writer); + IOUtil.closeQuietly(output); + IOUtil.closeQuietly(writer); } } return writer; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java index 624515579c..be97ff2fe3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AbstractAnalysisCache.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +34,7 @@ import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.cache.internal.ClasspathFingerprinter; import net.sourceforge.pmd.reporting.FileAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; /** @@ -181,7 +181,7 @@ public abstract class AbstractAnalysisCache implements AnalysisCache { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - String extension = FilenameUtils.getExtension(file.toString()); + String extension = IOUtil.getFilenameExtension(file.toString()); if ("jar".equalsIgnoreCase(extension)) { fileVisitor.visitFile(file, attrs); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisResult.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisResult.java index 36b558893b..6972912a91 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisResult.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/AnalysisResult.java @@ -13,10 +13,9 @@ import java.util.List; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; -import org.apache.commons.io.IOUtils; - import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.util.IOUtil; /** * The result of a single file analysis. @@ -45,8 +44,7 @@ public class AnalysisResult { new BufferedInputStream(Files.newInputStream(sourceFile.toPath())), new Adler32()); ) { // Just read it, the CheckedInputStream will update the checksum on it's own - IOUtils.skipFully(stream, sourceFile.length()); - + IOUtil.skipFully(stream, sourceFile.length()); return stream.getChecksum().getValue(); } catch (final IOException ignored) { // We don't really care, if it's unreadable diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java index be93115f1d..29e6457efe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cache/internal/RawFileFingerprinter.java @@ -13,10 +13,11 @@ import java.util.Set; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.util.IOUtil; + /** * Base fingerprinter for raw files. */ @@ -41,7 +42,7 @@ public class RawFileFingerprinter implements ClasspathEntryFingerprinter { public void fingerprint(URL entry, Checksum checksum) throws IOException { try (CheckedInputStream inputStream = new CheckedInputStream(entry.openStream(), checksum)) { // Just read it, the CheckedInputStream will update the checksum on it's own - while (IOUtils.skip(inputStream, Long.MAX_VALUE) == Long.MAX_VALUE) { + while (IOUtil.skipFully(inputStream, Long.MAX_VALUE) == Long.MAX_VALUE) { // just loop } } catch (final FileNotFoundException ignored) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index bcc83fb7b5..cc4c49bdcf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -15,13 +15,13 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.util.FileFinder; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -93,8 +93,7 @@ public class CPD { current.add(signature); } - if (!FilenameUtils.equalsNormalizedOnSystem(file.getAbsoluteFile().getCanonicalPath(), - file.getAbsolutePath())) { + if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { System.err.println("Skipping " + file + " since it appears to be a symlink"); return; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java index 99cf8f8829..b7ac55af7b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java @@ -14,8 +14,7 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; -import org.apache.commons.io.ByteOrderMark; -import org.apache.commons.io.input.BOMInputStream; +import net.sourceforge.pmd.util.IOUtil; public class SourceCode { @@ -112,11 +111,10 @@ public class SourceCode { @Override public Reader getReader() throws Exception { - BOMInputStream inputStream = new BOMInputStream(Files.newInputStream(file.toPath()), ByteOrderMark.UTF_8, - ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE); + IOUtil.BomAwareInputStream inputStream = new IOUtil.BomAwareInputStream(Files.newInputStream(file.toPath())); - if (inputStream.hasBOM()) { - encoding = inputStream.getBOMCharsetName(); + if (inputStream.hasBom()) { + encoding = inputStream.getBomCharsetName(); } return new InputStreamReader(inputStream, encoding); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java index a481c909f7..2338f8c10d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java @@ -19,10 +19,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Scanner; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.util.IOUtil; + /** * Implementation that handles a Document as a file in the filesystem and receives operations in a sorted manner * (i.e. the regions are sorted). This improves the efficiency of reading the file by only scanning it once while @@ -159,7 +160,7 @@ public class DocumentFile implements Document, Closeable { } private void writeUntilEOF() throws IOException { - IOUtils.copy(reader, writer); + IOUtil.copy(reader, writer); } /* package-private */ List getLineToOffset() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java index 697d16f927..55e23569fb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +23,7 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.util.FileUtil; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -166,7 +166,7 @@ public final class FileCollectionUtil { LOG.trace("Adding database source object {}", falseFilePath); try (Reader sourceCode = dbmsMetadata.getSourceCode(sourceObject)) { - String source = IOUtils.toString(sourceCode); + String source = IOUtil.readToString(sourceCode); collector.addSourceFile(source, falseFilePath); } catch (SQLException ex) { collector.getReporter().warnEx("Cannot get SourceCode for {} - skipping ...", diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java index 705ef530a7..c7d77e1de8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java @@ -12,11 +12,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import org.apache.commons.io.IOUtils; - import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.FileDataSource; @@ -68,7 +67,7 @@ class NioTextFile implements TextFile { } try (BufferedReader br = Files.newBufferedReader(path, charset)) { - return IOUtils.toString(br); + return IOUtil.readToString(br); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java index ea12ba8630..363bcebcdf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/AbstractRenderer.java @@ -9,8 +9,6 @@ import java.io.Writer; import java.util.Collections; import java.util.List; -import org.apache.commons.io.IOUtils; - import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.cli.PMDParameters; @@ -113,7 +111,7 @@ public abstract class AbstractRenderer extends AbstractPropertySource implements } catch (IOException e) { throw new IllegalStateException(e); } finally { - IOUtils.closeQuietly(writer); + IOUtil.closeQuietly(writer); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index 92d26e1adb..566ed812b9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -22,7 +22,6 @@ import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; -import org.apache.commons.io.output.WriterOutputStream; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.PMDVersion; @@ -30,6 +29,7 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.StringUtil; /** @@ -258,7 +258,7 @@ public class XMLRenderer extends AbstractIncrementingRenderer { public void setWriter(final Writer writer) { String encoding = getProperty(ENCODING); // for backwards compatibility, create a OutputStream that writes to the writer. - this.stream = new WriterOutputStream(writer, encoding); + this.stream = IOUtil.fromWriter(writer, encoding); XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); try { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java index 874a004bcf..c632997409 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileFinder.java @@ -8,10 +8,9 @@ import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; -import org.apache.commons.io.comparator.PathFileComparator; - import net.sourceforge.pmd.annotation.InternalApi; /** @@ -49,7 +48,12 @@ public class FileFinder { return; } - Arrays.sort(candidates, PathFileComparator.PATH_INSENSITIVE_COMPARATOR); + Arrays.sort(candidates, new Comparator() { + @Override + public int compare(File o1, File o2) { + return o1.getPath().compareToIgnoreCase(o2.getPath()); + } + }); for (File tmp : candidates) { if (tmp.isDirectory()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java index 38484692ed..c4aec51b09 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/FileUtil.java @@ -23,7 +23,6 @@ import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.annotation.InternalApi; @@ -183,7 +182,7 @@ public final class FileUtil { * @throws IOException if the file couldn't be read */ public static String readFilelist(File filelist) throws IOException { - String filePaths = FileUtils.readFileToString(filelist); + String filePaths = IOUtil.readFileToString(filelist); filePaths = StringUtils.trimToEmpty(filePaths); filePaths = filePaths.replaceAll("\\r?\\n", ","); filePaths = filePaths.replaceAll(",+", ","); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java index 09a162bc1a..61129d4431 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/IOUtil.java @@ -7,21 +7,30 @@ package net.sourceforge.pmd.util; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; import java.util.List; +import java.util.Objects; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; @@ -36,6 +45,8 @@ import net.sourceforge.pmd.annotation.InternalApi; @Deprecated public final class IOUtil { + private static final int BUFFER_SIZE = 8192; + private IOUtil() { } @@ -120,7 +131,7 @@ public final class IOUtil { public static void tryCloseClassLoader(ClassLoader classLoader) { if (classLoader instanceof Closeable) { - IOUtils.closeQuietly((Closeable) classLoader); + closeQuietly((Closeable) classLoader); } } @@ -169,4 +180,308 @@ public final class IOUtil { throw pendingException; } } + + public static void closeQuietly(Closeable closeable) { + try { + closeable.close(); + } catch (IOException ignored) { + // ignored + } + } + + public static byte[] toByteArray(InputStream stream) throws IOException { + byte[] result = new byte[0]; + byte[] buffer = new byte[BUFFER_SIZE]; + int count = stream.read(buffer); + while (count > -1) { + byte[] newResult = new byte[result.length + count]; + System.arraycopy(result, 0, newResult, 0, result.length); + System.arraycopy(buffer, 0, newResult, result.length, count); + result = newResult; + count = stream.read(buffer); + } + return result; + } + + public static long skipFully(InputStream stream, long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException(); + } + + long bytesToSkip = n; + byte[] buffer = new byte[(int) Math.min(BUFFER_SIZE, bytesToSkip)]; + while (bytesToSkip > 0) { + int count = stream.read(buffer, 0, (int) Math.min(BUFFER_SIZE, bytesToSkip)); + if (count < 0) { + // reached eof + break; + } + bytesToSkip -= count; + } + return n - bytesToSkip; + } + + public static String normalizePath(String path) { + Path path1 = Paths.get(path); + path1.isAbsolute(); + String normalized = path1.normalize().toString(); + if (normalized.contains("." + File.separator) || normalized.contains(".." + File.separator) || "".equals(normalized)) { + return null; + } + return normalized; + } + + public static boolean equalsNormalizedPaths(String path1, String path2) { + return Objects.equals(normalizePath(path1), normalizePath(path2)); + } + + public static String getFilenameExtension(String name) { + String filename = Paths.get(name).getFileName().toString(); + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(dot + 1); + } + return ""; + } + + public static String getFilenameBase(String name) { + String filename = Paths.get(name).getFileName().toString(); + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(0, dot); + } + return filename; + } + + public static void copy(InputStream from, OutputStream to) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int count = from.read(buffer); + while (count > -1) { + to.write(buffer, 0, count); + count = from.read(buffer); + } + } + + public static void copy(Reader from, Writer to) throws IOException { + char[] buffer = new char[BUFFER_SIZE]; + int count = from.read(buffer); + while (count > -1) { + to.write(buffer, 0, count); + count = from.read(buffer); + } + } + + public static String readFileToString(File file) throws IOException { + return readFileToString(file, Charset.defaultCharset()); + } + + public static String readFileToString(File file, Charset charset) throws IOException { + byte[] bytes = Files.readAllBytes(file.toPath()); + return charset.decode(ByteBuffer.wrap(bytes)).toString(); + } + + public static String readToString(Reader reader) throws IOException { + StringBuilder sb = new StringBuilder(BUFFER_SIZE); + char[] buffer = new char[BUFFER_SIZE]; + int count = reader.read(buffer); + while (count > -1) { + sb.append(buffer, 0, count); + count = reader.read(buffer); + } + return sb.toString(); + } + + public static String readToString(InputStream stream, Charset charset) throws IOException { + byte[] bytes = toByteArray(stream); + return charset.decode(ByteBuffer.wrap(bytes)).toString(); + } + + public static InputStream fromReader(Reader reader) throws IOException { + class ReaderInputStream extends InputStream { + private final Reader reader; + private final CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE); + private final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private final CharsetEncoder encoder; + + private boolean eof; + + ReaderInputStream(Reader reader) { + this.reader = reader; + encoder = Charset.defaultCharset().newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + charBuffer.clear(); + byteBuffer.clear(); + byteBuffer.flip(); // byte buffer is empty at the beginning, no bytes read yet + } + + @Override + public int read() throws IOException { + if (!byteBuffer.hasRemaining()) { + if (charBuffer.hasRemaining() && !eof) { + int count = reader.read(charBuffer); + eof = count == -1; + } + byteBuffer.clear(); + charBuffer.flip(); + encoder.encode(charBuffer, byteBuffer, eof); + byteBuffer.flip(); + charBuffer.flip(); + } + + if (byteBuffer.hasRemaining()) { + return byteBuffer.get(); + } + + return -1; + } + + @Override + public int available() throws IOException { + return byteBuffer.remaining(); + } + + @Override + public void close() throws IOException { + reader.close(); + } + } + + return new ReaderInputStream(reader); + } + + public static OutputStream fromWriter(Writer writer, String encoding) throws UnsupportedCharsetException { + class WriterOutputStream extends OutputStream { + private final Writer writer; + private final CharsetDecoder decoder; + private final ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); + private final CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE); + + WriterOutputStream(Writer writer, String encoding) throws UnsupportedCharsetException { + this.writer = writer; + Charset charset = Charset.forName(encoding); + decoder = charset.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + byteBuffer.clear(); + charBuffer.clear(); + } + + @Override + public void write(int b) throws IOException { + if (!byteBuffer.hasRemaining()) { + decodeByteBuffer(false); + } + byteBuffer.put((byte) b); + } + + @Override + public void flush() throws IOException { + decodeByteBuffer(false); + } + + private void decodeByteBuffer(boolean isClosing) throws IOException { + byteBuffer.flip(); + charBuffer.clear(); + decoder.decode(byteBuffer, charBuffer, isClosing); + writer.write(charBuffer.array(), 0, charBuffer.position()); + writer.flush(); + byteBuffer.compact(); + } + + @Override + public void close() throws IOException { + flush(); + decodeByteBuffer(true); + writer.close(); + } + } + + return new WriterOutputStream(writer, encoding); + } + + /** + *

+ * Input stream that skips an optional byte order mark at the beginning + * of the stream. Whether the stream had a byte order mark (encoded in either UTF-8, + * UTF-16LE or UTF-16BE) can be checked with {@link #hasBom()}. The corresponding + * charset can be retrieved with {@link #getBomCharsetName()}. + *

+ * + *

+ * If the stream didn't had a BOM, then no bytes are skipped. + *

+ */ + public static class BomAwareInputStream extends FilterInputStream { + + private byte[] begin; + int beginIndex; + + private String charset; + + public BomAwareInputStream(InputStream in) { + super(in); + begin = determineBom(); + } + + private byte[] determineBom() { + byte[] bytes = new byte[3]; + try { + int count = in.read(bytes); + if (count == 3 && bytes[0] == (byte) 0xef && bytes[1] == (byte) 0xbb && bytes[2] == (byte) 0xbf) { + charset = StandardCharsets.UTF_8.name(); + return new byte[0]; // skip all 3 bytes + } else if (count >= 2 && bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) { + charset = StandardCharsets.UTF_16BE.name(); + return new byte[] { bytes[2] }; + } else if (count >= 2 && bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe) { + charset = StandardCharsets.UTF_16LE.name(); + return new byte[] { bytes[2] }; + } else if (count == 3) { + return bytes; + } + + if (count < 0) { + return new byte[0]; + } + + byte[] read = new byte[count]; + for (int i = 0; i < count; i++) { + read[i] = bytes[i]; + } + return read; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public int read() throws IOException { + if (beginIndex < begin.length) { + return begin[beginIndex++]; + } + return super.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (beginIndex < begin.length) { + int count = 0; + for (; count < len && beginIndex < begin.length; beginIndex++) { + b[off + count] = begin[beginIndex]; + count++; + } + return count; + } + return super.read(b, off, len); + } + + public boolean hasBom() { + return charset != null; + } + + public String getBomCharsetName() { + return charset; + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java index ec21c09f4b..fe530ae40c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/ReaderDataSource.java @@ -8,8 +8,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import org.apache.commons.io.input.ReaderInputStream; - +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.internal.AbstractDataSource; /** @@ -50,7 +49,7 @@ public class ReaderDataSource extends AbstractDataSource { */ @Override public InputStream getInputStream() throws IOException { - return new ReaderInputStream(reader); + return IOUtil.fromReader(reader); } /** diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java index 0914045572..fa48c86698 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java @@ -22,9 +22,9 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.List; -import org.apache.commons.io.IOUtils; import org.junit.Test; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.ResourceLoader; import com.github.tomakehurst.wiremock.junit.WireMockRule; @@ -123,7 +123,7 @@ public class RuleSetReferenceIdTest { assertRuleSetReferenceId(true, rulesetUrl, true, null, rulesetUrl, ruleSetReferenceId); try (InputStream inputStream = ruleSetReferenceId.getInputStream(new ResourceLoader())) { - String loaded = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + String loaded = IOUtil.readToString(inputStream, StandardCharsets.UTF_8); assertEquals("xyz", loaded); } @@ -139,8 +139,8 @@ public class RuleSetReferenceIdTest { String path = "/profiles/export?format=pmd&language=java&name=Sonar%2520way"; String completePath = path + "/DummyBasicMockRule"; String hostpart = "http://localhost:" + wireMockRule.port(); - String basicRuleSet = IOUtils - .toString(RuleSetReferenceId.class.getResourceAsStream("/rulesets/dummy/basic.xml"), StandardCharsets.UTF_8); + String basicRuleSet = IOUtil + .readToString(RuleSetReferenceId.class.getResourceAsStream("/rulesets/dummy/basic.xml"), StandardCharsets.UTF_8); stubFor(head(urlEqualTo(completePath)).willReturn(aResponse().withStatus(404))); stubFor(head(urlEqualTo(path)).willReturn(aResponse().withStatus(200).withHeader("Content-type", "text/xml"))); @@ -152,7 +152,7 @@ public class RuleSetReferenceIdTest { ruleSetReferenceId); try (InputStream inputStream = ruleSetReferenceId.getInputStream(new ResourceLoader())) { - String loaded = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + String loaded = IOUtil.readToString(inputStream, StandardCharsets.UTF_8); assertEquals(basicRuleSet, loaded); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 503b8d0c98..bb28775238 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.commons.io.FilenameUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.Test; @@ -45,6 +44,7 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; +import net.sourceforge.pmd.util.IOUtil; public class RuleSetTest { @@ -523,7 +523,7 @@ public class RuleSetTest { assertThat(error.getMsg(), containsString("java.lang.IllegalStateException: Test exception while applying rule\n")); assertThat(error.getMsg(), containsString("Rule applied on node=dummyRootNode[@Image=Foo]")); assertThat(error.getError().getCause(), instanceOf(IllegalStateException.class)); - assertThat(FilenameUtils.normalize(error.getFile(), true), equalTo("samplefile.dummy")); + assertThat(IOUtil.normalizePath(error.getFile()), equalTo("samplefile.dummy")); assertThat(report.getViolations(), hasSize(1)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index 6e2ae33f8f..4fa4b5fd8e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import org.apache.commons.io.IOUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileRule; import org.junit.AfterClass; @@ -24,6 +23,8 @@ import org.junit.rules.TestRule; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.util.IOUtil; + public class PMDTaskTest { @Rule @@ -89,7 +90,7 @@ public class PMDTaskTest { buildRule.executeTarget("testWithShortFilenames"); try (InputStream in = new FileInputStream("target/pmd-ant-test.txt")) { - String actual = IOUtils.toString(in, StandardCharsets.UTF_8); + String actual = IOUtil.readToString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.trim(); Assert.assertEquals("sample.dummy:1:\tSampleXPathRule:\tTest Rule 2", actual); @@ -102,11 +103,11 @@ public class PMDTaskTest { try (InputStream in = new FileInputStream("target/pmd-ant-xml.xml"); InputStream expectedStream = PMDTaskTest.class.getResourceAsStream("xml/expected-pmd-ant-xml.xml")) { - String actual = IOUtils.toString(in, StandardCharsets.UTF_8); + String actual = IOUtil.readToString(in, StandardCharsets.UTF_8); actual = actual.replaceFirst("timestamp=\"[^\"]+\"", "timestamp=\"\""); actual = actual.replaceFirst("\\.xsd\" version=\"[^\"]+\"", ".xsd\" version=\"\""); - String expected = IOUtils.toString(expectedStream, StandardCharsets.UTF_8); + String expected = IOUtil.readToString(expectedStream, StandardCharsets.UTF_8); expected = expected.replaceFirst("timestamp=\"[^\"]+\"", "timestamp=\"\""); expected = expected.replaceFirst("\\.xsd\" version=\"[^\"]+\"", ".xsd\" version=\"\""); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java index 47106eb626..f2e95e9205 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java @@ -20,7 +20,6 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.commons.io.IOUtils; import org.hamcrest.Matcher; import org.junit.AfterClass; import org.junit.Before; @@ -34,6 +33,7 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMD.StatusCode; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.util.IOUtil; /** * @@ -135,7 +135,7 @@ public class CoreCliTest { runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET, "--report-file", reportFile, "--debug"); assertTrue("Report file should have been created", Files.exists(reportFile)); - String reportText = IOUtils.toString(Files.newBufferedReader(reportFile, StandardCharsets.UTF_8)); + String reportText = IOUtil.readToString(Files.newBufferedReader(reportFile, StandardCharsets.UTF_8)); assertThat(reportText, not(containsStringIgnoringCase("error"))); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index 5c21740f85..74604cc10a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -7,11 +7,11 @@ package net.sourceforge.pmd.cpd; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.apache.commons.io.FilenameUtils; import org.junit.Test; public class CPDFilelistTest { @@ -28,7 +28,7 @@ public class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(FilenameUtils.getName(path)); + simpleNames.add(Paths.get(path).getFileName().toString()); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); @@ -46,7 +46,7 @@ public class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(FilenameUtils.getName(path)); + simpleNames.add(Paths.get(path).getFileName().toString()); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java index 085475f8d0..af8d5ada20 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdXsltTest.java @@ -17,10 +17,11 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; +import net.sourceforge.pmd.util.IOUtil; + public class CpdXsltTest { /* Sample ant build.xml file. Run with "ant cpdxsl". @@ -49,7 +50,7 @@ public class CpdXsltTest { transformer.setErrorListener(errorListener); transformer.transform(cpdReport, result); - String expected = IOUtils.toString(CpdXsltTest.class.getResourceAsStream("ExpectedCpdHtmlReport.html"), StandardCharsets.UTF_8); + String expected = IOUtil.readToString(CpdXsltTest.class.getResourceAsStream("ExpectedCpdHtmlReport.html"), StandardCharsets.UTF_8); Assert.assertEquals(expected, result.getWriter().toString()); Assert.assertTrue("XSLT errors occured: " + errorListener, errorListener.hasNoErrors()); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java index ece529938d..d578dec542 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java @@ -16,12 +16,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import net.sourceforge.pmd.util.IOUtil; + public class DocumentFileTest { private static final String FILE_PATH = "psvm.java"; @@ -51,8 +52,8 @@ public class DocumentFileTest { @Test public void shouldPreserveNewlines() throws IOException { - final String testFileContent = IOUtils.toString( - DocumentFileTest.class.getResource("ShouldPreserveNewlines.java"), StandardCharsets.UTF_8); + final String testFileContent = IOUtil.readToString( + DocumentFileTest.class.getResourceAsStream("ShouldPreserveNewlines.java"), StandardCharsets.UTF_8); writeContentToTemporaryFile(testFileContent); try (DocumentFile documentFile = new DocumentFile(temporaryFile, StandardCharsets.UTF_8)) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index 884e82ce99..f84aa9e635 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -13,8 +13,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -32,6 +30,7 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; +import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; public abstract class AbstractRendererTest { @@ -126,7 +125,7 @@ public abstract class AbstractRendererTest { */ protected String readFile(String relativePath) { try (InputStream in = getClass().getResourceAsStream(relativePath)) { - return IOUtils.toString(in, StandardCharsets.UTF_8); + return IOUtil.readToString(in, StandardCharsets.UTF_8); } catch (IOException e) { throw new RuntimeException(e); } @@ -221,7 +220,7 @@ public abstract class AbstractRendererTest { throw new AssertionError(e); } - return FileUtils.readFileToString(file, expectedEncoding); + return IOUtil.readFileToString(file, expectedEncoding); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java index 9faefb055f..858826c5a9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SarifRendererTest.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.reporting.FileAnalysisListener; - public class SarifRendererTest extends AbstractRendererTest { @org.junit.Rule diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java index aa6a10f28f..ec81f1bd5a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java @@ -12,7 +12,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import javax.xml.parsers.DocumentBuilderFactory; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -33,6 +32,7 @@ import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; +import net.sourceforge.pmd.util.IOUtil; public class XMLRendererTest extends AbstractRendererTest { @Rule // Restores system properties after test @@ -169,7 +169,7 @@ public class XMLRendererTest extends AbstractRendererTest { renderer.flush(); try (FileInputStream input = new FileInputStream(reportFile)) { - return IOUtils.toString(input, expectedCharset); + return IOUtil.readToString(input, expectedCharset); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java index f7e964be7f..e4fc711fb8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/YAHTMLRendererTest.java @@ -14,7 +14,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -28,6 +27,7 @@ import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; +import net.sourceforge.pmd.util.IOUtil; public class YAHTMLRendererTest extends AbstractRendererTest { @@ -75,8 +75,8 @@ public class YAHTMLRendererTest extends AbstractRendererTest { for (String file : htmlFiles) { try (FileInputStream in = new FileInputStream(new File(outputDir, file)); InputStream expectedIn = YAHTMLRendererTest.class.getResourceAsStream("yahtml/" + file)) { - String data = IOUtils.toString(in, StandardCharsets.UTF_8); - String expected = normalizeLineSeparators(IOUtils.toString(expectedIn, StandardCharsets.UTF_8)); + String data = IOUtil.readToString(in, StandardCharsets.UTF_8); + String expected = normalizeLineSeparators(IOUtil.readToString(expectedIn, StandardCharsets.UTF_8)); assertEquals("File " + file + " is different", expected, data); } @@ -84,8 +84,8 @@ public class YAHTMLRendererTest extends AbstractRendererTest { } private static String normalizeLineSeparators(String s) { - return s.replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_WINDOWS), IOUtils.LINE_SEPARATOR_UNIX) - .replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_UNIX), PMD.EOL); + return s.replaceAll(Pattern.quote("\r\n"), "\n") + .replaceAll(Pattern.quote("\n"), PMD.EOL); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java new file mode 100644 index 0000000000..7b21230fb6 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/IOUtilTest.java @@ -0,0 +1,284 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +public class IOUtilTest { + + @Test + public void testReadAllBytes() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(5, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testToByteArrayResize() throws IOException { + int size = 8192 + 8192 + 10; + byte[] data = new byte[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (InputStream stream = new ByteArrayInputStream(data)) { + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(size, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testSkipFully() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + Assert.assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + IOUtil.skipFully(stream, -1); + } + }); + + Assert.assertEquals(3, IOUtil.skipFully(stream, 3)); + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(2, bytes.length); + Assert.assertArrayEquals("45".getBytes(StandardCharsets.UTF_8), bytes); + } + } + + @Test + public void testSkipFully2() throws IOException { + byte[] data = "12345".getBytes(StandardCharsets.UTF_8); + try (InputStream stream = new ByteArrayInputStream(data)) { + // skip more bytes than the stream contains + Assert.assertEquals(data.length, IOUtil.skipFully(stream, data.length + 1)); + byte[] bytes = IOUtil.toByteArray(stream); + Assert.assertEquals(0, bytes.length); + } + } + + @Test + public void testNormalizePath() { + if (SystemUtils.IS_OS_UNIX) { + Assert.assertEquals("ab/cd.txt", IOUtil.normalizePath("ab/ef/../cd.txt")); + Assert.assertEquals("/a.txt", IOUtil.normalizePath("/x/../../a.txt")); + Assert.assertEquals("/foo", IOUtil.normalizePath("//../foo")); + Assert.assertEquals("/foo", IOUtil.normalizePath("/foo//")); + Assert.assertEquals("/foo", IOUtil.normalizePath("/foo/./")); + Assert.assertEquals("/bar", IOUtil.normalizePath("/foo/../bar")); + Assert.assertEquals("/bar", IOUtil.normalizePath("/foo/../bar/")); + Assert.assertEquals("/baz", IOUtil.normalizePath("/foo/../bar/../baz")); + Assert.assertEquals("/foo/bar", IOUtil.normalizePath("//foo//./bar")); + Assert.assertEquals("foo", IOUtil.normalizePath("foo/bar/..")); + Assert.assertEquals("bar", IOUtil.normalizePath("foo/../bar")); + Assert.assertEquals("/foo/baz", IOUtil.normalizePath("//foo/bar/../baz")); + Assert.assertEquals("~/bar", IOUtil.normalizePath("~/foo/../bar/")); + Assert.assertEquals("/", IOUtil.normalizePath("/../")); + Assert.assertEquals("bar", IOUtil.normalizePath("~/../bar")); + Assert.assertEquals("bar", IOUtil.normalizePath("./bar")); + + Assert.assertNull(IOUtil.normalizePath("../foo")); + Assert.assertNull(IOUtil.normalizePath("foo/../../bar")); + Assert.assertNull(IOUtil.normalizePath(".")); + + Assert.assertTrue(IOUtil.equalsNormalizedPaths("foo/../bar", "bar/./")); + } + + if (SystemUtils.IS_OS_WINDOWS) { + Assert.assertEquals("ab\\cd.txt", IOUtil.normalizePath("ab\\ef\\..\\cd.txt")); + Assert.assertEquals("\\a.txt", IOUtil.normalizePath("\\x\\..\\..\\a.txt")); + Assert.assertEquals("\\foo", IOUtil.normalizePath("\\foo\\\\")); + Assert.assertEquals("\\foo", IOUtil.normalizePath("\\foo\\.\\")); + Assert.assertEquals("\\bar", IOUtil.normalizePath("\\foo\\..\\bar")); + Assert.assertEquals("\\bar", IOUtil.normalizePath("\\foo\\..\\bar\\")); + Assert.assertEquals("\\baz", IOUtil.normalizePath("\\foo\\..\\bar\\..\\baz")); + Assert.assertEquals("\\\\foo\\bar\\", IOUtil.normalizePath("\\\\foo\\bar")); + Assert.assertEquals("\\\\foo\\bar\\baz", IOUtil.normalizePath("\\\\foo\\bar\\..\\baz")); + Assert.assertEquals("foo", IOUtil.normalizePath("foo\\bar\\..")); + Assert.assertEquals("bar", IOUtil.normalizePath("foo\\..\\bar")); + Assert.assertEquals("\\foo\\baz", IOUtil.normalizePath("\\foo\\bar\\..\\baz")); + Assert.assertEquals("\\", IOUtil.normalizePath("\\..\\")); + Assert.assertEquals("bar", IOUtil.normalizePath(".\\bar")); + + Assert.assertNull(IOUtil.normalizePath("\\\\..\\foo")); + Assert.assertNull(IOUtil.normalizePath("..\\foo")); + Assert.assertNull(IOUtil.normalizePath("foo\\..\\..\\bar")); + Assert.assertNull(IOUtil.normalizePath(".")); + Assert.assertNull(IOUtil.normalizePath("\\\\foo\\\\.\\bar")); + Assert.assertNull(IOUtil.normalizePath("\\\\foo\\.\\bar")); + + Assert.assertTrue(IOUtil.equalsNormalizedPaths("foo\\..\\bar", "bar\\.\\")); + + Assert.assertEquals("C:\\bar", IOUtil.normalizePath("C:\\..\\bar")); + Assert.assertEquals("ab\\cd.txt", IOUtil.normalizePath("ab\\ef\\..\\cd.txt")); + Assert.assertEquals("C:\\ab\\cd.txt", IOUtil.normalizePath("C:\\ab\\ef\\..\\.\\cd.txt")); + Assert.assertNull(IOUtil.normalizePath("..\\foo")); + Assert.assertNull(IOUtil.normalizePath("foo\\..\\..\\bar")); + } + } + + @Test + public void testFilenameExtension() { + Assert.assertEquals("txt", IOUtil.getFilenameExtension("ab/cd.txt")); + Assert.assertEquals("txt", IOUtil.getFilenameExtension("ab.cd.txt")); + Assert.assertEquals("", IOUtil.getFilenameExtension("ab/cd")); + Assert.assertEquals("html", IOUtil.getFilenameExtension("cd.html")); + } + + @Test + public void testFilenameBase() { + Assert.assertEquals("cd", IOUtil.getFilenameBase("ab/cd.txt")); + Assert.assertEquals("ab.cd", IOUtil.getFilenameBase("ab.cd.txt")); + Assert.assertEquals("cd", IOUtil.getFilenameBase("ab/cd")); + } + + @Test + public void testBomAwareStream() throws IOException { + assertBomStream("No BOM".getBytes(StandardCharsets.UTF_8), "No BOM", null); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_8), "BOM", StandardCharsets.UTF_8.name()); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_16LE), "BOM", StandardCharsets.UTF_16LE.name()); + assertBomStream("\ufeffBOM".getBytes(StandardCharsets.UTF_16BE), "BOM", StandardCharsets.UTF_16BE.name()); + } + + private void assertBomStream(byte[] data, String expectedData, String expectedCharset) throws IOException { + try (IOUtil.BomAwareInputStream stream = new IOUtil.BomAwareInputStream(new ByteArrayInputStream(data))) { + if (expectedCharset != null) { + Assert.assertTrue(stream.hasBom()); + Assert.assertEquals(expectedCharset, stream.getBomCharsetName()); + Assert.assertEquals(expectedData, new String(IOUtil.toByteArray(stream), stream.getBomCharsetName())); + + } else { + Assert.assertFalse(stream.hasBom()); + Assert.assertNull(stream.getBomCharsetName()); + Assert.assertEquals(expectedData, new String(IOUtil.toByteArray(stream), StandardCharsets.UTF_8)); + } + } + } + + @Test + public void testOutputStreamFromWriter() throws IOException { + StringWriter writer = new StringWriter(); + try (OutputStream outputStream = IOUtil.fromWriter(writer, "UTF-8")) { + outputStream.write("abc".getBytes(StandardCharsets.UTF_8)); + } + Assert.assertEquals("abc", writer.toString()); + } + + @Test + public void testInputStreamFromReader() throws IOException { + try (InputStream inputStream = IOUtil.fromReader(new StringReader("abc"))) { + byte[] bytes = IOUtil.toByteArray(inputStream); + Assert.assertEquals("abc", new String(bytes, StandardCharsets.UTF_8)); + } + } + + @Test + public void testCopyStream() throws IOException { + int size = 8192 + 8192 + 10; + byte[] data = new byte[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (InputStream stream = new ByteArrayInputStream(data); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + IOUtil.copy(stream, out); + byte[] bytes = out.toByteArray(); + Assert.assertEquals(size, bytes.length); + Assert.assertArrayEquals(data, bytes); + } + } + + @Test + public void testCopyReader() throws IOException { + int size = 8192 + 8192 + 10; + char[] data = new char[size]; + for (int i = 0; i < size; i++) { + data[i] = 'A'; + } + try (Reader reader = new CharArrayReader(data); + StringWriter writer = new StringWriter()) { + IOUtil.copy(reader, writer); + char[] chars = writer.toString().toCharArray(); + Assert.assertEquals(size, chars.length); + Assert.assertArrayEquals(data, chars); + } + } + + @Test + public void testReadEmptyStream() throws IOException { + try (InputStream in = new ByteArrayInputStream(new byte[0])) { + byte[] bytes = IOUtil.toByteArray(in); + Assert.assertNotNull(bytes); + Assert.assertEquals(0, bytes.length); + } + } + + @Test + public void testCloseQuietly() { + class Stream extends InputStream { + private boolean closed = false; + + @Override + public int read() throws IOException { + return 0; + } + + @Override + public void close() throws IOException { + closed = true; + throw new IOException("test"); + } + + public boolean isClosed() { + return closed; + } + } + + Stream stream = new Stream(); + IOUtil.closeQuietly(stream); + Assert.assertTrue(stream.isClosed()); + } + + @Test + public void testReadFileToString() throws IOException { + String testString = "Test ABC"; + Path tempFile = Files.createTempFile("pmd", ".txt"); + Files.write(tempFile, testString.getBytes(Charset.defaultCharset())); + Assert.assertEquals(testString, IOUtil.readFileToString(tempFile.toFile())); + } + + @Test + public void testReadToString() throws IOException { + String testString = "testReadToString"; + Reader reader = new StringReader(testString); + Assert.assertEquals(testString, IOUtil.readToString(reader)); + } + + @Test + public void testReadStreamToString() throws IOException { + String testString = "testReadStreamToString"; + InputStream stream = new ByteArrayInputStream(testString.getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(testString, IOUtil.readToString(stream, StandardCharsets.UTF_8)); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java index 2282641b40..7b3aa6e48e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeExportCliTest.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.util.treeexport; import static org.hamcrest.Matchers.containsString; import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -15,7 +16,6 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import org.apache.commons.io.IOUtils; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; import org.junit.Assert; @@ -67,7 +67,7 @@ public class TreeExportCliTest { } private static InputStream stdinContaining(String input) { - return IOUtils.toInputStream(input, StandardCharsets.UTF_8); + return new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)); } static class IoSpy { diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index e8d3b680af..ec704c797e 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -271,10 +271,6 @@ slf4j-simple
- - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java index 6d11b7d9d7..2c840d2487 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AntIT.java @@ -8,15 +8,19 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; import org.junit.Assume; import org.junit.Test; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.util.IOUtil; /** * This test calls ant in a fake terminal to make sure we have a {@link java.io.Console} connected. @@ -41,10 +45,28 @@ public class AntIT extends AbstractBinaryDistributionTest { private File prepareAntTestProjectFolder() throws IOException { - File sourceProjectFolder = new File("src/test/resources/ant-it"); - File projectFolder = folder.newFolder(); - FileUtils.copyDirectory(sourceProjectFolder, projectFolder); - return projectFolder; + final Path sourceProjectFolder = new File("src/test/resources/ant-it").toPath(); + final Path projectFolder = folder.newFolder().toPath(); + Files.walkFileTree(sourceProjectFolder, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + assert !dir.isAbsolute(); + Path target = projectFolder.resolve(sourceProjectFolder.relativize(dir)); + if (!target.toFile().exists()) { + target.toFile().mkdir(); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + assert !file.isAbsolute(); + Path target = projectFolder.resolve(sourceProjectFolder.relativize(file)); + Files.copy(file, target); + return FileVisitResult.CONTINUE; + } + }); + return projectFolder.toFile(); } @@ -64,7 +86,7 @@ public class AntIT extends AbstractBinaryDistributionTest { @Override public void run() { try (InputStream in = process.getInputStream()) { - String output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + String output = IOUtil.readToString(process.getInputStream(), StandardCharsets.UTF_8); result.withOutput(output); } catch (IOException e) { result.withOutput("Exception occurred: " + e.toString()); diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java index 1ab6ddbba2..b61e94d7a7 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java @@ -5,17 +5,19 @@ package net.sourceforge.pmd.it; import java.io.IOException; +import java.io.Reader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.util.IOUtil; /** * Executes PMD from command line. Deals with the differences, when PMD is run on Windows or on Linux. @@ -59,7 +61,7 @@ public class PMDExecutor { public void run() { String output; try { - output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8); + output = IOUtil.readToString(process.getInputStream(), StandardCharsets.UTF_8); result.withOutput(output); } catch (IOException e) { result.withOutput("Exception occurred: " + e.toString()); @@ -72,7 +74,7 @@ public class PMDExecutor { public void run() { String error; try { - error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8); + error = IOUtil.readToString(process.getErrorStream(), StandardCharsets.UTF_8); result.withErrorOutput(error); } catch (IOException e) { result.withErrorOutput("Exception occurred: " + e.toString()); @@ -87,7 +89,9 @@ public class PMDExecutor { String report = null; if (reportFile != null) { - report = IOUtils.toString(reportFile.toUri(), StandardCharsets.UTF_8); + try (Reader reader = Files.newBufferedReader(reportFile, StandardCharsets.UTF_8)) { + report = IOUtil.readToString(reader); + } } return result.withExitCode(exitCode).withReport(report).build(); } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java index 27a8b325d6..dbfe2d13a5 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java @@ -17,7 +17,8 @@ import java.util.List; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; -import org.apache.commons.io.IOUtils; + +import net.sourceforge.pmd.util.IOUtil; /** * Extracts a zip file with preserving the unix file permissions. @@ -49,7 +50,7 @@ public class ZipFileExtractor { } else { try (InputStream data = zip.getInputStream(entry); OutputStream fileOut = new FileOutputStream(file);) { - IOUtils.copy(data, fileOut); + IOUtil.copy(data, fileOut); } if ((entry.getUnixMode() & OWNER_EXECUTABLE) == OWNER_EXECUTABLE) { file.setExecutable(true); diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 384f4ea8b1..d3fd823e21 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -83,10 +83,6 @@ pmd-core ${project.version} - - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java index 3a7ee37eb4..499b7b67e3 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DeadLinksChecker.java @@ -36,10 +36,10 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.util.IOUtil; /** * Checks links to local pages for non-existing link-targets. @@ -299,7 +299,7 @@ public class DeadLinksChecker { private String fileToString(Path mdFile) { try (InputStream inputStream = Files.newInputStream(mdFile)) { - return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + return IOUtil.readToString(inputStream, StandardCharsets.UTF_8); } catch (IOException ex) { throw new RuntimeException("error reading " + mdFile, ex); } diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index ddec28206a..2b3b19c424 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -10,16 +10,16 @@ import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.io.FilenameUtils; - import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.util.IOUtil; public final class GenerateRuleDocsCmd { @@ -53,8 +53,8 @@ public final class GenerateRuleDocsCmd { try { List additionalRulesets = new ArrayList<>(); Pattern rulesetPattern = Pattern.compile("^.+" + Pattern.quote(File.separator) + "pmd-\\w+" - + Pattern.quote(FilenameUtils.normalize("/src/main/resources/rulesets/")) - + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); + + Pattern.quote(IOUtil.normalizePath(File.separator + Paths.get("src", "main", "resources", "rulesets").toString() + File.separator)) + + "\\w+" + Pattern.quote(File.separator) + "\\w+.xml$"); Files.walkFileTree(basePath, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 66951fa188..f73fd8056d 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -28,8 +28,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,6 +43,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.util.IOUtil; public class RuleDocGenerator { private static final Logger LOG = LoggerFactory.getLogger(RuleDocGenerator.class); @@ -131,7 +132,7 @@ public class RuleDocGenerator { } private Path getAbsoluteOutputPath(String filename) { - return root.resolve(FilenameUtils.normalize(filename)); + return root.resolve(IOUtil.normalizePath(filename)); } private Map> sortRulesets(List rulesets) { @@ -608,17 +609,13 @@ public class RuleDocGenerator { // is replaced by a correct path. for (List rulesets : sortedRulesets.values()) { for (RuleSet ruleset : rulesets) { - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - String rulesetFilename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()), true); + String rulesetFilename = normalizeForwardSlashes(StringUtils.chomp(ruleset.getFileName())); allRulesets.put(ruleset.getFileName(), rulesetFilename); for (Rule rule : ruleset.getRules()) { String ruleClass = rule.getRuleClass(); String relativeSourceFilename = ruleClass.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + ".java"; - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRules.put(ruleClass, FilenameUtils.normalize(relativeSourceFilename, true)); + allRules.put(ruleClass, normalizeForwardSlashes(relativeSourceFilename)); } } } @@ -640,9 +637,7 @@ public class RuleDocGenerator { } if (foundRuleClass != null) { Path foundPath = root.relativize(file); - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRules.put(foundRuleClass, FilenameUtils.normalize(foundPath.toString(), true)); + allRules.put(foundRuleClass, normalizeForwardSlashes(foundPath.toString())); } String foundRuleset = null; @@ -654,9 +649,7 @@ public class RuleDocGenerator { } if (foundRuleset != null) { Path foundPath = root.relativize(file); - // Note: the path is normalized to unix path separators, so that the editme link - // uses forward slashes - allRulesets.put(foundRuleset, FilenameUtils.normalize(foundPath.toString(), true)); + allRulesets.put(foundRuleset, normalizeForwardSlashes(foundPath.toString())); } } return FileVisitResult.CONTINUE; @@ -666,4 +659,14 @@ public class RuleDocGenerator { throw new RuntimeException(e); } } + + private static String normalizeForwardSlashes(String path) { + String normalized = IOUtil.normalizePath(path); + if (SystemUtils.IS_OS_WINDOWS) { + // Note: windows path separators are changed to forward slashes, + // so that the editme link works + normalized = normalized.replaceAll(Pattern.quote(File.separator), "/"); + } + return normalized; + } } diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java index f928fe1de6..a7afef7b5b 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleSetUtils.java @@ -4,12 +4,14 @@ package net.sourceforge.pmd.docs; -import org.apache.commons.io.FilenameUtils; +import java.io.File; + import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.util.IOUtil; public final class RuleSetUtils { @@ -28,7 +30,7 @@ public final class RuleSetUtils { } public static String getRuleSetFilename(String rulesetFileName) { - return FilenameUtils.getBaseName(StringUtils.chomp(rulesetFileName)); + return IOUtil.getFilenameBase(StringUtils.chomp(rulesetFileName)); } /** @@ -50,8 +52,8 @@ public final class RuleSetUtils { } public static String getRuleSetClasspath(RuleSet ruleset) { - final String RESOURCES_PATH = "/resources/"; - String filename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()), true); + final String RESOURCES_PATH = File.separator + "resources" + File.separator; + String filename = IOUtil.normalizePath(StringUtils.chomp(ruleset.getFileName())); int startIndex = filename.lastIndexOf(RESOURCES_PATH); if (startIndex > -1) { return filename.substring(startIndex + RESOURCES_PATH.length()); diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java index dc5ee7e626..3f90cbe4cb 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.PMD; @@ -49,7 +48,7 @@ public class MockedFileWriter implements FileWriter { } public static String normalizeLineSeparators(String s) { - return s.replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_WINDOWS), IOUtils.LINE_SEPARATOR_UNIX) - .replaceAll(Pattern.quote(IOUtils.LINE_SEPARATOR_UNIX), PMD.EOL); + return s.replaceAll(Pattern.quote("\r\n"), "\n") + .replaceAll(Pattern.quote("\n"), PMD.EOL); } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java index dc96ec33a5..f994834900 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -11,11 +11,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,6 +23,7 @@ import org.junit.rules.TemporaryFolder; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; +import net.sourceforge.pmd.util.IOUtil; public class RuleDocGeneratorTest { @@ -52,7 +52,7 @@ public class RuleDocGeneratorTest { private static String loadResource(String name) throws IOException { return MockedFileWriter.normalizeLineSeparators( - IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream(name), StandardCharsets.UTF_8)); + IOUtil.readToString(RuleDocGeneratorTest.class.getResourceAsStream(name), StandardCharsets.UTF_8)); } @Test @@ -69,15 +69,15 @@ public class RuleDocGeneratorTest { assertEquals(3, writer.getData().size()); FileEntry languageIndex = writer.getData().get(0); - assertTrue(FilenameUtils.normalize(languageIndex.getFilename(), true).endsWith("docs/pages/pmd/rules/java.md")); + assertTrue(IOUtil.normalizePath(languageIndex.getFilename()).endsWith(Paths.get("docs", "pages", "pmd", "rules", "java.md").toString())); assertEquals(loadResource("/expected/java.md"), languageIndex.getContent()); FileEntry ruleSetIndex = writer.getData().get(1); - assertTrue(FilenameUtils.normalize(ruleSetIndex.getFilename(), true).endsWith("docs/pages/pmd/rules/java/sample.md")); + assertTrue(IOUtil.normalizePath(ruleSetIndex.getFilename()).endsWith(Paths.get("docs", "pages", "pmd", "rules", "java", "sample.md").toString())); assertEquals(loadResource("/expected/sample.md"), ruleSetIndex.getContent()); FileEntry sidebar = writer.getData().get(2); - assertTrue(FilenameUtils.normalize(sidebar.getFilename(), true).endsWith("docs/_data/sidebars/pmd_sidebar.yml")); + assertTrue(IOUtil.normalizePath(sidebar.getFilename()).endsWith(Paths.get("docs", "_data", "sidebars", "pmd_sidebar.yml").toString())); assertEquals(loadResource("/expected/pmd_sidebar.yml"), sidebar.getContent()); } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index 8a56f9077e..d603abccf0 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -10,15 +10,15 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.List; -import org.apache.commons.io.FilenameUtils; import org.junit.Test; import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.util.IOUtil; public class RuleSetResolverTest { private static final List EXCLUDED_RULESETS = listOf( - FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml") + IOUtil.normalizePath("pmd-test/src/main/resources/rulesets/dummy/basic.xml") ); @Test diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java index 97e562e49b..0ae7e05323 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SystemUtils; import org.junit.Before; import org.junit.Test; @@ -27,6 +26,7 @@ import org.yaml.snakeyaml.Yaml; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.IOUtil; public class SidebarGeneratorTest { private MockedFileWriter writer = new MockedFileWriter(); @@ -55,7 +55,7 @@ public class SidebarGeneratorTest { String yaml = new Yaml(options).dump(result); String expected = MockedFileWriter.normalizeLineSeparators( - IOUtils.toString(SidebarGeneratorTest.class.getResourceAsStream("sidebar.yml"), StandardCharsets.UTF_8)); + IOUtil.readToString(SidebarGeneratorTest.class.getResourceAsStream("sidebar.yml"), StandardCharsets.UTF_8)); assertEquals(expected, yaml); } } diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java index a2ce2533b0..9042c90243 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java @@ -19,4 +19,5 @@ public final class HtmlParser implements net.sourceforge.pmd.lang.ast.Parser { HtmlTreeBuilder builder = new HtmlTreeBuilder(); return builder.build(doc, data, task, new HashMap<>()); } + } } diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 1b25ad4b65..12edfa299e 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -140,10 +140,6 @@ org.ow2.asm asm - - commons-io - commons-io - org.apache.commons commons-lang3 diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 506dd0234e..339df23fbd 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -86,10 +86,6 @@ rhino 1.7.14 - - commons-io - commons-io - junit diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index 3f1e9bd3e8..41dc37f3b3 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -112,10 +112,6 @@ org.apache.commons commons-lang3 - - commons-io - commons-io - - - commons-io - commons-io - 2.6 - org.apache.commons commons-lang3